From 86a49f764ebb31fe84001419387f4ebc3b0ee25b Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Sun, 26 Nov 2023 22:15:25 +0800 Subject: [PATCH] Add haptic feedback for drag and drop in Downloads --- .../hippo/ehviewer/ui/main/DownloadCard.kt | 12 +--- .../ehviewer/ui/scene/DownloadsFragment.kt | 14 ++--- .../ehviewer/ui/scene/GalleryListFragment.kt | 49 ++------------- .../com/hippo/ehviewer/ui/tools/DragHandle.kt | 59 +++++++++++++++++++ 4 files changed, 72 insertions(+), 62 deletions(-) create mode 100644 app/src/main/kotlin/com/hippo/ehviewer/ui/tools/DragHandle.kt diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/main/DownloadCard.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/main/DownloadCard.kt index b7a158d7f..29756a9a1 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/main/DownloadCard.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/main/DownloadCard.kt @@ -9,7 +9,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Pause import androidx.compose.material.icons.filled.PlayArrow -import androidx.compose.material.icons.filled.Reorder import androidx.compose.material3.Card import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -49,6 +48,7 @@ import com.hippo.ehviewer.download.downloadDir import com.hippo.ehviewer.ktbuilder.imageRequest import com.hippo.ehviewer.ui.tools.CropDefaults import com.hippo.ehviewer.ui.tools.CrystalCard +import com.hippo.ehviewer.ui.tools.DragHandle import com.hippo.ehviewer.ui.tools.GalleryListCardRating import com.hippo.ehviewer.util.FileUtils import com.hippo.ehviewer.util.sendTo @@ -242,15 +242,7 @@ fun ReorderableItemScope.DownloadCard( bottom.linkTo(parent.bottom) }, ) { - IconButton( - onClick = { }, - modifier = Modifier.draggableHandle(), - ) { - Icon( - imageVector = Icons.Default.Reorder, - contentDescription = null, - ) - } + DragHandle() if (downloadState == DownloadInfo.STATE_WAIT || downloadState == DownloadInfo.STATE_DOWNLOAD) { IconButton(onClick = onStop) { Icon(imageVector = Icons.Default.Pause, contentDescription = null) diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/scene/DownloadsFragment.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/scene/DownloadsFragment.kt index 79060ef0b..812a0f5c1 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/scene/DownloadsFragment.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/scene/DownloadsFragment.kt @@ -37,7 +37,6 @@ import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.filled.Pause import androidx.compose.material.icons.filled.PlayArrow -import androidx.compose.material.icons.filled.Reorder import androidx.compose.material3.DismissDirection import androidx.compose.material3.DismissValue import androidx.compose.material3.DropdownMenu @@ -66,6 +65,7 @@ import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLayoutDirection +import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringArrayResource import androidx.compose.ui.res.stringResource @@ -89,9 +89,11 @@ import com.hippo.ehviewer.ui.main.FabLayout import com.hippo.ehviewer.ui.navToReader import com.hippo.ehviewer.ui.showMoveDownloadLabelList import com.hippo.ehviewer.ui.tools.Deferred +import com.hippo.ehviewer.ui.tools.DragHandle import com.hippo.ehviewer.ui.tools.FastScrollLazyColumn import com.hippo.ehviewer.ui.tools.LocalDialogState import com.hippo.ehviewer.ui.tools.SwipeToDismissBox2 +import com.hippo.ehviewer.ui.tools.draggingHapticFeedback import com.hippo.ehviewer.util.containsIgnoreCase import com.hippo.ehviewer.util.findActivity import com.hippo.ehviewer.util.mapToLongArray @@ -123,6 +125,7 @@ fun DownloadsScreen(navigator: NavController) { val activity = remember(context) { context.findActivity() } val coroutineScope = rememberCoroutineScope() val dialogState = LocalDialogState.current + val view = LocalView.current val title = stringResource(R.string.scene_download_title, label ?: stringResource(R.string.download_all)) val hint = stringResource(R.string.search_bar_hint, title) val defaultName = stringResource(R.string.default_download_label_name) @@ -208,6 +211,7 @@ fun DownloadsScreen(navigator: NavController) { coroutineScope.launch { DownloadManager.moveLabel(from.index - 2, to.index - 2) } + view.performHapticFeedback(draggingHapticFeedback) } LazyColumn( @@ -299,12 +303,7 @@ fun DownloadsScreen(navigator: NavController) { ) { Icon(imageVector = Icons.Default.Edit, contentDescription = null) } - IconButton( - onClick = {}, - modifier = Modifier.draggableHandle(), - ) { - Icon(imageVector = Icons.Default.Reorder, contentDescription = null) - } + DragHandle() } }, ) @@ -412,6 +411,7 @@ fun DownloadsScreen(navigator: NavController) { coroutineScope.launchIO { EhDB.updateDownloadInfo(newList) } + view.performHapticFeedback(draggingHapticFeedback) } FastScrollLazyColumn( modifier = Modifier.nestedScroll(searchBarConnection), diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/scene/GalleryListFragment.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/scene/GalleryListFragment.kt index ec9ba761c..62d93f30c 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/scene/GalleryListFragment.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/scene/GalleryListFragment.kt @@ -3,7 +3,6 @@ package com.hippo.ehviewer.ui.scene import android.graphics.Bitmap import android.net.Uri import android.os.Bundle -import android.view.HapticFeedbackConstants import android.view.LayoutInflater import android.view.View import android.view.ViewConfiguration @@ -43,7 +42,6 @@ import androidx.compose.material.icons.automirrored.filled.LastPage import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.ImageSearch import androidx.compose.material.icons.filled.Refresh -import androidx.compose.material.icons.filled.Reorder import androidx.compose.material.icons.filled.Translate import androidx.compose.material.icons.outlined.Bookmarks import androidx.compose.material3.DismissDirection @@ -148,17 +146,16 @@ import com.hippo.ehviewer.ui.main.NormalSearch import com.hippo.ehviewer.ui.main.SearchAdvanced import com.hippo.ehviewer.ui.scene.GalleryListFragment.Companion.toStartArgs import com.hippo.ehviewer.ui.tools.Deferred +import com.hippo.ehviewer.ui.tools.DragHandle import com.hippo.ehviewer.ui.tools.LocalDialogState import com.hippo.ehviewer.ui.tools.SwipeToDismissBox2 import com.hippo.ehviewer.ui.tools.animateFloatMergePredictiveBackAsState +import com.hippo.ehviewer.ui.tools.draggingHapticFeedback import com.hippo.ehviewer.ui.tools.observed import com.hippo.ehviewer.ui.tools.rememberInVM import com.hippo.ehviewer.util.AppConfig import com.hippo.ehviewer.util.findActivity import com.hippo.ehviewer.util.getParcelableCompat -import com.hippo.ehviewer.util.isAtLeastOMR1 -import com.hippo.ehviewer.util.isAtLeastR -import com.hippo.ehviewer.util.isAtLeastU import com.hippo.ehviewer.util.pickVisualMedia import com.ramcosta.composedestinations.annotation.Destination import eu.kanade.tachiyomi.util.lang.launchIO @@ -416,14 +413,7 @@ fun GalleryListScreen(lub: ListUrlBuilder, navigator: NavController) { list.zip(range).forEach { it.first.position = it.second } EhDB.updateQuickSearch(list) } - val feedbackConstant = if (isAtLeastU) { - HapticFeedbackConstants.SEGMENT_FREQUENT_TICK - } else if (isAtLeastOMR1) { - HapticFeedbackConstants.TEXT_HANDLE_MOVE - } else { - HapticFeedbackConstants.CLOCK_TICK - } - view.performHapticFeedback(feedbackConstant) + view.performHapticFeedback(draggingHapticFeedback) } LazyColumn( modifier = Modifier.fillMaxSize(), @@ -477,38 +467,7 @@ fun GalleryListScreen(lub: ListUrlBuilder, navigator: NavController) { Text(text = item.name) }, trailingContent = { - IconButton( - onClick = {}, - modifier = Modifier.draggableHandle( - onDragStarted = { - val feedbackConstant = if (isAtLeastU) { - HapticFeedbackConstants.DRAG_START - } else if (isAtLeastR) { - HapticFeedbackConstants.GESTURE_START - } else if (isAtLeastOMR1) { - HapticFeedbackConstants.KEYBOARD_PRESS - } else { - HapticFeedbackConstants.VIRTUAL_KEY - } - view.performHapticFeedback(feedbackConstant) - }, - onDragStopped = { - val feedbackConstant = if (isAtLeastR) { - HapticFeedbackConstants.GESTURE_END - } else if (isAtLeastOMR1) { - HapticFeedbackConstants.KEYBOARD_RELEASE - } else { - HapticFeedbackConstants.VIRTUAL_KEY_RELEASE - } - view.performHapticFeedback(feedbackConstant) - }, - ), - ) { - Icon( - imageVector = Icons.Default.Reorder, - contentDescription = null, - ) - } + DragHandle() }, ) } diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/DragHandle.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/DragHandle.kt new file mode 100644 index 000000000..a773f56fb --- /dev/null +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/DragHandle.kt @@ -0,0 +1,59 @@ +package com.hippo.ehviewer.ui.tools + +import android.view.HapticFeedbackConstants +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Reorder +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalView +import com.hippo.ehviewer.util.isAtLeastOMR1 +import com.hippo.ehviewer.util.isAtLeastR +import com.hippo.ehviewer.util.isAtLeastU +import sh.calvin.reorderable.ReorderableItemScope + +@Composable +fun ReorderableItemScope.DragHandle(modifier: Modifier = Modifier) { + val view = LocalView.current + IconButton( + onClick = {}, + modifier = modifier.draggableHandle( + onDragStarted = { + val feedbackConstant = if (isAtLeastU) { + HapticFeedbackConstants.DRAG_START + } else if (isAtLeastR) { + HapticFeedbackConstants.GESTURE_START + } else if (isAtLeastOMR1) { + HapticFeedbackConstants.KEYBOARD_PRESS + } else { + HapticFeedbackConstants.VIRTUAL_KEY + } + view.performHapticFeedback(feedbackConstant) + }, + onDragStopped = { + val feedbackConstant = if (isAtLeastR) { + HapticFeedbackConstants.GESTURE_END + } else if (isAtLeastOMR1) { + HapticFeedbackConstants.KEYBOARD_RELEASE + } else { + HapticFeedbackConstants.VIRTUAL_KEY_RELEASE + } + view.performHapticFeedback(feedbackConstant) + }, + ), + ) { + Icon( + imageVector = Icons.Default.Reorder, + contentDescription = null, + ) + } +} + +val draggingHapticFeedback = if (isAtLeastU) { + HapticFeedbackConstants.SEGMENT_FREQUENT_TICK +} else if (isAtLeastOMR1) { + HapticFeedbackConstants.TEXT_HANDLE_MOVE +} else { + HapticFeedbackConstants.CLOCK_TICK +}