From 7a5bab41f6ede93a0b9a0d7ddeea81e9a797efc8 Mon Sep 17 00:00:00 2001
From: FooIbar <118464521+FooIbar@users.noreply.github.com>
Date: Sun, 7 Apr 2024 13:57:27 +0800
Subject: [PATCH] Add option to disable list item animations (#997)
It may crash on some devices.
---
app/src/main/kotlin/com/hippo/ehviewer/Settings.kt | 1 +
.../com/hippo/ehviewer/ui/main/SearchFilter.kt | 5 ++++-
.../com/hippo/ehviewer/ui/screen/DownloadsScreen.kt | 9 +++++++--
.../com/hippo/ehviewer/ui/screen/FavoritesScreen.kt | 12 ++++++++++--
.../ehviewer/ui/screen/GalleryCommentsScreen.kt | 6 +++++-
.../com/hippo/ehviewer/ui/screen/HistoryScreen.kt | 4 +++-
.../com/hippo/ehviewer/ui/screen/SearchBarScreen.kt | 5 ++++-
.../com/hippo/ehviewer/ui/settings/AdvancedScreen.kt | 12 ++++++++++++
.../com/hippo/ehviewer/ui/settings/FilterScreen.kt | 8 ++++++--
.../kotlin/com/hippo/ehviewer/ui/tools/Modifier.kt | 6 ++++++
app/src/main/res/values-zh-rCN/strings.xml | 2 ++
app/src/main/res/values/strings.xml | 2 ++
12 files changed, 62 insertions(+), 10 deletions(-)
create mode 100644 app/src/main/kotlin/com/hippo/ehviewer/ui/tools/Modifier.kt
diff --git a/app/src/main/kotlin/com/hippo/ehviewer/Settings.kt b/app/src/main/kotlin/com/hippo/ehviewer/Settings.kt
index 6b1161d4d..4d8495770 100644
--- a/app/src/main/kotlin/com/hippo/ehviewer/Settings.kt
+++ b/app/src/main/kotlin/com/hippo/ehviewer/Settings.kt
@@ -116,6 +116,7 @@ object Settings : DataStorePreferences(null) {
val showGalleryPages = boolPref("show_gallery_pages", true)
val qSSaveProgress = boolPref("qs_save_progress", true)
val security = boolPref("require_unlock", false)
+ val animateItems = boolPref("animate_items", true)
val recentDownloadLabel = stringOrNullPref("recent_download_label", null)
var downloadScheme by stringOrNullPref("image_scheme", null)
diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/main/SearchFilter.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/main/SearchFilter.kt
index 7daa6dc01..1b8d03c27 100644
--- a/app/src/main/kotlin/com/hippo/ehviewer/ui/main/SearchFilter.kt
+++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/main/SearchFilter.kt
@@ -40,7 +40,9 @@ import com.hippo.ehviewer.asMutableState
import com.hippo.ehviewer.client.EhTagDatabase
import com.hippo.ehviewer.client.EhUtils
import com.hippo.ehviewer.client.data.GalleryInfo
+import com.hippo.ehviewer.collectAsState
import com.hippo.ehviewer.ui.tools.LocalDialogState
+import com.hippo.ehviewer.ui.tools.thenIf
import com.hippo.ehviewer.util.toIntOrDefault
import kotlinx.coroutines.launch
@@ -68,6 +70,7 @@ fun SearchFilter(
val scope = rememberCoroutineScope()
val context = LocalContext.current
val dialogState = LocalDialogState.current
+ val animateItems by Settings.animateItems.collectAsState()
fun isCategoryChecked(bit: Int) = category and bit != 0
val categories = remember(category) { categoryTable.sortedBy { !isCategoryChecked(it.first) } }
LazyRow(
@@ -82,7 +85,7 @@ fun SearchFilter(
selected = isCategoryChecked(it.first),
onClick = { onCategoryChanged(category xor it.first) },
label = { Text(text = stringResource(id = it.second)) },
- modifier = Modifier.animateItem(),
+ modifier = Modifier.thenIf(animateItems) { animateItem() },
)
}
}
diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/DownloadsScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/DownloadsScreen.kt
index 0d76c772b..aa581a0d2 100644
--- a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/DownloadsScreen.kt
+++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/DownloadsScreen.kt
@@ -111,6 +111,7 @@ import com.hippo.ehviewer.ui.tools.SwipeToDismissBox2
import com.hippo.ehviewer.ui.tools.delegateSnapshotUpdate
import com.hippo.ehviewer.ui.tools.draggingHapticFeedback
import com.hippo.ehviewer.ui.tools.rememberInVM
+import com.hippo.ehviewer.ui.tools.thenIf
import com.hippo.ehviewer.util.findActivity
import com.hippo.ehviewer.util.mapToLongArray
import com.ramcosta.composedestinations.annotation.Destination
@@ -135,6 +136,7 @@ fun DownloadsScreen(navigator: DestinationsNavigator) = composing(navigator) {
var filterState by rememberSaveable { mutableStateOf(DownloadsFilterState(Settings.recentDownloadLabel.value)) }
var invalidateKey by rememberSaveable { mutableStateOf(false) }
var searchBarOffsetY by remember(filterState.label) { mutableIntStateOf(0) }
+ val animateItems by Settings.animateItems.collectAsState()
var fabExpanded by remember { mutableStateOf(false) }
var fabHidden by remember { mutableStateOf(false) }
@@ -481,7 +483,7 @@ fun DownloadsScreen(navigator: DestinationsNavigator) = composing(navigator) {
onClick = ::onItemClick.partially1(info),
onLongClick = { navigator.navigate(info.galleryInfo.asDst()) },
info = info,
- modifier = Modifier.animateItemPlacement(),
+ modifier = Modifier.thenIf(animateItems) { animateItemPlacement() },
badgeText = info.pages.takeIf { it > 0 }?.toString(),
)
}
@@ -494,7 +496,10 @@ fun DownloadsScreen(navigator: DestinationsNavigator) = composing(navigator) {
) {
items(list, key = { it.gid }) { info ->
val checked = info.gid in checkedInfoMap
- CheckableItem(checked = checked, modifier = Modifier.animateItem()) { interactionSource ->
+ CheckableItem(
+ checked = checked,
+ modifier = Modifier.thenIf(animateItems) { animateItem() },
+ ) { interactionSource ->
DownloadCard(
onClick = {
if (selectMode) {
diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/FavoritesScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/FavoritesScreen.kt
index 766276bb4..26a2e353d 100644
--- a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/FavoritesScreen.kt
+++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/FavoritesScreen.kt
@@ -77,6 +77,7 @@ import com.hippo.ehviewer.ui.startDownload
import com.hippo.ehviewer.ui.tools.delegateSnapshotUpdate
import com.hippo.ehviewer.ui.tools.foldToLoadResult
import com.hippo.ehviewer.ui.tools.rememberInVM
+import com.hippo.ehviewer.ui.tools.thenIf
import com.hippo.ehviewer.util.displayString
import com.hippo.ehviewer.util.mapToLongArray
import com.ramcosta.composedestinations.annotation.Destination
@@ -97,6 +98,7 @@ fun FavouritesScreen(navigator: DestinationsNavigator) = composing(navigator) {
// Immutables
val localFavName = stringResource(R.string.local_favorites)
val cloudFavName = stringResource(R.string.cloud_favorites)
+ val animateItems by Settings.animateItems.collectAsState()
// Meta State
var urlBuilder by rememberSaveable { mutableStateOf(FavListUrlBuilder(favCat = Settings.recentFavCat)) }
@@ -266,7 +268,10 @@ fun FavouritesScreen(navigator: DestinationsNavigator) = composing(navigator) {
listMode = listMode,
detailItemContent = { info ->
val checked = info.gid in checkedInfoMap
- CheckableItem(checked = checked, modifier = Modifier.animateItemPlacement()) { interactionSource ->
+ CheckableItem(
+ checked = checked,
+ modifier = Modifier.thenIf(animateItems) { animateItemPlacement() },
+ ) { interactionSource ->
GalleryInfoListItem(
onClick = {
if (selectMode) {
@@ -292,7 +297,10 @@ fun FavouritesScreen(navigator: DestinationsNavigator) = composing(navigator) {
},
thumbItemContent = { info ->
val checked = info.gid in checkedInfoMap
- CheckableItem(checked = checked, modifier = Modifier.animateItemPlacement()) { interactionSource ->
+ CheckableItem(
+ checked = checked,
+ modifier = Modifier.thenIf(animateItems) { animateItemPlacement() },
+ ) { interactionSource ->
GalleryInfoGridItem(
onClick = {
if (selectMode) {
diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/GalleryCommentsScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/GalleryCommentsScreen.kt
index cb5057d00..e0f5f2144 100644
--- a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/GalleryCommentsScreen.kt
+++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/GalleryCommentsScreen.kt
@@ -74,12 +74,14 @@ import androidx.core.text.inSpans
import androidx.core.text.parseAsHtml
import com.hippo.ehviewer.EhApplication
import com.hippo.ehviewer.R
+import com.hippo.ehviewer.Settings
import com.hippo.ehviewer.client.EhCookieStore
import com.hippo.ehviewer.client.EhEngine
import com.hippo.ehviewer.client.EhFilter.remember
import com.hippo.ehviewer.client.EhUrl
import com.hippo.ehviewer.client.data.GalleryComment
import com.hippo.ehviewer.client.data.ListUrlBuilder
+import com.hippo.ehviewer.collectAsState
import com.hippo.ehviewer.dao.Filter
import com.hippo.ehviewer.dao.FilterMode
import com.hippo.ehviewer.ui.LockDrawer
@@ -92,6 +94,7 @@ import com.hippo.ehviewer.ui.tools.animateFloatMergePredictiveBackAsState
import com.hippo.ehviewer.ui.tools.normalizeSpan
import com.hippo.ehviewer.ui.tools.rememberBBCodeTextToolbar
import com.hippo.ehviewer.ui.tools.snackBarPadding
+import com.hippo.ehviewer.ui.tools.thenIf
import com.hippo.ehviewer.ui.tools.toBBCode
import com.hippo.ehviewer.ui.tools.updateSpan
import com.hippo.ehviewer.util.ReadableTime
@@ -152,6 +155,7 @@ fun GalleryCommentsScreen(gid: Long, navigator: DestinationsNavigator) = composi
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
var commenting by rememberSaveable { mutableStateOf(false) }
val animationProgress by animateFloatMergePredictiveBackAsState(enable = commenting) { commenting = false }
+ val animateItems by Settings.animateItems.collectAsState()
val galleryDetail = remember { EhApplication.galleryDetailCache[gid]!! }
val userCommentBackField = remember { mutableStateOf(TextFieldValue()) }
@@ -356,7 +360,7 @@ fun GalleryCommentsScreen(gid: Long, navigator: DestinationsNavigator) = composi
}
GalleryCommentCard(
- modifier = Modifier.animateItem(),
+ modifier = Modifier.thenIf(animateItems) { animateItem() },
comment = item,
onUserClick = {
navigate(
diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/HistoryScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/HistoryScreen.kt
index 656f79b03..358906f2a 100644
--- a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/HistoryScreen.kt
+++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/HistoryScreen.kt
@@ -54,6 +54,7 @@ import com.hippo.ehviewer.ui.tools.Deferred
import com.hippo.ehviewer.ui.tools.FastScrollLazyColumn
import com.hippo.ehviewer.ui.tools.SwipeToDismissBox2
import com.hippo.ehviewer.ui.tools.rememberInVM
+import com.hippo.ehviewer.ui.tools.thenIf
import com.hippo.ehviewer.util.FavouriteStatusRouter
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
@@ -67,6 +68,7 @@ import kotlinx.coroutines.launch
fun HistoryScreen(navigator: DestinationsNavigator) = composing(navigator) {
val title = stringResource(id = R.string.history)
val hint = stringResource(R.string.search_bar_hint, title)
+ val animateItems by Settings.animateItems.collectAsState()
var searchBarOffsetY by remember { mutableIntStateOf(0) }
var keyword by rememberSaveable { mutableStateOf("") }
@@ -142,7 +144,7 @@ fun HistoryScreen(navigator: DestinationsNavigator) = composing(navigator) {
SwipeToDismissBox2(
state = dismissState,
backgroundContent = {},
- modifier = Modifier.animateItem(),
+ modifier = Modifier.thenIf(animateItems) { animateItem() },
) {
GalleryInfoListItem(
onClick = { navigate(info.asDst()) },
diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/SearchBarScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/SearchBarScreen.kt
index 5533f92fc..ab1ffd2c5 100644
--- a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/SearchBarScreen.kt
+++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/SearchBarScreen.kt
@@ -66,12 +66,14 @@ import com.hippo.ehviewer.R
import com.hippo.ehviewer.Settings
import com.hippo.ehviewer.client.EhTagDatabase
import com.hippo.ehviewer.client.data.TagNamespace
+import com.hippo.ehviewer.collectAsState
import com.hippo.ehviewer.dao.Search
import com.hippo.ehviewer.dao.SearchDao
import com.hippo.ehviewer.ui.LocalNavDrawerState
import com.hippo.ehviewer.ui.LockDrawer
import com.hippo.ehviewer.ui.tools.LocalDialogState
import com.hippo.ehviewer.ui.tools.rememberCompositionActiveState
+import com.hippo.ehviewer.ui.tools.thenIf
import com.jamal.composeprefs3.ui.ifNotNullThen
import com.jamal.composeprefs3.ui.ifTrueThen
import eu.kanade.tachiyomi.util.lang.launchIO
@@ -121,6 +123,7 @@ fun SearchBarScreen(
val scope = rememberCoroutineScope { Dispatchers.IO }
val context = LocalContext.current
val dialogState = LocalDialogState.current
+ val animateItems by Settings.animateItems.collectAsState()
class TagSuggestion(
override val hint: String?,
@@ -329,7 +332,7 @@ fun SearchBarScreen(
}
},
colors = ListItemDefaults.colors(containerColor = Color.Transparent),
- modifier = Modifier.clickable { it.onClick() }.animateItem(),
+ modifier = Modifier.clickable { it.onClick() }.thenIf(animateItems) { animateItem() },
)
}
}
diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/AdvancedScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/AdvancedScreen.kt
index db59e46ad..4a5194942 100644
--- a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/AdvancedScreen.kt
+++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/AdvancedScreen.kt
@@ -33,6 +33,7 @@ import com.hippo.ehviewer.BuildConfig
import com.hippo.ehviewer.EhDB
import com.hippo.ehviewer.R
import com.hippo.ehviewer.Settings
+import com.hippo.ehviewer.asMutableState
import com.hippo.ehviewer.client.CHROME_USER_AGENT
import com.hippo.ehviewer.client.EhCookieStore
import com.hippo.ehviewer.client.EhEngine
@@ -42,6 +43,8 @@ import com.hippo.ehviewer.ui.tools.observed
import com.hippo.ehviewer.util.AppConfig
import com.hippo.ehviewer.util.Crash
import com.hippo.ehviewer.util.ReadableTime
+import com.hippo.ehviewer.util.isAtLeastQ
+import com.jamal.composeprefs3.ui.prefs.SwitchPref
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
@@ -142,6 +145,15 @@ fun AdvancedScreen(navigator: DestinationsNavigator) {
title = stringResource(id = R.string.preload_thumb_aggressively),
value = Settings::preloadThumbAggressively,
)
+ if (!isAtLeastQ) {
+ var animateItems by Settings.animateItems.asMutableState()
+ SwitchPref(
+ checked = animateItems,
+ onMutate = { animateItems = !animateItems },
+ title = stringResource(id = R.string.animate_items),
+ summary = stringResource(id = R.string.animate_items_summary),
+ )
+ }
IntSliderPreference(
maxValue = 5,
minValue = 1,
diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/FilterScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/FilterScreen.kt
index 083df7c9b..6ca83a22d 100644
--- a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/FilterScreen.kt
+++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/FilterScreen.kt
@@ -49,14 +49,17 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.unit.dp
import com.hippo.ehviewer.R
+import com.hippo.ehviewer.Settings
import com.hippo.ehviewer.client.EhFilter
import com.hippo.ehviewer.client.EhFilter.forget
import com.hippo.ehviewer.client.EhFilter.remember
import com.hippo.ehviewer.client.EhFilter.trigger
+import com.hippo.ehviewer.collectAsState
import com.hippo.ehviewer.dao.Filter
import com.hippo.ehviewer.dao.FilterMode
import com.hippo.ehviewer.ui.tools.Deferred
import com.hippo.ehviewer.ui.tools.LocalDialogState
+import com.hippo.ehviewer.ui.tools.thenIf
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
@@ -74,6 +77,7 @@ fun FilterScreen(navigator: DestinationsNavigator) {
val dialogState = LocalDialogState.current
val textIsEmpty = stringResource(R.string.text_is_empty)
val labelExist = stringResource(R.string.label_text_exist)
+ val animateItems by Settings.animateItems.collectAsState()
fun addFilter() {
scope.launch {
@@ -227,7 +231,7 @@ fun FilterScreen(navigator: DestinationsNavigator) {
stickyHeader(key = filterMode) {
Text(
text = stringResource(id = title),
- modifier = Modifier.padding(horizontal = 24.dp, vertical = 8.dp).animateItem(),
+ modifier = Modifier.padding(horizontal = 24.dp, vertical = 8.dp).thenIf(animateItems) { animateItem() },
color = MaterialTheme.colorScheme.tertiary,
style = MaterialTheme.typography.titleMedium,
)
@@ -235,7 +239,7 @@ fun FilterScreen(navigator: DestinationsNavigator) {
items(filters, key = { requireNotNull(it.id) }) { filter ->
val filterCheckBoxRecomposeScope = currentRecomposeScope
Row(
- modifier = Modifier.fillMaxWidth().animateItem().clickable { filter.trigger { filterCheckBoxRecomposeScope.invalidate() } },
+ modifier = Modifier.fillMaxWidth().thenIf(animateItems) { animateItem() }.clickable { filter.trigger { filterCheckBoxRecomposeScope.invalidate() } },
verticalAlignment = Alignment.CenterVertically,
) {
Checkbox(
diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/Modifier.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/Modifier.kt
new file mode 100644
index 000000000..087ca3d16
--- /dev/null
+++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/Modifier.kt
@@ -0,0 +1,6 @@
+package com.hippo.ehviewer.ui.tools
+
+import androidx.compose.ui.Modifier
+
+inline fun Modifier.thenIf(condition: Boolean, crossinline block: Modifier.() -> Modifier) =
+ if (condition) block() else this
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 305a80b08..4ffa49133 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -501,6 +501,8 @@
详情模式下缩略图大小
账户
激进地预载缩略图
+ 列表项目动画
+ 如果应用崩溃请尝试禁用此项
自动检查更新
从不
每天
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 0a8a33f13..fe6b91f9e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -564,6 +564,8 @@
Thumb size in detail mode
Account
Preload thumbs aggressively
+ List item animations
+ Try disabling this if you are facing crashes
User Agent
Automatically check for updates
Never