From b5748aa8e6d13bcb0da16abd47491e4d6e865597 Mon Sep 17 00:00:00 2001 From: Alex Baker Date: Fri, 29 Sep 2023 01:45:15 -0500 Subject: [PATCH] New drawer --- app/build.gradle.kts | 1 + .../todoroo/astrid/activity/MainActivity.kt | 174 ++++++++-- .../astrid/activity/MainActivityViewModel.kt | 174 ++++++++++ .../astrid/adapter/ActionViewHolder.kt | 37 -- .../astrid/adapter/NavigationDrawerAdapter.kt | 14 +- .../astrid/adapter/SeparatorViewHolder.kt | 6 - .../astrid/adapter/SubheaderClickHandler.kt | 6 +- .../com/todoroo/astrid/api/CaldavFilter.kt | 2 +- .../com/todoroo/astrid/api/CustomFilter.kt | 2 - .../java/com/todoroo/astrid/api/Filter.kt | 5 +- .../com/todoroo/astrid/api/FilterListItem.kt | 2 - .../com/todoroo/astrid/api/GtasksFilter.kt | 2 +- .../com/todoroo/astrid/api/SearchFilter.kt | 2 - .../java/com/todoroo/astrid/api/TagFilter.kt | 2 +- .../astrid/core/BuiltInFilterExposer.kt | 18 +- .../NavigationDrawerCustomization.kt | 5 - .../org/tasks/compose/drawer/DrawerAction.kt | 5 + .../org/tasks/compose/drawer/DrawerItem.kt | 22 ++ .../tasks/compose/drawer/TaskListDrawer.kt | 318 ++++++++++++++++++ .../java/org/tasks/dialogs/NewFilterDialog.kt | 4 +- .../main/java/org/tasks/extensions/Number.kt | 11 - .../java/org/tasks/filters/CaldavFilters.kt | 13 +- .../java/org/tasks/filters/FilterProvider.kt | 69 +--- .../org/tasks/filters/GoogleTaskFilters.kt | 9 +- .../java/org/tasks/filters/LocationFilters.kt | 9 +- .../java/org/tasks/filters/MyTasksFilter.kt | 2 - .../tasks/filters/NavigationDrawerAction.kt | 15 - .../filters/NavigationDrawerSeparator.kt | 11 - .../org/tasks/filters/NotificationsFilter.kt | 2 - .../java/org/tasks/filters/PlaceFilter.kt | 2 +- .../tasks/filters/RecentlyModifiedFilter.kt | 2 - .../java/org/tasks/filters/SnoozedFilter.kt | 2 - .../main/java/org/tasks/filters/TagFilters.kt | 9 +- .../java/org/tasks/filters/TodayFilter.kt | 2 - .../preferences/fragments/LookAndFeel.kt | 2 +- .../org/tasks/ui/NavigationDrawerFragment.kt | 122 ------- .../org/tasks/ui/NavigationDrawerViewModel.kt | 75 ----- .../res/layout-w820dp/task_list_activity.xml | 5 + .../main/res/layout/filter_adapter_action.xml | 47 --- .../res/layout/filter_adapter_separator.xml | 15 - .../res/layout/fragment_navigation_drawer.xml | 8 - .../main/res/layout/task_list_activity.xml | 5 + app/src/main/res/values/styles.xml | 15 - .../app_googleplayDebug-classes.txt | 135 ++++---- .../app_googleplayDebug-composables.txt | 36 ++ .../app_googleplayDebug-module.json | 40 +-- deps_fdroid.txt | 4 + deps_googleplay.txt | 4 + gradle/libs.versions.toml | 1 + 49 files changed, 850 insertions(+), 623 deletions(-) create mode 100644 app/src/main/java/com/todoroo/astrid/activity/MainActivityViewModel.kt delete mode 100644 app/src/main/java/com/todoroo/astrid/adapter/ActionViewHolder.kt delete mode 100644 app/src/main/java/com/todoroo/astrid/adapter/SeparatorViewHolder.kt create mode 100644 app/src/main/java/org/tasks/compose/drawer/DrawerAction.kt create mode 100644 app/src/main/java/org/tasks/compose/drawer/DrawerItem.kt create mode 100644 app/src/main/java/org/tasks/compose/drawer/TaskListDrawer.kt delete mode 100644 app/src/main/java/org/tasks/extensions/Number.kt delete mode 100644 app/src/main/java/org/tasks/filters/NavigationDrawerAction.kt delete mode 100644 app/src/main/java/org/tasks/filters/NavigationDrawerSeparator.kt delete mode 100644 app/src/main/java/org/tasks/ui/NavigationDrawerFragment.kt delete mode 100644 app/src/main/java/org/tasks/ui/NavigationDrawerViewModel.kt delete mode 100644 app/src/main/res/layout/filter_adapter_action.xml delete mode 100644 app/src/main/res/layout/filter_adapter_separator.xml delete mode 100644 app/src/main/res/layout/fragment_navigation_drawer.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 7fe8d82e2..b2c53a699 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -207,6 +207,7 @@ dependencies { debugImplementation(libs.kotlin.reflect) implementation(libs.kotlin.jdk8) + implementation(libs.kotlin.immutable) implementation(libs.okhttp) implementation(libs.persistent.cookiejar) implementation(libs.gson) diff --git a/app/src/main/java/com/todoroo/astrid/activity/MainActivity.kt b/app/src/main/java/com/todoroo/astrid/activity/MainActivity.kt index 9a08742e4..d71544d19 100644 --- a/app/src/main/java/com/todoroo/astrid/activity/MainActivity.kt +++ b/app/src/main/java/com/todoroo/astrid/activity/MainActivity.kt @@ -8,12 +8,23 @@ package com.todoroo.astrid.activity import android.content.Intent import android.os.Bundle import android.view.View +import androidx.activity.result.contract.ActivityResultContracts +import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.view.ActionMode +import androidx.compose.material.MaterialTheme +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.rememberModalBottomSheetState +import androidx.compose.runtime.rememberCoroutineScope +import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import com.google.android.material.composethemeadapter.MdcTheme import com.todoroo.andlib.utility.AndroidUtilities import com.todoroo.astrid.activity.TaskEditFragment.Companion.newTaskEditFragment import com.todoroo.astrid.activity.TaskListFragment.TaskListFragmentCallbackHandler +import com.todoroo.astrid.adapter.SubheaderClickHandler import com.todoroo.astrid.api.Filter import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.data.Task @@ -29,20 +40,32 @@ import kotlinx.coroutines.withContext import org.tasks.BuildConfig import org.tasks.LocalBroadcastManager import org.tasks.R +import org.tasks.Tasks.Companion.IS_GENERIC +import org.tasks.activities.NavigationDrawerCustomization import org.tasks.analytics.Firebase import org.tasks.billing.Inventory +import org.tasks.billing.PurchaseActivity +import org.tasks.compose.collectAsStateLifecycleAware +import org.tasks.compose.drawer.DrawerAction +import org.tasks.compose.drawer.DrawerItem +import org.tasks.compose.drawer.TaskListDrawer import org.tasks.data.AlarmDao import org.tasks.data.LocationDao import org.tasks.data.Place import org.tasks.data.TagDataDao import org.tasks.databinding.TaskListActivityBinding +import org.tasks.dialogs.NewFilterDialog import org.tasks.dialogs.WhatsNewDialog import org.tasks.extensions.Context.nightMode +import org.tasks.extensions.Context.openUri import org.tasks.extensions.hideKeyboard +import org.tasks.filters.FilterProvider import org.tasks.filters.PlaceFilter import org.tasks.intents.TaskIntents.getTaskListIntent import org.tasks.location.LocationPickerActivity import org.tasks.preferences.DefaultFilterProvider +import org.tasks.preferences.HelpAndFeedback +import org.tasks.preferences.MainPreferences import org.tasks.preferences.Preferences import org.tasks.themes.ColorProvider import org.tasks.themes.Theme @@ -50,8 +73,6 @@ import org.tasks.themes.ThemeColor import org.tasks.ui.EmptyTaskEditFragment.Companion.newEmptyTaskEditFragment import org.tasks.ui.MainActivityEvent import org.tasks.ui.MainActivityEventBus -import org.tasks.ui.NavigationDrawerFragment -import org.tasks.ui.NavigationDrawerFragment.Companion.newNavigationDrawer import timber.log.Timber import javax.inject.Inject @@ -71,14 +92,23 @@ class MainActivity : AppCompatActivity(), TaskListFragmentCallbackHandler { @Inject lateinit var eventBus: MainActivityEventBus @Inject lateinit var firebase: Firebase + private val viewModel: MainActivityViewModel by viewModels() private var currentNightMode = 0 private var currentPro = false - private var filter: Filter? = null private var actionMode: ActionMode? = null private lateinit var binding: TaskListActivityBinding + private val filter: Filter? + get() = viewModel.state.value.filter + + private val settingsRequest = + registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { + recreate() + } + /** @see android.app.Activity.onCreate */ + @OptIn(ExperimentalMaterial3Api::class) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) theme.applyTheme(this) @@ -86,15 +116,112 @@ class MainActivity : AppCompatActivity(), TaskListFragmentCallbackHandler { currentPro = inventory.hasPro binding = TaskListActivityBinding.inflate(layoutInflater) setContentView(binding.root) - if (savedInstanceState != null) { - filter = savedInstanceState.getParcelable(EXTRA_FILTER) - applyTheme() - } handleIntent() + binding.composeView.setContent { + val state = viewModel.state.collectAsStateLifecycleAware().value + if (state.drawerOpen) { + MdcTheme { + val sheetState = rememberModalBottomSheetState( + skipPartiallyExpanded = preferences.isTopAppBar, + ) + ModalBottomSheet( + sheetState = sheetState, + containerColor = MaterialTheme.colors.surface, + onDismissRequest = { viewModel.setDrawerOpen(false) } + ) { + val scope = rememberCoroutineScope() + TaskListDrawer( + begForMoney = state.begForMoney, + filters = state.drawerItems, + onClick = { + when (it) { + is DrawerItem.Filter -> { + openTaskListFragment(it.type()) + scope.launch(Dispatchers.Default) { + sheetState.hide() + viewModel.setDrawerOpen(false) + } + } + is DrawerItem.Header -> { + viewModel.toggleCollapsed(it.type()) + } + } + }, + onAddClick = { + scope.launch(Dispatchers.Default) { + sheetState.hide() + viewModel.setDrawerOpen(false) + val subheaderType = it.type() + val rc = subheaderType.addIntentRc + if (rc == FilterProvider.REQUEST_NEW_FILTER) { + NewFilterDialog.newFilterDialog().show( + supportFragmentManager, + SubheaderClickHandler.FRAG_TAG_NEW_FILTER + ) + } else { + val intent = subheaderType.addIntent ?: return@launch + startActivityForResult(intent, rc) + } + } + }, + onDrawerAction = { + viewModel.setDrawerOpen(false) + when (it) { + DrawerAction.PURCHASE -> + if (IS_GENERIC) + openUri(R.string.url_donate) + else + startActivity( + Intent( + this@MainActivity, + PurchaseActivity::class.java + ) + ) + + DrawerAction.CUSTOMIZE_DRAWER -> + startActivity( + Intent( + this@MainActivity, + NavigationDrawerCustomization::class.java + ) + ) + + DrawerAction.SETTINGS -> + settingsRequest.launch( + Intent( + this@MainActivity, + MainPreferences::class.java + ) + ) + + DrawerAction.HELP_AND_FEEDBACK -> + startActivity( + Intent( + this@MainActivity, + HelpAndFeedback::class.java + ) + ) + } + }, + onErrorClick = { + startActivity(Intent(this@MainActivity, MainPreferences::class.java)) + }, + ) + } + } + } + } + eventBus .onEach(this::process) .launchIn(lifecycleScope) + + lifecycleScope.launch { + lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) { + applyTheme() + } + } } private suspend fun process(event: MainActivityEvent) = when (event) { @@ -106,14 +233,13 @@ class MainActivity : AppCompatActivity(), TaskListFragmentCallbackHandler { public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { when (requestCode) { - NavigationDrawerFragment.REQUEST_SETTINGS -> recreate() - NavigationDrawerFragment.REQUEST_NEW_LIST -> + REQUEST_NEW_LIST -> if (resultCode == RESULT_OK) { data ?.getParcelableExtra(OPEN_FILTER) ?.let { startActivity(getTaskListIntent(this, it)) } } - NavigationDrawerFragment.REQUEST_NEW_PLACE -> + REQUEST_NEW_PLACE -> if (resultCode == RESULT_OK) { data ?.getParcelableExtra(LocationPickerActivity.EXTRA_PLACE) @@ -130,14 +256,9 @@ class MainActivity : AppCompatActivity(), TaskListFragmentCallbackHandler { handleIntent() } - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - outState.putParcelable(EXTRA_FILTER, filter) - } - private fun clearUi() { finishActionMode() - navigationDrawer?.dismiss() + viewModel.setDrawerOpen(false) } private suspend fun getTaskToLoad(filter: Filter?): Task? { @@ -274,8 +395,10 @@ class MainActivity : AppCompatActivity(), TaskListFragmentCallbackHandler { } private fun setFilter(newFilter: Filter?) { - filter = newFilter - applyTheme() + newFilter?.let { + viewModel.setFilter(it) + applyTheme() + } } private fun openTaskListFragment(filter: Filter?, force: Boolean = false) { @@ -291,8 +414,7 @@ class MainActivity : AppCompatActivity(), TaskListFragmentCallbackHandler { if (!force && filter == newFilter) { return } - filter = newFilter - defaultFilterProvider.lastViewedFilter = newFilter + viewModel.setFilter(newFilter) applyTheme() supportFragmentManager .beginTransaction() @@ -308,10 +430,8 @@ class MainActivity : AppCompatActivity(), TaskListFragmentCallbackHandler { } private val filterColor: ThemeColor - get() = if (filter != null && filter!!.tint != 0) colorProvider.getThemeColor(filter!!.tint, true) else theme.themeColor - - private val navigationDrawer: NavigationDrawerFragment? - get() = supportFragmentManager.findFragmentByTag(FRAG_TAG_NAV_DRAWER) as? NavigationDrawerFragment + get() = filter?.tint?.takeIf { it != 0 } + ?.let { colorProvider.getThemeColor(it, true) } ?: theme.themeColor override fun onResume() { super.onResume() @@ -362,7 +482,7 @@ class MainActivity : AppCompatActivity(), TaskListFragmentCallbackHandler { override fun onNavigationIconClicked() { hideKeyboard() - newNavigationDrawer(filter).show(supportFragmentManager, FRAG_TAG_NAV_DRAWER) + viewModel.setDrawerOpen(true) } private val taskListFragment: TaskListFragment? @@ -413,10 +533,10 @@ class MainActivity : AppCompatActivity(), TaskListFragmentCallbackHandler { const val FINISH_AFFINITY = "finish_affinity" private const val FRAG_TAG_TASK_LIST = "frag_tag_task_list" private const val FRAG_TAG_WHATS_NEW = "frag_tag_whats_new" - private const val FRAG_TAG_NAV_DRAWER = "frag_tag_nav_drawer" - private const val EXTRA_FILTER = "extra_filter" private const val FLAG_FROM_HISTORY = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY + const val REQUEST_NEW_LIST = 10100 + const val REQUEST_NEW_PLACE = 10104 val Intent.getFilter: Filter? get() = if (isFromHistory) { diff --git a/app/src/main/java/com/todoroo/astrid/activity/MainActivityViewModel.kt b/app/src/main/java/com/todoroo/astrid/activity/MainActivityViewModel.kt new file mode 100644 index 000000000..d9558741e --- /dev/null +++ b/app/src/main/java/com/todoroo/astrid/activity/MainActivityViewModel.kt @@ -0,0 +1,174 @@ +package com.todoroo.astrid.activity + +import android.annotation.SuppressLint +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.todoroo.astrid.api.CaldavFilter +import com.todoroo.astrid.api.CustomFilter +import com.todoroo.astrid.api.Filter +import com.todoroo.astrid.api.Filter.Companion.NO_COUNT +import com.todoroo.astrid.api.GtasksFilter +import com.todoroo.astrid.api.TagFilter +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toPersistentList +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import org.tasks.LocalBroadcastManager +import org.tasks.R +import org.tasks.Tasks.Companion.IS_GENERIC +import org.tasks.billing.Inventory +import org.tasks.compose.drawer.DrawerItem +import org.tasks.data.CaldavDao +import org.tasks.data.TaskDao +import org.tasks.filters.FilterProvider +import org.tasks.filters.NavigationDrawerSubheader +import org.tasks.filters.PlaceFilter +import org.tasks.preferences.DefaultFilterProvider +import org.tasks.preferences.Preferences +import org.tasks.themes.ColorProvider +import org.tasks.themes.CustomIcons +import timber.log.Timber +import javax.inject.Inject + +@HiltViewModel +@SuppressLint("StaticFieldLeak") +class MainActivityViewModel @Inject constructor( + private val defaultFilterProvider: DefaultFilterProvider, + private val filterProvider: FilterProvider, + private val taskDao: TaskDao, + private val localBroadcastManager: LocalBroadcastManager, + private val inventory: Inventory, + private val colorProvider: ColorProvider, + private val caldavDao: CaldavDao, + private val preferences: Preferences, +) : ViewModel() { + + data class State( + val begForMoney: Boolean = false, + val filter: Filter? = null, + val drawerOpen: Boolean = false, + val drawerItems: ImmutableList = persistentListOf(), + ) + + private val _state = MutableStateFlow(State()) + val state = _state.asStateFlow() + + private val refreshReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + when (intent?.action) { + LocalBroadcastManager.REFRESH, + LocalBroadcastManager.REFRESH_LIST -> updateFilters() + } + } + } + + fun setFilter(filter: Filter) { + _state.update { it.copy(filter = filter) } + defaultFilterProvider.lastViewedFilter = filter + } + + fun setDrawerOpen(open: Boolean) { + _state.update { it.copy(drawerOpen = open) } + } + + init { + localBroadcastManager.registerRefreshListReceiver(refreshReceiver) + updateFilters() + _state.update { + it.copy( + begForMoney = if (IS_GENERIC) !inventory.hasTasksAccount else !inventory.hasPro + ) + } + } + + override fun onCleared() { + localBroadcastManager.unregisterReceiver(refreshReceiver) + } + + fun updateFilters() = viewModelScope.launch(Dispatchers.Default) { + filterProvider + .drawerItems() + .map { item -> + when (item) { + is Filter -> + DrawerItem.Filter( + title = item.title ?: "", + icon = getIcon(item), + color = getColor(item), + count = item.count.takeIf { it != NO_COUNT } ?: try { + taskDao.count(item) + } catch (e: Exception) { + Timber.e(e) + 0 + }, + shareCount = if (item is CaldavFilter) item.principals else 0, + type = { item }, + ) + is NavigationDrawerSubheader -> + DrawerItem.Header( + title = item.title ?: "", + collapsed = item.isCollapsed, + hasError = item.error, + canAdd = item.addIntent != null, + type = { item }, + ) + else -> throw IllegalArgumentException() + } + } + .let { filters -> _state.update { it.copy(drawerItems = filters.toPersistentList()) } } + } + + private fun getColor(filter: Filter): Int { + if (filter.tint != 0) { + val color = colorProvider.getThemeColor(filter.tint, true) + if (color.isFree || inventory.purchasedThemes()) { + return color.primaryColor + } + } + return 0 + } + + private fun getIcon(filter: Filter): Int { + if (filter.icon < 1000 || filter.icon == CustomIcons.PLACE || inventory.hasPro) { + val icon = CustomIcons.getIconResId(filter.icon) + if (icon != null) { + return icon + } + } + return when (filter) { + is TagFilter -> R.drawable.ic_outline_label_24px + is GtasksFilter, + is CaldavFilter -> R.drawable.ic_list_24px + + is CustomFilter -> R.drawable.ic_outline_filter_list_24px + is PlaceFilter -> R.drawable.ic_outline_place_24px + else -> filter.icon + } + } + + fun toggleCollapsed(subheader: NavigationDrawerSubheader) = viewModelScope.launch { + val collapsed = !subheader.isCollapsed + when (subheader.subheaderType) { + NavigationDrawerSubheader.SubheaderType.PREFERENCE -> { + preferences.setBoolean(subheader.id.toInt(), collapsed) + localBroadcastManager.broadcastRefreshList() + } + NavigationDrawerSubheader.SubheaderType.GOOGLE_TASKS, + NavigationDrawerSubheader.SubheaderType.CALDAV, + NavigationDrawerSubheader.SubheaderType.TASKS, + NavigationDrawerSubheader.SubheaderType.ETESYNC -> { + caldavDao.setCollapsed(subheader.id, collapsed) + localBroadcastManager.broadcastRefreshList() + } + else -> {} + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/todoroo/astrid/adapter/ActionViewHolder.kt b/app/src/main/java/com/todoroo/astrid/adapter/ActionViewHolder.kt deleted file mode 100644 index 7b81ce2b6..000000000 --- a/app/src/main/java/com/todoroo/astrid/adapter/ActionViewHolder.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.todoroo.astrid.adapter - -import android.content.Context -import android.view.View -import android.widget.ImageView -import android.widget.TextView -import androidx.recyclerview.widget.RecyclerView -import org.tasks.databinding.FilterAdapterActionBinding -import org.tasks.filters.NavigationDrawerAction -import org.tasks.themes.DrawableUtil - -class ActionViewHolder internal constructor( - private val context: Context, - itemView: View, - private val onClick: (NavigationDrawerAction) -> Unit -) : RecyclerView.ViewHolder(itemView) { - - private val row: View - private val text: TextView - private val icon: ImageView - - init { - FilterAdapterActionBinding.bind(itemView).let { - row = it.row - text = it.text - icon = it.icon - } - } - - fun bind(filter: NavigationDrawerAction) { - text.text = filter.title - icon.setImageDrawable(DrawableUtil.getWrapped(context, filter.icon)) - row.setOnClickListener { - onClick.invoke(filter) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/todoroo/astrid/adapter/NavigationDrawerAdapter.kt b/app/src/main/java/com/todoroo/astrid/adapter/NavigationDrawerAdapter.kt index 7028935c4..55adff9ec 100644 --- a/app/src/main/java/com/todoroo/astrid/adapter/NavigationDrawerAdapter.kt +++ b/app/src/main/java/com/todoroo/astrid/adapter/NavigationDrawerAdapter.kt @@ -18,7 +18,6 @@ import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.channels.Channel import org.tasks.activities.DragAndDropDiffer import org.tasks.billing.Inventory -import org.tasks.filters.NavigationDrawerAction import org.tasks.filters.NavigationDrawerSubheader import org.tasks.themes.ColorProvider import java.util.LinkedList @@ -38,7 +37,6 @@ class NavigationDrawerAdapter @Inject constructor( DragAndDropDiffer> { private lateinit var onClick: (FilterListItem?) -> Unit - private var selected: Filter? = null override val channel = Channel>(Channel.UNLIMITED) override val updates: Queue, DiffUtil.DiffResult?>> = LinkedList() override val scope: CoroutineScope = @@ -58,11 +56,6 @@ class NavigationDrawerAdapter @Inject constructor( override fun getItemCount() = items.size - fun setSelected(selected: Filter?) { - this.selected = selected - notifyDataSetChanged() - } - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { val type = FilterListItem.Type.values()[viewType] val view = LayoutInflater.from(parent.context).inflate(type.layout, parent, false) @@ -70,20 +63,17 @@ class NavigationDrawerAdapter @Inject constructor( FilterListItem.Type.ITEM -> FilterViewHolder( view, true, locale, activity, inventory, colorProvider) { onClickFilter(it) } FilterListItem.Type.SUBHEADER -> SubheaderViewHolder(view, subheaderClickHandler) - FilterListItem.Type.ACTION -> ActionViewHolder(activity, view) { onClickFilter(it) } - else -> SeparatorViewHolder(view) } } private fun onClickFilter(filter: FilterListItem?) = - onClick(if (filter == selected) null else filter) + onClick(filter) override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { val item = getItem(position) when (item.itemType) { FilterListItem.Type.ITEM -> - (holder as FilterViewHolder).bind(item as Filter, item == selected, max(item.count, 0)) - FilterListItem.Type.ACTION -> (holder as ActionViewHolder).bind(item as NavigationDrawerAction) + (holder as FilterViewHolder).bind(item as Filter, false, max(item.count, 0)) FilterListItem.Type.SUBHEADER -> (holder as SubheaderViewHolder).bind((item as NavigationDrawerSubheader)) else -> {} diff --git a/app/src/main/java/com/todoroo/astrid/adapter/SeparatorViewHolder.kt b/app/src/main/java/com/todoroo/astrid/adapter/SeparatorViewHolder.kt deleted file mode 100644 index 94d808557..000000000 --- a/app/src/main/java/com/todoroo/astrid/adapter/SeparatorViewHolder.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.todoroo.astrid.adapter - -import android.view.View -import androidx.recyclerview.widget.RecyclerView - -class SeparatorViewHolder internal constructor(itemView: View) : RecyclerView.ViewHolder(itemView) \ No newline at end of file diff --git a/app/src/main/java/com/todoroo/astrid/adapter/SubheaderClickHandler.kt b/app/src/main/java/com/todoroo/astrid/adapter/SubheaderClickHandler.kt index 34ce6599d..1eb60470c 100644 --- a/app/src/main/java/com/todoroo/astrid/adapter/SubheaderClickHandler.kt +++ b/app/src/main/java/com/todoroo/astrid/adapter/SubheaderClickHandler.kt @@ -8,6 +8,7 @@ import kotlinx.coroutines.launch import org.tasks.LocalBroadcastManager import org.tasks.data.CaldavDao import org.tasks.dialogs.NewFilterDialog +import org.tasks.filters.FilterProvider import org.tasks.filters.NavigationDrawerSubheader import org.tasks.filters.NavigationDrawerSubheader.SubheaderType.CALDAV import org.tasks.filters.NavigationDrawerSubheader.SubheaderType.ETESYNC @@ -16,7 +17,6 @@ import org.tasks.filters.NavigationDrawerSubheader.SubheaderType.PREFERENCE import org.tasks.filters.NavigationDrawerSubheader.SubheaderType.TASKS import org.tasks.preferences.MainPreferences import org.tasks.preferences.Preferences -import org.tasks.ui.NavigationDrawerFragment import javax.inject.Inject class SubheaderClickHandler @Inject constructor( @@ -42,7 +42,7 @@ class SubheaderClickHandler @Inject constructor( override fun onAdd(subheader: NavigationDrawerSubheader) { when (subheader.addIntentRc) { - NavigationDrawerFragment.REQUEST_NEW_FILTER -> + FilterProvider.REQUEST_NEW_FILTER -> NewFilterDialog.newFilterDialog().show( (activity as AppCompatActivity).supportFragmentManager, FRAG_TAG_NEW_FILTER @@ -55,6 +55,6 @@ class SubheaderClickHandler @Inject constructor( activity.startActivity(Intent(activity, MainPreferences::class.java)) companion object { - private const val FRAG_TAG_NEW_FILTER = "frag_tag_new_filter" + const val FRAG_TAG_NEW_FILTER = "frag_tag_new_filter" } } \ No newline at end of file diff --git a/app/src/main/java/com/todoroo/astrid/api/CaldavFilter.kt b/app/src/main/java/com/todoroo/astrid/api/CaldavFilter.kt index 4daf7bbb8..4f708cf9f 100644 --- a/app/src/main/java/com/todoroo/astrid/api/CaldavFilter.kt +++ b/app/src/main/java/com/todoroo/astrid/api/CaldavFilter.kt @@ -15,7 +15,7 @@ import org.tasks.data.TaskDao.TaskCriteria.activeAndVisible data class CaldavFilter( val calendar: CaldavCalendar, val principals: Int = 0, - override var count: Int = NO_COUNT, + override val count: Int = NO_COUNT, ) : Filter { override val title: String? get() = calendar.name diff --git a/app/src/main/java/com/todoroo/astrid/api/CustomFilter.kt b/app/src/main/java/com/todoroo/astrid/api/CustomFilter.kt index 8705f5878..58e1bff45 100644 --- a/app/src/main/java/com/todoroo/astrid/api/CustomFilter.kt +++ b/app/src/main/java/com/todoroo/astrid/api/CustomFilter.kt @@ -1,13 +1,11 @@ package com.todoroo.astrid.api -import com.todoroo.astrid.api.Filter.Companion.NO_COUNT import kotlinx.parcelize.Parcelize import org.tasks.themes.CustomIcons @Parcelize data class CustomFilter( val filter: org.tasks.data.Filter, - override var count: Int = NO_COUNT, ) : Filter { override val title: String? get() = filter.title diff --git a/app/src/main/java/com/todoroo/astrid/api/Filter.kt b/app/src/main/java/com/todoroo/astrid/api/Filter.kt index 63f1829dd..1f4a4c1de 100644 --- a/app/src/main/java/com/todoroo/astrid/api/Filter.kt +++ b/app/src/main/java/com/todoroo/astrid/api/Filter.kt @@ -1,7 +1,6 @@ package com.todoroo.astrid.api import android.os.Parcelable -import com.todoroo.astrid.api.Filter.Companion.NO_COUNT import kotlinx.parcelize.Parcelize interface Filter : FilterListItem, Parcelable { @@ -14,7 +13,8 @@ interface Filter : FilterListItem, Parcelable { val tint: Int get() = 0 @Deprecated("Remove this") - var count: Int + val count: Int + get() = NO_COUNT val order: Int get() = NO_ORDER override val itemType: FilterListItem.Type @@ -49,7 +49,6 @@ data class FilterImpl( override val valuesForNewTasks: String? = null, override val icon: Int = -1, override val tint: Int = 0, - override var count: Int = NO_COUNT, ) : Filter { override fun areItemsTheSame(other: FilterListItem): Boolean { return other is Filter && sql == other.sql diff --git a/app/src/main/java/com/todoroo/astrid/api/FilterListItem.kt b/app/src/main/java/com/todoroo/astrid/api/FilterListItem.kt index af25fb4fe..61d896c56 100644 --- a/app/src/main/java/com/todoroo/astrid/api/FilterListItem.kt +++ b/app/src/main/java/com/todoroo/astrid/api/FilterListItem.kt @@ -10,8 +10,6 @@ interface FilterListItem { enum class Type(@param:LayoutRes val layout: Int) { ITEM(R.layout.filter_adapter_row), - ACTION(R.layout.filter_adapter_action), SUBHEADER(R.layout.filter_adapter_subheader), - SEPARATOR(R.layout.filter_adapter_separator) } } diff --git a/app/src/main/java/com/todoroo/astrid/api/GtasksFilter.kt b/app/src/main/java/com/todoroo/astrid/api/GtasksFilter.kt index 19e4a273d..1d44768b6 100644 --- a/app/src/main/java/com/todoroo/astrid/api/GtasksFilter.kt +++ b/app/src/main/java/com/todoroo/astrid/api/GtasksFilter.kt @@ -15,7 +15,7 @@ import org.tasks.data.TaskDao.TaskCriteria.activeAndVisible @Parcelize data class GtasksFilter( val list: CaldavCalendar, - override var count: Int = NO_COUNT, + override val count: Int = NO_COUNT, ) : Filter { override val title: String? get() = list.name diff --git a/app/src/main/java/com/todoroo/astrid/api/SearchFilter.kt b/app/src/main/java/com/todoroo/astrid/api/SearchFilter.kt index f67e26150..fdc12928a 100644 --- a/app/src/main/java/com/todoroo/astrid/api/SearchFilter.kt +++ b/app/src/main/java/com/todoroo/astrid/api/SearchFilter.kt @@ -4,7 +4,6 @@ import com.todoroo.andlib.sql.Criterion import com.todoroo.andlib.sql.Join import com.todoroo.andlib.sql.Query import com.todoroo.andlib.sql.QueryTemplate -import com.todoroo.astrid.api.Filter.Companion.NO_COUNT import com.todoroo.astrid.data.Task import kotlinx.parcelize.Parcelize import org.tasks.data.CaldavCalendar @@ -18,7 +17,6 @@ import org.tasks.data.UserActivity data class SearchFilter( override val title: String, val query: String, - override var count: Int = NO_COUNT, ) : Filter { override val sql: String get() { diff --git a/app/src/main/java/com/todoroo/astrid/api/TagFilter.kt b/app/src/main/java/com/todoroo/astrid/api/TagFilter.kt index 6106ac117..28d91599d 100644 --- a/app/src/main/java/com/todoroo/astrid/api/TagFilter.kt +++ b/app/src/main/java/com/todoroo/astrid/api/TagFilter.kt @@ -14,7 +14,7 @@ import org.tasks.data.TaskDao.TaskCriteria.activeAndVisible @Parcelize data class TagFilter( val tagData: TagData, - override var count: Int = NO_COUNT, + override val count: Int = NO_COUNT, override var filterOverride: String? = null, ) : AstridOrderingFilter { override val title: String? diff --git a/app/src/main/java/com/todoroo/astrid/core/BuiltInFilterExposer.kt b/app/src/main/java/com/todoroo/astrid/core/BuiltInFilterExposer.kt index a26301fd7..ed6f41e2d 100644 --- a/app/src/main/java/com/todoroo/astrid/core/BuiltInFilterExposer.kt +++ b/app/src/main/java/com/todoroo/astrid/core/BuiltInFilterExposer.kt @@ -30,20 +30,14 @@ import org.tasks.filters.TodayFilter import org.tasks.preferences.Preferences import javax.inject.Inject -/** - * Exposes Astrid's built in filters to the NavigationDrawerFragment - * - * @author Tim Su @todoroo.com> - */ class BuiltInFilterExposer @Inject constructor( @param:ApplicationContext private val context: Context, private val preferences: Preferences, - private val taskDao: TaskDao) { + private val taskDao: TaskDao +) { val myTasksFilter: Filter - get() { - return getMyTasksFilter(context.resources) - } + get() = getMyTasksFilter(context.resources) suspend fun filters(): List { val r = context.resources @@ -67,9 +61,7 @@ class BuiltInFilterExposer @Inject constructor( } companion object { - fun getMyTasksFilter(r: Resources): AstridOrderingFilter { - return MyTasksFilter(r.getString(R.string.BFE_Active)) - } + fun getMyTasksFilter(r: Resources) = MyTasksFilter(r.getString(R.string.BFE_Active)) fun getTodayFilter(r: Resources): Filter { return TodayFilter(r.getString(R.string.today)) @@ -148,7 +140,7 @@ class BuiltInFilterExposer @Inject constructor( ) fun getRecentlyModifiedFilter(r: Resources) = - RecentlyModifiedFilter(r.getString(R.string.BFE_Recent)) + RecentlyModifiedFilter(r.getString(R.string.BFE_Recent)) fun getSnoozedFilter(r: Resources) = SnoozedFilter(r.getString(R.string.filter_snoozed)) diff --git a/app/src/main/java/org/tasks/activities/NavigationDrawerCustomization.kt b/app/src/main/java/org/tasks/activities/NavigationDrawerCustomization.kt index fbefbc428..4a65d9918 100644 --- a/app/src/main/java/org/tasks/activities/NavigationDrawerCustomization.kt +++ b/app/src/main/java/org/tasks/activities/NavigationDrawerCustomization.kt @@ -100,11 +100,6 @@ class NavigationDrawerCustomization : ThemedInjectingAppCompatActivity(), Toolba private fun updateFilters() = lifecycleScope.launch { filterProvider .drawerCustomizationItems() - .onEach { f -> - if (f is Filter) { - f.count = 0 - } - } .let { adapter.submitList(it) } } diff --git a/app/src/main/java/org/tasks/compose/drawer/DrawerAction.kt b/app/src/main/java/org/tasks/compose/drawer/DrawerAction.kt new file mode 100644 index 000000000..bf7aab157 --- /dev/null +++ b/app/src/main/java/org/tasks/compose/drawer/DrawerAction.kt @@ -0,0 +1,5 @@ +package org.tasks.compose.drawer + +enum class DrawerAction { + PURCHASE, CUSTOMIZE_DRAWER, SETTINGS, HELP_AND_FEEDBACK +} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/compose/drawer/DrawerItem.kt b/app/src/main/java/org/tasks/compose/drawer/DrawerItem.kt new file mode 100644 index 000000000..e115b3f27 --- /dev/null +++ b/app/src/main/java/org/tasks/compose/drawer/DrawerItem.kt @@ -0,0 +1,22 @@ +package org.tasks.compose.drawer + +import org.tasks.filters.NavigationDrawerSubheader + +sealed interface DrawerItem { + data class Filter( + val title: String, + val icon: Int, + val color: Int = 0, + val count: Int = 0, + val shareCount: Int = 0, + val selected: Boolean = false, + val type: () -> com.todoroo.astrid.api.Filter, + ) : DrawerItem + data class Header( + val title: String, + val collapsed: Boolean, + val hasError: Boolean, + val canAdd: Boolean, + val type: () -> NavigationDrawerSubheader, + ) : DrawerItem +} diff --git a/app/src/main/java/org/tasks/compose/drawer/TaskListDrawer.kt b/app/src/main/java/org/tasks/compose/drawer/TaskListDrawer.kt new file mode 100644 index 000000000..027915ab2 --- /dev/null +++ b/app/src/main/java/org/tasks/compose/drawer/TaskListDrawer.kt @@ -0,0 +1,318 @@ +package org.tasks.compose.drawer + +import android.content.res.Configuration +import androidx.compose.animation.animateContentSize +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.spring +import androidx.compose.animation.core.tween +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.asPaddingValues +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.mandatorySystemGestures +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material.ContentAlpha +import androidx.compose.material.IconButton +import androidx.compose.material.MaterialTheme +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Add +import androidx.compose.material.icons.outlined.ExpandMore +import androidx.compose.material.icons.outlined.PeopleOutline +import androidx.compose.material.icons.outlined.PermIdentity +import androidx.compose.material.icons.outlined.SyncProblem +import androidx.compose.material3.Divider +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.rotate +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.google.android.material.composethemeadapter.MdcTheme +import com.todoroo.astrid.api.FilterImpl +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf +import org.tasks.R +import org.tasks.Tasks.Companion.IS_GENERIC +import org.tasks.extensions.formatNumber +import org.tasks.filters.NavigationDrawerSubheader +import timber.log.Timber + +@Composable +fun TaskListDrawer( + begForMoney: Boolean, + filters: ImmutableList, + onClick: (DrawerItem) -> Unit, + onDrawerAction: (DrawerAction) -> Unit, + onAddClick: (DrawerItem.Header) -> Unit, + onErrorClick: () -> Unit, +) { + val insets = WindowInsets.mandatorySystemGestures + LazyColumn( + modifier = Modifier + .padding( + bottom = insets + .asPaddingValues() + .calculateBottomPadding() + ) + .animateContentSize( + animationSpec = spring( + dampingRatio = Spring.DampingRatioNoBouncy, + stiffness = Spring.StiffnessMedium + ) + ) + ) { + items(items = filters) { + when (it) { + is DrawerItem.Filter -> FilterItem(item = it, onClick = { onClick(it) }) + is DrawerItem.Header -> HeaderItem( + item = it, + canAdd = it.canAdd, + toggleCollapsed = { onClick(it) }, + onAddClick = { onAddClick(it) }, + onErrorClick = onErrorClick, + ) + } + } + item { + Divider(modifier = Modifier.fillMaxWidth()) + } + if (begForMoney) { + item { + MenuAction( + icon = R.drawable.ic_outline_attach_money_24px, + title = if (IS_GENERIC) R.string.TLA_menu_donate else R.string.name_your_price + ) { + onDrawerAction(DrawerAction.PURCHASE) + } + } + } + item { + MenuAction( + icon = R.drawable.ic_outline_edit_24px, + title = R.string.manage_drawer + ) { + onDrawerAction(DrawerAction.CUSTOMIZE_DRAWER) + } + } + item { + MenuAction( + icon = R.drawable.ic_outline_settings_24px, + title = R.string.TLA_menu_settings + ) { + onDrawerAction(DrawerAction.SETTINGS) + } + } + item { + MenuAction( + icon = R.drawable.ic_outline_help_outline_24px, + title = R.string.help_and_feedback + ) { + onDrawerAction(DrawerAction.HELP_AND_FEEDBACK) + } + } + } +} + +@Composable +private fun FilterItem( + item: DrawerItem.Filter, + onClick: () -> Unit, +) { + MenuRow( + onClick = onClick, + ) { + if (item.icon != -1) { + DrawerIcon(icon = item.icon, color = item.color) + } + Spacer(modifier = Modifier.width(16.dp)) + Text( + text = item.title, + color = MaterialTheme.colors.onSurface, + modifier = Modifier.weight(1f), + ) + if (item.shareCount > 0) { + Icon( + imageVector = when (item.shareCount) { + 1 -> Icons.Outlined.PermIdentity + else -> Icons.Outlined.PeopleOutline + }, + contentDescription = null, + tint = MaterialTheme.colors.onSurface.copy( + alpha = ContentAlpha.medium + ), + ) + } + Box( + modifier = Modifier.width(48.dp), + contentAlignment = Alignment.CenterEnd, + ) { + if (item.count > 0) { + val locale = LocalConfiguration.current.locales[0] + Text( + text = locale.formatNumber(item.count), + color = MaterialTheme.colors.onSurface, + ) + } + } + } +} + +@Composable +private fun MenuAction( + icon: Int, + title: Int, + onClick: () -> Unit, +) { + MenuRow(onClick = onClick) { + DrawerIcon(icon = icon) + Spacer(modifier = Modifier.width(16.dp)) + Text( + text = stringResource(id = title), + color = MaterialTheme.colors.onSurface, + modifier = Modifier.weight(1f), + ) + } +} + +@Composable +private fun DrawerIcon(icon: Int, color: Int = 0) { + Icon( + modifier = Modifier.size(24.dp), + painter = painterResource(id = icon), + contentDescription = null, + tint = when (color) { + 0 -> MaterialTheme.colors.onSurface + else -> Color(color) + }.copy(alpha = ContentAlpha.medium) + ) +} + +@Composable +private fun HeaderItem( + item: DrawerItem.Header, + canAdd: Boolean, + toggleCollapsed: () -> Unit, + onAddClick: () -> Unit, + onErrorClick: () -> Unit, +) { + Column { + Divider(modifier = Modifier.fillMaxWidth()) + MenuRow( + padding = PaddingValues(start = 16.dp), + onClick = toggleCollapsed, + ) { + Text( + modifier = Modifier.weight(1f), + text = item.title, + color = MaterialTheme.colors.onSurface, + ) + IconButton(onClick = toggleCollapsed) { + val rotation by animateFloatAsState( + targetValue = if (item.collapsed) 0f else 180f, + animationSpec = tween(250), + label = "arrow rotation", + ) + Timber.d("rotation: $rotation") + Icon( + modifier = Modifier.rotate(rotation), + imageVector = Icons.Outlined.ExpandMore, + contentDescription = null, + tint = MaterialTheme.colors.onSurface, + ) + } + if (canAdd) { + IconButton(onClick = onAddClick) { + Icon( + imageVector = Icons.Outlined.Add, + contentDescription = null, + tint = MaterialTheme.colors.onSurface, + ) + } + } + if (item.hasError) { + IconButton(onClick = onErrorClick) { + Icon( + imageVector = Icons.Outlined.SyncProblem, + contentDescription = null, + tint = MaterialTheme.colors.error, + ) + } + } + } + } +} + +@Composable +private fun MenuRow( + padding: PaddingValues = PaddingValues(horizontal = 16.dp), + onClick: () -> Unit, + content: @Composable RowScope.() -> Unit, +) { + Row( + modifier = Modifier + .clickable(onClick = onClick) + .height(48.dp) + .padding(padding) + .fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + content = content + ) +} + +@Preview(showBackground = true) +@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +fun MenuPreview() { + MdcTheme { + TaskListDrawer( + filters = persistentListOf( + DrawerItem.Filter( + title = "My Tasks", + icon = R.drawable.ic_outline_all_inbox_24px, + type = { FilterImpl() }, + ), + DrawerItem.Header( + title = "Filters", + collapsed = false, + canAdd = true, + hasError = false, + type = { + NavigationDrawerSubheader( + null, + false, + false, + NavigationDrawerSubheader.SubheaderType.PREFERENCE, + 0L, + 0, + null + ) + }, + ) + ), + onClick = {}, + onDrawerAction = {}, + begForMoney = true, + onAddClick = {}, + onErrorClick = {}, + ) + } +} diff --git a/app/src/main/java/org/tasks/dialogs/NewFilterDialog.kt b/app/src/main/java/org/tasks/dialogs/NewFilterDialog.kt index c09362604..c5b269f3e 100644 --- a/app/src/main/java/org/tasks/dialogs/NewFilterDialog.kt +++ b/app/src/main/java/org/tasks/dialogs/NewFilterDialog.kt @@ -4,6 +4,7 @@ import android.app.Dialog import android.content.Intent import android.os.Bundle import androidx.fragment.app.DialogFragment +import com.todoroo.astrid.activity.MainActivity import com.todoroo.astrid.api.CustomFilterCriterion import com.todoroo.astrid.core.CriterionInstance import com.todoroo.astrid.core.CriterionInstance.Companion.TYPE_INTERSECT @@ -14,7 +15,6 @@ import dagger.hilt.android.AndroidEntryPoint import org.tasks.R import org.tasks.activities.FilterSettingsActivity import org.tasks.filters.FilterCriteriaProvider -import org.tasks.ui.NavigationDrawerFragment import javax.inject.Inject @AndroidEntryPoint @@ -123,7 +123,7 @@ class NewFilterDialog : DialogFragment() { intent.putExtra(FilterSettingsActivity.EXTRA_TITLE, title) intent.putExtra(FilterSettingsActivity.EXTRA_CRITERIA, serialize(list)) } - activity?.startActivityForResult(intent, NavigationDrawerFragment.REQUEST_NEW_LIST) + activity?.startActivityForResult(intent, MainActivity.REQUEST_NEW_LIST) dismiss() } diff --git a/app/src/main/java/org/tasks/extensions/Number.kt b/app/src/main/java/org/tasks/extensions/Number.kt deleted file mode 100644 index f7be061fb..000000000 --- a/app/src/main/java/org/tasks/extensions/Number.kt +++ /dev/null @@ -1,11 +0,0 @@ -package org.tasks.extensions - -import android.content.res.Resources -import android.util.TypedValue - -val Number.dp: Float - get() = TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, - this.toFloat(), - Resources.getSystem().displayMetrics - ) \ No newline at end of file diff --git a/app/src/main/java/org/tasks/filters/CaldavFilters.kt b/app/src/main/java/org/tasks/filters/CaldavFilters.kt index 9bda26c32..683addbea 100644 --- a/app/src/main/java/org/tasks/filters/CaldavFilters.kt +++ b/app/src/main/java/org/tasks/filters/CaldavFilters.kt @@ -9,12 +9,9 @@ data class CaldavFilters( @JvmField val count: Int, @JvmField val principals: Int, ) { - fun toCaldavFilter(): CaldavFilter { - val filter = CaldavFilter( - calendar = caldavCalendar, - principals = principals, - ) - filter.count = count - return filter - } + fun toCaldavFilter(): CaldavFilter = CaldavFilter( + calendar = caldavCalendar, + principals = principals, + count = count, + ) } diff --git a/app/src/main/java/org/tasks/filters/FilterProvider.kt b/app/src/main/java/org/tasks/filters/FilterProvider.kt index a7b31fcda..086f6c4aa 100644 --- a/app/src/main/java/org/tasks/filters/FilterProvider.kt +++ b/app/src/main/java/org/tasks/filters/FilterProvider.kt @@ -2,6 +2,7 @@ package org.tasks.filters import android.content.Context import android.content.Intent +import com.todoroo.astrid.activity.MainActivity import com.todoroo.astrid.api.CustomFilter import com.todoroo.astrid.api.Filter import com.todoroo.astrid.api.Filter.Companion.NO_ORDER @@ -10,11 +11,8 @@ import com.todoroo.astrid.core.BuiltInFilterExposer import dagger.hilt.android.qualifiers.ApplicationContext import org.tasks.BuildConfig import org.tasks.R -import org.tasks.Tasks.Companion.IS_GENERIC import org.tasks.activities.GoogleTaskListSettingsActivity -import org.tasks.activities.NavigationDrawerCustomization import org.tasks.activities.TagSettingsActivity -import org.tasks.billing.Inventory import org.tasks.caldav.BaseCaldavCalendarSettingsActivity import org.tasks.data.CaldavAccount import org.tasks.data.CaldavAccount.Companion.TYPE_ETESYNC @@ -27,28 +25,24 @@ import org.tasks.data.LocationDao import org.tasks.data.TagDataDao import org.tasks.filters.NavigationDrawerSubheader.SubheaderType import org.tasks.location.LocationPickerActivity -import org.tasks.preferences.HelpAndFeedback -import org.tasks.preferences.MainPreferences import org.tasks.preferences.Preferences -import org.tasks.ui.NavigationDrawerFragment import javax.inject.Inject class FilterProvider @Inject constructor( @param:ApplicationContext private val context: Context, - private val inventory: Inventory, private val builtInFilterExposer: BuiltInFilterExposer, private val filterDao: FilterDao, private val tagDataDao: TagDataDao, private val googleTaskListDao: GoogleTaskListDao, private val caldavDao: CaldavDao, private val preferences: Preferences, - private val locationDao: LocationDao) { - + private val locationDao: LocationDao +) { suspend fun listPickerItems(): List = caldavFilters(false) - suspend fun navDrawerItems(): List = - getAllFilters(hideUnused = true).plus(navDrawerFooter) + suspend fun drawerItems(): List = + getAllFilters(showCreate = true) suspend fun filterPickerItems(): List = getAllFilters(showCreate = false) @@ -95,7 +89,7 @@ class FilterProvider @Inject constructor( collapsed, SubheaderType.PREFERENCE, R.string.p_collapse_filters.toLong(), - NavigationDrawerFragment.REQUEST_NEW_FILTER, + REQUEST_NEW_FILTER, if (showCreate) Intent() else null)) .apply { if (collapsed) return this } .plusAllIf(showBuiltIn) { @@ -116,7 +110,7 @@ class FilterProvider @Inject constructor( collapsed, SubheaderType.PREFERENCE, R.string.p_collapse_tags.toLong(), - NavigationDrawerFragment.REQUEST_NEW_LIST, + MainActivity.REQUEST_NEW_LIST, if (showCreate) { Intent(context, TagSettingsActivity::class.java) } else { @@ -143,7 +137,7 @@ class FilterProvider @Inject constructor( collapsed, SubheaderType.PREFERENCE, R.string.p_collapse_locations.toLong(), - NavigationDrawerFragment.REQUEST_NEW_PLACE, + MainActivity.REQUEST_NEW_PLACE, if (showCreate) { Intent(context, LocationPickerActivity::class.java) } else { @@ -176,45 +170,6 @@ class FilterProvider @Inject constructor( .toList() .plusAllIf(BuildConfig.DEBUG) { getDebugFilters() } - private val navDrawerFooter: List - get() = listOf(NavigationDrawerSeparator()) - .plusIf(IS_GENERIC && !inventory.hasTasksAccount) { - NavigationDrawerAction( - context.getString(R.string.TLA_menu_donate), - R.drawable.ic_outline_attach_money_24px, - NavigationDrawerFragment.REQUEST_DONATE) - } - .plusIf(!inventory.hasPro) { - NavigationDrawerAction( - context.getString(R.string.name_your_price), - R.drawable.ic_outline_attach_money_24px, - NavigationDrawerFragment.REQUEST_PURCHASE) - } - .plus( - NavigationDrawerAction( - context.getString(R.string.manage_drawer), - R.drawable.ic_outline_edit_24px, - 0, - Intent(context, NavigationDrawerCustomization::class.java) - ) - ) - .plus( - NavigationDrawerAction( - context.getString(R.string.TLA_menu_settings), - R.drawable.ic_outline_settings_24px, - NavigationDrawerFragment.REQUEST_SETTINGS, - Intent(context, MainPreferences::class.java) - ) - ) - .plus( - NavigationDrawerAction( - context.getString(R.string.help_and_feedback), - R.drawable.ic_outline_help_outline_24px, - 0, - Intent(context, HelpAndFeedback::class.java) - ) - ) - private suspend fun googleTaskFilter(account: CaldavAccount, showCreate: Boolean): List = listOf( NavigationDrawerSubheader( @@ -223,7 +178,7 @@ class FilterProvider @Inject constructor( account.isCollapsed, SubheaderType.GOOGLE_TASKS, account.id, - NavigationDrawerFragment.REQUEST_NEW_LIST, + MainActivity.REQUEST_NEW_LIST, if (showCreate) { Intent(context, GoogleTaskListSettingsActivity::class.java) .putExtra(GoogleTaskListSettingsActivity.EXTRA_ACCOUNT, account) @@ -267,7 +222,7 @@ class FilterProvider @Inject constructor( else -> SubheaderType.CALDAV }, account.id, - NavigationDrawerFragment.REQUEST_NEW_LIST, + MainActivity.REQUEST_NEW_LIST, if (showCreate) { Intent(context, account.listSettingsClass()) .putExtra( @@ -285,6 +240,7 @@ class FilterProvider @Inject constructor( .sort()) companion object { + const val REQUEST_NEW_FILTER = 101015 private val COMPARATOR = Comparator { f1, f2 -> when { f1.order == NO_ORDER && f2.order == NO_ORDER -> @@ -307,9 +263,6 @@ class FilterProvider @Inject constructor( private suspend fun Collection.plusAllIf(predicate: Boolean, item: suspend () -> Iterable): List = plus(if (predicate) item() else emptyList()) - private fun Iterable.plusIf(predicate: Boolean, item: () -> T): Iterable = - if (predicate) plus(item()) else this - private fun Iterable.filterIf(predicate: Boolean, predicate2: (T) -> Boolean): Iterable = if (predicate) filter(predicate2) else this } diff --git a/app/src/main/java/org/tasks/filters/GoogleTaskFilters.kt b/app/src/main/java/org/tasks/filters/GoogleTaskFilters.kt index 6acc258ff..e264b2a53 100644 --- a/app/src/main/java/org/tasks/filters/GoogleTaskFilters.kt +++ b/app/src/main/java/org/tasks/filters/GoogleTaskFilters.kt @@ -8,9 +8,8 @@ data class GoogleTaskFilters( @JvmField @Embedded val googleTaskList: CaldavCalendar, @JvmField val count: Int, ) { - fun toGtasksFilter(): GtasksFilter { - val filter = GtasksFilter(googleTaskList) - filter.count = count - return filter - } + fun toGtasksFilter(): GtasksFilter = GtasksFilter( + list = googleTaskList, + count = count, + ) } diff --git a/app/src/main/java/org/tasks/filters/LocationFilters.kt b/app/src/main/java/org/tasks/filters/LocationFilters.kt index b54cd99c6..bb9466a61 100644 --- a/app/src/main/java/org/tasks/filters/LocationFilters.kt +++ b/app/src/main/java/org/tasks/filters/LocationFilters.kt @@ -7,9 +7,8 @@ data class LocationFilters( @JvmField @Embedded var place: Place, @JvmField var count: Int ) { - fun toLocationFilter(): PlaceFilter { - val filter = PlaceFilter(place) - filter.count = count - return filter - } + fun toLocationFilter(): PlaceFilter = PlaceFilter( + place = place, + count = count, + ) } diff --git a/app/src/main/java/org/tasks/filters/MyTasksFilter.kt b/app/src/main/java/org/tasks/filters/MyTasksFilter.kt index 5c0734185..f2efaf717 100644 --- a/app/src/main/java/org/tasks/filters/MyTasksFilter.kt +++ b/app/src/main/java/org/tasks/filters/MyTasksFilter.kt @@ -3,7 +3,6 @@ package org.tasks.filters import com.todoroo.andlib.sql.Criterion import com.todoroo.andlib.sql.QueryTemplate import com.todoroo.astrid.api.AstridOrderingFilter -import com.todoroo.astrid.api.Filter.Companion.NO_COUNT import com.todoroo.astrid.api.FilterListItem import com.todoroo.astrid.data.Task import kotlinx.parcelize.Parcelize @@ -14,7 +13,6 @@ import org.tasks.themes.CustomIcons data class MyTasksFilter( override val title: String, override var filterOverride: String? = null, - override var count: Int = NO_COUNT, ) : AstridOrderingFilter { override val icon: Int get() = CustomIcons.ALL_INBOX diff --git a/app/src/main/java/org/tasks/filters/NavigationDrawerAction.kt b/app/src/main/java/org/tasks/filters/NavigationDrawerAction.kt deleted file mode 100644 index 2faf437f9..000000000 --- a/app/src/main/java/org/tasks/filters/NavigationDrawerAction.kt +++ /dev/null @@ -1,15 +0,0 @@ -package org.tasks.filters - -import android.content.Intent -import com.todoroo.astrid.api.FilterListItem - -data class NavigationDrawerAction( - val title: String, - val icon: Int, - val requestCode: Int, - val intent: Intent? = null, -) : FilterListItem { - override val itemType = FilterListItem.Type.ACTION - - override fun areItemsTheSame(other: FilterListItem) = this == other -} diff --git a/app/src/main/java/org/tasks/filters/NavigationDrawerSeparator.kt b/app/src/main/java/org/tasks/filters/NavigationDrawerSeparator.kt deleted file mode 100644 index 039bde728..000000000 --- a/app/src/main/java/org/tasks/filters/NavigationDrawerSeparator.kt +++ /dev/null @@ -1,11 +0,0 @@ -package org.tasks.filters - -import com.todoroo.astrid.api.FilterListItem - -class NavigationDrawerSeparator : FilterListItem { - override val itemType = FilterListItem.Type.SEPARATOR - - override fun areItemsTheSame(other: FilterListItem): Boolean { - return other is NavigationDrawerSeparator - } -} diff --git a/app/src/main/java/org/tasks/filters/NotificationsFilter.kt b/app/src/main/java/org/tasks/filters/NotificationsFilter.kt index c5a58359e..5e5b9c9a6 100644 --- a/app/src/main/java/org/tasks/filters/NotificationsFilter.kt +++ b/app/src/main/java/org/tasks/filters/NotificationsFilter.kt @@ -3,7 +3,6 @@ package org.tasks.filters import com.todoroo.andlib.sql.Join import com.todoroo.andlib.sql.QueryTemplate import com.todoroo.astrid.api.Filter -import com.todoroo.astrid.api.Filter.Companion.NO_COUNT import com.todoroo.astrid.api.FilterListItem import com.todoroo.astrid.data.Task import kotlinx.parcelize.Parcelize @@ -13,7 +12,6 @@ import org.tasks.notifications.Notification @Parcelize data class NotificationsFilter( override val title: String, - override var count: Int = NO_COUNT, ) : Filter { override val icon: Int get() = R.drawable.ic_outline_notifications_24px diff --git a/app/src/main/java/org/tasks/filters/PlaceFilter.kt b/app/src/main/java/org/tasks/filters/PlaceFilter.kt index 97dcfe77d..f4dc0d70a 100644 --- a/app/src/main/java/org/tasks/filters/PlaceFilter.kt +++ b/app/src/main/java/org/tasks/filters/PlaceFilter.kt @@ -19,7 +19,7 @@ import org.tasks.themes.CustomIcons @Parcelize data class PlaceFilter( val place: Place, - override var count: Int = NO_COUNT, + override val count: Int = NO_COUNT, ) : Filter { override val valuesForNewTasks: String get() = AndroidUtilities.mapToSerializedString(mapOf(Place.KEY to place.uid!!)) diff --git a/app/src/main/java/org/tasks/filters/RecentlyModifiedFilter.kt b/app/src/main/java/org/tasks/filters/RecentlyModifiedFilter.kt index d5b0b82b7..b71dfe245 100644 --- a/app/src/main/java/org/tasks/filters/RecentlyModifiedFilter.kt +++ b/app/src/main/java/org/tasks/filters/RecentlyModifiedFilter.kt @@ -4,7 +4,6 @@ import com.todoroo.andlib.sql.Criterion.Companion.and import com.todoroo.andlib.sql.Order.Companion.desc import com.todoroo.andlib.sql.QueryTemplate import com.todoroo.astrid.api.Filter -import com.todoroo.astrid.api.Filter.Companion.NO_COUNT import com.todoroo.astrid.api.FilterListItem import com.todoroo.astrid.data.Task import kotlinx.parcelize.Parcelize @@ -14,7 +13,6 @@ import org.tasks.time.DateTime @Parcelize data class RecentlyModifiedFilter( override val title: String, - override var count: Int = NO_COUNT, ) : Filter { override val icon: Int get() = CustomIcons.HISTORY diff --git a/app/src/main/java/org/tasks/filters/SnoozedFilter.kt b/app/src/main/java/org/tasks/filters/SnoozedFilter.kt index 3319a3224..0ecec47eb 100644 --- a/app/src/main/java/org/tasks/filters/SnoozedFilter.kt +++ b/app/src/main/java/org/tasks/filters/SnoozedFilter.kt @@ -4,7 +4,6 @@ import com.todoroo.andlib.sql.Criterion.Companion.and import com.todoroo.andlib.sql.Join.Companion.inner import com.todoroo.andlib.sql.QueryTemplate import com.todoroo.astrid.api.Filter -import com.todoroo.astrid.api.Filter.Companion.NO_COUNT import com.todoroo.astrid.api.FilterListItem import com.todoroo.astrid.data.Task import kotlinx.parcelize.Parcelize @@ -15,7 +14,6 @@ import org.tasks.data.TaskDao.TaskCriteria.activeAndVisible @Parcelize data class SnoozedFilter( override val title: String, - override var count: Int = NO_COUNT, ) : Filter { override val icon: Int get() = R.drawable.ic_snooze_white_24dp diff --git a/app/src/main/java/org/tasks/filters/TagFilters.kt b/app/src/main/java/org/tasks/filters/TagFilters.kt index 55a541444..de46e2062 100644 --- a/app/src/main/java/org/tasks/filters/TagFilters.kt +++ b/app/src/main/java/org/tasks/filters/TagFilters.kt @@ -8,9 +8,8 @@ data class TagFilters( @JvmField @Embedded var tagData: TagData, @JvmField var count: Int, ) { - fun toTagFilter(): TagFilter { - val filter = TagFilter(tagData) - filter.count = count - return filter - } + fun toTagFilter(): TagFilter = TagFilter( + tagData = tagData, + count = count, + ) } diff --git a/app/src/main/java/org/tasks/filters/TodayFilter.kt b/app/src/main/java/org/tasks/filters/TodayFilter.kt index f825a1c0d..c0ce1ccf7 100644 --- a/app/src/main/java/org/tasks/filters/TodayFilter.kt +++ b/app/src/main/java/org/tasks/filters/TodayFilter.kt @@ -4,7 +4,6 @@ import com.todoroo.andlib.sql.Criterion import com.todoroo.andlib.sql.QueryTemplate import com.todoroo.andlib.utility.AndroidUtilities import com.todoroo.astrid.api.AstridOrderingFilter -import com.todoroo.astrid.api.Filter.Companion.NO_COUNT import com.todoroo.astrid.api.FilterListItem import com.todoroo.astrid.api.PermaSql import com.todoroo.astrid.data.Task @@ -16,7 +15,6 @@ import org.tasks.themes.CustomIcons data class TodayFilter( override val title: String, override var filterOverride: String? = null, - override var count: Int = NO_COUNT, ) : AstridOrderingFilter { override val sql: String get() = QueryTemplate() diff --git a/app/src/main/java/org/tasks/preferences/fragments/LookAndFeel.kt b/app/src/main/java/org/tasks/preferences/fragments/LookAndFeel.kt index 22b738647..ae46ce1c9 100644 --- a/app/src/main/java/org/tasks/preferences/fragments/LookAndFeel.kt +++ b/app/src/main/java/org/tasks/preferences/fragments/LookAndFeel.kt @@ -36,7 +36,6 @@ import org.tasks.themes.ThemeBase.DEFAULT_BASE_THEME import org.tasks.themes.ThemeBase.EXTRA_THEME_OVERRIDE import org.tasks.themes.ThemeColor import org.tasks.themes.ThemeColor.getLauncherColor -import org.tasks.ui.NavigationDrawerFragment.Companion.REQUEST_PURCHASE import java.util.Locale import javax.inject.Inject @@ -246,6 +245,7 @@ class LookAndFeel : InjectingPreferenceFragment() { private const val REQUEST_ACCENT_PICKER = 10003 private const val REQUEST_LAUNCHER_PICKER = 10004 private const val REQUEST_LOCALE = 10006 + private const val REQUEST_PURCHASE = 10007 private const val FRAG_TAG_LOCALE_PICKER = "frag_tag_locale_picker" private const val FRAG_TAG_THEME_PICKER = "frag_tag_theme_picker" private const val FRAG_TAG_COLOR_PICKER = "frag_tag_color_picker" diff --git a/app/src/main/java/org/tasks/ui/NavigationDrawerFragment.kt b/app/src/main/java/org/tasks/ui/NavigationDrawerFragment.kt deleted file mode 100644 index 3f605c252..000000000 --- a/app/src/main/java/org/tasks/ui/NavigationDrawerFragment.kt +++ /dev/null @@ -1,122 +0,0 @@ -package org.tasks.ui - -import android.app.Dialog -import android.content.Intent -import android.content.res.Configuration -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.FrameLayout -import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.flowWithLifecycle -import androidx.lifecycle.lifecycleScope -import androidx.recyclerview.widget.DefaultItemAnimator -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import com.google.android.material.bottomsheet.BottomSheetBehavior -import com.google.android.material.bottomsheet.BottomSheetDialog -import com.google.android.material.bottomsheet.BottomSheetDialogFragment -import com.todoroo.astrid.adapter.NavigationDrawerAdapter -import com.todoroo.astrid.api.Filter -import com.todoroo.astrid.api.FilterListItem -import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import org.tasks.R -import org.tasks.billing.PurchaseActivity -import org.tasks.extensions.Context.openUri -import org.tasks.filters.NavigationDrawerAction -import org.tasks.intents.TaskIntents -import org.tasks.preferences.Preferences -import javax.inject.Inject - -@AndroidEntryPoint -class NavigationDrawerFragment : BottomSheetDialogFragment() { - @Inject lateinit var adapter: NavigationDrawerAdapter - @Inject lateinit var preferences: Preferences - - override fun getTheme() = R.style.CustomBottomSheetDialog - - private lateinit var recyclerView: RecyclerView - private val viewModel: NavigationDrawerViewModel by activityViewModels() - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - arguments?.getParcelable(EXTRA_SELECTED)?.let { - viewModel.setSelected(it) - } - } - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val dialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog - dialog.setOnShowListener { - val bottomSheet = - dialog.findViewById(com.google.android.material.R.id.design_bottom_sheet) - val behavior = BottomSheetBehavior.from(bottomSheet!!) - behavior.skipCollapsed = true - if (preferences.isTopAppBar) { - behavior.state = BottomSheetBehavior.STATE_EXPANDED - } else if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) { - behavior.state = BottomSheetBehavior.STATE_HALF_EXPANDED - } - } - return dialog - } - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { - val layout = inflater.inflate(R.layout.fragment_navigation_drawer, container, false) - recyclerView = layout.findViewById(R.id.recycler_view) - (recyclerView.itemAnimator as DefaultItemAnimator).supportsChangeAnimations = false - adapter.setOnClick(this::onFilterItemSelected) - recyclerView.layoutManager = LinearLayoutManager(context) - recyclerView.adapter = adapter - viewModel - .viewState - .flowWithLifecycle(lifecycle, Lifecycle.State.RESUMED) - .onEach { - adapter.setSelected(it.selected) - adapter.submitList(it.filters) - } - .launchIn(lifecycleScope) - return layout - } - - private fun onFilterItemSelected(item: FilterListItem?) { - if (item is Filter) { - viewModel.setSelected(item) - activity?.startActivity(TaskIntents.getTaskListIntent(activity, item)) - } else if (item is NavigationDrawerAction) { - when (item.requestCode) { - REQUEST_PURCHASE -> - startActivity(Intent(context, PurchaseActivity::class.java)) - REQUEST_DONATE -> context?.openUri(R.string.url_donate) - else -> item.intent?.let { - activity?.startActivityForResult(it, item.requestCode) - } - } - } - dismiss() - } - - companion object { - const val REQUEST_NEW_LIST = 10100 - const val REQUEST_SETTINGS = 10101 - const val REQUEST_PURCHASE = 10102 - const val REQUEST_DONATE = 10103 - const val REQUEST_NEW_PLACE = 10104 - const val REQUEST_NEW_FILTER = 101015 - private const val EXTRA_SELECTED = "extra_selected" - - fun newNavigationDrawer(selected: Filter?): NavigationDrawerFragment { - val fragment = NavigationDrawerFragment() - fragment.arguments = Bundle().apply { - putParcelable(EXTRA_SELECTED, selected) - } - return fragment - } - } -} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/ui/NavigationDrawerViewModel.kt b/app/src/main/java/org/tasks/ui/NavigationDrawerViewModel.kt deleted file mode 100644 index cef8c0e7c..000000000 --- a/app/src/main/java/org/tasks/ui/NavigationDrawerViewModel.kt +++ /dev/null @@ -1,75 +0,0 @@ -package org.tasks.ui - -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.todoroo.astrid.api.Filter -import com.todoroo.astrid.api.Filter.Companion.NO_COUNT -import com.todoroo.astrid.api.FilterListItem -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.launch -import org.tasks.LocalBroadcastManager -import org.tasks.data.TaskDao -import org.tasks.filters.FilterProvider -import timber.log.Timber -import javax.inject.Inject - -@HiltViewModel -class NavigationDrawerViewModel @Inject constructor( - private val filterProvider: FilterProvider, - private val taskDao: TaskDao, - private val localBroadcastManager: LocalBroadcastManager, -) : ViewModel() { - data class ViewState( - val selected: Filter? = null, - val filters: List = emptyList(), - ) - - private val _viewState = MutableStateFlow(ViewState()) - private val refreshReceiver = object : BroadcastReceiver() { - override fun onReceive(context: Context?, intent: Intent?) { - when (intent?.action) { - LocalBroadcastManager.REFRESH, - LocalBroadcastManager.REFRESH_LIST -> updateFilters() - } - } - } - - val viewState: StateFlow - get() = _viewState.asStateFlow() - - fun setSelected(filter: Filter?) { - _viewState.update { it.copy(selected = filter) } - } - - fun updateFilters() = viewModelScope.launch { - filterProvider - .navDrawerItems() - .onEach { - if (it is Filter && it.count == NO_COUNT) { - it.count = try { - taskDao.count(it) - } catch (e: Exception) { - Timber.e(e) - 0 - } - } - } - .let { filters -> _viewState.update { it.copy(filters = filters) } } - } - - override fun onCleared() { - localBroadcastManager.unregisterReceiver(refreshReceiver) - } - - init { - localBroadcastManager.registerRefreshListReceiver(refreshReceiver) - updateFilters() - } -} \ No newline at end of file diff --git a/app/src/main/res/layout-w820dp/task_list_activity.xml b/app/src/main/res/layout-w820dp/task_list_activity.xml index aa0ffef91..130a1e463 100644 --- a/app/src/main/res/layout-w820dp/task_list_activity.xml +++ b/app/src/main/res/layout-w820dp/task_list_activity.xml @@ -29,5 +29,10 @@ + + diff --git a/app/src/main/res/layout/filter_adapter_action.xml b/app/src/main/res/layout/filter_adapter_action.xml deleted file mode 100644 index d02a7eebe..000000000 --- a/app/src/main/res/layout/filter_adapter_action.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/layout/filter_adapter_separator.xml b/app/src/main/res/layout/filter_adapter_separator.xml deleted file mode 100644 index d159998e0..000000000 --- a/app/src/main/res/layout/filter_adapter_separator.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - diff --git a/app/src/main/res/layout/fragment_navigation_drawer.xml b/app/src/main/res/layout/fragment_navigation_drawer.xml deleted file mode 100644 index b2d157165..000000000 --- a/app/src/main/res/layout/fragment_navigation_drawer.xml +++ /dev/null @@ -1,8 +0,0 @@ - diff --git a/app/src/main/res/layout/task_list_activity.xml b/app/src/main/res/layout/task_list_activity.xml index f7eff6f0b..2cd2d2480 100644 --- a/app/src/main/res/layout/task_list_activity.xml +++ b/app/src/main/res/layout/task_list_activity.xml @@ -18,5 +18,10 @@ android:visibility="gone" tools:ignore="InconsistentLayout" /> + + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index dd17d19d2..4b5d3deda 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -264,19 +264,4 @@ ?attr/colorSecondary - - - - - diff --git a/compose-metrics/app_googleplayDebug-classes.txt b/compose-metrics/app_googleplayDebug-classes.txt index 7e783ac31..3e11def1e 100644 --- a/compose-metrics/app_googleplayDebug-classes.txt +++ b/compose-metrics/app_googleplayDebug-classes.txt @@ -148,11 +148,33 @@ unstable class MainActivity { runtime var alarmDao: AlarmDao unstable var eventBus: MutableSharedFlow{ org.tasks.ui.MainActivityEventBus } unstable var firebase: Firebase + unstable val viewModel$delegate: Lazy stable var currentNightMode: Int stable var currentPro: Boolean - runtime var filter: Filter? unstable var actionMode: ActionMode? unstable var binding: TaskListActivityBinding + unstable val settingsRequest: ActivityResultLauncher<@[FlexibleNullability] Intent?> + = Unstable +} +stable class State { + stable val begForMoney: Boolean + runtime val filter: Filter? + stable val drawerOpen: Boolean + runtime val drawerItems: ImmutableList + = +} +unstable class MainActivityViewModel { + unstable val defaultFilterProvider: DefaultFilterProvider + unstable val filterProvider: FilterProvider + stable val taskDao: TaskDao + unstable val localBroadcastManager: LocalBroadcastManager + unstable val inventory: Inventory + unstable val colorProvider: ColorProvider + stable val caldavDao: CaldavDao + unstable val preferences: Preferences + unstable val _state: MutableStateFlow + unstable val state: StateFlow + stable val refreshReceiver: = Unstable } unstable class ShareLinkActivity { @@ -222,14 +244,6 @@ unstable class TaskListFragment { unstable val listSettingsRequest: ActivityResultLauncher<@[FlexibleNullability] Intent?> = Unstable } -unstable class ActionViewHolder { - unstable val context: Context - stable val onClick: Function1 - unstable val row: View - unstable val text: TextView - unstable val icon: ImageView - = Unstable -} unstable class AstridTaskAdapter { unstable val list: TaskListMetadata runtime val filter: AstridOrderingFilter @@ -267,7 +281,6 @@ unstable class NavigationDrawerAdapter { unstable val colorProvider: ColorProvider unstable val subheaderClickHandler: SubheaderClickHandler stable var onClick: Function1 - runtime var selected: Filter? unstable val channel: Channel> unstable val updates: Queue, DiffResult?>> unstable val scope: CoroutineScope @@ -275,9 +288,6 @@ unstable class NavigationDrawerAdapter { stable var dragging: Boolean = Unstable } -stable class SeparatorViewHolder { - = Stable -} unstable class SubheaderClickHandler { unstable val activity: Activity unstable val preferences: Preferences @@ -329,37 +339,34 @@ stable class BooleanCriterion { unstable class CaldavFilter { unstable val calendar: CaldavCalendar stable val principals: Int - stable var count: Int + stable val count: Int = Unstable } -unstable class CustomFilter { +stable class CustomFilter { stable val filter: Filter - stable var count: Int - = Unstable + = Stable } -unstable class FilterImpl { +stable class FilterImpl { stable val title: String? stable val sql: String? stable val valuesForNewTasks: String? stable val icon: Int stable val tint: Int - stable var count: Int - = Unstable + = Stable } unstable class GtasksFilter { unstable val list: CaldavCalendar - stable var count: Int + stable val count: Int = Unstable } -unstable class SearchFilter { +stable class SearchFilter { stable val title: String stable val query: String - stable var count: Int - = Unstable + = Stable } unstable class TagFilter { unstable val tagData: TagData - stable var count: Int + stable val count: Int stable var filterOverride: String? = Unstable } @@ -758,7 +765,7 @@ unstable class FilterSettingsActivity { unstable var nameLayout: TextInputLayout unstable var recyclerView: RecyclerView unstable var fab: ExtendedFloatingActionButton - unstable var filter: CustomFilter? + stable var filter: CustomFilter? unstable var adapter: CustomFilterAdapter unstable var criteria: MutableList stable val defaultIcon: Int @@ -1277,6 +1284,24 @@ unstable class PurchaseText { unstable val featureList: List = Unstable } +stable class Filter { + stable val title: String + stable val icon: Int + stable val color: Int + stable val count: Int + stable val shareCount: Int + stable val selected: Boolean + stable val type: Function0 + = Stable +} +stable class Header { + stable val title: String + stable val collapsed: Boolean + stable val hasError: Boolean + stable val canAdd: Boolean + stable val type: Function0 + = Stable +} unstable class DashClockExtension { unstable val job: CompletableJob unstable val scope: CoroutineScope @@ -1907,7 +1932,6 @@ unstable class FilterCriteriaProvider { } unstable class FilterProvider { unstable val context: Context - unstable val inventory: Inventory unstable val builtInFilterExposer: BuiltInFilterExposer runtime val filterDao: FilterDao stable val tagDataDao: TagDataDao @@ -1930,21 +1954,8 @@ unstable class LocationFilters { unstable class MyTasksFilter { stable val title: String stable var filterOverride: String? - stable var count: Int - = Unstable -} -unstable class NavigationDrawerAction { - stable val title: String - stable val icon: Int - stable val requestCode: Int - unstable val intent: Intent? - stable val itemType: Type = Unstable } -stable class NavigationDrawerSeparator { - stable val itemType: Type - = Stable -} unstable class NavigationDrawerSubheader { stable val title: String? stable val error: Boolean @@ -1956,25 +1967,22 @@ unstable class NavigationDrawerSubheader { stable val itemType: Type = Unstable } -unstable class NotificationsFilter { +stable class NotificationsFilter { stable val title: String - stable var count: Int - = Unstable + = Stable } -unstable class PlaceFilter { +stable class PlaceFilter { stable val place: Place - stable var count: Int - = Unstable + stable val count: Int + = Stable } -unstable class RecentlyModifiedFilter { +stable class RecentlyModifiedFilter { stable val title: String - stable var count: Int - = Unstable + = Stable } -unstable class SnoozedFilter { +stable class SnoozedFilter { stable val title: String - stable var count: Int - = Unstable + = Stable } unstable class TagFilters { unstable var tagData: TagData @@ -1984,7 +1992,6 @@ unstable class TagFilters { unstable class TodayFilter { stable val title: String stable var filterOverride: String? - stable var count: Int = Unstable } unstable class CommentBarFragment { @@ -2857,7 +2864,7 @@ unstable class TagPickerActivity { unstable var inventory: Inventory unstable var colorProvider: ColorProvider unstable val viewModel$delegate: Lazy - unstable var taskIds: ArrayList? + unstable var taskIds: ArrayList?{ kotlin.collections.TypeAliasesKt.ArrayList? } unstable var editText: EditText = Unstable } @@ -3062,26 +3069,6 @@ unstable class OpenTask { stable class ClearTaskEditFragment { = Stable } -unstable class NavigationDrawerFragment { - unstable var adapter: NavigationDrawerAdapter - unstable var preferences: Preferences - unstable var recyclerView: RecyclerView - unstable val viewModel$delegate: Lazy - = Unstable -} -unstable class ViewState { - runtime val selected: Filter? - unstable val filters: List - = Unstable -} -unstable class NavigationDrawerViewModel { - unstable val filterProvider: FilterProvider - stable val taskDao: TaskDao - unstable val localBroadcastManager: LocalBroadcastManager - unstable val _viewState: MutableStateFlow - stable val refreshReceiver: - = Unstable -} unstable class SubtaskControlSet { unstable var activity: Activity unstable var taskCompleter: TaskCompleter diff --git a/compose-metrics/app_googleplayDebug-composables.txt b/compose-metrics/app_googleplayDebug-composables.txt index e29e6df42..74e1baf96 100644 --- a/compose-metrics/app_googleplayDebug-composables.txt +++ b/compose-metrics/app_googleplayDebug-composables.txt @@ -397,6 +397,42 @@ restartable skippable scheme("[androidx.compose.ui.UiComposable, [androidx.compo stable content: Function2 stable onClick: Function0? = @static null ) +restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun TaskListDrawer( + stable begForMoney: Boolean + filters: ImmutableList + stable onClick: Function1 + stable onDrawerAction: Function1 + stable onAddClick: Function1 + stable onErrorClick: Function0 +) +restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun FilterItem( + stable item: Filter + stable onClick: Function0 +) +restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun MenuAction( + stable icon: Int + stable title: Int + stable onClick: Function0 +) +restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun DrawerIcon( + stable icon: Int + stable color: Int = @static 0 +) +restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun HeaderItem( + stable item: Header + stable canAdd: Boolean + stable toggleCollapsed: Function0 + stable onAddClick: Function0 + stable onErrorClick: Function0 +) +restartable skippable scheme("[androidx.compose.ui.UiComposable, [androidx.compose.ui.UiComposable]]") fun MenuRow( + stable padding: PaddingValues? = @static PaddingValues( + horizontal = 16 . dp +) + stable onClick: Function0 + stable content: @[ExtensionFunctionType] Function3 +) +restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun MenuPreview() restartable scheme("[androidx.compose.ui.UiComposable]") fun AlarmRow( unstable vm: ReminderControlSetViewModel? = @dynamic viewModel(null, null, null, null, $composer, 0, 0b1111) stable permissionStatus: PermissionStatus diff --git a/compose-metrics/app_googleplayDebug-module.json b/compose-metrics/app_googleplayDebug-module.json index e44329ed1..8561715fb 100644 --- a/compose-metrics/app_googleplayDebug-module.json +++ b/compose-metrics/app_googleplayDebug-module.json @@ -1,25 +1,25 @@ { - "skippableComposables": 365, - "restartableComposables": 489, + "skippableComposables": 387, + "restartableComposables": 514, "readonlyComposables": 0, - "totalComposables": 495, - "restartGroups": 489, - "totalGroups": 598, - "staticArguments": 762, - "certainArguments": 314, - "knownStableArguments": 4864, - "knownUnstableArguments": 139, - "unknownStableArguments": 9, - "totalArguments": 5012, + "totalComposables": 520, + "restartGroups": 514, + "totalGroups": 630, + "staticArguments": 792, + "certainArguments": 329, + "knownStableArguments": 5083, + "knownUnstableArguments": 138, + "unknownStableArguments": 11, + "totalArguments": 5232, "markedStableClasses": 0, - "inferredStableClasses": 100, - "inferredUnstableClasses": 345, + "inferredStableClasses": 108, + "inferredUnstableClasses": 334, "inferredUncertainClasses": 1, - "effectivelyStableClasses": 100, - "totalClasses": 446, - "memoizedLambdas": 524, - "singletonLambdas": 182, - "singletonComposableLambdas": 90, - "composableLambdas": 223, - "totalLambdas": 633 + "effectivelyStableClasses": 108, + "totalClasses": 443, + "memoizedLambdas": 549, + "singletonLambdas": 188, + "singletonComposableLambdas": 94, + "composableLambdas": 238, + "totalLambdas": 667 } \ No newline at end of file diff --git a/deps_fdroid.txt b/deps_fdroid.txt index 96faa60aa..6e2d7ae85 100644 --- a/deps_fdroid.txt +++ b/deps_fdroid.txt @@ -578,6 +578,10 @@ ++--- io.noties.markwon:ext-tasklist:4.6.2 +| \--- io.noties.markwon:core:4.6.2 (*) ++--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 (*) +++--- org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.6 ++| \--- org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm:0.3.6 ++| +--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0 -> 1.9.10 (*) ++| \--- org.jetbrains.kotlin:kotlin-stdlib-common:1.9.0 -> 1.9.10 ++--- com.squareup.okhttp3:okhttp:4.12.0 (*) ++--- com.github.franmontiel:PersistentCookieJar:1.0.1 +| \--- com.squareup.okhttp3:okhttp:3.1.2 -> 4.12.0 (*) diff --git a/deps_googleplay.txt b/deps_googleplay.txt index 420f6d472..e0566cc5f 100644 --- a/deps_googleplay.txt +++ b/deps_googleplay.txt @@ -793,6 +793,10 @@ ++--- io.noties.markwon:ext-tasklist:4.6.2 +| \--- io.noties.markwon:core:4.6.2 (*) ++--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 (*) +++--- org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.6 ++| \--- org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm:0.3.6 ++| +--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0 -> 1.9.10 (*) ++| \--- org.jetbrains.kotlin:kotlin-stdlib-common:1.9.0 -> 1.9.10 ++--- com.squareup.okhttp3:okhttp:4.12.0 (*) ++--- com.github.franmontiel:PersistentCookieJar:1.0.1 +| \--- com.squareup.okhttp3:okhttp:3.1.2 -> 4.12.0 (*) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6ffac763d..baf4eec77 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -126,6 +126,7 @@ gson = { module = "com.google.code.gson:gson", version.ref = "gson" } jchronic = { module = "com.rubiconproject.oss:jchronic", version.ref = "jchronic" } junit = { module = "junit:junit", version.ref = "junit-junit" } kotlin-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } +kotlin-immutable = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable", version = "0.3.6" } kotlin-jdk8 = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" } kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" } kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinx-coroutines-test" }