Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e51d54e346 | ||
|
|
e06b1f0177 | ||
|
|
178a870608 | ||
|
|
a7d3d7c651 | ||
|
|
c13024f1c7 | ||
|
|
c53c0ef727 | ||
|
|
cd93fe2646 | ||
|
|
7b506058a4 | ||
|
|
f658f74f35 | ||
|
|
eae5efa69d | ||
|
|
4e6051085e | ||
|
|
f174ccfc04 | ||
|
|
e1252d0554 | ||
|
|
afe638fb90 | ||
|
|
17699ce889 | ||
|
|
4afa06b7b9 | ||
|
|
2d02815aba | ||
|
|
ae1824c6a2 | ||
|
|
d63cc39478 | ||
|
|
51d6775429 | ||
|
|
35d92f1386 | ||
|
|
ba0ef6dccc | ||
|
|
2a325e6700 | ||
|
|
4e7566f06d | ||
|
|
d95bf4b71d | ||
|
|
43ddf6ccc4 | ||
|
|
b55da640e1 | ||
|
|
90534c3c9f | ||
|
|
88c9e5e902 | ||
|
|
ac41117a78 | ||
|
|
2418c5f625 | ||
|
|
52156aa9ff | ||
|
|
6010fb69a4 | ||
|
|
d5abbb3584 | ||
|
|
8c27504ee2 | ||
|
|
8a20525da3 | ||
|
|
c7b8326b52 | ||
|
|
ee8b80651e | ||
|
|
589676c52a | ||
|
|
83085d5f9a | ||
|
|
a1a204d0d5 | ||
|
|
9a43c36758 | ||
|
|
e017efe35f | ||
|
|
1645c17ab7 | ||
|
|
075b800932 | ||
|
|
56f9138db4 | ||
|
|
20c1b7acdb | ||
|
|
a10022d797 | ||
|
|
aafbbbec38 | ||
|
|
028a994070 | ||
|
|
581125db62 | ||
|
|
295770002a | ||
|
|
1a66d84bd0 | ||
|
|
cd3f51b66d | ||
|
|
6d8ac4c3da | ||
|
|
156382eaad | ||
|
|
2c155fe599 | ||
|
|
323de69ba0 | ||
|
|
0cdc6be34f | ||
|
|
36c1703763 | ||
|
|
ee3d2f681c | ||
|
|
11ce900868 | ||
|
|
b936dd2533 | ||
|
|
fab8cd0c1f | ||
|
|
ec3a962ec8 | ||
|
|
348813109b | ||
|
|
59598094df | ||
|
|
8b6ae59eda | ||
|
|
b3d8653882 | ||
|
|
719d1ebbb8 | ||
|
|
a23f902e9a | ||
|
|
79559635e1 | ||
|
|
c57a4e4132 | ||
|
|
c6f7d9d869 | ||
|
|
9fcddd06e4 | ||
|
|
3526a2b0e9 | ||
|
|
56187aa4dc |
No files matched your search
@@ -0,0 +1 @@
|
||||
blank_issues_enabled: false
|
||||
@@ -25,16 +25,16 @@ jobs:
|
||||
sudo udevadm trigger --name-match=kvm
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 21
|
||||
|
||||
- name: Install Rust Toolchain
|
||||
run: rustup target add armv7-linux-androideabi aarch64-linux-android x86_64-linux-android
|
||||
run: rustup target add armv7-linux-androideabi
|
||||
|
||||
- name: Rust Cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
|
||||
+22
-11
@@ -12,16 +12,16 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 21
|
||||
|
||||
- name: Install Rust Toolchain
|
||||
run: rustup target add armv7-linux-androideabi aarch64-linux-android x86_64-linux-android
|
||||
run: rustup target add armv7-linux-androideabi
|
||||
|
||||
- name: Rust Cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
@@ -42,6 +42,9 @@ jobs:
|
||||
with:
|
||||
cache-read-only: true
|
||||
|
||||
- name: Check build-logic
|
||||
run: ./gradlew :build-logic:convention:check
|
||||
|
||||
- name: Spotless Check
|
||||
run: ./gradlew spotlessCheck
|
||||
|
||||
@@ -52,16 +55,16 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 21
|
||||
|
||||
- name: Install Rust Toolchain
|
||||
run: rustup target add armv7-linux-androideabi aarch64-linux-android x86_64-linux-android
|
||||
run: rustup target add armv7-linux-androideabi
|
||||
|
||||
- name: Rust Cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
@@ -79,9 +82,13 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: app/.cxx
|
||||
key: cmake-default-${{ hashFiles('settings.gradle.kts', 'app/src/main/cpp/CMakeLists.txt', 'app/src/main/rust/Cargo.lock') }}
|
||||
key: cmake-default-${{ hashFiles('rust-toolchain.toml', 'settings.gradle.kts', 'app/src/main/cpp/CMakeLists.txt', 'app/src/main/rust/Cargo.lock') }}
|
||||
restore-keys: cmake-default-
|
||||
|
||||
# https://github.com/mikepenz/AboutLibraries/issues/1225
|
||||
- name: Generate aboutlibraries.json
|
||||
run: ./gradlew :app:prepareLibraryDefinitionsDefaultRelease --rerun
|
||||
|
||||
- name: Build
|
||||
run: ./gradlew assembleDefaultRelease
|
||||
|
||||
@@ -120,16 +127,16 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 21
|
||||
|
||||
- name: Install Rust Toolchain
|
||||
run: rustup target add armv7-linux-androideabi aarch64-linux-android x86_64-linux-android
|
||||
run: rustup target add armv7-linux-androideabi
|
||||
|
||||
- name: Rust Cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
@@ -147,9 +154,13 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: app/.cxx
|
||||
key: cmake-marshmallow-${{ hashFiles('settings.gradle.kts', 'app/src/main/cpp/CMakeLists.txt', 'app/src/main/rust/Cargo.lock') }}
|
||||
key: cmake-marshmallow-${{ hashFiles('rust-toolchain.toml', 'settings.gradle.kts', 'app/src/main/cpp/CMakeLists.txt', 'app/src/main/rust/Cargo.lock') }}
|
||||
restore-keys: cmake-marshmallow-
|
||||
|
||||
# https://github.com/mikepenz/AboutLibraries/issues/1225
|
||||
- name: Generate aboutlibraries.json
|
||||
run: ./gradlew :app:prepareLibraryDefinitionsMarshmallowRelease --rerun
|
||||
|
||||
- name: Build
|
||||
run: ./gradlew assembleMarshmallowRelease
|
||||
|
||||
|
||||
@@ -19,16 +19,16 @@ jobs:
|
||||
sudo udevadm trigger --name-match=kvm
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 21
|
||||
|
||||
- name: Install Rust Toolchain
|
||||
run: rustup target add armv7-linux-androideabi aarch64-linux-android x86_64-linux-android
|
||||
run: rustup target add armv7-linux-androideabi
|
||||
|
||||
- name: Rust Cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
@@ -51,6 +51,12 @@ jobs:
|
||||
run: ./gradlew generateBaselineProfile
|
||||
-Pandroid.testoptions.manageddevices.emulator.gpu="swiftshader_indirect"
|
||||
|
||||
# https://github.com/mikepenz/AboutLibraries/issues/1225
|
||||
- name: Generate aboutlibraries.json
|
||||
run: ./gradlew
|
||||
:app:prepareLibraryDefinitionsDefaultRelease --rerun
|
||||
:app:prepareLibraryDefinitionsMarshmallowRelease --rerun
|
||||
|
||||
- name: Build
|
||||
run: ./gradlew assembleRelease
|
||||
|
||||
|
||||
+3
-2
@@ -1,8 +1,9 @@
|
||||
.gradle
|
||||
/local.properties
|
||||
.DS_Store
|
||||
/build
|
||||
/.kotlin
|
||||
build
|
||||
.cxx
|
||||
.kotlin
|
||||
*.iml
|
||||
.idea
|
||||
.vscode
|
||||
|
||||
@@ -1,5 +1,37 @@
|
||||
# 变更日志
|
||||
|
||||
## [1.14.3] - 2025-09-01
|
||||
|
||||
### 新功能
|
||||
|
||||
* 支持以 label: 语法搜索下载标签
|
||||
|
||||
### Bug 修复
|
||||
|
||||
* [阅读器] 关闭滤镜设置时崩溃 [#2573](https://github.com/FooIbar/EhViewer/issues/2573)
|
||||
* 某些情况下搜索相似画廊时出错
|
||||
|
||||
|
||||
## [1.14.2] - 2025-08-16
|
||||
|
||||
### 改进
|
||||
|
||||
* [下载] 支持将最低下载速度设为 0 [#2545](https://github.com/FooIbar/EhViewer/issues/2545)
|
||||
* [下载] 移除令人困惑的连接超时设置 [#2556](https://github.com/FooIbar/EhViewer/issues/2556)
|
||||
|
||||
### Bug 修复
|
||||
|
||||
* 下载速度计算不准确导致下载失败误报 [#2545](https://github.com/FooIbar/EhViewer/issues/2545)
|
||||
* 无法解析带有 location: 标签的画廊
|
||||
|
||||
|
||||
## [1.14.1] - 2025-07-31
|
||||
|
||||
### 改进
|
||||
|
||||
* 更新 GitHub 访问令牌
|
||||
|
||||
|
||||
## [1.14.0] - 2025-07-23
|
||||
|
||||
### 重大变更
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
/build
|
||||
/.cxx
|
||||
+16
-55
@@ -1,20 +1,15 @@
|
||||
import com.mikepenz.aboutlibraries.plugin.DuplicateMode
|
||||
import com.mikepenz.aboutlibraries.plugin.DuplicateRule
|
||||
import java.util.regex.Pattern
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmDefaultMode
|
||||
|
||||
val isRelease: Boolean
|
||||
get() = gradle.startParameter.taskNames.any { it.contains("Release") }
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
alias(libs.plugins.kotlin.compose)
|
||||
alias(libs.plugins.ehviewer.android.application)
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
alias(libs.plugins.ksp)
|
||||
alias(libs.plugins.spotless)
|
||||
alias(libs.plugins.aboutlibrariesPlugin)
|
||||
alias(libs.plugins.composeCompilerReportGenerator)
|
||||
alias(libs.plugins.baselineprofile)
|
||||
}
|
||||
|
||||
@@ -61,7 +56,7 @@ android {
|
||||
defaultConfig {
|
||||
applicationId = "moe.tarsin.ehviewer"
|
||||
versionCode = 180063
|
||||
versionName = "1.14.0"
|
||||
versionName = "1.14.3"
|
||||
// versionNameSuffix = "-SNAPSHOT"
|
||||
buildConfigField("String", "RAW_VERSION_NAME", "\"$versionName${versionNameSuffix.orEmpty()}\"")
|
||||
buildConfigField("String", "COMMIT_SHA", "\"$commitSha\"")
|
||||
@@ -79,9 +74,10 @@ android {
|
||||
flavorDimensions += "api"
|
||||
|
||||
productFlavors {
|
||||
create("default")
|
||||
create("default") {
|
||||
minSdk = 26
|
||||
}
|
||||
create("marshmallow") {
|
||||
minSdk = 23
|
||||
applicationIdSuffix = ".m"
|
||||
versionNameSuffix = "-M"
|
||||
}
|
||||
@@ -97,16 +93,13 @@ android {
|
||||
isCoreLibraryDesugaringEnabled = true
|
||||
}
|
||||
|
||||
lint {
|
||||
checkReleaseBuilds = false
|
||||
disable += setOf("MissingTranslation", "MissingQuantity")
|
||||
error += setOf("InlinedApi")
|
||||
}
|
||||
|
||||
packaging {
|
||||
dex {
|
||||
useLegacyPackaging = false
|
||||
}
|
||||
jniLibs {
|
||||
excludes += "**/libdatastore_shared_counter.so" // DataStore multi-process
|
||||
}
|
||||
resources {
|
||||
// Required by Layout Inspector
|
||||
pickFirsts += "/META-INF/androidx.compose.ui_ui.version"
|
||||
@@ -176,6 +169,10 @@ baselineProfile {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.core.data)
|
||||
implementation(projects.core.i18n)
|
||||
implementation(projects.core.ui)
|
||||
|
||||
// https://developer.android.com/jetpack/androidx/releases/activity
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(libs.androidx.appcompat)
|
||||
@@ -189,7 +186,10 @@ dependencies {
|
||||
implementation(libs.compose.destinations.core)
|
||||
ksp(libs.compose.destinations.compiler)
|
||||
|
||||
implementation(libs.compose.preference)
|
||||
implementation(libs.compose.preference) {
|
||||
// R8 won't remove it because it adds a content provider
|
||||
exclude(group = "org.jetbrains.compose.components", module = "components-resources")
|
||||
}
|
||||
|
||||
implementation(libs.androidx.core)
|
||||
implementation(libs.androidx.core.splashscreen)
|
||||
@@ -254,37 +254,11 @@ dependencies {
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(21)
|
||||
|
||||
// https://kotlinlang.org/docs/gradle-compiler-options.html#all-compiler-options
|
||||
compilerOptions {
|
||||
jvmDefault = JvmDefaultMode.NO_COMPATIBILITY
|
||||
progressiveMode = true
|
||||
optIn.addAll(
|
||||
"coil3.annotation.ExperimentalCoilApi",
|
||||
"androidx.compose.foundation.layout.ExperimentalLayoutApi",
|
||||
"androidx.compose.material3.ExperimentalMaterial3Api",
|
||||
"androidx.compose.material3.ExperimentalMaterial3ExpressiveApi",
|
||||
"androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi",
|
||||
"androidx.compose.ui.ExperimentalComposeUiApi",
|
||||
"androidx.compose.foundation.ExperimentalFoundationApi",
|
||||
"androidx.compose.animation.ExperimentalAnimationApi",
|
||||
"androidx.compose.animation.ExperimentalSharedTransitionApi",
|
||||
"androidx.compose.runtime.ExperimentalComposeRuntimeApi",
|
||||
"androidx.paging.ExperimentalPagingApi",
|
||||
"kotlin.ExperimentalStdlibApi",
|
||||
"kotlin.concurrent.atomics.ExperimentalAtomicApi",
|
||||
"kotlin.contracts.ExperimentalContracts",
|
||||
"kotlin.time.ExperimentalTime",
|
||||
"kotlinx.coroutines.ExperimentalCoroutinesApi",
|
||||
"kotlinx.coroutines.FlowPreview",
|
||||
"kotlinx.serialization.ExperimentalSerializationApi",
|
||||
"splitties.experimental.ExperimentalSplittiesApi",
|
||||
"splitties.preferences.DataStorePreferencesPreview",
|
||||
)
|
||||
freeCompilerArgs.addAll(
|
||||
"-Xcontext-parameters",
|
||||
"-Xannotation-default-target=param-property",
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -304,16 +278,3 @@ aboutLibraries {
|
||||
duplicationRule = DuplicateRule.GROUP
|
||||
}
|
||||
}
|
||||
|
||||
val ktlintVersion = libs.ktlint.get().version
|
||||
|
||||
spotless {
|
||||
kotlin {
|
||||
// https://github.com/diffplug/spotless/issues/111
|
||||
target("src/**/*.kt")
|
||||
ktlint(ktlintVersion)
|
||||
}
|
||||
kotlinGradle {
|
||||
ktlint(ktlintVersion)
|
||||
}
|
||||
}
|
||||
@@ -129,6 +129,8 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Android")
|
||||
set(ANDROID 1)
|
||||
endif ()
|
||||
|
||||
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ../../../../rust-toolchain.toml)
|
||||
unset(Rust_TOOLCHAIN CACHE)
|
||||
FetchContent_Declare(
|
||||
Corrosion
|
||||
GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git
|
||||
|
||||
@@ -37,6 +37,10 @@ import coil3.request.allowRgb565
|
||||
import coil3.request.crossfade
|
||||
import coil3.serviceLoaderEnabled
|
||||
import coil3.util.DebugLogger
|
||||
import com.ehviewer.core.ui.util.initSETConnection
|
||||
import com.ehviewer.core.util.launchIO
|
||||
import com.ehviewer.core.util.launchUI
|
||||
import com.ehviewer.core.util.withUIContext
|
||||
import com.hippo.ehviewer.client.EhTagDatabase
|
||||
import com.hippo.ehviewer.coil.AnimatedWebPDecoder
|
||||
import com.hippo.ehviewer.coil.CropBorderInterceptor
|
||||
@@ -59,7 +63,6 @@ import com.hippo.ehviewer.ui.keepNoMediaFileStatus
|
||||
import com.hippo.ehviewer.ui.lockObserver
|
||||
import com.hippo.ehviewer.ui.screen.detailCache
|
||||
import com.hippo.ehviewer.ui.tools.dataStateFlow
|
||||
import com.hippo.ehviewer.ui.tools.initSETConnection
|
||||
import com.hippo.ehviewer.util.AppConfig
|
||||
import com.hippo.ehviewer.util.CrashHandler
|
||||
import com.hippo.ehviewer.util.FavouriteStatusRouter
|
||||
@@ -70,9 +73,6 @@ import com.hippo.ehviewer.util.isAtLeastP
|
||||
import com.hippo.ehviewer.util.isAtLeastS
|
||||
import com.hippo.ehviewer.util.isAtLeastSExtension7
|
||||
import com.hippo.files.deleteContent
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
import eu.kanade.tachiyomi.util.lang.launchUI
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.engine.okhttp.OkHttp
|
||||
@@ -130,7 +130,7 @@ class EhApplication :
|
||||
launchIO { dataStateFlow.value }
|
||||
launchIO { OSUtils.totalMemory }
|
||||
launch {
|
||||
if (DownloadManager.labelList.isNotEmpty() && Settings.downloadFilterMode.key !in Settings.prefs) {
|
||||
if (DownloadManager.labelList.isNotEmpty() && Settings.downloadFilterMode !in Settings.snapshot()) {
|
||||
Settings.downloadFilterMode.value = DownloadsFilterMode.CUSTOM.flag
|
||||
}
|
||||
DownloadManager.readMetadataFromLocal()
|
||||
|
||||
@@ -146,6 +146,8 @@ object EhDB {
|
||||
dao.fill(raw.position)
|
||||
}
|
||||
|
||||
suspend fun searchDownloadLabel(keyword: String, limit: Int) = db.downloadLabelDao().search("%$keyword%", limit)
|
||||
|
||||
suspend fun putDownloadArtist(gid: Long, artists: List<DownloadArtist>) {
|
||||
if (artists.isNotEmpty()) {
|
||||
val dao = db.downloadArtistDao()
|
||||
|
||||
@@ -9,6 +9,9 @@ import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.remember
|
||||
import com.ehviewer.core.preferences.DataStorePreferences
|
||||
import com.ehviewer.core.preferences.PrefDelegate
|
||||
import com.ehviewer.core.preferences.edit
|
||||
import com.hippo.ehviewer.client.EhCookieStore
|
||||
import com.hippo.ehviewer.client.data.FavListUrlBuilder
|
||||
import com.hippo.ehviewer.download.DownloadsFilterMode
|
||||
@@ -25,47 +28,12 @@ import kotlinx.coroutines.flow.conflate
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import kotlinx.coroutines.flow.flatMapMerge
|
||||
import kotlinx.coroutines.flow.map
|
||||
import splitties.preferences.BoolPref
|
||||
import splitties.preferences.DataStorePreferences
|
||||
import splitties.preferences.FloatPref
|
||||
import splitties.preferences.IntPref
|
||||
import splitties.preferences.LongPref
|
||||
import splitties.preferences.PrefDelegate
|
||||
import splitties.preferences.StringOrNullPref
|
||||
import splitties.preferences.StringPref
|
||||
import splitties.preferences.StringSetOrNullPref
|
||||
import splitties.preferences.StringSetPref
|
||||
import splitties.preferences.edit
|
||||
|
||||
@Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
|
||||
fun <T> PrefDelegate<T>.getValue(): T = when (this) {
|
||||
is BoolPref -> value
|
||||
is IntPref -> value
|
||||
is FloatPref -> value
|
||||
is LongPref -> value
|
||||
is StringPref -> value
|
||||
is StringOrNullPref -> value
|
||||
is StringSetPref -> value
|
||||
is StringSetOrNullPref -> value
|
||||
} as T
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <T> PrefDelegate<T>.setValue(newValue: T) = when (this) {
|
||||
is BoolPref -> value = newValue as Boolean
|
||||
is IntPref -> value = newValue as Int
|
||||
is FloatPref -> value = newValue as Float
|
||||
is LongPref -> value = newValue as Long
|
||||
is StringPref -> value = newValue as String
|
||||
is StringOrNullPref -> value = newValue as? String
|
||||
is StringSetPref -> value = newValue as Set<String?>
|
||||
is StringSetOrNullPref -> value = newValue as? Set<String?>
|
||||
}
|
||||
|
||||
@Stable
|
||||
@Composable
|
||||
inline fun <R, T> PrefDelegate<T>.collectAsState(crossinline transform: @DisallowComposableCalls (T) -> R): State<R> {
|
||||
val flow = remember { valueFlow().map { transform(it) } }
|
||||
val init = getValue()
|
||||
val init = value
|
||||
return flow.collectAsState(transform(init))
|
||||
}
|
||||
|
||||
@@ -73,32 +41,28 @@ inline fun <R, T> PrefDelegate<T>.collectAsState(crossinline transform: @Disallo
|
||||
@Composable
|
||||
fun <T> PrefDelegate<T>.collectAsState(): State<T> {
|
||||
val flow = remember { valueFlow() }
|
||||
val init = getValue()
|
||||
val init = value
|
||||
return flow.collectAsState(init)
|
||||
}
|
||||
|
||||
@Stable
|
||||
@Composable
|
||||
fun <T> PrefDelegate<T>.asMutableState(): MutableState<T> {
|
||||
val flow = remember { valueFlow() }
|
||||
val init = getValue()
|
||||
val readOnly = flow.collectAsState(init)
|
||||
val readOnly = collectAsState()
|
||||
return remember {
|
||||
object : MutableState<T> {
|
||||
override var value: T
|
||||
get() = readOnly.value
|
||||
set(value) {
|
||||
setValue(value)
|
||||
this@asMutableState.value = value
|
||||
}
|
||||
override fun component1() = readOnly.value
|
||||
override fun component2() = ::setValue
|
||||
override fun component2() = this@asMutableState::value::set
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object Settings : DataStorePreferences(null) {
|
||||
private const val KEY_SHOW_TAG_TRANSLATIONS = "show_tag_translations"
|
||||
|
||||
@Suppress("ktlint:standard:backing-property-naming")
|
||||
private val _favFlow = MutableSharedFlow<Unit>()
|
||||
val favChangesFlow = _favFlow.debounce(1000)
|
||||
@@ -107,9 +71,9 @@ object Settings : DataStorePreferences(null) {
|
||||
var favCloudCount by intPref("fav_cloud", 0).emitTo(_favFlow)
|
||||
|
||||
// Eh
|
||||
val gallerySite = intPref("gallery_site_2", 0).observed { updateWhenGallerySiteChanges() }
|
||||
val gallerySite = intPref("gallery_site_2", 0).observed(::updateWhenGallerySiteChanges)
|
||||
val defaultFavSlot = intPref("default_favorite_slot", -2)
|
||||
val theme = intPref("theme_2", -1).observed { updateWhenThemeChanges() }
|
||||
val theme = intPref("theme_2", -1).observed(::updateWhenThemeChanges)
|
||||
val blackDarkTheme = boolPref("black_dark_theme", false)
|
||||
val harmonizeCategoryColor = boolPref("harmonize_category_color", true)
|
||||
val launchPage = intPref("launch_page_2", 0)
|
||||
@@ -121,17 +85,16 @@ object Settings : DataStorePreferences(null) {
|
||||
val showVoteStatus = boolPref("show_vote_status", false)
|
||||
val showComments = boolPref("show_gallery_comments", true)
|
||||
val commentThreshold = intPref("comment_threshold", -100)
|
||||
val showTagTranslations = boolPref(KEY_SHOW_TAG_TRANSLATIONS, false).observed { updateWhenTagTranslationChanges() }
|
||||
val showTagTranslations = boolPref("show_tag_translations", false).observed(::updateWhenTagTranslationChanges)
|
||||
val meteredNetworkWarning = boolPref("cellular_network_warning", false)
|
||||
val showJpnTitle = boolPref("show_jpn_title", false)
|
||||
val requestNews = boolPref("request_news", false).observed { updateWhenRequestNewsChanges() }
|
||||
val hideHvEvents = boolPref("hide_hv_events", false)
|
||||
|
||||
// Download
|
||||
val mediaScan = boolPref("media_scan", false).observed { updateWhenKeepMediaStatusChanges() }
|
||||
val mediaScan = boolPref("media_scan", false).observed(::updateWhenKeepMediaStatusChanges)
|
||||
val multiThreadDownload = intPref("download_thread_2", 3)
|
||||
val downloadDelay = intPref("download_delay_3", 1000)
|
||||
val connTimeout = intPref("conn_timeout", 10)
|
||||
val timeoutSpeed = intPref("timeout_speed_level", 6)
|
||||
val preloadImage = intPref("preload_image_2", 5)
|
||||
val downloadOriginImage = boolPref("download_origin_image", false)
|
||||
@@ -167,15 +130,15 @@ object Settings : DataStorePreferences(null) {
|
||||
val needSignIn = boolPref("need_sign_in", true)
|
||||
val gridView = boolPref("grid_view", false)
|
||||
val qSSaveProgress = boolPref("qs_save_progress", true)
|
||||
val displayName = stringOrNullPref("display_name", null)
|
||||
val avatar = stringOrNullPref("avatar", null)
|
||||
val recentDownloadLabel = stringOrNullPref("recent_download_label", null)
|
||||
val displayName = stringOrNullPref("display_name")
|
||||
val avatar = stringOrNullPref("avatar")
|
||||
val recentDownloadLabel = stringOrNullPref("recent_download_label")
|
||||
|
||||
var downloadScheme by stringOrNullPref("image_scheme", null)
|
||||
var downloadAuthority by stringOrNullPref("image_authority", null)
|
||||
var downloadPath by stringOrNullPref("image_path", null)
|
||||
var downloadQuery by stringOrNullPref("image_query", null)
|
||||
var downloadFragment by stringOrNullPref("image_fragment", null)
|
||||
var downloadScheme by stringOrNullPref("image_scheme")
|
||||
var downloadAuthority by stringOrNullPref("image_authority")
|
||||
var downloadPath by stringOrNullPref("image_path")
|
||||
var downloadQuery by stringOrNullPref("image_query")
|
||||
var downloadFragment by stringOrNullPref("image_fragment")
|
||||
var archivePasswds by stringSetPref("archive_passwds")
|
||||
var appLinkVerifyTip by boolPref("app_link_verify_tip", false)
|
||||
var hasDefaultDownloadLabel by boolPref("has_default_download_label", false)
|
||||
@@ -185,7 +148,7 @@ object Settings : DataStorePreferences(null) {
|
||||
var requestNewsTime by intPref("request_news_time", 0).observed { updateWhenRequestNewsChanges() }
|
||||
var lastDawnDays by intPref("last_dawn_days", 0)
|
||||
var recentToplist by stringPref("recent_toplist", "11")
|
||||
var defaultDownloadLabel by stringOrNullPref("default_download_label", null)
|
||||
var defaultDownloadLabel by stringOrNullPref("default_download_label")
|
||||
var lastUpdateTime by longPref("last_update_time", BuildConfig.COMMIT_TIME)
|
||||
|
||||
// Reader
|
||||
@@ -223,43 +186,39 @@ object Settings : DataStorePreferences(null) {
|
||||
val stripExtraneousAds = boolPref("strip_extraneous_ads", false)
|
||||
|
||||
init {
|
||||
if ("CN" == Locale.getDefault().country) {
|
||||
edit {
|
||||
if (KEY_SHOW_TAG_TRANSLATIONS !in prefs) showTagTranslations.value = true
|
||||
edit { pref ->
|
||||
if ("CN" == Locale.getDefault().country) {
|
||||
if (showTagTranslations !in pref) pref[showTagTranslations] = true
|
||||
}
|
||||
if (pref[downloadFilterMode] == DownloadsFilterMode.ARTIST.flag && pref[recentDownloadLabel] == null) {
|
||||
pref[recentDownloadLabel] = ""
|
||||
}
|
||||
}
|
||||
if (downloadFilterMode.value == DownloadsFilterMode.ARTIST.flag && recentDownloadLabel.value == null) {
|
||||
recentDownloadLabel.value = ""
|
||||
}
|
||||
}
|
||||
|
||||
interface Delegate<R> {
|
||||
val flowGetter: () -> Flow<Unit>
|
||||
fun changesFlow(): Flow<Unit>
|
||||
operator fun getValue(thisRef: Any?, prop: KProperty<*>?): R
|
||||
operator fun setValue(thisRef: Any?, prop: KProperty<*>?, value: R)
|
||||
}
|
||||
|
||||
private fun intArrayPref(key: String, count: Int) = object : Delegate<IntArray> {
|
||||
override val flowGetter: () -> Flow<Unit> = { _value.asFlow().flatMapMerge { it.changesFlow() }.conflate() }
|
||||
|
||||
@Suppress("ktlint:standard:backing-property-naming")
|
||||
private var _value = (0 until count).map { intPref("${key}_$it", 0) }.toTypedArray()
|
||||
override fun getValue(thisRef: Any?, prop: KProperty<*>?): IntArray = _value.map { it.value }.toIntArray()
|
||||
private val delegates = Array(count) { intPref("${key}_$it", 0) }
|
||||
override fun changesFlow(): Flow<Unit> = delegates.asFlow().flatMapMerge { it.changesFlow() }.conflate()
|
||||
override fun getValue(thisRef: Any?, prop: KProperty<*>?) = IntArray(delegates.size) { delegates[it].value }
|
||||
override fun setValue(thisRef: Any?, prop: KProperty<*>?, value: IntArray) {
|
||||
check(value.size == count)
|
||||
edit { value.zip(_value) { v, d -> d.value = v } }
|
||||
edit { pref -> value.zip(delegates) { v, d -> pref[d] = v } }
|
||||
}
|
||||
}
|
||||
|
||||
private fun stringArrayPref(key: String, count: Int, defMetaValue: String) = object : Delegate<Array<String>> {
|
||||
override val flowGetter: () -> Flow<Unit> = { _value.asFlow().flatMapMerge { it.changesFlow() }.conflate() }
|
||||
|
||||
@Suppress("ktlint:standard:backing-property-naming")
|
||||
private var _value = (0 until count).map { stringPref("${key}_$it", "$defMetaValue $it") }.toTypedArray()
|
||||
override fun getValue(thisRef: Any?, prop: KProperty<*>?): Array<String> = _value.map { it.value }.toTypedArray()
|
||||
private val delegates = Array(count) { stringPref("${key}_$it", "$defMetaValue $it") }
|
||||
override fun changesFlow(): Flow<Unit> = delegates.asFlow().flatMapMerge { it.changesFlow() }.conflate()
|
||||
override fun getValue(thisRef: Any?, prop: KProperty<*>?) = Array(delegates.size) { delegates[it].value }
|
||||
override fun setValue(thisRef: Any?, prop: KProperty<*>?, value: Array<String>) {
|
||||
check(value.size == count)
|
||||
edit { value.zip(_value) { v, d -> d.value = v } }
|
||||
edit { pref -> value.zip(delegates) { v, d -> pref[d] = v } }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,68 +2,73 @@ package com.hippo.ehviewer
|
||||
|
||||
import android.app.UiModeManager
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import arrow.core.Either.Companion.catch
|
||||
import com.ehviewer.core.preferences.PrefDelegate
|
||||
import com.ehviewer.core.util.withIOContext
|
||||
import com.hippo.ehviewer.client.EhEngine
|
||||
import com.hippo.ehviewer.client.EhTagDatabase
|
||||
import com.hippo.ehviewer.dailycheck.updateDailyCheckWork
|
||||
import com.hippo.ehviewer.ui.keepNoMediaFileStatus
|
||||
import com.hippo.ehviewer.util.isAtLeastS
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.drop
|
||||
import kotlinx.coroutines.flow.emitAll
|
||||
import kotlinx.coroutines.launch
|
||||
import splitties.init.appCtx
|
||||
import splitties.preferences.PrefDelegate
|
||||
import splitties.systemservices.uiModeManager
|
||||
|
||||
private val collectScope = CoroutineScope(Dispatchers.Main + SupervisorJob())
|
||||
fun <T, R : PrefDelegate<T>> R.observed(func: (Unit) -> Unit) = apply { collectScope.launch { changesFlow().collect(func) } }
|
||||
fun <T, R : Settings.Delegate<T>> R.observed(func: (Unit) -> Unit) = apply { collectScope.launch { flowGetter().collect(func) } }
|
||||
fun <T, R : Settings.Delegate<T>> R.emitTo(flow: MutableSharedFlow<Unit>) = apply { collectScope.launch { flowGetter().collect { flow.emit(Unit) } } }
|
||||
fun <T, R : PrefDelegate<T>> R.emitTo(flow: MutableSharedFlow<Unit>) = apply { collectScope.launch { changesFlow().collect { flow.emit(Unit) } } }
|
||||
private const val TAG = "SettingsCollector"
|
||||
|
||||
fun updateWhenKeepMediaStatusChanges() {
|
||||
collectScope.launchIO {
|
||||
runCatching {
|
||||
keepNoMediaFileStatus()
|
||||
private val collectScope = CoroutineScope(Dispatchers.Main + SupervisorJob())
|
||||
fun <T, R : PrefDelegate<T>> R.observed(func: suspend (T) -> Unit) = apply { collectScope.launch { valueFlow().drop(1).collectLatest(func) } }
|
||||
fun <T, R : Settings.Delegate<T>> R.emitTo(flow: MutableSharedFlow<Unit>) = apply { collectScope.launch { flow.emitAll(changesFlow()) } }
|
||||
fun <T, R : PrefDelegate<T>> R.emitTo(flow: MutableSharedFlow<Unit>) = apply { collectScope.launch { flow.emitAll(changesFlow()) } }
|
||||
|
||||
suspend fun updateWhenKeepMediaStatusChanges(mediaScan: Boolean) {
|
||||
withIOContext {
|
||||
catch {
|
||||
keepNoMediaFileStatus(mediaScan = mediaScan)
|
||||
}.onLeft {
|
||||
logcat(TAG, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateWhenThemeChanges() {
|
||||
collectScope.launch {
|
||||
delay(100) // Avoid recompose being cancelled
|
||||
if (isAtLeastS) {
|
||||
val mode = when (Settings.theme.value) {
|
||||
AppCompatDelegate.MODE_NIGHT_NO -> UiModeManager.MODE_NIGHT_NO
|
||||
AppCompatDelegate.MODE_NIGHT_YES -> UiModeManager.MODE_NIGHT_YES
|
||||
else -> UiModeManager.MODE_NIGHT_AUTO
|
||||
}
|
||||
uiModeManager.setApplicationNightMode(mode)
|
||||
suspend fun updateWhenThemeChanges(theme: Int) {
|
||||
delay(100) // Avoid recompose being cancelled
|
||||
if (isAtLeastS) {
|
||||
val mode = when (theme) {
|
||||
AppCompatDelegate.MODE_NIGHT_NO -> UiModeManager.MODE_NIGHT_NO
|
||||
AppCompatDelegate.MODE_NIGHT_YES -> UiModeManager.MODE_NIGHT_YES
|
||||
else -> UiModeManager.MODE_NIGHT_AUTO
|
||||
}
|
||||
AppCompatDelegate.setDefaultNightMode(Settings.theme.value)
|
||||
uiModeManager.setApplicationNightMode(mode)
|
||||
}
|
||||
AppCompatDelegate.setDefaultNightMode(theme)
|
||||
}
|
||||
|
||||
fun updateWhenRequestNewsChanges() {
|
||||
updateDailyCheckWork(appCtx)
|
||||
}
|
||||
|
||||
fun updateWhenGallerySiteChanges() {
|
||||
suspend fun updateWhenGallerySiteChanges(gallerySite: Int) {
|
||||
if (Settings.hasSignedIn.value) {
|
||||
collectScope.launchIO {
|
||||
runCatching {
|
||||
EhEngine.getUConfig()
|
||||
}.onFailure {
|
||||
logcat(it)
|
||||
}
|
||||
catch {
|
||||
EhEngine.getUConfig(gallerySite)
|
||||
}.onLeft {
|
||||
logcat(TAG, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateWhenTagTranslationChanges() {
|
||||
EhTagDatabase.launchUpdate()
|
||||
fun updateWhenTagTranslationChanges(enabled: Boolean) {
|
||||
if (enabled) {
|
||||
EhTagDatabase.launchUpdate()
|
||||
}
|
||||
}
|
||||
@@ -21,8 +21,8 @@ import arrow.core.partially2
|
||||
import arrow.core.right
|
||||
import arrow.fx.coroutines.parMap
|
||||
import arrow.fx.coroutines.parZip
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.hippo.ehviewer.EhDB
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.data.BaseGalleryInfo
|
||||
import com.hippo.ehviewer.client.data.FavListUrlBuilder
|
||||
@@ -212,7 +212,8 @@ object EhEngine {
|
||||
return ehRequest(url, EhUrl.URL_FORUMS).fetchUsingAsByteBuffer(ProfileParser::parse)
|
||||
}
|
||||
|
||||
suspend fun getUConfig(url: String = EhUrl.uConfigUrl) {
|
||||
suspend fun getUConfig(gallerySite: Int) {
|
||||
val url = EhUrl.getUConfigUrl(gallerySite)
|
||||
runSuspendCatching {
|
||||
ehRequest(url).fetchUsingAsByteBuffer(UserConfigParser::parse)
|
||||
}.onFailure { throwable ->
|
||||
|
||||
@@ -68,6 +68,7 @@ object EhTagDatabase : CoroutineScope {
|
||||
// Copied from https://github.com/EhTagTranslation/EhSyringe/blob/c0cf88da04081072492829673aca34380b14dae6/src/plugin/suggest.ts#L37
|
||||
// TODO: Optimize
|
||||
private val nsScore = mapOf(
|
||||
TagNamespace.Location.prefix to 10f,
|
||||
TagNamespace.Other.prefix to 10f,
|
||||
TagNamespace.Female.prefix to 9f,
|
||||
TagNamespace.Male.prefix to 8.5f,
|
||||
@@ -82,7 +83,7 @@ object EhTagDatabase : CoroutineScope {
|
||||
|
||||
data class Tag(val tag: String, val hint: String?, val score: Float)
|
||||
|
||||
context(ctx: Context)
|
||||
context(_: Context)
|
||||
fun suggestion(rawKeyword: String, expectTranslate: Boolean): List<Tag> {
|
||||
val keyword = PREFIXES.fold(rawKeyword) { kwd, pfx -> kwd.removePrefix(pfx) }
|
||||
if (!initialized || keyword.isEmpty()) return emptyList()
|
||||
|
||||
@@ -80,12 +80,11 @@ object EhUrl {
|
||||
else -> ORIGIN_E
|
||||
}
|
||||
|
||||
val uConfigUrl: String
|
||||
get() = when (Settings.gallerySite.value) {
|
||||
SITE_E -> URL_UCONFIG_E
|
||||
SITE_EX -> URL_UCONFIG_EX
|
||||
else -> URL_UCONFIG_E
|
||||
}
|
||||
fun getUConfigUrl(gallerySite: Int = Settings.gallerySite.value) = when (gallerySite) {
|
||||
SITE_E -> URL_UCONFIG_E
|
||||
SITE_EX -> URL_UCONFIG_EX
|
||||
else -> URL_UCONFIG_E
|
||||
}
|
||||
|
||||
val myTagsUrl: String
|
||||
get() = when (Settings.gallerySite.value) {
|
||||
|
||||
@@ -27,6 +27,7 @@ import androidx.compose.runtime.Stable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.core.net.toUri
|
||||
import arrow.core.memoize
|
||||
import com.ehviewer.core.util.withUIContext
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.data.GalleryDetail
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo
|
||||
@@ -38,7 +39,6 @@ import com.hippo.ehviewer.util.addTextToClipboard
|
||||
import com.materialkolor.hct.Hct
|
||||
import com.materialkolor.ktx.from
|
||||
import com.materialkolor.ktx.toColor
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
import splitties.systemservices.downloadManager
|
||||
|
||||
object EhUtils {
|
||||
|
||||
@@ -16,6 +16,7 @@ sealed class TagNamespace(val value: String, val prefix: String?) {
|
||||
object Female : TagNamespace("female", "f")
|
||||
object Group : TagNamespace("group", "g")
|
||||
object Language : TagNamespace("language", "l")
|
||||
object Location : TagNamespace("location", "loc")
|
||||
object Male : TagNamespace("male", "m")
|
||||
object Mixed : TagNamespace("mixed", "x")
|
||||
object Other : TagNamespace("other", "o")
|
||||
@@ -31,6 +32,7 @@ sealed class TagNamespace(val value: String, val prefix: String?) {
|
||||
Female.value -> Female
|
||||
Group.value -> Group
|
||||
Language.value -> Language
|
||||
Location.value -> Location
|
||||
Male.value -> Male
|
||||
Mixed.value -> Mixed
|
||||
Other.value -> Other
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
package com.hippo.ehviewer.client.exception
|
||||
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
|
||||
class CloudflareBypassException : EhException(R.string.cloudflare_bypass_failed)
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
package com.hippo.ehviewer.client.exception
|
||||
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
|
||||
class InsufficientFundsException : EhException(R.string.insufficient_funds)
|
||||
@@ -15,6 +15,6 @@
|
||||
*/
|
||||
package com.hippo.ehviewer.client.exception
|
||||
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
|
||||
class NoHathClientException() : EhException(R.string.download_archive_failure_no_hath)
|
||||
@@ -1,5 +1,5 @@
|
||||
package com.hippo.ehviewer.client.exception
|
||||
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
|
||||
class NoHitsFoundException : EhException(R.string.gallery_list_empty_hit)
|
||||
@@ -1,5 +1,5 @@
|
||||
package com.hippo.ehviewer.client.exception
|
||||
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
|
||||
class NotLoggedInException : EhException(R.string.sign_in_first)
|
||||
@@ -1,5 +1,5 @@
|
||||
package com.hippo.ehviewer.client.exception
|
||||
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
|
||||
class QuotaExceededException : EhException(R.string.error_509)
|
||||
@@ -19,10 +19,10 @@ import androidx.work.PeriodicWorkRequest
|
||||
import androidx.work.PeriodicWorkRequestBuilder
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.WorkerParameters
|
||||
import com.ehviewer.core.util.withIOContext
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.EhEngine
|
||||
import eu.kanade.tachiyomi.util.lang.withIOContext
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import java.time.Duration
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@@ -14,6 +14,9 @@ interface DownloadLabelDao {
|
||||
@Query("SELECT * FROM DOWNLOAD_LABELS ORDER BY POSITION LIMIT :limit OFFSET :offset")
|
||||
suspend fun list(offset: Int, limit: Int): List<DownloadLabel>
|
||||
|
||||
@Query("SELECT LABEL FROM DOWNLOAD_LABELS WHERE LABEL LIKE :pattern ORDER BY POSITION LIMIT :limit")
|
||||
suspend fun search(pattern: String, limit: Int): List<String>
|
||||
|
||||
@Query("UPDATE DOWNLOAD_LABELS SET POSITION = POSITION - 1 WHERE POSITION > :position")
|
||||
suspend fun fill(position: Int)
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.MapColumn
|
||||
import androidx.room.Query
|
||||
import androidx.room.RoomWarnings
|
||||
import androidx.room.Transaction
|
||||
import androidx.room.Update
|
||||
import androidx.room.Upsert
|
||||
@@ -12,11 +13,12 @@ import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface DownloadsDao {
|
||||
@Suppress("ktlint:standard:annotation")
|
||||
// https://issuetracker.google.com/327583152
|
||||
@Suppress("ktlint:standard:annotation", RoomWarnings.UNNECESSARY_NULLABILITY_IN_DAO_RETURN_TYPE)
|
||||
@Query("SELECT LABEL, COUNT(*) AS COUNT FROM DOWNLOADS LEFT JOIN DOWNLOAD_LABELS USING(LABEL) GROUP BY LABEL")
|
||||
fun countByLabel(): Flow<Map<@MapColumn("LABEL") String?, @MapColumn("COUNT") Int>>
|
||||
|
||||
@Suppress("ktlint:standard:annotation")
|
||||
@Suppress("ktlint:standard:annotation", RoomWarnings.UNNECESSARY_NULLABILITY_IN_DAO_RETURN_TYPE)
|
||||
@Query("SELECT ARTIST, COUNT(*) AS COUNT FROM DOWNLOADS LEFT JOIN DOWNLOAD_ARTISTS USING(GID) GROUP BY ARTIST ORDER BY COUNT DESC, ARTIST")
|
||||
fun countByArtist(): Flow<Map<@MapColumn("ARTIST") String?, @MapColumn("COUNT") Int>>
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.core.util.size
|
||||
import arrow.fx.coroutines.parMapNotNull
|
||||
import com.ehviewer.core.preferences.edit
|
||||
import com.hippo.ehviewer.EhDB
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.data.BaseGalleryInfo
|
||||
@@ -64,7 +65,6 @@ import logcat.LogPriority
|
||||
import okio.Path
|
||||
import okio.Path.Companion.toOkioPath
|
||||
import okio.Path.Companion.toPath
|
||||
import splitties.preferences.edit
|
||||
|
||||
object DownloadManager : OnSpiderListener, CoroutineScope {
|
||||
override val coroutineContext = Dispatchers.IO + Job()
|
||||
@@ -711,9 +711,7 @@ object DownloadManager : OnSpiderListener, CoroutineScope {
|
||||
fun start() {
|
||||
if (currentJob == null) {
|
||||
currentJob = launch {
|
||||
tracker.speedFlow().collect { speed ->
|
||||
updateSpeed(speed.toLong())
|
||||
}
|
||||
tracker.speedFlow().collect(::updateSpeed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,8 @@ import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.savedstate.serialization.decodeFromSavedState
|
||||
import androidx.savedstate.serialization.encodeToSavedState
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.ehviewer.core.util.unsafeLazy
|
||||
import com.hippo.ehviewer.client.EhUtils
|
||||
import com.hippo.ehviewer.client.data.BaseGalleryInfo
|
||||
import com.hippo.ehviewer.dao.DownloadInfo
|
||||
@@ -39,7 +40,6 @@ import com.hippo.ehviewer.ui.MainActivity
|
||||
import com.hippo.ehviewer.util.FileUtils
|
||||
import com.hippo.ehviewer.util.ReadableTime
|
||||
import com.hippo.ehviewer.util.isAtLeastS
|
||||
import com.hippo.ehviewer.util.unsafeLazy
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -137,7 +137,7 @@ class DownloadService :
|
||||
.setAutoCancel(false)
|
||||
.setCategory(NotificationCompat.CATEGORY_PROGRESS)
|
||||
.addAction(
|
||||
R.drawable.v_pause_x24,
|
||||
com.hippo.ehviewer.R.drawable.v_pause_x24,
|
||||
getString(R.string.stat_download_action_stop_all),
|
||||
piStopAll,
|
||||
)
|
||||
@@ -174,7 +174,7 @@ class DownloadService :
|
||||
|
||||
private fun init509Notification(): NotificationHandler {
|
||||
val builder = NotificationCompat.Builder(applicationContext, channelId)
|
||||
.setSmallIcon(R.drawable.ic_baseline_warning_24)
|
||||
.setSmallIcon(com.hippo.ehviewer.R.drawable.ic_baseline_warning_24)
|
||||
.setContentTitle(getString(R.string.stat_509_alert_title))
|
||||
.setContentText(getString(R.string.stat_509_alert_text))
|
||||
.setStyle(
|
||||
|
||||
@@ -53,7 +53,7 @@ suspend inline fun <T> useArchivePageLoader(
|
||||
{ _, _ -> closeArchive() },
|
||||
)
|
||||
check(size > 0) { "Archive have no content!" }
|
||||
if (needPassword() && archivePasswds.filterNotNull().none(::providePassword)) {
|
||||
if (needPassword() && archivePasswds.none(::providePassword)) {
|
||||
archivePasswds += passwdProvider(::providePassword)
|
||||
}
|
||||
val loader = install(
|
||||
|
||||
@@ -4,6 +4,7 @@ import androidx.collection.SieveCache
|
||||
import androidx.collection.mutableIntObjectMapOf
|
||||
import arrow.fx.coroutines.ExitCase
|
||||
import arrow.fx.coroutines.bracketCase
|
||||
import com.ehviewer.core.util.withNonCancellableContext
|
||||
import com.hippo.ehviewer.EhDB
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.image.Image
|
||||
@@ -13,7 +14,6 @@ import com.hippo.ehviewer.util.OSUtils
|
||||
import com.hippo.ehviewer.util.detectAds
|
||||
import com.hippo.ehviewer.util.displayString
|
||||
import com.hippo.ehviewer.util.isAtLeastO
|
||||
import eu.kanade.tachiyomi.util.lang.withNonCancellableContext
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.atomics.AtomicInt
|
||||
import kotlin.concurrent.read
|
||||
|
||||
@@ -50,19 +50,19 @@ import com.hippo.ehviewer.ktbuilder.execute
|
||||
import com.hippo.ehviewer.ktbuilder.imageRequest
|
||||
import com.hippo.ehviewer.util.isAtLeastP
|
||||
import com.hippo.ehviewer.util.isAtLeastU
|
||||
import com.hippo.ehviewer.util.updateAndGet
|
||||
import com.hippo.files.openFileDescriptor
|
||||
import com.hippo.files.toUri
|
||||
import java.nio.ByteBuffer
|
||||
import kotlin.concurrent.atomics.AtomicInt
|
||||
import kotlin.concurrent.atomics.decrementAndFetch
|
||||
import kotlin.concurrent.atomics.updateAndFetch
|
||||
import okio.Path
|
||||
import splitties.init.appCtx
|
||||
|
||||
class Image private constructor(image: CoilImage, private val src: ImageSource) {
|
||||
val refcnt = AtomicInt(1)
|
||||
|
||||
fun pin() = refcnt.updateAndGet { if (it != 0) it + 1 else 0 } != 0
|
||||
fun pin() = refcnt.updateAndFetch { if (it != 0) it + 1 else 0 } != 0
|
||||
|
||||
fun unpin() = (refcnt.decrementAndFetch() == 0).also { if (it) recycle() }
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import android.net.http.UrlRequest
|
||||
import android.net.http.UrlResponseInfo
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresExtension
|
||||
import com.ehviewer.core.preferences.edit
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.util.restartApplication
|
||||
import io.ktor.client.engine.HttpClientEngineBase
|
||||
@@ -40,7 +41,6 @@ import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.channels.consumeEach
|
||||
import kotlinx.coroutines.job
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import splitties.preferences.edit
|
||||
|
||||
class CronetEngine(override val config: CronetConfig) : HttpClientEngineBase("Cronet") {
|
||||
// Limit thread to 1 since we are async & non-blocking
|
||||
@@ -108,7 +108,7 @@ class CronetEngine(override val config: CronetConfig) : HttpClientEngineBase("Cr
|
||||
val message = error.message.orEmpty()
|
||||
if ("ERR_FILE_NOT_FOUND" in message || "ERR_PROXY_CONNECTION_FAILED" in message) {
|
||||
Settings.edit(true) {
|
||||
enableCronet.value = false
|
||||
it[enableCronet] = false
|
||||
}
|
||||
with(config.context) { restartApplication() }
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.hippo.ehviewer.client.data.TagNamespace.Character
|
||||
import com.hippo.ehviewer.client.data.TagNamespace.Cosplayer
|
||||
import com.hippo.ehviewer.client.data.TagNamespace.Female
|
||||
import com.hippo.ehviewer.client.data.TagNamespace.Group
|
||||
import com.hippo.ehviewer.client.data.TagNamespace.Location
|
||||
import com.hippo.ehviewer.client.data.TagNamespace.Male
|
||||
import com.hippo.ehviewer.client.data.TagNamespace.Mixed
|
||||
import com.hippo.ehviewer.client.data.TagNamespace.Other
|
||||
@@ -60,7 +61,7 @@ fun GalleryInfo.getComicInfo(): ComicInfo {
|
||||
Group -> groups.addAll(list)
|
||||
Character -> characters.addAll(list)
|
||||
Parody -> parodies.addAll(list)
|
||||
Other -> otherTags.addAll(list)
|
||||
Location, Other -> otherTags.addAll(list)
|
||||
Female, Male, Mixed -> ns.prefix.let { prefix ->
|
||||
list.forEach { tag -> otherTags.add("$prefix:$tag") }
|
||||
}
|
||||
@@ -76,7 +77,7 @@ fun GalleryInfo.getComicInfo(): ComicInfo {
|
||||
Group -> groups.add(tag)
|
||||
Character -> characters.add(tag)
|
||||
Parody -> if (tag != TAG_ORIGINAL) parodies.add(tag)
|
||||
Other -> otherTags.add(tag)
|
||||
Location, Other -> otherTags.add(tag)
|
||||
Female, Male, Mixed -> ns.prefix.let { otherTags.add("$it:$tag") }
|
||||
else -> Unit
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import kotlin.time.Duration.Companion.seconds
|
||||
import kotlin.time.Instant
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
@@ -47,7 +47,7 @@ class SpeedTracker(val window: Duration = 1.seconds) {
|
||||
}
|
||||
}
|
||||
|
||||
fun speedFlow(sample: Duration = 1.seconds) = fixedRate(sample).map {
|
||||
fun speedFlow(sample: Duration = 1.seconds) = fixedRate(sample).mapNotNull {
|
||||
mutex.withLock {
|
||||
start?.let { start ->
|
||||
val now = Clock.System.now()
|
||||
@@ -55,8 +55,8 @@ class SpeedTracker(val window: Duration = 1.seconds) {
|
||||
val window = passed.coerceIn(10.milliseconds, window)
|
||||
val cutoff = now - window
|
||||
received.removeIf { time, _ -> time < cutoff }
|
||||
received.fold(0) { total, _, v -> total + v } / (window / 1.seconds)
|
||||
} ?: 0.0
|
||||
(received.fold(0) { total, _, v -> total + v } / (window / 1.seconds)).toLong()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,16 +72,21 @@ suspend inline fun <R> timeoutBySpeed(
|
||||
) = coroutineScope {
|
||||
val onTimeout = AtomicReference<OnTimeout?>(t).let { { e: IOException -> it.exchange(null)?.invoke(e) } }
|
||||
val watchdog = launch {
|
||||
delay(Settings.connTimeout.value.seconds)
|
||||
onTimeout(ConnectTimeoutException(url, Settings.connTimeout.value * 1000L))
|
||||
val timeout = 10.seconds
|
||||
delay(timeout)
|
||||
onTimeout(ConnectTimeoutException(url, timeout.inWholeMilliseconds))
|
||||
}
|
||||
val tracker = SpeedTracker(2.seconds)
|
||||
request {
|
||||
var prev = 0L
|
||||
onDownload { done, total ->
|
||||
val bytesRead = (done - prev).toInt()
|
||||
tracker.track(bytesRead)
|
||||
l(total!!, done, bytesRead)
|
||||
if (done == total!!) {
|
||||
tracker.reset()
|
||||
} else {
|
||||
tracker.track(bytesRead)
|
||||
}
|
||||
l(total, done, bytesRead)
|
||||
prev = done
|
||||
}
|
||||
timeout { reset() }
|
||||
@@ -89,10 +94,10 @@ suspend inline fun <R> timeoutBySpeed(
|
||||
watchdog.cancel()
|
||||
resp.status.ensureSuccess()
|
||||
val speedWatchdog = launch {
|
||||
val timeoutSpeed = speedLevelToSpeed(Settings.timeoutSpeed.value) * 1024.0
|
||||
tracker.speedFlow(1.seconds).collect { speed ->
|
||||
if (timeoutSpeed != 0.0 && speed < timeoutSpeed) {
|
||||
onTimeout(LowSpeedException(url, speed.toLong()))
|
||||
val timeoutSpeed = speedLevelToSpeed(Settings.timeoutSpeed.value) * 1024L
|
||||
tracker.speedFlow().collect { speed ->
|
||||
if (speed < timeoutSpeed) {
|
||||
onTimeout(LowSpeedException(url, speed))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,7 +109,9 @@ suspend inline fun <R> timeoutBySpeed(
|
||||
}
|
||||
}
|
||||
|
||||
fun speedLevelToSpeed(level: Int) = 2f.pow(level).roundToInt()
|
||||
const val MIN_SPEED_LEVEL = 3
|
||||
|
||||
fun speedLevelToSpeed(level: Int) = if (level == MIN_SPEED_LEVEL) 0 else 2f.pow(level).roundToInt()
|
||||
|
||||
inline fun <T, R> MutableObjectIntMap<T>.fold(initial: R, operation: (acc: R, T, Int) -> R): R {
|
||||
var accumulator = initial
|
||||
|
||||
@@ -20,7 +20,7 @@ import androidx.annotation.IntDef
|
||||
import arrow.core.Either
|
||||
import arrow.core.getOrElse
|
||||
import arrow.core.partially1
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.EhEngine
|
||||
import com.hippo.ehviewer.client.EhUrl
|
||||
|
||||
@@ -35,8 +35,10 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.ehviewer.core.ui.component.LabeledCheckbox
|
||||
import com.ehviewer.core.util.withIOContext
|
||||
import com.hippo.ehviewer.EhDB
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.EhEngine
|
||||
import com.hippo.ehviewer.client.EhUtils
|
||||
@@ -54,7 +56,6 @@ import com.hippo.ehviewer.download.tempDownloadDir
|
||||
import com.hippo.ehviewer.ui.destinations.ReaderScreenDestination
|
||||
import com.hippo.ehviewer.ui.reader.ReaderScreenArgs
|
||||
import com.hippo.ehviewer.ui.tools.DialogState
|
||||
import com.hippo.ehviewer.ui.tools.LabeledCheckbox
|
||||
import com.hippo.ehviewer.ui.tools.awaitConfirmationOrCancel
|
||||
import com.hippo.ehviewer.ui.tools.awaitResult
|
||||
import com.hippo.ehviewer.ui.tools.awaitSelectDate
|
||||
@@ -75,7 +76,6 @@ import com.hippo.files.exists
|
||||
import com.hippo.files.isDirectory
|
||||
import com.hippo.files.write
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import eu.kanade.tachiyomi.util.lang.withIOContext
|
||||
import kotlin.time.Clock
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
@@ -100,10 +100,10 @@ private fun ensureNoMediaFile(downloadDir: Path) {
|
||||
|
||||
private val lck = Mutex()
|
||||
|
||||
suspend fun keepNoMediaFileStatus(downloadDir: Path = downloadLocation) {
|
||||
suspend fun keepNoMediaFileStatus(downloadDir: Path = downloadLocation, mediaScan: Boolean = Settings.mediaScan.value) {
|
||||
if (downloadDir.isDirectory) {
|
||||
lck.withLock {
|
||||
if (Settings.mediaScan.value) {
|
||||
if (mediaScan) {
|
||||
removeNoMediaFile(downloadDir)
|
||||
} else {
|
||||
ensureNoMediaFile(downloadDir)
|
||||
@@ -114,7 +114,7 @@ suspend fun keepNoMediaFileStatus(downloadDir: Path = downloadLocation) {
|
||||
|
||||
fun getFavoriteIcon(favorited: Boolean) = if (favorited) Icons.Default.Favorite else Icons.Default.FavoriteBorder
|
||||
|
||||
context(_: DialogState, context: MainActivity)
|
||||
context(_: DialogState, _: MainActivity)
|
||||
suspend fun startDownload(forceDefault: Boolean, vararg galleryInfos: BaseGalleryInfo) {
|
||||
if (isAtLeastT) {
|
||||
requestPermission(Manifest.permission.POST_NOTIFICATIONS)
|
||||
|
||||
@@ -10,11 +10,11 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.ehviewer.core.ui.util.NoopTransitionsVisibilityScope
|
||||
import com.ehviewer.core.ui.util.TransitionsVisibilityScope
|
||||
import com.ehviewer.core.ui.util.togetherWith
|
||||
import com.hippo.ehviewer.ui.tools.DialogState
|
||||
import com.hippo.ehviewer.ui.tools.LocalGlobalDialogState
|
||||
import com.hippo.ehviewer.ui.tools.NoopTransitionsVisibilityScope
|
||||
import com.hippo.ehviewer.ui.tools.TransitionsVisibilityScope
|
||||
import com.hippo.ehviewer.ui.tools.togetherWith
|
||||
import com.hippo.ehviewer.util.findActivity
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
||||
@@ -24,7 +24,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.EhUrl
|
||||
import com.hippo.ehviewer.client.EhUtils
|
||||
@@ -76,7 +76,7 @@ fun GalleryInfoBottomSheet(detail: GalleryDetail) {
|
||||
LazyColumn(contentPadding = WindowInsets.systemBars.only(WindowInsetsSides.Bottom).asPaddingValues()) {
|
||||
itemsIndexed(data) { index, (key, content) ->
|
||||
Row(
|
||||
modifier = Modifier.padding(horizontal = dimensionResource(id = R.dimen.keyline_margin)).clickable {
|
||||
modifier = Modifier.padding(horizontal = dimensionResource(id = com.hippo.ehviewer.R.dimen.keyline_margin)).clickable {
|
||||
if (index == INDEX_PARENT) {
|
||||
if (content != null) {
|
||||
navWithUrl(content)
|
||||
|
||||
@@ -70,7 +70,6 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.runtime.currentCompositeKeyHashCode
|
||||
@@ -97,14 +96,21 @@ import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.lerp
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.lifecycle.compose.LifecycleResumeEffect
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.ehviewer.core.ui.component.LabeledCheckbox
|
||||
import com.ehviewer.core.ui.component.LocalSideSheetState
|
||||
import com.ehviewer.core.ui.component.MutableSideSheet
|
||||
import com.ehviewer.core.ui.icons.EhIcons
|
||||
import com.ehviewer.core.ui.icons.filled.Subscriptions
|
||||
import com.ehviewer.core.ui.util.LocalSnackBarFabPadding
|
||||
import com.ehviewer.core.ui.util.LocalWindowSizeClass
|
||||
import com.ehviewer.core.util.withIOContext
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.data.ListUrlBuilder
|
||||
import com.hippo.ehviewer.client.parser.GalleryDetailUrlParser
|
||||
@@ -112,8 +118,6 @@ import com.hippo.ehviewer.client.parser.GalleryPageUrlParser
|
||||
import com.hippo.ehviewer.collectAsState
|
||||
import com.hippo.ehviewer.download.DownloadService
|
||||
import com.hippo.ehviewer.download.downloadLocation
|
||||
import com.hippo.ehviewer.icons.EhIcons
|
||||
import com.hippo.ehviewer.icons.filled.Subscriptions
|
||||
import com.hippo.ehviewer.ui.destinations.DownloadScreenDestination
|
||||
import com.hippo.ehviewer.ui.destinations.DownloadsScreenDestination
|
||||
import com.hippo.ehviewer.ui.destinations.FavouritesScreenDestination
|
||||
@@ -130,8 +134,6 @@ import com.hippo.ehviewer.ui.screen.asDstWith
|
||||
import com.hippo.ehviewer.ui.screen.navWithUrl
|
||||
import com.hippo.ehviewer.ui.settings.showNewVersion
|
||||
import com.hippo.ehviewer.ui.tools.DialogState
|
||||
import com.hippo.ehviewer.ui.tools.LabeledCheckbox
|
||||
import com.hippo.ehviewer.ui.tools.LocalWindowSizeClass
|
||||
import com.hippo.ehviewer.ui.tools.awaitConfirmationOrCancel
|
||||
import com.hippo.ehviewer.ui.tools.awaitInputText
|
||||
import com.hippo.ehviewer.updater.AppUpdater
|
||||
@@ -150,7 +152,6 @@ import com.ramcosta.composedestinations.DestinationsNavHost
|
||||
import com.ramcosta.composedestinations.spec.Direction
|
||||
import com.ramcosta.composedestinations.utils.currentDestinationAsState
|
||||
import com.ramcosta.composedestinations.utils.rememberDestinationsNavigator
|
||||
import eu.kanade.tachiyomi.util.lang.withIOContext
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.delay
|
||||
@@ -394,7 +395,7 @@ class MainActivity : EhActivity() {
|
||||
.windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Bottom)),
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.sadpanda_low_poly),
|
||||
painter = painterResource(id = com.hippo.ehviewer.R.drawable.sadpanda_low_poly),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp),
|
||||
contentScale = ContentScale.FillWidth,
|
||||
@@ -532,7 +533,6 @@ val LocalNavDrawerState = compositionLocalOf<DrawerState> { error("CompositionLo
|
||||
|
||||
val LocalDrawerHandle = compositionLocalOf<SnapshotStateList<Long>> { error("CompositionLocal LocalDrawerHandle not present!") }
|
||||
val LocalSnackBarHostState = compositionLocalOf<SnackbarHostState> { error("CompositionLocal LocalSnackBarHostState not present!") }
|
||||
val LocalSnackBarFabPadding = compositionLocalOf<State<Dp>> { error("CompositionLocal LocalSnackBarFabPadding not present!") }
|
||||
val LocalSharedTransitionScope = compositionLocalOf<SharedTransitionScope> { error("CompositionLocal LocalSharedTransitionScope not present!") }
|
||||
|
||||
@Composable
|
||||
|
||||
@@ -29,7 +29,7 @@ import androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL
|
||||
import androidx.biometric.registerForAuthenticationResult
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
|
||||
context(ctx: Context)
|
||||
|
||||
@@ -20,7 +20,7 @@ import android.content.Context
|
||||
import android.widget.Toast
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import androidx.core.net.toUri
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.hippo.ehviewer.client.data.GalleryDetail
|
||||
import com.hippo.ehviewer.client.parser.GalleryPageUrlParser
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
@@ -32,7 +32,7 @@ fun openBrowser(url: String) {
|
||||
if (url.isEmpty()) return
|
||||
try {
|
||||
intent.launchUrl(ctx, url.toUri())
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
} catch (_: ActivityNotFoundException) {
|
||||
Toast.makeText(ctx, R.string.no_browser_installed, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ fun postLogin() = GlobalScope.async(Dispatchers.IO) {
|
||||
// Get cookies for image limits
|
||||
launch {
|
||||
catch {
|
||||
EhEngine.getUConfig(EhUrl.URL_UCONFIG_E)
|
||||
EhEngine.getUConfig(EhUrl.SITE_E)
|
||||
EhCookieStore.flush()
|
||||
}.onLeft {
|
||||
logcat(it)
|
||||
@@ -45,7 +45,7 @@ fun postLogin() = GlobalScope.async(Dispatchers.IO) {
|
||||
|
||||
// Sad panda check
|
||||
catch {
|
||||
EhEngine.getUConfig(EhUrl.URL_UCONFIG_EX)
|
||||
EhEngine.getUConfig(EhUrl.SITE_EX)
|
||||
EhCookieStore.flush()
|
||||
Settings.gallerySite.value = EhUrl.SITE_EX
|
||||
}.onLeft {
|
||||
|
||||
@@ -58,7 +58,12 @@ import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.ehviewer.core.ui.util.LocalWindowSizeClass
|
||||
import com.ehviewer.core.ui.util.isExpanded
|
||||
import com.ehviewer.core.ui.util.thenIf
|
||||
import com.ehviewer.core.util.launchIO
|
||||
import com.ehviewer.core.util.withUIContext
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.EhEngine
|
||||
import com.hippo.ehviewer.client.EhUrl
|
||||
@@ -66,18 +71,13 @@ import com.hippo.ehviewer.client.EhUtils
|
||||
import com.hippo.ehviewer.ui.Screen
|
||||
import com.hippo.ehviewer.ui.destinations.WebViewSignInScreenDestination
|
||||
import com.hippo.ehviewer.ui.openBrowser
|
||||
import com.hippo.ehviewer.ui.tools.LocalWindowSizeClass
|
||||
import com.hippo.ehviewer.ui.tools.awaitConfirmationOrCancel
|
||||
import com.hippo.ehviewer.ui.tools.isExpanded
|
||||
import com.hippo.ehviewer.ui.tools.thenIf
|
||||
import com.hippo.ehviewer.util.displayString
|
||||
import com.jamal.composeprefs3.ui.ifTrueThen
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
import kotlinx.coroutines.Job
|
||||
import moe.tarsin.launchIO
|
||||
import moe.tarsin.navigate
|
||||
|
||||
@Destination<RootGraph>(start = true)
|
||||
@@ -142,7 +142,7 @@ fun AnimatedVisibilityScope.SignInScreen(navigator: DestinationsNavigator) = Scr
|
||||
fun UsernameAndPasswordTextField() {
|
||||
OutlinedTextField(
|
||||
state = username,
|
||||
modifier = Modifier.width(dimensionResource(id = R.dimen.single_max_width))
|
||||
modifier = Modifier.width(dimensionResource(id = com.hippo.ehviewer.R.dimen.single_max_width))
|
||||
.semantics { contentType = ContentType.Username }
|
||||
.thenIf(!showUsernameError) { padding(bottom = 16.dp) },
|
||||
label = { Text(stringResource(R.string.username)) },
|
||||
@@ -154,7 +154,7 @@ fun AnimatedVisibilityScope.SignInScreen(navigator: DestinationsNavigator) = Scr
|
||||
)
|
||||
OutlinedSecureTextField(
|
||||
state = password,
|
||||
modifier = Modifier.width(dimensionResource(id = R.dimen.single_max_width))
|
||||
modifier = Modifier.width(dimensionResource(id = com.hippo.ehviewer.R.dimen.single_max_width))
|
||||
.semantics { contentType = ContentType.Password }
|
||||
.thenIf(!showPasswordError) { padding(bottom = 16.dp) },
|
||||
label = { Text(stringResource(R.string.password)) },
|
||||
@@ -180,27 +180,27 @@ fun AnimatedVisibilityScope.SignInScreen(navigator: DestinationsNavigator) = Scr
|
||||
when {
|
||||
!windowSizeClass.isExpanded -> {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).systemBarsPadding().padding(dimensionResource(id = R.dimen.keyline_margin)),
|
||||
modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).systemBarsPadding().padding(dimensionResource(id = com.hippo.ehviewer.R.dimen.keyline_margin)),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_launcher_foreground),
|
||||
painter = painterResource(id = com.hippo.ehviewer.R.drawable.ic_launcher_foreground),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.padding(dimensionResource(id = R.dimen.keyline_margin)),
|
||||
modifier = Modifier.padding(dimensionResource(id = com.hippo.ehviewer.R.dimen.keyline_margin)),
|
||||
)
|
||||
UsernameAndPasswordTextField()
|
||||
Text(
|
||||
text = stringResource(id = R.string.app_waring),
|
||||
modifier = Modifier.widthIn(max = dimensionResource(id = R.dimen.single_max_width)).padding(top = 24.dp),
|
||||
modifier = Modifier.widthIn(max = dimensionResource(id = com.hippo.ehviewer.R.dimen.single_max_width)).padding(top = 24.dp),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(id = R.string.app_waring_2),
|
||||
modifier = Modifier.widthIn(max = dimensionResource(id = R.dimen.single_max_width)).padding(top = 12.dp),
|
||||
modifier = Modifier.widthIn(max = dimensionResource(id = com.hippo.ehviewer.R.dimen.single_max_width)).padding(top = 12.dp),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(dimensionResource(id = R.dimen.keyline_margin)))
|
||||
Row(modifier = Modifier.padding(top = dimensionResource(R.dimen.keyline_margin))) {
|
||||
Spacer(modifier = Modifier.height(dimensionResource(id = com.hippo.ehviewer.R.dimen.keyline_margin)))
|
||||
Row(modifier = Modifier.padding(top = dimensionResource(com.hippo.ehviewer.R.dimen.keyline_margin))) {
|
||||
FilledTonalButton(
|
||||
onClick = { openBrowser(EhUrl.URL_REGISTER) },
|
||||
shapes = ButtonDefaults.shapes(),
|
||||
@@ -255,18 +255,18 @@ fun AnimatedVisibilityScope.SignInScreen(navigator: DestinationsNavigator) = Scr
|
||||
}
|
||||
else -> {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).systemBarsPadding().padding(dimensionResource(id = R.dimen.keyline_margin)),
|
||||
modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).systemBarsPadding().padding(dimensionResource(id = com.hippo.ehviewer.R.dimen.keyline_margin)),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.width(dimensionResource(id = R.dimen.signinscreen_landscape_caption_frame_width)).padding(dimensionResource(id = R.dimen.keyline_margin)),
|
||||
modifier = Modifier.width(dimensionResource(id = com.hippo.ehviewer.R.dimen.signinscreen_landscape_caption_frame_width)).padding(dimensionResource(id = com.hippo.ehviewer.R.dimen.keyline_margin)),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_launcher_foreground),
|
||||
painter = painterResource(id = com.hippo.ehviewer.R.drawable.ic_launcher_foreground),
|
||||
contentDescription = null,
|
||||
alignment = Alignment.Center,
|
||||
modifier = Modifier.padding(dimensionResource(id = R.dimen.keyline_margin)),
|
||||
modifier = Modifier.padding(dimensionResource(id = com.hippo.ehviewer.R.dimen.keyline_margin)),
|
||||
)
|
||||
Text(
|
||||
text = stringResource(id = R.string.app_waring),
|
||||
|
||||
@@ -22,7 +22,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.hippo.ehviewer.client.parser.Archive
|
||||
import com.hippo.ehviewer.client.parser.Funds
|
||||
|
||||
|
||||
@@ -43,7 +43,11 @@ import arrow.core.none
|
||||
import arrow.core.some
|
||||
import coil3.compose.AsyncImage
|
||||
import coil3.network.HttpException
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.ehviewer.core.ui.component.RollingNumber
|
||||
import com.ehviewer.core.ui.component.RollingNumberPlaceholder
|
||||
import com.ehviewer.core.util.launch
|
||||
import com.ehviewer.core.util.launchIO
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.EhEngine
|
||||
import com.hippo.ehviewer.client.EhUtils
|
||||
@@ -65,8 +69,6 @@ import kotlinx.coroutines.flow.merge
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import moe.tarsin.coroutines.runSwallowingWithUI
|
||||
import moe.tarsin.launch
|
||||
import moe.tarsin.launchIO
|
||||
|
||||
private val limitScope = CoroutineScope(Dispatchers.IO)
|
||||
private val refreshEvent = MutableSharedFlow<Unit>()
|
||||
@@ -107,7 +109,7 @@ fun AvatarIcon() {
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
CircularWavyProgressIndicator()
|
||||
Spacer(modifier = Modifier.size(dimensionResource(id = R.dimen.keyline_margin)))
|
||||
Spacer(modifier = Modifier.size(dimensionResource(id = com.hippo.ehviewer.R.dimen.keyline_margin)))
|
||||
Text(text = placeholder)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,15 +39,15 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.ehviewer.core.ui.component.CrystalCard
|
||||
import com.ehviewer.core.ui.util.TransitionsVisibilityScope
|
||||
import com.ehviewer.core.ui.util.listThumbGenerator
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.EhUtils
|
||||
import com.hippo.ehviewer.dao.DownloadInfo
|
||||
import com.hippo.ehviewer.download.DownloadManager
|
||||
import com.hippo.ehviewer.ui.tools.CrystalCard
|
||||
import com.hippo.ehviewer.ui.tools.GalleryListCardRating
|
||||
import com.hippo.ehviewer.ui.tools.TransitionsVisibilityScope
|
||||
import com.hippo.ehviewer.ui.tools.listThumbGenerator
|
||||
import com.hippo.ehviewer.util.FileUtils
|
||||
|
||||
@Composable
|
||||
|
||||
@@ -18,13 +18,13 @@ import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import coil3.compose.AsyncImagePainter
|
||||
import coil3.compose.rememberAsyncImagePainter
|
||||
import com.ehviewer.core.ui.util.SETNodeGenerator
|
||||
import com.ehviewer.core.ui.util.SharedElementBox
|
||||
import com.ehviewer.core.ui.util.TransitionsVisibilityScope
|
||||
import com.ehviewer.core.ui.util.thenIf
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo
|
||||
import com.hippo.ehviewer.ktbuilder.imageRequest
|
||||
import com.hippo.ehviewer.ui.tools.SETNodeGenerator
|
||||
import com.hippo.ehviewer.ui.tools.SharedElementBox
|
||||
import com.hippo.ehviewer.ui.tools.TransitionsVisibilityScope
|
||||
import com.hippo.ehviewer.ui.tools.shouldCrop
|
||||
import com.hippo.ehviewer.ui.tools.thenIf
|
||||
|
||||
@Composable
|
||||
@NonRestartableComposable
|
||||
|
||||
@@ -11,6 +11,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.ehviewer.core.ui.component.RollingNumber
|
||||
|
||||
@Composable
|
||||
fun FundsItem(
|
||||
|
||||
@@ -23,7 +23,7 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.core.text.parseAsHtml
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.hippo.ehviewer.client.data.GalleryComment
|
||||
import com.hippo.ehviewer.ui.legacy.CoilImageGetter
|
||||
import com.hippo.ehviewer.ui.legacy.LinkifyTextView
|
||||
@@ -50,7 +50,7 @@ fun GalleryCommentCard(
|
||||
},
|
||||
modifier = modifier,
|
||||
) {
|
||||
val margin = dimensionResource(id = R.dimen.keyline_margin)
|
||||
val margin = dimensionResource(id = com.hippo.ehviewer.R.dimen.keyline_margin)
|
||||
Row(
|
||||
modifier = Modifier.padding(horizontal = margin, vertical = 8.dp).fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
|
||||
@@ -32,14 +32,14 @@ import androidx.compose.ui.res.pluralStringResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.ehviewer.core.ui.icons.EhIcons
|
||||
import com.ehviewer.core.ui.icons.big.SadAndroid
|
||||
import com.ehviewer.core.ui.util.TransitionsVisibilityScope
|
||||
import com.ehviewer.core.ui.util.detailThumbGenerator
|
||||
import com.hippo.ehviewer.client.EhUtils
|
||||
import com.hippo.ehviewer.client.data.GalleryDetail
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo
|
||||
import com.hippo.ehviewer.icons.EhIcons
|
||||
import com.hippo.ehviewer.icons.big.SadAndroid
|
||||
import com.hippo.ehviewer.ui.tools.TransitionsVisibilityScope
|
||||
import com.hippo.ehviewer.ui.tools.detailThumbGenerator
|
||||
|
||||
@Composable
|
||||
fun GalleryDetailHeaderInfoCard(
|
||||
@@ -91,21 +91,21 @@ fun GalleryDetailHeaderCard(
|
||||
EhThumbCard(
|
||||
key = remember(info.gid) { info },
|
||||
modifier = Modifier.size(
|
||||
dimensionResource(id = R.dimen.gallery_detail_thumb_width),
|
||||
dimensionResource(id = R.dimen.gallery_detail_thumb_height),
|
||||
dimensionResource(id = com.hippo.ehviewer.R.dimen.gallery_detail_thumb_width),
|
||||
dimensionResource(id = com.hippo.ehviewer.R.dimen.gallery_detail_thumb_height),
|
||||
),
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.weight(0.5F))
|
||||
Column(
|
||||
modifier = Modifier.height(dimensionResource(id = R.dimen.gallery_detail_thumb_height)),
|
||||
modifier = Modifier.height(dimensionResource(id = com.hippo.ehviewer.R.dimen.gallery_detail_thumb_height)),
|
||||
horizontalAlignment = Alignment.End,
|
||||
) {
|
||||
(info as? GalleryDetail)?.let {
|
||||
GalleryDetailHeaderInfoCard(
|
||||
detail = it,
|
||||
onClick = onInfoCardClick,
|
||||
modifier = Modifier.padding(top = 8.dp, end = dimensionResource(id = R.dimen.keyline_margin)),
|
||||
modifier = Modifier.padding(top = 8.dp, end = dimensionResource(id = com.hippo.ehviewer.R.dimen.keyline_margin)),
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.weight(1F))
|
||||
@@ -113,7 +113,7 @@ fun GalleryDetailHeaderCard(
|
||||
AssistChip(
|
||||
onClick = onCategoryChipClick,
|
||||
label = { Text(text = categoryText, overflow = TextOverflow.Visible, softWrap = false, maxLines = 1) },
|
||||
modifier = Modifier.padding(horizontal = dimensionResource(id = R.dimen.keyline_margin)),
|
||||
modifier = Modifier.padding(horizontal = dimensionResource(id = com.hippo.ehviewer.R.dimen.keyline_margin)),
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Default.Label,
|
||||
@@ -125,7 +125,7 @@ fun GalleryDetailHeaderCard(
|
||||
AssistChip(
|
||||
onClick = onUploaderChipClick,
|
||||
label = { Text(text = uploaderText, overflow = TextOverflow.Visible, softWrap = false, maxLines = 1) },
|
||||
modifier = Modifier.padding(horizontal = dimensionResource(id = R.dimen.keyline_margin)),
|
||||
modifier = Modifier.padding(horizontal = dimensionResource(id = com.hippo.ehviewer.R.dimen.keyline_margin)),
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
imageVector = Icons.Default.NoAccounts,
|
||||
|
||||
@@ -38,17 +38,17 @@ import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil3.compose.AsyncImage
|
||||
import com.ehviewer.core.ui.component.CrystalCard
|
||||
import com.ehviewer.core.ui.component.ElevatedCard
|
||||
import com.ehviewer.core.ui.util.SharedElementBox
|
||||
import com.ehviewer.core.ui.util.TransitionsVisibilityScope
|
||||
import com.ehviewer.core.ui.util.listThumbGenerator
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.EhUtils
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo.Companion.NOT_FAVORITED
|
||||
import com.hippo.ehviewer.download.DownloadManager
|
||||
import com.hippo.ehviewer.ui.tools.CrystalCard
|
||||
import com.hippo.ehviewer.ui.tools.ElevatedCard
|
||||
import com.hippo.ehviewer.ui.tools.GalleryListCardRating
|
||||
import com.hippo.ehviewer.ui.tools.SharedElementBox
|
||||
import com.hippo.ehviewer.ui.tools.TransitionsVisibilityScope
|
||||
import com.hippo.ehviewer.ui.tools.listThumbGenerator
|
||||
import com.hippo.ehviewer.util.FavouriteStatusRouter
|
||||
|
||||
@Composable
|
||||
|
||||
@@ -51,23 +51,23 @@ import androidx.paging.LoadState
|
||||
import androidx.paging.compose.LazyPagingItems
|
||||
import androidx.paging.compose.itemContentType
|
||||
import androidx.paging.compose.itemKey
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.ehviewer.core.ui.component.FastScrollLazyVerticalGrid
|
||||
import com.ehviewer.core.ui.component.FastScrollLazyVerticalStaggeredGrid
|
||||
import com.ehviewer.core.ui.icons.EhIcons
|
||||
import com.ehviewer.core.ui.icons.big.SadAndroid
|
||||
import com.ehviewer.core.util.launch
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.data.BaseGalleryInfo
|
||||
import com.hippo.ehviewer.client.exception.NoHitsFoundException
|
||||
import com.hippo.ehviewer.coil.PrefetchAround
|
||||
import com.hippo.ehviewer.collectAsState
|
||||
import com.hippo.ehviewer.icons.EhIcons
|
||||
import com.hippo.ehviewer.icons.big.SadAndroid
|
||||
import com.hippo.ehviewer.ktbuilder.imageRequest
|
||||
import com.hippo.ehviewer.ui.screen.collectDetailSizeAsState
|
||||
import com.hippo.ehviewer.ui.tools.FastScrollLazyVerticalGrid
|
||||
import com.hippo.ehviewer.ui.tools.FastScrollLazyVerticalStaggeredGrid
|
||||
import com.hippo.ehviewer.util.displayString
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.drop
|
||||
import kotlinx.coroutines.flow.first
|
||||
import moe.tarsin.launch
|
||||
|
||||
@Stable
|
||||
operator fun PaddingValues.plus(r: PaddingValues) = object : PaddingValues {
|
||||
@@ -79,7 +79,7 @@ operator fun PaddingValues.plus(r: PaddingValues) = object : PaddingValues {
|
||||
}
|
||||
|
||||
@Composable
|
||||
context(_: CoroutineScope, ctx: Context)
|
||||
context(_: CoroutineScope, _: Context)
|
||||
fun GalleryList(
|
||||
modifier: Modifier = Modifier,
|
||||
data: LazyPagingItems<BaseGalleryInfo>,
|
||||
@@ -95,8 +95,8 @@ fun GalleryList(
|
||||
onRefresh: () -> Unit,
|
||||
onLoading: () -> Unit,
|
||||
) {
|
||||
val marginH = dimensionResource(id = R.dimen.gallery_list_margin_h)
|
||||
val marginV = dimensionResource(id = R.dimen.gallery_list_margin_v)
|
||||
val marginH = dimensionResource(id = com.hippo.ehviewer.R.dimen.gallery_list_margin_h)
|
||||
val marginV = dimensionResource(id = com.hippo.ehviewer.R.dimen.gallery_list_margin_v)
|
||||
|
||||
var isRefreshing by remember { mutableStateOf(false) }
|
||||
val refreshState = rememberPullToRefreshState()
|
||||
@@ -132,8 +132,8 @@ fun GalleryList(
|
||||
modifier = contentModifier.fillMaxSize(),
|
||||
state = detailListState,
|
||||
contentPadding = contentPadding + PaddingValues(marginH, marginV),
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.gallery_list_interval)),
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.gallery_list_interval)),
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(com.hippo.ehviewer.R.dimen.gallery_list_interval)),
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(com.hippo.ehviewer.R.dimen.gallery_list_interval)),
|
||||
) {
|
||||
items(
|
||||
count = data.itemCount,
|
||||
@@ -155,7 +155,7 @@ fun GalleryList(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val gridInterval = dimensionResource(R.dimen.gallery_grid_interval)
|
||||
val gridInterval = dimensionResource(com.hippo.ehviewer.R.dimen.gallery_grid_interval)
|
||||
val thumbColumns by Settings.thumbColumns.collectAsState()
|
||||
FastScrollLazyVerticalStaggeredGrid(
|
||||
columns = StaggeredGridCells.Fixed(thumbColumns),
|
||||
|
||||
@@ -23,10 +23,10 @@ import androidx.compose.ui.unit.IntSize
|
||||
import coil3.BitmapImage
|
||||
import coil3.compose.AsyncImagePainter.State
|
||||
import coil3.compose.rememberAsyncImagePainter
|
||||
import com.ehviewer.core.ui.component.CrystalCard
|
||||
import com.hippo.ehviewer.client.data.GalleryPreview
|
||||
import com.hippo.ehviewer.client.data.V2GalleryPreview
|
||||
import com.hippo.ehviewer.ktbuilder.imageRequest
|
||||
import com.hippo.ehviewer.ui.tools.CrystalCard
|
||||
import com.hippo.ehviewer.ui.tools.shouldCrop
|
||||
|
||||
@Composable
|
||||
|
||||
@@ -17,7 +17,7 @@ import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil3.compose.AsyncImage
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
|
||||
@Composable
|
||||
fun ImageSearch(
|
||||
@@ -29,7 +29,7 @@ fun ImageSearch(
|
||||
) {
|
||||
AnimatedVisibility(visible = image != null, modifier = Modifier.align(Alignment.CenterHorizontally)) {
|
||||
Card {
|
||||
val maxSize = dimensionResource(id = R.dimen.image_search_max_size)
|
||||
val maxSize = dimensionResource(id = com.hippo.ehviewer.R.dimen.image_search_max_size)
|
||||
AsyncImage(
|
||||
model = image,
|
||||
contentDescription = null,
|
||||
|
||||
@@ -10,7 +10,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.paging.LoadState
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.hippo.ehviewer.util.displayString
|
||||
|
||||
@Composable
|
||||
|
||||
@@ -33,19 +33,19 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import arrow.core.raise.ensure
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.ehviewer.core.ui.component.DropdownFilterChip
|
||||
import com.ehviewer.core.ui.util.thenIf
|
||||
import com.ehviewer.core.util.launch
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.EhTagDatabase
|
||||
import com.hippo.ehviewer.client.EhUtils
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo.Companion.S_LANG_TAGS
|
||||
import com.hippo.ehviewer.collectAsState
|
||||
import com.hippo.ehviewer.ui.tools.DialogState
|
||||
import com.hippo.ehviewer.ui.tools.DropdownFilterChip
|
||||
import com.hippo.ehviewer.ui.tools.awaitResult
|
||||
import com.hippo.ehviewer.ui.tools.thenIf
|
||||
import com.hippo.ehviewer.util.toIntOrDefault
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import moe.tarsin.launch
|
||||
|
||||
private val categoryTable = arrayOf(
|
||||
EhUtils.DOUJINSHI to R.string.doujinshi,
|
||||
@@ -117,7 +117,7 @@ fun SearchFilter(
|
||||
selectedItemIndex = language + 1,
|
||||
onSelectedItemIndexChange = { onLanguageChange(it - 1) },
|
||||
)
|
||||
val minRatingItems = stringArrayResource(id = R.array.search_min_rating)
|
||||
val minRatingItems = stringArrayResource(id = com.hippo.ehviewer.R.array.search_min_rating)
|
||||
val minRatingStr = stringResource(id = R.string.search_sr)
|
||||
DropdownFilterChip(
|
||||
label = minRatingStr,
|
||||
|
||||
@@ -22,7 +22,7 @@ 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 com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.hippo.ehviewer.client.parser.Torrent
|
||||
import com.hippo.ehviewer.client.parser.format
|
||||
|
||||
|
||||
@@ -21,10 +21,10 @@ import androidx.core.graphics.alpha
|
||||
import androidx.core.graphics.blue
|
||||
import androidx.core.graphics.green
|
||||
import androidx.core.graphics.red
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.ehviewer.core.ui.component.RollingNumber
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.asMutableState
|
||||
import com.hippo.ehviewer.ui.main.RollingNumber
|
||||
|
||||
@Composable
|
||||
fun ColorFilterSetting() = Column(modifier = Modifier.verticalScroll(rememberScrollState()).navigationBarsPadding()) {
|
||||
@@ -87,7 +87,7 @@ fun ColorFilterSetting() = Column(modifier = Modifier.verticalScroll(rememberScr
|
||||
}
|
||||
SpinnerChoice(
|
||||
title = stringResource(id = R.string.pref_color_filter_mode),
|
||||
entries = stringArrayResource(id = R.array.color_filter_modes),
|
||||
entries = stringArrayResource(id = com.hippo.ehviewer.R.array.color_filter_modes),
|
||||
values = arrayOf("0", "1", "2", "3", "4", "5"),
|
||||
field = Settings.colorFilterMode.asMutableState(),
|
||||
)
|
||||
|
||||
@@ -13,8 +13,8 @@ import android.webkit.MimeTypeMap
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.core.content.FileProvider
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.hippo.ehviewer.BuildConfig.APPLICATION_ID
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.client.EhUrl
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo
|
||||
import com.hippo.ehviewer.gallery.Page
|
||||
@@ -121,7 +121,7 @@ suspend fun save(page: Page) {
|
||||
try {
|
||||
ctx.contentResolver.delete(imageUri, null, null)
|
||||
} catch (e: Exception) {
|
||||
e.logcat(e)
|
||||
logcat("SavePage", e)
|
||||
}
|
||||
snackbar(cannotSave)
|
||||
} else if (isAtLeastQ) {
|
||||
|
||||
@@ -37,8 +37,9 @@ import coil3.DrawableImage
|
||||
import coil3.compose.AsyncImagePainter
|
||||
import coil3.compose.SubcomposeAsyncImage
|
||||
import coil3.compose.SubcomposeAsyncImageContent
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.ehviewer.core.ui.util.thenIf
|
||||
import com.google.accompanist.drawablepainter.DrawablePainter
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.collectAsState
|
||||
import com.hippo.ehviewer.gallery.Page
|
||||
@@ -47,7 +48,6 @@ import com.hippo.ehviewer.gallery.PageStatus
|
||||
import com.hippo.ehviewer.gallery.progressObserved
|
||||
import com.hippo.ehviewer.gallery.statusObserved
|
||||
import com.hippo.ehviewer.image.Image
|
||||
import com.hippo.ehviewer.ui.tools.thenIf
|
||||
import com.hippo.ehviewer.util.AdsPlaceholderFile
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.CombinedCircularProgressIndicator
|
||||
import kotlinx.coroutines.awaitCancellation
|
||||
|
||||
@@ -11,7 +11,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.stringArrayResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.asMutableState
|
||||
import com.hippo.ehviewer.util.isAtLeastP
|
||||
@@ -20,8 +20,8 @@ import com.hippo.ehviewer.util.isAtLeastP
|
||||
fun ReaderGeneralSetting() = Column(modifier = Modifier.verticalScroll(rememberScrollState()).navigationBarsPadding()) {
|
||||
SpinnerChoice(
|
||||
title = stringResource(id = R.string.pref_reader_theme),
|
||||
entries = stringArrayResource(id = R.array.reader_themes),
|
||||
values = stringArrayResource(id = R.array.reader_themes_values),
|
||||
entries = stringArrayResource(id = com.hippo.ehviewer.R.array.reader_themes),
|
||||
values = stringArrayResource(id = com.hippo.ehviewer.R.array.reader_themes_values),
|
||||
field = Settings.readerTheme.asMutableState(),
|
||||
)
|
||||
SwitchChoice(
|
||||
|
||||
@@ -17,7 +17,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringArrayResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.asMutableState
|
||||
import com.hippo.ehviewer.collectAsState
|
||||
@@ -27,13 +27,13 @@ import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
|
||||
fun ReaderModeSetting() = Column(modifier = Modifier.verticalScroll(rememberScrollState()).navigationBarsPadding()) {
|
||||
SpinnerChoice(
|
||||
title = stringResource(id = R.string.pref_category_reading_mode),
|
||||
entries = stringArrayResource(id = R.array.viewers_selector),
|
||||
entries = stringArrayResource(id = com.hippo.ehviewer.R.array.viewers_selector),
|
||||
values = arrayOf("0", "1", "2", "3", "4", "5"),
|
||||
field = Settings.readingMode.asMutableState(),
|
||||
)
|
||||
SpinnerChoice(
|
||||
title = stringResource(id = R.string.rotation_type),
|
||||
entries = stringArrayResource(id = R.array.rotation_type),
|
||||
entries = stringArrayResource(id = com.hippo.ehviewer.R.array.rotation_type),
|
||||
values = arrayOf("0", "8", "16", "24", "32", "40", "48"),
|
||||
field = Settings.orientationMode.asMutableState(),
|
||||
)
|
||||
@@ -60,7 +60,7 @@ private fun PagerSetting() = Column {
|
||||
val pagerNav = Settings.readerPagerNav.asMutableState()
|
||||
SpinnerChoice(
|
||||
title = stringResource(id = R.string.pref_viewer_nav),
|
||||
entries = stringArrayResource(id = R.array.pager_nav),
|
||||
entries = stringArrayResource(id = com.hippo.ehviewer.R.array.pager_nav),
|
||||
values = arrayOf("0", "1", "2", "3", "4", "5"),
|
||||
field = pagerNav,
|
||||
)
|
||||
@@ -68,7 +68,7 @@ private fun PagerSetting() = Column {
|
||||
Column {
|
||||
SpinnerChoice(
|
||||
title = stringResource(id = R.string.pref_read_with_tapping_inverted),
|
||||
entries = stringArrayResource(id = R.array.invert_tapping_mode),
|
||||
entries = stringArrayResource(id = com.hippo.ehviewer.R.array.invert_tapping_mode),
|
||||
values = arrayOf("0", "1", "2", "3"),
|
||||
field = Settings.readerPagerNavInverted.asMutableState(),
|
||||
)
|
||||
@@ -81,7 +81,7 @@ private fun PagerSetting() = Column {
|
||||
val scaleType = Settings.imageScaleType.asMutableState()
|
||||
SpinnerChoice(
|
||||
title = stringResource(id = R.string.pref_image_scale_type),
|
||||
entries = stringArrayResource(id = R.array.image_scale_type),
|
||||
entries = stringArrayResource(id = com.hippo.ehviewer.R.array.image_scale_type),
|
||||
values = arrayOf("1", "2", "3", "4", "5", "6"),
|
||||
field = scaleType,
|
||||
)
|
||||
@@ -93,7 +93,7 @@ private fun PagerSetting() = Column {
|
||||
}
|
||||
SpinnerChoice(
|
||||
title = stringResource(id = R.string.pref_zoom_start),
|
||||
entries = stringArrayResource(id = R.array.zoom_start),
|
||||
entries = stringArrayResource(id = com.hippo.ehviewer.R.array.zoom_start),
|
||||
values = arrayOf("1", "2", "3", "4"),
|
||||
field = Settings.zoomStart.asMutableState(),
|
||||
)
|
||||
@@ -113,22 +113,22 @@ private fun WebtoonSetting() = Column {
|
||||
val webtoonNav = Settings.readerWebtoonNav.asMutableState()
|
||||
SpinnerChoice(
|
||||
title = stringResource(id = R.string.pref_viewer_nav),
|
||||
entries = stringArrayResource(id = R.array.webtoon_nav),
|
||||
entries = stringArrayResource(id = com.hippo.ehviewer.R.array.webtoon_nav),
|
||||
values = arrayOf("0", "1", "2", "3", "4", "5"),
|
||||
field = webtoonNav,
|
||||
)
|
||||
AnimatedVisibility(visible = webtoonNav.value != 5) {
|
||||
SpinnerChoice(
|
||||
title = stringResource(id = R.string.pref_read_with_tapping_inverted),
|
||||
entries = stringArrayResource(id = R.array.invert_tapping_mode),
|
||||
entries = stringArrayResource(id = com.hippo.ehviewer.R.array.invert_tapping_mode),
|
||||
values = arrayOf("0", "1", "2", "3"),
|
||||
field = Settings.readerWebtoonNavInverted.asMutableState(),
|
||||
)
|
||||
}
|
||||
SpinnerChoice(
|
||||
title = stringResource(id = R.string.pref_webtoon_side_padding),
|
||||
entries = stringArrayResource(id = R.array.webtoon_side_padding),
|
||||
values = stringArrayResource(id = R.array.webtoon_side_padding_values),
|
||||
entries = stringArrayResource(id = com.hippo.ehviewer.R.array.webtoon_side_padding),
|
||||
values = stringArrayResource(id = com.hippo.ehviewer.R.array.webtoon_side_padding_values),
|
||||
field = Settings.webtoonSidePadding.asMutableState(),
|
||||
)
|
||||
SwitchChoice(
|
||||
|
||||
@@ -59,9 +59,14 @@ import arrow.core.Either
|
||||
import arrow.core.Either.Companion.catch
|
||||
import arrow.core.raise.ensure
|
||||
import arrow.core.right
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.ehviewer.core.ui.util.Await
|
||||
import com.ehviewer.core.ui.util.asyncInVM
|
||||
import com.ehviewer.core.ui.util.thenIf
|
||||
import com.ehviewer.core.util.launch
|
||||
import com.ehviewer.core.util.launchIO
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import com.hippo.ehviewer.EhDB
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.data.BaseGalleryInfo
|
||||
import com.hippo.ehviewer.collectAsState
|
||||
@@ -78,12 +83,9 @@ import com.hippo.ehviewer.ui.MainActivity
|
||||
import com.hippo.ehviewer.ui.Screen
|
||||
import com.hippo.ehviewer.ui.theme.EhTheme
|
||||
import com.hippo.ehviewer.ui.theme.scrim
|
||||
import com.hippo.ehviewer.ui.tools.Await
|
||||
import com.hippo.ehviewer.ui.tools.DialogState
|
||||
import com.hippo.ehviewer.ui.tools.asyncInVM
|
||||
import com.hippo.ehviewer.ui.tools.awaitInputText
|
||||
import com.hippo.ehviewer.ui.tools.dialog
|
||||
import com.hippo.ehviewer.ui.tools.thenIf
|
||||
import com.hippo.ehviewer.util.displayString
|
||||
import com.hippo.ehviewer.util.hasAds
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
@@ -94,7 +96,6 @@ import eu.kanade.tachiyomi.ui.reader.ReaderAppBars
|
||||
import eu.kanade.tachiyomi.ui.reader.ReaderContentOverlay
|
||||
import eu.kanade.tachiyomi.ui.reader.ReaderPageSheetMeta
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
import kotlin.coroutines.resume
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.awaitCancellation
|
||||
@@ -104,7 +105,6 @@ import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.serialization.Serializable
|
||||
import moe.tarsin.kt.unreachable
|
||||
import moe.tarsin.launch
|
||||
import moe.tarsin.string
|
||||
import okio.Path.Companion.toPath
|
||||
|
||||
@@ -377,7 +377,7 @@ fun ReaderScreen(pageLoader: PageLoader, info: BaseGalleryInfo?) {
|
||||
}
|
||||
}
|
||||
|
||||
context(ctx: Context, state: DialogState, nav: DestinationsNavigator)
|
||||
context(_: Context, _: DialogState, nav: DestinationsNavigator)
|
||||
suspend inline fun <T> usePageLoader(args: ReaderScreenArgs, crossinline block: suspend (PageLoader) -> T) = when (args) {
|
||||
is ReaderScreenArgs.Gallery -> {
|
||||
val info = args.info
|
||||
@@ -407,7 +407,7 @@ suspend inline fun <T> usePageLoader(args: ReaderScreenArgs, crossinline block:
|
||||
|
||||
@Composable
|
||||
private fun collectBackgroundColorAsState(): State<Color> {
|
||||
val grey = colorResource(R.color.reader_background_dark)
|
||||
val grey = colorResource(com.hippo.ehviewer.R.color.reader_background_dark)
|
||||
val dark = isSystemInDarkTheme()
|
||||
return Settings.readerTheme.collectAsState { theme ->
|
||||
when (theme) {
|
||||
|
||||
@@ -15,7 +15,7 @@ import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
private val tabs = intArrayOf(
|
||||
|
||||
@@ -25,7 +25,7 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.hippo.ehviewer.ui.tools.Slider
|
||||
import com.ehviewer.core.ui.component.Slider
|
||||
|
||||
@Composable
|
||||
fun SpinnerChoice(title: String, entries: Array<String>, values: Array<String>, field: MutableState<Int>) {
|
||||
|
||||
@@ -23,6 +23,8 @@ import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
|
||||
import androidx.compose.foundation.lazy.staggeredgrid.items
|
||||
import androidx.compose.foundation.text.input.rememberTextFieldState
|
||||
import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.DriveFileMove
|
||||
import androidx.compose.material.icons.automirrored.filled.Sort
|
||||
@@ -80,8 +82,25 @@ import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import arrow.core.partially1
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.ehviewer.core.ui.component.FAB_ANIMATE_TIME
|
||||
import com.ehviewer.core.ui.component.FabLayout
|
||||
import com.ehviewer.core.ui.component.FastScrollLazyColumn
|
||||
import com.ehviewer.core.ui.component.FastScrollLazyVerticalStaggeredGrid
|
||||
import com.ehviewer.core.ui.component.LocalSideSheetState
|
||||
import com.ehviewer.core.ui.component.ProvideSideSheetContent
|
||||
import com.ehviewer.core.ui.icons.EhIcons
|
||||
import com.ehviewer.core.ui.icons.big.Download
|
||||
import com.ehviewer.core.ui.util.Await
|
||||
import com.ehviewer.core.ui.util.asyncState
|
||||
import com.ehviewer.core.ui.util.rememberInVM
|
||||
import com.ehviewer.core.ui.util.thenIf
|
||||
import com.ehviewer.core.util.launch
|
||||
import com.ehviewer.core.util.launchIO
|
||||
import com.ehviewer.core.util.onEachLatest
|
||||
import com.ehviewer.core.util.withNonCancellableContext
|
||||
import com.ehviewer.core.util.withUIContext
|
||||
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.EhTagDatabase
|
||||
@@ -93,47 +112,30 @@ import com.hippo.ehviewer.download.DownloadManager.downloadInfoList
|
||||
import com.hippo.ehviewer.download.DownloadService
|
||||
import com.hippo.ehviewer.download.DownloadsFilterMode
|
||||
import com.hippo.ehviewer.download.SortMode
|
||||
import com.hippo.ehviewer.icons.EhIcons
|
||||
import com.hippo.ehviewer.icons.big.Download
|
||||
import com.hippo.ehviewer.ui.DrawerHandle
|
||||
import com.hippo.ehviewer.ui.LocalSideSheetState
|
||||
import com.hippo.ehviewer.ui.ProvideSideSheetContent
|
||||
import com.hippo.ehviewer.ui.Screen
|
||||
import com.hippo.ehviewer.ui.confirmRemoveDownloadRange
|
||||
import com.hippo.ehviewer.ui.main.DownloadCard
|
||||
import com.hippo.ehviewer.ui.main.FAB_ANIMATE_TIME
|
||||
import com.hippo.ehviewer.ui.main.FabLayout
|
||||
import com.hippo.ehviewer.ui.main.GalleryInfoGridItem
|
||||
import com.hippo.ehviewer.ui.main.plus
|
||||
import com.hippo.ehviewer.ui.navToReader
|
||||
import com.hippo.ehviewer.ui.showMoveDownloadLabelList
|
||||
import com.hippo.ehviewer.ui.tools.Await
|
||||
import com.hippo.ehviewer.ui.tools.DialogState
|
||||
import com.hippo.ehviewer.ui.tools.FastScrollLazyColumn
|
||||
import com.hippo.ehviewer.ui.tools.FastScrollLazyVerticalStaggeredGrid
|
||||
import com.hippo.ehviewer.ui.tools.HapticFeedbackType
|
||||
import com.hippo.ehviewer.ui.tools.asyncState
|
||||
import com.hippo.ehviewer.ui.tools.awaitConfirmationOrCancel
|
||||
import com.hippo.ehviewer.ui.tools.awaitInputText
|
||||
import com.hippo.ehviewer.ui.tools.awaitSelectAction
|
||||
import com.hippo.ehviewer.ui.tools.awaitSelectItemWithCheckBox
|
||||
import com.hippo.ehviewer.ui.tools.awaitSingleChoice
|
||||
import com.hippo.ehviewer.ui.tools.rememberHapticFeedback
|
||||
import com.hippo.ehviewer.ui.tools.rememberInVM
|
||||
import com.hippo.ehviewer.ui.tools.thenIf
|
||||
import com.hippo.ehviewer.util.mapToLongArray
|
||||
import com.hippo.ehviewer.util.takeAndClear
|
||||
import com.jamal.composeprefs3.ui.ifTrueThen
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import eu.kanade.tachiyomi.util.lang.withNonCancellableContext
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
import kotlin.math.roundToInt
|
||||
import kotlinx.coroutines.delay
|
||||
import moe.tarsin.coroutines.onEachLatest
|
||||
import moe.tarsin.launch
|
||||
import moe.tarsin.launchIO
|
||||
import moe.tarsin.navigate
|
||||
import sh.calvin.reorderable.ReorderableItem
|
||||
import sh.calvin.reorderable.rememberReorderableLazyListState
|
||||
@@ -451,6 +453,15 @@ fun AnimatedVisibilityScope.DownloadsScreen(navigator: DestinationsNavigator) =
|
||||
}
|
||||
}
|
||||
|
||||
val searchFieldState = rememberTextFieldState()
|
||||
class DownloadLabelSuggestion(private val label: String) : Suggestion() {
|
||||
override val keyword = LABEL_PREFIX + label
|
||||
override fun onClick() {
|
||||
searchFieldState.setTextAndPlaceCursorAtEnd(keyword)
|
||||
searchBarExpanded = false
|
||||
switchLabel(label)
|
||||
}
|
||||
}
|
||||
SearchBarScreen(
|
||||
onApplySearch = { filterState = filterState.copy(keyword = it) },
|
||||
expanded = searchBarExpanded,
|
||||
@@ -461,6 +472,15 @@ fun AnimatedVisibilityScope.DownloadsScreen(navigator: DestinationsNavigator) =
|
||||
},
|
||||
title = title,
|
||||
searchFieldHint = hint,
|
||||
searchFieldState = searchFieldState,
|
||||
suggestionProvider = { query ->
|
||||
val label = query.substringAfter(LABEL_PREFIX, "")
|
||||
if (label.isEmpty()) {
|
||||
emptyList()
|
||||
} else {
|
||||
EhDB.searchDownloadLabel(label, 10).map(::DownloadLabelSuggestion)
|
||||
}
|
||||
},
|
||||
searchBarOffsetY = { searchBarOffsetY },
|
||||
trailingIcon = {
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
@@ -523,7 +543,7 @@ fun AnimatedVisibilityScope.DownloadsScreen(navigator: DestinationsNavigator) =
|
||||
},
|
||||
) { contentPadding ->
|
||||
val height by collectListThumbSizeAsState()
|
||||
val realPadding = contentPadding + PaddingValues(dimensionResource(id = R.dimen.gallery_list_margin_h), dimensionResource(id = R.dimen.gallery_list_margin_v))
|
||||
val realPadding = contentPadding + PaddingValues(dimensionResource(id = com.hippo.ehviewer.R.dimen.gallery_list_margin_h), dimensionResource(id = com.hippo.ehviewer.R.dimen.gallery_list_margin_v))
|
||||
val searchBarConnection = remember {
|
||||
val slop = ViewConfiguration.get(contextOf<Context>()).scaledTouchSlop
|
||||
val topPaddingPx = with(density) { contentPadding.calculateTopPadding().roundToPx() }
|
||||
@@ -548,7 +568,7 @@ fun AnimatedVisibilityScope.DownloadsScreen(navigator: DestinationsNavigator) =
|
||||
|
||||
Crossfade(targetState = gridView, label = "Downloads") { showGridView ->
|
||||
if (showGridView) {
|
||||
val gridInterval = dimensionResource(R.dimen.gallery_grid_interval)
|
||||
val gridInterval = dimensionResource(com.hippo.ehviewer.R.dimen.gallery_grid_interval)
|
||||
val thumbColumns by Settings.thumbColumns.collectAsState()
|
||||
FastScrollLazyVerticalStaggeredGrid(
|
||||
columns = StaggeredGridCells.Fixed(thumbColumns),
|
||||
@@ -571,7 +591,7 @@ fun AnimatedVisibilityScope.DownloadsScreen(navigator: DestinationsNavigator) =
|
||||
FastScrollLazyColumn(
|
||||
modifier = Modifier.nestedScroll(searchBarConnection).fillMaxSize(),
|
||||
contentPadding = realPadding,
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.gallery_list_interval)),
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(com.hippo.ehviewer.R.dimen.gallery_list_interval)),
|
||||
) {
|
||||
items(list, key = { it.gid }) { info ->
|
||||
val checked = info.gid in checkedInfoMap
|
||||
@@ -660,7 +680,7 @@ fun AnimatedVisibilityScope.DownloadsScreen(navigator: DestinationsNavigator) =
|
||||
}
|
||||
onClick(Icons.AutoMirrored.Default.Sort) {
|
||||
val oldMode = SortMode.from(sortMode)
|
||||
val sortModes = contextOf<Context>().resources.getStringArray(R.array.download_sort_modes).toList()
|
||||
val sortModes = contextOf<Context>().resources.getStringArray(com.hippo.ehviewer.R.array.download_sort_modes).toList()
|
||||
val (selected, checked) = awaitSelectItemWithCheckBox(
|
||||
sortModes,
|
||||
R.string.sort_by,
|
||||
@@ -674,7 +694,7 @@ fun AnimatedVisibilityScope.DownloadsScreen(navigator: DestinationsNavigator) =
|
||||
invalidateKey = !invalidateKey
|
||||
}
|
||||
onClick(Icons.Default.FilterList) {
|
||||
val downloadStates = contextOf<Context>().resources.getStringArray(R.array.download_state).toList()
|
||||
val downloadStates = contextOf<Context>().resources.getStringArray(com.hippo.ehviewer.R.array.download_state).toList()
|
||||
val state = awaitSingleChoice(
|
||||
downloadStates,
|
||||
filterState.state + 1,
|
||||
@@ -712,3 +732,5 @@ fun AnimatedVisibilityScope.DownloadsScreen(navigator: DestinationsNavigator) =
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val LABEL_PREFIX = "label:"
|
||||
@@ -53,42 +53,42 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.ehviewer.core.ui.component.FAB_ANIMATE_TIME
|
||||
import com.ehviewer.core.ui.component.FabLayout
|
||||
import com.ehviewer.core.ui.component.LocalSideSheetState
|
||||
import com.ehviewer.core.ui.component.ProvideSideSheetContent
|
||||
import com.ehviewer.core.ui.icons.EhIcons
|
||||
import com.ehviewer.core.ui.icons.filled.GoTo
|
||||
import com.ehviewer.core.ui.util.asyncState
|
||||
import com.ehviewer.core.ui.util.thenIf
|
||||
import com.ehviewer.core.util.launch
|
||||
import com.ehviewer.core.util.onEachLatest
|
||||
import com.ehviewer.core.util.withUIContext
|
||||
import com.hippo.ehviewer.EhDB
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.EhEngine
|
||||
import com.hippo.ehviewer.client.data.BaseGalleryInfo
|
||||
import com.hippo.ehviewer.client.data.FavListUrlBuilder
|
||||
import com.hippo.ehviewer.collectAsState
|
||||
import com.hippo.ehviewer.icons.EhIcons
|
||||
import com.hippo.ehviewer.icons.filled.GoTo
|
||||
import com.hippo.ehviewer.ui.DrawerHandle
|
||||
import com.hippo.ehviewer.ui.LocalSideSheetState
|
||||
import com.hippo.ehviewer.ui.ProvideSideSheetContent
|
||||
import com.hippo.ehviewer.ui.Screen
|
||||
import com.hippo.ehviewer.ui.awaitSelectDate
|
||||
import com.hippo.ehviewer.ui.main.AvatarIcon
|
||||
import com.hippo.ehviewer.ui.main.FAB_ANIMATE_TIME
|
||||
import com.hippo.ehviewer.ui.main.FabLayout
|
||||
import com.hippo.ehviewer.ui.main.GalleryInfoGridItem
|
||||
import com.hippo.ehviewer.ui.main.GalleryInfoListItem
|
||||
import com.hippo.ehviewer.ui.main.GalleryList
|
||||
import com.hippo.ehviewer.ui.startDownload
|
||||
import com.hippo.ehviewer.ui.tools.asyncState
|
||||
import com.hippo.ehviewer.ui.tools.awaitConfirmationOrCancel
|
||||
import com.hippo.ehviewer.ui.tools.awaitSelectItem
|
||||
import com.hippo.ehviewer.ui.tools.thenIf
|
||||
import com.hippo.ehviewer.util.mapToLongArray
|
||||
import com.hippo.ehviewer.util.takeAndClear
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
import kotlin.math.roundToInt
|
||||
import kotlinx.coroutines.delay
|
||||
import moe.tarsin.coroutines.onEachLatest
|
||||
import moe.tarsin.coroutines.runSwallowingWithUI
|
||||
import moe.tarsin.launch
|
||||
import moe.tarsin.navigate
|
||||
|
||||
@Destination<RootGraph>
|
||||
|
||||
@@ -14,13 +14,13 @@ import androidx.paging.PagingState
|
||||
import androidx.paging.cachedIn
|
||||
import androidx.paging.filter
|
||||
import androidx.savedstate.compose.serialization.serializers.MutableStateSerializer
|
||||
import com.ehviewer.core.util.withIOContext
|
||||
import com.hippo.ehviewer.EhDB
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.EhEngine
|
||||
import com.hippo.ehviewer.client.data.BaseGalleryInfo
|
||||
import com.hippo.ehviewer.client.data.FavListUrlBuilder
|
||||
import com.hippo.ehviewer.ui.tools.foldToLoadResult
|
||||
import eu.kanade.tachiyomi.util.lang.withIOContext
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.map
|
||||
import moe.tarsin.coroutines.runSuspendCatching
|
||||
|
||||
@@ -51,7 +51,6 @@ import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
||||
import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults
|
||||
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableLongStateOf
|
||||
@@ -70,7 +69,6 @@ import androidx.compose.ui.layout.onGloballyPositioned
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.platform.LocalTextToolbar
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
@@ -84,7 +82,13 @@ import androidx.core.text.buildSpannedString
|
||||
import androidx.core.text.getSpans
|
||||
import androidx.core.text.inSpans
|
||||
import androidx.core.text.parseAsHtml
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.ehviewer.core.ui.util.animateFloatMergePredictiveBackAsState
|
||||
import com.ehviewer.core.ui.util.snackBarPadding
|
||||
import com.ehviewer.core.ui.util.thenIf
|
||||
import com.ehviewer.core.util.launch
|
||||
import com.ehviewer.core.util.launchIO
|
||||
import com.ehviewer.core.util.withUIContext
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.EhEngine
|
||||
import com.hippo.ehviewer.client.EhFilter.remember
|
||||
@@ -99,14 +103,11 @@ import com.hippo.ehviewer.ui.jumpToReaderByPage
|
||||
import com.hippo.ehviewer.ui.main.GalleryCommentCard
|
||||
import com.hippo.ehviewer.ui.main.NavigationIcon
|
||||
import com.hippo.ehviewer.ui.openBrowser
|
||||
import com.hippo.ehviewer.ui.tools.animateFloatMergePredictiveBackAsState
|
||||
import com.hippo.ehviewer.ui.tools.addBBCodeTextContextMenuItems
|
||||
import com.hippo.ehviewer.ui.tools.awaitConfirmationOrCancel
|
||||
import com.hippo.ehviewer.ui.tools.awaitSelectAction
|
||||
import com.hippo.ehviewer.ui.tools.normalizeSpan
|
||||
import com.hippo.ehviewer.ui.tools.rememberBBCodeTextToolbar
|
||||
import com.hippo.ehviewer.ui.tools.showNoButton
|
||||
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
|
||||
@@ -115,13 +116,9 @@ import com.hippo.ehviewer.util.displayString
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import kotlin.collections.forEach
|
||||
import kotlin.math.roundToInt
|
||||
import moe.tarsin.coroutines.runSuspendCatching
|
||||
import moe.tarsin.launch
|
||||
import moe.tarsin.launchIO
|
||||
import moe.tarsin.navigate
|
||||
import moe.tarsin.snackbar
|
||||
|
||||
@@ -298,7 +295,7 @@ fun AnimatedVisibilityScope.GalleryCommentsScreen(gid: Long, navigator: Destinat
|
||||
}
|
||||
},
|
||||
) { paddingValues ->
|
||||
val keylineMargin = dimensionResource(id = R.dimen.keyline_margin)
|
||||
val keylineMargin = dimensionResource(id = com.hippo.ehviewer.R.dimen.keyline_margin)
|
||||
var editTextMeasured by remember { mutableStateOf(MinimumContentPaddingEditText) }
|
||||
var isRefreshing by remember { mutableStateOf(false) }
|
||||
val refreshState = rememberPullToRefreshState()
|
||||
@@ -479,18 +476,15 @@ fun AnimatedVisibilityScope.GalleryCommentsScreen(gid: Long, navigator: Destinat
|
||||
},
|
||||
) {
|
||||
val color = MaterialTheme.colorScheme.onPrimaryContainer
|
||||
val toolbar = rememberBBCodeTextToolbar(userCommentBackField)
|
||||
CompositionLocalProvider(LocalTextToolbar provides toolbar) {
|
||||
BasicTextField(
|
||||
value = userComment,
|
||||
onValueChange = { textFieldValue ->
|
||||
userComment = textFieldValue.updateSpan(userComment)
|
||||
},
|
||||
modifier = Modifier.weight(1f).padding(keylineMargin),
|
||||
textStyle = MaterialTheme.typography.bodyLarge.merge(color = color),
|
||||
cursorBrush = SolidColor(MaterialTheme.colorScheme.primary),
|
||||
)
|
||||
}
|
||||
BasicTextField(
|
||||
value = userComment,
|
||||
onValueChange = { textFieldValue ->
|
||||
userComment = textFieldValue.updateSpan(userComment)
|
||||
},
|
||||
modifier = Modifier.weight(1f).padding(keylineMargin).addBBCodeTextContextMenuItems(userCommentBackField),
|
||||
textStyle = MaterialTheme.typography.bodyLarge.merge(color = color),
|
||||
cursorBrush = SolidColor(MaterialTheme.colorScheme.primary),
|
||||
)
|
||||
IconButton(
|
||||
onClick = { launchIO { sendComment() } },
|
||||
shapes = IconButtonDefaults.shapes(),
|
||||
|
||||
@@ -68,8 +68,24 @@ import androidx.paging.compose.itemKey
|
||||
import arrow.core.partially1
|
||||
import arrow.fx.coroutines.parMap
|
||||
import arrow.fx.coroutines.parZip
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.ehviewer.core.ui.component.CrystalCard
|
||||
import com.ehviewer.core.ui.component.FastScrollLazyVerticalGrid
|
||||
import com.ehviewer.core.ui.component.FilledTertiaryIconButton
|
||||
import com.ehviewer.core.ui.component.FilledTertiaryIconToggleButton
|
||||
import com.ehviewer.core.ui.icons.EhIcons
|
||||
import com.ehviewer.core.ui.icons.filled.Magnet
|
||||
import com.ehviewer.core.ui.util.LocalWindowSizeClass
|
||||
import com.ehviewer.core.ui.util.TransitionsVisibilityScope
|
||||
import com.ehviewer.core.ui.util.isExpanded
|
||||
import com.ehviewer.core.ui.util.rememberInVM
|
||||
import com.ehviewer.core.util.async
|
||||
import com.ehviewer.core.util.launch
|
||||
import com.ehviewer.core.util.launchIO
|
||||
import com.ehviewer.core.util.launchUI
|
||||
import com.ehviewer.core.util.withIOContext
|
||||
import com.ehviewer.core.util.withUIContext
|
||||
import com.hippo.ehviewer.EhDB
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.EhEngine
|
||||
import com.hippo.ehviewer.client.EhFilter.remember
|
||||
@@ -96,8 +112,6 @@ import com.hippo.ehviewer.dao.DownloadInfo
|
||||
import com.hippo.ehviewer.dao.Filter
|
||||
import com.hippo.ehviewer.dao.FilterMode
|
||||
import com.hippo.ehviewer.download.DownloadManager
|
||||
import com.hippo.ehviewer.icons.EhIcons
|
||||
import com.hippo.ehviewer.icons.filled.Magnet
|
||||
import com.hippo.ehviewer.ktbuilder.executeIn
|
||||
import com.hippo.ehviewer.ktbuilder.imageRequest
|
||||
import com.hippo.ehviewer.ui.GalleryInfoBottomSheet
|
||||
@@ -117,15 +131,9 @@ import com.hippo.ehviewer.ui.modifyFavorites
|
||||
import com.hippo.ehviewer.ui.navToReader
|
||||
import com.hippo.ehviewer.ui.openBrowser
|
||||
import com.hippo.ehviewer.ui.startDownload
|
||||
import com.hippo.ehviewer.ui.tools.CrystalCard
|
||||
import com.hippo.ehviewer.ui.tools.DialogState
|
||||
import com.hippo.ehviewer.ui.tools.FastScrollLazyVerticalGrid
|
||||
import com.hippo.ehviewer.ui.tools.FilledTertiaryIconButton
|
||||
import com.hippo.ehviewer.ui.tools.FilledTertiaryIconToggleButton
|
||||
import com.hippo.ehviewer.ui.tools.GalleryDetailRating
|
||||
import com.hippo.ehviewer.ui.tools.GalleryRatingBar
|
||||
import com.hippo.ehviewer.ui.tools.LocalWindowSizeClass
|
||||
import com.hippo.ehviewer.ui.tools.TransitionsVisibilityScope
|
||||
import com.hippo.ehviewer.ui.tools.awaitConfirmationOrCancel
|
||||
import com.hippo.ehviewer.ui.tools.awaitResult
|
||||
import com.hippo.ehviewer.ui.tools.awaitSelectAction
|
||||
@@ -135,8 +143,6 @@ import com.hippo.ehviewer.ui.tools.foldToLoadResult
|
||||
import com.hippo.ehviewer.ui.tools.getClippedRefreshKey
|
||||
import com.hippo.ehviewer.ui.tools.getLimit
|
||||
import com.hippo.ehviewer.ui.tools.getOffset
|
||||
import com.hippo.ehviewer.ui.tools.isExpanded
|
||||
import com.hippo.ehviewer.ui.tools.rememberInVM
|
||||
import com.hippo.ehviewer.ui.tools.showNoButton
|
||||
import com.hippo.ehviewer.util.FavouriteStatusRouter
|
||||
import com.hippo.ehviewer.util.addTextToClipboard
|
||||
@@ -144,8 +150,6 @@ import com.hippo.ehviewer.util.bgWork
|
||||
import com.hippo.ehviewer.util.displayString
|
||||
import com.hippo.ehviewer.util.flattenForEach
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import eu.kanade.tachiyomi.util.lang.withIOContext
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import io.ktor.http.encodeURLParameter
|
||||
import kotlin.coroutines.resume
|
||||
@@ -154,12 +158,8 @@ import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.CoroutineStart
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import moe.tarsin.async
|
||||
import moe.tarsin.coroutines.runSuspendCatching
|
||||
import moe.tarsin.coroutines.runSwallowingWithUI
|
||||
import moe.tarsin.launch
|
||||
import moe.tarsin.launchIO
|
||||
import moe.tarsin.launchUI
|
||||
import moe.tarsin.navigate
|
||||
import moe.tarsin.snackbar
|
||||
import moe.tarsin.string
|
||||
@@ -174,7 +174,7 @@ fun GalleryDetailContent(
|
||||
voteTag: VoteTag,
|
||||
modifier: Modifier,
|
||||
) {
|
||||
val keylineMargin = dimensionResource(R.dimen.keyline_margin)
|
||||
val keylineMargin = dimensionResource(com.hippo.ehviewer.R.dimen.keyline_margin)
|
||||
val galleryDetail = galleryInfo.asGalleryDetail()
|
||||
val windowSizeClass = LocalWindowSizeClass.current
|
||||
val thumbColumns by Settings.thumbColumns.collectAsState()
|
||||
@@ -262,8 +262,8 @@ fun GalleryDetailContent(
|
||||
columns = GridCells.Fixed(thumbColumns),
|
||||
contentPadding = contentPadding,
|
||||
modifier = modifier.padding(horizontal = keylineMargin),
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(id = R.dimen.strip_item_padding)),
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(id = R.dimen.strip_item_padding_v)),
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(id = com.hippo.ehviewer.R.dimen.strip_item_padding)),
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(id = com.hippo.ehviewer.R.dimen.strip_item_padding_v)),
|
||||
) {
|
||||
item(
|
||||
key = "header",
|
||||
@@ -325,8 +325,8 @@ fun GalleryDetailContent(
|
||||
columns = GridCells.Fixed(thumbColumns),
|
||||
contentPadding = contentPadding,
|
||||
modifier = modifier.padding(horizontal = keylineMargin),
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(id = R.dimen.strip_item_padding)),
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(id = R.dimen.strip_item_padding_v)),
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(id = com.hippo.ehviewer.R.dimen.strip_item_padding)),
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(id = com.hippo.ehviewer.R.dimen.strip_item_padding_v)),
|
||||
) {
|
||||
item(
|
||||
key = "header",
|
||||
@@ -340,7 +340,7 @@ fun GalleryDetailContent(
|
||||
onUploaderChipClick = ::onUploaderChipClick.partially1(galleryInfo),
|
||||
onBlockUploaderIconClick = ::showFilterUploaderDialog.partially1(galleryInfo),
|
||||
onCategoryChipClick = ::onCategoryChipClick,
|
||||
modifier = Modifier.width(dimensionResource(id = R.dimen.gallery_detail_card_landscape_width)).padding(vertical = keylineMargin),
|
||||
modifier = Modifier.width(dimensionResource(id = com.hippo.ehviewer.R.dimen.gallery_detail_card_landscape_width)).padding(vertical = keylineMargin),
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
@@ -443,7 +443,7 @@ fun BelowHeader(galleryDetail: GalleryDetail, voteTag: VoteTag) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = dimensionResource(id = R.dimen.strip_item_padding_v))
|
||||
.padding(bottom = dimensionResource(id = com.hippo.ehviewer.R.dimen.strip_item_padding_v))
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.clickable(onClick = ::navigateToCommentScreen),
|
||||
contentAlignment = Alignment.Center,
|
||||
@@ -463,7 +463,7 @@ fun BelowHeader(galleryDetail: GalleryDetail, voteTag: VoteTag) {
|
||||
navigate(info.gid asDstWith info.token)
|
||||
}
|
||||
}
|
||||
val keylineMargin = dimensionResource(R.dimen.keyline_margin)
|
||||
val keylineMargin = dimensionResource(com.hippo.ehviewer.R.dimen.keyline_margin)
|
||||
Spacer(modifier = Modifier.size(keylineMargin))
|
||||
if (galleryDetail.newerVersions.isNotEmpty()) {
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
@@ -765,7 +765,7 @@ fun BelowHeader(galleryDetail: GalleryDetail, voteTag: VoteTag) {
|
||||
Spacer(modifier = Modifier.size(keylineMargin))
|
||||
if (Settings.showComments.value) {
|
||||
GalleryDetailComment(galleryDetail.comments.comments)
|
||||
Spacer(modifier = Modifier.size(dimensionResource(id = R.dimen.strip_item_padding_v)))
|
||||
Spacer(modifier = Modifier.size(dimensionResource(id = com.hippo.ehviewer.R.dimen.strip_item_padding_v)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -784,7 +784,7 @@ private fun getRatingText(rating: Float): Int = when ((rating * 2).roundToInt())
|
||||
else -> R.string.rating_none
|
||||
}
|
||||
|
||||
private fun List<GalleryTagGroup>.artistTag() = find { (ns, _) -> ns == TagNamespace.Artist || ns == TagNamespace.Cosplayer }?.let { (ns, tags) -> "$ns:${tags[0].text}" }
|
||||
private fun List<GalleryTagGroup>.artistTag() = find { (ns, _) -> ns == TagNamespace.Artist || ns == TagNamespace.Cosplayer }?.let { (ns, tags) -> "${ns.value}:${tags[0].text}" }
|
||||
|
||||
@Composable
|
||||
context(_: Context)
|
||||
@@ -829,7 +829,7 @@ private fun GalleryDetail.collectPreviewItems() = rememberInVM(previewList) {
|
||||
}.flow.cachedIn(viewModelScope)
|
||||
}.collectAsLazyPagingItems()
|
||||
|
||||
context(ctx: Context)
|
||||
context(_: Context)
|
||||
private fun LazyGridScope.galleryPreview(detail: GalleryDetail, data: LazyPagingItems<GalleryPreview>, onClick: (Int) -> Unit) {
|
||||
val isV2Thumb = detail.previewList.first() is V2GalleryPreview
|
||||
items(
|
||||
|
||||
@@ -34,9 +34,15 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.util.fastJoinToString
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.ehviewer.core.ui.util.LocalWindowSizeClass
|
||||
import com.ehviewer.core.ui.util.isExpanded
|
||||
import com.ehviewer.core.ui.util.launchInVM
|
||||
import com.ehviewer.core.ui.util.rememberInVM
|
||||
import com.ehviewer.core.util.launchIO
|
||||
import com.ehviewer.core.util.withIOContext
|
||||
import com.hippo.ehviewer.EhApplication.Companion.imageCache
|
||||
import com.hippo.ehviewer.EhDB
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.EhEngine
|
||||
import com.hippo.ehviewer.client.EhUrl
|
||||
@@ -58,12 +64,8 @@ import com.hippo.ehviewer.ui.main.GalleryDetailErrorTip
|
||||
import com.hippo.ehviewer.ui.main.NavigationIcon
|
||||
import com.hippo.ehviewer.ui.navToReader
|
||||
import com.hippo.ehviewer.ui.openBrowser
|
||||
import com.hippo.ehviewer.ui.tools.LocalWindowSizeClass
|
||||
import com.hippo.ehviewer.ui.tools.awaitConfirmationOrCancel
|
||||
import com.hippo.ehviewer.ui.tools.awaitSelectTags
|
||||
import com.hippo.ehviewer.ui.tools.isExpanded
|
||||
import com.hippo.ehviewer.ui.tools.launchInVM
|
||||
import com.hippo.ehviewer.ui.tools.rememberInVM
|
||||
import com.hippo.ehviewer.util.AppHelper
|
||||
import com.hippo.ehviewer.util.awaitActivityResult
|
||||
import com.hippo.ehviewer.util.bgWork
|
||||
@@ -73,11 +75,9 @@ import com.hippo.files.toOkioPath
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import eu.kanade.tachiyomi.util.lang.withIOContext
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import kotlinx.serialization.Serializable
|
||||
import moe.tarsin.coroutines.runSuspendCatching
|
||||
import moe.tarsin.launchIO
|
||||
import moe.tarsin.snackbar
|
||||
import moe.tarsin.tip
|
||||
|
||||
@@ -249,7 +249,7 @@ fun AnimatedVisibilityScope.GalleryDetailScreen(args: GalleryDetailScreenArgs, n
|
||||
) {
|
||||
Text(text = stringResource(id = R.string.clear_image_cache_confirm))
|
||||
}
|
||||
(0..<gd.pages).forEach {
|
||||
repeat(gd.pages) {
|
||||
val key = getImageKey(gd.gid, it)
|
||||
imageCache.remove(key)
|
||||
}
|
||||
|
||||
@@ -69,8 +69,22 @@ import androidx.paging.LoadState
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import arrow.core.raise.ensure
|
||||
import arrow.core.raise.ensureNotNull
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.ehviewer.core.ui.component.FAB_ANIMATE_TIME
|
||||
import com.ehviewer.core.ui.component.FabLayout
|
||||
import com.ehviewer.core.ui.component.FastScrollLazyColumn
|
||||
import com.ehviewer.core.ui.component.LocalSideSheetState
|
||||
import com.ehviewer.core.ui.component.ProvideSideSheetContent
|
||||
import com.ehviewer.core.ui.icons.EhIcons
|
||||
import com.ehviewer.core.ui.icons.filled.GoTo
|
||||
import com.ehviewer.core.ui.util.Await
|
||||
import com.ehviewer.core.ui.util.asyncState
|
||||
import com.ehviewer.core.ui.util.thenIf
|
||||
import com.ehviewer.core.util.launch
|
||||
import com.ehviewer.core.util.launchIO
|
||||
import com.ehviewer.core.util.onEachLatest
|
||||
import com.ehviewer.core.util.withUIContext
|
||||
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.EhTagDatabase
|
||||
@@ -87,46 +101,32 @@ import com.hippo.ehviewer.client.parser.GalleryDetailUrlParser
|
||||
import com.hippo.ehviewer.client.parser.GalleryPageUrlParser
|
||||
import com.hippo.ehviewer.collectAsState
|
||||
import com.hippo.ehviewer.dao.QuickSearch
|
||||
import com.hippo.ehviewer.icons.EhIcons
|
||||
import com.hippo.ehviewer.icons.filled.GoTo
|
||||
import com.hippo.ehviewer.ui.DrawerHandle
|
||||
import com.hippo.ehviewer.ui.LocalSideSheetState
|
||||
import com.hippo.ehviewer.ui.ProvideSideSheetContent
|
||||
import com.hippo.ehviewer.ui.Screen
|
||||
import com.hippo.ehviewer.ui.awaitSelectDate
|
||||
import com.hippo.ehviewer.ui.destinations.ProgressScreenDestination
|
||||
import com.hippo.ehviewer.ui.doGalleryInfoAction
|
||||
import com.hippo.ehviewer.ui.main.AdvancedSearchOption
|
||||
import com.hippo.ehviewer.ui.main.AvatarIcon
|
||||
import com.hippo.ehviewer.ui.main.FAB_ANIMATE_TIME
|
||||
import com.hippo.ehviewer.ui.main.FabLayout
|
||||
import com.hippo.ehviewer.ui.main.GalleryInfoGridItem
|
||||
import com.hippo.ehviewer.ui.main.GalleryInfoListItem
|
||||
import com.hippo.ehviewer.ui.main.GalleryList
|
||||
import com.hippo.ehviewer.ui.main.SearchFilter
|
||||
import com.hippo.ehviewer.ui.tools.Await
|
||||
import com.hippo.ehviewer.ui.tools.DialogState
|
||||
import com.hippo.ehviewer.ui.tools.FastScrollLazyColumn
|
||||
import com.hippo.ehviewer.ui.tools.HapticFeedbackType
|
||||
import com.hippo.ehviewer.ui.tools.asyncState
|
||||
import com.hippo.ehviewer.ui.tools.awaitConfirmationOrCancel
|
||||
import com.hippo.ehviewer.ui.tools.awaitInputText
|
||||
import com.hippo.ehviewer.ui.tools.awaitInputTextWithCheckBox
|
||||
import com.hippo.ehviewer.ui.tools.rememberHapticFeedback
|
||||
import com.hippo.ehviewer.ui.tools.rememberMutableStateInDataStore
|
||||
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
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import com.ramcosta.composedestinations.spec.Direction
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.random.Random
|
||||
import kotlinx.coroutines.delay
|
||||
import moe.tarsin.coroutines.onEachLatest
|
||||
import moe.tarsin.launch
|
||||
import moe.tarsin.launchIO
|
||||
import moe.tarsin.navigate
|
||||
import moe.tarsin.snackbar
|
||||
import moe.tarsin.string
|
||||
@@ -194,8 +194,8 @@ fun AnimatedVisibilityScope.GalleryListScreen(
|
||||
FavouriteStatusRouter.Observe(data)
|
||||
val listMode by Settings.listMode.collectAsState()
|
||||
|
||||
val entries = stringArrayResource(id = R.array.toplist_entries)
|
||||
val values = stringArrayResource(id = R.array.toplist_values)
|
||||
val entries = stringArrayResource(id = com.hippo.ehviewer.R.array.toplist_entries)
|
||||
val values = stringArrayResource(id = com.hippo.ehviewer.R.array.toplist_values)
|
||||
val toplists = remember { entries zip values }
|
||||
val quickSearchName = getSuitableTitleForUrlBuilder(urlBuilder, false)
|
||||
var saveProgress by Settings.qSSaveProgress.asMutableState()
|
||||
@@ -482,10 +482,10 @@ fun AnimatedVisibilityScope.GalleryListScreen(
|
||||
searchFieldState = searchFieldState,
|
||||
suggestionProvider = {
|
||||
GalleryDetailUrlParser.parse(it, false)?.run {
|
||||
GalleryDetailUrlSuggestion(gid, token)
|
||||
listOf(GalleryDetailUrlSuggestion(gid, token))
|
||||
} ?: GalleryPageUrlParser.parse(it, false)?.run {
|
||||
GalleryPageUrlSuggestion(gid, pToken, page)
|
||||
}
|
||||
listOf(GalleryPageUrlSuggestion(gid, pToken, page))
|
||||
}.orEmpty()
|
||||
},
|
||||
localSearch = false,
|
||||
searchBarOffsetY = { searchBarOffsetY },
|
||||
|
||||
@@ -11,12 +11,12 @@ import androidx.paging.PagingSource
|
||||
import androidx.paging.PagingState
|
||||
import androidx.paging.cachedIn
|
||||
import androidx.savedstate.compose.serialization.serializers.MutableStateSerializer
|
||||
import com.ehviewer.core.util.withIOContext
|
||||
import com.hippo.ehviewer.client.EhEngine
|
||||
import com.hippo.ehviewer.client.data.BaseGalleryInfo
|
||||
import com.hippo.ehviewer.client.data.ListUrlBuilder
|
||||
import com.hippo.ehviewer.client.data.ListUrlBuilder.Companion.MODE_TOPLIST
|
||||
import com.hippo.ehviewer.ui.tools.foldToLoadResult
|
||||
import eu.kanade.tachiyomi.util.lang.withIOContext
|
||||
import moe.tarsin.coroutines.runSuspendCatching
|
||||
|
||||
class GalleryListViewModel(lub: ListUrlBuilder, savedStateHandle: SavedStateHandle) : ViewModel() {
|
||||
|
||||
@@ -42,21 +42,22 @@ import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import androidx.paging.compose.itemContentType
|
||||
import androidx.paging.compose.itemKey
|
||||
import androidx.paging.map
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.ehviewer.core.ui.component.FastScrollLazyColumn
|
||||
import com.ehviewer.core.ui.icons.EhIcons
|
||||
import com.ehviewer.core.ui.icons.big.History
|
||||
import com.ehviewer.core.ui.util.Await
|
||||
import com.ehviewer.core.ui.util.rememberInVM
|
||||
import com.ehviewer.core.ui.util.thenIf
|
||||
import com.ehviewer.core.util.launch
|
||||
import com.hippo.ehviewer.EhDB
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.collectAsState
|
||||
import com.hippo.ehviewer.icons.EhIcons
|
||||
import com.hippo.ehviewer.icons.big.History
|
||||
import com.hippo.ehviewer.ui.DrawerHandle
|
||||
import com.hippo.ehviewer.ui.Screen
|
||||
import com.hippo.ehviewer.ui.doGalleryInfoAction
|
||||
import com.hippo.ehviewer.ui.main.GalleryInfoListItem
|
||||
import com.hippo.ehviewer.ui.tools.Await
|
||||
import com.hippo.ehviewer.ui.tools.FastScrollLazyColumn
|
||||
import com.hippo.ehviewer.ui.tools.awaitConfirmationOrCancel
|
||||
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
|
||||
@@ -64,7 +65,6 @@ import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import kotlin.math.roundToInt
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.map
|
||||
import moe.tarsin.launch
|
||||
import moe.tarsin.navigate
|
||||
|
||||
@Destination<RootGraph>
|
||||
@@ -133,13 +133,13 @@ fun AnimatedVisibilityScope.HistoryScreen(navigator: DestinationsNavigator) = Sc
|
||||
}
|
||||
}
|
||||
}
|
||||
val marginH = dimensionResource(id = R.dimen.gallery_list_margin_h)
|
||||
val marginH = dimensionResource(id = com.hippo.ehviewer.R.dimen.gallery_list_margin_h)
|
||||
val cardHeight by collectListThumbSizeAsState()
|
||||
val showPages by Settings.showGalleryPages.collectAsState()
|
||||
FastScrollLazyColumn(
|
||||
modifier = Modifier.nestedScroll(searchBarConnection).fillMaxSize(),
|
||||
contentPadding = paddingValues,
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.gallery_list_interval)),
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(com.hippo.ehviewer.R.dimen.gallery_list_interval)),
|
||||
) {
|
||||
items(
|
||||
count = historyData.itemCount,
|
||||
|
||||
@@ -27,22 +27,22 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.ehviewer.core.ui.util.snackBarPadding
|
||||
import com.ehviewer.core.util.launch
|
||||
import com.ehviewer.core.util.launchUI
|
||||
import com.ehviewer.core.util.withIOContext
|
||||
import com.hippo.ehviewer.client.data.ListUrlBuilder
|
||||
import com.hippo.ehviewer.client.data.ListUrlBuilder.Companion.MODE_IMAGE_SEARCH
|
||||
import com.hippo.ehviewer.ui.Screen
|
||||
import com.hippo.ehviewer.ui.main.ImageSearch
|
||||
import com.hippo.ehviewer.ui.main.plus
|
||||
import com.hippo.ehviewer.ui.tools.snackBarPadding
|
||||
import com.hippo.ehviewer.util.pickVisualMedia
|
||||
import com.hippo.ehviewer.util.sha1
|
||||
import com.hippo.files.toOkioPath
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import eu.kanade.tachiyomi.util.lang.withIOContext
|
||||
import moe.tarsin.launch
|
||||
import moe.tarsin.launchUI
|
||||
import moe.tarsin.navigate
|
||||
import moe.tarsin.snackbar
|
||||
|
||||
@@ -50,8 +50,8 @@ import moe.tarsin.snackbar
|
||||
@Composable
|
||||
fun AnimatedVisibilityScope.ImageSearchScreen(navigator: DestinationsNavigator) = Screen(navigator) {
|
||||
val selectImageFirst = stringResource(R.string.select_image_first)
|
||||
val marginH = dimensionResource(id = R.dimen.gallery_list_margin_h)
|
||||
val marginV = dimensionResource(id = R.dimen.gallery_list_margin_v)
|
||||
val marginH = dimensionResource(id = com.hippo.ehviewer.R.dimen.gallery_list_margin_h)
|
||||
val marginV = dimensionResource(id = com.hippo.ehviewer.R.dimen.gallery_list_margin_v)
|
||||
var imageUri by rememberSaveable { mutableStateOf<Uri?>(null) }
|
||||
|
||||
Scaffold(
|
||||
@@ -87,19 +87,19 @@ fun AnimatedVisibilityScope.ImageSearchScreen(navigator: DestinationsNavigator)
|
||||
modifier = Modifier.padding(
|
||||
paddingValues = contentPadding + PaddingValues(marginH, marginV),
|
||||
).padding(
|
||||
vertical = dimensionResource(id = R.dimen.search_layout_margin_v),
|
||||
vertical = dimensionResource(id = com.hippo.ehviewer.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),
|
||||
horizontal = dimensionResource(id = com.hippo.ehviewer.R.dimen.search_category_padding_h),
|
||||
vertical = dimensionResource(id = com.hippo.ehviewer.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)),
|
||||
modifier = Modifier.height(dimensionResource(id = com.hippo.ehviewer.R.dimen.search_category_title_height)),
|
||||
fontWeight = FontWeight.Bold,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
|
||||
@@ -25,7 +25,7 @@ private fun urlToDestination(url: String): Direction? {
|
||||
}
|
||||
|
||||
@MainThread
|
||||
context(nav: DestinationsNavigator)
|
||||
context(_: DestinationsNavigator)
|
||||
fun navWithUrl(url: String): Boolean {
|
||||
val dest = urlToDestination(url) ?: return false
|
||||
navigate(dest)
|
||||
|
||||
@@ -21,16 +21,16 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.ehviewer.core.ui.icons.EhIcons
|
||||
import com.ehviewer.core.ui.icons.big.SadAndroid
|
||||
import com.ehviewer.core.util.withUIContext
|
||||
import com.hippo.ehviewer.client.EhEngine
|
||||
import com.hippo.ehviewer.icons.EhIcons
|
||||
import com.hippo.ehviewer.icons.big.SadAndroid
|
||||
import com.hippo.ehviewer.ui.Screen
|
||||
import com.hippo.ehviewer.util.displayString
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
import moe.tarsin.coroutines.runSuspendCatching
|
||||
import moe.tarsin.navigate
|
||||
|
||||
|
||||
@@ -65,8 +65,11 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.compose.ui.unit.coerceAtMost
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.ehviewer.core.ui.util.thenIf
|
||||
import com.ehviewer.core.util.launchIO
|
||||
import com.ehviewer.core.util.launchUI
|
||||
import com.hippo.ehviewer.EhApplication.Companion.searchDatabase
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.EhTagDatabase
|
||||
import com.hippo.ehviewer.client.data.TagNamespace
|
||||
@@ -79,12 +82,9 @@ import com.hippo.ehviewer.ui.theme.scrim
|
||||
import com.hippo.ehviewer.ui.tools.DialogState
|
||||
import com.hippo.ehviewer.ui.tools.awaitConfirmationOrCancel
|
||||
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 com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
import eu.kanade.tachiyomi.util.lang.launchUI
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
@@ -94,7 +94,7 @@ import kotlinx.coroutines.launch
|
||||
import moe.tarsin.navigate
|
||||
|
||||
fun interface SuggestionProvider {
|
||||
fun providerSuggestions(text: String): Suggestion?
|
||||
suspend fun providerSuggestions(text: String): List<Suggestion>
|
||||
}
|
||||
|
||||
abstract class Suggestion {
|
||||
@@ -164,7 +164,7 @@ fun SearchBarScreen(
|
||||
fun mergedSuggestionFlow(): Flow<Suggestion> = with(context) {
|
||||
flow {
|
||||
val query = searchFieldState.text.toString()
|
||||
suggestionProvider?.run { providerSuggestions(query)?.let { emit(it) } }
|
||||
suggestionProvider?.run { providerSuggestions(query).forEach { emit(it) } }
|
||||
mSearchDatabase.suggestions(query, 128).forEach { emit(KeywordSuggestion(it)) }
|
||||
val index = if (localSearch) query.lastIndexOf(' ') else query.lastIndexOfAny(TagTerminators)
|
||||
val keyword = query.substring(index + 1).trimStart()
|
||||
|
||||
@@ -21,9 +21,11 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.fromHtml
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.ehviewer.core.util.launch
|
||||
import com.ehviewer.core.util.withUIContext
|
||||
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.download.downloadLocation
|
||||
@@ -42,9 +44,7 @@ import com.hippo.files.delete
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
import moe.tarsin.coroutines.runSuspendCatching
|
||||
import moe.tarsin.launch
|
||||
import moe.tarsin.navigate
|
||||
import moe.tarsin.snackbar
|
||||
import moe.tarsin.string
|
||||
@@ -108,8 +108,8 @@ fun AnimatedVisibilityScope.AboutScreen(navigator: DestinationsNavigator) = Scre
|
||||
)
|
||||
SimpleMenuPreferenceInt(
|
||||
title = stringResource(id = R.string.auto_updates),
|
||||
entry = R.array.update_frequency,
|
||||
entryValueRes = R.array.update_frequency_values,
|
||||
entry = com.hippo.ehviewer.R.array.update_frequency,
|
||||
entryValueRes = com.hippo.ehviewer.R.array.update_frequency_values,
|
||||
state = Settings.updateIntervalDays.asMutableState(),
|
||||
)
|
||||
WorkPreference(title = stringResource(id = R.string.settings_about_check_for_updates)) {
|
||||
|
||||
@@ -30,9 +30,11 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.core.net.toUri
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.ehviewer.core.util.launch
|
||||
import com.ehviewer.core.util.withIOContext
|
||||
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.EhEngine
|
||||
@@ -57,7 +59,6 @@ import com.hippo.files.toOkioPath
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import eu.kanade.tachiyomi.util.lang.withIOContext
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import java.io.File
|
||||
import java.util.zip.ZipEntry
|
||||
@@ -67,7 +68,6 @@ import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.merge
|
||||
import me.zhanghai.compose.preference.DropdownListPreference
|
||||
import moe.tarsin.coroutines.runSuspendCatching
|
||||
import moe.tarsin.launch
|
||||
import moe.tarsin.snackbar
|
||||
import moe.tarsin.string
|
||||
|
||||
@@ -169,8 +169,8 @@ fun AnimatedVisibilityScope.AdvancedScreen(navigator: DestinationsNavigator) = S
|
||||
}
|
||||
SimpleMenuPreferenceInt(
|
||||
title = stringResource(id = R.string.settings_advanced_read_cache_size),
|
||||
entry = R.array.read_cache_size_entries,
|
||||
entryValueRes = R.array.read_cache_size_entry_values,
|
||||
entry = com.hippo.ehviewer.R.array.read_cache_size_entries,
|
||||
entryValueRes = com.hippo.ehviewer.R.array.read_cache_size_entry_values,
|
||||
state = Settings.readCacheSize.asMutableState(),
|
||||
)
|
||||
var currentLanguage by remember { mutableStateOf(getAppLanguage()) }
|
||||
|
||||
@@ -29,8 +29,10 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import arrow.fx.coroutines.parMap
|
||||
import arrow.fx.coroutines.parMapNotNull
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.ehviewer.core.util.launch
|
||||
import com.ehviewer.core.util.launchIO
|
||||
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.EhEngine.fillGalleryListByApi
|
||||
@@ -44,6 +46,7 @@ import com.hippo.ehviewer.download.DownloadManager
|
||||
import com.hippo.ehviewer.download.downloadDir
|
||||
import com.hippo.ehviewer.download.downloadLocation
|
||||
import com.hippo.ehviewer.spider.COMIC_INFO_FILE
|
||||
import com.hippo.ehviewer.spider.MIN_SPEED_LEVEL
|
||||
import com.hippo.ehviewer.spider.SpiderDen
|
||||
import com.hippo.ehviewer.spider.SpiderQueen.Companion.SPIDER_INFO_FILENAME
|
||||
import com.hippo.ehviewer.spider.readComicInfo
|
||||
@@ -72,8 +75,6 @@ import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import moe.tarsin.coroutines.runSuspendCatching
|
||||
import moe.tarsin.launch
|
||||
import moe.tarsin.launchIO
|
||||
import moe.tarsin.snackbar
|
||||
import moe.tarsin.string
|
||||
import okio.Path
|
||||
@@ -178,29 +179,21 @@ fun AnimatedVisibilityScope.DownloadScreen(navigator: DestinationsNavigator) = S
|
||||
SimpleMenuPreferenceInt(
|
||||
title = stringResource(id = R.string.settings_download_concurrency),
|
||||
summary = stringResource(id = R.string.settings_download_concurrency_summary, multiThreadDownload.value),
|
||||
entry = R.array.multi_thread_download_entries,
|
||||
entryValueRes = R.array.multi_thread_download_entry_values,
|
||||
entry = com.hippo.ehviewer.R.array.multi_thread_download_entries,
|
||||
entryValueRes = com.hippo.ehviewer.R.array.multi_thread_download_entry_values,
|
||||
state = multiThreadDownload,
|
||||
)
|
||||
val downloadDelay = Settings.downloadDelay.asMutableState()
|
||||
SimpleMenuPreferenceInt(
|
||||
title = stringResource(id = R.string.settings_download_download_delay),
|
||||
summary = stringResource(id = R.string.settings_download_download_delay_summary, downloadDelay.value),
|
||||
entry = R.array.download_delay_entries,
|
||||
entryValueRes = R.array.download_delay_entry_values,
|
||||
entry = com.hippo.ehviewer.R.array.download_delay_entries,
|
||||
entryValueRes = com.hippo.ehviewer.R.array.download_delay_entry_values,
|
||||
state = downloadDelay,
|
||||
)
|
||||
IntSliderPreference(
|
||||
maxValue = 10,
|
||||
minValue = 2,
|
||||
step = 7,
|
||||
title = stringResource(id = R.string.settings_download_connection_timeout),
|
||||
state = Settings.connTimeout.asMutableState(),
|
||||
)
|
||||
IntSliderPreference(
|
||||
maxValue = 10,
|
||||
minValue = 4,
|
||||
step = 5,
|
||||
minValue = MIN_SPEED_LEVEL,
|
||||
title = stringResource(id = R.string.settings_download_timeout_speed),
|
||||
state = Settings.timeoutSpeed.asMutableState(),
|
||||
display = ::speedLevelToSpeed,
|
||||
@@ -209,8 +202,8 @@ fun AnimatedVisibilityScope.DownloadScreen(navigator: DestinationsNavigator) = S
|
||||
SimpleMenuPreferenceInt(
|
||||
title = stringResource(id = R.string.settings_download_preload_image),
|
||||
summary = stringResource(id = R.string.settings_download_preload_image_summary, preloadImage.value),
|
||||
entry = R.array.preload_image_entries,
|
||||
entryValueRes = R.array.preload_image_entry_values,
|
||||
entry = com.hippo.ehviewer.R.array.preload_image_entries,
|
||||
entryValueRes = com.hippo.ehviewer.R.array.preload_image_entry_values,
|
||||
state = preloadImage,
|
||||
)
|
||||
SwitchPreference(
|
||||
|
||||
@@ -28,7 +28,8 @@ 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 com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.ehviewer.core.util.launch
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.asMutableState
|
||||
import com.hippo.ehviewer.client.EhCookieStore
|
||||
@@ -49,7 +50,6 @@ import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import kotlinx.datetime.LocalTime
|
||||
import moe.tarsin.launch
|
||||
import moe.tarsin.navigate
|
||||
import moe.tarsin.snackbar
|
||||
|
||||
@@ -94,7 +94,7 @@ fun AnimatedVisibilityScope.EhScreen(navigator: DestinationsNavigator) = Screen(
|
||||
val warning = stringResource(id = R.string.settings_eh_identity_cookies_signed)
|
||||
val state = rememberTextFieldState(cookies.joinToString("\n") { (k, v) -> "$k: $v" })
|
||||
Text(text = AnnotatedString.fromHtml(warning))
|
||||
Spacer(modifier = Modifier.size(dimensionResource(id = R.dimen.keyline_margin)))
|
||||
Spacer(modifier = Modifier.size(dimensionResource(id = com.hippo.ehviewer.R.dimen.keyline_margin)))
|
||||
OutlinedTextField(
|
||||
state = state,
|
||||
readOnly = true,
|
||||
@@ -119,8 +119,8 @@ fun AnimatedVisibilityScope.EhScreen(navigator: DestinationsNavigator) = Screen(
|
||||
val gallerySite = Settings.gallerySite.asMutableState()
|
||||
SimpleMenuPreferenceInt(
|
||||
title = stringResource(id = R.string.settings_eh_gallery_site),
|
||||
entry = R.array.gallery_site_entries,
|
||||
entryValueRes = R.array.gallery_site_entry_values,
|
||||
entry = com.hippo.ehviewer.R.array.gallery_site_entries,
|
||||
entryValueRes = com.hippo.ehviewer.R.array.gallery_site_entry_values,
|
||||
state = gallerySite,
|
||||
)
|
||||
Preference(
|
||||
@@ -161,8 +161,8 @@ fun AnimatedVisibilityScope.EhScreen(navigator: DestinationsNavigator) = Screen(
|
||||
}
|
||||
SimpleMenuPreferenceInt(
|
||||
title = stringResource(id = R.string.dark_theme),
|
||||
entry = R.array.night_mode_entries,
|
||||
entryValueRes = R.array.night_mode_values,
|
||||
entry = com.hippo.ehviewer.R.array.night_mode_entries,
|
||||
entryValueRes = com.hippo.ehviewer.R.array.night_mode_values,
|
||||
state = Settings.theme.asMutableState(),
|
||||
)
|
||||
SwitchPreference(
|
||||
@@ -175,15 +175,15 @@ fun AnimatedVisibilityScope.EhScreen(navigator: DestinationsNavigator) = Screen(
|
||||
)
|
||||
SimpleMenuPreferenceInt(
|
||||
title = stringResource(id = R.string.settings_eh_launch_page),
|
||||
entry = R.array.launch_page_entries,
|
||||
entryValueRes = R.array.launch_page_entry_values,
|
||||
entry = com.hippo.ehviewer.R.array.launch_page_entries,
|
||||
entryValueRes = com.hippo.ehviewer.R.array.launch_page_entry_values,
|
||||
state = Settings.launchPage.asMutableState(),
|
||||
)
|
||||
val listMode = Settings.listMode.asMutableState()
|
||||
SimpleMenuPreferenceInt(
|
||||
title = stringResource(id = R.string.settings_eh_list_mode),
|
||||
entry = R.array.list_mode_entries,
|
||||
entryValueRes = R.array.list_mode_entry_values,
|
||||
entry = com.hippo.ehviewer.R.array.list_mode_entries,
|
||||
entryValueRes = com.hippo.ehviewer.R.array.list_mode_entry_values,
|
||||
state = listMode,
|
||||
)
|
||||
AnimatedVisibility(visible = listMode.value == 0) {
|
||||
@@ -197,8 +197,8 @@ fun AnimatedVisibilityScope.EhScreen(navigator: DestinationsNavigator) = Screen(
|
||||
)
|
||||
SimpleMenuPreferenceInt(
|
||||
title = stringResource(id = R.string.settings_eh_detail_size),
|
||||
entry = R.array.detail_size_entries,
|
||||
entryValueRes = R.array.detail_size_entry_values,
|
||||
entry = com.hippo.ehviewer.R.array.detail_size_entries,
|
||||
entryValueRes = com.hippo.ehviewer.R.array.detail_size_entry_values,
|
||||
state = Settings.detailSize.asMutableState(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -53,7 +53,11 @@ import androidx.compose.ui.res.stringArrayResource
|
||||
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.ehviewer.core.i18n.R
|
||||
import com.ehviewer.core.ui.util.Await
|
||||
import com.ehviewer.core.ui.util.thenIf
|
||||
import com.ehviewer.core.util.async
|
||||
import com.ehviewer.core.util.launch
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.EhFilter
|
||||
import com.hippo.ehviewer.client.EhFilter.forget
|
||||
@@ -64,17 +68,13 @@ import com.hippo.ehviewer.dao.Filter
|
||||
import com.hippo.ehviewer.dao.FilterMode
|
||||
import com.hippo.ehviewer.ui.Screen
|
||||
import com.hippo.ehviewer.ui.main.NavigationIcon
|
||||
import com.hippo.ehviewer.ui.tools.Await
|
||||
import com.hippo.ehviewer.ui.tools.awaitConfirmationOrCancel
|
||||
import com.hippo.ehviewer.ui.tools.dialog
|
||||
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
|
||||
import kotlin.coroutines.resume
|
||||
import moe.tarsin.async
|
||||
import moe.tarsin.coroutines.groupByToObserved
|
||||
import moe.tarsin.launch
|
||||
|
||||
@Destination<RootGraph>
|
||||
@Composable
|
||||
@@ -88,7 +88,7 @@ fun AnimatedVisibilityScope.FilterScreen(navigator: DestinationsNavigator) = Scr
|
||||
fun addFilter() {
|
||||
launch {
|
||||
dialog { cont ->
|
||||
val types = stringArrayResource(id = R.array.filter_entries)
|
||||
val types = stringArrayResource(id = com.hippo.ehviewer.R.array.filter_entries)
|
||||
val type = rememberTextFieldState(types[0])
|
||||
val state = rememberTextFieldState()
|
||||
var error by remember { mutableStateOf<String?>(null) }
|
||||
|
||||
@@ -13,7 +13,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.hippo.ehviewer.ui.Screen
|
||||
import com.hippo.ehviewer.ui.main.NavigationIcon
|
||||
import com.hippo.ehviewer.ui.openBrowser
|
||||
@@ -37,7 +37,7 @@ fun AnimatedVisibilityScope.LicenseScreen(navigator: DestinationsNavigator) = Sc
|
||||
)
|
||||
},
|
||||
) { paddingValues ->
|
||||
val libraries by rememberLibraries(R.raw.aboutlibraries)
|
||||
val libraries by rememberLibraries(com.hippo.ehviewer.R.raw.aboutlibraries)
|
||||
LibrariesContainer(
|
||||
libraries = libraries,
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
|
||||
@@ -9,9 +9,9 @@ import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.google.accompanist.web.WebView
|
||||
import com.google.accompanist.web.rememberWebViewState
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.client.EhUrl
|
||||
import com.hippo.ehviewer.ui.Screen
|
||||
import com.hippo.ehviewer.ui.main.NavigationIcon
|
||||
|
||||
@@ -21,7 +21,7 @@ import androidx.compose.ui.res.integerArrayResource
|
||||
import androidx.compose.ui.res.stringArrayResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import com.hippo.ehviewer.ui.main.RollingNumber
|
||||
import com.ehviewer.core.ui.component.RollingNumber
|
||||
import com.hippo.ehviewer.ui.openBrowser
|
||||
import com.hippo.ehviewer.util.ProgressDialog
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
|
||||
@@ -12,8 +12,9 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.ehviewer.core.util.launch
|
||||
import com.hippo.ehviewer.EhApplication.Companion.searchDatabase
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.asMutableState
|
||||
import com.hippo.ehviewer.ui.Screen
|
||||
@@ -23,7 +24,6 @@ import com.hippo.ehviewer.ui.tools.awaitConfirmationOrCancel
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import moe.tarsin.launch
|
||||
import moe.tarsin.snackbar
|
||||
|
||||
@Destination<RootGraph>
|
||||
|
||||
@@ -16,9 +16,9 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.icons.EhIcons
|
||||
import com.hippo.ehviewer.icons.filled.SadPanda
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.ehviewer.core.ui.icons.EhIcons
|
||||
import com.ehviewer.core.ui.icons.filled.SadPanda
|
||||
import com.hippo.ehviewer.ui.Screen
|
||||
import com.hippo.ehviewer.ui.destinations.AboutScreenDestination
|
||||
import com.hippo.ehviewer.ui.destinations.AdvancedScreenDestination
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.hippo.ehviewer.ui.settings
|
||||
|
||||
import android.webkit.WebView
|
||||
import androidx.compose.animation.AnimatedVisibilityScope
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
@@ -16,10 +15,11 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.ehviewer.core.util.launch
|
||||
import com.google.accompanist.web.WebView
|
||||
import com.google.accompanist.web.rememberWebViewNavigator
|
||||
import com.google.accompanist.web.rememberWebViewState
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.client.EhCookieStore
|
||||
import com.hippo.ehviewer.client.EhUrl
|
||||
import com.hippo.ehviewer.ui.Screen
|
||||
@@ -28,7 +28,6 @@ import com.hippo.ehviewer.util.setDefaultSettings
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import moe.tarsin.launch
|
||||
import moe.tarsin.snackbar
|
||||
|
||||
private const val APPLY_JS = "javascript:(function(){var apply = document.getElementById(\"apply\").children[0];apply.click();})();"
|
||||
@@ -36,7 +35,7 @@ private const val APPLY_JS = "javascript:(function(){var apply = document.getEle
|
||||
@Destination<RootGraph>
|
||||
@Composable
|
||||
fun AnimatedVisibilityScope.UConfigScreen(navigator: DestinationsNavigator) = Screen(navigator) {
|
||||
val url = EhUrl.uConfigUrl
|
||||
val url = EhUrl.getUConfigUrl()
|
||||
val wvNavigator = rememberWebViewNavigator()
|
||||
Scaffold(
|
||||
topBar = {
|
||||
|
||||
@@ -18,9 +18,9 @@ import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.ehviewer.core.ui.component.scrollbarStyle
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.collectAsState
|
||||
import com.hippo.ehviewer.ui.tools.scrollbarStyle
|
||||
import com.hippo.ehviewer.util.isAtLeastOMR1
|
||||
import com.hippo.ehviewer.util.isAtLeastS
|
||||
import com.materialkolor.dynamicColorScheme
|
||||
|
||||
+73
-113
@@ -1,18 +1,17 @@
|
||||
package com.hippo.ehviewer.ui.tools
|
||||
|
||||
import android.view.ActionMode
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.content.Context
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.text.contextmenu.builder.item
|
||||
import androidx.compose.foundation.text.contextmenu.data.TextContextMenuKeys
|
||||
import androidx.compose.foundation.text.contextmenu.modifier.appendTextContextMenuComponents
|
||||
import androidx.compose.foundation.text.contextmenu.modifier.filterTextContextMenuComponents
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.geometry.Rect
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.platform.TextToolbar
|
||||
import androidx.compose.ui.platform.TextToolbarStatus
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.TextRange
|
||||
@@ -25,8 +24,7 @@ import androidx.compose.ui.text.input.getTextAfterSelection
|
||||
import androidx.compose.ui.text.input.getTextBeforeSelection
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.ui.MainActivity
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.hippo.ehviewer.util.toRangeSet
|
||||
import io.github.petertrr.diffutils.diffInline
|
||||
import io.github.petertrr.diffutils.patch.ChangeDelta
|
||||
@@ -36,8 +34,7 @@ import io.github.petertrr.diffutils.patch.InsertDelta
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import moe.tarsin.kt.unreachable
|
||||
|
||||
typealias PlatformRect = android.graphics.Rect
|
||||
import moe.tarsin.string
|
||||
|
||||
fun TextFieldValue.updateSpan(origin: TextFieldValue): TextFieldValue {
|
||||
val oriSpan = origin.annotatedString.spanStyles
|
||||
@@ -141,118 +138,81 @@ fun AnnotatedString.toBBCode() = buildString {
|
||||
}
|
||||
|
||||
@Composable
|
||||
context(activity: MainActivity)
|
||||
fun rememberBBCodeTextToolbar(textFieldValue: MutableState<TextFieldValue>): TextToolbar {
|
||||
context(_: Context)
|
||||
fun Modifier.addBBCodeTextContextMenuItems(textFieldValue: MutableState<TextFieldValue>): Modifier {
|
||||
var tfv by textFieldValue
|
||||
val view = LocalView.current
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val toolbar = remember {
|
||||
object : TextToolbar {
|
||||
private var actionMode: ActionMode? = null
|
||||
private val callback = object : TextActionModeCallback() {
|
||||
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
|
||||
super.onCreateActionMode(mode, menu)
|
||||
activity.menuInflater.inflate(R.menu.context_comment, menu)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
|
||||
super.onPrepareActionMode(mode, menu)
|
||||
if (tfv.selection.collapsed) {
|
||||
menu.removeGroup(R.id.bbcode_group)
|
||||
return appendTextContextMenuComponents {
|
||||
if (tfv.selection.collapsed) return@appendTextContextMenuComponents
|
||||
BBCodeFormat.entries.forEach { item ->
|
||||
item(key = item, label = string(item.id)) {
|
||||
val capturedTfv = tfv
|
||||
val start = capturedTfv.selection.min
|
||||
val end = capturedTfv.selection.max
|
||||
val annotatedString = buildAnnotatedString {
|
||||
val len = capturedTfv.text.length
|
||||
fun addStyle(style: SpanStyle) {
|
||||
append(capturedTfv.annotatedString)
|
||||
addStyle(style, start, end)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
|
||||
val handled = super.onActionItemClicked(mode, item)
|
||||
if (handled) {
|
||||
return true
|
||||
}
|
||||
val capturedTfv = tfv
|
||||
val start = capturedTfv.selection.min
|
||||
val end = capturedTfv.selection.max
|
||||
val annotatedString = buildAnnotatedString {
|
||||
val len = capturedTfv.text.length
|
||||
fun addStyle(style: SpanStyle) {
|
||||
append(capturedTfv.annotatedString)
|
||||
addStyle(style, start, end)
|
||||
fun addTextDecoration(decoration: TextDecoration) {
|
||||
append(capturedTfv.getTextBeforeSelection(len))
|
||||
val ans = capturedTfv.getSelectedText()
|
||||
val spans = ans.spanStyles.filter {
|
||||
it.item.textDecoration == null || it.item.textDecoration == decoration
|
||||
}
|
||||
fun addTextDecoration(decoration: TextDecoration) {
|
||||
withStyle(SpanStyle(textDecoration = decoration)) {
|
||||
append(AnnotatedString(ans.text, spans))
|
||||
}
|
||||
append(capturedTfv.getTextAfterSelection(len))
|
||||
}
|
||||
when (item) {
|
||||
BBCodeFormat.Bold -> addStyle(SpanStyle(fontWeight = FontWeight.Bold))
|
||||
BBCodeFormat.Italic -> addStyle(SpanStyle(fontStyle = FontStyle.Italic))
|
||||
BBCodeFormat.Underline -> addTextDecoration(TextDecoration.Underline)
|
||||
BBCodeFormat.Strikethrough -> addTextDecoration(TextDecoration.LineThrough)
|
||||
BBCodeFormat.Url -> append(capturedTfv.annotatedString)
|
||||
BBCodeFormat.Clear -> {
|
||||
append(capturedTfv.getTextBeforeSelection(len))
|
||||
val ans = capturedTfv.getSelectedText()
|
||||
val spans = ans.spanStyles.filter {
|
||||
it.item.textDecoration == null || it.item.textDecoration == decoration
|
||||
}
|
||||
withStyle(SpanStyle(textDecoration = decoration)) {
|
||||
append(AnnotatedString(ans.text, spans))
|
||||
}
|
||||
append(capturedTfv.getSelectedText().text)
|
||||
append(capturedTfv.getTextAfterSelection(len))
|
||||
}
|
||||
when (item.itemId) {
|
||||
R.id.action_bold -> addStyle(SpanStyle(fontWeight = FontWeight.Bold))
|
||||
R.id.action_italic -> addStyle(SpanStyle(fontStyle = FontStyle.Italic))
|
||||
R.id.action_underline -> addTextDecoration(TextDecoration.Underline)
|
||||
R.id.action_strikethrough -> addTextDecoration(TextDecoration.LineThrough)
|
||||
R.id.action_url -> append(capturedTfv.annotatedString)
|
||||
R.id.action_clear -> {
|
||||
append(capturedTfv.getTextBeforeSelection(len))
|
||||
append(capturedTfv.getSelectedText().text)
|
||||
append(capturedTfv.getTextAfterSelection(len))
|
||||
}
|
||||
}
|
||||
}
|
||||
tfv = TextFieldValue(
|
||||
annotatedString = buildAnnotatedString {
|
||||
append(annotatedString)
|
||||
// Hacky: Trigger recomposition to hide text toolbar
|
||||
append(Char.MIN_VALUE)
|
||||
},
|
||||
selection = TextRange(end),
|
||||
)
|
||||
coroutineScope.launch {
|
||||
// Hacky: Let TextField recompose first
|
||||
delay(100)
|
||||
tfv = tfv.copy(annotatedString = annotatedString)
|
||||
}
|
||||
|
||||
mode.finish()
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onDestroyActionMode(mode: ActionMode) {
|
||||
actionMode = null
|
||||
}
|
||||
}
|
||||
|
||||
override var status = TextToolbarStatus.Hidden
|
||||
|
||||
override fun hide() {
|
||||
status = TextToolbarStatus.Hidden
|
||||
actionMode?.finish()
|
||||
actionMode = null
|
||||
}
|
||||
|
||||
override fun showMenu(
|
||||
rect: Rect,
|
||||
onCopyRequested: (() -> Unit)?,
|
||||
onPasteRequested: (() -> Unit)?,
|
||||
onCutRequested: (() -> Unit)?,
|
||||
onSelectAllRequested: (() -> Unit)?,
|
||||
) {
|
||||
callback.rect = rect
|
||||
callback.onCopyRequested = onCopyRequested
|
||||
callback.onCutRequested = onCutRequested
|
||||
callback.onPasteRequested = onPasteRequested
|
||||
callback.onSelectAllRequested = onSelectAllRequested
|
||||
if (actionMode == null) {
|
||||
status = TextToolbarStatus.Shown
|
||||
actionMode = view.startActionMode(callback, ActionMode.TYPE_FLOATING)
|
||||
} else {
|
||||
actionMode?.invalidate()
|
||||
tfv = TextFieldValue(
|
||||
annotatedString = buildAnnotatedString {
|
||||
append(annotatedString)
|
||||
// Hacky: Trigger recomposition to hide text toolbar
|
||||
append(Char.MIN_VALUE)
|
||||
},
|
||||
selection = TextRange(end),
|
||||
)
|
||||
coroutineScope.launch {
|
||||
// Hacky: Let TextField recompose first
|
||||
delay(100)
|
||||
tfv = tfv.copy(annotatedString = annotatedString)
|
||||
}
|
||||
close()
|
||||
}
|
||||
}
|
||||
}.filterTextContextMenuComponents {
|
||||
when (it.key) {
|
||||
TextContextMenuKeys.CutKey,
|
||||
TextContextMenuKeys.CopyKey,
|
||||
TextContextMenuKeys.PasteKey,
|
||||
TextContextMenuKeys.SelectAllKey,
|
||||
is BBCodeFormat,
|
||||
-> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
return toolbar
|
||||
}
|
||||
|
||||
private enum class BBCodeFormat(@StringRes val id: Int) {
|
||||
Bold(R.string.format_bold),
|
||||
Italic(R.string.format_italic),
|
||||
Underline(R.string.format_underline),
|
||||
Strikethrough(R.string.format_strikethrough),
|
||||
Url(R.string.format_url),
|
||||
Clear(R.string.format_plain),
|
||||
}
|
||||
@@ -4,11 +4,11 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisallowComposableCalls
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.datastore.dataStoreFile
|
||||
import androidx.datastore.preferences.core.PreferenceDataStoreFactory
|
||||
import androidx.datastore.preferences.core.byteArrayPreferencesKey
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.core.emptyPreferences
|
||||
import androidx.datastore.preferences.preferencesDataStoreFile
|
||||
import androidx.lifecycle.compose.LifecycleResumeEffect
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -22,7 +22,7 @@ import kotlinx.serialization.serializer
|
||||
import splitties.init.appCtx
|
||||
|
||||
val dataStoreScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||
val dataStore = PreferenceDataStoreFactory.create { appCtx.dataStoreFile("Remembered.preferences_pb") }
|
||||
val dataStore = PreferenceDataStoreFactory.create { appCtx.preferencesDataStoreFile("Remembered") }
|
||||
val dataStateFlow = dataStore.data.stateIn(dataStoreScope, SharingStarted.Eagerly, emptyPreferences())
|
||||
|
||||
// Find out how make this work with any generic `Serializable` type
|
||||
|
||||
@@ -93,7 +93,9 @@ import arrow.core.Either
|
||||
import arrow.core.raise.Raise
|
||||
import arrow.core.raise.either
|
||||
import arrow.core.right
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.ehviewer.core.ui.component.FastScrollLazyColumn
|
||||
import com.ehviewer.core.ui.component.LabeledCheckbox
|
||||
import com.hippo.ehviewer.client.EhTagDatabase
|
||||
import com.hippo.ehviewer.client.EhTagDatabase.suggestion
|
||||
import com.jamal.composeprefs3.ui.ifNotNullThen
|
||||
@@ -182,7 +184,7 @@ suspend fun <R> awaitResult(
|
||||
)
|
||||
}
|
||||
|
||||
context(ctx: Context, state: DialogState)
|
||||
context(_: Context, _: DialogState)
|
||||
suspend fun awaitSelectTags(): List<String> = dialog { cont ->
|
||||
val selected = remember { mutableStateListOf<String>() }
|
||||
val state = rememberTextFieldState()
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.ui.tools
|
||||
|
||||
import android.view.ActionMode
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.compose.ui.geometry.Rect
|
||||
|
||||
open class TextActionModeCallback(
|
||||
var rect: Rect = Rect.Zero,
|
||||
var onCopyRequested: (() -> Unit)? = null,
|
||||
var onPasteRequested: (() -> Unit)? = null,
|
||||
var onCutRequested: (() -> Unit)? = null,
|
||||
var onSelectAllRequested: (() -> Unit)? = null,
|
||||
) : ActionMode.Callback2() {
|
||||
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
|
||||
onCopyRequested?.let {
|
||||
addMenuItem(menu, MenuItemOption.Copy)
|
||||
}
|
||||
onPasteRequested?.let {
|
||||
addMenuItem(menu, MenuItemOption.Paste)
|
||||
}
|
||||
onCutRequested?.let {
|
||||
addMenuItem(menu, MenuItemOption.Cut)
|
||||
}
|
||||
onSelectAllRequested?.let {
|
||||
addMenuItem(menu, MenuItemOption.SelectAll)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// this method is called to populate new menu items when the actionMode was invalidated
|
||||
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
|
||||
updateMenuItems(menu)
|
||||
// should return true so that new menu items are populated
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
MenuItemOption.Copy.id -> onCopyRequested?.invoke()
|
||||
MenuItemOption.Paste.id -> onPasteRequested?.invoke()
|
||||
MenuItemOption.Cut.id -> onCutRequested?.invoke()
|
||||
MenuItemOption.SelectAll.id -> onSelectAllRequested?.invoke()
|
||||
else -> return false
|
||||
}
|
||||
mode.finish()
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onDestroyActionMode(mode: ActionMode) = Unit
|
||||
|
||||
override fun onGetContentRect(mode: ActionMode, view: View, outRect: PlatformRect) {
|
||||
outRect.set(rect.left.toInt(), rect.top.toInt(), rect.right.toInt(), rect.bottom.toInt())
|
||||
}
|
||||
|
||||
private fun updateMenuItems(menu: Menu) {
|
||||
addOrRemoveMenuItem(menu, MenuItemOption.Copy, onCopyRequested)
|
||||
addOrRemoveMenuItem(menu, MenuItemOption.Paste, onPasteRequested)
|
||||
addOrRemoveMenuItem(menu, MenuItemOption.Cut, onCutRequested)
|
||||
addOrRemoveMenuItem(menu, MenuItemOption.SelectAll, onSelectAllRequested)
|
||||
}
|
||||
|
||||
private fun addMenuItem(menu: Menu, item: MenuItemOption) {
|
||||
menu.add(0, item.id, item.order, item.titleResource)
|
||||
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM)
|
||||
}
|
||||
|
||||
private fun addOrRemoveMenuItem(
|
||||
menu: Menu,
|
||||
item: MenuItemOption,
|
||||
callback: (() -> Unit)?,
|
||||
) {
|
||||
when {
|
||||
callback != null && menu.findItem(item.id) == null -> addMenuItem(menu, item)
|
||||
callback == null && menu.findItem(item.id) != null -> menu.removeItem(item.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class MenuItemOption(val id: Int) {
|
||||
Copy(0),
|
||||
Paste(1),
|
||||
Cut(2),
|
||||
SelectAll(3),
|
||||
;
|
||||
|
||||
val titleResource: Int
|
||||
get() = when (this) {
|
||||
Copy -> android.R.string.copy
|
||||
Paste -> android.R.string.paste
|
||||
Cut -> android.R.string.cut
|
||||
SelectAll -> android.R.string.selectAll
|
||||
}
|
||||
|
||||
/**
|
||||
* This item will be shown before all items that have order greater than this value.
|
||||
*/
|
||||
val order = id
|
||||
}
|
||||
@@ -109,8 +109,8 @@ private suspend inline fun ghStatement(
|
||||
private val GithubTokenParts = arrayOf(
|
||||
"github",
|
||||
"pat",
|
||||
"11A4H2ACI0XXTRsYkt1Mps",
|
||||
"uJ5n8UbLv20j6Ue4k1x25Z7u63EaCdDNYooxSeI4GJaQYKXWDZEtTmDVwnh",
|
||||
"11A4H2ACI0iGDuL1O6wPYW",
|
||||
"OTFg8xaCNUwR1NHaJE1AT3LoYPfz6bouI7E7ReLf8GjIRFHCL5UsHL9EnWP",
|
||||
)
|
||||
|
||||
data class Release(
|
||||
|
||||
@@ -20,7 +20,7 @@ import android.content.Intent
|
||||
import android.widget.Toast
|
||||
import arrow.core.Either
|
||||
import arrow.core.getOrElse
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
|
||||
object AppHelper {
|
||||
fun share(from: Activity, text: String?): Boolean {
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
package com.hippo.ehviewer.util
|
||||
|
||||
import kotlin.concurrent.atomics.AtomicInt
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
|
||||
inline fun AtomicInt.loop(action: (Int) -> Unit): Nothing {
|
||||
contract { callsInPlace(action, InvocationKind.AT_LEAST_ONCE) }
|
||||
do {
|
||||
action(load())
|
||||
} while (true)
|
||||
}
|
||||
|
||||
inline fun <R> AtomicInt.update(function: (Int) -> Int, transform: (old: Int, new: Int) -> R): R {
|
||||
contract {
|
||||
callsInPlace(function, InvocationKind.AT_LEAST_ONCE)
|
||||
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
|
||||
}
|
||||
loop { cur ->
|
||||
val upd = function(cur)
|
||||
if (compareAndSet(cur, upd)) return transform(cur, upd)
|
||||
}
|
||||
}
|
||||
|
||||
inline fun AtomicInt.updateAndGet(function: (Int) -> Int): Int {
|
||||
contract { callsInPlace(function, InvocationKind.AT_LEAST_ONCE) }
|
||||
return update(function) { _, new -> new }
|
||||
}
|
||||
@@ -24,7 +24,7 @@ import android.content.Context
|
||||
import android.os.Build
|
||||
import android.view.textclassifier.TextClassifier
|
||||
import androidx.core.os.persistableBundleOf
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
import com.hippo.ehviewer.ui.MainActivity
|
||||
import moe.tarsin.tip
|
||||
import splitties.systemservices.clipboardManager
|
||||
@@ -39,7 +39,7 @@ fun copyTextToClipboard(text: CharSequence?, isSensitive: Boolean) {
|
||||
)
|
||||
}
|
||||
|
||||
context(ctx: Context)
|
||||
context(_: Context)
|
||||
fun addTextToClipboard(text: CharSequence?, useToast: Boolean = false) {
|
||||
copyTextToClipboard(text, false)
|
||||
// Avoid double notify user since system have done that on Tiramisu above
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package com.hippo.ehviewer.util
|
||||
|
||||
import com.hippo.ehviewer.R
|
||||
import com.ehviewer.core.i18n.R
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import io.ktor.client.network.sockets.ConnectTimeoutException
|
||||
import io.ktor.client.network.sockets.SocketTimeoutException
|
||||
|
||||
Loaded 100 of 226 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user