From 69a26aeedb3317a2473d747e27a10f712d46c018 Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Sun, 13 Aug 2023 22:51:05 +0800 Subject: [PATCH] Implement swipe-to-remove in Downloads and Favorites --- .../com/hippo/ehviewer/ui/CommonOperations.kt | 37 +++++++++++++++---- .../hippo/ehviewer/ui/scene/DownloadsScene.kt | 25 +++++++++---- .../hippo/ehviewer/ui/scene/FavoritesScene.kt | 36 ++++++++++++++++++ .../com/hippo/ehviewer/ui/tools/Dialog.kt | 11 +++++- 4 files changed, 92 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/com/hippo/ehviewer/ui/CommonOperations.kt b/app/src/main/java/com/hippo/ehviewer/ui/CommonOperations.kt index c33006506..a80af410a 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/CommonOperations.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/CommonOperations.kt @@ -20,6 +20,8 @@ import android.content.Context import android.content.Intent import android.os.Build import androidx.appcompat.app.AlertDialog +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Download @@ -28,7 +30,12 @@ import androidx.compose.material.icons.filled.Favorite import androidx.compose.material.icons.filled.FavoriteBorder import androidx.compose.material.icons.filled.HeartBroken import androidx.compose.material.icons.filled.MenuBook +import androidx.compose.material3.Checkbox import androidx.compose.material3.Text +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.res.stringResource import androidx.core.content.ContextCompat import com.hippo.ehviewer.EhDB @@ -55,6 +62,7 @@ import com.hippo.ehviewer.util.requestPermission import com.hippo.unifile.UniFile import eu.kanade.tachiyomi.ui.reader.ReaderActivity import eu.kanade.tachiyomi.util.lang.launchNow +import eu.kanade.tachiyomi.util.lang.withIOContext import eu.kanade.tachiyomi.util.lang.withUIContext import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.sync.Mutex @@ -312,7 +320,6 @@ suspend fun DialogState.doGalleryInfoAction(info: BaseGalleryInfo, context: Cont 1 -> withUIContext { if (downloaded) { confirmRemoveDownload(info) - DownloadManager.deleteDownload(info.gid) } else { CommonOperations.startDownload(this@with, info, false) } @@ -342,12 +349,28 @@ suspend fun DialogState.doGalleryInfoAction(info: BaseGalleryInfo, context: Cont private const val MAX_FAVNOTE_CHAR = 200 -suspend fun DialogState.confirmRemoveDownload(info: GalleryInfo) = awaitPermissionOrCancel( - confirmText = android.R.string.ok, - dismissText = android.R.string.cancel, - title = R.string.download_remove_dialog_title, - text = { Text(text = stringResource(id = R.string.download_remove_dialog_message, info.title.orEmpty())) }, -) +suspend fun DialogState.confirmRemoveDownload(info: GalleryInfo, onDismiss: () -> Unit = {}) { + var checked by mutableStateOf(Settings.removeImageFiles) + awaitPermissionOrCancel( + confirmText = android.R.string.ok, + dismissText = android.R.string.cancel, + title = R.string.download_remove_dialog_title, + onDismiss = onDismiss, + text = { + Column { + Text(text = stringResource(id = R.string.download_remove_dialog_message, info.title.orEmpty())) + Row(verticalAlignment = Alignment.CenterVertically) { + Checkbox(checked = checked, onCheckedChange = { checked = it }) + Text(text = stringResource(id = R.string.download_remove_dialog_check_text)) + } + } + }, + ) + Settings.removeImageFiles = checked + withIOContext { + DownloadManager.deleteDownload(info.gid, checked) + } +} suspend fun DialogState.showMoveDownloadLabel(info: GalleryInfo) { val defaultLabel = appCtx.getString(R.string.default_download_label_name) diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadsScene.kt b/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadsScene.kt index 9cd427282..205afa326 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadsScene.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadsScene.kt @@ -47,6 +47,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp @@ -58,7 +59,6 @@ import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper.ACTION_STATE_DRAG import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import androidx.recyclerview.widget.SimpleItemAnimator import androidx.recyclerview.widget.StaggeredGridLayoutManager import arrow.core.partially1 import coil.compose.AsyncImage @@ -84,6 +84,7 @@ import com.hippo.ehviewer.download.DownloadService import com.hippo.ehviewer.download.DownloadService.Companion.clear import com.hippo.ehviewer.download.downloadDir import com.hippo.ehviewer.ktbuilder.imageRequest +import com.hippo.ehviewer.ui.confirmRemoveDownload import com.hippo.ehviewer.ui.legacy.AutoStaggeredGridLayoutManager import com.hippo.ehviewer.ui.legacy.BaseDialogBuilder import com.hippo.ehviewer.ui.legacy.CheckBoxDialogBuilder @@ -98,6 +99,7 @@ import com.hippo.ehviewer.ui.main.requestOf import com.hippo.ehviewer.ui.navToReader import com.hippo.ehviewer.ui.setMD3Content import com.hippo.ehviewer.ui.tools.CropDefaults +import com.hippo.ehviewer.ui.tools.DialogState import com.hippo.ehviewer.util.FileUtils import com.hippo.ehviewer.util.LongList import com.hippo.ehviewer.util.sendTo @@ -137,6 +139,8 @@ class DownloadsScene : private lateinit var mLabels: MutableList private var mType = -1 + private val dialogState = DialogState() + private fun initLabels() { context ?: return val listLabel = downloadManager.labelList @@ -247,6 +251,7 @@ class DownloadsScene : savedInstanceState: Bundle?, ): View { _binding = SceneDownloadBinding.inflate(inflater, container!!) + container.addView(ComposeView(inflater.context).apply { setMD3Content { dialogState.Intercept() } }) binding.run { setLiftOnScrollTargetView(recyclerView) mViewTransition = ViewTransition(content, tip) @@ -288,11 +293,6 @@ class DownloadsScene : recyclerView.layoutManager = layoutManager recyclerView.clipToPadding = false recyclerView.clipChildren = false - // Cancel change animation - val itemAnimator = recyclerView.itemAnimator - if (itemAnimator is SimpleItemAnimator) { - itemAnimator.supportsChangeAnimations = false - } val interval = resources.getDimensionPixelOffset(R.dimen.gallery_list_interval) val decoration = object : RecyclerView.ItemDecoration() { override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) { @@ -313,7 +313,7 @@ class DownloadsScene : } override fun isLongPressDragEnabled() = false override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int { - return makeMovementFlags(ItemTouchHelper.UP or ItemTouchHelper.DOWN, 0) + return makeMovementFlags(ItemTouchHelper.UP or ItemTouchHelper.DOWN, ItemTouchHelper.LEFT) } override fun onMove( recyclerView: RecyclerView, @@ -336,7 +336,16 @@ class DownloadsScene : } return true } - override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {} + override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { + val position = viewHolder.bindingAdapterPosition + val info = mList!![position] + lifecycleScope.launchUI { + dialogState.confirmRemoveDownload(info) { + mAdapter!!.notifyItemChanged(position) + } + mAdapter!!.notifyItemRemoved(position) + } + } }) mItemTouchHelper!!.attachToRecyclerView(recyclerView) fastScroller.attachToRecyclerView(recyclerView) diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/FavoritesScene.kt b/app/src/main/java/com/hippo/ehviewer/ui/scene/FavoritesScene.kt index ebdafb5fe..7ef0c2b4e 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/FavoritesScene.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/FavoritesScene.kt @@ -51,6 +51,8 @@ import androidx.paging.PagingConfig import androidx.paging.PagingSource import androidx.paging.PagingState import androidx.paging.cachedIn +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.RecyclerView import com.google.android.material.datepicker.CalendarConstraints import com.google.android.material.datepicker.CalendarConstraints.DateValidator import com.google.android.material.datepicker.CompositeDateValidator @@ -78,6 +80,7 @@ import com.hippo.ehviewer.ui.legacy.HandlerDrawable import com.hippo.ehviewer.ui.legacy.ViewTransition import com.hippo.ehviewer.ui.legacy.WindowInsetsAnimationHelper import com.hippo.ehviewer.ui.setMD3Content +import com.hippo.ehviewer.ui.tools.DialogState import com.hippo.ehviewer.util.ExceptionUtils import com.hippo.ehviewer.util.SimpleHandler import com.hippo.ehviewer.util.getValue @@ -160,6 +163,8 @@ class FavoritesScene : SearchBarScene() { } } + private val dialogState = DialogState() + override val fabLayout get() = binding.fabLayout private fun onItemClick(position: Int) { @@ -225,6 +230,7 @@ class FavoritesScene : SearchBarScene() { savedInstanceState: Bundle?, ): View { _binding = SceneFavoritesBinding.inflate(inflater, container!!) + container.addView(ComposeView(inflater.context).apply { setMD3Content { dialogState.Intercept() } }) setOnApplySearch { if (!tracker.isInCustomChoice) { switchFav(urlBuilder.favCat, it) @@ -384,6 +390,36 @@ class FavoritesScene : SearchBarScene() { } switchFav(Settings.recentFavCat) } + val itemTouchHelper = ItemTouchHelper(object : ItemTouchHelper.Callback() { + override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int { + return makeMovementFlags(0, ItemTouchHelper.LEFT) + } + override fun onMove( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder, + target: RecyclerView.ViewHolder, + ) = false + override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { + val position = viewHolder.bindingAdapterPosition + val info = mAdapter!!.peek(position)!! + lifecycleScope.launchIO { + dialogState.awaitPermissionOrCancel( + confirmText = R.string.delete, + dismissText = android.R.string.cancel, + title = R.string.delete_favorites_dialog_title, + onDismiss = { mAdapter!!.notifyItemChanged(position) }, + ) { + Text(text = stringResource(id = R.string.delete_favorites_dialog_message, 1)) + } + if (urlBuilder.favCat == FavListUrlBuilder.FAV_CAT_LOCAL) { + EhDB.removeLocalFavorites(info) + } else { + EhEngine.modifyFavorites(info.gid, info.token) + } + } + } + }) + itemTouchHelper.attachToRecyclerView(binding.recyclerView) allowEmptySearch = false return binding.root } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/tools/Dialog.kt b/app/src/main/java/com/hippo/ehviewer/ui/tools/Dialog.kt index 39e224e37..bb99fec6e 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/tools/Dialog.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/tools/Dialog.kt @@ -148,18 +148,25 @@ class DialogState { @StringRes confirmText: Int? = null, @StringRes dismissText: Int? = null, @StringRes title: Int? = null, + onDismiss: () -> Unit = {}, text: (@Composable () -> Unit)? = null, ) { return dialog { cont -> AlertDialog( - onDismissRequest = { cont.cancel() }, + onDismissRequest = { + onDismiss() + cont.cancel() + }, confirmButton = { TextButton(onClick = { cont.resume(Unit) }) { Text(text = stringResource(id = confirmText ?: android.R.string.ok)) } }, dismissButton = dismissText.ifNotNullThen { - TextButton(onClick = { cont.cancel() }) { + TextButton(onClick = { + onDismiss() + cont.cancel() + }) { Text(text = stringResource(id = dismissText!!)) } },