From eba7d137ee5c6adfc71f448ec378ef9579cc8d95 Mon Sep 17 00:00:00 2001 From: Jobobby04 Date: Sat, 10 Sep 2022 14:06:39 -0400 Subject: [PATCH] Convert biometric times to compose --- .../category/BiometricTimesScreen.kt | 91 ++++++ .../category/BiometricTimesState.kt | 28 ++ .../biometric/BiometricTimesContent.kt | 34 ++ .../biometric/BiometricTimesListItem.kt | 45 +++ .../biometric/BiometricTimesAdapter.kt | 30 -- .../biometric/BiometricTimesController.kt | 300 +----------------- .../biometric/BiometricTimesHolder.kt | 33 -- .../category/biometric/BiometricTimesItem.kt | 62 ---- .../biometric/BiometricTimesPresenter.kt | 86 ++--- .../ui/category/biometric/TimeRangeItem.kt | 3 + .../main/res/layout/categories_controller.xml | 23 -- app/src/main/res/layout/categories_item.xml | 42 --- app/src/main/res/menu/category_selection.xml | 19 -- app/src/main/res/values/strings_sy.xml | 2 + 14 files changed, 269 insertions(+), 529 deletions(-) create mode 100644 app/src/main/java/eu/kanade/presentation/category/BiometricTimesScreen.kt create mode 100644 app/src/main/java/eu/kanade/presentation/category/BiometricTimesState.kt create mode 100644 app/src/main/java/eu/kanade/presentation/category/components/biometric/BiometricTimesContent.kt create mode 100644 app/src/main/java/eu/kanade/presentation/category/components/biometric/BiometricTimesListItem.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/category/biometric/BiometricTimesAdapter.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/category/biometric/BiometricTimesHolder.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/category/biometric/BiometricTimesItem.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/category/biometric/TimeRangeItem.kt delete mode 100644 app/src/main/res/layout/categories_controller.xml delete mode 100755 app/src/main/res/layout/categories_item.xml delete mode 100755 app/src/main/res/menu/category_selection.xml diff --git a/app/src/main/java/eu/kanade/presentation/category/BiometricTimesScreen.kt b/app/src/main/java/eu/kanade/presentation/category/BiometricTimesScreen.kt new file mode 100644 index 000000000..a93ff3fae --- /dev/null +++ b/app/src/main/java/eu/kanade/presentation/category/BiometricTimesScreen.kt @@ -0,0 +1,91 @@ +package eu.kanade.presentation.category + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import eu.kanade.presentation.category.components.CategoryDeleteDialog +import eu.kanade.presentation.category.components.CategoryFloatingActionButton +import eu.kanade.presentation.category.components.biometric.BiometricTimesContent +import eu.kanade.presentation.components.AppBar +import eu.kanade.presentation.components.EmptyScreen +import eu.kanade.presentation.components.LoadingScreen +import eu.kanade.presentation.components.Scaffold +import eu.kanade.presentation.util.horizontalPadding +import eu.kanade.presentation.util.plus +import eu.kanade.presentation.util.topPaddingValues +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.ui.category.biometric.BiometricTimesPresenter +import eu.kanade.tachiyomi.ui.category.biometric.BiometricTimesPresenter.Dialog +import eu.kanade.tachiyomi.util.system.toast +import kotlinx.coroutines.flow.collectLatest +import kotlin.time.Duration + +@Composable +fun BiometricTimesScreen( + presenter: BiometricTimesPresenter, + navigateUp: () -> Unit, + openCreateDialog: (Duration?) -> Unit, +) { + val lazyListState = rememberLazyListState() + Scaffold( + topBar = { scrollBehavior -> + AppBar( + navigateUp = navigateUp, + title = stringResource(R.string.biometric_lock_times), + scrollBehavior = scrollBehavior, + ) + }, + floatingActionButton = { + CategoryFloatingActionButton( + lazyListState = lazyListState, + onCreate = { presenter.dialog = Dialog.Create }, + ) + }, + ) { paddingValues -> + val context = LocalContext.current + when { + presenter.isLoading -> LoadingScreen() + presenter.isEmpty -> EmptyScreen(textResource = R.string.biometric_lock_times_empty) + else -> { + BiometricTimesContent( + state = presenter, + lazyListState = lazyListState, + paddingValues = paddingValues + topPaddingValues + PaddingValues(horizontal = horizontalPadding), + ) + } + } + + val onDismissRequest = { presenter.dialog = null } + when (val dialog = presenter.dialog) { + Dialog.Create -> { + LaunchedEffect(Unit) { + openCreateDialog(null) + } + } + is Dialog.Delete -> { + CategoryDeleteDialog( + onDismissRequest = onDismissRequest, + onDelete = { presenter.deleteTimeRanges(dialog.timeRange) }, + title = stringResource(R.string.delete_time_range), + text = stringResource(R.string.delete_time_range_confirmation, dialog.timeRange.formattedString), + ) + } + else -> {} + } + LaunchedEffect(Unit) { + presenter.events.collectLatest { event -> + when (event) { + is BiometricTimesPresenter.Event.TimeConflicts -> { + context.toast(R.string.biometric_lock_time_conflicts) + } + is BiometricTimesPresenter.Event.InternalError -> { + context.toast(R.string.internal_error) + } + } + } + } + } +} diff --git a/app/src/main/java/eu/kanade/presentation/category/BiometricTimesState.kt b/app/src/main/java/eu/kanade/presentation/category/BiometricTimesState.kt new file mode 100644 index 000000000..d73cc864b --- /dev/null +++ b/app/src/main/java/eu/kanade/presentation/category/BiometricTimesState.kt @@ -0,0 +1,28 @@ +package eu.kanade.presentation.category + +import androidx.compose.runtime.Stable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import eu.kanade.tachiyomi.ui.category.biometric.BiometricTimesPresenter +import eu.kanade.tachiyomi.ui.category.biometric.TimeRangeItem + +@Stable +interface BiometricTimesState { + val isLoading: Boolean + var dialog: BiometricTimesPresenter.Dialog? + val timeRanges: List + val isEmpty: Boolean +} + +fun BiometricTimesState(): BiometricTimesState { + return BiometricTimesStateImpl() +} + +class BiometricTimesStateImpl : BiometricTimesState { + override var isLoading: Boolean by mutableStateOf(true) + override var dialog: BiometricTimesPresenter.Dialog? by mutableStateOf(null) + override var timeRanges: List by mutableStateOf(emptyList()) + override val isEmpty: Boolean by derivedStateOf { timeRanges.isEmpty() } +} diff --git a/app/src/main/java/eu/kanade/presentation/category/components/biometric/BiometricTimesContent.kt b/app/src/main/java/eu/kanade/presentation/category/components/biometric/BiometricTimesContent.kt new file mode 100644 index 000000000..e0be2e38c --- /dev/null +++ b/app/src/main/java/eu/kanade/presentation/category/components/biometric/BiometricTimesContent.kt @@ -0,0 +1,34 @@ +package eu.kanade.presentation.category.components.biometric + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.foundation.lazy.items +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import eu.kanade.presentation.category.BiometricTimesState +import eu.kanade.presentation.components.LazyColumn +import eu.kanade.tachiyomi.ui.category.biometric.BiometricTimesPresenter + +@Composable +fun BiometricTimesContent( + state: BiometricTimesState, + lazyListState: LazyListState, + paddingValues: PaddingValues, +) { + val timeRanges = state.timeRanges + LazyColumn( + state = lazyListState, + contentPadding = paddingValues, + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + items(timeRanges) { timeRange -> + BiometricTimesListItem( + modifier = Modifier.animateItemPlacement(), + timeRange = timeRange, + onDelete = { state.dialog = BiometricTimesPresenter.Dialog.Delete(timeRange) }, + ) + } + } +} diff --git a/app/src/main/java/eu/kanade/presentation/category/components/biometric/BiometricTimesListItem.kt b/app/src/main/java/eu/kanade/presentation/category/components/biometric/BiometricTimesListItem.kt new file mode 100644 index 000000000..fd666ac43 --- /dev/null +++ b/app/src/main/java/eu/kanade/presentation/category/components/biometric/BiometricTimesListItem.kt @@ -0,0 +1,45 @@ +package eu.kanade.presentation.category.components.biometric + +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.material.icons.Icons +import androidx.compose.material.icons.outlined.Delete +import androidx.compose.material.icons.outlined.Label +import androidx.compose.material3.ElevatedCard +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import eu.kanade.presentation.util.horizontalPadding +import eu.kanade.tachiyomi.ui.category.biometric.TimeRangeItem + +@Composable +fun BiometricTimesListItem( + modifier: Modifier, + timeRange: TimeRangeItem, + onDelete: () -> Unit, +) { + ElevatedCard( + modifier = modifier, + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(start = horizontalPadding, top = horizontalPadding, end = horizontalPadding), + verticalAlignment = Alignment.CenterVertically, + ) { + Icon(imageVector = Icons.Outlined.Label, contentDescription = "") + Text(text = timeRange.formattedString, modifier = Modifier.padding(start = horizontalPadding)) + } + Row { + Spacer(modifier = Modifier.weight(1f)) + IconButton(onClick = onDelete) { + Icon(imageVector = Icons.Outlined.Delete, contentDescription = "") + } + } + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/category/biometric/BiometricTimesAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/category/biometric/BiometricTimesAdapter.kt deleted file mode 100644 index 7e278c39d..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/category/biometric/BiometricTimesAdapter.kt +++ /dev/null @@ -1,30 +0,0 @@ -package eu.kanade.tachiyomi.ui.category.biometric - -import eu.davidea.flexibleadapter.FlexibleAdapter - -/** - * Custom adapter for categories. - * - * @param controller The containing controller. - */ -class BiometricTimesAdapter(controller: BiometricTimesController) : - FlexibleAdapter(null, controller, true) { - - /** - * Clears the active selections from the list and the model. - */ - override fun clearSelection() { - super.clearSelection() - (0 until itemCount).forEach { getItem(it)?.isSelected = false } - } - - /** - * Toggles the selection of the given position. - * - * @param position The position to toggle. - */ - override fun toggleSelection(position: Int) { - super.toggleSelection(position) - getItem(position)?.isSelected = isSelected(position) - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/category/biometric/BiometricTimesController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/category/biometric/BiometricTimesController.kt index 34f494e4e..a5daeada7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/category/biometric/BiometricTimesController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/category/biometric/BiometricTimesController.kt @@ -1,27 +1,11 @@ package eu.kanade.tachiyomi.ui.category.biometric -import android.view.LayoutInflater -import android.view.Menu -import android.view.MenuItem -import android.view.View -import androidx.appcompat.app.AppCompatActivity -import androidx.appcompat.view.ActionMode -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton -import com.google.android.material.snackbar.Snackbar +import androidx.compose.runtime.Composable import com.google.android.material.timepicker.MaterialTimePicker -import dev.chrisbanes.insetter.applyInsetter -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.SelectableAdapter -import eu.davidea.flexibleadapter.helpers.UndoHelper +import eu.kanade.presentation.category.BiometricTimesScreen import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.databinding.CategoriesControllerBinding -import eu.kanade.tachiyomi.ui.base.controller.FabController -import eu.kanade.tachiyomi.ui.base.controller.NucleusController +import eu.kanade.tachiyomi.ui.base.controller.FullComposeController import eu.kanade.tachiyomi.ui.main.MainActivity -import eu.kanade.tachiyomi.util.system.toast -import eu.kanade.tachiyomi.util.view.shrinkOnScroll import kotlin.time.Duration import kotlin.time.Duration.Companion.hours import kotlin.time.Duration.Companion.minutes @@ -29,276 +13,20 @@ import kotlin.time.Duration.Companion.minutes /** * Controller to manage the lock times for the biometric lock. */ -class BiometricTimesController : - NucleusController(), - FabController, - ActionMode.Callback, - FlexibleAdapter.OnItemClickListener, - FlexibleAdapter.OnItemLongClickListener, - UndoHelper.OnActionListener { +class BiometricTimesController : FullComposeController() { - /** - * Object used to show ActionMode toolbar. - */ - private var actionMode: ActionMode? = null - - /** - * Adapter containing biometric lock time items. - */ - private var adapter: BiometricTimesAdapter? = null - - private var actionFab: ExtendedFloatingActionButton? = null - private var actionFabScrollListener: RecyclerView.OnScrollListener? = null - - /** - * Undo helper used for restoring a deleted lock time. - */ - private var undoHelper: UndoHelper? = null - - /** - * Creates the presenter for this controller. Not to be manually called. - */ override fun createPresenter() = BiometricTimesPresenter() - /** - * Returns the toolbar title to show when this controller is attached. - */ - override fun getTitle(): String? { - return resources?.getString(R.string.biometric_lock_times) + @Composable + override fun ComposeContent() { + BiometricTimesScreen( + presenter = presenter, + navigateUp = router::popCurrentController, + openCreateDialog = ::showTimePicker, + ) } - override fun createBinding(inflater: LayoutInflater) = CategoriesControllerBinding.inflate(inflater) - - /** - * Called after view inflation. Used to initialize the view. - * - * @param view The view of this controller. - */ - override fun onViewCreated(view: View) { - super.onViewCreated(view) - - binding.recycler.applyInsetter { - type(navigationBars = true) { - padding() - } - } - - adapter = BiometricTimesAdapter(this@BiometricTimesController) - binding.recycler.layoutManager = LinearLayoutManager(view.context) - binding.recycler.setHasFixedSize(true) - binding.recycler.adapter = adapter - adapter?.isPermanentDelete = false - - actionFabScrollListener = actionFab?.shrinkOnScroll(binding.recycler) - } - - override fun configureFab(fab: ExtendedFloatingActionButton) { - actionFab = fab - fab.setText(R.string.action_add) - fab.setIconResource(R.drawable.ic_add_24dp) - fab.setOnClickListener { - showTimePicker() - } - } - - override fun cleanupFab(fab: ExtendedFloatingActionButton) { - fab.setOnClickListener(null) - actionFabScrollListener?.let { binding.recycler.removeOnScrollListener(it) } - actionFab = null - } - - /** - * Called when the view is being destroyed. Used to release references and remove callbacks. - * - * @param view The view of this controller. - */ - override fun onDestroyView(view: View) { - // Manually call callback to delete lock times if required - undoHelper?.onDeleteConfirmed(Snackbar.Callback.DISMISS_EVENT_MANUAL) - undoHelper = null - actionMode = null - adapter = null - super.onDestroyView(view) - } - - /** - * Called from the presenter when the biometric lock times are updated. - * - * @param biometricTimeItems The new list of lock times to display. - */ - fun setBiometricTimeItems(biometricTimeItems: List) { - actionMode?.finish() - adapter?.updateDataSet(biometricTimeItems) - if (biometricTimeItems.isNotEmpty()) { - binding.emptyView.hide() - val selected = biometricTimeItems.filter { it.isSelected } - if (selected.isNotEmpty()) { - selected.forEach { onItemLongClick(biometricTimeItems.indexOf(it)) } - } - } else { - binding.emptyView.show(R.string.biometric_lock_times_empty) - } - } - - /** - * Called when action mode is first created. The menu supplied will be used to generate action - * buttons for the action mode. - * - * @param mode ActionMode being created. - * @param menu Menu used to populate action buttons. - * @return true if the action mode should be created, false if entering this mode should be - * aborted. - */ - override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean { - // Inflate menu. - mode.menuInflater.inflate(R.menu.category_selection, menu) - // Enable adapter multi selection. - adapter?.mode = SelectableAdapter.Mode.MULTI - return true - } - - /** - * Called to refresh an action mode's action menu whenever it is invalidated. - * - * @param mode ActionMode being prepared. - * @param menu Menu used to populate action buttons. - * @return true if the menu or action mode was updated, false otherwise. - */ - override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean { - val adapter = adapter ?: return false - val count = adapter.selectedItemCount - mode.title = count.toString() - - // Show edit button only when one item is selected - val editItem = mode.menu.findItem(R.id.action_edit) - editItem.isVisible = false - return true - } - - /** - * Called to report a user click on an action button. - * - * @param mode The current ActionMode. - * @param item The item that was clicked. - * @return true if this callback handled the event, false if the standard MenuItem invocation - * should continue. - */ - override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { - val adapter = adapter ?: return false - - when (item.itemId) { - R.id.action_delete -> { - undoHelper = UndoHelper(adapter, this) - undoHelper?.start( - adapter.selectedPositions, - (activity as? MainActivity)?.binding?.rootCoordinator!!, - R.string.biometric_lock_time_deleted_snack, - R.string.action_undo, - 3000, - ) - - mode.finish() - } - else -> return false - } - return true - } - - /** - * Called when an action mode is about to be exited and destroyed. - * - * @param mode The current ActionMode being destroyed. - */ - override fun onDestroyActionMode(mode: ActionMode) { - // Reset adapter to single selection - adapter?.mode = SelectableAdapter.Mode.IDLE - adapter?.clearSelection() - actionMode = null - } - - /** - * Called when an item in the list is clicked. - * - * @param position The position of the clicked item. - * @return true if this click should enable selection mode. - */ - override fun onItemClick(view: View, position: Int): Boolean { - // Check if action mode is initialized and selected item exist. - return if (actionMode != null && position != RecyclerView.NO_POSITION) { - toggleSelection(position) - true - } else { - false - } - } - - /** - * Called when an item in the list is long clicked. - * - * @param position The position of the clicked item. - */ - override fun onItemLongClick(position: Int) { - val activity = activity as? AppCompatActivity ?: return - - // Check if action mode is initialized. - if (actionMode == null) { - // Initialize action mode - actionMode = activity.startSupportActionMode(this) - } - - // Set item as selected - toggleSelection(position) - } - - /** - * Toggle the selection state of an item. - * If the item was the last one in the selection and is unselected, the ActionMode is finished. - * - * @param position The position of the item to toggle. - */ - private fun toggleSelection(position: Int) { - val adapter = adapter ?: return - - // Mark the position selected - adapter.toggleSelection(position) - - if (adapter.selectedItemCount == 0) { - actionMode?.finish() - } else { - actionMode?.invalidate() - } - } - - /** - * Called when the undo action is clicked in the snackbar. - * - * @param action The action performed. - */ - override fun onActionCanceled(action: Int, positions: MutableList?) { - adapter?.restoreDeletedItems() - undoHelper = null - } - - /** - * Called when the time to restore the items expires. - * - * @param action The action performed. - * @param event The event that triggered the action - */ - override fun onActionConfirmed(action: Int, event: Int) { - val adapter = adapter ?: return - presenter.deleteTimeRanges(adapter.deletedItems.map { it.timeRange }) - undoHelper = null - } - - /** - * Called from the presenter when a time range conflicts with another. - */ - fun onTimeRangeConflictsError() { - activity?.toast(R.string.biometric_lock_time_conflicts) - } - - fun showTimePicker(startTime: Duration? = null) { + private fun showTimePicker(startTime: Duration? = null) { val picker = MaterialTimePicker.Builder() .setTitleText(if (startTime == null) R.string.biometric_lock_start_time else R.string.biometric_lock_end_time) .setInputMode(MaterialTimePicker.INPUT_MODE_CLOCK) @@ -306,11 +34,15 @@ class BiometricTimesController : picker.addOnPositiveButtonClickListener { val timeRange = picker.hour.hours + picker.minute.minutes if (startTime != null) { + presenter.dialog = null presenter.createTimeRange(TimeRange(startTime, timeRange)) } else { showTimePicker(timeRange) } } + picker.addOnDismissListener { + presenter.dialog = null + } picker.show((activity as MainActivity).supportFragmentManager, null) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/category/biometric/BiometricTimesHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/category/biometric/BiometricTimesHolder.kt deleted file mode 100644 index 2d1817278..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/category/biometric/BiometricTimesHolder.kt +++ /dev/null @@ -1,33 +0,0 @@ -package eu.kanade.tachiyomi.ui.category.biometric - -import android.view.View -import androidx.core.view.isVisible -import eu.davidea.viewholders.FlexibleViewHolder -import eu.kanade.tachiyomi.databinding.CategoriesItemBinding -import eu.kanade.tachiyomi.util.system.dpToPx -import kotlin.time.ExperimentalTime - -/** - * Holder used to display category items. - * - * @param view The view used by category items. - * @param adapter The adapter containing this holder. - */ -class BiometricTimesHolder(view: View, val adapter: BiometricTimesAdapter) : FlexibleViewHolder(view, adapter) { - - private val binding = CategoriesItemBinding.bind(view) - - /** - * Binds this holder with the given category. - * - * @param timeRange The category to bind. - */ - @OptIn(ExperimentalTime::class) - fun bind(timeRange: TimeRange) { - binding.innerContainer.minimumHeight = 60.dpToPx - - // Set capitalized title. - binding.title.text = timeRange.getFormattedString(itemView.context) - binding.reorder.isVisible = false - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/category/biometric/BiometricTimesItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/category/biometric/BiometricTimesItem.kt deleted file mode 100644 index b65c2c229..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/category/biometric/BiometricTimesItem.kt +++ /dev/null @@ -1,62 +0,0 @@ -package eu.kanade.tachiyomi.ui.category.biometric - -import android.view.View -import androidx.recyclerview.widget.RecyclerView -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.kanade.tachiyomi.R - -/** - * Category item for a recycler view. - */ -class BiometricTimesItem(val timeRange: TimeRange) : AbstractFlexibleItem() { - - /** - * Whether this item is currently selected. - */ - var isSelected = false - - /** - * Returns the layout resource for this item. - */ - override fun getLayoutRes(): Int { - return R.layout.categories_item - } - - /** - * Returns a new view holder for this item. - * - * @param view The view of this item. - * @param adapter The adapter of this item. - */ - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): BiometricTimesHolder { - return BiometricTimesHolder(view, adapter as BiometricTimesAdapter) - } - - /** - * Binds the given view holder with this item. - * - * @param adapter The adapter of this item. - * @param holder The holder to bind. - * @param position The position of this item in the adapter. - * @param payloads List of partial changes. - */ - override fun bindViewHolder( - adapter: FlexibleAdapter>, - holder: BiometricTimesHolder, - position: Int, - payloads: List?, - ) { - holder.bind(timeRange) - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - return false - } - - override fun hashCode(): Int { - return timeRange.hashCode() - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/category/biometric/BiometricTimesPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/category/biometric/BiometricTimesPresenter.kt index 62197ab70..fb8209f8d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/category/biometric/BiometricTimesPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/category/biometric/BiometricTimesPresenter.kt @@ -1,44 +1,44 @@ package eu.kanade.tachiyomi.ui.category.biometric +import android.app.Application import android.os.Bundle +import eu.kanade.presentation.category.BiometricTimesState +import eu.kanade.presentation.category.BiometricTimesStateImpl import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter -import eu.kanade.tachiyomi.util.lang.launchUI -import eu.kanade.tachiyomi.util.lang.withUIContext +import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.preference.plusAssign -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.consumeAsFlow import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get /** * Presenter of [BiometricTimesController]. Used to manage the categories of the library. */ -class BiometricTimesPresenter : BasePresenter() { - - /** - * List containing categories. - */ - private var timeRanges: List = emptyList() +class BiometricTimesPresenter( + private val state: BiometricTimesStateImpl = BiometricTimesState() as BiometricTimesStateImpl, +) : BasePresenter(), BiometricTimesState by state { val preferences: PreferencesHelper = Injekt.get() - /** - * Called when the presenter is created. - * - * @param savedState The saved state of this presenter. - */ + private val _events: Channel = Channel(Int.MAX_VALUE) + val events = _events.consumeAsFlow() + override fun onCreate(savedState: Bundle?) { super.onCreate(savedState) - - preferences.authenticatorTimeRanges().asFlow().onEach { prefTimeRanges -> - timeRanges = prefTimeRanges.toList() - .mapNotNull(TimeRange::fromPreferenceString) - - withUIContext { - view?.setBiometricTimeItems(timeRanges.map(::BiometricTimesItem)) - } - }.launchIn(presenterScope) + presenterScope.launchIO { + // todo usecase + preferences.authenticatorTimeRanges().asFlow() + .collectLatest { + val context = view?.activity ?: Injekt.get() + state.isLoading = false + state.timeRanges = it.toList() + .mapNotNull(TimeRange::fromPreferenceString) + .map { TimeRangeItem(it, it.getFormattedString(context)) } + } + } } /** @@ -47,15 +47,16 @@ class BiometricTimesPresenter : BasePresenter() { * @param name The name of the category to create. */ fun createTimeRange(timeRange: TimeRange) { - // Do not allow duplicate categories. - if (timeRangeConflicts(timeRange)) { - launchUI { - view?.onTimeRangeConflictsError() + // todo usecase + presenterScope.launchIO { + // Do not allow duplicate categories. + if (timeRangeConflicts(timeRange)) { + _events.send(Event.TimeConflicts) + return@launchIO } - return - } - preferences.authenticatorTimeRanges() += timeRange.toPreferenceString() + preferences.authenticatorTimeRanges() += timeRange.toPreferenceString() + } } /** @@ -63,16 +64,29 @@ class BiometricTimesPresenter : BasePresenter() { * * @param timeRanges The list of categories to delete. */ - fun deleteTimeRanges(timeRanges: List) { - preferences.authenticatorTimeRanges().set( - this.timeRanges.filterNot { it in timeRanges }.map(TimeRange::toPreferenceString).toSet(), - ) + fun deleteTimeRanges(timeRange: TimeRangeItem) { + // todo usecase + presenterScope.launchIO { + preferences.authenticatorTimeRanges().set( + state.timeRanges.filterNot { it == timeRange }.map { it.timeRange.toPreferenceString() }.toSet(), + ) + } } /** * Returns true if a category with the given name already exists. */ private fun timeRangeConflicts(timeRange: TimeRange): Boolean { - return timeRanges.any { timeRange.conflictsWith(it) } + return timeRanges.any { timeRange.conflictsWith(it.timeRange) } + } + + sealed class Event { + object TimeConflicts : Event() + object InternalError : Event() + } + + sealed class Dialog { + object Create : Dialog() + data class Delete(val timeRange: TimeRangeItem) : Dialog() } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/category/biometric/TimeRangeItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/category/biometric/TimeRangeItem.kt new file mode 100644 index 000000000..3b04c1bdb --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/category/biometric/TimeRangeItem.kt @@ -0,0 +1,3 @@ +package eu.kanade.tachiyomi.ui.category.biometric + +data class TimeRangeItem(val timeRange: TimeRange, val formattedString: String) diff --git a/app/src/main/res/layout/categories_controller.xml b/app/src/main/res/layout/categories_controller.xml deleted file mode 100644 index 7e4f65d62..000000000 --- a/app/src/main/res/layout/categories_controller.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/layout/categories_item.xml b/app/src/main/res/layout/categories_item.xml deleted file mode 100755 index 53e83a938..000000000 --- a/app/src/main/res/layout/categories_item.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - diff --git a/app/src/main/res/menu/category_selection.xml b/app/src/main/res/menu/category_selection.xml deleted file mode 100755 index 95ed1acb9..000000000 --- a/app/src/main/res/menu/category_selection.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/values/strings_sy.xml b/app/src/main/res/values/strings_sy.xml index b18ebf93f..3c6a2c8ae 100644 --- a/app/src/main/res/values/strings_sy.xml +++ b/app/src/main/res/values/strings_sy.xml @@ -394,6 +394,8 @@ Tags deleted Delete tag Do you wish to delete the tag %s + Delete time range + Do you wish to delete the time range %s? Redundant