Compare commits

..
19 Commits
Author SHA1 Message Date
FooIbar 70082090a1 Prepare for release 1.10.1
Releases / Build (push) Has been cancelled
2023-12-19 18:34:50 +08:00
FooIbar dbc1a32496 Update changelog for 1.10.1 (#526)
[no ci]
2023-12-19 18:34:04 +08:00
FooIbar e8c9ee5dd0 Cleanup obsolete logs 2023-12-19 16:41:33 +08:00
FooIbar 5c3657736b Dump heap on OOM 2023-12-19 16:41:33 +08:00
FooIbar 5191408d06 Provide dismiss action for snackbars with actions 2023-12-19 14:09:33 +08:00
FooIbar 347ce6854d Fix missing content warning cookie in guest mode 2023-12-19 11:17:58 +08:00
FooIbar f86dec9b2e Workaround unable to exit search field on API < 28 2023-12-19 09:46:24 +08:00
FooIbar c74b128b1a Debounce slider value changes 2023-12-19 09:46:13 +08:00
FooIbar 8ee28a4b4f Update history regardless of availability
In case the gallery is taken down by DMCA.
2023-12-18 13:56:01 +08:00
renovate[bot] 3b07b6997d Update dependency com.squareup.okhttp3:okhttp-bom to v5.0.0-alpha.12 2023-12-17 16:15:10 +00:00
FooIbar f59023ed03 Disable some unnecessary logging 2023-12-17 23:05:47 +08:00
FooIbar 3fc13e61c3 Refine download delay 2023-12-17 22:52:04 +08:00
FooIbar ae22f448a8 Revert to use OkHttp as fallback engine
This partially reverts commit 7b67210876

Reason for revert: `setApplicationProtocols` used by Apache 5 HC is not
available until API 29.
2023-12-17 19:12:56 +08:00
renovate[bot] 450c15d51e Update dependency com.squareup.okio:okio-jvm to v3.7.0 2023-12-17 01:17:50 +00:00
FooIbar d9e5bb4399 Fix unreleased lock 2023-12-17 01:03:57 +08:00
FooIbar 369904f659 Don't update page state on force retry 2023-12-16 16:35:49 +08:00
FooIbar 96302b2344 Log before making requests instead of after 2023-12-16 16:26:13 +08:00
FooIbar eeffb12e3b Cleanup code 2023-12-15 15:42:03 +08:00
FooIbar 57c12f7df0 Prepare for next iteration 2023-12-14 22:54:08 +08:00
44 changed files with 210 additions and 285 deletions

No files matched your search

+13
View File
@@ -1,5 +1,18 @@
# 变更日志
## 未发布
### 重大变更
* 由于 Apache HttpClient 5 在 Android 10 以下无法使用,后备 HTTP 引擎恢复为 OkHttp [#514](https://github.com/FooIbar/EhViewer/issues/514)
### Bug 修复
* 修复获取 pToken 失败导致无限加载 [#508](https://github.com/FooIbar/EhViewer/issues/508)
* 修复阅读页快速滑动滑块导致下载延时过多累计 [#512](https://github.com/FooIbar/EhViewer/issues/512)
* 修复 Android 9 以下的设备上无法退出搜索栏 [#513](https://github.com/FooIbar/EhViewer/issues/513)
## 1.10.0
### 重大变更
+2 -1
View File
@@ -75,7 +75,7 @@ and [Dynamic Color](https://m3.material.io/styles/color/dynamic-color/overview)
\* Archives and GIFs are not supported
** Fall back to Apache5 on devices that don't support [HttpEngine](https://developer.android.com/reference/android/net/http/HttpEngine)
** Fall back to OkHttp on devices that don't support [HttpEngine](https://developer.android.com/reference/android/net/http/HttpEngine)
<a href="https://github.com/FooIbar/EhViewer/releases">
<img alt="Get it on GitHub" src="https://github.com/Ehviewer-Overhauled/Art/blob/master/get-it-on-github.svg" width="200px"/>
@@ -95,6 +95,7 @@ Here is the libraries
- [Kotlin & KotlinX](https://kotlinlang.org/)
- [MDC-Android](https://github.com/material-components/material-components-android)
- [Material Icons](https://github.com/google/material-design-icons)
- [OkHttp](https://square.github.io/okhttp/)
- [Ktor](https://ktor.io/)
- [Coil](https://coil-kt.github.io/coil/)
- [Compose Destinations](https://composedestinations.rafaelcosta.xyz/)
+6 -2
View File
@@ -64,8 +64,8 @@ android {
applicationId = "moe.tarsin.ehviewer"
minSdk = 28
targetSdk = 34
versionCode = 180047
versionName = "1.10.0"
versionCode = 180048
versionName = "1.10.1"
resourceConfigurations.addAll(
listOf(
"zh",
@@ -235,6 +235,10 @@ dependencies {
implementation(libs.bundles.splitties)
// https://square.github.io/okhttp/changelogs/changelog/
implementation(platform(libs.okhttp.bom))
implementation(libs.bundles.okhttp)
implementation(libs.okio.jvm)
implementation(libs.diff)
-8
View File
@@ -14,14 +14,6 @@
# Ktor logger
-dontwarn org.slf4j.impl.StaticLoggerBinder
# Apache5 Http Client
-dontwarn org.ietf.jgss.GSSContext
-dontwarn org.ietf.jgss.GSSCredential
-dontwarn org.ietf.jgss.GSSException
-dontwarn org.ietf.jgss.GSSManager
-dontwarn org.ietf.jgss.GSSName
-dontwarn org.ietf.jgss.Oid
-dontwarn org.conscrypt.Conscrypt
-allowaccessmodification
-1
View File
@@ -139,7 +139,6 @@
android:launchMode="singleTask"
android:taskAffinity=""
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
<activity android:name=".ui.WebViewActivity" />
<service
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
@@ -51,15 +51,22 @@ import com.hippo.ehviewer.util.FavouriteStatusRouter
import com.hippo.ehviewer.util.FileUtils
import com.hippo.ehviewer.util.ReadableTime
import com.hippo.ehviewer.util.isAtLeastP
import com.hippo.ehviewer.util.isAtLeastQ
import com.hippo.ehviewer.util.isAtLeastS
import com.hippo.ehviewer.util.isCronetAvailable
import eu.kanade.tachiyomi.network.interceptor.UncaughtExceptionInterceptor
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.withUIContext
import eu.kanade.tachiyomi.util.system.logcat
import io.ktor.client.HttpClient
import io.ktor.client.engine.apache5.Apache5
import io.ktor.client.engine.okhttp.OkHttp
import io.ktor.client.plugins.cookies.HttpCookies
import java.io.File
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.launch
import moe.tarsin.coroutines.runSuspendCatching
import okhttp3.AsyncDns
import okhttp3.android.AndroidAsyncDns
import okio.Path.Companion.toOkioPath
import splitties.arch.room.roomDb
import splitties.init.appCtx
@@ -105,6 +112,22 @@ class EhApplication : Application(), SingletonImageLoader.Factory {
launchIO {
DownloadManager.isIdle
}
launchIO {
runSuspendCatching {
val files = mutableListOf<File>()
AppConfig.externalCrashDir?.listFiles()?.let { files.addAll(it) }
AppConfig.externalParseErrorDir?.listFiles()?.let { files.addAll(it) }
files.sortByDescending { it.lastModified() }
files.forEachIndexed { index, file ->
ensureActive()
if (index > 9) {
file.delete()
}
}
}.onFailure {
it.printStackTrace()
}
}
launchIO {
cleanupDownload()
}
@@ -178,7 +201,15 @@ class EhApplication : Application(), SingletonImageLoader.Factory {
}
}
} else {
HttpClient(Apache5) {
HttpClient(OkHttp) {
engine {
config {
if (isAtLeastQ) {
dns(AsyncDns.toDns(AndroidAsyncDns.IPv4, AndroidAsyncDns.IPv6))
}
addInterceptor(UncaughtExceptionInterceptor())
}
}
install(HttpCookies) {
storage = EhCookieStore
}
@@ -65,16 +65,18 @@ object EhCookieStore : CookiesStorage {
override fun close() = Unit
fun load(url: Url): List<Cookie> {
val cookies = manager.getCookie(url.toString()) ?: return emptyList()
val cookies = manager.getCookie(url.toString())
val checkTips = EhUrl.DOMAIN_E in url.host
return parseClientCookiesHeader(cookies)
.map { Cookie(it.key, it.value, maxAge = Int.MAX_VALUE) }
.filterTo(mutableListOf()) { it.name != KEY_UTMP_NAME }.apply {
if (checkTips) {
removeAll { it.name == KEY_CONTENT_WARNING }
add(sTipsCookie)
return cookies?.let {
parseClientCookiesHeader(cookies)
.map { Cookie(it.key, it.value, maxAge = Int.MAX_VALUE) }
.filterTo(mutableListOf()) { it.name != KEY_UTMP_NAME }.apply {
if (checkTips) {
removeAll { it.name == KEY_CONTENT_WARNING }
add(sTipsCookie)
}
}
}
} ?: if (checkTips) listOf(sTipsCookie) else emptyList()
}
override suspend fun get(requestUrl: Url) = load(requestUrl)
@@ -52,12 +52,10 @@ import com.hippo.ehviewer.dailycheck.showEventNotification
import com.hippo.ehviewer.util.AppConfig
import com.hippo.ehviewer.util.ReadableTime
import com.hippo.ehviewer.util.StatusCodeException
import eu.kanade.tachiyomi.util.system.logcat
import io.ktor.client.request.forms.append
import io.ktor.client.statement.HttpStatement
import io.ktor.client.statement.bodyAsChannel
import io.ktor.client.statement.bodyAsText
import io.ktor.client.statement.request
import io.ktor.http.ContentType
import io.ktor.utils.io.core.writeFully
import io.ktor.utils.io.pool.DirectByteBufferPool
@@ -79,8 +77,6 @@ import moe.tarsin.coroutines.runSuspendCatching
import org.jsoup.Jsoup
import splitties.init.appCtx
const val TAG = "EhEngine"
// https://ehwiki.org/wiki/API#Basics
private const val MAX_REQUEST_SIZE = 25
private const val MAX_SEQUENTIAL_REQUESTS = 5
@@ -145,7 +141,6 @@ fun rethrowExactly(code: Int, body: Either<String, ByteBuffer>, e: Throwable): N
val httpContentPool = DirectByteBufferPool(8, 0x80000)
suspend inline fun <T> HttpStatement.fetchUsingAsText(crossinline block: suspend String.() -> T) = execute { response ->
logcat(tag = TAG) { response.request.url.toString() }
val body = response.bodyAsText()
runSuspendCatching {
block(body)
@@ -155,7 +150,6 @@ suspend inline fun <T> HttpStatement.fetchUsingAsText(crossinline block: suspend
}
suspend inline fun <T> HttpStatement.fetchUsingAsByteBuffer(crossinline block: suspend ByteBuffer.() -> T) = execute { response ->
logcat(tag = TAG) { response.request.url.toString() }
httpContentPool.useInstance { buffer ->
with(response.bodyAsChannel()) { while (!isClosedForRead) readAvailable(buffer) }
buffer.flip()
@@ -18,6 +18,7 @@ package com.hippo.ehviewer.client
import com.hippo.ehviewer.EhApplication.Companion.ktorClient
import com.hippo.ehviewer.EhApplication.Companion.noRedirectKtorClient
import com.hippo.ehviewer.Settings
import eu.kanade.tachiyomi.util.system.logcat
import io.ktor.client.request.HttpRequestBuilder
import io.ktor.client.request.forms.FormBuilder
import io.ktor.client.request.forms.FormDataContent
@@ -67,11 +68,12 @@ suspend inline fun ehRequest(
url: String,
referer: String? = null,
origin: String? = null,
verbose: Boolean = true,
builder: HttpRequestBuilder.() -> Unit = {},
) = ktorClient.prepareRequest(url) {
applyEhConfig(referer, origin)
apply(builder)
}
}.also { if (verbose) logcat("EhRequest") { url } }
suspend inline fun noRedirectEhRequest(
url: String,
@@ -81,7 +83,7 @@ suspend inline fun noRedirectEhRequest(
) = noRedirectKtorClient.prepareRequest(url) {
applyEhConfig(referer, origin)
apply(builder)
}
}.also { logcat("EhRequest") { url } }
inline fun HttpRequestBuilder.formBody(builder: ParametersBuilder.() -> Unit) {
method = HttpMethod.Post
@@ -1,5 +0,0 @@
package com.hippo.ehviewer.client.exception
import com.hippo.ehviewer.R
class CloudflareBypassException : EhException(R.string.cloudflare_bypass_failed)
@@ -25,7 +25,7 @@ class KtorHttpUriFetcher(private val data: String, private val options: Options,
val diskCacheKey = options.diskCacheKey ?: data
val diskCache = requireNotNull(imageLoader.diskCache)
val snapshot = diskCache.openSnapshot(diskCacheKey) ?: run {
val success = ehRequest(data).execute {
val success = ehRequest(data, verbose = false).execute {
diskCache.suspendEdit(diskCacheKey) {
it.bodyAsChannel().copyTo(data.toFile())
}
@@ -384,16 +384,16 @@ class SpiderQueen private constructor(val galleryInfo: GalleryInfo) : CoroutineS
galleryInfo.gid,
galleryInfo.token!!,
)
return try {
return runSuspendCatching {
ehRequest(url, referer).fetchUsingAsText {
GalleryMultiPageViewerPTokenParser.parse(this).forEachIndexed { index, s ->
spiderInfo.pTokenMap[index] = s
}
spiderInfo.pTokenMap[index]
}
} catch (e: Throwable) {
ExceptionUtils.throwIfFatal(e)
return null
}.getOrElse {
it.printStackTrace()
null
}
}
@@ -416,13 +416,13 @@ class SpiderQueen private constructor(val galleryInfo: GalleryInfo) : CoroutineS
previewIndex,
false,
)
return try {
return runSuspendCatching {
ehRequest(url, referer).fetchUsingAsText {
readPreviews(this, previewIndex, spiderInfo)
spiderInfo.pTokenMap[index]
}
} catch (e: Throwable) {
ExceptionUtils.throwIfFatal(e)
}.getOrElse {
it.printStackTrace()
null
}
}
@@ -522,7 +522,7 @@ class SpiderQueen private constructor(val galleryInfo: GalleryInfo) : CoroutineS
private val mDownloadDelay = Settings.downloadDelay.milliseconds
private val downloadTimeout = Settings.downloadTimeout.seconds
private val delayLock = Mutex()
private var delayedTime = TimeSource.Monotonic.markNow()
private var lastRequestTime = TimeSource.Monotonic.markNow()
private var isDownloadMode = false
fun cancelDecode(index: Int) {
@@ -556,7 +556,7 @@ class SpiderQueen private constructor(val galleryInfo: GalleryInfo) : CoroutineS
val state = mPageStateArray[index]
if (!force && state == STATE_FINISHED) return
val currentJob = mFetcherJobMap[index]
if (force) currentJob?.cancel()
if (force) currentJob?.cancel(CancellationException(FORCE_RETRY))
if (currentJob?.isActive != true) {
mFetcherJobMap[index] = launch {
runCatching {
@@ -566,7 +566,9 @@ class SpiderQueen private constructor(val galleryInfo: GalleryInfo) : CoroutineS
}.onFailure {
if (it is CancellationException) {
Log.d(WORKER_DEBUG_TAG, "Download image $index cancelled")
updatePageState(index, STATE_FAILED, "Cancelled")
if (it.message != FORCE_RETRY) {
updatePageState(index, STATE_FAILED, "Cancelled")
}
throw it
}
updatePageState(index, STATE_FAILED, ExceptionUtils.getReadableString(it))
@@ -603,28 +605,23 @@ class SpiderQueen private constructor(val galleryInfo: GalleryInfo) : CoroutineS
}
}
val previousPToken: String?
val pToken: String
val pToken: String?
pTokenLock.withLock {
pToken = getPToken(index) ?: return updatePageState(
index,
STATE_FAILED,
PTOKEN_FAILED_MESSAGE,
).also {
// Non-local return will not release the lock
// https://github.com/Kotlin/kotlinx.coroutines/issues/3985
pToken = getPToken(index)
if (pToken == null) {
updatePageState(index, STATE_FAILED, PTOKEN_FAILED_MESSAGE)
mSpiderInfo.pTokenMap[index] = TOKEN_FAILED
}
previousPToken = getPToken(index - 1)
}
pToken ?: return
delayLock.withLock {
val new = delayedTime + mDownloadDelay
val duration = new.elapsedNow()
if (duration.isNegative()) {
delayedTime = new
delay(-duration)
} else {
delayedTime = TimeSource.Monotonic.markNow()
}
delay(mDownloadDelay - lastRequestTime.elapsedNow())
lastRequestTime = TimeSource.Monotonic.markNow()
}
var skipHathKey: String? = null
@@ -694,7 +691,6 @@ class SpiderQueen private constructor(val galleryInfo: GalleryInfo) : CoroutineS
referer = null
}
checkNotNull(targetImageUrl)
Log.d(WORKER_DEBUG_TAG, targetImageUrl)
repeat(3) { times ->
runCatching {
@@ -782,6 +778,7 @@ private val PTOKEN_FAILED_MESSAGE = appCtx.getString(R.string.error_get_ptoken_e
private val ERROR_TIMEOUT = appCtx.getString(R.string.error_timeout)
private val DECODE_ERROR = appCtx.getString(R.string.error_decoding_failed)
private val URL_509_PATTERN = Regex("\\.org/.+/509s?\\.gif")
private const val FORCE_RETRY = "Force retry"
private const val WORKER_DEBUG_TAG = "SpiderQueenWorker"
private fun check509(url: String) {
@@ -275,7 +275,7 @@ class MainActivity : EhActivity() {
LaunchedEffect(Unit) {
if (connectivityManager.isActiveNetworkMetered) {
if (isAtLeastQ) {
val ret = snackbarState.showSnackbar(warning, settings)
val ret = snackbarState.showSnackbar(warning, settings, true)
if (ret == SnackbarResult.ActionPerformed) {
val panelIntent = Intent(android.provider.Settings.Panel.ACTION_INTERNET_CONNECTIVITY)
startActivity(panelIntent)
@@ -304,7 +304,7 @@ class MainActivity : EhActivity() {
launch = { navController.navigate(ProgressScreenDestination(result2.gid, result2.pToken, result2.page)) }
}
launch?.let {
val ret = snackbarState.showSnackbar(snackMessage, snackAction)
val ret = snackbarState.showSnackbar(snackMessage, snackAction, true)
if (ret == SnackbarResult.ActionPerformed) it()
}
}
@@ -1,18 +0,0 @@
package com.hippo.ehviewer.ui
import android.os.Bundle
import com.hippo.ehviewer.ui.webview.WebViewScreen
class WebViewActivity : EhActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val url = intent.extras?.getString(KEY_URL) ?: return
setMD3Content {
WebViewScreen(url = url) { finish() }
}
}
companion object {
const val KEY_URL = "url"
}
}
@@ -259,7 +259,7 @@ fun SignInScreen(navigator: DestinationsNavigator) {
withStyle(
style = SpanStyle(textDecoration = TextDecoration.Underline),
) {
append(stringResource(id = R.string.tourist_mode))
append(stringResource(id = R.string.guest_mode))
}
},
)
@@ -356,7 +356,7 @@ fun SignInScreen(navigator: DestinationsNavigator) {
withStyle(
style = SpanStyle(textDecoration = TextDecoration.Underline),
) {
append(stringResource(id = R.string.tourist_mode))
append(stringResource(id = R.string.guest_mode))
}
},
)
@@ -1,6 +1,5 @@
package com.hippo.ehviewer.ui.main
import android.content.Intent
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -39,7 +38,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
@@ -51,17 +49,13 @@ import androidx.paging.compose.itemContentType
import androidx.paging.compose.itemKey
import com.hippo.ehviewer.R
import com.hippo.ehviewer.Settings
import com.hippo.ehviewer.client.EhUrl
import com.hippo.ehviewer.client.data.BaseGalleryInfo
import com.hippo.ehviewer.client.exception.CloudflareBypassException
import com.hippo.ehviewer.collectAsState
import com.hippo.ehviewer.icons.EhIcons
import com.hippo.ehviewer.icons.big.SadAndroid
import com.hippo.ehviewer.ui.WebViewActivity
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.ui.tools.LocalDialogState
import com.hippo.ehviewer.util.ExceptionUtils
import kotlinx.coroutines.CoroutineScope
@@ -81,7 +75,6 @@ fun GalleryList(
onRefresh: suspend CoroutineScope.() -> Unit,
onLoading: suspend CoroutineScope.() -> Unit,
) {
val context = LocalContext.current
val layoutDirection = LocalLayoutDirection.current
val marginH = dimensionResource(id = R.dimen.gallery_list_margin_h)
val marginV = dimensionResource(id = R.dimen.gallery_list_margin_v)
@@ -175,7 +168,6 @@ fun GalleryList(
}
}
val dialogState = LocalDialogState.current
when (val state = data.loadState.refresh) {
is LoadState.Loading -> if (!refreshState.isRefreshing && scrollToTopOnRefresh) {
LaunchedEffect(Unit) {
@@ -200,18 +192,6 @@ fun GalleryList(
is LoadState.Error -> {
Surface {
LaunchedEffect(state) {
if (state.error.cause is CloudflareBypassException) {
dialogState.awaitPermissionOrCancel(title = R.string.cloudflare_bypass_failed) {
Text(text = stringResource(id = R.string.open_in_webview))
}
context.startActivity(
Intent(context, WebViewActivity::class.java).apply {
putExtra(WebViewActivity.KEY_URL, EhUrl.host)
},
)
}
}
ErrorTip(
modifier = Modifier.widthIn(max = 228.dp)
.clip(ShapeDefaults.Small).clickable { data.retry() },
@@ -138,6 +138,7 @@ fun DownloadsScreen(navigator: DestinationsNavigator) {
val labelsList = DownloadManager.labelList
val newLabel = stringResource(R.string.new_label_title)
val renameLabel = stringResource(R.string.rename_label_title)
val labelsStr = stringResource(R.string.download_labels)
val labelEmpty = stringResource(R.string.label_text_is_empty)
val defaultInvalid = stringResource(R.string.label_text_is_invalid)
@@ -276,7 +277,7 @@ fun DownloadsScreen(navigator: DestinationsNavigator) {
IconButton(
onClick = {
coroutineScope.launch {
val new = dialogState.awaitInputText(title = newLabel, hint = labelsStr) { text ->
val new = dialogState.awaitInputText(title = renameLabel, hint = labelsStr) { text ->
when {
text.isBlank() -> labelEmpty
text == defaultName -> defaultInvalid
@@ -275,6 +275,7 @@ fun GalleryDetailScreen(args: GalleryDetailScreenArgs, navigator: DestinationsNa
}
}
}.onFailure {
galleryInfo?.let { info -> EhDB.putHistoryInfo(info.findBaseInfo()) }
getDetailError = ExceptionUtils.getReadableString(it)
}.getOrNull()
galleryDetail?.let {
@@ -3,6 +3,7 @@ package com.hippo.ehviewer.ui.screen
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.clickable
import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
@@ -19,6 +20,7 @@ import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.layout.union
import androidx.compose.foundation.layout.windowInsetsPadding
@@ -77,6 +79,7 @@ import com.hippo.ehviewer.ui.LocalNavDrawerState
import com.hippo.ehviewer.ui.LockDrawer
import com.hippo.ehviewer.ui.main.FAB_ANIMATE_TIME
import com.hippo.ehviewer.ui.tools.LocalDialogState
import com.hippo.ehviewer.util.isAtLeastP
import com.jamal.composeprefs3.ui.ifNotNullThen
import com.jamal.composeprefs3.ui.ifTrueThen
import eu.kanade.tachiyomi.util.lang.launchIO
@@ -233,15 +236,21 @@ fun SearchBarScreen(
Modifier
}
// Workaround IME not hiding after performing search
// https://github.com/FooIbar/EhViewer/pull/412
val view = LocalView.current
val keyboardManager = remember { SoftwareKeyboardControllerCompat(view) }
SideEffect {
if (!active) keyboardManager.hide()
}
Box(modifier = Modifier.fillMaxSize()) {
if (isAtLeastP) {
// Workaround IME not hiding after performing search
// https://github.com/FooIbar/EhViewer/pull/412
val view = LocalView.current
val keyboardManager = remember { SoftwareKeyboardControllerCompat(view) }
SideEffect {
if (!active) keyboardManager.hide()
}
} else {
// Workaround unable to exit search field
// https://android.googlesource.com/platform/frameworks/support/+/8f81d46e2c3f37564d0b7515ec9411314645e187
Box(Modifier.size(1.dp).focusable())
}
Scaffold(
topBar = {
// Placeholder, fill immutable SearchBar padding
@@ -289,8 +298,8 @@ fun SearchBarScreen(
}
active = it
},
title = title?.let { { Text(it, overflow = TextOverflow.Ellipsis) } },
placeholder = searchFieldHint?.let { { Text(it) } },
title = title.ifNotNullThen { Text(title!!, overflow = TextOverflow.Ellipsis) },
placeholder = searchFieldHint.ifNotNullThen { Text(searchFieldHint!!) },
leadingIcon = {
if (active) {
IconButton(onClick = { hideSearchView() }) {
@@ -142,7 +142,7 @@ fun AdvancedScreen(navigator: DestinationsNavigator) {
var enableCronet by Settings::enableCronet.observed
Preference(
title = stringResource(id = R.string.settings_advanced_http_engine),
summary = if (enableCronet) "Cronet" else "Apache 5",
summary = if (enableCronet) "Cronet" else "OkHttp",
) {
coroutineScope.launch {
dialogState.awaitPermissionOrCancel(
@@ -156,7 +156,7 @@ fun AdvancedScreen(navigator: DestinationsNavigator) {
onClick = { enableCronet = false },
shape = SegmentedButtonDefaults.itemShape(index = 0, count = 2),
) {
Text("Apache 5")
Text("OkHttp")
}
SegmentedButton(
selected = enableCronet,
@@ -84,12 +84,12 @@ fun EhScreen(navigator: DestinationsNavigator) {
},
snackbarHost = { SnackbarHost(snackbarHostState) },
) { paddingValues ->
val touristMode = stringResource(id = R.string.settings_eh_identity_cookies_tourist)
val guestMode = stringResource(id = R.string.settings_eh_identity_cookies_guest)
val copiedToClipboard = stringResource(id = R.string.copied_to_clipboard)
Column(modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection).verticalScroll(rememberScrollState()).padding(paddingValues)) {
Preference(
title = stringResource(id = R.string.account_name),
summary = Settings.displayName ?: touristMode,
summary = Settings.displayName ?: guestMode,
) {
val eCookies = EhCookieStore.load(Url(EhUrl.HOST_E))
val exCookies = EhCookieStore.load(Url(EhUrl.HOST_EX))
@@ -114,7 +114,7 @@ fun EhScreen(navigator: DestinationsNavigator) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) launchSnackBar(copiedToClipboard)
}
} else {
setMessage(touristMode)
setMessage(guestMode)
}
setPositiveButton(R.string.settings_eh_sign_out) { _, _ ->
EhUtils.signOut()
@@ -78,5 +78,9 @@ object Crash {
it.printStackTrace()
file.delete()
}
if (t is OutOfMemoryError) {
val dumpFile = File(dir, "hprof-$nowString.hprof")
Debug.dumpHprofData(dumpFile.absolutePath)
}
}
}
@@ -0,0 +1,28 @@
package eu.kanade.tachiyomi.network.interceptor
import java.io.IOException
import okhttp3.Interceptor
import okhttp3.Response
/**
* Catches any uncaught exceptions from later in the chain and rethrows as a non-fatal
* IOException to avoid catastrophic failure.
*
* This should be the first interceptor in the client.
*
* See [Interceptor](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-interceptor/index.html)
*/
class UncaughtExceptionInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
return try {
chain.proceed(chain.request())
} catch (e: Exception) {
if (e is IOException) {
throw e
} else {
throw IOException(e)
}
}
}
}
@@ -51,9 +51,11 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheetFix
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
@@ -110,6 +112,8 @@ import eu.kanade.tachiyomi.util.system.isNightMode
import java.io.File
import java.io.IOException
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.merge
@@ -322,6 +326,12 @@ class ReaderActivity : EhActivity() {
dialogState.Intercept()
val showSeekbar by Settings.showReaderSeekbar.collectAsState()
val sliderValueFlow = remember { MutableStateFlow(0) }
LaunchedEffect(sliderValueFlow) {
sliderValueFlow.drop(1).debounce(200).collect {
moveToPageIndex(it)
}
}
ReaderAppBars(
visible = menuVisible,
isRtl = isRtl,
@@ -330,7 +340,8 @@ class ReaderActivity : EhActivity() {
totalPages = totalPage,
onSliderValueChange = {
isScrollingThroughPages = true
moveToPageIndex(it)
sliderValueFlow.value = it
currentPage = it + 1
},
onClickSettings = { readerSettingSheetDialog?.show() },
)
@@ -6,7 +6,6 @@ import android.content.res.Resources
import android.graphics.Color
import android.os.Build
import android.provider.Settings
import android.util.Log
import android.view.Display
import android.view.View
import androidx.annotation.AttrRes
@@ -125,7 +124,3 @@ fun Context.createReaderThemeContext(): Context {
}
return this
}
inline fun Any.logcat(priority: Int = Log.DEBUG, tag: String? = null, message: () -> String) {
Log.println(priority, tag ?: javaClass.simpleName, message())
}
@@ -0,0 +1,11 @@
package eu.kanade.tachiyomi.util.system
import android.util.Log
inline fun logcat(tag: String, priority: Int = Log.DEBUG, message: () -> String) {
Log.println(priority, tag, message())
}
inline fun Any.logcat(priority: Int = Log.DEBUG, message: () -> String) {
Log.println(priority, javaClass.simpleName, message())
}
+2 -12
View File
@@ -59,21 +59,12 @@
<string name="gallery_list_search_bar_hint_exhentai">ExHentai durchsuchen</string>
<string name="gallery_list_search_bar_hint_e_hentai">E-Hentai durchsuchen</string>
<string name="gallery_list_empty_hit">Die Welt ist so groß und der Panda sitzt ganz allein</string>
<string name="search">Suchen</string>
<string name="keyword_search">Suche nach dem Schlüsselwort</string>
<string name="image_search">Suche nach einem Bild</string>
<string name="search_normal">Normale Suche</string>
<string name="search_advance">Erweiterte Optionen</string>
<string name="search_image">Suche nach einem Bild</string>
<string name="search_normal_search">Normale Suche</string>
<string name="search_subscription_search">Suche in gefolgten Etiketten</string>
<string name="search_specify_uploader">Einen Uploader spezifizieren</string>
<string name="search_specify_tag">Ein Etikett spezifizieren</string>
<string name="search_enable_advance">Erweitere Optionen aktivieren</string>
<string name="search_sh">Gelöschte Galerien anzeigen</string>
<string name="search_sto">Nur die Galerien mit Torrentdateien anzeigen</string>
<string name="search_sr">Mindestpunktzahl: </string>
<string name="search_sp">Seiten: </string>
<string name="search_sp_to">zu</string>
<string name="search_sf">Standard-Filter deaktivieren für: </string>
<string name="search_sfl">Sprache</string>
@@ -231,7 +222,6 @@
<string name="add_filter">Blocker erstellen</string>
<string name="filter_text">Blocker-Text</string>
<string name="filter_tip">Die blockierten Galerien wurde in EHentai nicht angezeigt.\n\nTitel-Blocker: die Galerien, deren Titel das Wort enthalten, ausschließen.\n\nUploader-Blocker: Die vom Uploader hochgeladen Galerien ausschließen.\n\nEtikett-Blocker: Die Galerien, die die Etikett haben, ausschließen. Es wird länger dauern, Galerieliste zu laden.\n\nEtikettnamensraum-Blocker: Die Galerien, die den Etikettnamensraum haben, ausschließen. Es wird länger dauern, Galerieliste zu laden.</string>
<string name="search_tip">Normale Suche: Nur durchsuchen.\n\nSuche in gefolgten Etiketten: Nur die Galerien, die die gefolgten Etiketten haben, durchsuchen.\n\nUploader spezifizieren: Die Galerien, die vom Uploader hochgeladen sind, auflisten. Andere Optionen werden ignoriert.\n\nEtikett spezifizieren: Die Galerien, die das Etikett haben, auflisten. Nur ein Etikett kann eingegeben werden. Andere Optionen werden ignoriert.</string>
<string name="u_config">EHentai-Einstellungen</string>
<string name="apply_tip">Zum Speichern der Einstellungen auf Häkchen tippen</string>
<string name="share_image">Das Bild teilen</string>
@@ -315,7 +305,7 @@
<string name="app_waring">Inhalt dieser App kommt aus dem Internet, von dem einige dir körperlichen oder emotionalen Schaden zufügen kann. Du hast es wirklich erfahrt und möchtest immer noch fortsetzen.</string>
<string name="error_username_cannot_empty">Gibt den Nutzername ein</string>
<string name="error_password_cannot_empty">Gibt das Passwort ein</string>
<string name="tourist_mode">Anmeldung überspringen</string>
<string name="guest_mode">Anmeldung überspringen</string>
<string name="sign_in_failed">Anmeldung fehlgeschlagen</string>
<string name="clipboard_gallery_url_snack_message">Ein Link von einen Galerie wurde in Zwischenablage erkennt.</string>
<string name="clipboard_gallery_url_snack_action">Gehen</string>
@@ -340,7 +330,7 @@
<string name="download_reset_reading_progress">Lesefortschritte zurücksetzen</string>
<string name="reset_reading_progress_message">Lesefortschritte aus allen heruntergeladenen Galerie zurücksetzen?</string>
<string name="settings_eh_identity_cookies_signed">Wenn jemand die folgenden Informationen hat, kann er sich als du anmelden. &lt;br&gt;&lt;b&gt;ALSO GEHEIM HALTEN!&lt;/b&gt;&lt;br&gt;&lt;br&gt;%s</string>
<string name="settings_eh_identity_cookies_tourist">Du hast dich noch nicht angemeldet.</string>
<string name="settings_eh_identity_cookies_guest">Du hast dich noch nicht angemeldet.</string>
<string name="settings_eh_identity_cookies_copy">Kopieren</string>
<string name="settings_eh_show_gallery_pages">Galerieseitenzahl anzeigen</string>
<string name="settings_eh_show_gallery_pages_summary">Die Galerieseitenzahle in der Galerieliste anzeigen</string>
+1 -13
View File
@@ -63,7 +63,7 @@
<string name="app_waring">El contenido de esta aplicación es de Internet. Algunas de ellas pueden causarle daño psicológico. Ahora sabes los riesgos anteriormente mencionado y te gustaría asumirlos</string>
<string name="error_username_cannot_empty">El usuario no puede estar vacío</string>
<string name="tourist_mode">Saltar inicio de sesión</string>
<string name="guest_mode">Saltar inicio de sesión</string>
<string name="sign_in_failed">Inicio de sesión fallido</string>
<string name="get_it">Entendido</string>
@@ -78,29 +78,17 @@
<string name="gallery_list_search_bar_hint_e_hentai">Buscar E-Hentai</string>
<string name="gallery_list_empty_hit">El mundo es grande y el panda está sentado solo</string>
<string name="search">Buscar</string>
<string name="keyword_search">Búsqueda por palabras clave</string>
<string name="image_search">Búsqueda de imagen</string>
<string name="search_normal">Búsqueda normal</string>
<string name="search_advance">Opciones avanzadas</string>
<string name="search_image">Búsqueda de imágenes</string>
<string name="search_normal_search">Búsqueda general</string>
<string name="search_specify_uploader">Uploader específicos</string>
<string name="search_specify_tag">Etiquetas específicas</string>
<string name="search_enable_advance">Habilitar opciones avanzadas</string>
<string name="search_tip">Búsqueda normal: Solo busca.\n\nBúsqueda de suscripción: Buscar etiquetas suscriptas.n\nUploader especifico: Listar de galerías subidas por el uploader. Otras opciones serán ignoradas.\n\nEtiquetas específicas: Listar las galerías que contienen la etiqueta. Solo se acepta una etiqueta. Otras opciones serán ignoradas.</string>
<string name="search_sh">Mostrar galerías borradas</string>
<string name="search_sto">Mostrar solo galerías con torrents</string>
<string name="search_sr">Valoración mínima:</string>
<string name="search_subscription_search">Búsqueda de etiquetas suscritas</string>
<string name="select_image">Seleccionar imagen</string>
<string name="search_uss">Usar búsqueda similar</string>
<string name="search_osc">Buscar solo portadas</string>
<string name="select_image_first">Seleccione la imagen primero</string>
<string name="search_sp">Páginas:</string>
<string name="search_sp_to">hasta</string>
<string name="search_sf">Deshabilitar filtros para:</string>
<string name="search_sfl">Lenguaje</string>
+2 -14
View File
@@ -64,7 +64,7 @@
<string name="app_waring">Le contenu de l\'application provient de l\'Internet, dont quelque chose peut vous causer le dommage physique ou mentale. Vous l\'avez bien compris et voulez continuer quand même.</string>
<string name="error_username_cannot_empty">Nom d\'ultilisateur ne doit pas être une chaîne vide</string>
<string name="error_password_cannot_empty">Mot de passe ne doit pas être une chaîne vide</string>
<string name="tourist_mode">Continuer sans se connecter</string>
<string name="guest_mode">Continuer sans se connecter</string>
<string name="sign_in_failed">Impossible de se connecter</string>
<string name="get_it">Compris</string>
@@ -81,26 +81,14 @@
<string name="gallery_list_empty_hit">Le monde est grand et le panda s\'assied tout seul.</string>
<string name="gallery_list_empty_hit_subscription">Vous pouvez suivre des tags à Paramètres -> EH -> Mes tags.</string>
<string name="search">Chercher</string>
<string name="keyword_search">Recherche du mot-clé</string>
<string name="image_search">Recherche d\'une image</string>
<string name="search_normal">Recherche normale</string>
<string name="search_advance">Options avancées</string>
<string name="search_image">Recherche d\'une image</string>
<string name="search_subscription_search">Recherche dans les tags suivis</string>
<string name="search_normal_search">Chercher</string>
<string name="search_specify_uploader">Spécifier un uploader</string>
<string name="search_specify_tag">Spécifier un tag</string>
<string name="search_enable_advance">Activer options avancées</string>
<string name="search_tip">Recherche normale : faire recherche.\n\nRecherche dans les tags suivis : seulement rechercher dans les tags suivis.\n\nSpécifer un uploader : faire la liste des galeries mises en ligne par l\'uploader spécifié. Autres options sont ignorées.\n\nSpécifier un tag : faire la liste des galeries qui contiennent le tag spécifié. Vous ne pouvez spécifier qu\'un tag. Autres options sont ignorées.</string>
<string name="search_sh">Montrer les galeries supprimées</string>
<string name="search_sto">Seulement montrer galeries avec des fiches torrent</string>
<string name="search_sr">Minimum note :</string>
<string name="search_sp">Pages : </string>
<string name="search_sp_to">à</string>
<string name="search_sf">Désactiver le filtre par défaut pour les items suivants :</string>
<string name="search_sfl">Langue</string>
@@ -448,7 +436,7 @@
<string name="settings_advanced_app_language_title">Langue de l\'app (Language)</string>
<string name="app_language_system">Langue du système (défaut)</string>
<string name="settings_eh_identity_cookies_signed">Tout le monde peut utiliser les cookies de ce compte pour s\'y connecter.&lt;br&gt;&lt;b&gt;GARDEZ-LES BIEN !&lt;/b&gt;&lt;br&gt;&lt;br&gt;%s</string>
<string name="settings_eh_identity_cookies_tourist">Vous ne vous êtes pas connecté.</string>
<string name="settings_eh_identity_cookies_guest">Vous ne vous êtes pas connecté.</string>
<string name="settings_eh_identity_cookies_copy">Copier</string>
<string name="settings_eh_show_gallery_pages">Montrer les nombres des pages des galeries</string>
<string name="settings_eh_show_gallery_pages_summary">Montrer le nombre des pages des galerie dans la liste des galeries</string>
+2 -14
View File
@@ -62,7 +62,7 @@
<string name="app_waring">このアプリの内容はインターネットから取得したものです。その内容の一部は使用者のお体や心理に傷つける恐れがあります。それでも続行しますか?</string>
<string name="error_username_cannot_empty">ユーザー名を入力してください。</string>
<string name="error_password_cannot_empty">パスワードを入力してください。</string>
<string name="tourist_mode">ログインをスキップ</string>
<string name="guest_mode">ログインをスキップ</string>
<string name="sign_in_failed">ログインに失敗しました。</string>
<string name="get_it">了解</string>
@@ -79,26 +79,14 @@
<string name="gallery_list_empty_hit">何も見つかりませんでした。</string>
<string name="gallery_list_empty_hit_subscription">リストは空です。設定->EH->マイタグにてタグを購読することができます。</string>
<string name="search">検索</string>
<string name="keyword_search">キーワードで検索</string>
<string name="image_search">画像で検索</string>
<string name="search_normal">検索</string>
<string name="search_advance">その他のオプション</string>
<string name="search_image">画像検索</string>
<string name="search_normal_search">一般検索</string>
<string name="search_subscription_search">購読検索</string>
<string name="search_specify_uploader">アップローダを検索</string>
<string name="search_specify_tag">タグを検索</string>
<string name="search_enable_advance">その他のオプションを有効化</string>
<string name="search_tip">普通検索:検索します。\n\n購読検索:購読しているタグで検索します。\n\nアップローダを検索:このアップローダーがアップロードしたギャラリーをリストします。他のオプションは無視されます。\n\nタグを検索:指定したタグを含むギャラリーをリストします。他のオプションは無視されます。</string>
<string name="search_sh">削除済みのギャラリーを表示</string>
<string name="search_sto">Torrentのあるギャラリーのみを表示</string>
<string name="search_sr">評価の下限:</string>
<string name="search_sp">ページ:</string>
<string name="search_sp_to">から</string>
<string name="search_sp_suffix">まで</string>
<string name="search_sf">既定フィルターから下記のものを例外とする:</string>
@@ -446,7 +434,7 @@
<string name="download_service">ダウンロードサービス</string>
<string name="settings_eh_identity_cookies_signed">下記の情報を持つ誰でもこのアカウントにログインできますので&lt;br&gt;&lt;b&gt;絶対に他人に渡さないように!&lt;/b&gt;&lt;br&gt;&lt;br&gt;%s</string>
<string name="settings_eh_identity_cookies_tourist">まだログインしていません</string>
<string name="settings_eh_identity_cookies_guest">まだログインしていません</string>
<string name="settings_eh_identity_cookies_copy">コピー</string>
<string name="settings_eh_show_gallery_pages">ギャラリーページ数を表示</string>
<string name="settings_eh_show_gallery_pages_summary">リストにギャラリーのページの数量を表示します</string>
+2 -12
View File
@@ -66,7 +66,7 @@
<string name="app_waring">이 앱의 내용은 인터넷에서 받아옵니다. 이 앱을 사용하기 위해서는 받아온 내용 중 일부가 사용자에게 가할 수 있는 신체적 또는 정신적 피해를 감수해야 합니다.</string>
<string name="error_username_cannot_empty">사용자 이름은 비워둘 수 없습니다.</string>
<string name="error_password_cannot_empty">비밀번호는 비워둘 수 없습니다.</string>
<string name="tourist_mode">로그인 없이 사용</string>
<string name="guest_mode">로그인 없이 사용</string>
<string name="sign_in_failed">로그인 실패</string>
<string name="get_it">알겠습니다</string>
@@ -82,20 +82,10 @@
<string name="gallery_list_search_bar_open_gallery">갤러리 열기</string>
<string name="gallery_list_empty_hit">The World is Big and the panda sit alone</string>
<string name="search">검색</string>
<string name="keyword_search">키워드 검색</string>
<string name="image_search">이미지 검색</string>
<string name="search_normal">일반 검색</string>
<string name="search_advance">고급 옵션</string>
<string name="search_image">이미지 검색</string>
<string name="search_normal_search">일반 검색</string>
<string name="search_specify_uploader">업로더 지정</string>
<string name="search_specify_tag">태그 지정</string>
<string name="search_enable_advance">고급 옵션 사용</string>
<string name="search_tip">일반 검색: 그냥 검색합니다.\n\n업로더 지정: 해당 업로더가 업로드한 갤러리를 검색합니다. 다른 옵션은 무시됩니다.\n\n태그 지정: 해당 태그를 포함한 갤러리를 검색합니다. 하나의 태그만 지정할 수 있으며, 다른 옵션은 무시됩니다.</string>
<string name="search_sh">Expunged 갤러리 표시</string>
<string name="search_sto">토렌트 파일이 있는 갤러리만 검색</string>
@@ -274,7 +264,7 @@
<string name="settings_eh">EH</string>
<string name="settings_eh_sign_out">로그아웃</string>
<string name="settings_eh_identity_cookies_signed">식별용 쿠키를 사용하여 이 계정으로 로그인할 수 있습니다.&lt;br&gt;&lt;b&gt;안전하게 보관하세요.&lt;/b&gt;&lt;br&gt;&lt;br&gt;%s</string>
<string name="settings_eh_identity_cookies_tourist">로그인 상태가 아닙니다.</string>
<string name="settings_eh_identity_cookies_guest">로그인 상태가 아닙니다.</string>
<string name="settings_eh_identity_cookies_copy">복사</string>
<string name="settings_u_config">E-Hentai 설정</string>
<string name="settings_u_config_summary">E-Hentai 사이트의 설정 페이지를 엽니다.</string>
+2 -9
View File
@@ -30,24 +30,18 @@
<string name="no_torrents">Ingen torrenter</string>
<string name="search_sfl">Språk</string>
<string name="search_osc">Kun søk i omslag</string>
<string name="tourist_mode">Fortsett uten å logge inn</string>
<string name="guest_mode">Fortsett uten å logge inn</string>
<string name="select_scene">Hvilken galleriside vil du besøke\?</string>
<string name="favored_times">♥ %d</string>
<string name="torrent_count">Torrent (%d)</string>
<string name="share">Del</string>
<string name="gallery_list_search_bar_open_gallery">Åpne galleriet</string>
<string name="search_normal_search">Normalt søk</string>
<string name="search_subscription_search">Abonnementssøk</string>
<string name="search_sp_to">til</string>
<string name="search_sfu">Opplaster</string>
<string name="delete">Slett</string>
<string name="search">Søk</string>
<string name="image_search">Bildesøk</string>
<string name="search_normal">Normalt søk</string>
<string name="search_cover">Søk i omslag</string>
<string name="search_image">Bildesøk</string>
<string name="search_specify_uploader">Angi opplaster</string>
<string name="search_specify_tag">Angi etikett</string>
<string name="select_image">Velg bilde</string>
<string name="star_3">3 stjerner</string>
<string name="read">Les</string>
@@ -64,7 +58,6 @@
<string name="refresh">Gjenoppfrisk</string>
<string name="get_it">Skjønner</string>
<string name="keyword_search">Nøkkelordssøk</string>
<string name="search_sp">Sider:</string>
<string name="search_sft">Etiketter</string>
<string name="any">Ubegrenset</string>
<plurals name="page_count">
@@ -97,7 +90,7 @@
<string name="key_pages">Sider</string>
<string name="share_image">Del bilde</string>
<string name="settings_eh_sign_out">Logg ut</string>
<string name="settings_eh_identity_cookies_tourist">Turistmodus</string>
<string name="settings_eh_identity_cookies_guest">Turistmodus</string>
<string name="settings_eh_identity_cookies_copy">Kopier informasjonskapsler</string>
<string name="settings_about_version">Byggversjon</string>
<string name="settings_my_tags">Mine etiketter</string>
@@ -1,4 +1,3 @@
<resources>
<dimen name="bottom_sheet_width">480dp</dimen>
<dimen name="screen_edge_margin">24dp</dimen>
</resources>
+2 -12
View File
@@ -66,7 +66,7 @@
<string name="app_waring">เนื้อหาภายในแอปพลิเคชั่นนี้มาจากอินเทอร์เน็ตทั้งหมด อาจมีเนื้อหาบางอย่างที่มีผลต่อร่างกายหรือจิตใจของคุณ คุณจะต้องยอมรับความเสี่ยงที่ดังกล่าว และดำเนินการต่อไป</string>
<string name="error_username_cannot_empty">ชื่อผู้ใช้ห้ามเว้นว่างไว้</string>
<string name="error_password_cannot_empty">รหัสผ่านห้ามเว้นว่างไว้</string>
<string name="tourist_mode">ดำเนินการต่อโดยไม่ต้องลงชื่อเข้าระบบ</string>
<string name="guest_mode">ดำเนินการต่อโดยไม่ต้องลงชื่อเข้าระบบ</string>
<string name="sign_in_failed">การลงชื่อเข้าใช้ล้มเหลว</string>
<string name="get_it">เข้าใจแล้ว ไม่ต้องแสดงอีก</string>
@@ -82,20 +82,10 @@
<string name="gallery_list_search_bar_open_gallery">เปิดแกลเลอรี่</string>
<string name="gallery_list_empty_hit">โลกนั้นนะมันกว้างงงงงใหญ่ไพรศาล และแพนด้านั่งโดดเดี่ยวเดียวดายในโลกกลมๆ</string>
<string name="search">ค้นหา</string>
<string name="keyword_search">คีย์เวิร์ดในการค้นหา</string>
<string name="image_search">ค้นหาด้วยรูป</string>
<string name="search_normal">ค้นหาแบบธรรมดา</string>
<string name="search_advance">ตัวเลือกขั้นสูง</string>
<string name="search_image">ค้นหาด้วยรูปภาพ</string>
<string name="search_normal_search">ค้นหาแบบธรรมดา</string>
<string name="search_specify_uploader">ระบุผู้อัพโหลด</string>
<string name="search_specify_tag">ระบุป้ายกำกับ</string>
<string name="search_enable_advance">เปิดโหมดค้นหาขั้นสูง</string>
<string name="search_tip">ค้นหาแบบธรรมดา: ค้นหาแบบง๊ายง่าย\n\nระบุผู้อัพโหลด: ค้นหาแกลเลอรี่ที่อัพโหลดโดยระบุชื่อผู้ใช้ของคนอัพโหลด โดยตัวเลือกอื่นๆจะถูกข้ามไป\n\nระบุป้ายกำกับ: ค้นหาแกลเลอรี่ที่ถูกใส่ป้ายกำกับหรือแท็กไว้ในแต่ละแกลเลอรี่ ระบุได้เพียงหนึ่งป้ายกำกับ ตัวเลือกอื่นๆจะถูกข้ามไป</string>
<string name="search_sh">แสดงแกลเลอรี่ที่ถูกลบออก</string>
<string name="search_sto">แสดงผลเฉพาะแกลเลอรี่ที่มีไฟล์ Torrents</string>
@@ -289,7 +279,7 @@
<string name="settings_eh">EH</string>
<string name="settings_eh_sign_out">ออกจากระบบ</string>
<string name="settings_eh_identity_cookies_signed">คุกกี๊ยืนยันตัวตนสามารถใช้ลงชื่อเข้าสู่ระบบสำหรับบัญชีนี้เท่านั้น&lt;br&gt;&lt;b&gt;เก็บ รักษา ให้ ปลอดภัย&lt;/b&gt;&lt;br&gt;&lt;br&gt;%s</string>
<string name="settings_eh_identity_cookies_tourist">ยังไม่ลงชื่อเข้าสู่ระบบ</string>
<string name="settings_eh_identity_cookies_guest">ยังไม่ลงชื่อเข้าสู่ระบบ</string>
<string name="settings_eh_identity_cookies_copy">คัดลอก</string>
<string name="settings_u_config">การตั้งค่า EHentai</string>
<string name="settings_u_config_summary">การตั้งค่าบนเว็บไซต์ EHentai</string>
+1 -10
View File
@@ -42,21 +42,13 @@
<string name="error_invalid_number">Geçersiz numara</string>
<string name="error_username_cannot_empty">Kullanıcı adı boş bir metin olamaz</string>
<string name="error_password_cannot_empty">Şifre boş bir metin olamaz</string>
<string name="tourist_mode">Giriş yapmaksızın devam et</string>
<string name="guest_mode">Giriş yapmaksızın devam et</string>
<string name="sign_in_failed">Giriş başarısız</string>
<string name="get_it">Anladım</string>
<string name="gallery_list_empty_hit">Dünya büyük olmasına rağmen pandacık yalnız oturuyor</string>
<string name="search">Arayın</string>
<string name="keyword_search">Anahtar Kelime Arama</string>
<string name="image_search">Görsel Arama</string>
<string name="search_normal">Normal Arama</string>
<string name="search_advance">Gelişmiş Seçenekler</string>
<string name="search_image">Görsel Arama</string>
<string name="search_normal_search">Normal arama</string>
<string name="search_subscription_search">Abonelik araması</string>
<string name="search_specify_uploader">Belirli yükleyici</string>
<string name="search_specify_tag">Belirli etiket</string>
<string name="search_enable_advance">Gelişmiş seçenekleri etkinleştir</string>
<string name="search_sh">Yalnızca Silinen Galerileri Gösteri</string>
<string name="search_sto">Yalnızca Torrent\'li Galerileri Göster</string>
<string name="search_sr">En Az Derecelendirme:</string>
@@ -91,7 +83,6 @@
<string name="select_scene">Hangi galeri sitesini ziyaret etmek istiyorsunuz\?</string>
<string name="gallery_list_search_bar_hint_e_hentai">E-Hentai\'de Arayın</string>
<string name="gallery_list_empty_hit_subscription">Etiketlere Ayarlar-&gt;EH-&gt;Etiketlerim yerinden abone olun</string>
<string name="search_sp">Sayfa:</string>
<string name="duplicate_quick_search">Aynı bir hızlı arama zaten var. Adı \"%s\".</string>
<string name="search_sp_err1">Sayfa oranı en fazla 10un altında olamaz</string>
<string name="readme">BENIOKU</string>
+2 -14
View File
@@ -58,12 +58,10 @@
<string name="app_waring_2">继续使用即代表您同意上述条款。</string>
<string name="error_username_cannot_empty">用户名不可为空</string>
<string name="error_password_cannot_empty">密码不可为空</string>
<string name="tourist_mode">游客模式</string>
<string name="guest_mode">游客模式</string>
<string name="sign_in_failed">登录失败</string>
<string name="sign_in_failed_tip">若持续出错,请尝试“通过网页登录”或“通过 Cookie 登录”。</string>
<string name="get_it">知道了</string>
<string name="cloudflare_bypass_failed">未能绕过 Cloudflare 验证</string>
<string name="open_in_webview">在 WebView 中打开链接以通过验证?</string>
<!-- Select scene -->
<string name="select_scene">你想选择哪个画廊站点?</string>
<string name="select_scene_explain">e-hentai:对任何人开放\nexhentai:仅对登录用户开放</string>
@@ -74,22 +72,12 @@
<string name="gallery_list_search_bar_open_gallery">打开画廊</string>
<string name="gallery_list_empty_hit">什么都没有找到</string>
<string name="gallery_list_empty_hit_subscription">请至 设置-&gt;EH-&gt;我的标签 订阅标签</string>
<string name="search">搜索</string>
<string name="keyword_search">关键字搜索</string>
<string name="image_search">图片搜索</string>
<string name="search_normal">普通搜索</string>
<string name="search_advance">高级选项</string>
<string name="search_image">图片搜索</string>
<string name="search_normal_search">一般搜索</string>
<string name="search_subscription_search">订阅搜索</string>
<string name="search_specify_uploader">指定上传者</string>
<string name="search_specify_tag">指定标签</string>
<string name="search_enable_advance">启用高级选项</string>
<string name="search_tip">一般搜索:就是搜索。\n\n订阅搜素:在订阅中搜索。\n\n指定上传者:列出该上传者上传的画廊。其他选项会被忽略。\n\n指定标签:列出含有该标签的画廊。其他选项会被忽略。</string>
<string name="search_sh">已删除</string>
<string name="search_sto">有种子</string>
<string name="search_sr">最低评分:</string>
<string name="search_sp">页数:</string>
<string name="search_sp_to">到</string>
<string name="search_sp_err1">页数最大值至少为 10</string>
<string name="search_sp_err2">页数范围差至少为 20</string>
@@ -307,7 +295,7 @@
<string name="settings_eh">EH</string>
<string name="settings_eh_sign_out">退出登录</string>
<string name="settings_eh_identity_cookies_signed">身份 Cookie 可用于登录该账号。&lt;br&gt;&lt;b&gt;注意数据安全&lt;/b&gt;&lt;br&gt;&lt;br&gt;%s</string>
<string name="settings_eh_identity_cookies_tourist">游客模式</string>
<string name="settings_eh_identity_cookies_guest">游客模式</string>
<string name="settings_eh_identity_cookies_copy">复制 Cookies</string>
<string name="settings_u_config">EHentai 设置</string>
<string name="settings_u_config_summary">EHentai 网站上的设置</string>
+2 -14
View File
@@ -67,7 +67,7 @@
<string name="app_waring">本應用中內容來自互聯網,部分內容可能對您的生理及心理造成難以恢復的傷害。本應用作者不會對由本應用造成的任何後果負責。\n\n未成年人應在監護人指導下使用本應用。</string>
<string name="error_username_cannot_empty">用户名不可為空</string>
<string name="error_password_cannot_empty">密碼不可為空</string>
<string name="tourist_mode">跳過登錄</string>
<string name="guest_mode">跳過登錄</string>
<string name="sign_in_failed">登錄失敗</string>
<string name="sign_in_failed_tip">若持續出錯,請嘗試“通過網頁登錄”或“通過 Cookie 登錄”。</string>
<string name="get_it">知道了</string>
@@ -85,26 +85,14 @@
<string name="gallery_list_empty_hit">什麼都沒有找到</string>
<string name="gallery_list_empty_hit_subscription">請至 設置->EH->我的標籤 訂閲標籤</string>
<string name="search">搜索</string>
<string name="keyword_search">關鍵字搜索</string>
<string name="image_search">圖片搜索</string>
<string name="search_normal">普通搜索</string>
<string name="search_advance">高級選項</string>
<string name="search_image">圖片搜索</string>
<string name="search_normal_search">一般搜索</string>
<string name="search_subscription_search">訂閲搜索</string>
<string name="search_specify_uploader">指定上傳者</string>
<string name="search_specify_tag">指定標籤</string>
<string name="search_enable_advance">啟用高級選項</string>
<string name="search_tip">一般搜索:就是搜索。\n\n訂閲搜素:在訂閲中搜索。\n\n指定上傳者:列出該上傳者上傳的畫廊。其他選項會被忽略。\n\n指定標籤:列出含有該標籤的畫廊。其他選項會被忽略。</string>
<string name="search_sh">已刪除</string>
<string name="search_sto">有種子</string>
<string name="search_sr">最低評分:</string>
<string name="search_sp">頁數:</string>
<string name="search_sp_to">到</string>
<string name="search_sf">禁用排除項:</string>
<string name="search_sfl">語言</string>
@@ -319,7 +307,7 @@
<string name="settings_eh">EH</string>
<string name="settings_eh_sign_out">退出登錄</string>
<string name="settings_eh_identity_cookies_signed">身份 Cookie 可用於登錄該賬號。&lt;br&gt;&lt;b&gt;注意數據安全&lt;/b&gt;&lt;br&gt;&lt;br&gt;%s</string>
<string name="settings_eh_identity_cookies_tourist">未登錄</string>
<string name="settings_eh_identity_cookies_guest">未登錄</string>
<string name="settings_eh_identity_cookies_copy">複製</string>
<string name="settings_u_config">EHentai 設置</string>
<string name="settings_u_config_summary">EHentai 網站上的設置</string>
+2 -14
View File
@@ -58,12 +58,10 @@
<string name="app_waring_2">繼續使用即表示您同意上述條款。</string>
<string name="error_username_cannot_empty">使用者名稱欄位不能為空</string>
<string name="error_password_cannot_empty">密碼欄位不能為空</string>
<string name="tourist_mode">略過登入</string>
<string name="guest_mode">略過登入</string>
<string name="sign_in_failed">登入失敗</string>
<string name="sign_in_failed_tip">若持續出錯,請嘗試“通過網頁登入”或“通過 Cookie 登入”。</string>
<string name="get_it">知道了</string>
<string name="cloudflare_bypass_failed">未能繞過 Cloudflare 驗證</string>
<string name="open_in_webview">在 WebView 中開啟網址以完成驗證?</string>
<!-- Select scene -->
<string name="select_scene">你想選擇哪個圖庫站台?</string>
<string name="select_scene_explain">e-hentai:對任何人開放\nexhentai:僅對登入使用者開放</string>
@@ -74,22 +72,12 @@
<string name="gallery_list_search_bar_open_gallery">開啟圖庫</string>
<string name="gallery_list_empty_hit">什麼都沒找到</string>
<string name="gallery_list_empty_hit_subscription">請至 設定-&gt;EH-&gt;我的標籤 訂閱標籤</string>
<string name="search">搜尋</string>
<string name="keyword_search">關鍵字搜尋</string>
<string name="image_search">圖片搜尋</string>
<string name="search_normal">一般搜尋</string>
<string name="search_advance">進階選項</string>
<string name="search_image">圖片搜尋</string>
<string name="search_normal_search">一般搜尋</string>
<string name="search_subscription_search">訂閱搜尋</string>
<string name="search_specify_uploader">指定上傳者</string>
<string name="search_specify_tag">指定標籤</string>
<string name="search_enable_advance">啟用進階選項</string>
<string name="search_tip">一般搜尋:就是搜尋。\n\n訂閱搜尋:在訂閱中搜尋。\n\n指定上傳者:列出該上傳者上傳的圖庫。其他選項會被忽略。\n\n指定標籤:列出含有該標籤的圖庫。其他選項會被忽略。</string>
<string name="search_sh">已被移除</string>
<string name="search_sto">有種子</string>
<string name="search_sr">最低評分:</string>
<string name="search_sp">頁數:</string>
<string name="search_sp_to">到</string>
<string name="search_sp_err1">頁數最大值至少為 10</string>
<string name="search_sp_err2">頁數範圍差至少為 20</string>
@@ -307,7 +295,7 @@
<string name="settings_eh">EH</string>
<string name="settings_eh_sign_out">登出</string>
<string name="settings_eh_identity_cookies_signed">身份 Cookie 可用於登入該帳號。&lt;br&gt;&lt;b&gt;請注意資料安全&lt;/b&gt;&lt;br&gt;&lt;br&gt;%s</string>
<string name="settings_eh_identity_cookies_tourist">訪客模式</string>
<string name="settings_eh_identity_cookies_guest">訪客模式</string>
<string name="settings_eh_identity_cookies_copy">複製 Cookies</string>
<string name="settings_u_config">EHentai 設定</string>
<string name="settings_u_config_summary">EHentai 網站上的設定</string>
-1
View File
@@ -56,6 +56,5 @@
<dimen name="gallery_detail_thumb_height">192dp</dimen>
<dimen name="bottom_sheet_width">0dp</dimen>
<dimen name="screen_edge_margin">16dp</dimen>
</resources>
-1
View File
@@ -16,7 +16,6 @@
<resources>
<item name="copy" type="id" />
<item name="reader_pager" type="id" />
</resources>
+2 -14
View File
@@ -96,12 +96,10 @@
<string name="app_waring_2">By continuing to use it, you agree to the above terms.</string>
<string name="error_username_cannot_empty">Username cannot be an empty string</string>
<string name="error_password_cannot_empty">Password cannot be an empty string</string>
<string name="tourist_mode">Continue without signing in</string>
<string name="guest_mode">Continue without signing in</string>
<string name="sign_in_failed">Sign in failed</string>
<string name="sign_in_failed_tip">If this issue continues, try \"Sign in via WebView\" or \"Sign in via cookies\".</string>
<string name="get_it">Got it</string>
<string name="cloudflare_bypass_failed">Failed to bypass Cloudflare challenge</string>
<string name="open_in_webview">Open URL in WebView to finish the challenge?</string>
<!-- Select scene -->
<string name="select_scene">Which gallery site do you want to visit?</string>
<string name="select_scene_explain">e-hentai: available for anyone\nexhentai: available for who has signed in</string>
@@ -112,22 +110,12 @@
<string name="gallery_list_search_bar_open_gallery">Open the gallery</string>
<string name="gallery_list_empty_hit">The World is Big and the panda sit alone</string>
<string name="gallery_list_empty_hit_subscription">Subscribe to tags in Settings-&gt;EH-&gt;My tags</string>
<string name="search">Search</string>
<string name="keyword_search">Keyword search</string>
<string name="image_search">Image search</string>
<string name="search_normal">Normal Search</string>
<string name="search_advance">Advanced Options</string>
<string name="search_image">Image Search</string>
<string name="search_normal_search">Normal search</string>
<string name="search_subscription_search">Subscription search</string>
<string name="search_specify_uploader">Specify uploader</string>
<string name="search_specify_tag">Specify tag</string>
<string name="search_enable_advance">Enable advance options</string>
<string name="search_tip">Normal search: Simply search.\n\nSubscription search: Search in subscriptions.\n\nSpecify uploader: Search for galleries uploaded by the specified uploader. All other options will be ignored.\n\nSpecify tag: Search for galleries that contain the specified tag. Only one tag accepted. Other options will be ignored.</string>
<string name="search_sh">Expunged</string>
<string name="search_sto">Has Torrent</string>
<string name="search_sr">Minimum Rating:</string>
<string name="search_sp">Pages:</string>
<string name="search_sp_to">to</string>
<string name="search_sp_err1">The page range maximum cannot be below 10</string>
<string name="search_sp_err2">The page range must be at least 20</string>
@@ -348,7 +336,7 @@
<string name="settings_eh">EH</string>
<string name="settings_eh_sign_out">Sign out</string>
<string name="settings_eh_identity_cookies_signed">Identity cookies can be used to sign in to this account.&lt;br&gt;&lt;b&gt;KEEP IT SAFE&lt;/b&gt;&lt;br&gt;&lt;br&gt;%s</string>
<string name="settings_eh_identity_cookies_tourist">Tourist mode</string>
<string name="settings_eh_identity_cookies_guest">Guest mode</string>
<string name="settings_eh_identity_cookies_copy">Copy Cookies</string>
<string name="settings_u_config">EHentai settings</string>
<string name="settings_u_config_summary">Settings on EHentai website</string>
+2 -1
View File
@@ -74,7 +74,7 @@
\* 不支持压缩包和 GIF
** 在不支持 [HttpEngine](https://developer.android.com/reference/android/net/http/HttpEngine) 的设备上回退到 Apache5
** 在不支持 [HttpEngine](https://developer.android.com/reference/android/net/http/HttpEngine) 的设备上回退到 OkHttp
<a href="https://github.com/FooIbar/EhViewer/releases">
<img alt="Get it on GitHub" src="https://github.com/Ehviewer-Overhauled/Art/blob/master/get-it-on-github.svg" width="200px"/>
@@ -94,6 +94,7 @@
- [Kotlin & KotlinX](https://kotlinlang.org/)
- [MDC-Android](https://github.com/material-components/material-components-android)
- [Material Icons](https://github.com/google/material-design-icons)
- [OkHttp](https://square.github.io/okhttp/)
- [Ktor](https://ktor.io/)
- [Coil](https://coil-kt.github.io/coil/)
- [Compose Destinations](https://composedestinations.rafaelcosta.xyz/)
+2 -1
View File
@@ -74,7 +74,7 @@
\* 不支援壓縮包和 GIF
** 在不支援 [HttpEngine](https://developer.android.com/reference/android/net/http/HttpEngine) 的裝置上回退到 Apache5
** 在不支援 [HttpEngine](https://developer.android.com/reference/android/net/http/HttpEngine) 的裝置上回退到 OkHttp
<a href="https://github.com/FooIbar/EhViewer/releases">
<img alt="Get it on GitHub" src="https://github.com/Ehviewer-Overhauled/Art/blob/master/get-it-on-github.svg" width="200px"/>
@@ -94,6 +94,7 @@
- [Kotlin & KotlinX](https://kotlinlang.org/)
- [MDC-Android](https://github.com/material-components/material-components-android)
- [Material Icons](https://github.com/google/material-design-icons)
- [OkHttp](https://square.github.io/okhttp/)
- [Ktor](https://ktor.io/)
- [Coil](https://coil-kt.github.io/coil/)
- [Compose Destinations](https://composedestinations.rafaelcosta.xyz/)
+7 -3
View File
@@ -28,7 +28,8 @@ kotlinx-serialization = "1.6.2"
ksp = "2.0.0-Beta1-1.0.15"
ktlint = "1.0.1"
ktor = "3.0.0-beta-1"
okio = "3.6.0"
okhttp3 = "5.0.0-alpha.12"
okio = "3.7.0"
splitties = "3.0.0"
[libraries]
@@ -95,10 +96,12 @@ kotlinx-serialization-json-okio = { module = "org.jetbrains.kotlinx:kotlinx-seri
# For renovate
ktlint = { module = "com.pinterest:ktlint", version.ref = "ktlint" }
ktor-client-apache5 = { module = "io.ktor:ktor-client-apache5", version.ref = "ktor" }
ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" }
material = { module = "com.google.android.material:material", version.ref = "google-android-material" }
okhttp-android = { module = "com.squareup.okhttp3:okhttp-android" }
okhttp-bom = { module = "com.squareup.okhttp3:okhttp-bom", version.ref = "okhttp3" }
okio-jvm = { module = "com.squareup.okio:okio-jvm", version.ref = "okio" }
photoview = "com.github.chrisbanes:PhotoView:2.3.0"
@@ -118,7 +121,8 @@ coil = ["coil-compose", "coil-gif"]
compose = ["compose-foundation", "compose-material-icons-extended", "compose-material3", "compose-material3-windowsizeclass", "compose-ui-util"]
cronet = ["cronet-api", "cronet-gms"]
kotlinx-serialization = ["kotlinx-serialization-cbor", "kotlinx-serialization-json", "kotlinx-serialization-json-okio"]
ktor = ["ktor-client-apache5"]
ktor = ["ktor-client-okhttp"]
okhttp = ["okhttp-android"]
splitties = ["splitties-appctx", "splitties-arch-room", "splitties-preferences", "splitties-systemservices"]
[plugins]