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 72d3bc4e3..5f1c44c97 100644 --- a/app/src/main/java/com/todoroo/astrid/activity/TaskListFragment.java +++ b/app/src/main/java/com/todoroo/astrid/activity/TaskListFragment.java @@ -384,7 +384,7 @@ public final class TaskListFragment extends InjectingFragment filter.supportsSubtasks() || BuiltInFilterExposer.isInbox(context, filter) || BuiltInFilterExposer.isTodayFilter(context, filter); - SortDialog.newSortDialog(supportsManualSort) + SortDialog.newSortDialog(supportsManualSort, filter instanceof GtasksFilter) .show(getChildFragmentManager(), FRAG_TAG_SORT_DIALOG); return true; case R.id.menu_show_hidden: diff --git a/app/src/main/java/com/todoroo/astrid/adapter/GoogleTaskAdapter.java b/app/src/main/java/com/todoroo/astrid/adapter/GoogleTaskAdapter.java index 5489cacc3..f2d23677e 100644 --- a/app/src/main/java/com/todoroo/astrid/adapter/GoogleTaskAdapter.java +++ b/app/src/main/java/com/todoroo/astrid/adapter/GoogleTaskAdapter.java @@ -9,65 +9,14 @@ import org.tasks.BuildConfig; import org.tasks.data.GoogleTask; import org.tasks.data.GoogleTaskDao; import org.tasks.data.TaskContainer; -import org.tasks.tasklist.ViewHolder; -public final class GoogleTaskAdapter extends TaskAdapter { +public final class GoogleTaskAdapter extends GoogleTaskManualSortAdapter { - private final TaskDao taskDao; - private final GoogleTaskDao googleTaskDao; + private final boolean newTasksOnTop; - GoogleTaskAdapter(TaskDao taskDao, GoogleTaskDao googleTaskDao) { - this.taskDao = taskDao; - this.googleTaskDao = googleTaskDao; - } - - @Override - public int getIndent(TaskContainer task) { - return task.getIndent(); - } - - @Override - public boolean canMove(ViewHolder sourceVh, ViewHolder targetVh) { - TaskContainer source = sourceVh.task; - int to = targetVh.getAdapterPosition(); - - if (!source.hasChildren() || to <= 0 || to >= getCount() - 1) { - return true; - } - - TaskContainer target = targetVh.task; - if (sourceVh.getAdapterPosition() < to) { - if (target.hasChildren()) { - return false; - } - if (target.hasParent()) { - return target.isLastSubtask(); - } - return true; - } else { - if (target.hasChildren()) { - return true; - } - if (target.hasParent()) { - return target.getParent() == source.getId() && target.secondarySort == 0; - } - return true; - } - } - - @Override - public int maxIndent(int previousPosition, TaskContainer task) { - return task.hasChildren() ? 0 : 1; - } - - @Override - public int minIndent(int nextPosition, TaskContainer task) { - return task.hasChildren() || !getTask(nextPosition).hasParent() ? 0 : 1; - } - - @Override - public boolean supportsParentingOrManualSort() { - return true; + GoogleTaskAdapter(TaskDao taskDao, GoogleTaskDao googleTaskDao, boolean newTasksOnTop) { + super(taskDao, googleTaskDao); + this.newTasksOnTop = newTasksOnTop; } @Override @@ -75,33 +24,21 @@ public final class GoogleTaskAdapter extends TaskAdapter { TaskContainer task = getTask(from); GoogleTask googleTask = task.getGoogleTask(); TaskContainer previous = to > 0 ? getTask(to - 1) : null; - - if (previous == null) { - googleTaskDao.move(googleTask, 0, 0); - } else if (to == getCount() || to <= from) { - if (indent == 0) { - googleTaskDao.move(googleTask, 0, previous.getPrimarySort() + 1); - } else if (previous.hasParent()) { - googleTaskDao.move(googleTask, previous.getParent(), previous.getSecondarySort() + 1); - } else { - googleTaskDao.move(googleTask, previous.getId(), 0); + if (indent == 0) { + if (googleTask.getIndent() == 0) { + return; } + googleTaskDao.move( + googleTask, 0, newTasksOnTop ? 0 : googleTaskDao.getBottom(googleTask.getListId(), 0)); } else { - if (indent == 0) { - googleTaskDao.move( - googleTask, - 0, - task.hasParent() ? previous.getPrimarySort() + 1 : previous.getPrimarySort()); - } else if (previous.hasParent()) { - googleTaskDao.move( - googleTask, - previous.getParent(), - task.getParent() == previous.getParent() - ? previous.getSecondarySort() - : previous.getSecondarySort() + 1); - } else { - googleTaskDao.move(googleTask, previous.getId(), 0); + long newParent = previous.hasParent() ? previous.getParent() : previous.getId(); + if (googleTask.getParent() == newParent) { + return; } + googleTaskDao.move( + googleTask, + newParent, + newTasksOnTop ? 0 : googleTaskDao.getBottom(googleTask.getListId(), newParent)); } Task update = task.getTask(); diff --git a/app/src/main/java/com/todoroo/astrid/adapter/GoogleTaskManualSortAdapter.java b/app/src/main/java/com/todoroo/astrid/adapter/GoogleTaskManualSortAdapter.java new file mode 100644 index 000000000..5fa8c4074 --- /dev/null +++ b/app/src/main/java/com/todoroo/astrid/adapter/GoogleTaskManualSortAdapter.java @@ -0,0 +1,116 @@ +package com.todoroo.astrid.adapter; + +import static com.todoroo.andlib.utility.DateUtilities.now; + +import com.todoroo.astrid.dao.TaskDao; +import com.todoroo.astrid.data.SyncFlags; +import com.todoroo.astrid.data.Task; +import org.tasks.BuildConfig; +import org.tasks.data.GoogleTask; +import org.tasks.data.GoogleTaskDao; +import org.tasks.data.TaskContainer; +import org.tasks.tasklist.ViewHolder; + +public class GoogleTaskManualSortAdapter extends TaskAdapter { + + protected final TaskDao taskDao; + protected final GoogleTaskDao googleTaskDao; + + GoogleTaskManualSortAdapter(TaskDao taskDao, GoogleTaskDao googleTaskDao) { + this.taskDao = taskDao; + this.googleTaskDao = googleTaskDao; + } + + @Override + public int getIndent(TaskContainer task) { + return task.getIndent(); + } + + @Override + public boolean canMove(ViewHolder sourceVh, ViewHolder targetVh) { + TaskContainer source = sourceVh.task; + int to = targetVh.getAdapterPosition(); + + if (!source.hasChildren() || to <= 0 || to >= getCount() - 1) { + return true; + } + + TaskContainer target = targetVh.task; + if (sourceVh.getAdapterPosition() < to) { + if (target.hasChildren()) { + return false; + } + if (target.hasParent()) { + return target.isLastSubtask(); + } + return true; + } else { + if (target.hasChildren()) { + return true; + } + if (target.hasParent()) { + return target.getParent() == source.getId() && target.secondarySort == 0; + } + return true; + } + } + + @Override + public int maxIndent(int previousPosition, TaskContainer task) { + return task.hasChildren() ? 0 : 1; + } + + @Override + public int minIndent(int nextPosition, TaskContainer task) { + return task.hasChildren() || !getTask(nextPosition).hasParent() ? 0 : 1; + } + + @Override + public boolean supportsParentingOrManualSort() { + return true; + } + + @Override + public void moved(int from, int to, int indent) { + TaskContainer task = getTask(from); + GoogleTask googleTask = task.getGoogleTask(); + TaskContainer previous = to > 0 ? getTask(to - 1) : null; + + if (previous == null) { + googleTaskDao.move(googleTask, 0, 0); + } else if (to == getCount() || to <= from) { + if (indent == 0) { + googleTaskDao.move(googleTask, 0, previous.getPrimarySort() + 1); + } else if (previous.hasParent()) { + googleTaskDao.move(googleTask, previous.getParent(), previous.getSecondarySort() + 1); + } else { + googleTaskDao.move(googleTask, previous.getId(), 0); + } + } else { + if (indent == 0) { + googleTaskDao.move( + googleTask, + 0, + task.hasParent() ? previous.getPrimarySort() + 1 : previous.getPrimarySort()); + } else if (previous.hasParent()) { + googleTaskDao.move( + googleTask, + previous.getParent(), + task.getParent() == previous.getParent() + ? previous.getSecondarySort() + : previous.getSecondarySort() + 1); + } else { + googleTaskDao.move(googleTask, previous.getId(), 0); + } + } + + Task update = task.getTask(); + update.setModificationDate(now()); + update.putTransitory(SyncFlags.FORCE_SYNC, true); + taskDao.save(update); + + if (BuildConfig.DEBUG) { + googleTaskDao.validateSorting(task.getGoogleTaskList()); + } + } +} diff --git a/app/src/main/java/com/todoroo/astrid/adapter/TaskAdapterProvider.java b/app/src/main/java/com/todoroo/astrid/adapter/TaskAdapterProvider.java index 0a4e5108d..4aff43071 100644 --- a/app/src/main/java/com/todoroo/astrid/adapter/TaskAdapterProvider.java +++ b/app/src/main/java/com/todoroo/astrid/adapter/TaskAdapterProvider.java @@ -73,9 +73,7 @@ public class TaskAdapterProvider { GtasksFilter gtasksFilter = (GtasksFilter) filter; GoogleTaskList list = gtasksListService.getList(gtasksFilter.getStoreId()); if (list != null) { - return preferences.isManualSort() - ? createManualGoogleTaskAdapter(gtasksFilter) - : new TaskAdapter(); + return createGoogleTaskAdapter(gtasksFilter); } } else if (filter instanceof CaldavFilter) { CaldavFilter caldavFilter = (CaldavFilter) filter; @@ -105,10 +103,12 @@ public class TaskAdapterProvider { return new AstridTaskAdapter(list, filter, updater, taskDao); } - private TaskAdapter createManualGoogleTaskAdapter(GtasksFilter filter) { - String query = GtasksFilter.toManualOrder(filter.getSqlQuery()); + private TaskAdapter createGoogleTaskAdapter(GtasksFilter filter) { + String query = GtasksFilter.toSubtaskQuery(preferences, filter.getSqlQuery()); filter.setFilterQueryOverride(query); - return new GoogleTaskAdapter(taskDao, googleTaskDao); + return preferences.isManualSort() + ? new GoogleTaskManualSortAdapter(taskDao, googleTaskDao) + : new GoogleTaskAdapter(taskDao, googleTaskDao, preferences.addGoogleTasksToTop()); } private TaskAdapter createManualFilterTaskAdapter(Filter filter) { diff --git a/app/src/main/java/com/todoroo/astrid/api/GtasksFilter.java b/app/src/main/java/com/todoroo/astrid/api/GtasksFilter.java index 3f8c218c9..c22aa25ab 100644 --- a/app/src/main/java/com/todoroo/astrid/api/GtasksFilter.java +++ b/app/src/main/java/com/todoroo/astrid/api/GtasksFilter.java @@ -6,7 +6,9 @@ import androidx.annotation.NonNull; import com.todoroo.andlib.sql.Criterion; import com.todoroo.andlib.sql.Field; import com.todoroo.andlib.sql.Join; +import com.todoroo.andlib.sql.OrderType; import com.todoroo.andlib.sql.QueryTemplate; +import com.todoroo.astrid.core.SortHelper; import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.data.Task; import java.util.HashMap; @@ -14,6 +16,7 @@ import java.util.Map; import org.tasks.R; import org.tasks.data.GoogleTask; import org.tasks.data.GoogleTaskList; +import org.tasks.preferences.Preferences; public class GtasksFilter extends Filter { @@ -49,13 +52,56 @@ public class GtasksFilter extends Filter { icon = list.getIcon(); } - public static String toManualOrder(String query) { + public static String toSubtaskQuery(Preferences preferences, String query) { + boolean manualSort = preferences.isManualSort(); + String parentSort, childPrimarySort, childSecondarySort; + OrderType sortOrder; + OrderType titleOrder = OrderType.ASC; + if (manualSort) { + parentSort = "google_tasks.gt_order"; + childPrimarySort = "p.gt_order"; + childSecondarySort = "c.gt_order"; + sortOrder = OrderType.ASC; + } else { + int sortMode = preferences.getSortMode(); + parentSort = SortHelper.orderSelectForSortTypeRecursive(sortMode) + .replaceFirst("AS .*", ""); + childPrimarySort = SortHelper.orderSelectForSortTypeRecursive(sortMode) + .replaceFirst("AS .*", "") + .replaceAll("tasks\\.", "parent_tasks."); + childSecondarySort = SortHelper.orderSelectForSortTypeRecursive(sortMode) + .replaceFirst("AS .*", "") + .replaceAll("tasks\\.", "child_tasks."); + sortOrder = sortMode == SortHelper.SORT_MODIFIED ? OrderType.DESC : OrderType.ASC; + if (preferences.isReverseSort() && sortMode == SortHelper.SORT_ALPHA) { + titleOrder = OrderType.DESC; + } + } + if (preferences.isReverseSort()) { + sortOrder = sortOrder == OrderType.DESC ? OrderType.ASC : OrderType.DESC; + } query = query.replace( "WHERE", - "JOIN (SELECT google_tasks.*, COUNT(c.gt_id) AS children, 0 AS siblings, google_tasks.gt_order AS primary_sort, NULL AS secondary_sort FROM google_tasks LEFT JOIN google_tasks AS c ON c.gt_parent = google_tasks.gt_task WHERE google_tasks.gt_parent = 0 GROUP BY google_tasks.gt_task UNION SELECT c.*, 0 AS children, COUNT(s.gt_id) AS siblings, p.gt_order AS primary_sort, c.gt_order AS secondary_sort FROM google_tasks AS c LEFT JOIN google_tasks AS p ON c.gt_parent = p.gt_task LEFT JOIN tasks ON c.gt_parent = tasks._id LEFT JOIN google_tasks AS s ON s.gt_parent = p.gt_task WHERE c.gt_parent > 0 AND ((tasks.completed=0) AND (tasks.deleted=0) AND (tasks.hideUntil<(strftime('%s','now')*1000))) GROUP BY c.gt_task) as g2 ON g2.gt_id = google_tasks.gt_id WHERE"); + "JOIN (" + + "SELECT 0 AS indent, google_tasks.*, COUNT(c.gt_id) AS children, 0 AS siblings, " + parentSort + " AS primary_sort, NULL AS secondary_sort, UPPER(tasks.title) AS primary_title, NULL AS secondary_title " + + "FROM google_tasks " + + "LEFT JOIN google_tasks AS c ON c.gt_parent = google_tasks.gt_task " + + "LEFT JOIN tasks ON tasks._id = google_tasks.gt_task " + + "WHERE google_tasks.gt_parent = 0 " + + "GROUP BY google_tasks.gt_task " + + "UNION " + + "SELECT 1 AS indent, c.*, 0 AS children, COUNT(s.gt_id) AS siblings, " + childPrimarySort + " AS primary_sort, " + childSecondarySort + " AS secondary_sort, UPPER(parent_tasks.title) AS primary_title, UPPER(child_tasks.title) AS secondary_title " + + "FROM google_tasks AS c " + + "LEFT JOIN google_tasks AS p ON c.gt_parent = p.gt_task " + + "LEFT JOIN tasks AS parent_tasks ON c.gt_parent = parent_tasks._id " + + "LEFT JOIN tasks AS child_tasks ON c.gt_task = child_tasks._id " + + "LEFT JOIN google_tasks AS s ON s.gt_parent = p.gt_task " + + "WHERE c.gt_parent > 0 AND ((parent_tasks.completed=0) AND (parent_tasks.deleted=0) AND (parent_tasks.hideUntil<(strftime('%s','now')*1000))) " + + "GROUP BY c.gt_task" + + ") as g2 ON g2.gt_id = google_tasks.gt_id WHERE"); query = query.replaceAll("ORDER BY .*", ""); - query = query + "ORDER BY primary_sort ASC, secondary_sort ASC"; + query = query + "ORDER BY primary_sort " + sortOrder + ", primary_title " + titleOrder + ", indent ASC" + ", secondary_sort " + sortOrder + ", secondary_title " + titleOrder; return query; } diff --git a/app/src/main/java/com/todoroo/astrid/subtasks/SubtasksHelper.java b/app/src/main/java/com/todoroo/astrid/subtasks/SubtasksHelper.java index af9427448..14d7dc2c4 100644 --- a/app/src/main/java/com/todoroo/astrid/subtasks/SubtasksHelper.java +++ b/app/src/main/java/com/todoroo/astrid/subtasks/SubtasksHelper.java @@ -136,7 +136,7 @@ public class SubtasksHelper { if (shouldUseSubtasksFragmentForFilter(filter)) { if (filter instanceof GtasksFilter) { - query = GtasksFilter.toManualOrder(query); + query = GtasksFilter.toSubtaskQuery(preferences, query); } else { TagData tagData = tagDataDao.getTagByName(filter.listingTitle); TaskListMetadata tlm = null; diff --git a/app/src/main/java/org/tasks/data/GoogleTask.java b/app/src/main/java/org/tasks/data/GoogleTask.java index 6cf7fd992..2eca538c9 100644 --- a/app/src/main/java/org/tasks/data/GoogleTask.java +++ b/app/src/main/java/org/tasks/data/GoogleTask.java @@ -117,7 +117,7 @@ public class GoogleTask { } public int getIndent() { - return parent > 0 ? 0 : 1; + return parent > 0 ? 1 : 0; } public long getRemoteOrder() { diff --git a/app/src/main/java/org/tasks/dialogs/SortDialog.java b/app/src/main/java/org/tasks/dialogs/SortDialog.java index 72ff5775d..e2e2de2e3 100644 --- a/app/src/main/java/org/tasks/dialogs/SortDialog.java +++ b/app/src/main/java/org/tasks/dialogs/SortDialog.java @@ -23,6 +23,7 @@ import timber.log.Timber; public class SortDialog extends InjectingDialogFragment { private static final String EXTRA_MANUAL_ENABLED = "extra_manual_enabled"; + private static final String EXTRA_IS_GOOGLE_TASKS = "extra_is_google_tasks"; private static final String EXTRA_SELECTED_INDEX = "extra_selected_index"; @Inject Preferences preferences; @Inject DialogBuilder dialogBuilder; @@ -31,9 +32,12 @@ public class SortDialog extends InjectingDialogFragment { private AlertDialog alertDialog; private SortDialogCallback callback; - public static SortDialog newSortDialog(boolean manualEnabled) { + public static SortDialog newSortDialog(boolean manualEnabled, boolean isGoogleTasks) { SortDialog sortDialog = new SortDialog(); - sortDialog.manualEnabled = manualEnabled; + Bundle args = new Bundle(); + args.putBoolean(EXTRA_MANUAL_ENABLED, manualEnabled); + args.putBoolean(EXTRA_IS_GOOGLE_TASKS, isGoogleTasks); + sortDialog.setArguments(args); return sortDialog; } @@ -42,6 +46,10 @@ public class SortDialog extends InjectingDialogFragment { public Dialog onCreateDialog(Bundle savedInstanceState) { onCreate(savedInstanceState); + Bundle arguments = getArguments(); + manualEnabled = arguments.getBoolean(EXTRA_MANUAL_ENABLED); + boolean isGoogleTasks = arguments.getBoolean(EXTRA_IS_GOOGLE_TASKS); + if (savedInstanceState != null) { manualEnabled = savedInstanceState.getBoolean(EXTRA_MANUAL_ENABLED); selectedIndex = savedInstanceState.getInt(EXTRA_SELECTED_INDEX); @@ -52,7 +60,7 @@ public class SortDialog extends InjectingDialogFragment { List items = new ArrayList<>(); if (manualEnabled) { - items.add(getString(R.string.SSD_sort_drag)); + items.add(getString(isGoogleTasks ? R.string.SSD_sort_my_order : R.string.SSD_sort_drag)); } items.add(getString(R.string.SSD_sort_auto)); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c9aa2b9df..134a758b0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -33,6 +33,7 @@ File %1$s contained %2$s.\n\n Call Open Manual order with subtasks + My order Smart sort By title By due date