From 7b67210876975d5b63559204c476ea8b50d55e00 Mon Sep 17 00:00:00 2001 From: Vladimir Dostoyevskiy <99822064+VDostoyevskiy@users.noreply.github.com> Date: Wed, 6 Dec 2023 12:42:17 +0800 Subject: [PATCH] Fallback engine should be Apache5 HC (#456) * Drop explicitly okhttp dependency * Move to Apache5 HC instead * Fix build * Credit to Ktor instead * Refactor Cronet Preference --------- Co-authored-by: GG Bond --- README.md | 4 +- app/build.gradle.kts | 4 -- app/proguard-rules.pro | 8 ++++ .../com/hippo/ehviewer/util/CronetUtils.kt | 5 +-- .../com/hippo/ehviewer/EhApplication.kt | 26 ++--------- .../com/hippo/ehviewer/ktbuilder/Okhttp.kt | 5 --- .../ehviewer/ui/settings/AdvancedScreen.kt | 44 ++++++++++++++++--- .../UncaughtExceptionInterceptor.kt | 28 ------------ app/src/main/res/values-zh-rCN/strings.xml | 3 +- app/src/main/res/values-zh-rTW/strings.xml | 2 - app/src/main/res/values/strings.xml | 3 +- .../com/hippo/ehviewer/util/CronetUtils.kt | 9 +--- docs/README/zh-cn.md | 4 +- docs/README/zh-tw.md | 4 +- gradle/libs.versions.toml | 9 +--- 15 files changed, 65 insertions(+), 93 deletions(-) delete mode 100644 app/src/main/kotlin/com/hippo/ehviewer/ktbuilder/Okhttp.kt delete mode 100644 app/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/UncaughtExceptionInterceptor.kt diff --git a/README.md b/README.md index 4513056c8..d53f7921c 100644 --- a/README.md +++ b/README.md @@ -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 OkHttp on devices that don't support [HttpEngine](https://developer.android.com/reference/android/net/http/HttpEngine) +** Fall back to Apache5 on devices that don't support [HttpEngine](https://developer.android.com/reference/android/net/http/HttpEngine) Get it on GitHub @@ -95,7 +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/) - [libarchive](http://www.libarchive.org/) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 90038ef41..a92820452 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -240,10 +240,6 @@ 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) diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 354f15f55..6dcb1ee62 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -19,5 +19,13 @@ # 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 + -allowaccessmodification -repackageclasses diff --git a/app/src/gms/kotlin/com/hippo/ehviewer/util/CronetUtils.kt b/app/src/gms/kotlin/com/hippo/ehviewer/util/CronetUtils.kt index 80851c9dc..d041f5217 100644 --- a/app/src/gms/kotlin/com/hippo/ehviewer/util/CronetUtils.kt +++ b/app/src/gms/kotlin/com/hippo/ehviewer/util/CronetUtils.kt @@ -1,12 +1,11 @@ package com.hippo.ehviewer.util import com.google.android.gms.net.CronetProviderInstaller -import com.hippo.ehviewer.Settings import org.chromium.net.CronetProvider import splitties.init.appCtx -val isCronetSupported: Boolean - get() = Settings.enableCronet && CronetProvider.getAllProviders(appCtx).any { it.isEnabled }.also { +val isCronetAvailable: Boolean + get() = CronetProvider.getAllProviders(appCtx).any { it.isEnabled }.also { if (!it) { CronetProviderInstaller.installProvider(appCtx) } diff --git a/app/src/main/kotlin/com/hippo/ehviewer/EhApplication.kt b/app/src/main/kotlin/com/hippo/ehviewer/EhApplication.kt index 8a9d922be..e0973504d 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/EhApplication.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/EhApplication.kt @@ -37,7 +37,6 @@ import com.hippo.ehviewer.dailycheck.checkDawn import com.hippo.ehviewer.dao.SearchDatabase import com.hippo.ehviewer.download.DownloadManager import com.hippo.ehviewer.ktbuilder.diskCache -import com.hippo.ehviewer.ktbuilder.httpClient import com.hippo.ehviewer.ktbuilder.imageLoader import com.hippo.ehviewer.ktor.CronetEngine import com.hippo.ehviewer.legacy.cleanObsoleteCache @@ -49,20 +48,16 @@ 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.isCronetSupported -import eu.kanade.tachiyomi.network.interceptor.UncaughtExceptionInterceptor +import com.hippo.ehviewer.util.isCronetAvailable 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.okhttp.OkHttp +import io.ktor.client.engine.apache5.Apache5 import io.ktor.client.plugins.cookies.HttpCookies import kotlinx.coroutines.launch import moe.tarsin.kt.unreachable -import okhttp3.AsyncDns -import okhttp3.android.AndroidAsyncDns import okio.Path.Companion.toOkioPath import splitties.arch.room.roomDb import splitties.init.appCtx @@ -175,17 +170,14 @@ class EhApplication : Application(), ImageLoaderFactory { companion object { val ktorClient by lazy { - if (isCronetSupported) { + if (Settings.enableCronet && isCronetAvailable) { HttpClient(CronetEngine) { install(HttpCookies) { storage = EhCookieStore } } } else { - HttpClient(OkHttp) { - engine { - preconfigured = baseOkHttpClient - } + HttpClient(Apache5) { install(HttpCookies) { storage = EhCookieStore } @@ -202,16 +194,6 @@ class EhApplication : Application(), ImageLoaderFactory { } } - // Fallback to CIO when cronet unavailable after coil 3.0 release - private val baseOkHttpClient by lazy { - httpClient { - if (isAtLeastQ) { - dns(AsyncDns.toDns(AndroidAsyncDns.IPv4, AndroidAsyncDns.IPv6)) - } - addInterceptor(UncaughtExceptionInterceptor()) - } - } - val galleryDetailCache by lazy { LruCache(25).also { lifecycleScope.launch { diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ktbuilder/Okhttp.kt b/app/src/main/kotlin/com/hippo/ehviewer/ktbuilder/Okhttp.kt deleted file mode 100644 index 46b7ed7cc..000000000 --- a/app/src/main/kotlin/com/hippo/ehviewer/ktbuilder/Okhttp.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.hippo.ehviewer.ktbuilder - -import okhttp3.OkHttpClient - -inline fun httpClient(builder: OkHttpClient.Builder.() -> Unit): OkHttpClient = OkHttpClient.Builder().apply(builder).build() diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/AdvancedScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/AdvancedScreen.kt index f99315f26..0e4a8e6b4 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/AdvancedScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/AdvancedScreen.kt @@ -6,7 +6,9 @@ import android.net.Uri import android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS import android.provider.Settings.ACTION_APP_OPEN_BY_DEFAULT_SETTINGS import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll @@ -15,6 +17,9 @@ import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Scaffold +import androidx.compose.material3.SegmentedButton +import androidx.compose.material3.SegmentedButtonDefaults +import androidx.compose.material3.SingleChoiceSegmentedButtonRow import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text @@ -25,6 +30,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext @@ -42,6 +48,7 @@ import com.hippo.ehviewer.ui.tools.observed import com.hippo.ehviewer.util.AppConfig import com.hippo.ehviewer.util.Crash import com.hippo.ehviewer.util.ReadableTime +import com.hippo.ehviewer.util.isCronetAvailable import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.navigation.DestinationsNavigator import java.io.File @@ -132,11 +139,38 @@ fun AdvancedScreen(navigator: DestinationsNavigator) { entryValueRes = R.array.app_language_entry_values, value = Settings::language, ) - SwitchPreference( - title = stringResource(id = R.string.settings_advanced_cronet_integration), - summary = stringResource(id = R.string.settings_advanced_cronet_integration_summary), - value = Settings::enableCronet, - ) + var enableCronet by Settings::enableCronet.observed + Preference( + title = stringResource(id = R.string.settings_advanced_http_engine), + summary = if (enableCronet) "Cronet" else "Apache 5", + ) { + coroutineScope.launch { + dialogState.awaitPermissionOrCancel( + title = R.string.settings_advanced_http_engine, + showCancelButton = false, + ) { + Box(modifier = Modifier.fillMaxWidth()) { + SingleChoiceSegmentedButtonRow(modifier = Modifier.align(Alignment.Center)) { + SegmentedButton( + selected = !enableCronet, + onClick = { enableCronet = false }, + shape = SegmentedButtonDefaults.itemShape(index = 0, count = 2), + ) { + Text("Apache 5") + } + SegmentedButton( + selected = enableCronet, + onClick = { enableCronet = true }, + shape = SegmentedButtonDefaults.itemShape(index = 1, count = 2), + enabled = isCronetAvailable, + ) { + Text("Cronet") + } + } + } + } + } + } SwitchPreference( title = stringResource(id = R.string.preload_thumb_aggressively), value = Settings::preloadThumbAggressively, diff --git a/app/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/UncaughtExceptionInterceptor.kt b/app/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/UncaughtExceptionInterceptor.kt deleted file mode 100644 index a746ae773..000000000 --- a/app/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/UncaughtExceptionInterceptor.kt +++ /dev/null @@ -1,28 +0,0 @@ -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) - } - } - } -} diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 92912165b..041ae71ba 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -377,8 +377,7 @@ 已保存日志至 %s 阅读缓存大小 App 界面语言 - [实验性] Cronet 集成 - 使用 Cronet 下载图片(性能更好) + Http 引擎(重启生效) [实验性] 触摸溢出系数 增大此项将降低滑动手势敏感度 导出数据 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index ec5cd675c..afbf6c07c 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -375,8 +375,6 @@ 已將紀錄檔儲存至 %s 閱讀快取大小 應用程式介面語言 - [實驗性] Cronet 整合 - 使用 Cronet 下載圖片 (性能較佳) 匯出檔案 儲存資料至外接儲存設備,例如下載列表,快速搜尋列表 已匯出資料至 %s diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 624e465ed..43e43b8a1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -421,8 +421,7 @@ Logcat dumped to %s Read cache size App language - [Experimental] Cronet integration - Use Cronet for image download (better performance) + Http Engine (Restart required) [Experimental] Touch slop factor Increasing this will reduce swipe gesture sensitivity Export data diff --git a/app/src/oss/kotlin/com/hippo/ehviewer/util/CronetUtils.kt b/app/src/oss/kotlin/com/hippo/ehviewer/util/CronetUtils.kt index 4249c6ed9..4262e46b4 100644 --- a/app/src/oss/kotlin/com/hippo/ehviewer/util/CronetUtils.kt +++ b/app/src/oss/kotlin/com/hippo/ehviewer/util/CronetUtils.kt @@ -1,9 +1,4 @@ package com.hippo.ehviewer.util -import android.os.Build -import androidx.annotation.ChecksSdkIntAtLeast -import com.hippo.ehviewer.Settings - -@get:ChecksSdkIntAtLeast(Build.VERSION_CODES.S, extension = 7) -val isCronetSupported: Boolean - get() = Settings.enableCronet && isAtLeastSExtension7 +val isCronetAvailable: Boolean + get() = isAtLeastSExtension7 diff --git a/docs/README/zh-cn.md b/docs/README/zh-cn.md index ca98843d4..26d9bcb09 100644 --- a/docs/README/zh-cn.md +++ b/docs/README/zh-cn.md @@ -74,7 +74,7 @@ \* 不支持压缩包和 GIF -** 在不支持 [HttpEngine](https://developer.android.com/reference/android/net/http/HttpEngine) 的设备上回退到 OkHttp +** 在不支持 [HttpEngine](https://developer.android.com/reference/android/net/http/HttpEngine) 的设备上回退到 Apache5 Get it on GitHub @@ -94,7 +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/) - [libarchive](http://www.libarchive.org/) diff --git a/docs/README/zh-tw.md b/docs/README/zh-tw.md index d22abdb05..54f33eeee 100644 --- a/docs/README/zh-tw.md +++ b/docs/README/zh-tw.md @@ -74,7 +74,7 @@ \* 不支援壓縮包和 GIF -** 在不支援 [HttpEngine](https://developer.android.com/reference/android/net/http/HttpEngine) 的裝置上回退到 OkHttp +** 在不支援 [HttpEngine](https://developer.android.com/reference/android/net/http/HttpEngine) 的裝置上回退到 Apache5 Get it on GitHub @@ -94,7 +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/) - [libarchive](http://www.libarchive.org/) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b5027a63c..7eb3b50c3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -30,7 +30,6 @@ kotlinx-serialization = "1.6.2" ksp = "2.0.0-Beta1-1.0.15" ktlint = "1.0.1" ktor = "3.0.0-beta-1" -okhttp3 = "5.0.0-alpha.11" okio = "3.6.0" splitties = "3.0.0" @@ -103,13 +102,10 @@ kotlinx-serialization-json-okio = { module = "org.jetbrains.kotlinx:kotlinx-seri # For renovate ktlint = { module = "com.pinterest:ktlint", version.ref = "ktlint" } -ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" } +ktor-client-apache5 = { module = "io.ktor:ktor-client-apache5", 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" } -okhttp-coroutines = { module = "com.squareup.okhttp3:okhttp-coroutines" } okio-jvm = { module = "com.squareup.okio:okio-jvm", version.ref = "okio" } photoview = "com.github.chrisbanes:PhotoView:2.3.0" @@ -131,8 +127,7 @@ 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-okhttp"] -okhttp = ["okhttp-android", "okhttp-coroutines"] +ktor = ["ktor-client-apache5"] splitties = ["splitties-appctx", "splitties-arch-room", "splitties-preferences", "splitties-systemservices"] [plugins]