Compare commits
13
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e94424a83e | ||
|
|
56d30aa413 | ||
|
|
a6d215a0bf | ||
|
|
cf9441adb5 | ||
|
|
36b16d7be8 | ||
|
|
fab5532407 | ||
|
|
8ad4e8640f | ||
|
|
5b5a2a9a33 | ||
|
|
7719312355 | ||
|
|
48f1b0a958 | ||
|
|
9abeba6b15 | ||
|
|
75bec4314e | ||
|
|
b767c041b9 |
No files matched your search
@@ -64,7 +64,7 @@ android {
|
||||
minSdk = 28
|
||||
targetSdk = 34
|
||||
versionCode = 180043
|
||||
versionName = "1.8.9.6"
|
||||
versionName = "1.8.9.5-gms"
|
||||
resourceConfigurations.addAll(
|
||||
listOf(
|
||||
"zh",
|
||||
@@ -250,6 +250,9 @@ dependencies {
|
||||
implementation("io.ktor:ktor-io-jvm:$ktor")
|
||||
implementation("io.ktor:ktor-utils-jvm:$ktor")
|
||||
|
||||
implementation("org.chromium.net:cronet-api:113.5672.61")
|
||||
implementation("com.google.android.gms:play-services-cronet:18.0.1")
|
||||
|
||||
val serialization = "1.6.0"
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-cbor:$serialization")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$serialization")
|
||||
|
||||
@@ -25,6 +25,7 @@ import androidx.lifecycle.coroutineScope
|
||||
import coil.ImageLoaderFactory
|
||||
import coil.decode.ImageDecoderDecoder
|
||||
import coil.util.DebugLogger
|
||||
import com.google.android.gms.net.CronetProviderInstaller
|
||||
import com.hippo.ehviewer.client.EhCookieStore
|
||||
import com.hippo.ehviewer.client.EhTagDatabase
|
||||
import com.hippo.ehviewer.client.data.GalleryDetail
|
||||
@@ -77,6 +78,7 @@ class EhApplication : Application(), ImageLoaderFactory {
|
||||
handler?.uncaughtException(t, e)
|
||||
}
|
||||
super.onCreate()
|
||||
CronetProviderInstaller.installProvider(appCtx)
|
||||
System.loadLibrary("ehviewer")
|
||||
System.loadLibrary("ehviewer_rust")
|
||||
ReadableTime.initialize(this)
|
||||
|
||||
@@ -60,7 +60,6 @@ object Settings : DataStorePreferences(null) {
|
||||
var removeImageFiles by boolPref("include_pic", true)
|
||||
var needSignIn by boolPref("need_sign_in", true)
|
||||
var blackDarkTheme by boolPref("black_dark_theme", false).observed { updateWhenAmoledModeChanges() }
|
||||
var harmonizeCategoryColor by boolPref("harmonize_category_color", true)
|
||||
var preloadThumbAggressively by boolPref("preload_thumb_aggressively", false)
|
||||
var downloadOriginImage by boolPref("download_origin_image", false)
|
||||
var thumbSizeDp by intPref("thumb_size_", 120)
|
||||
|
||||
@@ -126,18 +126,12 @@ object EhUtils {
|
||||
MISC -> BG_COLOR_MISC
|
||||
else -> BG_COLOR_UNKNOWN
|
||||
}.toInt()
|
||||
return if (Settings.harmonizeCategoryColor) {
|
||||
harmonizeWithRole(context, primary)
|
||||
} else {
|
||||
primary
|
||||
}
|
||||
return harmonizeWithRole(context, primary)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun getCategoryColor(category: Int) = Color(getCategoryColor(LocalContext.current, category))
|
||||
|
||||
val categoryTextColor = Color(0xffe6e0e9)
|
||||
|
||||
fun signOut() {
|
||||
EhCookieStore.signOut()
|
||||
Settings.avatar = null
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
@file:Suppress("BlockingMethodInNonBlockingContext")
|
||||
@file:RequiresExtension(extension = Build.VERSION_CODES.S, version = 7)
|
||||
|
||||
package com.hippo.ehviewer.coil
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.webkit.MimeTypeMap
|
||||
import androidx.annotation.RequiresExtension
|
||||
import coil.ComponentRegistry
|
||||
import coil.ImageLoader
|
||||
import coil.decode.DataSource
|
||||
@@ -30,7 +27,7 @@ class CronetHttpUriFetcher(private val data: String, private val options: Option
|
||||
val diskCache = requireNotNull(imageLoader.diskCache)
|
||||
val snapshot = diskCache.openSnapshot(diskCacheKey) ?: run {
|
||||
val success = cronetRequest(data) {
|
||||
setCacheDisabled(true)
|
||||
disableCache()
|
||||
}.execute {
|
||||
diskCache.suspendEdit(diskCacheKey) {
|
||||
RandomAccessFile(data.toFile(), "rw").use {
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
@file:RequiresExtension(extension = Build.VERSION_CODES.S, version = 7)
|
||||
|
||||
package com.hippo.ehviewer.cronet
|
||||
|
||||
import android.net.http.HttpEngine
|
||||
import android.net.http.HttpException
|
||||
import android.net.http.UrlRequest
|
||||
import android.net.http.UrlResponseInfo
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresExtension
|
||||
import com.hippo.ehviewer.EhApplication
|
||||
import com.hippo.ehviewer.client.CHROME_ACCEPT
|
||||
import com.hippo.ehviewer.client.CHROME_ACCEPT_LANGUAGE
|
||||
@@ -20,6 +12,10 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okio.Path.Companion.toOkioPath
|
||||
import org.chromium.net.CronetEngine
|
||||
import org.chromium.net.CronetException
|
||||
import org.chromium.net.UrlRequest
|
||||
import org.chromium.net.UrlResponseInfo
|
||||
import splitties.init.appCtx
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.channels.FileChannel
|
||||
@@ -32,23 +28,24 @@ import kotlin.coroutines.resumeWithException
|
||||
private const val TAG = "CronetRequest"
|
||||
val pool = DirectByteBufferPool(32)
|
||||
|
||||
val cronetHttpClient: HttpEngine = HttpEngine.Builder(appCtx).apply {
|
||||
setEnableBrotli(true)
|
||||
val cronetHttpClient: CronetEngine = CronetEngine.Builder(appCtx).apply {
|
||||
enableBrotli(true)
|
||||
val cache = (appCtx.cacheDir.toOkioPath() / "http_cache").toFile().apply { mkdirs() }
|
||||
setStoragePath(cache.absolutePath)
|
||||
setEnableHttpCache(HttpEngine.Builder.HTTP_CACHE_DISK_NO_HTTP, 100 * 1024)
|
||||
enableHttpCache(CronetEngine.Builder.HTTP_CACHE_DISK_NO_HTTP, 100 * 1024)
|
||||
setUserAgent(CHROME_USER_AGENT)
|
||||
}.build()
|
||||
|
||||
val cronetHttpClientExecutor = EhApplication.baseOkHttpClient.dispatcher.executorService
|
||||
|
||||
// TODO: Rewrite this to use android.net.http.HttpEngine and make it Android 14 only when released
|
||||
class CronetRequest {
|
||||
lateinit var consumer: (ByteBuffer) -> Unit
|
||||
lateinit var onResponse: CronetRequest.(UrlResponseInfo) -> Unit
|
||||
lateinit var request: UrlRequest
|
||||
lateinit var onError: (Throwable) -> Unit
|
||||
lateinit var readerCont: Continuation<Unit>
|
||||
val callback = object : UrlRequest.Callback {
|
||||
val callback = object : UrlRequest.Callback() {
|
||||
override fun onRedirectReceived(req: UrlRequest, info: UrlResponseInfo, url: String) {
|
||||
logcat(tag = TAG) { "Redirected to $url" }
|
||||
req.followRedirect()
|
||||
@@ -67,18 +64,14 @@ class CronetRequest {
|
||||
readerCont.resume(Unit)
|
||||
}
|
||||
|
||||
override fun onFailed(req: UrlRequest, info: UrlResponseInfo?, e: HttpException) {
|
||||
override fun onFailed(req: UrlRequest, info: UrlResponseInfo?, e: CronetException) {
|
||||
onError(e)
|
||||
}
|
||||
|
||||
override fun onCanceled(req: UrlRequest, info: UrlResponseInfo?) {
|
||||
// No-op
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline fun cronetRequest(url: String, referer: String? = null, conf: UrlRequest.Builder.() -> Unit = {}) = CronetRequest().apply {
|
||||
request = cronetHttpClient.newUrlRequestBuilder(url, cronetHttpClientExecutor, callback).apply {
|
||||
request = cronetHttpClient.newUrlRequestBuilder(url, callback, cronetHttpClientExecutor).apply {
|
||||
addHeader("Cookie", EhCookieStore.getCookieHeader(url.toHttpUrl()))
|
||||
addHeader("Accept", CHROME_ACCEPT)
|
||||
addHeader("Accept-Language", CHROME_ACCEPT_LANGUAGE)
|
||||
|
||||
@@ -135,9 +135,9 @@ class SpiderDen(mGalleryInfo: GalleryInfo) {
|
||||
): Boolean {
|
||||
return if (isCronetSupported) {
|
||||
cronetRequest(url, referer) {
|
||||
setCacheDisabled(true)
|
||||
disableCache()
|
||||
}.execute { info ->
|
||||
val headers = info.headers.asMap
|
||||
val headers = info.allHeaders
|
||||
val type = headers["Content-Type"]?.first()?.toMediaType()?.subtype ?: "jpg"
|
||||
val length = headers["Content-Length"]!!.first().toLong()
|
||||
saveResponseMeta(index, type, length) { file ->
|
||||
|
||||
@@ -21,7 +21,6 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ProvideTextStyle
|
||||
import androidx.compose.material3.ShapeDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.contentColorFor
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.produceState
|
||||
@@ -29,14 +28,12 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.constraintlayout.compose.ConstraintLayout
|
||||
import 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
|
||||
@@ -112,7 +109,6 @@ fun GalleryInfoListItem(
|
||||
start.linkTo(parent.start)
|
||||
bottom.linkTo(parent.bottom)
|
||||
}.clip(ShapeDefaults.Small).background(categoryColor).padding(vertical = 2.dp, horizontal = 8.dp),
|
||||
color = if (Settings.harmonizeCategoryColor) Color.Unspecified else EhUtils.categoryTextColor,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
Row(
|
||||
@@ -202,11 +198,10 @@ fun GalleryInfoGridItem(
|
||||
modifier = Modifier.aspectRatio(aspect).fillMaxWidth(),
|
||||
contentScale = ContentScale.Crop,
|
||||
)
|
||||
val categoryColor = EhUtils.getCategoryColor(info.category)
|
||||
Badge(
|
||||
modifier = Modifier.align(Alignment.TopEnd).width(32.dp).height(24.dp),
|
||||
containerColor = categoryColor,
|
||||
contentColor = if (Settings.harmonizeCategoryColor) contentColorFor(categoryColor) else EhUtils.categoryTextColor,
|
||||
containerColor = EhUtils.getCategoryColor(info.category),
|
||||
contentColor = MaterialTheme.colorScheme.onSurface,
|
||||
) {
|
||||
simpleLang?.let {
|
||||
Text(text = it.uppercase())
|
||||
|
||||
@@ -46,7 +46,6 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
@@ -810,7 +809,6 @@ class DownloadsScene :
|
||||
Text(
|
||||
text = categoryText,
|
||||
modifier = Modifier.clip(ShapeDefaults.Small).background(categoryColor).padding(vertical = 2.dp, horizontal = 8.dp),
|
||||
color = if (Settings.harmonizeCategoryColor) Color.Unspecified else EhUtils.categoryTextColor,
|
||||
textAlign = TextAlign.Center,
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
)
|
||||
|
||||
@@ -224,10 +224,6 @@ fun EhScreen() {
|
||||
title = stringResource(id = R.string.black_dark_theme),
|
||||
value = Settings::blackDarkTheme,
|
||||
)
|
||||
SwitchPreference(
|
||||
title = stringResource(id = R.string.harmonize_category_color),
|
||||
value = Settings::harmonizeCategoryColor,
|
||||
)
|
||||
SimpleMenuPreferenceInt(
|
||||
title = stringResource(id = R.string.settings_eh_launch_page),
|
||||
entry = R.array.launch_page_entries,
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.hippo.ehviewer.ui.tools
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.Layout
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.roundToInt
|
||||
@@ -14,6 +15,7 @@ private const val QuarterCircle = PI / 2
|
||||
@Composable
|
||||
fun CircularLayout(
|
||||
modifier: Modifier = Modifier,
|
||||
radius: Dp,
|
||||
placeFirstItemInCenter: Boolean = false,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
@@ -21,7 +23,7 @@ fun CircularLayout(
|
||||
modifier = modifier,
|
||||
content = content,
|
||||
) { measurables, constraints ->
|
||||
val radiusPx = constraints.maxWidth * 0.4
|
||||
val radiusPx = radius.toPx()
|
||||
val itemConstraints = constraints.copy(minWidth = 0, minHeight = 0)
|
||||
val placeables = measurables.map { measurable -> measurable.measure(itemConstraints) }
|
||||
val outPlaceables = if (placeFirstItemInCenter) placeables.drop(1) else placeables
|
||||
|
||||
@@ -41,11 +41,11 @@ import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import com.jamal.composeprefs3.ui.ifNotNullThen
|
||||
import kotlinx.coroutines.CancellableContinuation
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
@@ -183,7 +183,6 @@ class DialogState {
|
||||
}
|
||||
AlertDialog(
|
||||
onDismissRequest = { cont.cancel() },
|
||||
properties = DialogProperties(usePlatformDefaultWidth = respectDefaultWidth),
|
||||
content = {
|
||||
Surface(
|
||||
modifier = with(Modifier) { if (!respectDefaultWidth) defaultMinSize(280.dp) else width(280.dp) },
|
||||
@@ -249,10 +248,12 @@ class DialogState {
|
||||
@StringRes title: Int,
|
||||
@StringRes hint: Int,
|
||||
maxChar: Int,
|
||||
adjustTextPosition: Boolean = true,
|
||||
): Pair<Int, String> = showNoButton(false) {
|
||||
Column {
|
||||
Text(text = stringResource(id = title), modifier = Modifier.padding(horizontal = 16.dp).padding(top = 16.dp), style = MaterialTheme.typography.titleMedium)
|
||||
CircularLayout(
|
||||
radius = 112.dp,
|
||||
modifier = Modifier.fillMaxWidth().aspectRatio(1F),
|
||||
placeFirstItemInCenter = true,
|
||||
) {
|
||||
@@ -260,7 +261,8 @@ class DialogState {
|
||||
TextField(
|
||||
value = note,
|
||||
onValueChange = { note = it },
|
||||
modifier = Modifier.fillMaxWidth(0.45F).aspectRatio(1F),
|
||||
modifier = Modifier.width(128.dp),
|
||||
textStyle = TextStyle(fontFamily = FontFamily.Monospace),
|
||||
label = { Text(text = stringResource(id = hint)) },
|
||||
trailingIcon = {
|
||||
if (note.isNotEmpty()) {
|
||||
@@ -276,6 +278,7 @@ class DialogState {
|
||||
textAlign = TextAlign.End,
|
||||
)
|
||||
},
|
||||
maxLines = 6,
|
||||
shape = ShapeDefaults.ExtraSmall,
|
||||
colors = TextFieldDefaults.colors(
|
||||
focusedIndicatorColor = Color.Transparent,
|
||||
@@ -284,18 +287,16 @@ class DialogState {
|
||||
)
|
||||
items.forEachIndexed { index, (icon, text) ->
|
||||
Column(
|
||||
modifier = Modifier.clip(IconWithTextCorner).clickable { dismissWith(index to note) }
|
||||
.fillMaxWidth(0.2F),
|
||||
modifier = Modifier.clip(IconWithTextCorner).clickable { dismissWith(index to note) },
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Icon(imageVector = icon, contentDescription = null, tint = AlertDialogDefaults.iconContentColor)
|
||||
Text(
|
||||
text = text,
|
||||
textAlign = TextAlign.Center,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 2,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
)
|
||||
if (index == 0 && adjustTextPosition) {
|
||||
Text(text = text, style = MaterialTheme.typography.bodySmall)
|
||||
Icon(imageVector = icon, contentDescription = null, tint = AlertDialogDefaults.iconContentColor)
|
||||
} else {
|
||||
Icon(imageVector = icon, contentDescription = null, tint = AlertDialogDefaults.iconContentColor)
|
||||
Text(text = text, style = MaterialTheme.typography.bodySmall)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package com.hippo.ehviewer.util
|
||||
|
||||
import android.os.Build
|
||||
import androidx.annotation.ChecksSdkIntAtLeast
|
||||
import com.google.android.gms.net.CronetProviderInstaller
|
||||
|
||||
@get:ChecksSdkIntAtLeast(Build.VERSION_CODES.S, extension = 7)
|
||||
val isCronetSupported: Boolean
|
||||
get() = isAtLeastSExtension7
|
||||
get() = isAtLeastQ && CronetProviderInstaller.isInstalled()
|
||||
@@ -1,8 +1,5 @@
|
||||
package com.hippo.ehviewer.util
|
||||
|
||||
import android.os.Build
|
||||
import android.os.ext.SdkExtensions
|
||||
|
||||
val isAtLeastQ = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
|
||||
val isAtLeastR = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
|
||||
val isAtLeastSExtension7 = isAtLeastR && SdkExtensions.getExtensionVersion(Build.VERSION_CODES.S) >= 7
|
||||
@@ -419,7 +419,7 @@
|
||||
<string name="default_favorites_collection">默认收藏夹</string>
|
||||
<string name="default_favorites_warning">启用此项将无法添加收藏备注</string>
|
||||
<string name="let_me_select">让我选择</string>
|
||||
<string name="favorite_note">收藏备注</string>
|
||||
<string name="favorite_note">备注</string>
|
||||
<string name="collections">收藏夹</string>
|
||||
<string name="need_sign_in">需要登录</string>
|
||||
<!-- Progress Scene -->
|
||||
@@ -476,7 +476,6 @@
|
||||
<string name="download_service">下载服务</string>
|
||||
<string name="favorite_name">收藏画廊</string>
|
||||
<string name="black_dark_theme">纯黑深色主题</string>
|
||||
<string name="harmonize_category_color">类别颜色适配动态配色</string>
|
||||
<string name="use_ci_update_channel">使用持续集成(CI)构建频道更新</string>
|
||||
<string name="download_filter">过滤</string>
|
||||
<string name="download_all">全部</string>
|
||||
|
||||
@@ -477,7 +477,7 @@
|
||||
<string name="default_favorites_collection">Default favorites collection</string>
|
||||
<string name="default_favorites_warning">You won\'t be able to add favorite notes if you enable this</string>
|
||||
<string name="let_me_select">Let me select</string>
|
||||
<string name="favorite_note">Favorite Note</string>
|
||||
<string name="favorite_note">Note</string>
|
||||
<string name="collections">Collections</string>
|
||||
<string name="need_sign_in">Signing in is required</string>
|
||||
<!-- Progress Scene -->
|
||||
@@ -541,7 +541,6 @@
|
||||
<string name="download_service">Download Service</string>
|
||||
<string name="favorite_name">Favorite</string>
|
||||
<string name="black_dark_theme">Black dark theme</string>
|
||||
<string name="harmonize_category_color">Harmonize category color for Dynamic Color</string>
|
||||
<string name="download_filter">Filter</string>
|
||||
<string name="download_all">All</string>
|
||||
<string name="download_start_all_reversed">Start all (reversed)</string>
|
||||
|
||||
Reference in new issue
Block a user