From c065525cefe318095e4dabbad39d26c38581202a Mon Sep 17 00:00:00 2001 From: Alex Baker Date: Mon, 29 May 2023 10:52:25 -0500 Subject: [PATCH] Add subtask sort configuration --- .../adapter/CaldavManualSortTaskAdapter.kt | 51 +------ .../adapter/GoogleTaskManualSortAdapter.kt | 87 +---------- .../com/todoroo/astrid/adapter/TaskAdapter.kt | 136 +++++++++++++++++- .../astrid/adapter/TaskAdapterDataSource.kt | 2 + .../com/todoroo/astrid/core/SortHelper.java | 19 ++- .../org/tasks/data/TaskListQueryRecursive.kt | 22 +-- .../org/tasks/dialogs/SortSettingsActivity.kt | 71 +++++++++ .../tasks/dialogs/SortSettingsViewModel.kt | 25 ++++ .../java/org/tasks/preferences/Preferences.kt | 8 ++ .../org/tasks/preferences/QueryPreferences.kt | 4 + .../tasklist/DragAndDropRecyclerAdapter.kt | 14 +- .../org/tasks/tasklist/SectionedDataSource.kt | 1 + .../tasks/widget/ScrollableViewsFactory.kt | 6 +- .../org/tasks/widget/WidgetPreferences.java | 20 +++ app/src/main/res/values/keys.xml | 2 + 15 files changed, 318 insertions(+), 150 deletions(-) diff --git a/app/src/main/java/com/todoroo/astrid/adapter/CaldavManualSortTaskAdapter.kt b/app/src/main/java/com/todoroo/astrid/adapter/CaldavManualSortTaskAdapter.kt index 924dfb4d7..90868eea7 100644 --- a/app/src/main/java/com/todoroo/astrid/adapter/CaldavManualSortTaskAdapter.kt +++ b/app/src/main/java/com/todoroo/astrid/adapter/CaldavManualSortTaskAdapter.kt @@ -5,59 +5,16 @@ import com.todoroo.astrid.service.TaskMover import org.tasks.LocalBroadcastManager import org.tasks.data.CaldavDao import org.tasks.data.GoogleTaskDao -import org.tasks.data.TaskContainer class CaldavManualSortTaskAdapter internal constructor( googleTaskDao: GoogleTaskDao, - private val caldavDao: CaldavDao, - private val taskDao: TaskDao, - private val localBroadcastManager: LocalBroadcastManager, + caldavDao: CaldavDao, + taskDao: TaskDao, + localBroadcastManager: LocalBroadcastManager, taskMover: TaskMover, ) : TaskAdapter(false, googleTaskDao, caldavDao, taskDao, localBroadcastManager, taskMover) { override suspend fun moved(from: Int, to: Int, indent: Int) { - val task = getTask(from) - val oldParent = task.parent - val newParent = changeParent(task, indent, to) - - if (oldParent == newParent && from == to) { - return - } - - val previous = if (to > 0) getTask(to - 1) else null - val next = if (to < count) getTask(to) else null - - val newPosition = when { - previous == null -> next!!.caldavSortOrder - 1 - indent > previous.indent && next?.indent == indent -> next.caldavSortOrder - 1 - indent > previous.indent -> null - indent == previous.indent -> previous.caldavSortOrder + 1 - else -> getTask((to - 1 downTo 0).find { getTask(it).indent == indent }!!).caldavSortOrder + 1 - } - caldavDao.move(task, oldParent, newParent, newPosition) - taskDao.touch(task.id) - localBroadcastManager.broadcastRefresh() - } - - private suspend fun changeParent(task: TaskContainer, indent: Int, to: Int): Long { - val newParent = findParent(indent, to)?.id ?: 0 - if (task.parent != newParent) { - changeParent(task, newParent) - } - return newParent - } - - private suspend fun changeParent(task: TaskContainer, newParent: Long) { - val caldavTask = task.caldavTask ?: return - if (newParent == 0L) { - caldavTask.remoteParent = "" - task.parent = 0 - } else { - val parentTask = caldavDao.getTask(newParent) ?: return - caldavTask.remoteParent = parentTask.remoteId - task.parent = newParent - } - caldavDao.update(caldavTask.id, caldavTask.remoteParent) - taskDao.save(task.task, null) + moveCaldavTask(from, to, indent) } } \ No newline at end of file diff --git a/app/src/main/java/com/todoroo/astrid/adapter/GoogleTaskManualSortAdapter.kt b/app/src/main/java/com/todoroo/astrid/adapter/GoogleTaskManualSortAdapter.kt index b94c6277f..fa2ff626e 100644 --- a/app/src/main/java/com/todoroo/astrid/adapter/GoogleTaskManualSortAdapter.kt +++ b/app/src/main/java/com/todoroo/astrid/adapter/GoogleTaskManualSortAdapter.kt @@ -2,98 +2,19 @@ package com.todoroo.astrid.adapter import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.service.TaskMover -import org.tasks.BuildConfig import org.tasks.LocalBroadcastManager import org.tasks.data.CaldavDao import org.tasks.data.GoogleTaskDao class GoogleTaskManualSortAdapter internal constructor( - private val googleTaskDao: GoogleTaskDao, + googleTaskDao: GoogleTaskDao, caldavDao: CaldavDao, - private val taskDao: TaskDao, - private val localBroadcastManager: LocalBroadcastManager, + taskDao: TaskDao, + localBroadcastManager: LocalBroadcastManager, taskMover: TaskMover, ) : TaskAdapter(false, googleTaskDao, caldavDao, taskDao, localBroadcastManager, taskMover) { override suspend fun moved(from: Int, to: Int, indent: Int) { - val task = getTask(from) - val googleTask = task.caldavTask ?: return - val list = googleTask.calendar ?: return - val previous = if (to > 0) getTask(to - 1) else null - if (previous == null) { - googleTaskDao.move( - task = task.task, - list = list, - newParent = 0, - newPosition = 0, - ) - } else if (to == count || to <= from) { - when { - indent == 0 -> - googleTaskDao.move( - task = task.task, - list = list, - newParent = 0, - newPosition = previous.primarySort + if (to == count) 0 else 1, - ) - previous.hasParent() && previous.parent == task.parent -> - googleTaskDao.move( - task = task.task, - list = list, - newParent = previous.parent, - newPosition = previous.secondarySort + if (to == count) 0 else 1, - ) - previous.hasParent() -> - googleTaskDao.move( - task = task.task, - list = list, - newParent = previous.parent, - newPosition = previous.secondarySort + 1, - ) - else -> - googleTaskDao.move( - task = task.task, - list = list, - newParent = previous.id, - newPosition = 0, - ) - } - } else { - when { - indent == 0 -> - googleTaskDao.move( - task = task.task, - list = list, - newParent = 0, - newPosition = previous.primarySort + if (task.hasParent()) 1 else 0, - ) - previous.hasParent() && previous.parent == task.parent -> - googleTaskDao.move( - task = task.task, - list = list, - newParent = previous.parent, - newPosition = previous.secondarySort, - ) - previous.hasParent() -> - googleTaskDao.move( - task = task.task, - list = list, - newParent = previous.parent, - newPosition = previous.secondarySort + 1, - ) - else -> - googleTaskDao.move( - task = task.task, - list = list, - newParent = previous.id, - newPosition = 0, - ) - } - } - taskDao.touch(task.id) - localBroadcastManager.broadcastRefresh() - if (BuildConfig.DEBUG) { - googleTaskDao.validateSorting(task.caldav!!) - } + moveGoogleTask(from, to, indent) } } \ No newline at end of file diff --git a/app/src/main/java/com/todoroo/astrid/adapter/TaskAdapter.kt b/app/src/main/java/com/todoroo/astrid/adapter/TaskAdapter.kt index a00d5fccc..6662483e8 100644 --- a/app/src/main/java/com/todoroo/astrid/adapter/TaskAdapter.kt +++ b/app/src/main/java/com/todoroo/astrid/adapter/TaskAdapter.kt @@ -8,6 +8,7 @@ package com.todoroo.astrid.adapter import com.todoroo.astrid.core.SortHelper.SORT_DUE import com.todoroo.astrid.core.SortHelper.SORT_IMPORTANCE import com.todoroo.astrid.core.SortHelper.SORT_LIST +import com.todoroo.astrid.core.SortHelper.SORT_MANUAL import com.todoroo.astrid.core.SortHelper.SORT_START import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.data.Task @@ -32,7 +33,6 @@ open class TaskAdapter( private val localBroadcastManager: LocalBroadcastManager, private val taskMover: TaskMover, ) { - private val selected = HashSet() private val collapsed = mutableSetOf(HEADER_COMPLETED) private lateinit var dataSource: TaskAdapterDataSource @@ -141,6 +141,12 @@ open class TaskAdapter( if ((newParent?.id ?: 0) == task.parent) { if (indent == 0) { changeSortGroup(task, if (from < to) to - 1 else to) + } else if (dataSource.subtaskSortMode == SORT_MANUAL) { + if (task.isGoogleTask) { + moveGoogleTask(from, to, indent) + } else { + moveCaldavTask(from, to, indent) + } } return } else if (newParent != null) { @@ -311,4 +317,132 @@ open class TaskAdapter( taskDao.touch(task.id) localBroadcastManager.broadcastRefresh() } + + protected suspend fun moveGoogleTask(from: Int, to: Int, indent: Int) { + val task = getTask(from) + val googleTask = task.caldavTask ?: return + val list = googleTask.calendar ?: return + val previous = if (to > 0) getTask(to - 1) else null + if (previous == null) { + googleTaskDao.move( + task = task.task, + list = list, + newParent = 0, + newPosition = 0, + ) + } else if (to == count || to <= from) { + when { + indent == 0 -> + googleTaskDao.move( + task = task.task, + list = list, + newParent = 0, + newPosition = previous.primarySort + if (to == count) 0 else 1, + ) + previous.hasParent() && previous.parent == task.parent -> + googleTaskDao.move( + task = task.task, + list = list, + newParent = previous.parent, + newPosition = previous.secondarySort + if (to == count) 0 else 1, + ) + previous.hasParent() -> + googleTaskDao.move( + task = task.task, + list = list, + newParent = previous.parent, + newPosition = previous.secondarySort + 1, + ) + else -> + googleTaskDao.move( + task = task.task, + list = list, + newParent = previous.id, + newPosition = 0, + ) + } + } else { + when { + indent == 0 -> + googleTaskDao.move( + task = task.task, + list = list, + newParent = 0, + newPosition = previous.primarySort + if (task.hasParent()) 1 else 0, + ) + previous.hasParent() && previous.parent == task.parent -> + googleTaskDao.move( + task = task.task, + list = list, + newParent = previous.parent, + newPosition = previous.secondarySort, + ) + previous.hasParent() -> + googleTaskDao.move( + task = task.task, + list = list, + newParent = previous.parent, + newPosition = previous.secondarySort + 1, + ) + else -> + googleTaskDao.move( + task = task.task, + list = list, + newParent = previous.id, + newPosition = 0, + ) + } + } + taskDao.touch(task.id) + localBroadcastManager.broadcastRefresh() + if (BuildConfig.DEBUG) { + googleTaskDao.validateSorting(task.caldav!!) + } + } + + protected suspend fun moveCaldavTask(from: Int, to: Int, indent: Int) { + val task = getTask(from) + val oldParent = task.parent + val newParent = changeCaldavParent(task, indent, to) + + if (oldParent == newParent && from == to) { + return + } + + val previous = if (to > 0) getTask(to - 1) else null + val next = if (to < count) getTask(to) else null + + val newPosition = when { + previous == null -> next!!.caldavSortOrder - 1 + indent > previous.indent && next?.indent == indent -> next.caldavSortOrder - 1 + indent > previous.indent -> null + indent == previous.indent -> previous.caldavSortOrder + 1 + else -> getTask((to - 1 downTo 0).find { getTask(it).indent == indent }!!).caldavSortOrder + 1 + } + caldavDao.move(task, oldParent, newParent, newPosition) + taskDao.touch(task.id) + localBroadcastManager.broadcastRefresh() + } + + private suspend fun changeCaldavParent(task: TaskContainer, indent: Int, to: Int): Long { + val newParent = findParent(indent, to)?.id ?: 0 + if (task.parent != newParent) { + changeCaldavParent(task, newParent) + } + return newParent + } + + private suspend fun changeCaldavParent(task: TaskContainer, newParent: Long) { + val caldavTask = task.caldavTask ?: return + if (newParent == 0L) { + caldavTask.remoteParent = "" + task.parent = 0 + } else { + val parentTask = caldavDao.getTask(newParent) ?: return + caldavTask.remoteParent = parentTask.remoteId + task.parent = newParent + } + caldavDao.update(caldavTask.id, caldavTask.remoteParent) + taskDao.save(task.task, null) + } } \ No newline at end of file diff --git a/app/src/main/java/com/todoroo/astrid/adapter/TaskAdapterDataSource.kt b/app/src/main/java/com/todoroo/astrid/adapter/TaskAdapterDataSource.kt index 45d622285..bda977a61 100644 --- a/app/src/main/java/com/todoroo/astrid/adapter/TaskAdapterDataSource.kt +++ b/app/src/main/java/com/todoroo/astrid/adapter/TaskAdapterDataSource.kt @@ -12,4 +12,6 @@ interface TaskAdapterDataSource { fun nearestHeader(position: Int): Long = -1 val sortMode: Int get() = -1 + + val subtaskSortMode: Int get() = -1 } \ No newline at end of file diff --git a/app/src/main/java/com/todoroo/astrid/core/SortHelper.java b/app/src/main/java/com/todoroo/astrid/core/SortHelper.java index 8156f5407..a89306f6d 100644 --- a/app/src/main/java/com/todoroo/astrid/core/SortHelper.java +++ b/app/src/main/java/com/todoroo/astrid/core/SortHelper.java @@ -42,6 +42,7 @@ public class SortHelper { public static final int SORT_START = 8; public static final int SORT_LIST = 9; public static final int SORT_COMPLETED = 10; + public static final int SORT_MANUAL = 11; public static final long APPLE_EPOCH = 978307200000L; // 1/1/2001 GMT @SuppressLint("DefaultLocale") @@ -215,10 +216,20 @@ public class SortHelper { : Order.desc("primary_group"); } - public static Order orderForSortTypeRecursive(int sortMode, boolean ascending) { - Order order = ascending || sortMode == SORT_GTASKS || sortMode == SORT_CALDAV - ? Order.asc("primary_sort").addSecondaryExpression(Order.asc("secondary_sort")) - : Order.desc("primary_sort").addSecondaryExpression(Order.desc("secondary_sort")); + public static Order orderForSortTypeRecursive( + int sortMode, + boolean primaryAscending, + int secondaryMode, + boolean secondaryAscending + ) { + Order order = primaryAscending || sortMode == SORT_GTASKS || sortMode == SORT_CALDAV + ? Order.asc("primary_sort") + : Order.desc("primary_sort"); + order.addSecondaryExpression( + secondaryAscending || secondaryMode == SORT_GTASKS || secondaryMode == SORT_CALDAV + ? Order.asc("secondary_sort") + : Order.desc("secondary_sort") + ); if (sortMode != SORT_ALPHA) { order.addSecondaryExpression(Order.asc("sort_title")); } diff --git a/app/src/main/java/org/tasks/data/TaskListQueryRecursive.kt b/app/src/main/java/org/tasks/data/TaskListQueryRecursive.kt index bb6e7f1f0..fabe7c348 100644 --- a/app/src/main/java/org/tasks/data/TaskListQueryRecursive.kt +++ b/app/src/main/java/org/tasks/data/TaskListQueryRecursive.kt @@ -54,24 +54,28 @@ internal object TaskListQueryRecursive { (manualSort || groupPreference == SortHelper.SORT_LIST) -> SortHelper.GROUP_NONE else -> groupPreference } - val sortPreference = preferences.sortMode val sortMode = when { manualSort && filter is GtasksFilter -> SortHelper.SORT_GTASKS manualSort && filter is CaldavFilter -> SortHelper.SORT_CALDAV - else -> sortPreference + else -> preferences.sortMode + } + val subtaskPreference = preferences.subtaskMode + val subtaskMode = when { + manualSort && filter is GtasksFilter -> SortHelper.SORT_GTASKS + manualSort && filter is CaldavFilter -> SortHelper.SORT_CALDAV + subtaskPreference == SortHelper.SORT_MANUAL -> SortHelper.SORT_CALDAV + else -> subtaskPreference } val completedMode = preferences.completedMode val groupAscending = preferences.groupAscending && groupMode != SortHelper.GROUP_NONE val sortAscending = preferences.sortAscending && sortMode != SortHelper.SORT_GTASKS && sortMode != SortHelper.SORT_CALDAV + val subtaskAscending = + preferences.subtaskAscending && subtaskMode != SortHelper.SORT_GTASKS && subtaskMode != SortHelper.SORT_CALDAV val primaryGroupSelector = SortHelper.orderSelectForSortTypeRecursive(groupMode, true) val primarySortSelect = SortHelper.orderSelectForSortTypeRecursive(sortMode, false) - val secondarySortSelect = if (sortMode == SortHelper.SORT_LIST) { - "NULL" - } else { - primarySortSelect - } + val subtaskSort = SortHelper.orderSelectForSortTypeRecursive(subtaskMode, false) val parentCompleted = if (preferences.completedTasksAtBottom) "tasks.completed > 0" else "0" val completionSort = if (preferences.completedTasksAtBottom) { "(CASE WHEN tasks.completed > 0 THEN ${SortHelper.orderSelectForSortTypeRecursive(completedMode, false)} ELSE 0 END)" @@ -94,9 +98,9 @@ internal object TaskListQueryRecursive { } } $parentQuery - UNION ALL SELECT tasks._id, recursive_tasks.parent_complete, $parentCompleted as subtask_complete, $completionSort as completion_sort, recursive_tasks.task as parent, tasks.collapsed as collapsed, CASE WHEN recursive_tasks.collapsed > 0 OR recursive_tasks.hidden > 0 THEN 1 ELSE 0 END as hidden, recursive_tasks.indent+1 AS sort_indent, UPPER(tasks.title) AS sort_title, recursive_tasks.primary_group as primary_group, recursive_tasks.primary_sort as primary_sort, $secondarySortSelect as secondary_sort, recursive_tasks.sort_group FROM tasks + UNION ALL SELECT tasks._id, recursive_tasks.parent_complete, $parentCompleted as subtask_complete, $completionSort as completion_sort, recursive_tasks.task as parent, tasks.collapsed as collapsed, CASE WHEN recursive_tasks.collapsed > 0 OR recursive_tasks.hidden > 0 THEN 1 ELSE 0 END as hidden, recursive_tasks.indent+1 AS sort_indent, UPPER(tasks.title) AS sort_title, recursive_tasks.primary_group as primary_group, recursive_tasks.primary_sort as primary_sort, $subtaskSort as secondary_sort, recursive_tasks.sort_group FROM tasks $SUBTASK_QUERY - ORDER BY parent_complete ASC, sort_indent DESC, subtask_complete ASC, completion_sort ${if (preferences.completedAscending) "ASC" else "DESC"}, ${SortHelper.orderForGroupTypeRecursive(groupMode, groupAscending)}, ${SortHelper.orderForSortTypeRecursive(sortMode, sortAscending)} + ORDER BY parent_complete ASC, sort_indent DESC, subtask_complete ASC, completion_sort ${if (preferences.completedAscending) "ASC" else "DESC"}, ${SortHelper.orderForGroupTypeRecursive(groupMode, groupAscending)}, ${SortHelper.orderForSortTypeRecursive(sortMode, sortAscending, subtaskMode, subtaskAscending)} ) SELECT * FROM recursive_tasks WHERE indent = (SELECT MAX(indent) FROM recursive_tasks as r WHERE r.task = recursive_tasks.task) """.trimIndent() diff --git a/app/src/main/java/org/tasks/dialogs/SortSettingsActivity.kt b/app/src/main/java/org/tasks/dialogs/SortSettingsActivity.kt index 3c7e721ce..576207d00 100644 --- a/app/src/main/java/org/tasks/dialogs/SortSettingsActivity.kt +++ b/app/src/main/java/org/tasks/dialogs/SortSettingsActivity.kt @@ -30,6 +30,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.ArrowDownward import androidx.compose.material.icons.outlined.ArrowUpward import androidx.compose.material.icons.outlined.ExpandCircleDown +import androidx.compose.material.icons.outlined.SubdirectoryArrowRight import androidx.compose.material.icons.outlined.SwapVert import androidx.compose.material3.Divider import androidx.compose.material3.ExperimentalMaterial3Api @@ -89,6 +90,7 @@ class SortSettingsActivity : ComponentActivity() { var showGroupPicker by remember { mutableStateOf(false) } var showSortPicker by remember { mutableStateOf(false) } var showCompletedPicker by remember { mutableStateOf(false) } + var showSubtaskPicker by remember { mutableStateOf(false) } ModalBottomSheet( onDismissRequest = { val forceReload = viewModel.forceReload @@ -111,19 +113,23 @@ class SortSettingsActivity : ComponentActivity() { groupMode = state.groupMode, sortMode = state.sortMode, completedMode = state.completedMode, + subtaskMode = state.subtaskMode, sortAscending = state.sortAscending, groupAscending = state.groupAscending, completedAscending = state.completedAscending, + subtaskAscending = state.subtaskAscending, manualSort = state.manualSort && manualEnabled, astridSort = state.astridSort && astridEnabled, completedAtBottom = state.completedAtBottom, setSortAscending = { viewModel.setSortAscending(it) }, setGroupAscending = { viewModel.setGroupAscending(it) }, setCompletedAscending = { viewModel.setCompletedAscending(it) }, + setSubtaskAscending = { viewModel.setSubtaskAscending(it) }, setCompletedAtBottom = { viewModel.setCompletedAtBottom(it) }, clickGroupMode = { showGroupPicker = true }, clickSortMode = { showSortPicker = true }, clickCompletedMode = { showCompletedPicker = true }, + clickSubtaskMode = { showSubtaskPicker = true }, ) } ) @@ -236,6 +242,41 @@ class SortSettingsActivity : ComponentActivity() { sheetState.show() } } + if (showSubtaskPicker) { + val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) + val closePicker: () -> Unit = { + scope.launch { + sheetState.hide() + showSubtaskPicker = false + } + } + ModalBottomSheet( + onDismissRequest = closePicker, + sheetState = sheetState, + containerColor = MaterialTheme.colors.surface, + scrimColor = Color.Transparent, + shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp), + content = { + Column( + modifier = Modifier + .verticalScroll(rememberScrollState()) + .fillMaxWidth() + ) { + SortPicker( + selected = state.subtaskMode, + options = subtaskOptions, + onClick = { + viewModel.setSubtaskMode(it) + closePicker() + } + ) + } + } + ) + LaunchedEffect(Unit) { + sheetState.show() + } + } LaunchedEffect(Unit) { mainSheetState.show() } @@ -322,6 +363,17 @@ val sortOptions = linkedMapOf( R.string.sort_created to SortHelper.SORT_CREATED, ) +val subtaskOptions = linkedMapOf( + R.string.SSD_sort_my_order to SortHelper.SORT_MANUAL, + R.string.SSD_sort_due to SortHelper.SORT_DUE, + R.string.SSD_sort_start to SortHelper.SORT_START, + R.string.SSD_sort_importance to SortHelper.SORT_IMPORTANCE, + R.string.SSD_sort_alpha to SortHelper.SORT_ALPHA, + R.string.SSD_sort_modified to SortHelper.SORT_MODIFIED, + R.string.SSD_sort_auto to SortHelper.SORT_AUTO, + R.string.sort_created to SortHelper.SORT_CREATED, +) + val groupOptions = linkedMapOf( R.string.none to SortHelper.GROUP_NONE, R.string.SSD_sort_due to SortHelper.SORT_DUE, @@ -397,19 +449,23 @@ fun BottomSheetContent( groupMode: Int, sortMode: Int, completedMode: Int, + subtaskMode: Int, sortAscending: Boolean, groupAscending: Boolean, completedAscending: Boolean, + subtaskAscending: Boolean, manualSort: Boolean, astridSort: Boolean, completedAtBottom: Boolean, setSortAscending: (Boolean) -> Unit, setGroupAscending: (Boolean) -> Unit, setCompletedAscending: (Boolean) -> Unit, + setSubtaskAscending: (Boolean) -> Unit, setCompletedAtBottom: (Boolean) -> Unit, clickGroupMode: () -> Unit, clickSortMode: () -> Unit, clickCompletedMode: () -> Unit, + clickSubtaskMode: () -> Unit, ) { SortRow( title = R.string.sort_grouping, @@ -436,6 +492,16 @@ fun BottomSheetContent( setAscending = setSortAscending, ) if (!astridSort) { + if (!manualSort) { + SortRow( + title = R.string.subtasks, + icon = Icons.Outlined.SubdirectoryArrowRight, + ascending = subtaskAscending, + sortMode = subtaskMode, + onClick = clickSubtaskMode, + setAscending = setSubtaskAscending, + ) + } Divider( modifier = Modifier .fillMaxWidth() @@ -558,6 +624,7 @@ private val Int.modeString: Int SortHelper.SORT_START -> R.string.SSD_sort_start SortHelper.SORT_LIST -> R.string.sort_list SortHelper.SORT_COMPLETED -> R.string.sort_completed + SortHelper.SORT_MANUAL -> R.string.SSD_sort_my_order else -> R.string.SSD_sort_auto } @@ -571,19 +638,23 @@ fun PreviewSortBottomSheet() { groupMode = SortHelper.GROUP_NONE, sortMode = SortHelper.SORT_AUTO, completedMode = SortHelper.SORT_ALPHA, + subtaskMode = SortHelper.SORT_MANUAL, sortAscending = false, groupAscending = false, completedAscending = false, + subtaskAscending = false, manualSort = false, astridSort = false, completedAtBottom = true, clickGroupMode = {}, clickSortMode = {}, clickCompletedMode = {}, + clickSubtaskMode = {}, setCompletedAtBottom = {}, setSortAscending = {}, setGroupAscending = {}, setCompletedAscending = {}, + setSubtaskAscending = {}, ) } } diff --git a/app/src/main/java/org/tasks/dialogs/SortSettingsViewModel.kt b/app/src/main/java/org/tasks/dialogs/SortSettingsViewModel.kt index d5d6544e5..570256b66 100644 --- a/app/src/main/java/org/tasks/dialogs/SortSettingsViewModel.kt +++ b/app/src/main/java/org/tasks/dialogs/SortSettingsViewModel.kt @@ -30,6 +30,8 @@ class SortSettingsViewModel @Inject constructor( val sortAscending: Boolean, val completedMode: Int, val completedAscending: Boolean, + val subtaskMode: Int, + val subtaskAscending: Boolean, ) private val widgetId = savedStateHandle[SortSettingsActivity.EXTRA_WIDGET_ID] ?: WIDGET_NONE private val preferences = @@ -48,6 +50,8 @@ class SortSettingsViewModel @Inject constructor( completedAtBottom = preferences.completedTasksAtBottom, sortMode = preferences.sortMode, sortAscending = preferences.sortAscending, + subtaskMode = preferences.subtaskMode, + subtaskAscending = preferences.subtaskAscending, ) private val _viewState = MutableStateFlow(initialState) val state = _viewState.asStateFlow() @@ -67,6 +71,11 @@ class SortSettingsViewModel @Inject constructor( _viewState.update { it.copy(completedAscending = ascending) } } + fun setSubtaskAscending(ascending: Boolean) { + preferences.subtaskAscending = ascending + _viewState.update { it.copy(subtaskAscending = ascending) } + } + fun setCompletedAtBottom(completedAtBottom: Boolean) { preferences.completedTasksAtBottom = completedAtBottom _viewState.update { it.copy(completedAtBottom = completedAtBottom) } @@ -131,6 +140,22 @@ class SortSettingsViewModel @Inject constructor( } } + fun setSubtaskMode(subtaskMode: Int) { + preferences.subtaskMode = subtaskMode + val ascending = when (subtaskMode) { + SortHelper.SORT_MODIFIED, + SortHelper.SORT_CREATED -> false + else -> true + } + preferences.subtaskAscending = ascending + _viewState.update { + it.copy( + subtaskMode = subtaskMode, + subtaskAscending = ascending + ) + } + } + fun setManual(value: Boolean) { preferences.isManualSort = value if (value) { diff --git a/app/src/main/java/org/tasks/preferences/Preferences.kt b/app/src/main/java/org/tasks/preferences/Preferences.kt index ff905cae3..9d2deb6a9 100644 --- a/app/src/main/java/org/tasks/preferences/Preferences.kt +++ b/app/src/main/java/org/tasks/preferences/Preferences.kt @@ -361,6 +361,10 @@ class Preferences @JvmOverloads constructor( get() = getInt(R.string.p_completed_mode, SortHelper.SORT_COMPLETED) set(value) { setInt(R.string.p_completed_mode, value) } + override var subtaskMode: Int + get() = getInt(R.string.p_subtask_mode, SortHelper.SORT_MANUAL) + set(value) { setInt(R.string.p_subtask_mode, value) } + override var showHidden: Boolean get() = getBoolean(R.string.p_show_hidden_tasks, true) set(value) { setBoolean(R.string.p_show_hidden_tasks, value) } @@ -510,6 +514,10 @@ class Preferences @JvmOverloads constructor( get() = getBoolean(R.string.p_completed_ascending, false) set(value) { setBoolean(R.string.p_completed_ascending, value) } + override var subtaskAscending: Boolean + get() = getBoolean(R.string.p_subtask_ascending, false) + set(value) { setBoolean(R.string.p_subtask_ascending, value) } + val defaultPriority: Int get() = getIntegerFromString(R.string.p_default_importance_key, Task.Priority.LOW) diff --git a/app/src/main/java/org/tasks/preferences/QueryPreferences.kt b/app/src/main/java/org/tasks/preferences/QueryPreferences.kt index 0507f0e8e..a8419ace3 100644 --- a/app/src/main/java/org/tasks/preferences/QueryPreferences.kt +++ b/app/src/main/java/org/tasks/preferences/QueryPreferences.kt @@ -7,6 +7,8 @@ interface QueryPreferences { var completedMode: Int + var subtaskMode: Int + var isManualSort: Boolean var isAstridSort: Boolean @@ -17,6 +19,8 @@ interface QueryPreferences { var completedAscending: Boolean + var subtaskAscending: Boolean + val showHidden: Boolean val showCompleted: Boolean diff --git a/app/src/main/java/org/tasks/tasklist/DragAndDropRecyclerAdapter.kt b/app/src/main/java/org/tasks/tasklist/DragAndDropRecyclerAdapter.kt index cf85caab7..ea6293faf 100644 --- a/app/src/main/java/org/tasks/tasklist/DragAndDropRecyclerAdapter.kt +++ b/app/src/main/java/org/tasks/tasklist/DragAndDropRecyclerAdapter.kt @@ -64,6 +64,9 @@ class DragAndDropRecyclerAdapter( override val sortMode: Int get() = items.groupMode + override val subtaskSortMode: Int + get() = items.subtaskMode + override fun getItemViewType(position: Int) = if (items.isHeader(position)) 1 else 0 override fun submitList(list: List) { @@ -91,11 +94,12 @@ class DragAndDropRecyclerAdapter( override fun transform(list: List): SectionedDataSource = SectionedDataSource( - list, - disableHeaders, - preferences.groupMode, - adapter.getCollapsed(), - preferences.completedTasksAtBottom, + tasks = list, + disableHeaders = disableHeaders, + groupMode = preferences.groupMode, + subtaskMode = preferences.subtaskMode, + collapsed = adapter.getCollapsed(), + completedAtBottom = preferences.completedTasksAtBottom, ) override fun diff(last: SectionedDataSource, next: SectionedDataSource) = diff --git a/app/src/main/java/org/tasks/tasklist/SectionedDataSource.kt b/app/src/main/java/org/tasks/tasklist/SectionedDataSource.kt index e085017a0..2b5c925e7 100644 --- a/app/src/main/java/org/tasks/tasklist/SectionedDataSource.kt +++ b/app/src/main/java/org/tasks/tasklist/SectionedDataSource.kt @@ -11,6 +11,7 @@ class SectionedDataSource constructor( tasks: List, disableHeaders: Boolean, val groupMode: Int, + val subtaskMode: Int, private val collapsed: Set, private val completedAtBottom: Boolean, ) { diff --git a/app/src/main/java/org/tasks/widget/ScrollableViewsFactory.kt b/app/src/main/java/org/tasks/widget/ScrollableViewsFactory.kt index eec11ef89..12db7f00c 100644 --- a/app/src/main/java/org/tasks/widget/ScrollableViewsFactory.kt +++ b/app/src/main/java/org/tasks/widget/ScrollableViewsFactory.kt @@ -73,10 +73,12 @@ internal class ScrollableViewsFactory( private var showTags = false private var collapsed = mutableSetOf(HEADER_COMPLETED) private var groupMode = -1 + private var subtaskMode = -1 private var tasks = SectionedDataSource( emptyList(), disableHeaders = false, - groupMode = 0, + groupMode = SortHelper.GROUP_NONE, + subtaskMode = SortHelper.SORT_MANUAL, collapsed, preferences.completedTasksAtBottom, ) @@ -101,6 +103,7 @@ internal class ScrollableViewsFactory( taskDao.fetchTasks { getQuery(filter) }, disableGroups, groupMode, + subtaskMode, collapsed, widgetPreferences.completedTasksAtBottom, ) @@ -379,6 +382,7 @@ internal class ScrollableViewsFactory( } groupMode = it } + subtaskMode = widgetPreferences.subtaskMode collapsed = widgetPreferences.collapsed compact = widgetPreferences.compact } diff --git a/app/src/main/java/org/tasks/widget/WidgetPreferences.java b/app/src/main/java/org/tasks/widget/WidgetPreferences.java index 8a03c21ef..fb0fcd96f 100644 --- a/app/src/main/java/org/tasks/widget/WidgetPreferences.java +++ b/app/src/main/java/org/tasks/widget/WidgetPreferences.java @@ -354,4 +354,24 @@ public class WidgetPreferences implements QueryPreferences { public void setCompletedAscending(boolean ascending) { preferences.setCompletedAscending(ascending); } + + @Override + public int getSubtaskMode() { + return preferences.getSubtaskMode(); + } + + @Override + public void setSubtaskMode(int mode) { + preferences.setSubtaskMode(mode); + } + + @Override + public boolean getSubtaskAscending() { + return preferences.getSubtaskAscending(); + } + + @Override + public void setSubtaskAscending(boolean ascending) { + preferences.setSubtaskAscending(ascending); + } } diff --git a/app/src/main/res/values/keys.xml b/app/src/main/res/values/keys.xml index 6e3d06106..0af9714a6 100644 --- a/app/src/main/res/values/keys.xml +++ b/app/src/main/res/values/keys.xml @@ -274,12 +274,14 @@ sort_ascending group_ascending completed_ascending + subtask_ascending manual_sort astrid_sort astrid_sort_enabled sort_mode group_mode completed_mode + subtask_mode @string/TEA_ctrl_hide_until_pref