Schedule toots

This commit is contained in:
tom79 2017-07-16 17:09:35 +02:00
parent 0d6fd936e7
commit c327bd8938
24 changed files with 895 additions and 27 deletions

View File

@ -58,6 +58,7 @@ import fr.gouv.etalab.mastodon.client.PatchBaseImageDownloader;
import fr.gouv.etalab.mastodon.fragments.DisplayAccountsFragment;
import fr.gouv.etalab.mastodon.fragments.DisplayFollowRequestSentFragment;
import fr.gouv.etalab.mastodon.fragments.DisplayNotificationsFragment;
import fr.gouv.etalab.mastodon.fragments.DisplayScheduledTootsFragment;
import fr.gouv.etalab.mastodon.helper.Helper;
import fr.gouv.etalab.mastodon.interfaces.OnUpdateAccountInfoInterface;
import fr.gouv.etalab.mastodon.sqlite.Sqlite;
@ -563,7 +564,12 @@ public class MainActivity extends AppCompatActivity
fragmentTag = "MUTED";
fragmentManager.beginTransaction()
.replace(R.id.main_app_container, accountsFragment, fragmentTag).addToBackStack(fragmentTag).commit();
}else if( id == R.id.nav_notification){
}else if (id == R.id.nav_scheduled) {
DisplayScheduledTootsFragment displayScheduledTootsFragment = new DisplayScheduledTootsFragment();
fragmentTag = "SCHEDULED";
fragmentManager.beginTransaction()
.replace(R.id.main_app_container, displayScheduledTootsFragment, fragmentTag).addToBackStack(fragmentTag).commit();
} else if( id == R.id.nav_notification){
toot.setVisibility(View.GONE);
DisplayNotificationsFragment notificationsFragment = new DisplayNotificationsFragment();
fragmentTag = "NOTIFICATIONS";

View File

@ -35,6 +35,7 @@ import android.text.Editable;
import android.text.Html;
import android.text.TextWatcher;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@ -44,6 +45,7 @@ import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
@ -51,6 +53,7 @@ import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.TimePicker;
import android.widget.Toast;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
@ -61,6 +64,9 @@ import com.nostra13.universalimageloader.core.display.SimpleBitmapDisplayer;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
@ -83,6 +89,7 @@ import fr.gouv.etalab.mastodon.helper.Helper;
import fr.gouv.etalab.mastodon.interfaces.OnPostActionInterface;
import fr.gouv.etalab.mastodon.interfaces.OnRetrieveAttachmentInterface;
import fr.gouv.etalab.mastodon.interfaces.OnRetrieveSearcAccountshInterface;
import fr.gouv.etalab.mastodon.jobs.ScheduledTootsSyncJob;
import fr.gouv.etalab.mastodon.sqlite.AccountDAO;
import fr.gouv.etalab.mastodon.sqlite.StatusStoredDAO;
import fr.gouv.etalab.mastodon.sqlite.Sqlite;
@ -521,7 +528,7 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc
}
return true;
case R.id.action_store:
storeToot();
storeToot(true);
return true;
case R.id.action_restore:
try{
@ -650,13 +657,102 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc
}
return true;
/*case R.id.action_schedule:
case R.id.action_schedule:
if(toot_content.getText().toString().trim().length() == 0 ){
Toast.makeText(getApplicationContext(),R.string.toot_error_no_content, Toast.LENGTH_LONG).show();
return true;
}
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(TootActivity.this);
LayoutInflater inflater = this.getLayoutInflater();
View dialogView = inflater.inflate(R.layout.datetime_picker, null);
dialogBuilder.setView(dialogView);
final AlertDialog alertDialog = dialogBuilder.create();
return true;*/
final DatePicker datePicker = (DatePicker) dialogView.findViewById(R.id.date_picker);
final TimePicker timePicker = (TimePicker) dialogView.findViewById(R.id.time_picker);
Button date_time_cancel = (Button) dialogView.findViewById(R.id.date_time_cancel);
final Button date_time_previous = (Button) dialogView.findViewById(R.id.date_time_previous);
final Button date_time_next = (Button) dialogView.findViewById(R.id.date_time_next);
final Button date_time_set = (Button) dialogView.findViewById(R.id.date_time_set);
//Buttons management
date_time_cancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
alertDialog.dismiss();
}
});
date_time_next.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
datePicker.setVisibility(View.GONE);
timePicker.setVisibility(View.VISIBLE);
date_time_previous.setVisibility(View.VISIBLE);
date_time_next.setVisibility(View.GONE);
date_time_set.setVisibility(View.VISIBLE);
}
});
date_time_previous.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
datePicker.setVisibility(View.VISIBLE);
timePicker.setVisibility(View.GONE);
date_time_previous.setVisibility(View.GONE);
date_time_next.setVisibility(View.VISIBLE);
date_time_set.setVisibility(View.GONE);
}
});
date_time_set.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int hour, minute;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
hour = timePicker.getHour();
minute = timePicker.getMinute();
}else {
//noinspection deprecation
hour = timePicker.getCurrentHour();
//noinspection deprecation
minute = timePicker.getCurrentMinute();
}
Calendar calendar = new GregorianCalendar(datePicker.getYear(),
datePicker.getMonth(),
datePicker.getDayOfMonth(),
hour,
minute);
long time = calendar.getTimeInMillis();
if( (time - new Date().getTime()) < 60000 ){
Toast.makeText(getApplicationContext(), R.string.toot_scheduled_date, Toast.LENGTH_LONG).show();
}else {
//Store the toot as draft first
storeToot(false);
//Schedules the toot
ScheduledTootsSyncJob.schedule(getApplicationContext(), true, currentToId, time);
//Clear content
toot_content.setText("");
toot_cw_content.setText("");
if( attachments != null) {
for (Attachment attachment : attachments) {
View namebar = findViewById(Integer.parseInt(attachment.getId()));
if (namebar != null && namebar.getParent() != null)
((ViewGroup) namebar.getParent()).removeView(namebar);
}
List<Attachment> tmp_attachment = new ArrayList<>();
tmp_attachment.addAll(attachments);
attachments.removeAll(tmp_attachment);
tmp_attachment.clear();
}
isSensitive = false;
toot_sensitive.setVisibility(View.GONE);
currentToId = -1;
Toast.makeText(TootActivity.this,R.string.toot_scheduled, Toast.LENGTH_LONG).show();
alertDialog.dismiss();
}
}
});
alertDialog.show();
return true;
default:
return super.onOptionsItemSelected(item);
}
@ -798,7 +894,7 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc
final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
boolean storeToot = sharedpreferences.getBoolean(Helper.SET_AUTO_STORE, true);
if( storeToot)
storeToot();
storeToot(true);
}
@Override
@ -827,6 +923,7 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc
}
isSensitive = false;
toot_sensitive.setVisibility(View.GONE);
currentToId = -1;
Toast.makeText(TootActivity.this,R.string.toot_sent, Toast.LENGTH_LONG).show();
}else {
Toast.makeText(TootActivity.this,R.string.toast_error, Toast.LENGTH_LONG).show();
@ -853,7 +950,7 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc
}
}
private void storeToot(){
private void storeToot(boolean message){
//Nothing to store here....
if(toot_content.getText().toString().trim().length() == 0 && (attachments == null || attachments.size() <1) && toot_cw_content.getText().toString().trim().length() == 0)
return;
@ -870,20 +967,21 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc
SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
try{
if( currentToId == -1 ) {
currentToId = new StatusStoredDAO(TootActivity.this, db).insertStatus(toot, false, null);
currentToId = new StatusStoredDAO(TootActivity.this, db).insertStatus(toot);
}else{
StoredStatus storedStatus = new StatusStoredDAO(TootActivity.this, db).getStatus(currentToId);
if( storedStatus != null ){
new StatusStoredDAO(TootActivity.this, db).updateStatus(currentToId, toot);
}else { //Might have been deleted, so it needs insertion
new StatusStoredDAO(TootActivity.this, db).insertStatus(toot, false, null);
new StatusStoredDAO(TootActivity.this, db).insertStatus(toot);
}
}
Toast.makeText(getApplicationContext(), R.string.toast_toot_saved, Toast.LENGTH_LONG).show();
if( message )
Toast.makeText(getApplicationContext(), R.string.toast_toot_saved, Toast.LENGTH_LONG).show();
}catch (Exception e){
Toast.makeText(getApplicationContext(), R.string.toast_error, Toast.LENGTH_LONG).show();
if( message)
Toast.makeText(getApplicationContext(), R.string.toast_error, Toast.LENGTH_LONG).show();
}
}

View File

@ -0,0 +1,58 @@
/* Copyright 2017 Thomas Schneider
*
* This file is a part of Mastalab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Mastalab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Thomas Schneider; if not,
* see <http://www.gnu.org/licenses>. */
package fr.gouv.etalab.mastodon.asynctasks;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.os.AsyncTask;
import java.util.List;
import fr.gouv.etalab.mastodon.client.Entities.StoredStatus;
import fr.gouv.etalab.mastodon.interfaces.OnRetrieveScheduledTootsInterface;
import fr.gouv.etalab.mastodon.sqlite.Sqlite;
import fr.gouv.etalab.mastodon.sqlite.StatusStoredDAO;
/**
* Created by Thomas on 16/07/2017.
* Retrieves scheduled toots for an account
*/
public class RetrieveScheduledTootsAsyncTask extends AsyncTask<Void, Void, Void> {
private Context context;
private OnRetrieveScheduledTootsInterface listener;
private List<StoredStatus> storedStatuses;
public RetrieveScheduledTootsAsyncTask(Context context, OnRetrieveScheduledTootsInterface onRetrieveScheduledTootsInterface){
this.context = context;
this.listener = onRetrieveScheduledTootsInterface;
}
@Override
protected Void doInBackground(Void... params) {
SQLiteDatabase db = Sqlite.getInstance(context, Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
storedStatuses = new StatusStoredDAO(context, db).getAllScheduled();
return null;
}
@Override
protected void onPostExecute(Void result) {
listener.onRetrieveScheduledToots(storedStatuses);
}
}

View File

@ -0,0 +1,175 @@
package fr.gouv.etalab.mastodon.drawers;
/* Copyright 2017 Thomas Schneider
*
* This file is a part of Mastalab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Mastalab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Thomas Schneider; if not,
* see <http://www.gnu.org/licenses>. */
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.database.sqlite.SQLiteDatabase;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;
import fr.gouv.etalab.mastodon.client.Entities.Status;
import fr.gouv.etalab.mastodon.client.Entities.StoredStatus;
import fr.gouv.etalab.mastodon.helper.Helper;
import fr.gouv.etalab.mastodon.sqlite.Sqlite;
import fr.gouv.etalab.mastodon.sqlite.StatusStoredDAO;
import mastodon.etalab.gouv.fr.mastodon.R;
import static fr.gouv.etalab.mastodon.helper.Helper.changeDrawableColor;
/**
* Created by Thomas on 16/07/2017.
* Adapter for scheduled toots
*/
public class ScheduledTootsListAdapter extends BaseAdapter {
private Context context;
private List<StoredStatus> storedStatuses;
private LayoutInflater layoutInflater;
private ScheduledTootsListAdapter scheduledTootsListAdapter;
public ScheduledTootsListAdapter(Context context, List<StoredStatus> storedStatuses){
this.context = context;
this.storedStatuses = storedStatuses;
layoutInflater = LayoutInflater.from(this.context);
scheduledTootsListAdapter = this;
}
@Override
public int getCount() {
return storedStatuses.size();
}
@Override
public Object getItem(int position) {
return storedStatuses.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
final StoredStatus storedStatus = storedStatuses.get(position);
final ViewHolder holder;
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.drawer_scheduled_toot, parent, false);
holder = new ViewHolder();
holder.scheduled_toot_title = (TextView) convertView.findViewById(R.id.scheduled_toot_title);
holder.scheduled_toot_date_creation = (TextView) convertView.findViewById(R.id.scheduled_toot_date_creation);
holder.scheduled_toot_media_count = (TextView) convertView.findViewById(R.id.scheduled_toot_media_count);
holder.scheduled_toot_delete = (ImageView) convertView.findViewById(R.id.scheduled_toot_delete);
holder.scheduled_toot_privacy = (ImageView) convertView.findViewById(R.id.scheduled_toot_privacy);
holder.scheduled_toot_date = (Button) convertView.findViewById(R.id.scheduled_toot_date);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
int theme = sharedpreferences.getInt(Helper.SET_THEME, Helper.THEME_DARK);
if( theme == Helper.THEME_DARK){
changeDrawableColor(context, R.drawable.ic_cancel,R.color.dark_text);
changeDrawableColor(context, R.drawable.ic_action_globe,R.color.dark_text);
changeDrawableColor(context, R.drawable.ic_action_lock_open,R.color.dark_text);
changeDrawableColor(context, R.drawable.ic_action_lock_closed,R.color.dark_text);
changeDrawableColor(context, R.drawable.ic_local_post_office,R.color.dark_text);
}else {
changeDrawableColor(context, R.drawable.ic_cancel,R.color.black);
changeDrawableColor(context, R.drawable.ic_action_globe,R.color.black);
changeDrawableColor(context, R.drawable.ic_action_lock_open,R.color.black);
changeDrawableColor(context, R.drawable.ic_action_lock_closed,R.color.black);
changeDrawableColor(context, R.drawable.ic_local_post_office,R.color.black);
}
final Status status = storedStatus.getStatus();
switch (status.getVisibility()) {
case "public":
holder.scheduled_toot_privacy.setImageResource(R.drawable.ic_action_globe);
break;
case "unlisted":
holder.scheduled_toot_privacy.setImageResource(R.drawable.ic_action_lock_open);
break;
case "private":
holder.scheduled_toot_privacy.setImageResource(R.drawable.ic_action_lock_closed);
break;
case "direct":
holder.scheduled_toot_privacy.setImageResource(R.drawable.ic_local_post_office);
break;
}
final SQLiteDatabase db = Sqlite.getInstance(context, Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
//Delete scheduled toot
holder.scheduled_toot_delete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setMessage(status.getContent() + '\n' + storedStatus.getCreation_date());
builder.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(R.string.remove_scheduled)
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
new StatusStoredDAO(context, db).remove(storedStatus.getId());
storedStatuses.remove(storedStatus);
scheduledTootsListAdapter.notifyDataSetChanged();
dialog.dismiss();
}
})
.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})
.show();
}
});
holder.scheduled_toot_media_count.setText(context.getString(R.string.media_count, status.getMedia_attachments().size()));
holder.scheduled_toot_date_creation.setText(Helper.dateToString(context, storedStatus.getCreation_date()));
holder.scheduled_toot_title.setText(status.getContent());
return convertView;
}
private class ViewHolder {
TextView scheduled_toot_title;
TextView scheduled_toot_date_creation;
TextView scheduled_toot_media_count;
ImageView scheduled_toot_delete;
ImageView scheduled_toot_privacy;
Button scheduled_toot_date;
}
}

View File

@ -0,0 +1,95 @@
package fr.gouv.etalab.mastodon.fragments;
/* Copyright 2017 Thomas Schneider
*
* This file is a part of Mastalab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Mastalab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Thomas Schneider; if not,
* see <http://www.gnu.org/licenses>. */
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import android.widget.RelativeLayout;
import java.util.List;
import fr.gouv.etalab.mastodon.asynctasks.RetrieveScheduledTootsAsyncTask;
import fr.gouv.etalab.mastodon.client.Entities.StoredStatus;
import fr.gouv.etalab.mastodon.drawers.ScheduledTootsListAdapter;
import fr.gouv.etalab.mastodon.interfaces.OnRetrieveScheduledTootsInterface;
import mastodon.etalab.gouv.fr.mastodon.R;
/**
* Created by Thomas on 16/07/2017.
* Fragment to display scheduled toots
*/
public class DisplayScheduledTootsFragment extends Fragment implements OnRetrieveScheduledTootsInterface {
private Context context;
private AsyncTask<Void, Void, Void> asyncTask;
private RelativeLayout mainLoader, textviewNoAction;
private ListView lv_scheduled_toots;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_scheduled_toots, container, false);
context = getContext();
lv_scheduled_toots = (ListView) rootView.findViewById(R.id.lv_scheduled_toots);
mainLoader = (RelativeLayout) rootView.findViewById(R.id.loader);
textviewNoAction = (RelativeLayout) rootView.findViewById(R.id.no_action);
mainLoader.setVisibility(View.VISIBLE);
asyncTask = new RetrieveScheduledTootsAsyncTask(context, DisplayScheduledTootsFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
return rootView;
}
@Override
public void onCreate(Bundle saveInstance)
{
super.onCreate(saveInstance);
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
this.context = context;
}
public void onDestroy() {
super.onDestroy();
if(asyncTask != null && asyncTask.getStatus() == AsyncTask.Status.RUNNING)
asyncTask.cancel(true);
}
@Override
public void onRetrieveScheduledToots(List<StoredStatus> storedStatuses) {
mainLoader.setVisibility(View.GONE);
if( storedStatuses != null && storedStatuses.size() > 0 ){
ScheduledTootsListAdapter scheduledTootsListAdapter = new ScheduledTootsListAdapter(context, storedStatuses);
lv_scheduled_toots.setAdapter(scheduledTootsListAdapter);
}else {
textviewNoAction.setVisibility(View.VISIBLE);
}
}
}

View File

@ -841,6 +841,9 @@ public class Helper {
navigationView.getMenu().performIdentifierAction(R.id.nav_home, 0);
SQLiteDatabase db = Sqlite.getInstance(activity, Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
Account account = new AccountDAO(activity,db).getAccountByID(userID);
//Can happen when an account has been deleted and there is a click on an old notification
if( account == null)
return;
//Locked account can see follow request
if (account.isLocked()) {
navigationView.getMenu().findItem(R.id.nav_follow_request).setVisible(true);

View File

@ -0,0 +1,26 @@
/* Copyright 2017 Thomas Schneider
*
* This file is a part of Mastalab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Mastalab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Thomas Schneider; if not,
* see <http://www.gnu.org/licenses>. */
package fr.gouv.etalab.mastodon.interfaces;
import java.util.List;
import fr.gouv.etalab.mastodon.client.Entities.StoredStatus;
/**
* Created by Thomas on 16/07/2017.
* Interface when scheduled toots have been retrieved
*/
public interface OnRetrieveScheduledTootsInterface {
void onRetrieveScheduledToots(List<StoredStatus> storedStatuses);
}

View File

@ -30,6 +30,8 @@ public class ApplicationJob implements JobCreator {
return new NotificationsSyncJob();
case HomeTimelineSyncJob.HOME_TIMELINE:
return new HomeTimelineSyncJob();
case ScheduledTootsSyncJob.SCHEDULED_TOOT:
return new ScheduledTootsSyncJob();
default:
return null;
}
@ -38,4 +40,8 @@ public class ApplicationJob implements JobCreator {
public static void cancelAllJob(String TAG){
JobManager.instance().cancelAllForTag(TAG);
}
private void cancelJob(int jobId) {
JobManager.instance().cancel(jobId);
}
}

View File

@ -0,0 +1,86 @@
package fr.gouv.etalab.mastodon.jobs;
/* Copyright 2017 Thomas Schneider
*
* This file is a part of Mastalab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Mastalab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Thomas Schneider; if not,
* see <http://www.gnu.org/licenses>. */
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.support.annotation.NonNull;
import com.evernote.android.job.Job;
import com.evernote.android.job.JobRequest;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import fr.gouv.etalab.mastodon.client.API;
import fr.gouv.etalab.mastodon.client.Entities.Status;
import fr.gouv.etalab.mastodon.client.Entities.StoredStatus;
import fr.gouv.etalab.mastodon.sqlite.Sqlite;
import fr.gouv.etalab.mastodon.sqlite.StatusStoredDAO;
/**
* Created by Thomas on 16/07/2017.
* Scheduled a toot a datetime
*/
public class ScheduledTootsSyncJob extends Job {
static final String SCHEDULED_TOOT = "job_scheduled_toot";
@NonNull
@Override
protected Result onRunJob(Params params) {
//Code refresh here
int jobId = params.getId();
SQLiteDatabase db = Sqlite.getInstance(getContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
StoredStatus storedStatus = new StatusStoredDAO(getContext(), db).getStatusScheduled(jobId);
//Retrieves the stored status
if( storedStatus != null){
//Retrieves the linked status to toot
Status status = storedStatus.getStatus();
if( status != null){
int statusCode = new API(getContext()).statusAction(status);
//Toot was sent
if( statusCode == 200){
new StatusStoredDAO(getContext(), db).updateScheduledDone(jobId, new Date());
}
}
}
return Result.SUCCESS;
}
public static int schedule(Context context, boolean updateCurrent, long id, long timestampScheduling){
long startMs = (timestampScheduling - new Date().getTime());
long endMs = startMs + TimeUnit.MINUTES.toMillis(5);
SQLiteDatabase db = Sqlite.getInstance(context, Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
int jobId = new JobRequest.Builder(ScheduledTootsSyncJob.SCHEDULED_TOOT)
.setExecutionWindow(startMs, endMs)
.setPersisted(true)
.setUpdateCurrent(updateCurrent)
.setRequiredNetworkType(JobRequest.NetworkType.CONNECTED)
.setRequirementsEnforced(false)
.build()
.schedule();
new StatusStoredDAO(context, db).scheduleStatus(id, jobId, new Date());
return jobId;
}
}

View File

@ -51,7 +51,7 @@ public class StatusStoredDAO {
* @param status Status
* @return boolean
*/
public long insertStatus(Status status, boolean isScheduled, Date scheduled_date)
public long insertStatus(Status status)
{
ContentValues values = new ContentValues();
String serializedStatus = Helper.statusToStringStorage(status);
@ -63,13 +63,11 @@ public class StatusStoredDAO {
return -1;
values.put(Sqlite.COL_STATUS_SERIALIZED, serializedStatus);
values.put(Sqlite.COL_DATE_CREATION, Helper.dateToString(context, new Date()));
values.put(Sqlite.COL_IS_SCHEDULED, isScheduled?1:0);
values.put(Sqlite.COL_IS_SCHEDULED, 0);
values.put(Sqlite.COL_INSTANCE, instance);
values.put(Sqlite.COL_USER_ID, userId);
values.put(Sqlite.COL_SENT, 0);
if( isScheduled && scheduled_date != null)
values.put(Sqlite.COL_DATE_SCHEDULED, Helper.dateToString(context, scheduled_date));
//Inserts stored status
long last_id;
try{
@ -98,31 +96,49 @@ public class StatusStoredDAO {
new String[]{String.valueOf(id)});
}
/**
* Update scheduled date for a Status in database
* @param scheduled_date Date
* Schedule a status in db
* @param id long
* @param jobId int
* @param date_scheduled Date
* @return boolean
*/
public int updateScheduledDate(long id, Date scheduled_date) {
public int scheduleStatus(long id, int jobId, Date date_scheduled ) {
ContentValues values = new ContentValues();
values.put(Sqlite.COL_DATE_SCHEDULED, Helper.dateToString(context, scheduled_date));
values.put(Sqlite.COL_IS_SCHEDULED, jobId);
values.put(Sqlite.COL_DATE_SCHEDULED, Helper.dateToString(context, date_scheduled));
return db.update(Sqlite.TABLE_STATUSES_STORED,
values, Sqlite.COL_ID + " = ? ",
new String[]{String.valueOf(id)});
}
/**
* Update scheduled date for a Status in database
* @param scheduled_date Date
* @return boolean
*/
public int updateScheduledDate(int jobid, Date scheduled_date) {
ContentValues values = new ContentValues();
values.put(Sqlite.COL_DATE_SCHEDULED, Helper.dateToString(context, scheduled_date));
return db.update(Sqlite.TABLE_STATUSES_STORED,
values, Sqlite.COL_IS_SCHEDULED + " = ? ",
new String[]{String.valueOf(jobid)});
}
/**
* Update date when task is done for a scheduled Status in database
* @param jobid int
* @param date_sent Date
* @return boolean
*/
public int updateScheduledDone(long id, Date date_sent) {
public int updateScheduledDone(int jobid, Date date_sent) {
ContentValues values = new ContentValues();
values.put(Sqlite.COL_DATE_SENT, Helper.dateToString(context, date_sent));
values.put(Sqlite.COL_SENT, 1);
return db.update(Sqlite.TABLE_STATUSES_STORED,
values, Sqlite.COL_ID + " = ? ",
new String[]{String.valueOf(id)});
values, Sqlite.COL_IS_SCHEDULED + " = ? ",
new String[]{String.valueOf(jobid)});
}
//------- REMOVE -------
@ -176,6 +192,22 @@ public class StatusStoredDAO {
}
}
/**
* Returns all scheduled Statuses in db
* @return stored status List<StoredStatus>
*/
public List<StoredStatus> getAllScheduled(){
try {
SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
String instance = Helper.getLiveInstance(context);
Cursor c = db.query(Sqlite.TABLE_STATUSES_STORED, null, Sqlite.COL_USER_ID + " = '" + userId+ "' AND " + Sqlite.COL_INSTANCE + " = '" + instance+ "' AND " + Sqlite.COL_IS_SCHEDULED + " > 0 AND " + Sqlite.COL_SENT + " = 0", null, null, null, Sqlite.COL_DATE_CREATION + " DESC", null);
return cursorToListStatuses(c);
} catch (Exception e) {
return null;
}
}
/**
* Returns all not sent Statuses in db
* @return stored status List<StoredStatus>
@ -222,6 +254,18 @@ public class StatusStoredDAO {
}
/**
* Returns a stored status by id of job in db
* @return stored status StoredStatus
*/
public StoredStatus getStatusScheduled(int jobid){
try {
Cursor c = db.query(Sqlite.TABLE_STATUSES_STORED, null, Sqlite.COL_IS_SCHEDULED + " = '" + jobid + "'", null, null, null, null, null);
return cursorToStoredStatus(c);
} catch (Exception e) {
return null;
}
}
/***
* Method to hydrate Stored statuses from database
@ -240,7 +284,7 @@ public class StatusStoredDAO {
Status status = Helper.restoreStatusFromString(c.getString(c.getColumnIndex(Sqlite.COL_STATUS_SERIALIZED)));
storedStatus.setStatus(status);
storedStatus.setSent(c.getInt(c.getColumnIndex(Sqlite.COL_SENT)) == 1);
storedStatus.setScheduled(c.getInt(c.getColumnIndex(Sqlite.COL_IS_SCHEDULED)) == 1);
storedStatus.setScheduled(c.getInt(c.getColumnIndex(Sqlite.COL_IS_SCHEDULED)) > 0 );
storedStatus.setCreation_date(Helper.stringToDate(context, c.getString(c.getColumnIndex(Sqlite.COL_DATE_CREATION))));
storedStatus.setScheduled_date(Helper.stringToDate(context, c.getString(c.getColumnIndex(Sqlite.COL_DATE_SCHEDULED))));
storedStatus.setSent_date(Helper.stringToDate(context, c.getString(c.getColumnIndex(Sqlite.COL_DATE_SENT))));
@ -267,7 +311,7 @@ public class StatusStoredDAO {
Status status = Helper.restoreStatusFromString(c.getString(c.getColumnIndex(Sqlite.COL_STATUS_SERIALIZED)));
storedStatus.setStatus(status);
storedStatus.setSent(c.getInt(c.getColumnIndex(Sqlite.COL_SENT)) == 1);
storedStatus.setScheduled(c.getInt(c.getColumnIndex(Sqlite.COL_IS_SCHEDULED)) == 1);
storedStatus.setScheduled(c.getInt(c.getColumnIndex(Sqlite.COL_IS_SCHEDULED)) > 0 );
storedStatus.setCreation_date(Helper.stringToDate(context, c.getString(c.getColumnIndex(Sqlite.COL_DATE_CREATION))));
storedStatus.setScheduled_date(Helper.stringToDate(context, c.getString(c.getColumnIndex(Sqlite.COL_DATE_SCHEDULED))));
storedStatus.setSent_date(Helper.stringToDate(context, c.getString(c.getColumnIndex(Sqlite.COL_DATE_SENT))));

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 578 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 851 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2017 Thomas Schneider
This file is a part of Mastalab
This program is free software; you can redistribute it and/or modify it under the terms of the
GNU General Public License as published by the Free Software Foundation; either version 3 of the
License, or (at your option) any later version.
Mastalab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
Public License for more details.
You should have received a copy of the GNU General Public License along with Thomas Schneider; if not,
see <http://www.gnu.org/licenses>.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<DatePicker
android:id="@+id/date_picker"
android:layout_width="match_parent"
android:calendarViewShown="true"
android:gravity="center"
android:spinnersShown="false"
android:layout_height="wrap_content" />
<TimePicker
android:visibility="gone"
android:gravity="center"
android:id="@+id/time_picker"
android:layout_weight="4"
android:layout_width="match_parent"
android:layout_height="0dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/date_time_cancel"
android:layout_weight="1"
android:layout_width="0dp"
android:text="@string/cancel"
android:layout_height="wrap_content"
tools:ignore="ButtonStyle" />
<Button
android:visibility="gone"
android:id="@+id/date_time_previous"
android:layout_weight="1"
android:layout_width="0dp"
android:text="@string/previous"
android:layout_height="wrap_content"
tools:ignore="ButtonStyle" />
<Button
android:id="@+id/date_time_next"
android:layout_weight="1"
android:layout_width="0dp"
android:text="@string/next"
android:layout_height="wrap_content"
tools:ignore="ButtonStyle" />
<Button
android:visibility="gone"
android:id="@+id/date_time_set"
android:layout_weight="1"
android:layout_width="0dp"
android:text="@string/save"
android:layout_height="wrap_content"
tools:ignore="ButtonStyle" />
</LinearLayout>
</LinearLayout>

View File

@ -56,5 +56,4 @@
android:layout_height="40dp"
android:padding="10dp"
tools:ignore="ContentDescription" />
</LinearLayout>

View File

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2017 Thomas Schneider
This file is a part of Mastalab
This program is free software; you can redistribute it and/or modify it under the terms of the
GNU General Public License as published by the Free Software Foundation; either version 3 of the
License, or (at your option) any later version.
Mastalab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
Public License for more details.
You should have received a copy of the GNU General Public License along with Thomas Schneider; if not,
see <http://www.gnu.org/licenses>.
-->
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:card_view="http://schemas.android.com/tools"
android:layout_marginTop="10dp"
style="?attr/cardStyle"
card_view:cardPreventCornerOverlap="true"
app:cardUseCompatPadding="true">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:padding="5dp"
android:id="@+id/account_container"
android:orientation="vertical">
<TextView
android:id="@+id/scheduled_toot_title"
android:textStyle="bold"
android:maxLines="3"
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/scheduled_toot_privacy"
android:padding="10dp"
android:layout_width="25dp"
android:layout_height="25dp"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/scheduled_toot_media_count"
android:layout_width="150dp"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/scheduled_toot_date_creation"
android:textSize="12sp"
android:layout_marginTop="5dp"
android:textStyle="italic"
android:layout_marginEnd="10dp"
android:layout_marginRight="10dp"
android:gravity="end"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
<Button
android:layout_marginStart="10dp"
android:layout_marginLeft="10dp"
android:focusableInTouchMode="false"
android:id="@+id/scheduled_toot_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ImageView
android:id="@+id/scheduled_toot_delete"
android:src="@drawable/ic_cancel"
android:layout_gravity="center"
android:gravity="center"
android:layout_width="40dp"
android:layout_height="40dp"
android:padding="10dp"
tools:ignore="ContentDescription" />
</LinearLayout>
</android.support.v7.widget.CardView>

View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2017 Thomas Schneider
This file is a part of Mastalab
This program is free software; you can redistribute it and/or modify it under the terms of the
GNU General Public License as published by the Free Software Foundation; either version 3 of the
License, or (at your option) any later version.
Mastalab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
Public License for more details.
You should have received a copy of the GNU General Public License along with Thomas Schneider; if not,
see <http://www.gnu.org/licenses>.
-->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/fab_margin"
android:paddingRight="@dimen/fab_margin"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Listview Scheduled toots -->
<ListView
android:id="@+id/lv_scheduled_toots"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none"
android:divider="@null"
>
</ListView>
<RelativeLayout
android:id="@+id/no_action"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/no_action_text"
android:padding="10dp"
android:layout_centerInParent="true"
android:gravity="center"
android:textSize="25sp"
android:layout_gravity="center"
android:textStyle="italic|bold"
android:typeface="serif"
android:text="@string/no_scheduled_toots"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:padding="10dp"
android:layout_below="@+id/no_action_text"
android:layout_marginTop="10dp"
android:gravity="center"
android:textStyle="italic"
android:layout_gravity="center"
android:typeface="serif"
android:text="@string/no_scheduled_toots_indications"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
<!-- Main Loader -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/loader"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
>
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true" />
</RelativeLayout>
</RelativeLayout>

View File

@ -30,6 +30,10 @@
android:id="@+id/nav_notification"
android:icon="@drawable/ic_notifications"
android:title="@string/notifications" />
<item
android:id="@+id/nav_scheduled"
android:icon="@drawable/ic_schedule_black"
android:title="@string/scheduled_toots" />
<item
android:id="@+id/nav_follow_request"
android:icon="@drawable/ic_group_add"

View File

@ -16,9 +16,9 @@
android:title="@string/restore"
android:icon="@drawable/ic_restore"
app:showAsAction="never" />
<!--<item
<item
android:id="@+id/action_schedule"
android:title="@string/schedule"
android:icon="@drawable/ic_schedule"
app:showAsAction="never" />-->
app:showAsAction="never" />
</menu>

View File

@ -44,6 +44,8 @@
<string name="speech_not_supported">Désolé ! Votre appareil ne supporte pas la commande vocale !</string>
<string name="delete_all">Tout effacer</string>
<string name="schedule">Programmer</string>
<string name="next">Suivant</string>
<string name="previous">Précédent</string>
<!--- Menu -->
<string name="home_menu">Accueil</string>
<string name="local_menu">Fil public local</string>
@ -65,6 +67,8 @@
<string name="choose_file">Veuillez sélectionner un fichier</string>
<string name="choose_file_error">Aucun explorateur de fichiers trouvé !</string>
<string name="click_to_change">Cliquer sur le chemin pour changer</string>
<string name="scheduled_toots">Pouets programmés</string>
<!-- Status -->
<string name="no_status">Aucun pouet à afficher !</string>
<string name="fav_added">Pouet ajouté aux favoris !</string>
@ -179,6 +183,14 @@
<string name="authorize">Autoriser</string>
<string name="reject">Rejeter</string>
<!-- Scheduled toots -->
<string name="no_scheduled_toots">Aucun pouet programmé à afficher !</string>
<string name="no_scheduled_toots_indications">Ecrivez un toot, puis choisissez <b>Programmer</b> dans le menu du haut.</string>
<string name="remove_scheduled">Supprimer le pouet programmé ?</string>
<string name="media_count">Média(s): %d</string>
<string name="toot_scheduled">Le pouet a été programmé !</string>
<string name="toot_scheduled_date">La date doit être supérieure à l\'heure actuelle !</string>
<!-- Notifications -->
<string name="no_notifications">Aucune notification à afficher</string>
<string name="notif_mention">a mentionné votre pouet</string>

View File

@ -46,6 +46,9 @@
<string name="delete_all">Delete all</string>
<string name="schedule">Schedule</string>
<string name="next">Next</string>
<string name="previous">Previous</string>
<!--- Menu -->
<string name="home_menu">Home</string>
<string name="local_menu">Local timeline</string>
@ -67,6 +70,8 @@
<string name="choose_file">Please select a file</string>
<string name="choose_file_error">No file explorer found!</string>
<string name="click_to_change">Click on the path to change it</string>
<string name="scheduled_toots">Scheduled toots</string>
<!-- Status -->
<string name="no_status">No toot to display</string>
<string name="fav_added">The toot was added to favourites</string>
@ -183,6 +188,15 @@
<string name="authorize">Authorize</string>
<string name="reject">Reject</string>
<!-- Scheduled toots -->
<string name="no_scheduled_toots">No scheduled toot to display!</string>
<string name="no_scheduled_toots_indications">Write a toot and then choose <b>Schedule</b> from the top menu.</string>
<string name="remove_scheduled">Delete scheduled toot?</string>
<string name="media_count">Media: %d</string>
<string name="toot_scheduled">The toot has been scheduled!</string>
<string name="toot_scheduled_date">The scheduled date must be greater than the current hour!</string>
<!-- Notifications -->
<string name="no_notifications">No notification to display</string>
<string name="notif_mention">mentioned your status</string>