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