Implement swipe-to-remove in Downloads and Favorites

This commit is contained in:
FooIbar committed 2023-08-14 21:11:55 +08:00
1 parent dd607b41e9
commit 69a26aeedb
4 files changed
+92 -17

No files matched your search

@@ -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)
@@ -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<String>
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)
@@ -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
}
@@ -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!!))
}
},