Add subtask sort configuration

pull/2390/merge
Alex Baker 3 years ago
parent 049f995e96
commit c065525cef

@ -5,59 +5,16 @@ import com.todoroo.astrid.service.TaskMover
import org.tasks.LocalBroadcastManager import org.tasks.LocalBroadcastManager
import org.tasks.data.CaldavDao import org.tasks.data.CaldavDao
import org.tasks.data.GoogleTaskDao import org.tasks.data.GoogleTaskDao
import org.tasks.data.TaskContainer
class CaldavManualSortTaskAdapter internal constructor( class CaldavManualSortTaskAdapter internal constructor(
googleTaskDao: GoogleTaskDao, googleTaskDao: GoogleTaskDao,
private val caldavDao: CaldavDao, caldavDao: CaldavDao,
private val taskDao: TaskDao, taskDao: TaskDao,
private val localBroadcastManager: LocalBroadcastManager, localBroadcastManager: LocalBroadcastManager,
taskMover: TaskMover, taskMover: TaskMover,
) : TaskAdapter(false, googleTaskDao, caldavDao, taskDao, localBroadcastManager, taskMover) { ) : TaskAdapter(false, googleTaskDao, caldavDao, taskDao, localBroadcastManager, taskMover) {
override suspend fun moved(from: Int, to: Int, indent: Int) { override suspend fun moved(from: Int, to: Int, indent: Int) {
val task = getTask(from) moveCaldavTask(from, to, indent)
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)
} }
} }

@ -2,98 +2,19 @@ package com.todoroo.astrid.adapter
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.service.TaskMover import com.todoroo.astrid.service.TaskMover
import org.tasks.BuildConfig
import org.tasks.LocalBroadcastManager import org.tasks.LocalBroadcastManager
import org.tasks.data.CaldavDao import org.tasks.data.CaldavDao
import org.tasks.data.GoogleTaskDao import org.tasks.data.GoogleTaskDao
class GoogleTaskManualSortAdapter internal constructor( class GoogleTaskManualSortAdapter internal constructor(
private val googleTaskDao: GoogleTaskDao, googleTaskDao: GoogleTaskDao,
caldavDao: CaldavDao, caldavDao: CaldavDao,
private val taskDao: TaskDao, taskDao: TaskDao,
private val localBroadcastManager: LocalBroadcastManager, localBroadcastManager: LocalBroadcastManager,
taskMover: TaskMover, taskMover: TaskMover,
) : TaskAdapter(false, googleTaskDao, caldavDao, taskDao, localBroadcastManager, taskMover) { ) : TaskAdapter(false, googleTaskDao, caldavDao, taskDao, localBroadcastManager, taskMover) {
override suspend fun moved(from: Int, to: Int, indent: Int) { override suspend fun moved(from: Int, to: Int, indent: Int) {
val task = getTask(from) moveGoogleTask(from, to, indent)
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!!)
}
} }
} }

@ -8,6 +8,7 @@ package com.todoroo.astrid.adapter
import com.todoroo.astrid.core.SortHelper.SORT_DUE import com.todoroo.astrid.core.SortHelper.SORT_DUE
import com.todoroo.astrid.core.SortHelper.SORT_IMPORTANCE import com.todoroo.astrid.core.SortHelper.SORT_IMPORTANCE
import com.todoroo.astrid.core.SortHelper.SORT_LIST 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.core.SortHelper.SORT_START
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.data.Task import com.todoroo.astrid.data.Task
@ -32,7 +33,6 @@ open class TaskAdapter(
private val localBroadcastManager: LocalBroadcastManager, private val localBroadcastManager: LocalBroadcastManager,
private val taskMover: TaskMover, private val taskMover: TaskMover,
) { ) {
private val selected = HashSet<Long>() private val selected = HashSet<Long>()
private val collapsed = mutableSetOf(HEADER_COMPLETED) private val collapsed = mutableSetOf(HEADER_COMPLETED)
private lateinit var dataSource: TaskAdapterDataSource private lateinit var dataSource: TaskAdapterDataSource
@ -141,6 +141,12 @@ open class TaskAdapter(
if ((newParent?.id ?: 0) == task.parent) { if ((newParent?.id ?: 0) == task.parent) {
if (indent == 0) { if (indent == 0) {
changeSortGroup(task, if (from < to) to - 1 else to) 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 return
} else if (newParent != null) { } else if (newParent != null) {
@ -311,4 +317,132 @@ open class TaskAdapter(
taskDao.touch(task.id) taskDao.touch(task.id)
localBroadcastManager.broadcastRefresh() 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)
}
} }

@ -12,4 +12,6 @@ interface TaskAdapterDataSource {
fun nearestHeader(position: Int): Long = -1 fun nearestHeader(position: Int): Long = -1
val sortMode: Int get() = -1 val sortMode: Int get() = -1
val subtaskSortMode: Int get() = -1
} }

@ -42,6 +42,7 @@ public class SortHelper {
public static final int SORT_START = 8; public static final int SORT_START = 8;
public static final int SORT_LIST = 9; public static final int SORT_LIST = 9;
public static final int SORT_COMPLETED = 10; 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 public static final long APPLE_EPOCH = 978307200000L; // 1/1/2001 GMT
@SuppressLint("DefaultLocale") @SuppressLint("DefaultLocale")
@ -215,10 +216,20 @@ public class SortHelper {
: Order.desc("primary_group"); : Order.desc("primary_group");
} }
public static Order orderForSortTypeRecursive(int sortMode, boolean ascending) { public static Order orderForSortTypeRecursive(
Order order = ascending || sortMode == SORT_GTASKS || sortMode == SORT_CALDAV int sortMode,
? Order.asc("primary_sort").addSecondaryExpression(Order.asc("secondary_sort")) boolean primaryAscending,
: Order.desc("primary_sort").addSecondaryExpression(Order.desc("secondary_sort")); 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) { if (sortMode != SORT_ALPHA) {
order.addSecondaryExpression(Order.asc("sort_title")); order.addSecondaryExpression(Order.asc("sort_title"));
} }

@ -54,24 +54,28 @@ internal object TaskListQueryRecursive {
(manualSort || groupPreference == SortHelper.SORT_LIST) -> SortHelper.GROUP_NONE (manualSort || groupPreference == SortHelper.SORT_LIST) -> SortHelper.GROUP_NONE
else -> groupPreference else -> groupPreference
} }
val sortPreference = preferences.sortMode
val sortMode = when { val sortMode = when {
manualSort && filter is GtasksFilter -> SortHelper.SORT_GTASKS manualSort && filter is GtasksFilter -> SortHelper.SORT_GTASKS
manualSort && filter is CaldavFilter -> SortHelper.SORT_CALDAV 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 completedMode = preferences.completedMode
val groupAscending = val groupAscending =
preferences.groupAscending && groupMode != SortHelper.GROUP_NONE preferences.groupAscending && groupMode != SortHelper.GROUP_NONE
val sortAscending = val sortAscending =
preferences.sortAscending && sortMode != SortHelper.SORT_GTASKS && sortMode != SortHelper.SORT_CALDAV 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 primaryGroupSelector = SortHelper.orderSelectForSortTypeRecursive(groupMode, true)
val primarySortSelect = SortHelper.orderSelectForSortTypeRecursive(sortMode, false) val primarySortSelect = SortHelper.orderSelectForSortTypeRecursive(sortMode, false)
val secondarySortSelect = if (sortMode == SortHelper.SORT_LIST) { val subtaskSort = SortHelper.orderSelectForSortTypeRecursive(subtaskMode, false)
"NULL"
} else {
primarySortSelect
}
val parentCompleted = if (preferences.completedTasksAtBottom) "tasks.completed > 0" else "0" val parentCompleted = if (preferences.completedTasksAtBottom) "tasks.completed > 0" else "0"
val completionSort = if (preferences.completedTasksAtBottom) { val completionSort = if (preferences.completedTasksAtBottom) {
"(CASE WHEN tasks.completed > 0 THEN ${SortHelper.orderSelectForSortTypeRecursive(completedMode, false)} ELSE 0 END)" "(CASE WHEN tasks.completed > 0 THEN ${SortHelper.orderSelectForSortTypeRecursive(completedMode, false)} ELSE 0 END)"
@ -94,9 +98,9 @@ internal object TaskListQueryRecursive {
} }
} }
$parentQuery $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 $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 ) SELECT * FROM recursive_tasks
WHERE indent = (SELECT MAX(indent) FROM recursive_tasks as r WHERE r.task = recursive_tasks.task) WHERE indent = (SELECT MAX(indent) FROM recursive_tasks as r WHERE r.task = recursive_tasks.task)
""".trimIndent() """.trimIndent()

@ -30,6 +30,7 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ArrowDownward import androidx.compose.material.icons.outlined.ArrowDownward
import androidx.compose.material.icons.outlined.ArrowUpward import androidx.compose.material.icons.outlined.ArrowUpward
import androidx.compose.material.icons.outlined.ExpandCircleDown import androidx.compose.material.icons.outlined.ExpandCircleDown
import androidx.compose.material.icons.outlined.SubdirectoryArrowRight
import androidx.compose.material.icons.outlined.SwapVert import androidx.compose.material.icons.outlined.SwapVert
import androidx.compose.material3.Divider import androidx.compose.material3.Divider
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
@ -89,6 +90,7 @@ class SortSettingsActivity : ComponentActivity() {
var showGroupPicker by remember { mutableStateOf(false) } var showGroupPicker by remember { mutableStateOf(false) }
var showSortPicker by remember { mutableStateOf(false) } var showSortPicker by remember { mutableStateOf(false) }
var showCompletedPicker by remember { mutableStateOf(false) } var showCompletedPicker by remember { mutableStateOf(false) }
var showSubtaskPicker by remember { mutableStateOf(false) }
ModalBottomSheet( ModalBottomSheet(
onDismissRequest = { onDismissRequest = {
val forceReload = viewModel.forceReload val forceReload = viewModel.forceReload
@ -111,19 +113,23 @@ class SortSettingsActivity : ComponentActivity() {
groupMode = state.groupMode, groupMode = state.groupMode,
sortMode = state.sortMode, sortMode = state.sortMode,
completedMode = state.completedMode, completedMode = state.completedMode,
subtaskMode = state.subtaskMode,
sortAscending = state.sortAscending, sortAscending = state.sortAscending,
groupAscending = state.groupAscending, groupAscending = state.groupAscending,
completedAscending = state.completedAscending, completedAscending = state.completedAscending,
subtaskAscending = state.subtaskAscending,
manualSort = state.manualSort && manualEnabled, manualSort = state.manualSort && manualEnabled,
astridSort = state.astridSort && astridEnabled, astridSort = state.astridSort && astridEnabled,
completedAtBottom = state.completedAtBottom, completedAtBottom = state.completedAtBottom,
setSortAscending = { viewModel.setSortAscending(it) }, setSortAscending = { viewModel.setSortAscending(it) },
setGroupAscending = { viewModel.setGroupAscending(it) }, setGroupAscending = { viewModel.setGroupAscending(it) },
setCompletedAscending = { viewModel.setCompletedAscending(it) }, setCompletedAscending = { viewModel.setCompletedAscending(it) },
setSubtaskAscending = { viewModel.setSubtaskAscending(it) },
setCompletedAtBottom = { viewModel.setCompletedAtBottom(it) }, setCompletedAtBottom = { viewModel.setCompletedAtBottom(it) },
clickGroupMode = { showGroupPicker = true }, clickGroupMode = { showGroupPicker = true },
clickSortMode = { showSortPicker = true }, clickSortMode = { showSortPicker = true },
clickCompletedMode = { showCompletedPicker = true }, clickCompletedMode = { showCompletedPicker = true },
clickSubtaskMode = { showSubtaskPicker = true },
) )
} }
) )
@ -236,6 +242,41 @@ class SortSettingsActivity : ComponentActivity() {
sheetState.show() 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) { LaunchedEffect(Unit) {
mainSheetState.show() mainSheetState.show()
} }
@ -322,6 +363,17 @@ val sortOptions = linkedMapOf(
R.string.sort_created to SortHelper.SORT_CREATED, 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( val groupOptions = linkedMapOf(
R.string.none to SortHelper.GROUP_NONE, R.string.none to SortHelper.GROUP_NONE,
R.string.SSD_sort_due to SortHelper.SORT_DUE, R.string.SSD_sort_due to SortHelper.SORT_DUE,
@ -397,19 +449,23 @@ fun BottomSheetContent(
groupMode: Int, groupMode: Int,
sortMode: Int, sortMode: Int,
completedMode: Int, completedMode: Int,
subtaskMode: Int,
sortAscending: Boolean, sortAscending: Boolean,
groupAscending: Boolean, groupAscending: Boolean,
completedAscending: Boolean, completedAscending: Boolean,
subtaskAscending: Boolean,
manualSort: Boolean, manualSort: Boolean,
astridSort: Boolean, astridSort: Boolean,
completedAtBottom: Boolean, completedAtBottom: Boolean,
setSortAscending: (Boolean) -> Unit, setSortAscending: (Boolean) -> Unit,
setGroupAscending: (Boolean) -> Unit, setGroupAscending: (Boolean) -> Unit,
setCompletedAscending: (Boolean) -> Unit, setCompletedAscending: (Boolean) -> Unit,
setSubtaskAscending: (Boolean) -> Unit,
setCompletedAtBottom: (Boolean) -> Unit, setCompletedAtBottom: (Boolean) -> Unit,
clickGroupMode: () -> Unit, clickGroupMode: () -> Unit,
clickSortMode: () -> Unit, clickSortMode: () -> Unit,
clickCompletedMode: () -> Unit, clickCompletedMode: () -> Unit,
clickSubtaskMode: () -> Unit,
) { ) {
SortRow( SortRow(
title = R.string.sort_grouping, title = R.string.sort_grouping,
@ -436,6 +492,16 @@ fun BottomSheetContent(
setAscending = setSortAscending, setAscending = setSortAscending,
) )
if (!astridSort) { if (!astridSort) {
if (!manualSort) {
SortRow(
title = R.string.subtasks,
icon = Icons.Outlined.SubdirectoryArrowRight,
ascending = subtaskAscending,
sortMode = subtaskMode,
onClick = clickSubtaskMode,
setAscending = setSubtaskAscending,
)
}
Divider( Divider(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@ -558,6 +624,7 @@ private val Int.modeString: Int
SortHelper.SORT_START -> R.string.SSD_sort_start SortHelper.SORT_START -> R.string.SSD_sort_start
SortHelper.SORT_LIST -> R.string.sort_list SortHelper.SORT_LIST -> R.string.sort_list
SortHelper.SORT_COMPLETED -> R.string.sort_completed SortHelper.SORT_COMPLETED -> R.string.sort_completed
SortHelper.SORT_MANUAL -> R.string.SSD_sort_my_order
else -> R.string.SSD_sort_auto else -> R.string.SSD_sort_auto
} }
@ -571,19 +638,23 @@ fun PreviewSortBottomSheet() {
groupMode = SortHelper.GROUP_NONE, groupMode = SortHelper.GROUP_NONE,
sortMode = SortHelper.SORT_AUTO, sortMode = SortHelper.SORT_AUTO,
completedMode = SortHelper.SORT_ALPHA, completedMode = SortHelper.SORT_ALPHA,
subtaskMode = SortHelper.SORT_MANUAL,
sortAscending = false, sortAscending = false,
groupAscending = false, groupAscending = false,
completedAscending = false, completedAscending = false,
subtaskAscending = false,
manualSort = false, manualSort = false,
astridSort = false, astridSort = false,
completedAtBottom = true, completedAtBottom = true,
clickGroupMode = {}, clickGroupMode = {},
clickSortMode = {}, clickSortMode = {},
clickCompletedMode = {}, clickCompletedMode = {},
clickSubtaskMode = {},
setCompletedAtBottom = {}, setCompletedAtBottom = {},
setSortAscending = {}, setSortAscending = {},
setGroupAscending = {}, setGroupAscending = {},
setCompletedAscending = {}, setCompletedAscending = {},
setSubtaskAscending = {},
) )
} }
} }

@ -30,6 +30,8 @@ class SortSettingsViewModel @Inject constructor(
val sortAscending: Boolean, val sortAscending: Boolean,
val completedMode: Int, val completedMode: Int,
val completedAscending: Boolean, val completedAscending: Boolean,
val subtaskMode: Int,
val subtaskAscending: Boolean,
) )
private val widgetId = savedStateHandle[SortSettingsActivity.EXTRA_WIDGET_ID] ?: WIDGET_NONE private val widgetId = savedStateHandle[SortSettingsActivity.EXTRA_WIDGET_ID] ?: WIDGET_NONE
private val preferences = private val preferences =
@ -48,6 +50,8 @@ class SortSettingsViewModel @Inject constructor(
completedAtBottom = preferences.completedTasksAtBottom, completedAtBottom = preferences.completedTasksAtBottom,
sortMode = preferences.sortMode, sortMode = preferences.sortMode,
sortAscending = preferences.sortAscending, sortAscending = preferences.sortAscending,
subtaskMode = preferences.subtaskMode,
subtaskAscending = preferences.subtaskAscending,
) )
private val _viewState = MutableStateFlow(initialState) private val _viewState = MutableStateFlow(initialState)
val state = _viewState.asStateFlow() val state = _viewState.asStateFlow()
@ -67,6 +71,11 @@ class SortSettingsViewModel @Inject constructor(
_viewState.update { it.copy(completedAscending = ascending) } _viewState.update { it.copy(completedAscending = ascending) }
} }
fun setSubtaskAscending(ascending: Boolean) {
preferences.subtaskAscending = ascending
_viewState.update { it.copy(subtaskAscending = ascending) }
}
fun setCompletedAtBottom(completedAtBottom: Boolean) { fun setCompletedAtBottom(completedAtBottom: Boolean) {
preferences.completedTasksAtBottom = completedAtBottom preferences.completedTasksAtBottom = completedAtBottom
_viewState.update { it.copy(completedAtBottom = 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) { fun setManual(value: Boolean) {
preferences.isManualSort = value preferences.isManualSort = value
if (value) { if (value) {

@ -361,6 +361,10 @@ class Preferences @JvmOverloads constructor(
get() = getInt(R.string.p_completed_mode, SortHelper.SORT_COMPLETED) get() = getInt(R.string.p_completed_mode, SortHelper.SORT_COMPLETED)
set(value) { setInt(R.string.p_completed_mode, value) } 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 override var showHidden: Boolean
get() = getBoolean(R.string.p_show_hidden_tasks, true) get() = getBoolean(R.string.p_show_hidden_tasks, true)
set(value) { setBoolean(R.string.p_show_hidden_tasks, value) } 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) get() = getBoolean(R.string.p_completed_ascending, false)
set(value) { setBoolean(R.string.p_completed_ascending, value) } 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 val defaultPriority: Int
get() = getIntegerFromString(R.string.p_default_importance_key, Task.Priority.LOW) get() = getIntegerFromString(R.string.p_default_importance_key, Task.Priority.LOW)

@ -7,6 +7,8 @@ interface QueryPreferences {
var completedMode: Int var completedMode: Int
var subtaskMode: Int
var isManualSort: Boolean var isManualSort: Boolean
var isAstridSort: Boolean var isAstridSort: Boolean
@ -17,6 +19,8 @@ interface QueryPreferences {
var completedAscending: Boolean var completedAscending: Boolean
var subtaskAscending: Boolean
val showHidden: Boolean val showHidden: Boolean
val showCompleted: Boolean val showCompleted: Boolean

@ -64,6 +64,9 @@ class DragAndDropRecyclerAdapter(
override val sortMode: Int override val sortMode: Int
get() = items.groupMode get() = items.groupMode
override val subtaskSortMode: Int
get() = items.subtaskMode
override fun getItemViewType(position: Int) = if (items.isHeader(position)) 1 else 0 override fun getItemViewType(position: Int) = if (items.isHeader(position)) 1 else 0
override fun submitList(list: List<TaskContainer>) { override fun submitList(list: List<TaskContainer>) {
@ -91,11 +94,12 @@ class DragAndDropRecyclerAdapter(
override fun transform(list: List<TaskContainer>): SectionedDataSource = override fun transform(list: List<TaskContainer>): SectionedDataSource =
SectionedDataSource( SectionedDataSource(
list, tasks = list,
disableHeaders, disableHeaders = disableHeaders,
preferences.groupMode, groupMode = preferences.groupMode,
adapter.getCollapsed(), subtaskMode = preferences.subtaskMode,
preferences.completedTasksAtBottom, collapsed = adapter.getCollapsed(),
completedAtBottom = preferences.completedTasksAtBottom,
) )
override fun diff(last: SectionedDataSource, next: SectionedDataSource) = override fun diff(last: SectionedDataSource, next: SectionedDataSource) =

@ -11,6 +11,7 @@ class SectionedDataSource constructor(
tasks: List<TaskContainer>, tasks: List<TaskContainer>,
disableHeaders: Boolean, disableHeaders: Boolean,
val groupMode: Int, val groupMode: Int,
val subtaskMode: Int,
private val collapsed: Set<Long>, private val collapsed: Set<Long>,
private val completedAtBottom: Boolean, private val completedAtBottom: Boolean,
) { ) {

@ -73,10 +73,12 @@ internal class ScrollableViewsFactory(
private var showTags = false private var showTags = false
private var collapsed = mutableSetOf(HEADER_COMPLETED) private var collapsed = mutableSetOf(HEADER_COMPLETED)
private var groupMode = -1 private var groupMode = -1
private var subtaskMode = -1
private var tasks = SectionedDataSource( private var tasks = SectionedDataSource(
emptyList(), emptyList(),
disableHeaders = false, disableHeaders = false,
groupMode = 0, groupMode = SortHelper.GROUP_NONE,
subtaskMode = SortHelper.SORT_MANUAL,
collapsed, collapsed,
preferences.completedTasksAtBottom, preferences.completedTasksAtBottom,
) )
@ -101,6 +103,7 @@ internal class ScrollableViewsFactory(
taskDao.fetchTasks { getQuery(filter) }, taskDao.fetchTasks { getQuery(filter) },
disableGroups, disableGroups,
groupMode, groupMode,
subtaskMode,
collapsed, collapsed,
widgetPreferences.completedTasksAtBottom, widgetPreferences.completedTasksAtBottom,
) )
@ -379,6 +382,7 @@ internal class ScrollableViewsFactory(
} }
groupMode = it groupMode = it
} }
subtaskMode = widgetPreferences.subtaskMode
collapsed = widgetPreferences.collapsed collapsed = widgetPreferences.collapsed
compact = widgetPreferences.compact compact = widgetPreferences.compact
} }

@ -354,4 +354,24 @@ public class WidgetPreferences implements QueryPreferences {
public void setCompletedAscending(boolean ascending) { public void setCompletedAscending(boolean ascending) {
preferences.setCompletedAscending(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);
}
} }

@ -274,12 +274,14 @@
<string name="p_sort_ascending">sort_ascending</string> <string name="p_sort_ascending">sort_ascending</string>
<string name="p_group_ascending">group_ascending</string> <string name="p_group_ascending">group_ascending</string>
<string name="p_completed_ascending">completed_ascending</string> <string name="p_completed_ascending">completed_ascending</string>
<string name="p_subtask_ascending">subtask_ascending</string>
<string name="p_manual_sort">manual_sort</string> <string name="p_manual_sort">manual_sort</string>
<string name="p_astrid_sort">astrid_sort</string> <string name="p_astrid_sort">astrid_sort</string>
<string name="p_astrid_sort_enabled">astrid_sort_enabled</string> <string name="p_astrid_sort_enabled">astrid_sort_enabled</string>
<string name="p_sort_mode">sort_mode</string> <string name="p_sort_mode">sort_mode</string>
<string name="p_group_mode">group_mode</string> <string name="p_group_mode">group_mode</string>
<string name="p_completed_mode">completed_mode</string> <string name="p_completed_mode">completed_mode</string>
<string name="p_subtask_mode">subtask_mode</string>
<string-array name="TEA_control_sets_prefs"> <string-array name="TEA_control_sets_prefs">
<item>@string/TEA_ctrl_hide_until_pref</item> <item>@string/TEA_ctrl_hide_until_pref</item>

Loading…
Cancel
Save