Implement swipe-to-remove in Downloads and Favorites
This commit is contained in:
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!!))
|
||||
}
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user