Compare commits

...
50 Commits
Author SHA1 Message Date
FooIbar 71a4e84d7c Prepare for release 1.11.4
Releases / build (push) Has been cancelled
2024-04-21 22:45:54 +08:00
FooIbar d6c36146ba Fix losing reading progress on reading mode change 2024-04-21 22:35:22 +08:00
FooIbar 9013778f58 More workaround 2024-04-21 18:05:25 +08:00
renovate[bot] 1af03dd145 Update dependency sh.calvin.reorderable:reorderable to v1.5.1 2024-04-20 16:46:53 +00:00
FooIbar b83e0fd092 Workaround for lazy layout lagging (#1042) 2024-04-20 16:37:41 +00:00
GG Bond 1f8d786d05 Simplify to use Modifier.offset 2024-04-20 18:31:22 +08:00
GG Bond 5d87a4ada0 Never have a clipped shadow 2024-04-20 18:31:22 +08:00
GG Bond 4d4fb1e398 Eliminate unnecessary Column 2024-04-20 17:10:33 +08:00
GG Bond 05eea54f06 Fix primaryFab appear anim 2024-04-20 17:10:33 +08:00
GG Bond a00d9ef765 Mitigate shadow being clipped 2024-04-20 17:10:33 +08:00
GG Bond aa6ddac9c5 Avoid unnecessary zIndex 2024-04-20 17:10:33 +08:00
GG Bond 9a6f17c745 Image Search Card doesn't need to be scrollable now 2024-04-20 17:10:33 +08:00
GG Bond 980977aaf3 Ensure Image Search Card's shadow not being clipped 2024-04-20 17:10:33 +08:00
GG Bond 3ed0afaf91 Restore an alpha usage can be safely applied 2024-04-20 17:10:33 +08:00
GG Bond 250adb4b33 Fix shadow being clipped 2024-04-20 17:10:33 +08:00
GG Bond 0baf09cb53 Codestyle fix for DownloadCard 2024-04-20 17:10:33 +08:00
FooIbar d04a914b9b Tweak gallery card alignment and ensure min height 2024-04-19 19:08:21 +08:00
renovate[bot] 59775cb27c Update dependency com.android.application to v8.5.0-alpha06 2024-04-18 22:06:24 +00:00
GG Bond fb76cd7b1b Move compose out of snapshot 2024-04-18 11:25:52 +08:00
renovate[bot] 52fb5306b6 Update dependency dev.chrisbanes.compose:compose-bom to v2024.04.00-alpha02 2024-04-18 01:03:11 +00:00
renovate[bot] 74f7d95718 Update AndroidX & Jetpack Compose 2024-04-17 18:12:27 +00:00
renovate[bot] bcc1b78c7c Update compose snapshot 2024-04-18 00:00:56 +08:00
FooIbar 3978abea87 Fix UniFile.fromUri() for 2 path segments tree uri 2024-04-17 17:38:51 +08:00
FooIbar 556b726349 Update to follow the latest material slider specs
Tick size is 4 dp now.
2024-04-16 10:49:10 +08:00
FooIbar e0f16f31e6 Automatically backup data before updating 2024-04-16 00:08:30 +08:00
FooIbar b39e24467c Fix UniFile.fromUri for children of tree uri 2024-04-15 23:50:54 +08:00
FooIbar 65613f07ba Optimize DownloadCard interaction in select mode 2024-04-15 21:53:37 +08:00
FooIbar 666a7e4d77 Allow disable list item animations on API 29+ (#1024)
* Allow disable list item animations on API 29+

* Update list item animations setting description
2024-04-15 14:54:13 +08:00
renovate[bot] cf08bfdd9b Update dependency me.saket.telephoto:zoomable to v0.10.0 2024-04-14 21:58:40 +00:00
renovate[bot]andFooIbar 1f47d56c55 Update Kotlin, KSP and Compose Compiler (#1009)
* Update Kotlin, KSP and Compose Compiler

* Migrate to new compose compiler gradle plugin

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: FooIbar <118464521+FooIbar@users.noreply.github.com>
2024-04-14 14:38:22 +00:00
FooIbar 1a7dde848f Don't treat warnings as errors on debug build 2024-04-14 17:56:55 +08:00
GG Bond c8207308a8 Update predictive back animations to use interpolation curve of (0.1, 0.1, 0, 1) 2024-04-14 17:38:03 +08:00
renovate[bot] f23020cc6f Update dependency sh.calvin.reorderable:reorderable to v1.5.0 2024-04-12 18:46:12 +00:00
renovate[bot] 555e487f1e Update dependency com.android.application to v8.5.0-alpha05 2024-04-11 18:20:21 +00:00
renovate[bot] 107bb0b15d Update aboutlibraries to v11.1.3 2024-04-09 19:14:59 +00:00
FooIbar 87a2de393e Tweak select item dialog style 2024-04-10 03:04:49 +08:00
renovate[bot] a7e4412454 Update dependency com.mikepenz.aboutlibraries.plugin to v11.1.2 2024-04-09 14:42:54 +00:00
FooIbar 7dce4cfa8e Update Compose snapshot 2024-04-09 22:12:49 +08:00
FooIbar 2b30aca47c Update R8 and remove workaround 2024-04-09 21:44:57 +08:00
FooIbar 0073d2862b Fixed crash on deleting a label after renaming it 2024-04-09 18:39:25 +08:00
FooIbar b182c97185 Fix titleless select item dialog top padding 2024-04-09 10:46:44 +08:00
renovate[bot] a32119bada Update dependency com.android.application to v8.5.0-alpha04 2024-04-08 19:37:35 +00:00
FooIbar a928cfd066 Update libarchive to v3.7.3 2024-04-09 00:08:01 +08:00
FooIbar 8c7a371336 Highlight selected item in select item dialog 2024-04-07 23:32:47 +08:00
GG Bond 6f61eb4946 Update outdated comments 2024-04-07 21:21:37 +08:00
GG Bond 1096f05344 Move navigation out of snapshot 2024-04-07 21:21:37 +08:00
GG Bond 26f96d3e38 Move to telephoto 0.10.0-SNAPSHOT 2024-04-07 21:21:37 +08:00
FooIbar 4a02189a59 Rearrange code to move plurals resources together 2024-04-07 17:13:35 +08:00
FooIbar 7a5bab41f6 Add option to disable list item animations (#997)
It may crash on some devices.
2024-04-07 13:57:27 +08:00
FooIbar 84c32fb28d Prepare for next iteration 2024-04-06 17:10:28 +08:00
51 changed files with 824 additions and 697 deletions

No files matched your search

+19
View File
@@ -1,5 +1,24 @@
# 变更日志
## [1.11.4] - 2024-04-21
### 新功能
* 更新前自动备份数据
### 改进
* 在 设置-高级 中加入了禁用列表项目动画的选项,该功能在部分设备上导致崩溃/卡顿 [#996](https://github.com/FooIbar/EhViewer/issues/996) [#1023](https://github.com/FooIbar/EhViewer/issues/1023)
* 下载列表多选模式下避免误触 [#1016](https://github.com/FooIbar/EhViewer/issues/1016)
* 杂项 UI 优化
### Bug 修复
* 重命名下载标签后删除该标签时崩溃 [#1008](https://github.com/FooIbar/EhViewer/issues/1008)
* 搜索栏中某些输入法长按退格无法连续删除 [#606](https://github.com/FooIbar/EhViewer/issues/606)
* 评论/屏蔽/搜索建议/下载标签/快速搜索列表卡顿 [#1041](https://github.com/FooIbar/EhViewer/issues/1041)
## [1.11.3] - 2024-04-06
### 改进
+12 -9
View File
@@ -10,6 +10,7 @@ val isRelease: Boolean
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.kotlin.parcelize)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.ksp)
@@ -62,8 +63,9 @@ android {
applicationId = "moe.tarsin.ehviewer"
minSdk = 26
targetSdk = 34
versionCode = 180054
versionName = "1.11.3"
versionCode = 180055
versionName = "1.11.4"
// versionNameSuffix = "-SNAPSHOT"
resourceConfigurations.addAll(
listOf(
"zh",
@@ -131,9 +133,6 @@ android {
"-opt-in=kotlinx.serialization.ExperimentalSerializationApi",
"-opt-in=splitties.experimental.ExperimentalSplittiesApi",
"-opt-in=splitties.preferences.DataStorePreferencesPreview",
"-P", "plugin:androidx.compose.compiler.plugins.kotlin:experimentalStrongSkipping=true",
"-P", "plugin:androidx.compose.compiler.plugins.kotlin:nonSkippingGroupOptimization=true",
)
}
@@ -170,13 +169,17 @@ android {
viewBinding = true
}
composeOptions {
kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get()
}
namespace = "com.hippo.ehviewer"
}
composeCompiler {
// https://youtrack.jetbrains.com/issue/KT-67216
suppressKotlinVersionCompatibilityCheck = libs.versions.kotlin.get()
enableIntrinsicRemember = true
enableNonSkippingGroupOptimization = true
enableExperimentalStrongSkippingMode = true
}
androidComponents {
onVariants(selector().withBuildType("release")) {
it.packaging.resources.excludes.addAll(
-5
View File
@@ -11,11 +11,6 @@
public static final ** CREATOR;
}
# Workaround for https://issuetracker.google.com/331556916
-keepclassmembers class androidx.biometric.BiometricManager {
public int canAuthenticate(int);
}
# Ktor logger
-dontwarn org.slf4j.impl.StaticLoggerBinder
+2 -1
View File
@@ -63,6 +63,7 @@ option(ENABLE_CPIO OFF)
option(ENABLE_CAT OFF)
option(ENABLE_UNZIP OFF)
option(ENABLE_TEST OFF)
option(ENABLE_WERROR OFF)
# Configure libarchive link's static lib
SET(LIBARCHIVE_CUSTOM_LIBS "nettle" "liblzma")
@@ -70,7 +71,7 @@ SET(LIBARCHIVE_CUSTOM_LIBS "nettle" "liblzma")
FetchContent_Declare(
libarchive
GIT_REPOSITORY https://github.com/FooIbar/libarchive.git
GIT_TAG v3.7.2
GIT_TAG v3.7.3
)
FetchContent_MakeAvailable(libarchive)
@@ -32,6 +32,7 @@ import com.hippo.ehviewer.dao.QuickSearch
import com.hippo.ehviewer.dao.Schema17to18
import com.hippo.ehviewer.download.DownloadManager
import com.hippo.ehviewer.util.sendTo
import com.hippo.unifile.UniFile
import com.hippo.unifile.asUniFile
import kotlinx.coroutines.flow.Flow
import splitties.arch.room.roomDb
@@ -260,10 +261,10 @@ object EhDB {
db.filterDao().update(filter)
}
fun exportDB(context: Context, uri: Uri) {
fun exportDB(context: Context, file: UniFile) {
db.query("PRAGMA wal_checkpoint(FULL)", null).use { it.moveToNext() }
val dbFile = context.getDatabasePath(DB_NAME)
dbFile.asUniFile() sendTo uri.asUniFile()
dbFile.asUniFile() sendTo file
}
suspend fun importDB(context: Context, uri: Uri) {
@@ -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)
@@ -142,6 +143,7 @@ object Settings : DataStorePreferences(null) {
var showTagTranslations by boolPref(KEY_SHOW_TAG_TRANSLATIONS, false).observed { updateWhenTagTranslationChanges() }
var appLinkVerifyTip by boolPref("app_link_verify_tip", false)
var enabledSecurity by boolPref("enable_secure", false)
var backupBeforeUpdate by boolPref("backup_before_update", true)
var useCIUpdateChannel by boolPref("ci_update_channel", false)
var mediaScan by boolPref("media_scan", false).observed { updateWhenKeepMediaStatusChanges() }
var hasDefaultDownloadLabel by boolPref("has_default_download_label", false)
@@ -422,7 +422,7 @@ class MainActivity : EhActivity() {
ModalDrawerSheet(
modifier = Modifier.widthIn(max = (configuration.screenWidthDp - 112).dp),
drawerShape = ShapeDefaults.Large.copy(topEnd = CornerSize(0), bottomEnd = CornerSize(0)),
windowInsets = WindowInsets.systemBars.only(WindowInsetsSides.Top + WindowInsetsSides.End),
windowInsets = WindowInsets.systemBars.only(WindowInsetsSides.Vertical + WindowInsetsSides.End),
) {
CompositionLocalProvider(LocalViewConfiguration provides viewConfiguration) {
sheet(sideSheetState)
@@ -2,10 +2,10 @@ package com.hippo.ehviewer.ui.main
import androidx.compose.foundation.background
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Pause
@@ -18,19 +18,24 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProvideTextStyle
import androidx.compose.material3.ShapeDefaults
import androidx.compose.material3.Text
import androidx.compose.material3.minimumInteractiveComponentSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.movableContentOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.ConstraintSet
import androidx.constraintlayout.compose.Dimension
import arrow.core.Tuple9
import com.hippo.ehviewer.R
import com.hippo.ehviewer.Settings
import com.hippo.ehviewer.client.EhUtils
@@ -40,6 +45,51 @@ import com.hippo.ehviewer.ui.tools.CrystalCard
import com.hippo.ehviewer.ui.tools.GalleryListCardRating
import com.hippo.ehviewer.util.FileUtils
private val ids = Tuple9(1, 2, 3, 4, 5, 6, 7, 8, 9)
private val constraintSet = ConstraintSet {
val (
titleRef, uploaderRef, ratingRef, categoryRef, actionsRef, stateTextRef,
progressBarRef, progressTextRef, speedRef,
) = createRefsFor(1, 2, 3, 4, 5, 6, 7, 8, 9)
constrain(titleRef) {
top.linkTo(parent.top)
width = Dimension.matchParent
}
constrain(uploaderRef) {
start.linkTo(parent.start)
bottom.linkTo(actionsRef.top)
}
constrain(ratingRef) {
start.linkTo(parent.start)
top.linkTo(actionsRef.top, 1.dp)
}
constrain(categoryRef) {
start.linkTo(parent.start)
bottom.linkTo(parent.bottom)
}
constrain(progressBarRef) {
bottom.linkTo(actionsRef.top)
width = Dimension.matchParent
}
constrain(progressTextRef) {
bottom.linkTo(progressBarRef.top)
start.linkTo(progressBarRef.start)
}
constrain(speedRef) {
bottom.linkTo(progressBarRef.top)
end.linkTo(progressBarRef.end)
}
constrain(actionsRef) {
end.linkTo(parent.end, (-4).dp)
bottom.linkTo(parent.bottom, (-4).dp)
}
constrain(stateTextRef) {
end.linkTo(parent.end)
bottom.linkTo(actionsRef.top)
}
}
@Composable
fun DownloadCard(
onClick: () -> Unit,
@@ -48,143 +98,122 @@ fun DownloadCard(
onStart: () -> Unit,
onStop: () -> Unit,
info: DownloadInfo,
selectMode: Boolean,
modifier: Modifier = Modifier,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
) {
CrystalCard(
modifier = modifier,
onClick = onClick,
onLongClick = onLongClick,
interactionSource = interactionSource,
) {
Row {
Card(onClick = onThumbClick) {
) = CrystalCard(modifier = modifier, onClick = onClick, onLongClick = onLongClick, interactionSource = interactionSource) {
Row {
val thumb = remember {
movableContentOf<DownloadInfo> {
EhAsyncCropThumb(
key = info,
key = it,
modifier = Modifier.aspectRatio(DEFAULT_ASPECT).fillMaxSize(),
)
}
val stateFailed = stringResource(R.string.download_state_failed)
val stateFailed2 = stringResource(R.string.download_state_failed_2, info.legacy)
val downloadState by DownloadManager.collectDownloadState(info.gid)
val stateText = when (downloadState) {
DownloadInfo.STATE_NONE -> stringResource(R.string.download_state_none)
DownloadInfo.STATE_WAIT -> stringResource(R.string.download_state_wait)
DownloadInfo.STATE_DOWNLOAD -> null
DownloadInfo.STATE_FAILED -> if (info.legacy <= 0) stateFailed else stateFailed2
DownloadInfo.STATE_FINISH -> stringResource(R.string.download_state_finish)
else -> null // Chill, will be removed soon
}
if (selectMode) {
Card {
thumb(info)
}
ConstraintLayout(modifier = Modifier.padding(8.dp, 4.dp).fillMaxSize()) {
val (
titleRef, uploaderRef, ratingRef, categoryRef, actionsRef, stateTextRef,
progressBarRef, progressTextRef, speedRef,
) = createRefs()
Text(
text = EhUtils.getSuitableTitle(info),
maxLines = 2,
modifier = Modifier.constrainAs(titleRef) {
top.linkTo(parent.top)
start.linkTo(parent.start)
}.fillMaxWidth(),
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.titleSmall,
)
if (downloadState != DownloadInfo.STATE_DOWNLOAD) {
ProvideTextStyle(MaterialTheme.typography.labelLarge) {
info.uploader?.let {
Text(
text = it,
modifier = Modifier.constrainAs(uploaderRef) {
start.linkTo(parent.start)
bottom.linkTo(ratingRef.top)
}.alpha(if (info.disowned) 0.5f else 1f),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
GalleryListCardRating(
rating = info.rating,
modifier = Modifier.constrainAs(ratingRef) {
start.linkTo(parent.start)
bottom.linkTo(categoryRef.top)
},
)
val categoryColor = EhUtils.getCategoryColor(info.category)
val categoryText = EhUtils.getCategory(info.category).uppercase()
} else {
Card(onClick = onThumbClick) {
thumb(info)
}
}
val stateFailed = stringResource(R.string.download_state_failed)
val stateFailed2 = stringResource(R.string.download_state_failed_2, info.legacy)
val downloadState by DownloadManager.collectDownloadState(info.gid)
val stateText = when (downloadState) {
DownloadInfo.STATE_NONE -> stringResource(R.string.download_state_none)
DownloadInfo.STATE_WAIT -> stringResource(R.string.download_state_wait)
DownloadInfo.STATE_DOWNLOAD -> null
DownloadInfo.STATE_FAILED -> if (info.legacy <= 0) stateFailed else stateFailed2
DownloadInfo.STATE_FINISH -> stringResource(R.string.download_state_finish)
else -> null // Chill, will be removed soon
}
ConstraintLayout(
modifier = Modifier.padding(start = 8.dp, top = 2.dp, end = 4.dp, bottom = 4.dp).fillMaxSize(),
constraintSet = constraintSet,
) {
val (
titleRef, uploaderRef, ratingRef, categoryRef, actionsRef, stateTextRef,
progressBarRef, progressTextRef, speedRef,
) = ids
Text(
text = EhUtils.getSuitableTitle(info),
maxLines = 2,
modifier = Modifier.layoutId(titleRef),
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.titleSmall,
)
if (downloadState != DownloadInfo.STATE_DOWNLOAD) {
ProvideTextStyle(MaterialTheme.typography.labelLarge) {
info.uploader?.let {
Text(
text = categoryText,
modifier = Modifier.constrainAs(categoryRef) {
start.linkTo(parent.start)
bottom.linkTo(parent.bottom)
}.clip(ShapeDefaults.Small).background(categoryColor).padding(vertical = 2.dp, horizontal = 8.dp),
color = if (Settings.harmonizeCategoryColor) Color.Unspecified else EhUtils.categoryTextColor,
textAlign = TextAlign.Center,
text = it,
modifier = Modifier.layoutId(uploaderRef).alpha(if (info.disowned) 0.5f else 1f),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
} else {
val (total, finished, speed) = DownloadManager.updatedDownloadInfo(info) {
Triple(total, finished, speed)
}
ProvideTextStyle(MaterialTheme.typography.labelMedium) {
if (total <= 0 || finished < 0) {
LinearProgressIndicator(
modifier = Modifier.constrainAs(progressBarRef) {
bottom.linkTo(actionsRef.top)
}.fillMaxWidth(),
)
} else {
LinearProgressIndicator(
progress = { finished.toFloat() / total.toFloat() },
modifier = Modifier.constrainAs(progressBarRef) {
bottom.linkTo(actionsRef.top)
}.fillMaxWidth(),
)
Text(
text = "$finished/$total",
modifier = Modifier.constrainAs(progressTextRef) {
bottom.linkTo(progressBarRef.top)
start.linkTo(progressBarRef.start)
},
)
}
Text(
text = FileUtils.humanReadableByteCount(speed.coerceAtLeast(0), false) + "/S",
modifier = Modifier.constrainAs(speedRef) {
bottom.linkTo(progressBarRef.top)
end.linkTo(progressBarRef.end)
},
)
}
}
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.constrainAs(actionsRef) {
end.linkTo(parent.end)
bottom.linkTo(parent.bottom)
},
) {
if (downloadState == DownloadInfo.STATE_WAIT || downloadState == DownloadInfo.STATE_DOWNLOAD) {
IconButton(onClick = onStop) {
Icon(imageVector = Icons.Default.Pause, contentDescription = null)
}
} else {
IconButton(onClick = onStart) {
Icon(imageVector = Icons.Default.PlayArrow, contentDescription = null)
}
}
}
if (stateText != null) {
GalleryListCardRating(
rating = info.rating,
modifier = Modifier.layoutId(ratingRef),
)
val categoryColor = EhUtils.getCategoryColor(info.category)
val categoryText = EhUtils.getCategory(info.category).uppercase()
Text(
text = stateText,
modifier = Modifier.constrainAs(stateTextRef) {
end.linkTo(parent.end, 16.dp)
bottom.linkTo(actionsRef.top)
},
style = MaterialTheme.typography.titleSmall,
text = categoryText,
modifier = Modifier.layoutId(categoryRef).clip(ShapeDefaults.Small)
.background(categoryColor).padding(vertical = 2.dp, horizontal = 8.dp),
color = if (Settings.harmonizeCategoryColor) Color.Unspecified else EhUtils.categoryTextColor,
textAlign = TextAlign.Center,
)
}
} else {
val (total, finished, speed) = DownloadManager.updatedDownloadInfo(info) {
Triple(total, finished, speed)
}
ProvideTextStyle(MaterialTheme.typography.labelMedium) {
if (total <= 0 || finished < 0) {
LinearProgressIndicator(modifier = Modifier.layoutId(progressBarRef))
} else {
LinearProgressIndicator(
progress = { finished.toFloat() / total.toFloat() },
modifier = Modifier.layoutId(progressBarRef),
)
Text(
text = "$finished/$total",
modifier = Modifier.layoutId(progressTextRef),
)
}
Text(
text = FileUtils.humanReadableByteCount(speed.coerceAtLeast(0), false) + "/S",
modifier = Modifier.layoutId(speedRef),
)
}
}
val running = downloadState == DownloadInfo.STATE_WAIT || downloadState == DownloadInfo.STATE_DOWNLOAD
val icon = remember {
movableContentOf<Boolean> {
Icon(imageVector = if (it) Icons.Default.Pause else Icons.Default.PlayArrow, contentDescription = null)
}
}
if (selectMode) {
Box(modifier = Modifier.layoutId(actionsRef).minimumInteractiveComponentSize()) {
icon(running)
}
} else {
IconButton(onClick = if (running) onStop else onStart, modifier = Modifier.layoutId(actionsRef)) {
icon(running)
}
}
if (stateText != null) {
Text(
text = stateText,
modifier = Modifier.layoutId(stateTextRef),
style = MaterialTheme.typography.labelLarge,
)
}
}
}
@@ -3,7 +3,6 @@ package com.hippo.ehviewer.ui.main
import androidx.activity.compose.PredictiveBackHandler
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.EaseOut
import androidx.compose.animation.core.tween
import androidx.compose.foundation.MutatePriority
import androidx.compose.foundation.MutatorMutex
@@ -12,6 +11,7 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
@@ -31,9 +31,10 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.layout
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.lerp
import com.hippo.ehviewer.ui.tools.PredictiveBackEasing
import com.hippo.ehviewer.ui.tools.delegateSnapshotUpdate
import com.hippo.ehviewer.ui.tools.snackBarPadding
import eu.kanade.tachiyomi.util.lang.launchIO
@@ -140,69 +141,69 @@ fun FabLayout(
},
)
}
val appearState by state.appearProgress.asState()
val animatedProgress by state.expandProgress.asState()
PredictiveBackHandler(updatedExpanded) { flow ->
try {
state.mutatorMutex.mutate(MutatePriority.UserInput) {
flow.collect {
val eased = PredictiveBackEasing.transform(it.progress)
state.expandProgress.snapTo(1 - eased)
}
}
onExpandChanged(false)
} catch (e: CancellationException) {
coroutineScope.launch {
state.expand()
}
}
}
Box(
modifier = Modifier.fillMaxSize().navigationBarsPadding().padding(16.dp).snackBarPadding(),
modifier = Modifier.fillMaxSize().navigationBarsPadding().snackBarPadding(),
contentAlignment = Alignment.BottomEnd,
) {
Box(
modifier = Modifier.graphicsLayer {
if (!state.appearProgress.isRunning && !updatedHidden) {
Box(
modifier = Modifier.fillMaxSize().graphicsLayer { alpha = animatedProgress },
contentAlignment = Alignment.BottomEnd,
) {
with(secondaryFab) {
forEachIndexed { index, (imageVector, onClick) ->
SmallFloatingActionButton(
onClick = {
coroutineScope.launchIO {
runSuspendCatching {
onClick()
}
onExpandChanged(false)
}
},
modifier = Modifier.padding(20.dp).offset {
val distance = lerp(0, 150 * (size - index) + 50, animatedProgress)
IntOffset(0, -distance)
},
) {
Icon(imageVector = imageVector, contentDescription = null)
}
}
}
}
}
val appearState by state.appearProgress.asState()
FloatingActionButton(
onClick = { onExpandChanged(!updatedExpanded) },
modifier = Modifier.padding(16.dp).graphicsLayer {
rotationZ = lerp(-90f, 0f, appearState)
scaleX = appearState
scaleY = appearState
},
contentAlignment = Alignment.Center,
) {
val animatedProgress by state.expandProgress.asState()
PredictiveBackHandler(updatedExpanded) { flow ->
try {
state.mutatorMutex.mutate(MutatePriority.UserInput) {
flow.collect {
val eased = EaseOut.transform(it.progress)
state.expandProgress.snapTo(1 - eased)
}
}
onExpandChanged(false)
} catch (e: CancellationException) {
coroutineScope.launch {
state.expand()
}
}
}
FloatingActionButton(onClick = { onExpandChanged(!updatedExpanded) }) {
Icon(
imageVector = Icons.Default.Close,
contentDescription = null,
modifier = Modifier.graphicsLayer {
rotationZ = lerp(-135f, 0f, animatedProgress)
},
)
}
secondaryFab.run {
forEachIndexed { index, (imageVector, onClick) ->
SmallFloatingActionButton(
onClick = {
coroutineScope.launchIO {
runSuspendCatching {
onClick()
}
onExpandChanged(false)
}
},
modifier = Modifier.layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
layout(placeable.width, placeable.height) {
val distance = lerp(0, 150 * (size - index) + 50, animatedProgress)
placeable.placeRelative(0, -distance, -(size - index).toFloat())
}
}.graphicsLayer {
alpha = animatedProgress
},
) {
Icon(imageVector = imageVector, contentDescription = null)
}
}
}
Icon(
imageVector = Icons.Default.Close,
contentDescription = null,
modifier = Modifier.graphicsLayer {
rotationZ = lerp(-135f, 0f, animatedProgress)
},
)
}
}
}
@@ -42,6 +42,7 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.ConstraintSet
import androidx.constraintlayout.compose.Dimension
import arrow.core.Tuple7
import com.hippo.ehviewer.Settings
import com.hippo.ehviewer.client.EhUtils
@@ -60,15 +61,15 @@ private val constraintSet = ConstraintSet {
createRefsFor(1, 2, 3, 4, 5, 6, 7)
constrain(titleRef) {
top.linkTo(parent.top)
start.linkTo(parent.start)
width = Dimension.matchParent
}
constrain(uploaderRef) {
start.linkTo(parent.start)
bottom.linkTo(ratingRef.top)
bottom.linkTo(iconsRef.top)
}
constrain(ratingRef) {
start.linkTo(parent.start)
bottom.linkTo(categoryRef.top)
top.linkTo(iconsRef.top, 1.dp)
}
constrain(categoryRef) {
start.linkTo(parent.start)
@@ -76,7 +77,7 @@ private val constraintSet = ConstraintSet {
}
constrain(iconsRef) {
end.linkTo(parent.end)
bottom.linkTo(postedRef.top)
bottom.linkTo(categoryRef.top)
}
constrain(favRef) {
end.linkTo(parent.end)
@@ -84,7 +85,7 @@ private val constraintSet = ConstraintSet {
}
constrain(postedRef) {
end.linkTo(parent.end)
bottom.linkTo(parent.bottom)
linkTo(categoryRef.top, parent.bottom)
}
}
@@ -110,12 +111,15 @@ fun GalleryInfoListItem(
modifier = Modifier.aspectRatio(DEFAULT_ASPECT).fillMaxSize(),
)
}
ConstraintLayout(modifier = Modifier.padding(8.dp, 4.dp).fillMaxSize(), constraintSet = constraintSet) {
ConstraintLayout(
modifier = Modifier.padding(start = 8.dp, top = 2.dp, end = 4.dp, bottom = 4.dp).fillMaxSize(),
constraintSet = constraintSet,
) {
val (titleRef, uploaderRef, ratingRef, categoryRef, postedRef, favRef, iconsRef) = ids
Text(
text = EhUtils.getSuitableTitle(info),
maxLines = 2,
modifier = Modifier.layoutId(titleRef).fillMaxWidth(),
modifier = Modifier.layoutId(titleRef),
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.titleSmall,
)
@@ -145,6 +149,8 @@ fun GalleryInfoListItem(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.layoutId(iconsRef),
) {
// Placeholder to reserve minimum height
Text(text = "")
val download by DownloadManager.collectContainDownloadInfo(info.gid)
if (download) {
Icon(
@@ -153,7 +159,9 @@ fun GalleryInfoListItem(
modifier = Modifier.size(16.dp),
)
}
Text(text = info.simpleLanguage.orEmpty())
info.simpleLanguage?.let {
Text(text = it)
}
if (info.pages != 0 && showPages) {
Text(text = "${info.pages}P")
}
@@ -163,11 +171,12 @@ fun GalleryInfoListItem(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.layoutId(favRef),
) {
// Placeholder to reserve minimum height
Text(text = "")
if (isInFavScene) {
Text(
text = info.favoriteNote.orEmpty(),
fontStyle = FontStyle.Italic,
)
info.favoriteNote?.let {
Text(text = it, fontStyle = FontStyle.Italic)
}
} else {
val showFav by FavouriteStatusRouter.collectAsState(info) { it != NOT_FAVORITED }
if (showFav) {
@@ -176,7 +185,9 @@ fun GalleryInfoListItem(
contentDescription = null,
modifier = Modifier.size(16.dp),
)
Text(text = info.favoriteName.orEmpty())
info.favoriteName?.let {
Text(text = it)
}
}
}
}
@@ -3,8 +3,8 @@ package com.hippo.ehviewer.ui.main
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyRow
@@ -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,23 +70,31 @@ 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(
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp),
// Workaround for https://issuetracker.google.com/332939169
// contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp),
modifier = Modifier.padding(top = 8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
// Workaround for the first item's animation
// https://github.com/Calvin-LL/Reorderable/issues/4#issuecomment-1853131769
item {}
item {
Spacer(modifier = Modifier.width(8.dp))
}
items(categories, { it.first }) {
FilterChip(
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() },
)
}
item {
Spacer(modifier = Modifier.width(8.dp))
}
}
var languageFilter by Settings.languageFilter.asMutableState()
val languages = remember {
@@ -28,7 +28,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.util.VelocityTrackerAddPointsFix
import androidx.compose.ui.res.colorResource
import com.hippo.ehviewer.R
import com.hippo.ehviewer.Settings
@@ -57,15 +56,6 @@ import moe.tarsin.kt.unreachable
@Destination<RootGraph>
@Composable
fun ReaderScreen(info: BaseGalleryInfo, page: Int = -1, navigator: DestinationsNavigator) = composing(navigator) {
// Workaround for incorrect fling velocity on zoom gesture end
// https://github.com/saket/telephoto/issues/71
DisposableEffect(Unit) {
VelocityTrackerAddPointsFix = false
onDispose {
VelocityTrackerAddPointsFix = true
}
}
LockDrawer(true)
ConfigureKeepScreenOn()
val pageLoader = remember {
@@ -8,7 +8,6 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
@@ -16,7 +15,7 @@ import com.hippo.ehviewer.Settings
import com.hippo.ehviewer.gallery.PageLoader2
import kotlin.math.absoluteValue
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.filterNotNull
@Stable
class SliderPagerDoubleSync(
@@ -24,8 +23,10 @@ class SliderPagerDoubleSync(
private val pagerState: PagerState,
private val pageLoader: PageLoader2,
) {
private var sliderFollowPager by mutableStateOf(true)
var sliderValue by mutableIntStateOf(1)
private var requestScroll = true
private var sliderFollowPager = true
var sliderValue by mutableIntStateOf(pageLoader.startPage + 1)
private set
fun sliderScrollTo(index: Int) {
sliderFollowPager = false
@@ -37,52 +38,55 @@ class SliderPagerDoubleSync(
val fling by lazyListState.interactionSource.collectIsDraggedAsState()
val pagerFling by pagerState.interactionSource.collectIsDraggedAsState()
if (fling || pagerFling) sliderFollowPager = true
val startPage = remember { pageLoader.startPage }
val currentIndexFlow = remember(webtoon) {
requestScroll = true
if (webtoon) {
snapshotFlow { lazyListState.layoutInfo }.mapNotNull { info ->
info.visibleItemsInfo.lastOrNull {
it.offset <= maxOf(info.viewportStartOffset, info.viewportEndOffset - it.size)
}?.index
}
snapshotFlow {
with(lazyListState.layoutInfo) {
visibleItemsInfo.lastOrNull {
it.offset <= maxOf(viewportStartOffset, viewportEndOffset - it.size)
}?.index
}
}.filterNotNull()
} else {
snapshotFlow { pagerState.currentPage }
}
}
if (sliderFollowPager) {
LaunchedEffect(currentIndexFlow) {
currentIndexFlow.collect { index ->
LaunchedEffect(currentIndexFlow) {
currentIndexFlow.collect { index ->
if (sliderFollowPager && !requestScroll) {
sliderValue = index + 1
pageLoader.startPage = index
}
}
} else {
LaunchedEffect(webtoon) {
snapshotFlow { sliderValue - 1 }.collectLatest { index ->
if (webtoon) {
val noAnim = (index - lazyListState.firstVisibleItemIndex).absoluteValue > SMOOTH_SCROLL_THRESHOLD || !Settings.pageTransitions.value
if (noAnim) {
lazyListState.scrollToItem(index)
} else {
lazyListState.animateScrollToItem(index)
}
}
LaunchedEffect(webtoon) {
snapshotFlow { sliderValue - 1 }.collectLatest { index ->
if (sliderFollowPager && !requestScroll) return@collectLatest
if (webtoon) {
if (requestScroll) {
lazyListState.requestScrollToItem(index)
requestScroll = false
} else {
val noAnim = (index - pagerState.currentPage).absoluteValue > SMOOTH_SCROLL_THRESHOLD || !Settings.pageTransitions.value
if (noAnim) {
pagerState.scrollToPage(index)
if (smoothScroll((index - lazyListState.firstVisibleItemIndex))) {
lazyListState.animateScrollToItem(index)
} else {
lazyListState.scrollToItem(index)
}
}
} else {
if (requestScroll) {
pagerState.requestScrollToPage(index)
requestScroll = false
} else {
if (smoothScroll((index - pagerState.currentPage))) {
pagerState.animateScrollToPage(index)
} else {
pagerState.scrollToPage(index)
}
}
pageLoader.startPage = index
}
}
}
LaunchedEffect(Unit) {
if (webtoon) {
lazyListState.scrollToItem(startPage)
} else {
pagerState.scrollToPage(startPage)
pageLoader.startPage = index
}
}
}
@@ -100,4 +104,7 @@ fun rememberSliderPagerDoubleSyncState(
}
}
private fun smoothScroll(distance: Int) =
distance.absoluteValue < SMOOTH_SCROLL_THRESHOLD && Settings.pageTransitions.value
private const val SMOOTH_SCROLL_THRESHOLD = 50
@@ -29,6 +29,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.unit.dp
import kotlin.math.roundToInt
@Composable
fun SpinnerChoice(title: String, entries: Array<String>, values: Array<String>, field: MutableState<Int>) {
@@ -97,9 +98,7 @@ fun SliderChoice(
) {
startSlot()
val configuration = LocalConfiguration.current
// https://m3.material.io/components/sliders/specs
// Tick width is 2 dp
val maxTickCount = configuration.screenWidthDp / 6
val maxTickCount = configuration.screenWidthDp / (SliderDefaults.TickSize.value * 2.5f).roundToInt()
val steps = range.last - range.first - 1
val showTicks = steps < maxTickCount
Slider(
@@ -10,15 +10,11 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.itemsIndexed
@@ -111,6 +107,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 +132,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) }
@@ -204,7 +202,12 @@ fun DownloadsScreen(navigator: DestinationsNavigator) = composing(navigator) {
IconButton(
onClick = {
launch {
showSelectActions(R.string.default_download_label) {
val selected = if (!Settings.hasDefaultDownloadLabel) {
0
} else {
labelList.indexOfFirst { it.label == Settings.defaultDownloadLabel } + 2
}
showSelectActions(R.string.default_download_label, selected) {
onSelect(letMeSelect) {
Settings.hasDefaultDownloadLabel = false
}
@@ -238,9 +241,10 @@ fun DownloadsScreen(navigator: DestinationsNavigator) = composing(navigator) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
state = labelsListState,
contentPadding = WindowInsets.systemBars.only(WindowInsetsSides.Bottom).asPaddingValues(),
// Workaround for https://issuetracker.google.com/332939169
// contentPadding = WindowInsets.systemBars.only(WindowInsetsSides.Bottom).asPaddingValues(),
) {
stickyHeader {
item {
ListItem(
modifier = Modifier.clickable {
switchLabel("")
@@ -253,7 +257,7 @@ fun DownloadsScreen(navigator: DestinationsNavigator) = composing(navigator) {
},
)
}
stickyHeader {
item {
ListItem(
modifier = Modifier.clickable {
switchLabel(null)
@@ -266,7 +270,8 @@ fun DownloadsScreen(navigator: DestinationsNavigator) = composing(navigator) {
},
)
}
itemsIndexed(labelList, key = { _, item -> item.id!! }) { index, (item, _, id) ->
itemsIndexed(labelList, key = { _, item -> item.id!! }) { index, (label, _, id) ->
val item by rememberUpdatedState(label)
// Not using rememberSwipeToDismissBoxState to prevent LazyColumn from reusing it
val dismissState = remember { SwipeToDismissBoxState(SwipeToDismissBoxValue.Settled, density, positionalThreshold = positionalThreshold) }
LaunchedEffect(dismissState) {
@@ -481,7 +486,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) { animateItem() },
badgeText = info.pages.takeIf { it > 0 }?.toString(),
)
}
@@ -494,7 +499,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) {
@@ -521,6 +529,7 @@ fun DownloadsScreen(navigator: DestinationsNavigator) = composing(navigator) {
},
onStop = { launchIO { DownloadManager.stopDownload(info.gid) } },
info = info,
selectMode = selectMode,
modifier = Modifier.height(height),
interactionSource = interactionSource,
)
@@ -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) { animateItem() },
) { 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) { animateItem() },
) { interactionSource ->
GalleryInfoGridItem(
onClick = {
if (selectMode) {
@@ -64,8 +64,9 @@ import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalTextToolbar
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.fromHtml
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.parseAsHtml
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.max
@@ -74,12 +75,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 +95,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 +156,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()) }
@@ -298,10 +303,13 @@ fun GalleryCommentsScreen(gid: Long, navigator: DestinationsNavigator) = composi
modifier = Modifier.padding(horizontal = keylineMargin),
verticalArrangement = Arrangement.spacedBy(8.dp),
contentPadding = PaddingValues(
top = paddingValues.calculateTopPadding(),
// top = paddingValues.calculateTopPadding(),
bottom = paddingValues.calculateBottomPadding() + additionalPadding,
),
) {
item {
Spacer(modifier = Modifier.height(paddingValues.calculateTopPadding()))
}
items(
items = comments.comments,
key = { it.id },
@@ -333,7 +341,7 @@ fun GalleryCommentsScreen(gid: Long, navigator: DestinationsNavigator) = composi
}
if (comment.editable) {
onSelect(editComment) {
userComment = TextFieldValue(comment.comment.parseAsHtml())
userComment = TextFieldValue(AnnotatedString.fromHtml(comment.comment))
commentId = comment.id
commenting = true
}
@@ -356,7 +364,7 @@ fun GalleryCommentsScreen(gid: Long, navigator: DestinationsNavigator) = composi
}
GalleryCommentCard(
modifier = Modifier.animateItem(),
modifier = Modifier.thenIf(animateItems) { animateItem() },
comment = item,
onUserClick = {
navigate(
@@ -602,7 +602,7 @@ fun GalleryDetailScreen(args: GalleryDetailScreenArgs, navigator: DestinationsNa
showSnackbar(noCurrentTorrents)
} else {
val items = mTorrentList!!.map { it.format() }
val selected = showSelectItem(items, R.string.torrents, false)
val selected = showSelectItem(items, R.string.torrents, respectDefaultWidth = false)
val url = mTorrentList!![selected].url
val name = "${mTorrentList!![selected].name}.torrent"
val r = DownloadManager.Request(Uri.parse(url))
@@ -18,6 +18,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.hippo.ehviewer.R
import com.hippo.ehviewer.Settings
import com.hippo.ehviewer.collectAsState
@@ -63,7 +64,7 @@ fun collectListThumbSizeAsState(): State<Dp> {
val density = LocalDensity.current
return Settings.listThumbSize.collectAsState {
with(density) {
it.toDp() * 9
(it.toDp() * 9).coerceAtLeast(120.dp)
}
}
}
@@ -10,27 +10,19 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.lazy.staggeredgrid.rememberLazyStaggeredGridState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.Help
import androidx.compose.material.icons.automirrored.filled.LastPage
@@ -41,7 +33,6 @@ import androidx.compose.material.icons.filled.Shuffle
import androidx.compose.material.icons.outlined.Bookmarks
import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.ListItem
@@ -381,12 +372,11 @@ fun GalleryListScreen(lub: ListUrlBuilder, navigator: DestinationsNavigator) = c
LazyColumn(
modifier = Modifier.fillMaxSize(),
state = quickSearchListState,
contentPadding = WindowInsets.systemBars.only(WindowInsetsSides.Bottom).asPaddingValues(),
// Workaround for https://issuetracker.google.com/332939169
// contentPadding = WindowInsets.systemBars.only(WindowInsetsSides.Bottom).asPaddingValues(),
) {
// Fix the first item's reorder animation
stickyHeader {
HorizontalDivider()
}
item {}
itemsIndexed(quickSearchList, key = { _, item -> item.id!! }) { index, item ->
ReorderableItem(reorderableLazyListState, key = item.id!!) { isDragging ->
// Not using rememberSwipeToDismissBoxState to prevent LazyColumn from reusing it
@@ -606,42 +596,37 @@ fun GalleryListScreen(lub: ListUrlBuilder, navigator: DestinationsNavigator) = c
val layoutDirection = LocalLayoutDirection.current
val marginH = dimensionResource(id = R.dimen.gallery_list_margin_h)
val marginV = dimensionResource(id = R.dimen.gallery_list_margin_v)
Column(
modifier = Modifier.imePadding().verticalScroll(rememberScrollState())
.padding(
top = contentPadding.calculateTopPadding() + marginV,
start = contentPadding.calculateStartPadding(layoutDirection) + marginH,
end = contentPadding.calculateEndPadding(layoutDirection) + marginH,
bottom = 8.dp,
)
.windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Bottom))
.graphicsLayer {
scaleX = 1 - animatedSearchLayout
scaleY = 1 - animatedSearchLayout
alpha = 1 - animatedSearchLayout
},
ElevatedCard(
modifier = Modifier.graphicsLayer {
scaleX = 1 - animatedSearchLayout
scaleY = 1 - animatedSearchLayout
alpha = 1 - animatedSearchLayout
}.padding(
top = contentPadding.calculateTopPadding() + marginV,
start = contentPadding.calculateStartPadding(layoutDirection) + marginH,
end = contentPadding.calculateEndPadding(layoutDirection) + marginH,
bottom = 8.dp,
).padding(vertical = dimensionResource(id = R.dimen.search_layout_margin_v)),
) {
ElevatedCard(modifier = Modifier.padding(vertical = dimensionResource(id = R.dimen.search_layout_margin_v))) {
Column(
modifier = Modifier.padding(
horizontal = dimensionResource(id = R.dimen.search_category_padding_h),
vertical = dimensionResource(id = R.dimen.search_category_padding_v),
).fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = stringResource(id = R.string.search_image),
modifier = Modifier.height(dimensionResource(id = R.dimen.search_category_title_height)),
fontWeight = FontWeight.Bold,
style = MaterialTheme.typography.titleMedium,
)
ImageSearch(
image = imageUri,
onSelectImage = {
launch { imageUri = pickVisualMedia(ActivityResultContracts.PickVisualMedia.ImageOnly) }
},
)
}
Column(
modifier = Modifier.padding(
horizontal = dimensionResource(id = R.dimen.search_category_padding_h),
vertical = dimensionResource(id = R.dimen.search_category_padding_v),
).fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = stringResource(id = R.string.search_image),
modifier = Modifier.height(dimensionResource(id = R.dimen.search_category_title_height)),
fontWeight = FontWeight.Bold,
style = MaterialTheme.typography.titleMedium,
)
ImageSearch(
image = imageUri,
onSelectImage = {
launch { imageUri = pickVisualMedia(ActivityResultContracts.PickVisualMedia.ImageOnly) }
},
)
}
}
@@ -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()) },
@@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.displayCutoutPadding
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
@@ -66,12 +65,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 +122,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?,
@@ -304,9 +306,11 @@ fun SearchBarScreen(
activeState.Anchor()
filter?.invoke()
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = WindowInsets.navigationBars.union(WindowInsets.ime)
.only(WindowInsetsSides.Bottom).asPaddingValues(),
modifier = Modifier.fillMaxSize()
.windowInsetsPadding(WindowInsets.navigationBars.union(WindowInsets.ime).only(WindowInsetsSides.Bottom)),
// Workaround for https://issuetracker.google.com/332939169
// contentPadding = WindowInsets.navigationBars.union(WindowInsets.ime)
// .only(WindowInsetsSides.Bottom).asPaddingValues(),
) {
// Workaround for prepending before the first item
item {}
@@ -329,7 +333,7 @@ fun SearchBarScreen(
}
},
colors = ListItemDefaults.colors(containerColor = Color.Transparent),
modifier = Modifier.clickable { it.onClick() }.animateItem(),
modifier = Modifier.clickable { it.onClick() }.thenIf(animateItems) { animateItem() },
)
}
}
@@ -26,11 +26,14 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.parseAsHtml
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.fromHtml
import androidx.compose.ui.unit.dp
import com.hippo.ehviewer.BuildConfig
import com.hippo.ehviewer.EhDB
import com.hippo.ehviewer.R
import com.hippo.ehviewer.Settings
import com.hippo.ehviewer.download.downloadLocation
import com.hippo.ehviewer.ui.destinations.LicenseScreenDestination
import com.hippo.ehviewer.ui.tools.DialogState
import com.hippo.ehviewer.ui.tools.LocalDialogState
@@ -38,6 +41,7 @@ import com.hippo.ehviewer.ui.tools.observed
import com.hippo.ehviewer.updater.AppUpdater
import com.hippo.ehviewer.updater.Release
import com.hippo.ehviewer.util.AppConfig
import com.hippo.ehviewer.util.ReadableTime
import com.hippo.ehviewer.util.displayString
import com.hippo.ehviewer.util.installPackage
import com.ramcosta.composedestinations.annotation.Destination
@@ -47,6 +51,7 @@ import eu.kanade.tachiyomi.util.lang.withUIContext
import java.io.File
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.datetime.Clock
import moe.tarsin.coroutines.runSuspendCatching
private const val REPO_URL = "https://github.com/${BuildConfig.REPO_NAME}"
@@ -58,7 +63,7 @@ private fun versionCode() = "${BuildConfig.VERSION_NAME} (${BuildConfig.COMMIT_S
@Composable
@Stable
private fun author() = stringResource(R.string.settings_about_author_summary).replace('$', '@').parseAsHtml()
private fun author() = AnnotatedString.fromHtml(stringResource(R.string.settings_about_author_summary).replace('$', '@'))
@Destination<RootGraph>
@Composable
@@ -107,6 +112,10 @@ fun AboutScreen(navigator: DestinationsNavigator) {
title = stringResource(id = R.string.settings_about_version),
summary = versionCode(),
)
SwitchPreference(
title = stringResource(id = R.string.backup_before_update),
value = Settings::backupBeforeUpdate,
)
SwitchPreference(
title = stringResource(id = R.string.use_ci_update_channel),
value = Settings::useCIUpdateChannel,
@@ -144,6 +153,12 @@ suspend fun DialogState.showNewVersion(context: Context, release: Release) {
Text(text = release.changelog)
}
}
if (Settings.backupBeforeUpdate) {
val time = ReadableTime.getFilenamableTime(Clock.System.now().toEpochMilliseconds())
downloadLocation.createFile("$time.db")?.let {
EhDB.exportDB(context, it)
}
}
// TODO: Download in the background and show progress in notification
val file = File(AppConfig.tempDir, "update.apk").apply { delete() }
AppUpdater.downloadUpdate(release.downloadLink, file)
@@ -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.unifile.asUniFile
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,13 @@ fun AdvancedScreen(navigator: DestinationsNavigator) {
title = stringResource(id = R.string.preload_thumb_aggressively),
value = Settings::preloadThumbAggressively,
)
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,
@@ -172,7 +182,7 @@ fun AdvancedScreen(navigator: DestinationsNavigator) {
uri?.let {
context.runCatching {
grantUriPermission(BuildConfig.APPLICATION_ID, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
EhDB.exportDB(context, uri)
EhDB.exportDB(context, uri.asUniFile())
launchSnackBar(getString(R.string.settings_advanced_export_data_to, uri.toString()))
}.onFailure {
logcat(it)
@@ -94,6 +94,7 @@ fun DownloadScreen(navigator: DestinationsNavigator) {
keepNoMediaFileStatus(downloadLocationState)
}
}.onFailure {
logcat(it)
launchSnackBar(cannotGetDownloadLocation)
}
}
@@ -37,7 +37,8 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringArrayResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.parseAsHtml
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.fromHtml
import com.hippo.ehviewer.R
import com.hippo.ehviewer.Settings
import com.hippo.ehviewer.asMutableState
@@ -116,7 +117,7 @@ fun EhScreen(navigator: DestinationsNavigator) {
Column {
val warning = stringResource(id = R.string.settings_eh_identity_cookies_signed)
val str = cookies.joinToString("\n") { (k, v) -> "$k: $v" }
Text(text = warning.parseAsHtml())
Text(text = AnnotatedString.fromHtml(warning))
Spacer(modifier = Modifier.size(dimensionResource(id = R.dimen.keyline_margin)))
OutlinedTextField(
value = str,
@@ -241,7 +242,11 @@ fun EhScreen(navigator: DestinationsNavigator) {
addAll(Settings.favCat)
}
}
defaultFavSlot = dialogState.showSelectItem(items, R.string.default_favorites_collection) - 2
defaultFavSlot = dialogState.showSelectItem(
items = items,
title = R.string.default_favorites_collection,
selected = defaultFavSlot + 2,
) - 2
}
}
SimpleMenuPreferenceInt(
@@ -28,6 +28,7 @@ import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.MenuAnchorType
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
@@ -49,14 +50,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 +78,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 {
@@ -122,7 +127,7 @@ fun FilterScreen(navigator: DestinationsNavigator) {
onExpandedChange = { expanded = !expanded },
) {
OutlinedTextField(
modifier = Modifier.menuAnchor(),
modifier = Modifier.menuAnchor(MenuAnchorType.PrimaryNotEditable),
readOnly = true,
value = type,
onValueChange = {},
@@ -210,8 +215,9 @@ fun FilterScreen(navigator: DestinationsNavigator) {
) { paddingValues ->
Deferred({ allFilterMap.await() }) { filters ->
LazyColumn(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
contentPadding = paddingValues,
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection).padding(paddingValues),
// Workaround for https://issuetracker.google.com/332939169
// contentPadding = paddingValues,
) {
var showTip = true
filters.forEach { (filterMode, filters) ->
@@ -224,10 +230,10 @@ fun FilterScreen(navigator: DestinationsNavigator) {
FilterMode.COMMENT -> R.string.filter_comment
}
if (filters.isNotEmpty()) {
stickyHeader(key = filterMode) {
item(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 +241,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(
@@ -35,7 +35,7 @@ import androidx.compose.material3.DisplayMode
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.ListItem
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.ListItemDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.RadioButton
@@ -50,7 +50,6 @@ import androidx.compose.material3.TimePicker
import androidx.compose.material3.rememberDatePickerState
import androidx.compose.material3.rememberTimePickerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -442,12 +441,14 @@ class DialogState {
suspend fun showSelectItem(
items: List<String>,
@StringRes title: Int? = null,
selected: Int = -1,
respectDefaultWidth: Boolean = true,
) = showSelectItem(items, title?.right(), respectDefaultWidth)
) = showSelectItem(items, title?.right(), selected, respectDefaultWidth)
suspend fun showSelectItem(
items: List<String>,
title: Either<String, Int>?,
selected: Int = -1,
respectDefaultWidth: Boolean = true,
): Int = showNoButton(respectDefaultWidth) {
Column(modifier = Modifier.padding(vertical = 8.dp)) {
@@ -460,14 +461,11 @@ class DialogState {
}
LazyColumn {
itemsIndexed(items) { index, text ->
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.tertiary) {
Text(
text = text,
modifier = Modifier.clickable { dismissWith(index) }.fillMaxWidth()
.padding(horizontal = 24.dp, vertical = 16.dp),
style = MaterialTheme.typography.titleMedium,
)
}
CheckableItem(
text = text,
checked = index == selected,
modifier = Modifier.fillMaxWidth().clickable { dismissWith(index) }.padding(horizontal = 8.dp),
)
}
}
}
@@ -475,11 +473,12 @@ class DialogState {
suspend inline fun showSelectActions(
@StringRes title: Int? = null,
selected: Int = -1,
builder: ActionScope.() -> Unit,
) {
val (items, actions) = buildList { builder(ActionScope { action, that -> add(action to that) }) }.unzip()
val selected = showSelectItem(items, title)
actions[selected].invoke()
val index = showSelectItem(items, title, selected)
actions[index].invoke()
}
suspend fun showSelectItemWithCheckBox(
@@ -490,8 +489,6 @@ class DialogState {
initialChecked: Boolean = false,
): Pair<Int, Boolean> = showNoButton {
var checked by remember { mutableStateOf(initialChecked) }
val textStyle = MaterialTheme.typography.titleMedium
val selectedColor = MaterialTheme.colorScheme.tertiary
Column(modifier = Modifier.padding(vertical = 8.dp)) {
Text(
text = stringResource(id = title),
@@ -500,19 +497,10 @@ class DialogState {
)
LazyColumn {
itemsIndexed(items) { index, text ->
ListItem(
headlineContent = {
Text(
text = text,
style = if (index == selected) textStyle.copy(color = selectedColor) else textStyle,
)
},
modifier = Modifier.clickable { dismissWith(index to checked) }.fillMaxWidth(),
trailingContent = {
if (index == selected) {
Icon(imageVector = Icons.Default.Check, contentDescription = null, tint = selectedColor)
}
},
CheckableItem(
text = text,
checked = index == selected,
modifier = Modifier.fillMaxWidth().clickable { dismissWith(index to checked) }.padding(horizontal = 8.dp),
)
}
}
@@ -538,10 +526,11 @@ class DialogState {
headlineContent = {
Text(text = stringResource(id = text), style = MaterialTheme.typography.titleMedium)
},
modifier = Modifier.clickable { dismissWith(index) },
modifier = Modifier.clickable { dismissWith(index) }.padding(horizontal = 8.dp),
leadingContent = {
Icon(imageVector = icon, contentDescription = null, tint = AlertDialogDefaults.iconContentColor)
},
colors = ListItemDefaults.colors(containerColor = Color.Unspecified),
)
}
}
@@ -606,6 +595,25 @@ class DialogState {
}
}
@Composable
private fun CheckableItem(text: String, checked: Boolean, modifier: Modifier = Modifier) {
val textStyle = MaterialTheme.typography.titleMedium
val checkedColor = MaterialTheme.colorScheme.primary
ListItem(
headlineContent = {
Text(
text = text,
style = if (checked) textStyle.copy(color = checkedColor) else textStyle,
)
},
modifier = modifier,
trailingContent = checked.ifTrueThen {
Icon(imageVector = Icons.Default.Check, contentDescription = null, tint = checkedColor)
},
colors = ListItemDefaults.colors(containerColor = Color.Unspecified),
)
}
private val IconWithTextCorner = RoundedCornerShape(8.dp)
private val DatePickerTitlePadding = PaddingValues(start = 24.dp, end = 12.dp, top = 16.dp)
@@ -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
@@ -4,7 +4,7 @@ import androidx.activity.BackEventCompat
import androidx.activity.compose.PredictiveBackHandler
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.EaseOut
import androidx.compose.animation.core.CubicBezierEasing
import androidx.compose.animation.core.Easing
import androidx.compose.animation.core.VectorConverter
import androidx.compose.animation.core.spring
@@ -23,6 +23,8 @@ import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
val PredictiveBackEasing = CubicBezierEasing(0.1f, 0.1f, 0f, 1f)
/* enable 0 1
* animationProgress 1F 0F
*
@@ -41,7 +43,7 @@ import kotlinx.coroutines.launch
fun animateFloatMergePredictiveBackAsState(
enable: Boolean,
animationSpec: AnimationSpec<Float> = spring(),
predictiveBackInterpolator: Easing = EaseOut,
predictiveBackInterpolator: Easing = PredictiveBackEasing,
finishedListener: ((Float) -> Unit)? = null,
onBack: () -> Unit,
): State<Float> {
@@ -84,7 +86,7 @@ fun animateFloatMergePredictiveBackAsState(
fun animateFloatMergeOneWayPredictiveBackAsState(
enable: Boolean,
animationSpec: AnimationSpec<Float> = spring(),
predictiveBackInterpolator: Easing = EaseOut,
predictiveBackInterpolator: Easing = PredictiveBackEasing,
onBack: suspend () -> Unit,
): State<Float> {
val animatable = remember { Animatable(0f, Float.VectorConverter) }
@@ -15,45 +15,40 @@
*/
package com.hippo.unifile
import android.content.Context
import android.net.Uri
import android.provider.DocumentsContract
import splitties.init.appCtx
object DocumentsContractApi21 {
private const val PATH_DOCUMENT = "document"
private const val PATH_TREE = "tree"
private val resolver = appCtx.contentResolver
private val projection = arrayOf(
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
DocumentsContract.Document.COLUMN_DISPLAY_NAME,
DocumentsContract.Document.COLUMN_MIME_TYPE,
)
fun createFile(self: Uri, mimeType: String, displayName: String) = runCatching {
DocumentsContract.createDocument(resolver, self, mimeType, displayName)
}.getOrNull()
fun createDirectory(self: Uri, displayName: String) = createFile(self, DocumentsContract.Document.MIME_TYPE_DIR, displayName)
fun prepareTreeUri(treeUri: Uri?): Uri {
fun prepareTreeUri(treeUri: Uri): Uri {
val documentId = if (treeUri.pathSegments.size == 2) {
DocumentsContract.getTreeDocumentId(treeUri)
} else {
DocumentsContract.getDocumentId(treeUri)
}
return DocumentsContract.buildDocumentUriUsingTree(
treeUri,
DocumentsContract.getTreeDocumentId(treeUri),
documentId,
)
}
fun getTreeDocumentPath(documentUri: Uri): String {
val paths = documentUri.pathSegments
if (paths.size >= 4 && PATH_TREE == paths[0] && PATH_DOCUMENT == paths[2]) {
return paths[3]
}
error("Invalid URI: $documentUri")
}
fun buildChildUri(uri: Uri, displayName: String): Uri {
return DocumentsContract.buildDocumentUriUsingTree(
uri,
getTreeDocumentPath(uri) + "/" + displayName,
DocumentsContract.getDocumentId(uri) + "/" + displayName,
)
}
@@ -70,7 +65,7 @@ object DocumentsContractApi21 {
}
}
fun renameTo(context: Context, self: Uri, displayName: String) = runCatching {
DocumentsContract.renameDocument(context.contentResolver, self, displayName)
fun renameTo(self: Uri, displayName: String) = runCatching {
DocumentsContract.renameDocument(resolver, self, displayName)
}.getOrNull()
}
@@ -18,7 +18,7 @@ package com.hippo.unifile
import android.net.Uri
import java.io.IOException
class MediaFile(override val uri: Uri) : UniFile(null) {
class MediaFile(override val uri: Uri) : UniFile() {
override fun createFile(displayName: String) = null
override fun createDirectory(displayName: String) = null
@@ -24,8 +24,7 @@ import eu.kanade.tachiyomi.util.system.logcat
import java.io.File
import java.util.Locale
class RawFile(parent: UniFile?, private var file: File) : UniFile(parent) {
private val parent = parent as? RawFile
class RawFile(private val parent: RawFile?, private var file: File) : UniFile() {
private var cachePresent = false
private val allChildren by lazy {
@@ -17,7 +17,7 @@ package com.hippo.unifile
import android.net.Uri
class SingleDocumentFile(parent: UniFile?, override val uri: Uri) : UniFile(parent) {
class SingleDocumentFile(override val uri: Uri) : UniFile() {
override fun createFile(displayName: String) = null
@@ -18,19 +18,16 @@ package com.hippo.unifile
import android.net.Uri
import android.provider.DocumentsContract.Document.MIME_TYPE_DIR
import android.webkit.MimeTypeMap
import eu.kanade.tachiyomi.util.system.logcat
import splitties.init.appCtx
class TreeDocumentFile(
parent: UniFile?,
private val parent: UniFile?,
override var uri: Uri,
override var name: String = getFilenameForUri(uri),
mimeType: String? = null,
) : UniFile(parent) {
) : UniFile() {
private var cachePresent = false
private val allChildren by lazy {
cachePresent = true
logcat { "Directory lookup cache created for $name" }
DocumentsContractApi21.listFiles(uri).mapTo(mutableListOf()) { (uri, name, mimeType) ->
TreeDocumentFile(this, uri, name, mimeType)
}
@@ -98,7 +95,6 @@ class TreeDocumentFile(
override fun ensureDir(): Boolean {
if (isDirectory) return true
if (isFile) return false
val parent = parentFile
return if (parent != null && parent.ensureDir()) {
parent.createDirectory(name) != null
} else {
@@ -109,7 +105,6 @@ class TreeDocumentFile(
override fun ensureFile(): Boolean {
if (isFile) return true
if (isDirectory) return false
val parent = parentFile
return if (parent != null && parent.ensureDir()) {
parent.createFile(name) != null
} else {
@@ -135,7 +130,7 @@ class TreeDocumentFile(
}
override fun renameTo(displayName: String): Boolean {
val result = DocumentsContractApi21.renameTo(appCtx, uri, displayName)
val result = DocumentsContractApi21.renameTo(uri, displayName)
if (result != null) {
uri = result
name = displayName
@@ -28,7 +28,7 @@ import splitties.init.appCtx
* In Android files can be accessed via [java.io.File] and [android.net.Uri].
* The UniFile is designed to emulate File interface for both File and Uri.
*/
abstract class UniFile internal constructor(private val parent: UniFile?) {
sealed class UniFile {
/**
* Create a new file as a direct child of this directory.
*
@@ -79,21 +79,6 @@ abstract class UniFile internal constructor(private val parent: UniFile?) {
*/
abstract val type: String?
/**
* Return the parent file of this file. Only defined inside of the
* user-selected tree; you can never escape above the top of the tree.
*
*
* The underlying [android.provider.DocumentsProvider] only defines a
* forward mapping from parent to child, so the reverse mapping of child to
* parent offered here is purely a convenience method, and it may be
* incorrect if the underlying tree structure changes.
*
* @return parent of the file, or null if it is the top of the file tree
*/
val parentFile: UniFile?
get() = parent
/**
* Indicates if this file represents a *directory*.
*
@@ -235,7 +220,7 @@ abstract class UniFile internal constructor(private val parent: UniFile?) {
companion object {
fun fromFile(file: File) = RawFile(null, file)
private fun fromSingleUri(singleUri: Uri) = SingleDocumentFile(null, singleUri)
private fun fromSingleUri(singleUri: Uri) = SingleDocumentFile(singleUri)
private fun fromTreeUri(treeUri: Uri) = TreeDocumentFile(null, DocumentsContractApi21.prepareTreeUri(treeUri))
@@ -43,9 +43,7 @@ fun ChapterNavigator(
val layoutDirection = if (isRtl) LayoutDirection.Rtl else LayoutDirection.Ltr
val view = LocalView.current
val configuration = LocalConfiguration.current
// https://m3.material.io/components/sliders/specs
// Tick width is 2 dp
val maxTickCount = configuration.screenWidthDp / 6
val maxTickCount = configuration.screenWidthDp / (SliderDefaults.TickSize.value * 2.5f).roundToInt()
val showTicks = totalPages < maxTickCount
// Match with toolbar background color set in ReaderActivity
+33 -32
View File
@@ -97,10 +97,6 @@
<!-- Gallery detail -->
<string name="download">Descargar</string>
<string name="read">Leer</string>
<plurals name="page_count">
<item quantity="one">%d páginas</item>
<item quantity="other">%d páginas</item>
</plurals>
<string name="favored_times">\u2665 %d</string>
<string name="rating_text">%1$s (%2$.2f, %3$d)</string>
<string name="torrent_count">Torrent (%d)</string>
@@ -336,36 +332,8 @@
<!-- Readable Time -->
<string name="from_the_future">Desde el futuro</string>
<string name="just_now">Ahora</string>
<plurals name="some_minutes_ago">
<item quantity="one">Hace un minuto</item>
<item quantity="other">Hace %d minutos</item>
</plurals>
<plurals name="some_hours_ago">
<item quantity="one">Hace una hora</item>
<item quantity="other">Hace %d horas</item>
</plurals>
<string name="yesterday">Ayer</string>
<string name="some_days_ago">Hace %d días atrás</string>
<plurals name="second">
<item quantity="one">segundo</item>
<item quantity="other">segundos</item>
</plurals>
<plurals name="minute">
<item quantity="one">minuto</item>
<item quantity="other">minutos</item>
</plurals>
<plurals name="hour">
<item quantity="one">hora</item>
<item quantity="other">horas</item>
</plurals>
<plurals name="day">
<item quantity="one">día</item>
<item quantity="other">días</item>
</plurals>
<plurals name="year">
<item quantity="one">año</item>
<item quantity="other">años</item>
</plurals>
<string name="no_archives">Sin archivos</string>
<string name="download_archive_started">Descarga de archivos iniciada</string>
<string name="download_archive_failure">Error al descargar archivo</string>
@@ -399,4 +367,37 @@
<string name="apply_tip">Toca la marca de verificación para guardar los cambios</string>
<string name="dark_theme_follow_system">Sigue el sistema</string>
<plurals name="page_count">
<item quantity="one">%d páginas</item>
<item quantity="other">%d páginas</item>
</plurals>
<plurals name="some_minutes_ago">
<item quantity="one">Hace un minuto</item>
<item quantity="other">Hace %d minutos</item>
</plurals>
<plurals name="some_hours_ago">
<item quantity="one">Hace una hora</item>
<item quantity="other">Hace %d horas</item>
</plurals>
<plurals name="second">
<item quantity="one">segundo</item>
<item quantity="other">segundos</item>
</plurals>
<plurals name="minute">
<item quantity="one">minuto</item>
<item quantity="other">minutos</item>
</plurals>
<plurals name="hour">
<item quantity="one">hora</item>
<item quantity="other">horas</item>
</plurals>
<plurals name="day">
<item quantity="one">día</item>
<item quantity="other">días</item>
</plurals>
<plurals name="year">
<item quantity="one">año</item>
<item quantity="other">años</item>
</plurals>
</resources>
+24 -23
View File
@@ -105,10 +105,6 @@
<!-- Gallery detail -->
<string name="download">Télécharger</string>
<string name="read">Lire</string>
<plurals name="page_count">
<item quantity="one">%d page</item>
<item quantity="other">%d pages</item>
</plurals>
<string name="favored_times">\u2665 %d</string>
<string name="rating_text">%1$s (%2$.2f, %3$d)</string>
<string name="torrent_count">Torrent (%d)</string>
@@ -375,6 +371,30 @@
<!-- Readable Time -->
<string name="from_the_future">Du futur</string>
<string name="just_now">En ce moment</string>
<string name="yesterday">Hier</string>
<string name="some_days_ago">Il y a %d jours</string>
<string name="no_archives">Aucun fiche archives</string>
<string name="download_archive_started">Téléchargement du fiche archives a commencé</string>
<string name="download_archive_failure">Téléchargement du fiche archives a échoué</string>
<string name="download_archive_failure_no_hath">Client H@H est nécessaire pour le téléchargement de fiches archives</string>
<string name="settings_privacy">Confidentialité</string>
<string name="settings_privacy_secure">Empêcher les captures d\'ecran</string>
<string name="settings_privacy_secure_summary">Quand cette option est activée, vous ne pouvez prendre aucune capture d\'écran de l\'application, et le systéme n\'affiche pas le contenu d\'écran dans le sélecteur de tâches.\n\nRedémarrage de l\'application est nécessaire pour appliquer le changement</string>
<string name="settings_advanced_app_language_title">Langue de l\'app (Language)</string>
<string name="app_language_system">Langue du système (défaut)</string>
<string name="settings_eh_identity_cookies_signed">Tout le monde peut utiliser les cookies de ce compte pour s\'y connecter.&lt;br&gt;&lt;b&gt;GARDEZ-LES BIEN !&lt;/b&gt;</string>
<string name="settings_eh_identity_cookies_guest">Vous ne vous êtes pas connecté.</string>
<string name="settings_eh_show_gallery_pages">Montrer les nombres des pages des galeries</string>
<string name="settings_eh_show_gallery_pages_summary">Montrer le nombre des pages des galerie dans la liste des galeries</string>
<string name="dark_theme">Thème sombre</string>
<string name="dark_theme_follow_system">Identique au système</string>
<string name="dark_theme_off">Toujours actif</string>
<string name="dark_theme_on">Toujours inactif</string>
<plurals name="page_count">
<item quantity="one">%d page</item>
<item quantity="other">%d pages</item>
</plurals>
<plurals name="some_minutes_ago">
<item quantity="one">Il y a %d minute</item>
<item quantity="other">Il y a %d minutes</item>
@@ -383,8 +403,6 @@
<item quantity="one">Il y a %d heure</item>
<item quantity="other">Il y a %d heures</item>
</plurals>
<string name="yesterday">Hier</string>
<string name="some_days_ago">Il y a %d jours</string>
<plurals name="second">
<item quantity="one">%d sec</item>
<item quantity="other">secs</item>
@@ -405,22 +423,5 @@
<item quantity="one">%d an</item>
<item quantity="other">ans</item>
</plurals>
<string name="no_archives">Aucun fiche archives</string>
<string name="download_archive_started">Téléchargement du fiche archives a commencé</string>
<string name="download_archive_failure">Téléchargement du fiche archives a échoué</string>
<string name="download_archive_failure_no_hath">Client H@H est nécessaire pour le téléchargement de fiches archives</string>
<string name="settings_privacy">Confidentialité</string>
<string name="settings_privacy_secure">Empêcher les captures d\'ecran</string>
<string name="settings_privacy_secure_summary">Quand cette option est activée, vous ne pouvez prendre aucune capture d\'écran de l\'application, et le systéme n\'affiche pas le contenu d\'écran dans le sélecteur de tâches.\n\nRedémarrage de l\'application est nécessaire pour appliquer le changement</string>
<string name="settings_advanced_app_language_title">Langue de l\'app (Language)</string>
<string name="app_language_system">Langue du système (défaut)</string>
<string name="settings_eh_identity_cookies_signed">Tout le monde peut utiliser les cookies de ce compte pour s\'y connecter.&lt;br&gt;&lt;b&gt;GARDEZ-LES BIEN !&lt;/b&gt;</string>
<string name="settings_eh_identity_cookies_guest">Vous ne vous êtes pas connecté.</string>
<string name="settings_eh_show_gallery_pages">Montrer les nombres des pages des galeries</string>
<string name="settings_eh_show_gallery_pages_summary">Montrer le nombre des pages des galerie dans la liste des galeries</string>
<string name="dark_theme">Thème sombre</string>
<string name="dark_theme_follow_system">Identique au système</string>
<string name="dark_theme_off">Toujours actif</string>
<string name="dark_theme_on">Toujours inactif</string>
</resources>
+25 -25
View File
@@ -105,9 +105,6 @@
<!-- Gallery detail -->
<string name="download">ダウンロード</string>
<string name="read">閲覧</string>
<plurals name="page_count">
<item quantity="other">%dページ</item>
</plurals>
<string name="favored_times">\u2665 %d</string>
<string name="rating_text">%1$s (%2$.2f, %3$d)</string>
<string name="torrent_count">トレント (%d)</string>
@@ -372,30 +369,8 @@
<!-- Readable Time -->
<string name="from_the_future">未来から</string>
<string name="just_now">ただ今</string>
<plurals name="some_minutes_ago">
<item quantity="other">%d分前</item>
</plurals>
<plurals name="some_hours_ago">
<item quantity="other">%d時前</item>
</plurals>
<string name="yesterday">昨日</string>
<string name="some_days_ago">%d日前</string>
<plurals name="second">
<item quantity="other">秒</item>
</plurals>
<plurals name="minute">
<item quantity="other">分</item>
</plurals>
<plurals name="hour">
<item quantity="other">時</item>
</plurals>
<plurals name="day">
<item quantity="other">日</item>
</plurals>
<plurals name="year">
<item quantity="other">年</item>
</plurals>
<string name="no_archives">圧縮パッケージなし</string>
<string name="download_archive_started">圧縮パッケージのダウンロードを開始しました</string>
<string name="download_archive_failure">圧縮パッケージをダウンロードできませんでした</string>
@@ -417,4 +392,29 @@
<string name="settings_eh_show_gallery_pages">ギャラリーページ数を表示</string>
<string name="settings_eh_show_gallery_pages_summary">リストにギャラリーのページの数量を表示します</string>
<string name="dark_theme">暗いテーマ</string>
<plurals name="page_count">
<item quantity="other">%dページ</item>
</plurals>
<plurals name="some_minutes_ago">
<item quantity="other">%d分前</item>
</plurals>
<plurals name="some_hours_ago">
<item quantity="other">%d時前</item>
</plurals>
<plurals name="second">
<item quantity="other">秒</item>
</plurals>
<plurals name="minute">
<item quantity="other">分</item>
</plurals>
<plurals name="hour">
<item quantity="other">時</item>
</plurals>
<plurals name="day">
<item quantity="other">日</item>
</plurals>
<plurals name="year">
<item quantity="other">年</item>
</plurals>
</resources>
+20 -19
View File
@@ -100,9 +100,6 @@
<!-- Gallery detail -->
<string name="download">다운로드</string>
<string name="read">읽기</string>
<plurals name="page_count">
<item quantity="other">%d 페이지</item>
</plurals>
<string name="torrent_count">토렌트 (%d)</string>
<string name="share">공유</string>
<string name="rate">평가</string>
@@ -347,14 +344,32 @@
<!-- Readable Time -->
<string name="from_the_future">미래에</string>
<string name="just_now">방금</string>
<string name="yesterday">어제</string>
<string name="some_days_ago">%d일 전</string>
<string name="no_archives">아카이브 없음</string>
<string name="download_archive_started">아카이브 다운로드가 시작되었습니다.</string>
<string name="download_archive_failure">아카이브 다운로드에 실패했습니다.</string>
<string name="download_archive_failure_no_hath">아카이브를 다운로드하려면 H@H 클라이언트가 필요합니다.</string>
<string name="settings_privacy">개인 정보</string>
<string name="settings_privacy_secure">화면 캡처 허용 안 함</string>
<string name="settings_privacy_secure_summary">이 앱에서 스크린샷을 찍을 수 없게 하며, \"최근 앱\" 화면에서 앱의 내용을 보이지 않게 합니다.</string>
<string name="download_service">다운로드 서비스</string>
<string name="favorite_name">즐겨찾기</string>
<string name="dark_theme">어두운 테마</string>
<string name="dark_theme_follow_system">시스템 설정</string>
<string name="dark_theme_off">항상 꺼짐</string>
<string name="dark_theme_on">항상 켜짐</string>
<plurals name="page_count">
<item quantity="other">%d 페이지</item>
</plurals>
<plurals name="some_minutes_ago">
<item quantity="other">%d분 전</item>
</plurals>
<plurals name="some_hours_ago">
<item quantity="other">%d시간 전</item>
</plurals>
<string name="yesterday">어제</string>
<string name="some_days_ago">%d일 전</string>
<plurals name="second">
<item quantity="other">초</item>
</plurals>
@@ -370,18 +385,4 @@
<plurals name="year">
<item quantity="other">년</item>
</plurals>
<string name="no_archives">아카이브 없음</string>
<string name="download_archive_started">아카이브 다운로드가 시작되었습니다.</string>
<string name="download_archive_failure">아카이브 다운로드에 실패했습니다.</string>
<string name="download_archive_failure_no_hath">아카이브를 다운로드하려면 H@H 클라이언트가 필요합니다.</string>
<string name="settings_privacy">개인 정보</string>
<string name="settings_privacy_secure">화면 캡처 허용 안 함</string>
<string name="settings_privacy_secure_summary">이 앱에서 스크린샷을 찍을 수 없게 하며, \"최근 앱\" 화면에서 앱의 내용을 보이지 않게 합니다.</string>
<string name="download_service">다운로드 서비스</string>
<string name="favorite_name">즐겨찾기</string>
<string name="dark_theme">어두운 테마</string>
<string name="dark_theme_follow_system">시스템 설정</string>
<string name="dark_theme_off">항상 꺼짐</string>
<string name="dark_theme_on">항상 켜짐</string>
</resources>
+12 -11
View File
@@ -53,10 +53,6 @@
<string name="keyword_search">Nøkkelordssøk</string>
<string name="search_sft">Etiketter</string>
<string name="any">Ubegrenset</string>
<plurals name="page_count">
<item quantity="one">%d side</item>
<item quantity="other">%d sider</item>
</plurals>
<string name="more_comment">Flere kommentarer</string>
<string name="tag_vote_up">Stem opp</string>
<string name="pref_grayscale">Gråskala</string>
@@ -186,6 +182,18 @@
<string name="delete_search_history">Slett «%s» fra søkehistorikk\?</string>
<string name="collections">Samlinger</string>
<string name="tag_vote_down">Stem ned</string>
<string name="yesterday">I går</string>
<string name="some_days_ago">%d dager siden</string>
<string name="download_all">Alle</string>
<string name="action_save">Lagre</string>
<string name="settings_privacy">Personvern</string>
<string name="no_archives">Ingen arkiver</string>
<string name="action_save_to">Lagre i …</string>
<plurals name="page_count">
<item quantity="one">%d side</item>
<item quantity="other">%d sider</item>
</plurals>
<plurals name="some_minutes_ago">
<item quantity="one">Et minutt siden</item>
<item quantity="other">%d minutter siden</item>
@@ -198,19 +206,14 @@
<item quantity="one">Én time siden</item>
<item quantity="other">%d timer siden</item>
</plurals>
<string name="yesterday">I går</string>
<string name="some_days_ago">%d dager siden</string>
<plurals name="hour">
<item quantity="one">time</item>
<item quantity="other">timer</item>
</plurals>
<string name="download_all">Alle</string>
<string name="action_save">Lagre</string>
<plurals name="second">
<item quantity="one">sek</item>
<item quantity="other">sek</item>
</plurals>
<string name="settings_privacy">Personvern</string>
<plurals name="day">
<item quantity="one">dag</item>
<item quantity="other">dager</item>
@@ -219,6 +222,4 @@
<item quantity="one">år</item>
<item quantity="other">år</item>
</plurals>
<string name="no_archives">Ingen arkiver</string>
<string name="action_save_to">Lagre i …</string>
</resources>
+16 -15
View File
@@ -100,9 +100,6 @@
<!-- Gallery detail -->
<string name="download">ดาวน์โหลด</string>
<string name="read">อ่าน</string>
<plurals name="page_count">
<item quantity="other">%d หน้า</item>
</plurals>
<string name="favored_times">\u2665 %d</string>
<string name="rating_text">%1$s (%2$.2f, %3$d)</string>
<string name="torrent_count">Torrent (%d)</string>
@@ -362,14 +359,28 @@
<!-- Readable Time -->
<string name="from_the_future">จากอนาคต</string>
<string name="just_now">เดี๋ยวนี้</string>
<string name="yesterday">เมื่อวาน</string>
<string name="some_days_ago">%d วันที่ผ่านมา</string>
<string name="no_archives">ยังไม่มีไฟล์บีบอัด</string>
<string name="download_archive_started">เริ่มการดาวน์โหลดบีบอัด</string>
<string name="download_archive_failure">การดาวน์โหลดไฟล์บีบอัดล้มเหลว</string>
<string name="download_archive_failure_no_hath">ต้องการ H@H client เพื่อโหลดไฟล์บีบอัด</string>
<string name="settings_privacy">ความเป็นส่วนตัว</string>
<string name="settings_privacy_secure">ป้องกันการบันทึกภาพหน้าจอ</string>
<string name="settings_privacy_secure_summary">ป้องกันเนื้อหาต่างๆของตัวแอปจากการถูกบันทึกหน้าจอ หรือถูกแสดงในหน้ารายการ \"แอพล่าสุด\" ตอนสลับหรือระหว่างสลับไปแอปอื่น.</string>
<string name="download_service">บริการดาวน์โหลด</string>
<string name="favorite_name">รายการโปรด</string>
<plurals name="page_count">
<item quantity="other">%d หน้า</item>
</plurals>
<plurals name="some_minutes_ago">
<item quantity="other">%d นาทีที่ผ่านมา</item>
</plurals>
<plurals name="some_hours_ago">
<item quantity="other">%d ชั่วโมงที่ผ่านมา</item>
</plurals>
<string name="yesterday">เมื่อวาน</string>
<string name="some_days_ago">%d วันที่ผ่านมา</string>
<plurals name="second">
<item quantity="other">วิ</item>
</plurals>
@@ -385,14 +396,4 @@
<plurals name="year">
<item quantity="other">ปี</item>
</plurals>
<string name="no_archives">ยังไม่มีไฟล์บีบอัด</string>
<string name="download_archive_started">เริ่มการดาวน์โหลดบีบอัด</string>
<string name="download_archive_failure">การดาวน์โหลดไฟล์บีบอัดล้มเหลว</string>
<string name="download_archive_failure_no_hath">ต้องการ H@H client เพื่อโหลดไฟล์บีบอัด</string>
<string name="settings_privacy">ความเป็นส่วนตัว</string>
<string name="settings_privacy_secure">ป้องกันการบันทึกภาพหน้าจอ</string>
<string name="settings_privacy_secure_summary">ป้องกันเนื้อหาต่างๆของตัวแอปจากการถูกบันทึกหน้าจอ หรือถูกแสดงในหน้ารายการ \"แอพล่าสุด\" ตอนสลับหรือระหว่างสลับไปแอปอื่น.</string>
<string name="download_service">บริการดาวน์โหลด</string>
<string name="favorite_name">รายการโปรด</string>
</resources>
+31 -27
View File
@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2016 Hippo Seven
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,7 +12,8 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
--><resources>
-->
<resources>
<!-- Theme -->
<string name="homepage">主页</string>
<string name="subscription">订阅</string>
@@ -96,9 +96,6 @@
<!-- Gallery detail -->
<string name="download">下载</string>
<string name="read">阅读</string>
<plurals name="page_count">
<item quantity="other">%d 页</item>
</plurals>
<string name="favored_times">\u2665 %d</string>
<string name="rating_text">%1$s (%2$.2f, %3$d)</string>
<string name="torrent_count">种子 (%d)</string>
@@ -400,29 +397,8 @@
<!-- Readable Time -->
<string name="from_the_future">来自未来</string>
<string name="just_now">刚刚</string>
<plurals name="some_minutes_ago">
<item quantity="other">%d 分钟前</item>
</plurals>
<plurals name="some_hours_ago">
<item quantity="other">%d 小时前</item>
</plurals>
<string name="yesterday">昨天</string>
<string name="some_days_ago">%d 天前</string>
<plurals name="second">
<item quantity="other">秒</item>
</plurals>
<plurals name="minute">
<item quantity="other">分钟</item>
</plurals>
<plurals name="hour">
<item quantity="other">小时</item>
</plurals>
<plurals name="day">
<item quantity="other">天</item>
</plurals>
<plurals name="year">
<item quantity="other">年</item>
</plurals>
<string name="no_archives">没有压缩包</string>
<string name="download_archive_started">开始下载压缩包</string>
<string name="download_archive_failure">无法下载压缩包</string>
@@ -448,6 +424,7 @@
<string name="favorite_name">收藏画廊</string>
<string name="black_dark_theme">纯黑深色主题</string>
<string name="harmonize_category_color">类别颜色适配动态配色</string>
<string name="backup_before_update">更新前备份数据</string>
<string name="use_ci_update_channel">使用持续集成(CI)构建频道更新</string>
<string name="sort_by">排序依据</string>
<string name="added_time_desc">添加时间(降序)</string>
@@ -501,6 +478,8 @@
<string name="list_tile_thumb_size">详情模式下缩略图大小</string>
<string name="account_name">账户</string>
<string name="preload_thumb_aggressively">激进地预载缩略图</string>
<string name="animate_items">列表项目动画</string>
<string name="animate_items_summary">如遇崩溃/掉帧请尝试禁用此项</string>
<string name="auto_updates">自动检查更新</string>
<string name="update_frequency_never">从不</string>
<string name="update_frequency_daily">每天</string>
@@ -611,4 +590,29 @@
<string name="dark_theme_on">总是开启</string>
<string name="pref_crop_borders">裁剪边缘</string>
<!-- Tachiyomi Reader End-->
<plurals name="page_count">
<item quantity="other">%d 页</item>
</plurals>
<plurals name="some_minutes_ago">
<item quantity="other">%d 分钟前</item>
</plurals>
<plurals name="some_hours_ago">
<item quantity="other">%d 小时前</item>
</plurals>
<plurals name="second">
<item quantity="other">秒</item>
</plurals>
<plurals name="minute">
<item quantity="other">分钟</item>
</plurals>
<plurals name="hour">
<item quantity="other">小时</item>
</plurals>
<plurals name="day">
<item quantity="other">天</item>
</plurals>
<plurals name="year">
<item quantity="other">年</item>
</plurals>
</resources>
+25 -25
View File
@@ -110,9 +110,6 @@
<!-- Gallery detail -->
<string name="download">下載</string>
<string name="read">閲讀</string>
<plurals name="page_count">
<item quantity="other">%d 頁</item>
</plurals>
<string name="favored_times">\u2665 %d</string>
<string name="rating_text">%1$s (%2$.2f, %3$d)</string>
<string name="torrent_count">種子 (%d)</string>
@@ -397,30 +394,8 @@
<!-- Readable Time -->
<string name="from_the_future">來自未來</string>
<string name="just_now">剛剛</string>
<plurals name="some_minutes_ago">
<item quantity="other">%d 分鐘前</item>
</plurals>
<plurals name="some_hours_ago">
<item quantity="other">%d 小時前</item>
</plurals>
<string name="yesterday">昨天</string>
<string name="some_days_ago">%d 天前</string>
<plurals name="second">
<item quantity="other">秒</item>
</plurals>
<plurals name="minute">
<item quantity="other">分鐘</item>
</plurals>
<plurals name="hour">
<item quantity="other">小時</item>
</plurals>
<plurals name="day">
<item quantity="other">天</item>
</plurals>
<plurals name="year">
<item quantity="other">年</item>
</plurals>
<string name="no_archives">沒有壓縮包</string>
<string name="download_archive_started">開始下載壓縮包</string>
<string name="download_archive_failure">無法下載壓縮包</string>
@@ -517,4 +492,29 @@
<string name="disabled_nav">停用</string>
<!-- Tachiyomi Reader End-->
<plurals name="page_count">
<item quantity="other">%d 頁</item>
</plurals>
<plurals name="some_minutes_ago">
<item quantity="other">%d 分鐘前</item>
</plurals>
<plurals name="some_hours_ago">
<item quantity="other">%d 小時前</item>
</plurals>
<plurals name="second">
<item quantity="other">秒</item>
</plurals>
<plurals name="minute">
<item quantity="other">分鐘</item>
</plurals>
<plurals name="hour">
<item quantity="other">小時</item>
</plurals>
<plurals name="day">
<item quantity="other">天</item>
</plurals>
<plurals name="year">
<item quantity="other">年</item>
</plurals>
</resources>
+30 -29
View File
@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2016 Hippo Seven
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,7 +12,8 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
--><resources>
-->
<resources>
<!-- Theme -->
<string name="homepage">首頁</string>
<string name="subscription">訂閱</string>
@@ -96,9 +96,6 @@
<!-- Gallery detail -->
<string name="download">下載</string>
<string name="read">閱讀</string>
<plurals name="page_count">
<item quantity="other">%d 頁</item>
</plurals>
<string name="favored_times">\u2665 %d</string>
<string name="rating_text">%1$s (%2$.2f, %3$d)</string>
<string name="torrent_count">種子 (%d)</string>
@@ -303,7 +300,7 @@
<string name="settings_eh_show_gallery_pages_summary">在圖庫列表中顯示頁數</string>
<string name="settings_eh_show_gallery_comments">顯示畫廊評論</string>
<string name="settings_eh_show_gallery_comments_summary">在畫廊詳情頁中顯示評論</string>
<string name="settings_eh_show_gallery_comment_threshold">評論分數閥值</string>
<string name="settings_eh_show_gallery_comment_threshold">評論分數閥值</string>
<string name="settings_eh_show_gallery_comment_threshold_summary">隱藏小於或等於設定分數的評論(-101 為停用)</string>
<string name="settings_eh_show_tag_translations">顯示標籤翻譯</string>
<string name="settings_eh_show_tag_translations_summary">顯示翻譯後的標籤而非原始文字(需花費時間來下載資料檔案)</string>
@@ -399,29 +396,8 @@
<!-- Readable Time -->
<string name="from_the_future">來自未來</string>
<string name="just_now">剛剛</string>
<plurals name="some_minutes_ago">
<item quantity="other">%d 分鐘前</item>
</plurals>
<plurals name="some_hours_ago">
<item quantity="other">%d 小時前</item>
</plurals>
<string name="yesterday">昨天</string>
<string name="some_days_ago">%d 天前</string>
<plurals name="second">
<item quantity="other">秒</item>
</plurals>
<plurals name="minute">
<item quantity="other">分鐘</item>
</plurals>
<plurals name="hour">
<item quantity="other">小時</item>
</plurals>
<plurals name="day">
<item quantity="other">天</item>
</plurals>
<plurals name="year">
<item quantity="other">年</item>
</plurals>
<string name="no_archives">沒有壓縮檔</string>
<string name="download_archive_started">開始下載壓縮檔</string>
<string name="download_archive_failure">下載失敗</string>
@@ -507,7 +483,7 @@
<string name="update_frequency_weekly">每週</string>
<string name="update_frequency_biweekly">每兩週</string>
<string name="update_frequency_monthly">每月</string>
<string name="update_failed">更新失败:%s</string>
<string name="update_failed">更新失败:%s</string>
<string name="new_version_available">有新版本可用!</string>
<string name="already_latest_version">已是最新版本</string>
<string name="permission_denied">沒有權限</string>
@@ -610,4 +586,29 @@
<string name="dark_theme_on">總是開啟</string>
<string name="pref_crop_borders">邊緣裁剪</string>
<!-- Tachiyomi Reader End-->
<plurals name="page_count">
<item quantity="other">%d 頁</item>
</plurals>
<plurals name="some_minutes_ago">
<item quantity="other">%d 分鐘前</item>
</plurals>
<plurals name="some_hours_ago">
<item quantity="other">%d 小時前</item>
</plurals>
<plurals name="second">
<item quantity="other">秒</item>
</plurals>
<plurals name="minute">
<item quantity="other">分鐘</item>
</plurals>
<plurals name="hour">
<item quantity="other">小時</item>
</plurals>
<plurals name="day">
<item quantity="other">天</item>
</plurals>
<plurals name="year">
<item quantity="other">年</item>
</plurals>
</resources>
+40 -36
View File
@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2016 Hippo Seven
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,7 +12,8 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
--><resources>
-->
<resources>
<string name="app_name" translatable="false">EhViewer</string>
<string name="site_e" translatable="false">e-hentai</string>
<string name="site_ex" translatable="false">exhentai</string>
@@ -104,7 +104,7 @@
<string name="search_sp_err1">The page range maximum cannot be below 10</string>
<string name="search_sp_err2">The page range must be at least 20</string>
<!-- A placeholder for other languages -->
<string name="search_sp_suffix"/>
<string name="search_sp_suffix" />
<string name="search_sf">Disable default filters for:</string>
<string name="search_sfl">Language</string>
<string name="search_sfu">Uploader</string>
@@ -136,10 +136,6 @@
<!-- Gallery detail -->
<string name="download">Download</string>
<string name="read">Read</string>
<plurals name="page_count">
<item quantity="one">%d page</item>
<item quantity="other">%d pages</item>
</plurals>
<string name="favored_times">\u2665 %d</string>
<string name="rating_text">%1$s (%2$.2f, %3$d)</string>
<string name="torrent_count">Torrent (%d)</string>
@@ -458,36 +454,8 @@
<!-- Readable Time -->
<string name="from_the_future">From the future</string>
<string name="just_now">Just now</string>
<plurals name="some_minutes_ago">
<item quantity="one">A minute ago</item>
<item quantity="other">%d minutes ago</item>
</plurals>
<plurals name="some_hours_ago">
<item quantity="one">An hour ago</item>
<item quantity="other">%d hours ago</item>
</plurals>
<string name="yesterday">Yesterday</string>
<string name="some_days_ago">%d days ago</string>
<plurals name="second">
<item quantity="one">sec</item>
<item quantity="other">secs</item>
</plurals>
<plurals name="minute">
<item quantity="one">min</item>
<item quantity="other">mins</item>
</plurals>
<plurals name="hour">
<item quantity="one">hour</item>
<item quantity="other">hours</item>
</plurals>
<plurals name="day">
<item quantity="one">day</item>
<item quantity="other">days</item>
</plurals>
<plurals name="year">
<item quantity="one">year</item>
<item quantity="other">years</item>
</plurals>
<string name="no_archives">No Archives</string>
<string name="download_archive_started">Archive download started</string>
<string name="download_archive_failure">Failed to download archive</string>
@@ -551,6 +519,7 @@
<string name="app_link_not_verified_message">For Android 12 and newer, you need to manually add link to verified links in order to open E-Hentai links in EhViewer.</string>
<string name="app_link_not_verified_title">App links not verified</string>
<string name="open_by_default">Open by default</string>
<string name="backup_before_update">Backup data before update</string>
<string name="use_ci_update_channel">Use CI update channel</string>
<string name="settings_privacy_require_unlock">Require Unlock</string>
<string name="settings_privacy_require_unlock_delay">Lock Delay</string>
@@ -564,6 +533,8 @@
<string name="list_tile_thumb_size">Thumb size in detail mode</string>
<string name="account_name">Account</string>
<string name="preload_thumb_aggressively">Preload thumbs aggressively</string>
<string name="animate_items">List item animations</string>
<string name="animate_items_summary">Try disabling this if you are facing crashes / frame drops</string>
<string name="user_agent">User Agent</string>
<string name="auto_updates">Automatically check for updates</string>
<string name="update_frequency_never">Never</string>
@@ -674,4 +645,37 @@
<string name="dark_theme_off">Always off</string>
<string name="dark_theme_on">Always on</string>
<!-- Tachiyomi Reader End-->
<plurals name="page_count">
<item quantity="one">%d page</item>
<item quantity="other">%d pages</item>
</plurals>
<plurals name="some_minutes_ago">
<item quantity="one">A minute ago</item>
<item quantity="other">%d minutes ago</item>
</plurals>
<plurals name="some_hours_ago">
<item quantity="one">An hour ago</item>
<item quantity="other">%d hours ago</item>
</plurals>
<plurals name="second">
<item quantity="one">sec</item>
<item quantity="other">secs</item>
</plurals>
<plurals name="minute">
<item quantity="one">min</item>
<item quantity="other">mins</item>
</plurals>
<plurals name="hour">
<item quantity="one">hour</item>
<item quantity="other">hours</item>
</plurals>
<plurals name="day">
<item quantity="one">day</item>
<item quantity="other">days</item>
</plurals>
<plurals name="year">
<item quantity="one">year</item>
<item quantity="other">years</item>
</plurals>
</resources>
+1
View File
@@ -1,6 +1,7 @@
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.android) apply false
alias(libs.plugins.kotlin.compose) apply false
alias(libs.plugins.kotlin.parcelize) apply false
alias(libs.plugins.kotlin.serialization) apply false
alias(libs.plugins.ksp) apply false
+16 -18
View File
@@ -1,34 +1,33 @@
[versions]
aboutlibraries = "11.1.1"
aboutlibraries = "11.1.3"
accompanist = "0.34.0"
agp = "8.5.0-alpha03"
androidx-activity = "1.9.0-rc01"
agp = "8.5.0-alpha06"
androidx-activity = "1.9.0"
androidx-appcompat = "1.7.0-alpha03"
androidx-biometric = "1.2.0-alpha05"
androidx-browser = "1.8.0"
androidx-constraintlayout-compose = "1.1.0-alpha13"
androidx-core = "1.13.0-rc01"
androidx-core-splashscreen = "1.1.0-rc01"
androidx-lifecycle = "2.8.0-alpha04"
androidx-navigation = "2.8.0-SNAPSHOT"
androidx-core = "1.13.0"
androidx-core-splashscreen = "1.2.0-alpha01"
androidx-lifecycle = "2.8.0-beta01"
androidx-navigation = "2.8.0-alpha07"
androidx-paging = "3.3.0-beta01"
androidx-recyclerview = "1.4.0-alpha01"
androidx-room = "2.6.1"
androidx-work = "2.10.0-alpha01"
androidx-work = "2.10.0-alpha02"
arrow = "1.2.4"
coil = "3.0.0-alpha06"
compose-bom = "2024.04.00-alpha01"
compose-compiler = "1.5.11-dev-k2.0.0-Beta5-b5a216d0ac6"
compose-bom = "2024.04.00-alpha02"
compose-destinations = "2.1.0-beta01"
kotlin = "2.0.0-Beta5"
kotlin = "2.0.0-RC1"
kotlinx-coroutines = "1.8.0"
kotlinx-datetime = "0.6.0-RC.2"
kotlinx-serialization = "1.6.3"
ksp = "2.0.0-Beta5-1.0.20"
ksp = "2.0.0-RC1-1.0.20"
ktor = "3.0.0-beta-1"
okio = "3.9.0"
splitties = "3.0.0"
telephoto = "0.9.0"
telephoto = "0.10.0"
xmlutil = "0.86.3"
[libraries]
@@ -66,11 +65,9 @@ coil-gif = { module = "io.coil-kt.coil3:coil-gif" }
coil-network = { module = "io.coil-kt.coil3:coil-network-ktor" }
compose-bom = { module = "dev.chrisbanes.compose:compose-bom", version.ref = "compose-bom" }
# For renovate
compose-compiler = { module = "androidx.compose.compiler:compiler", version.ref = "compose-compiler" }
compose-foundation = { module = "androidx.compose.foundation:foundation", version = "1.7.0-SNAPSHOT" }
compose-foundation = { module = "androidx.compose.foundation:foundation" }
compose-material-icons-extended = { module = "androidx.compose.material:material-icons-extended" }
compose-material3 = { module = "androidx.compose.material3:material3", version = "1.3.0-SNAPSHOT" }
compose-material3 = { module = "androidx.compose.material3:material3" }
compose-material3-windowsizeclass = { module = "androidx.compose.material3:material3-window-size-class" }
compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" }
compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" }
@@ -101,7 +98,7 @@ okio-jvm = { module = "com.squareup.okio:okio-jvm", version.ref = "okio" }
photoview = "com.github.chrisbanes:PhotoView:2.3.0"
reorderable = "sh.calvin.reorderable:reorderable:1.4.0"
reorderable = "sh.calvin.reorderable:reorderable:1.5.1"
serialization-cbor = { module = "org.jetbrains.kotlinx:kotlinx-serialization-cbor", version.ref = "kotlinx-serialization" }
serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
@@ -128,6 +125,7 @@ aboutlibrariesPlugin = { id = "com.mikepenz.aboutlibraries.plugin", version.ref
android-application = { id = "com.android.application", version.ref = "agp" }
composeCompilerReportGenerator = { id = "dev.shreyaspatil.compose-compiler-report-generator", version = "1.2.0" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
+2 -3
View File
@@ -22,13 +22,12 @@
"ignoreUnstable": false
},
{
"groupName": "Kotlin, KSP and Compose Compiler",
"groupName": "Kotlin & KSP",
"matchPackagePrefixes": [
"org.jetbrains.kotlin."
],
"matchPackageNames": [
"com.google.devtools.ksp",
"androidx.compose.compiler:compiler"
"com.google.devtools.ksp"
],
"recreateWhen": "never"
},
+9 -4
View File
@@ -4,6 +4,15 @@ pluginManagement {
google()
mavenCentral()
}
// TODO: Remove on AGP 8.5.0-alpha05
buildscript {
repositories {
maven("https://storage.googleapis.com/r8-releases/raw")
}
dependencies {
classpath("com.android.tools:r8:8.5.4-dev")
}
}
}
dependencyResolutionManagement {
@@ -11,11 +20,7 @@ dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven("https://oss.sonatype.org/content/repositories/snapshots")
maven("https://jitpack.io")
maven("https://androidx.dev/storage/compose-compiler/repository/")
// TODO: Remove on Compose 1.7.0-alpha06 release
maven("https://androidx.dev/snapshots/builds/11644901/artifacts/repository")
}
}