diff --git a/app/src/main/java/com/todoroo/astrid/activity/TaskListFragment.java b/app/src/main/java/com/todoroo/astrid/activity/TaskListFragment.java index 255992ded..00f8722ba 100644 --- a/app/src/main/java/com/todoroo/astrid/activity/TaskListFragment.java +++ b/app/src/main/java/com/todoroo/astrid/activity/TaskListFragment.java @@ -525,9 +525,10 @@ public class TaskListFragment extends InjectingFragment } } else if (requestCode == REQUEST_MOVE_TASKS) { if (resultCode == RESULT_OK) { + tracker.reportEvent(Tracking.Events.MULTISELECT_MOVE); taskMover.move( taskAdapter.getSelected(), - data.getParcelableExtra(RemoteListSupportPicker.EXTRA_SELECTED)); + data.getParcelableExtra(RemoteListSupportPicker.EXTRA_SELECTED_FILTER)); recyclerAdapter.finishActionMode(); } } else { diff --git a/app/src/main/java/com/todoroo/astrid/adapter/FilterAdapter.java b/app/src/main/java/com/todoroo/astrid/adapter/FilterAdapter.java index b89036e1c..fd34096c4 100644 --- a/app/src/main/java/com/todoroo/astrid/adapter/FilterAdapter.java +++ b/app/src/main/java/com/todoroo/astrid/adapter/FilterAdapter.java @@ -183,8 +183,9 @@ public class FilterAdapter extends ArrayAdapter { this.selected = selected; } - public int indexOf(FilterListItem item) { - return items.indexOf(item); + public int indexOf(FilterListItem item, int defaultValue) { + int index = items.indexOf(item); + return index == -1 ? defaultValue : index; } @NonNull diff --git a/app/src/main/java/com/todoroo/astrid/service/TaskMover.java b/app/src/main/java/com/todoroo/astrid/service/TaskMover.java index 77d3e985b..11661db6f 100644 --- a/app/src/main/java/com/todoroo/astrid/service/TaskMover.java +++ b/app/src/main/java/com/todoroo/astrid/service/TaskMover.java @@ -15,6 +15,7 @@ import org.tasks.data.CaldavDao; import org.tasks.data.CaldavTask; import org.tasks.data.GoogleTask; import org.tasks.data.GoogleTaskDao; +import org.tasks.data.GoogleTaskListDao; import org.tasks.sync.SyncAdapters; public class TaskMover { @@ -22,13 +23,20 @@ public class TaskMover { private final CaldavDao caldavDao; private final GoogleTaskDao googleTaskDao; private final SyncAdapters syncAdapters; + private final GoogleTaskListDao googleTaskListDao; @Inject - public TaskMover(TaskDao taskDao, CaldavDao caldavDao, GoogleTaskDao googleTaskDao, SyncAdapters syncAdapters) { + public TaskMover( + TaskDao taskDao, + CaldavDao caldavDao, + GoogleTaskDao googleTaskDao, + SyncAdapters syncAdapters, + GoogleTaskListDao googleTaskListDao) { this.taskDao = taskDao; this.caldavDao = caldavDao; this.googleTaskDao = googleTaskDao; this.syncAdapters = syncAdapters; + this.googleTaskListDao = googleTaskListDao; } public void move(List tasks, Filter selectedList) { @@ -46,6 +54,21 @@ public class TaskMover { performMove(task, selectedList); } + public Filter getSingleFilter(List tasks) { + List caldavCalendars = caldavDao.getCalendars(tasks); + List googleTaskLists = googleTaskDao.getLists(tasks); + if (caldavCalendars.isEmpty()) { + if (googleTaskLists.size() == 1) { + return new GtasksFilter(googleTaskListDao.getByRemoteId(googleTaskLists.get(0))); + } + } else if (googleTaskLists.isEmpty()) { + if (caldavCalendars.size() == 1) { + return new CaldavFilter(caldavDao.getCalendar(caldavCalendars.get(0))); + } + } + return null; + } + private void performMove(Task task, Filter selectedList) { long id = task.getId(); GoogleTask googleTask = googleTaskDao.getByTaskId(id); diff --git a/app/src/main/java/org/tasks/activities/FilterSelectionActivity.java b/app/src/main/java/org/tasks/activities/FilterSelectionActivity.java index df3d73193..5a99722e9 100644 --- a/app/src/main/java/org/tasks/activities/FilterSelectionActivity.java +++ b/app/src/main/java/org/tasks/activities/FilterSelectionActivity.java @@ -35,7 +35,7 @@ public class FilterSelectionActivity extends InjectingAppCompatActivity { .newDialog() .setSingleChoiceItems( filterAdapter, - filterAdapter.indexOf(selected), + filterAdapter.indexOf(selected, -1), (dialog, which) -> { final Filter selectedFilter = (Filter) filterAdapter.getItem(which); Intent data = new Intent(); diff --git a/app/src/main/java/org/tasks/activities/RemoteListNativePicker.java b/app/src/main/java/org/tasks/activities/RemoteListNativePicker.java index 646ceddc9..135c43abb 100644 --- a/app/src/main/java/org/tasks/activities/RemoteListNativePicker.java +++ b/app/src/main/java/org/tasks/activities/RemoteListNativePicker.java @@ -32,8 +32,8 @@ public class RemoteListNativePicker extends InjectingNativeDialogFragment { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { - Bundle arguments = getArguments(); - Filter selected = arguments.getParcelable(EXTRA_SELECTED); + filterAdapter.populateRemoteListPicker(); + int selected = filterAdapter.indexOf(getArguments().getParcelable(EXTRA_SELECTED), 0); return createDialog(filterAdapter, dialogBuilder, selected, list -> handler.selectedList(list)); } diff --git a/app/src/main/java/org/tasks/activities/RemoteListSupportPicker.java b/app/src/main/java/org/tasks/activities/RemoteListSupportPicker.java index ec4fcaa4e..1296cebe3 100644 --- a/app/src/main/java/org/tasks/activities/RemoteListSupportPicker.java +++ b/app/src/main/java/org/tasks/activities/RemoteListSupportPicker.java @@ -20,7 +20,8 @@ import org.tasks.injection.InjectingDialogFragment; public class RemoteListSupportPicker extends InjectingDialogFragment { - public static final String EXTRA_SELECTED = "extra_selected"; + public static final String EXTRA_SELECTED_FILTER = "extra_selected_filter"; + private static final String EXTRA_NO_SELECTION = "extra_no_selection"; @Inject DialogBuilder dialogBuilder; @Inject FilterAdapter filterAdapter; @@ -29,9 +30,17 @@ public class RemoteListSupportPicker extends InjectingDialogFragment { Filter selected, Fragment targetFragment, int requestCode) { RemoteListSupportPicker dialog = new RemoteListSupportPicker(); Bundle arguments = new Bundle(); - if (selected != null) { - arguments.putParcelable(EXTRA_SELECTED, selected); - } + arguments.putParcelable(EXTRA_SELECTED_FILTER, selected); + dialog.setArguments(arguments); + dialog.setTargetFragment(targetFragment, requestCode); + return dialog; + } + + public static RemoteListSupportPicker newRemoteListSupportPicker( + Fragment targetFragment, int requestCode) { + RemoteListSupportPicker dialog = new RemoteListSupportPicker(); + Bundle arguments = new Bundle(); + arguments.putBoolean(EXTRA_NO_SELECTION, true); dialog.setArguments(arguments); dialog.setTargetFragment(targetFragment, requestCode); return dialog; @@ -40,10 +49,8 @@ public class RemoteListSupportPicker extends InjectingDialogFragment { public static AlertDialog createDialog( FilterAdapter filterAdapter, DialogBuilder dialogBuilder, - Filter selected, + int selectedIndex, RemoteListSelectionHandler handler) { - filterAdapter.populateRemoteListPicker(); - int selectedIndex = selected == null ? 0 : filterAdapter.indexOf(selected); return dialogBuilder .newDialog() .setSingleChoiceItems( @@ -66,8 +73,11 @@ public class RemoteListSupportPicker extends InjectingDialogFragment { @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { + filterAdapter.populateRemoteListPicker(); Bundle arguments = getArguments(); - Filter selected = arguments == null ? null : arguments.getParcelable(EXTRA_SELECTED); + int selected = + arguments.getBoolean(EXTRA_NO_SELECTION, false) + ? -1 : filterAdapter.indexOf(arguments.getParcelable(EXTRA_SELECTED_FILTER), 0); return createDialog(filterAdapter, dialogBuilder, selected, this::selected); } @@ -76,7 +86,7 @@ public class RemoteListSupportPicker extends InjectingDialogFragment { .onActivityResult( getTargetRequestCode(), Activity.RESULT_OK, - new Intent().putExtra(EXTRA_SELECTED, filter)); + new Intent().putExtra(EXTRA_SELECTED_FILTER, filter)); } @Override diff --git a/app/src/main/java/org/tasks/analytics/Tracking.java b/app/src/main/java/org/tasks/analytics/Tracking.java index 1371d3bfb..dd0bd57f2 100644 --- a/app/src/main/java/org/tasks/analytics/Tracking.java +++ b/app/src/main/java/org/tasks/analytics/Tracking.java @@ -28,6 +28,7 @@ public class Tracking { MULTISELECT_DELETE( R.string.tracking_category_event, R.string.tracking_event_multiselect_delete), MULTISELECT_CLONE(R.string.tracking_category_event, R.string.tracking_event_multiselect_clone), + MULTISELECT_MOVE(R.string.tracking_category_event, R.string.tracking_event_multiselect_clone), CLEAR_COMPLETED(R.string.tracking_category_event, R.string.tracking_action_clear_completed), UPGRADE(R.string.tracking_category_event, R.string.tracking_event_upgrade), DB_OPEN_FAILED(R.string.tracking_category_error, R.string.tracking_event_db_open), diff --git a/app/src/main/java/org/tasks/data/CaldavDao.java b/app/src/main/java/org/tasks/data/CaldavDao.java index db5435b15..c49b9ddb5 100644 --- a/app/src/main/java/org/tasks/data/CaldavDao.java +++ b/app/src/main/java/org/tasks/data/CaldavDao.java @@ -100,4 +100,7 @@ public interface CaldavDao { @Query("SELECT * FROM caldav_account WHERE name = :name COLLATE NOCASE LIMIT 1") CaldavAccount getAccountByName(String name); + + @Query("SELECT DISTINCT calendar FROM caldav_tasks WHERE deleted = 0 AND task IN (:tasks)") + List getCalendars(List tasks); } diff --git a/app/src/main/java/org/tasks/data/GoogleTaskDao.java b/app/src/main/java/org/tasks/data/GoogleTaskDao.java index 05a21b1cf..b93bf506f 100644 --- a/app/src/main/java/org/tasks/data/GoogleTaskDao.java +++ b/app/src/main/java/org/tasks/data/GoogleTaskDao.java @@ -51,4 +51,7 @@ public interface GoogleTaskDao { @Query("DELETE FROM google_tasks") void deleteAll(); + + @Query("SELECT DISTINCT list_id FROM google_tasks WHERE deleted = 0 AND task IN (:tasks)") + List getLists(List tasks); } diff --git a/app/src/main/java/org/tasks/tasklist/ActionModeProvider.java b/app/src/main/java/org/tasks/tasklist/ActionModeProvider.java index eb5cbcaed..4a890571d 100644 --- a/app/src/main/java/org/tasks/tasklist/ActionModeProvider.java +++ b/app/src/main/java/org/tasks/tasklist/ActionModeProvider.java @@ -1,5 +1,6 @@ package org.tasks.tasklist; +import static com.todoroo.astrid.activity.TaskListFragment.REQUEST_MOVE_TASKS; import static org.tasks.activities.RemoteListSupportPicker.newRemoteListSupportPicker; import android.content.Context; @@ -10,9 +11,11 @@ import android.view.MenuItem; import com.todoroo.astrid.activity.TaskListActivity; import com.todoroo.astrid.activity.TaskListFragment; import com.todoroo.astrid.adapter.TaskAdapter; +import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.data.Task; import com.todoroo.astrid.service.TaskDeleter; import com.todoroo.astrid.service.TaskDuplicator; +import com.todoroo.astrid.service.TaskMover; import java.util.List; import javax.inject.Inject; import org.tasks.R; @@ -20,6 +23,7 @@ import org.tasks.analytics.Tracker; import org.tasks.analytics.Tracking; import org.tasks.dialogs.DialogBuilder; import org.tasks.injection.ForActivity; +import org.tasks.sync.SyncAdapters; import org.tasks.ui.MenuColorizer; public class ActionModeProvider { @@ -30,7 +34,9 @@ public class ActionModeProvider { private final DialogBuilder dialogBuilder; private final TaskDeleter taskDeleter; private final TaskDuplicator taskDuplicator; + private final TaskMover taskMover; private final Tracker tracker; + private final SyncAdapters syncAdapters; @Inject public ActionModeProvider( @@ -38,12 +44,17 @@ public class ActionModeProvider { DialogBuilder dialogBuilder, TaskDeleter taskDeleter, TaskDuplicator taskDuplicator, - Tracker tracker) { + TaskMover taskMover, + Tracker tracker, + SyncAdapters syncAdapters + ) { this.context = context; this.dialogBuilder = dialogBuilder; this.taskDeleter = taskDeleter; this.taskDuplicator = taskDuplicator; + this.taskMover = taskMover; this.tracker = tracker; + this.syncAdapters = syncAdapters; } public ActionMode startActionMode( @@ -57,6 +68,9 @@ public class ActionModeProvider { public boolean onCreateActionMode(ActionMode actionMode, Menu menu) { MenuInflater inflater = actionMode.getMenuInflater(); inflater.inflate(R.menu.menu_multi_select, menu); + if (!syncAdapters.isSyncEnabled()) { + menu.findItem(R.id.move_tasks).setVisible(false); + } MenuColorizer.colorMenu(context, menu); return true; } @@ -70,7 +84,10 @@ public class ActionModeProvider { public boolean onActionItemClicked(ActionMode mode, MenuItem item) { switch (item.getItemId()) { case R.id.move_tasks: - newRemoteListSupportPicker(null, taskList, TaskListFragment.REQUEST_MOVE_TASKS) + Filter singleFilter = taskMover.getSingleFilter(adapter.getSelected()); + (singleFilter == null + ? newRemoteListSupportPicker(taskList, REQUEST_MOVE_TASKS) + : newRemoteListSupportPicker(singleFilter, taskList, REQUEST_MOVE_TASKS)) .show(taskList.getFragmentManager(), FRAG_TAG_REMOTE_LIST_PICKER); return true; case R.id.delete: diff --git a/app/src/main/java/org/tasks/ui/RemoteListFragment.java b/app/src/main/java/org/tasks/ui/RemoteListFragment.java index e13032bcf..f6fee6667 100644 --- a/app/src/main/java/org/tasks/ui/RemoteListFragment.java +++ b/app/src/main/java/org/tasks/ui/RemoteListFragment.java @@ -141,7 +141,7 @@ public class RemoteListFragment extends TaskEditControlFragment { public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_CODE_SELECT_LIST) { if (resultCode == RESULT_OK) { - setList(data.getParcelableExtra(RemoteListSupportPicker.EXTRA_SELECTED)); + setList(data.getParcelableExtra(RemoteListSupportPicker.EXTRA_SELECTED_FILTER)); } } else { super.onActivityResult(requestCode, resultCode, data); diff --git a/app/src/main/res/values/keys.xml b/app/src/main/res/values/keys.xml index 91f029eea..11d229c16 100644 --- a/app/src/main/res/values/keys.xml +++ b/app/src/main/res/values/keys.xml @@ -256,6 +256,7 @@ Task creation Multiselect delete Multiselect clone + Multiselect move badges_enabled badge_list Tasker/Locale