mirror of https://github.com/tasks/tasks
Separate group and sort configurations
parent
79e1a73c2a
commit
7945160536
@ -0,0 +1,30 @@
|
||||
package org.tasks.compose
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleEventObserver
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
|
||||
@Composable
|
||||
fun SystemBars(
|
||||
statusBarColor: Color,
|
||||
navigationBarColor: Color,
|
||||
) {
|
||||
val systemUiController = rememberSystemUiController()
|
||||
val lifecycleOwner = LocalLifecycleOwner.current
|
||||
DisposableEffect(lifecycleOwner) {
|
||||
val observer = LifecycleEventObserver { _, event ->
|
||||
if (event == Lifecycle.Event.ON_START) {
|
||||
systemUiController.setStatusBarColor(color = statusBarColor)
|
||||
systemUiController.setNavigationBarColor(color = navigationBarColor)
|
||||
}
|
||||
}
|
||||
lifecycleOwner.lifecycle.addObserver(observer)
|
||||
onDispose {
|
||||
lifecycleOwner.lifecycle.removeObserver(observer)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,231 +0,0 @@
|
||||
package org.tasks.dialogs;
|
||||
|
||||
import static android.app.Activity.RESULT_OK;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Button;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.todoroo.astrid.api.Filter;
|
||||
import com.todoroo.astrid.core.SortHelper;
|
||||
|
||||
import org.tasks.R;
|
||||
import org.tasks.preferences.Preferences;
|
||||
import org.tasks.preferences.QueryPreferences;
|
||||
import org.tasks.widget.WidgetPreferences;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
import timber.log.Timber;
|
||||
|
||||
@AndroidEntryPoint
|
||||
public class SortDialog extends DialogFragment {
|
||||
|
||||
private static final String EXTRA_MANUAL_ENABLED = "extra_manual_enabled";
|
||||
private static final String EXTRA_ASTRID_ENABLED = "extra_astrid_enabled";
|
||||
private static final String EXTRA_SELECTED_INDEX = "extra_selected_index";
|
||||
private static final String EXTRA_WIDGET_ID = "extra_widget_id";
|
||||
|
||||
@Inject Preferences appPreferences;
|
||||
@Inject DialogBuilder dialogBuilder;
|
||||
|
||||
private QueryPreferences preferences;
|
||||
private boolean manualEnabled;
|
||||
private boolean astridEnabled;
|
||||
private int selectedIndex;
|
||||
private AlertDialog alertDialog;
|
||||
private SortDialogCallback callback;
|
||||
|
||||
public static SortDialog newSortDialog(Filter filter) {
|
||||
SortDialog sortDialog = new SortDialog();
|
||||
Bundle args = new Bundle();
|
||||
args.putBoolean(EXTRA_MANUAL_ENABLED, filter.supportsManualSort());
|
||||
args.putBoolean(EXTRA_ASTRID_ENABLED, filter.supportsAstridSorting());
|
||||
sortDialog.setArguments(args);
|
||||
return sortDialog;
|
||||
}
|
||||
|
||||
public static SortDialog newSortDialog(Fragment target, int rc, Filter filter, int widgetId) {
|
||||
SortDialog dialog = newSortDialog(filter);
|
||||
dialog.setTargetFragment(target, rc);
|
||||
dialog.getArguments().putInt(EXTRA_WIDGET_ID, widgetId);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
onCreate(savedInstanceState);
|
||||
|
||||
Bundle arguments = getArguments();
|
||||
int widgetId = arguments.getInt(EXTRA_WIDGET_ID, -1);
|
||||
preferences = widgetId < 0
|
||||
? appPreferences
|
||||
: new WidgetPreferences(getContext(), appPreferences, widgetId);
|
||||
manualEnabled = arguments.getBoolean(EXTRA_MANUAL_ENABLED);
|
||||
astridEnabled = arguments.getBoolean(EXTRA_ASTRID_ENABLED)
|
||||
&& appPreferences.getBoolean(R.string.p_astrid_sort_enabled, false);
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
selectedIndex = savedInstanceState.getInt(EXTRA_SELECTED_INDEX);
|
||||
} else {
|
||||
selectedIndex = getIndex(preferences.getSortMode());
|
||||
}
|
||||
|
||||
List<String> items = new ArrayList<>();
|
||||
|
||||
if (manualEnabled) {
|
||||
items.add(getString(R.string.SSD_sort_my_order));
|
||||
} else if (astridEnabled) {
|
||||
items.add(getString(R.string.astrid_sort_order));
|
||||
}
|
||||
|
||||
items.add(getString(R.string.SSD_sort_auto));
|
||||
items.add(getString(R.string.SSD_sort_start));
|
||||
items.add(getString(R.string.SSD_sort_due));
|
||||
items.add(getString(R.string.SSD_sort_importance));
|
||||
items.add(getString(R.string.SSD_sort_alpha));
|
||||
items.add(getString(R.string.SSD_sort_modified));
|
||||
items.add(getString(R.string.sort_created));
|
||||
items.add(getString(R.string.sort_list));
|
||||
|
||||
if (manualEnabled) {
|
||||
if (preferences.isManualSort()) {
|
||||
selectedIndex = 0;
|
||||
}
|
||||
} else if (astridEnabled) {
|
||||
if (preferences.isAstridSort()) {
|
||||
selectedIndex = 0;
|
||||
}
|
||||
} else {
|
||||
selectedIndex -= 1;
|
||||
}
|
||||
|
||||
alertDialog =
|
||||
dialogBuilder
|
||||
.newDialog()
|
||||
.setSingleChoiceItems(
|
||||
items,
|
||||
selectedIndex,
|
||||
(dialog, which) -> {
|
||||
selectedIndex = which;
|
||||
enableReverse();
|
||||
})
|
||||
.setPositiveButton(R.string.TLA_menu_sort, (dialog, which) -> setSelection(false))
|
||||
.setNeutralButton(R.string.reverse, (dialog, which) -> setSelection(true))
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show();
|
||||
|
||||
enableReverse();
|
||||
|
||||
return alertDialog;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
|
||||
if (getTargetFragment() == null) {
|
||||
callback = (SortDialogCallback) activity;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
|
||||
outState.putInt(EXTRA_SELECTED_INDEX, selectedIndex);
|
||||
}
|
||||
|
||||
private void enableReverse() {
|
||||
if (manualEnabled) {
|
||||
Button reverse = alertDialog.getButton(DialogInterface.BUTTON_NEUTRAL);
|
||||
reverse.setEnabled(selectedIndex != 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void setSelection(boolean reverse) {
|
||||
preferences.setReverseSort(reverse);
|
||||
|
||||
boolean wasManual = preferences.isManualSort();
|
||||
boolean wasAstrid = preferences.isAstridSort();
|
||||
boolean isManual = manualEnabled && selectedIndex == 0;
|
||||
boolean isAstrid = astridEnabled && selectedIndex == 0;
|
||||
preferences.setManualSort(isManual);
|
||||
preferences.setAstridSort(isAstrid);
|
||||
|
||||
if (!isManual && !isAstrid) {
|
||||
preferences.setSortMode(getSortMode(manualEnabled || astridEnabled ? selectedIndex : selectedIndex + 1));
|
||||
}
|
||||
|
||||
Fragment targetFragment = getTargetFragment();
|
||||
if (targetFragment == null) {
|
||||
callback.sortChanged(wasManual != isManual || wasAstrid != isAstrid);
|
||||
} else {
|
||||
targetFragment.onActivityResult(getTargetRequestCode(), RESULT_OK, null);
|
||||
}
|
||||
}
|
||||
|
||||
private int getIndex(int sortMode) {
|
||||
switch (sortMode) {
|
||||
case SortHelper.SORT_AUTO:
|
||||
return 1;
|
||||
case SortHelper.SORT_START:
|
||||
return 2;
|
||||
case SortHelper.SORT_DUE:
|
||||
return 3;
|
||||
case SortHelper.SORT_IMPORTANCE:
|
||||
return 4;
|
||||
case SortHelper.SORT_ALPHA:
|
||||
return 5;
|
||||
case SortHelper.SORT_MODIFIED:
|
||||
return 6;
|
||||
case SortHelper.SORT_CREATED:
|
||||
return 7;
|
||||
case SortHelper.SORT_LIST:
|
||||
return 8;
|
||||
}
|
||||
|
||||
Timber.e("Invalid sort mode: %s", sortMode);
|
||||
return 1;
|
||||
}
|
||||
|
||||
private int getSortMode(int index) {
|
||||
switch (index) {
|
||||
case 1:
|
||||
return SortHelper.SORT_AUTO;
|
||||
case 2:
|
||||
return SortHelper.SORT_START;
|
||||
case 3:
|
||||
return SortHelper.SORT_DUE;
|
||||
case 4:
|
||||
return SortHelper.SORT_IMPORTANCE;
|
||||
case 5:
|
||||
return SortHelper.SORT_ALPHA;
|
||||
case 6:
|
||||
return SortHelper.SORT_MODIFIED;
|
||||
case 7:
|
||||
return SortHelper.SORT_CREATED;
|
||||
case 8:
|
||||
return SortHelper.SORT_LIST;
|
||||
}
|
||||
|
||||
Timber.e("Invalid sort mode: %s", index);
|
||||
return SortHelper.SORT_ALPHA;
|
||||
}
|
||||
|
||||
public interface SortDialogCallback {
|
||||
void sortChanged(boolean reload);
|
||||
}
|
||||
}
|
@ -0,0 +1,478 @@
|
||||
package org.tasks.dialogs
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Chip
|
||||
import androidx.compose.material.ChipDefaults
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.ArrowDownward
|
||||
import androidx.compose.material.icons.outlined.ArrowUpward
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import com.google.android.material.composethemeadapter.MdcTheme
|
||||
import com.todoroo.astrid.core.SortHelper
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.launch
|
||||
import org.tasks.R
|
||||
import org.tasks.compose.SystemBars
|
||||
import org.tasks.compose.collectAsStateLifecycleAware
|
||||
|
||||
@AndroidEntryPoint
|
||||
class SortSettingsActivity : ComponentActivity() {
|
||||
|
||||
private val viewModel: SortSettingsViewModel by viewModels()
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
window.statusBarColor = ContextCompat.getColor(this, android.R.color.transparent)
|
||||
setContent {
|
||||
MdcTheme {
|
||||
val scrimColor = if (isSystemInDarkTheme())
|
||||
Color(0x52454545)
|
||||
else
|
||||
MaterialTheme.colors.onSurface.copy(.5f)
|
||||
// edge-to-edge potentially fixed in material3 v1.2.0
|
||||
SystemBars(
|
||||
statusBarColor = scrimColor,
|
||||
navigationBarColor = MaterialTheme.colors.surface,
|
||||
)
|
||||
val state = viewModel.state.collectAsStateLifecycleAware().value
|
||||
val mainSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||
val scope = rememberCoroutineScope()
|
||||
var showGroupPicker by remember { mutableStateOf(false) }
|
||||
var showSortPicker by remember { mutableStateOf(false) }
|
||||
ModalBottomSheet(
|
||||
onDismissRequest = {
|
||||
val forceReload = viewModel.forceReload
|
||||
setResult(
|
||||
RESULT_OK,
|
||||
Intent().putExtra(EXTRA_FORCE_RELOAD, forceReload)
|
||||
)
|
||||
finish()
|
||||
overridePendingTransition(0, 0)
|
||||
},
|
||||
sheetState = mainSheetState,
|
||||
containerColor = MaterialTheme.colors.surface,
|
||||
scrimColor = scrimColor,
|
||||
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
|
||||
content = {
|
||||
BottomSheetContent(
|
||||
groupMode = state.groupMode,
|
||||
sortMode = state.sortMode,
|
||||
sortAscending = state.sortAscending,
|
||||
groupAscending = state.groupAscending,
|
||||
manualSort = state.manualSort && manualEnabled,
|
||||
astridSort = state.astridSort && astridEnabled,
|
||||
setSortAscending = { viewModel.setSortAscending(it) },
|
||||
setGroupAscending = { viewModel.setGroupAscending(it) },
|
||||
clickGroupMode = { showGroupPicker = true },
|
||||
clickSortMode = { showSortPicker = true },
|
||||
)
|
||||
}
|
||||
)
|
||||
if (showGroupPicker) {
|
||||
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||
val closePicker: () -> Unit = {
|
||||
scope.launch {
|
||||
sheetState.hide()
|
||||
showGroupPicker = false
|
||||
}
|
||||
}
|
||||
ModalBottomSheet(
|
||||
onDismissRequest = closePicker,
|
||||
sheetState = sheetState,
|
||||
containerColor = MaterialTheme.colors.surface,
|
||||
scrimColor = Color.Transparent,
|
||||
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
|
||||
content = {
|
||||
GroupSheetContent(
|
||||
selected = state.groupMode,
|
||||
onSelected = {
|
||||
viewModel.setGroupMode(it)
|
||||
closePicker()
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
LaunchedEffect(Unit) {
|
||||
sheetState.show()
|
||||
}
|
||||
}
|
||||
if (showSortPicker) {
|
||||
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||
val closePicker: () -> Unit = {
|
||||
scope.launch {
|
||||
sheetState.hide()
|
||||
showSortPicker = false
|
||||
}
|
||||
}
|
||||
ModalBottomSheet(
|
||||
onDismissRequest = closePicker,
|
||||
sheetState = sheetState,
|
||||
containerColor = MaterialTheme.colors.surface,
|
||||
scrimColor = Color.Transparent,
|
||||
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
|
||||
content = {
|
||||
SortSheetContent(
|
||||
manualSortEnabled = manualEnabled,
|
||||
astridSortEnabled = astridEnabled,
|
||||
setManualSort = {
|
||||
viewModel.setManual(true)
|
||||
closePicker()
|
||||
},
|
||||
setAstridSort = {
|
||||
viewModel.setAstrid(true)
|
||||
closePicker()
|
||||
},
|
||||
manualSortSelected = (manualEnabled && state.manualSort) || (astridEnabled && state.astridSort),
|
||||
selected = state.sortMode,
|
||||
onSelected = {
|
||||
viewModel.setSortMode(it)
|
||||
closePicker()
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
LaunchedEffect(Unit) {
|
||||
sheetState.show()
|
||||
}
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
mainSheetState.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val manualEnabled: Boolean by lazy {
|
||||
intent.getBooleanExtra(EXTRA_MANUAL_ORDER, false)
|
||||
}
|
||||
|
||||
private val astridEnabled: Boolean by lazy {
|
||||
intent.getBooleanExtra(EXTRA_ASTRID_ORDER, false)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val EXTRA_MANUAL_ORDER = "extra_manual_order"
|
||||
const val EXTRA_ASTRID_ORDER = "extra_astrid_order"
|
||||
const val EXTRA_WIDGET_ID = "extra_widget_id"
|
||||
const val EXTRA_FORCE_RELOAD = "extra_force_reload"
|
||||
const val WIDGET_NONE = -1
|
||||
|
||||
fun getIntent(
|
||||
context: Context,
|
||||
manualOrderEnabled: Boolean,
|
||||
astridOrderEnabled: Boolean,
|
||||
widgetId: Int? = null,
|
||||
) = Intent(context, SortSettingsActivity::class.java)
|
||||
.addFlags(FLAG_ACTIVITY_NO_ANIMATION)
|
||||
.putExtra(EXTRA_MANUAL_ORDER, manualOrderEnabled)
|
||||
.putExtra(EXTRA_ASTRID_ORDER, astridOrderEnabled)
|
||||
.apply {
|
||||
widgetId?.let {
|
||||
putExtra(EXTRA_WIDGET_ID, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SortSheetContent(
|
||||
manualSortSelected: Boolean,
|
||||
manualSortEnabled: Boolean,
|
||||
astridSortEnabled: Boolean,
|
||||
selected: Int,
|
||||
setManualSort: (Boolean) -> Unit,
|
||||
setAstridSort: (Boolean) -> Unit,
|
||||
onSelected: (Int) -> Unit,
|
||||
) {
|
||||
if (manualSortEnabled) {
|
||||
SortOption(resId = R.string.SSD_sort_my_order, selected = manualSortSelected) {
|
||||
setManualSort(true)
|
||||
}
|
||||
}
|
||||
if (astridSortEnabled) {
|
||||
SortOption(resId = R.string.astrid_sort_order, selected = manualSortSelected) {
|
||||
setAstridSort(true)
|
||||
}
|
||||
}
|
||||
SortOption(
|
||||
resId = R.string.SSD_sort_auto,
|
||||
selected = !manualSortSelected && selected == SortHelper.SORT_AUTO,
|
||||
onClick = { onSelected(SortHelper.SORT_AUTO) }
|
||||
)
|
||||
SortOption(
|
||||
resId = R.string.SSD_sort_start,
|
||||
selected = !manualSortSelected && selected == SortHelper.SORT_START,
|
||||
onClick = { onSelected(SortHelper.SORT_START) }
|
||||
)
|
||||
SortOption(
|
||||
resId = R.string.SSD_sort_due,
|
||||
selected = !manualSortSelected && selected == SortHelper.SORT_DUE,
|
||||
onClick = { onSelected(SortHelper.SORT_DUE) }
|
||||
)
|
||||
SortOption(
|
||||
resId = R.string.SSD_sort_importance,
|
||||
selected = !manualSortSelected && selected == SortHelper.SORT_IMPORTANCE,
|
||||
onClick = { onSelected(SortHelper.SORT_IMPORTANCE) }
|
||||
)
|
||||
SortOption(
|
||||
resId = R.string.SSD_sort_alpha,
|
||||
selected = !manualSortSelected && selected == SortHelper.SORT_ALPHA,
|
||||
onClick = { onSelected(SortHelper.SORT_ALPHA) }
|
||||
)
|
||||
SortOption(
|
||||
resId = R.string.SSD_sort_modified,
|
||||
selected = !manualSortSelected && selected == SortHelper.SORT_MODIFIED,
|
||||
onClick = { onSelected(SortHelper.SORT_MODIFIED) }
|
||||
)
|
||||
SortOption(
|
||||
resId = R.string.sort_created,
|
||||
selected = !manualSortSelected && selected == SortHelper.SORT_CREATED,
|
||||
onClick = { onSelected(SortHelper.SORT_CREATED) }
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun GroupSheetContent(
|
||||
selected: Int,
|
||||
onSelected: (Int) -> Unit,
|
||||
) {
|
||||
SortOption(
|
||||
resId = R.string.none,
|
||||
selected = selected == SortHelper.GROUP_NONE,
|
||||
onClick = { onSelected(SortHelper.GROUP_NONE) }
|
||||
)
|
||||
SortOption(
|
||||
resId = R.string.SSD_sort_due,
|
||||
selected = selected == SortHelper.SORT_DUE,
|
||||
onClick = { onSelected(SortHelper.SORT_DUE) }
|
||||
)
|
||||
SortOption(
|
||||
resId = R.string.SSD_sort_start,
|
||||
selected = selected == SortHelper.SORT_START,
|
||||
onClick = { onSelected(SortHelper.SORT_START) }
|
||||
)
|
||||
SortOption(
|
||||
resId = R.string.SSD_sort_importance,
|
||||
selected = selected == SortHelper.SORT_IMPORTANCE,
|
||||
onClick = { onSelected(SortHelper.SORT_IMPORTANCE) }
|
||||
)
|
||||
SortOption(
|
||||
resId = R.string.SSD_sort_modified,
|
||||
selected = selected == SortHelper.SORT_MODIFIED,
|
||||
onClick = { onSelected(SortHelper.SORT_MODIFIED) }
|
||||
)
|
||||
SortOption(
|
||||
resId = R.string.sort_created,
|
||||
selected = selected == SortHelper.SORT_CREATED,
|
||||
onClick = { onSelected(SortHelper.SORT_CREATED) }
|
||||
)
|
||||
SortOption(
|
||||
resId = R.string.sort_list,
|
||||
selected = selected == SortHelper.SORT_LIST,
|
||||
onClick = { onSelected(SortHelper.SORT_LIST) }
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SortOption(
|
||||
resId: Int,
|
||||
selected: Boolean,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { onClick() }
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||
text = stringResource(id = resId),
|
||||
style = MaterialTheme.typography.h6.copy(
|
||||
color = if (selected) MaterialTheme.colors.primary else MaterialTheme.colors.onSurface,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun BottomSheetContent(
|
||||
groupMode: Int,
|
||||
sortMode: Int,
|
||||
sortAscending: Boolean,
|
||||
groupAscending: Boolean,
|
||||
manualSort: Boolean,
|
||||
astridSort: Boolean,
|
||||
setSortAscending: (Boolean) -> Unit,
|
||||
setGroupAscending: (Boolean) -> Unit,
|
||||
clickGroupMode: () -> Unit,
|
||||
clickSortMode: () -> Unit,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.clickable { clickGroupMode() }
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.sort_grouping),
|
||||
style = MaterialTheme.typography.h6,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(id = groupMode.modeString),
|
||||
style = MaterialTheme.typography.body1,
|
||||
)
|
||||
}
|
||||
if (groupMode != SortHelper.GROUP_NONE) {
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
val displayAscending = when (groupMode) {
|
||||
SortHelper.SORT_IMPORTANCE -> !groupAscending
|
||||
else -> groupAscending
|
||||
}
|
||||
Chip(
|
||||
onClick = { setGroupAscending(!groupAscending) },
|
||||
shape = RoundedCornerShape(4.dp),
|
||||
border = ChipDefaults.outlinedBorder,
|
||||
colors = ChipDefaults.outlinedChipColors(),
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
imageVector = if (displayAscending) Icons.Outlined.ArrowUpward else Icons.Outlined.ArrowDownward,
|
||||
modifier = Modifier.size(16.dp),
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
content = {
|
||||
Text(
|
||||
text = stringResource(id = if (displayAscending) R.string.sort_ascending else R.string.sort_descending),
|
||||
style = MaterialTheme.typography.body1,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.clickable { clickSortMode() }
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.sort_sorting),
|
||||
style = MaterialTheme.typography.h6,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(
|
||||
id = when {
|
||||
manualSort -> R.string.SSD_sort_my_order
|
||||
astridSort -> R.string.astrid_sort_order
|
||||
else -> sortMode.modeString
|
||||
}
|
||||
),
|
||||
style = MaterialTheme.typography.body1,
|
||||
)
|
||||
}
|
||||
if (!(manualSort || astridSort)) {
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
val displayAscending = when (sortMode) {
|
||||
SortHelper.SORT_AUTO,
|
||||
SortHelper.SORT_IMPORTANCE -> !sortAscending
|
||||
else -> sortAscending
|
||||
}
|
||||
Chip(
|
||||
onClick = { setSortAscending(!sortAscending) },
|
||||
shape = RoundedCornerShape(4.dp),
|
||||
border = ChipDefaults.outlinedBorder,
|
||||
colors = ChipDefaults.outlinedChipColors(),
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
imageVector = if (displayAscending) Icons.Outlined.ArrowUpward else Icons.Outlined.ArrowDownward,
|
||||
modifier = Modifier.size(16.dp),
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
content = {
|
||||
Text(
|
||||
text = stringResource(id = if (displayAscending) R.string.sort_ascending else R.string.sort_descending),
|
||||
style = MaterialTheme.typography.body1,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val Int.modeString: Int
|
||||
get() = when (this) {
|
||||
SortHelper.GROUP_NONE -> R.string.none
|
||||
SortHelper.SORT_ALPHA -> R.string.SSD_sort_alpha
|
||||
SortHelper.SORT_DUE -> R.string.SSD_sort_due
|
||||
SortHelper.SORT_IMPORTANCE -> R.string.SSD_sort_importance
|
||||
SortHelper.SORT_MODIFIED -> R.string.SSD_sort_modified
|
||||
SortHelper.SORT_CREATED -> R.string.sort_created
|
||||
SortHelper.SORT_START -> R.string.SSD_sort_start
|
||||
SortHelper.SORT_LIST -> R.string.sort_list
|
||||
else -> R.string.SSD_sort_auto
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
fun PreviewSortBottomSheet() {
|
||||
MdcTheme {
|
||||
BottomSheetContent(
|
||||
groupMode = SortHelper.GROUP_NONE,
|
||||
sortMode = SortHelper.SORT_AUTO,
|
||||
sortAscending = false,
|
||||
groupAscending = false,
|
||||
manualSort = false,
|
||||
astridSort = false,
|
||||
clickGroupMode = {},
|
||||
clickSortMode = {},
|
||||
setSortAscending = {},
|
||||
setGroupAscending = {},
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,130 @@
|
||||
package org.tasks.dialogs
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.todoroo.astrid.core.SortHelper
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import org.tasks.dialogs.SortSettingsActivity.Companion.WIDGET_NONE
|
||||
import org.tasks.preferences.Preferences
|
||||
import org.tasks.widget.WidgetPreferences
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class SortSettingsViewModel @Inject constructor(
|
||||
@ApplicationContext context: Context,
|
||||
savedStateHandle: SavedStateHandle,
|
||||
private val appPreferences: Preferences,
|
||||
): ViewModel() {
|
||||
data class ViewState(
|
||||
val manualSort: Boolean,
|
||||
val astridSort: Boolean,
|
||||
val groupMode: Int,
|
||||
val groupAscending: Boolean,
|
||||
val sortMode: Int,
|
||||
val sortAscending: Boolean,
|
||||
)
|
||||
private val widgetId = savedStateHandle[SortSettingsActivity.EXTRA_WIDGET_ID] ?: WIDGET_NONE
|
||||
private val preferences =
|
||||
widgetId
|
||||
.takeIf { it != WIDGET_NONE }
|
||||
?.let { WidgetPreferences(context, appPreferences, it) }
|
||||
?: appPreferences
|
||||
|
||||
private val initialState = ViewState(
|
||||
manualSort = preferences.isManualSort,
|
||||
astridSort = preferences.isAstridSort,
|
||||
groupMode = preferences.groupMode,
|
||||
groupAscending = preferences.groupAscending,
|
||||
sortMode = preferences.sortMode,
|
||||
sortAscending = preferences.sortAscending,
|
||||
)
|
||||
private val _viewState = MutableStateFlow(initialState)
|
||||
val state = _viewState.asStateFlow()
|
||||
|
||||
fun setSortAscending(ascending: Boolean) {
|
||||
preferences.sortAscending = ascending
|
||||
_viewState.update { it.copy(sortAscending = ascending) }
|
||||
}
|
||||
|
||||
fun setGroupAscending(ascending: Boolean) {
|
||||
preferences.groupAscending = ascending
|
||||
_viewState.update { it.copy(groupAscending = ascending) }
|
||||
}
|
||||
|
||||
fun setGroupMode(groupMode: Int) {
|
||||
if (groupMode != SortHelper.GROUP_NONE) {
|
||||
preferences.isManualSort = false
|
||||
preferences.isAstridSort = false
|
||||
}
|
||||
preferences.groupMode = groupMode
|
||||
val ascending = when (groupMode) {
|
||||
SortHelper.SORT_MODIFIED,
|
||||
SortHelper.SORT_CREATED -> false
|
||||
else -> true
|
||||
}
|
||||
preferences.groupAscending = ascending
|
||||
_viewState.update {
|
||||
it.copy(
|
||||
manualSort = preferences.isManualSort,
|
||||
astridSort = preferences.isAstridSort,
|
||||
groupMode = groupMode,
|
||||
groupAscending = ascending,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun setSortMode(sortMode: Int) {
|
||||
preferences.isManualSort = false
|
||||
preferences.isAstridSort = false
|
||||
preferences.sortMode = sortMode
|
||||
val ascending = when (sortMode) {
|
||||
SortHelper.SORT_MODIFIED,
|
||||
SortHelper.SORT_CREATED -> false
|
||||
else -> true
|
||||
}
|
||||
preferences.sortAscending = ascending
|
||||
_viewState.update {
|
||||
it.copy(
|
||||
manualSort = false,
|
||||
astridSort = false,
|
||||
sortMode = sortMode,
|
||||
sortAscending = ascending,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun setManual(value: Boolean) {
|
||||
preferences.isManualSort = value
|
||||
if (value) {
|
||||
preferences.groupMode = SortHelper.GROUP_NONE
|
||||
}
|
||||
_viewState.update {
|
||||
it.copy(
|
||||
groupMode = if (value) SortHelper.GROUP_NONE else it.groupMode,
|
||||
manualSort = value,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun setAstrid(value: Boolean) {
|
||||
preferences.isAstridSort = value
|
||||
if (value) {
|
||||
preferences.groupMode = SortHelper.GROUP_NONE
|
||||
}
|
||||
_viewState.update {
|
||||
it.copy(
|
||||
groupMode = if (value) SortHelper.GROUP_NONE else it.groupMode,
|
||||
astridSort = value,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val forceReload: Boolean
|
||||
get() = initialState.manualSort != _viewState.value.manualSort
|
||||
|| initialState.astridSort != _viewState.value.astridSort
|
||||
}
|
Loading…
Reference in New Issue