Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10c7019aeb | ||
|
|
76aa401800 | ||
|
|
dab5878f74 | ||
|
|
ab8671cc3b | ||
|
|
2e52dda208 | ||
|
|
1ab08d0ae1 | ||
|
|
702be0ed43 | ||
|
|
4067543701 | ||
|
|
074689c60e | ||
|
|
2beb715df3 | ||
|
|
7ea2597899 | ||
|
|
34a8f92865 | ||
|
|
6e2a2de13f | ||
|
|
e073232352 | ||
|
|
6f49b4d23e | ||
|
|
97cad20ab1 | ||
|
|
9300a3d9ce | ||
|
|
28b7f80a8f | ||
|
|
a06d23753a |
No files matched your search
@@ -1,3 +1,4 @@
|
|||||||
[*.{kt,kts}]
|
[*.{kt,kts}]
|
||||||
|
ij_kotlin_imports_layout = *
|
||||||
ktlint_code_style = intellij_idea
|
ktlint_code_style = intellij_idea
|
||||||
ktlint_function_naming_ignore_when_annotated_with = Composable
|
ktlint_function_naming_ignore_when_annotated_with = Composable
|
||||||
@@ -18,11 +18,11 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
|
|
||||||
- name: Setup Java 21
|
- name: Setup Java 17
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
java-version: 21
|
java-version: 17
|
||||||
|
|
||||||
- name: Install Rust Toolchain
|
- name: Install Rust Toolchain
|
||||||
run: rustup update && rustup target add armv7-linux-androideabi aarch64-linux-android i686-linux-android x86_64-linux-android && rustup component add rustfmt clippy
|
run: rustup update && rustup target add armv7-linux-androideabi aarch64-linux-android i686-linux-android x86_64-linux-android && rustup component add rustfmt clippy
|
||||||
@@ -57,7 +57,7 @@ jobs:
|
|||||||
path: |
|
path: |
|
||||||
app/.cxx
|
app/.cxx
|
||||||
app/src/main/cpp/nettle/nettle
|
app/src/main/cpp/nettle/nettle
|
||||||
key: ${{ runner.os }}-cmake-${{ github.run_id }}
|
key: ${{ runner.os }}-cmake-${{ hashFiles('gradle/libs.versions.toml', 'app/build.gradle.kts', 'app/src/main/cpp/CMakeLists.txt') }}
|
||||||
restore-keys: ${{ runner.os }}-cmake-
|
restore-keys: ${{ runner.os }}-cmake-
|
||||||
|
|
||||||
- name: Build OSS
|
- name: Build OSS
|
||||||
|
|||||||
@@ -15,11 +15,11 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
|
|
||||||
- name: Setup Java 21
|
- name: Setup Java 17
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
java-version: 21
|
java-version: 17
|
||||||
|
|
||||||
- name: Install Rust Toolchain
|
- name: Install Rust Toolchain
|
||||||
run: rustup update && rustup target add armv7-linux-androideabi aarch64-linux-android i686-linux-android x86_64-linux-android
|
run: rustup update && rustup target add armv7-linux-androideabi aarch64-linux-android i686-linux-android x86_64-linux-android
|
||||||
@@ -35,7 +35,7 @@ jobs:
|
|||||||
path: |
|
path: |
|
||||||
app/.cxx
|
app/.cxx
|
||||||
app/src/main/cpp/nettle/nettle
|
app/src/main/cpp/nettle/nettle
|
||||||
key: ${{ runner.os }}-cmake-${{ github.run_id }}
|
key: ${{ runner.os }}-cmake-${{ hashFiles('gradle/libs.versions.toml', 'app/build.gradle.kts', 'app/src/main/cpp/CMakeLists.txt') }}
|
||||||
restore-keys: ${{ runner.os }}-cmake-
|
restore-keys: ${{ runner.os }}-cmake-
|
||||||
|
|
||||||
- name: Gradle Build
|
- name: Gradle Build
|
||||||
|
|||||||
+2
-12
@@ -65,7 +65,7 @@ android {
|
|||||||
minSdk = 28
|
minSdk = 28
|
||||||
targetSdk = 34
|
targetSdk = 34
|
||||||
versionCode = 180044
|
versionCode = 180044
|
||||||
versionName = "1.8.10.1"
|
versionName = "1.8.10.2"
|
||||||
resourceConfigurations.addAll(
|
resourceConfigurations.addAll(
|
||||||
listOf(
|
listOf(
|
||||||
"zh",
|
"zh",
|
||||||
@@ -276,7 +276,7 @@ dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
jvmToolchain(21)
|
jvmToolchain(17)
|
||||||
}
|
}
|
||||||
|
|
||||||
ksp {
|
ksp {
|
||||||
@@ -287,16 +287,6 @@ ksp {
|
|||||||
aboutLibraries {
|
aboutLibraries {
|
||||||
duplicationMode = MERGE
|
duplicationMode = MERGE
|
||||||
duplicationRule = GROUP
|
duplicationRule = GROUP
|
||||||
exclusionPatterns = listOf(
|
|
||||||
"androidx\\..*".toPattern(),
|
|
||||||
".*annotations".toPattern(),
|
|
||||||
"com\\.google\\.code\\..*".toPattern(),
|
|
||||||
"com\\.google\\.guava:listenablefuture".toPattern(),
|
|
||||||
"com\\.google\\.protobuf:protobuf-javalite".toPattern(),
|
|
||||||
"org\\.checkerframework:checker-qual".toPattern(),
|
|
||||||
"org\\.chromium\\.net:cronet-embedded".toPattern(),
|
|
||||||
"org\\.slf4j:slf4j-api".toPattern(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cargo {
|
cargo {
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
package com.hippo.ehviewer.cronet
|
package com.hippo.ehviewer.cronet
|
||||||
|
|
||||||
import com.hippo.ehviewer.EhApplication
|
|
||||||
import com.hippo.ehviewer.client.CHROME_ACCEPT
|
import com.hippo.ehviewer.client.CHROME_ACCEPT
|
||||||
import com.hippo.ehviewer.client.CHROME_ACCEPT_LANGUAGE
|
import com.hippo.ehviewer.client.CHROME_ACCEPT_LANGUAGE
|
||||||
import com.hippo.ehviewer.client.CHROME_USER_AGENT
|
import com.hippo.ehviewer.client.CHROME_USER_AGENT
|
||||||
import com.hippo.ehviewer.client.EhCookieStore
|
import com.hippo.ehviewer.client.EhCookieStore
|
||||||
import eu.kanade.tachiyomi.util.system.logcat
|
import java.nio.ByteBuffer
|
||||||
import io.ktor.utils.io.pool.DirectByteBufferPool
|
import kotlin.contracts.InvocationKind
|
||||||
|
import kotlin.contracts.contract
|
||||||
|
import kotlin.coroutines.Continuation
|
||||||
|
import kotlin.coroutines.resume
|
||||||
|
import kotlin.coroutines.resumeWithException
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
@@ -17,16 +20,6 @@ import org.chromium.net.CronetException
|
|||||||
import org.chromium.net.UrlRequest
|
import org.chromium.net.UrlRequest
|
||||||
import org.chromium.net.UrlResponseInfo
|
import org.chromium.net.UrlResponseInfo
|
||||||
import splitties.init.appCtx
|
import splitties.init.appCtx
|
||||||
import java.nio.ByteBuffer
|
|
||||||
import java.nio.channels.FileChannel
|
|
||||||
import kotlin.contracts.InvocationKind
|
|
||||||
import kotlin.contracts.contract
|
|
||||||
import kotlin.coroutines.Continuation
|
|
||||||
import kotlin.coroutines.resume
|
|
||||||
import kotlin.coroutines.resumeWithException
|
|
||||||
|
|
||||||
private const val TAG = "CronetRequest"
|
|
||||||
val pool = DirectByteBufferPool(32)
|
|
||||||
|
|
||||||
val cronetHttpClient: CronetEngine = CronetEngine.Builder(appCtx).apply {
|
val cronetHttpClient: CronetEngine = CronetEngine.Builder(appCtx).apply {
|
||||||
enableBrotli(true)
|
enableBrotli(true)
|
||||||
@@ -36,9 +29,6 @@ val cronetHttpClient: CronetEngine = CronetEngine.Builder(appCtx).apply {
|
|||||||
setUserAgent(CHROME_USER_AGENT)
|
setUserAgent(CHROME_USER_AGENT)
|
||||||
}.build()
|
}.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 {
|
class CronetRequest {
|
||||||
lateinit var consumer: (ByteBuffer) -> Unit
|
lateinit var consumer: (ByteBuffer) -> Unit
|
||||||
lateinit var onResponse: CronetRequest.(UrlResponseInfo) -> Unit
|
lateinit var onResponse: CronetRequest.(UrlResponseInfo) -> Unit
|
||||||
@@ -47,7 +37,6 @@ class CronetRequest {
|
|||||||
lateinit var readerCont: Continuation<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) {
|
override fun onRedirectReceived(req: UrlRequest, info: UrlResponseInfo, url: String) {
|
||||||
logcat(tag = TAG) { "Redirected to $url" }
|
|
||||||
req.followRedirect()
|
req.followRedirect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,29 +68,6 @@ inline fun cronetRequest(url: String, referer: String? = null, conf: UrlRequest.
|
|||||||
}.apply(conf).build()
|
}.apply(conf).build()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend inline fun CronetRequest.awaitBodyFully(crossinline callback: (ByteBuffer) -> Unit) {
|
|
||||||
val buffer = pool.borrow()
|
|
||||||
return try {
|
|
||||||
suspendCancellableCoroutine { cont ->
|
|
||||||
consumer = {
|
|
||||||
callback(it)
|
|
||||||
buffer.clear()
|
|
||||||
request.read(buffer)
|
|
||||||
}
|
|
||||||
onError = { readerCont.resumeWithException(it) }
|
|
||||||
readerCont = cont
|
|
||||||
request.read(buffer)
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
pool.recycle(buffer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend inline fun CronetRequest.copyToChannel(chan: FileChannel, crossinline listener: ((Int) -> Unit) = {}) = awaitBodyFully {
|
|
||||||
val bytes = chan.write(it)
|
|
||||||
listener(bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend inline fun <R> CronetRequest.execute(crossinline callback: suspend CronetRequest.(UrlResponseInfo) -> R): R {
|
suspend inline fun <R> CronetRequest.execute(crossinline callback: suspend CronetRequest.(UrlResponseInfo) -> R): R {
|
||||||
contract {
|
contract {
|
||||||
callsInPlace(callback, InvocationKind.EXACTLY_ONCE)
|
callsInPlace(callback, InvocationKind.EXACTLY_ONCE)
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ package com.hippo.ehviewer.util
|
|||||||
|
|
||||||
import com.google.android.gms.net.CronetProviderInstaller
|
import com.google.android.gms.net.CronetProviderInstaller
|
||||||
import com.hippo.ehviewer.Settings
|
import com.hippo.ehviewer.Settings
|
||||||
|
import org.chromium.net.CronetProvider
|
||||||
import splitties.init.appCtx
|
import splitties.init.appCtx
|
||||||
|
|
||||||
val isCronetSupported: Boolean
|
val isCronetSupported: Boolean
|
||||||
get() = Settings.enableCronet && CronetProviderInstaller.isInstalled().also {
|
get() = Settings.enableCronet && CronetProvider.getAllProviders(appCtx).any { it.isEnabled }.also {
|
||||||
if (!it) {
|
if (!it) {
|
||||||
CronetProviderInstaller.installProvider(appCtx)
|
CronetProviderInstaller.installProvider(appCtx)
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-11
@@ -23,14 +23,6 @@ import com.davemorrissey.labs.subscaleview.decoder.Decoder
|
|||||||
import com.davemorrissey.labs.subscaleview.decoder.ImageDecoder
|
import com.davemorrissey.labs.subscaleview.decoder.ImageDecoder
|
||||||
import com.davemorrissey.labs.subscaleview.provider.InputProvider
|
import com.davemorrissey.labs.subscaleview.provider.InputProvider
|
||||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import kotlinx.coroutines.SupervisorJob
|
|
||||||
import kotlinx.coroutines.cancel
|
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.concurrent.locks.ReadWriteLock
|
import java.util.concurrent.locks.ReadWriteLock
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||||
@@ -39,6 +31,14 @@ import kotlin.math.max
|
|||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
import kotlin.math.sqrt
|
import kotlin.math.sqrt
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.cancel
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays an image subsampled as necessary to avoid loading too much image data into memory. After zooming in,
|
* Displays an image subsampled as necessary to avoid loading too much image data into memory. After zooming in,
|
||||||
@@ -601,8 +601,8 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
|||||||
|
|
||||||
private fun onTouchEventInternal(event: MotionEvent): Boolean {
|
private fun onTouchEventInternal(event: MotionEvent): Boolean {
|
||||||
val touchCount = event.pointerCount
|
val touchCount = event.pointerCount
|
||||||
when (event.action) {
|
when (event.actionMasked) {
|
||||||
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_1_DOWN, MotionEvent.ACTION_POINTER_2_DOWN -> {
|
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> {
|
||||||
anim = null
|
anim = null
|
||||||
requestDisallowInterceptTouchEvent(true)
|
requestDisallowInterceptTouchEvent(true)
|
||||||
maxTouchCount = maxTouchCount.coerceAtLeast(touchCount)
|
maxTouchCount = maxTouchCount.coerceAtLeast(touchCount)
|
||||||
@@ -773,7 +773,7 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP, MotionEvent.ACTION_POINTER_2_UP -> {
|
MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> {
|
||||||
longClickJob?.cancel()
|
longClickJob?.cancel()
|
||||||
if (isQuickScaling) {
|
if (isQuickScaling) {
|
||||||
isQuickScaling = false
|
isQuickScaling = false
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ package com.hippo.ehviewer
|
|||||||
|
|
||||||
import com.hippo.ehviewer.client.EhUtils
|
import com.hippo.ehviewer.client.EhUtils
|
||||||
import com.hippo.ehviewer.client.data.FavListUrlBuilder
|
import com.hippo.ehviewer.client.data.FavListUrlBuilder
|
||||||
|
import java.util.Locale
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.asFlow
|
import kotlinx.coroutines.flow.asFlow
|
||||||
@@ -12,8 +14,6 @@ import kotlinx.coroutines.flow.debounce
|
|||||||
import kotlinx.coroutines.flow.flatMapMerge
|
import kotlinx.coroutines.flow.flatMapMerge
|
||||||
import splitties.preferences.DataStorePreferences
|
import splitties.preferences.DataStorePreferences
|
||||||
import splitties.preferences.edit
|
import splitties.preferences.edit
|
||||||
import java.util.Locale
|
|
||||||
import kotlin.reflect.KProperty
|
|
||||||
|
|
||||||
object Settings : DataStorePreferences(null) {
|
object Settings : DataStorePreferences(null) {
|
||||||
private const val KEY_SHOW_TAG_TRANSLATIONS = "show_tag_translations"
|
private const val KEY_SHOW_TAG_TRANSLATIONS = "show_tag_translations"
|
||||||
|
|||||||
@@ -16,6 +16,11 @@
|
|||||||
package com.hippo.ehviewer.client
|
package com.hippo.ehviewer.client
|
||||||
|
|
||||||
import android.webkit.CookieManager
|
import android.webkit.CookieManager
|
||||||
|
import androidx.compose.ui.util.fastAny
|
||||||
|
import androidx.compose.ui.util.fastFilter
|
||||||
|
import androidx.compose.ui.util.fastForEach
|
||||||
|
import androidx.compose.ui.util.fastJoinToString
|
||||||
|
import androidx.compose.ui.util.fastMapNotNull
|
||||||
import okhttp3.Cookie
|
import okhttp3.Cookie
|
||||||
import okhttp3.CookieJar
|
import okhttp3.CookieJar
|
||||||
import okhttp3.HttpUrl
|
import okhttp3.HttpUrl
|
||||||
@@ -24,13 +29,15 @@ import okhttp3.HttpUrl.Companion.toHttpUrl
|
|||||||
object EhCookieStore : CookieJar {
|
object EhCookieStore : CookieJar {
|
||||||
private val manager = CookieManager.getInstance()
|
private val manager = CookieManager.getInstance()
|
||||||
fun signOut() = manager.removeAllCookies(null)
|
fun signOut() = manager.removeAllCookies(null)
|
||||||
fun contains(url: HttpUrl, name: String) = get(url).any { it.name == name }
|
fun contains(url: HttpUrl, name: String) = get(url).fastAny { it.name == name }
|
||||||
|
|
||||||
fun get(url: HttpUrl): List<Cookie> {
|
fun get(url: HttpUrl): List<Cookie> {
|
||||||
val cookies = manager.getCookie(url.toString())
|
val cookies = manager.getCookie(url.toString())
|
||||||
|
|
||||||
return if (cookies != null && cookies.isNotEmpty()) {
|
return if (!cookies.isNullOrEmpty()) {
|
||||||
cookies.split(";").mapNotNull { Cookie.parse(url, it) }.filterNot { it.name == KEY_UTMP_NAME }
|
cookies.split(";").fastMapNotNull { setCookie ->
|
||||||
|
Cookie.parse(url, setCookie)?.takeUnless { it.name == KEY_UTMP_NAME }
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
emptyList()
|
emptyList()
|
||||||
}
|
}
|
||||||
@@ -57,9 +64,12 @@ object EhCookieStore : CookieJar {
|
|||||||
}.build()
|
}.build()
|
||||||
|
|
||||||
fun copyNecessaryCookies() {
|
fun copyNecessaryCookies() {
|
||||||
val cookie = get(EhUrl.HOST_E.toHttpUrl()).filter { it.name == KEY_STAR || it.name == KEY_IPB_MEMBER_ID || it.name == KEY_IPB_PASS_HASH || it.name == KEY_IGNEOUS }
|
val cookies = get(EhUrl.HOST_E.toHttpUrl())
|
||||||
cookie.forEach { manager.setCookie(EhUrl.HOST_EX, it.toString()) }
|
cookies.fastForEach {
|
||||||
flush()
|
if (it.name == KEY_STAR || it.name == KEY_IPB_MEMBER_ID || it.name == KEY_IPB_PASS_HASH) {
|
||||||
|
manager.setCookie(EhUrl.HOST_EX, it.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteCookie(url: HttpUrl, name: String) {
|
fun deleteCookie(url: HttpUrl, name: String) {
|
||||||
@@ -67,35 +77,29 @@ object EhCookieStore : CookieJar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun addCookie(cookie: Cookie) {
|
fun addCookie(cookie: Cookie) {
|
||||||
if (EhUrl.DOMAIN_E in cookie.domain) manager.setCookie(EhUrl.HOST_E, cookie.toString()) else manager.setCookie(EhUrl.DOMAIN_EX, cookie.toString())
|
manager.setCookie(if (EhUrl.DOMAIN_E == cookie.domain) EhUrl.HOST_E else EhUrl.HOST_EX, cookie.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun flush() = manager.flush()
|
fun flush() = manager.flush()
|
||||||
|
|
||||||
fun getCookieHeader(url: HttpUrl): String {
|
fun getCookieHeader(url: HttpUrl): String {
|
||||||
val cookies = loadForRequest(url)
|
return loadForRequest(url).fastJoinToString("; ") {
|
||||||
val cookieHeader = StringBuilder()
|
"${it.name}=${it.value}"
|
||||||
var i = 0
|
|
||||||
val size = cookies.size
|
|
||||||
while (i < size) {
|
|
||||||
if (i > 0) {
|
|
||||||
cookieHeader.append("; ")
|
|
||||||
}
|
|
||||||
val cookie = cookies[i]
|
|
||||||
cookieHeader.append(cookie.name).append('=').append(cookie.value)
|
|
||||||
i++
|
|
||||||
}
|
}
|
||||||
return cookieHeader.toString()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// See https://github.com/Ehviewer-Overhauled/Ehviewer/issues/873
|
// See https://github.com/Ehviewer-Overhauled/Ehviewer/issues/873
|
||||||
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) = cookies.filterNot { it.name == KEY_UTMP_NAME }.forEach { manager.setCookie(url.toString(), it.toString()) }.also { flush() }
|
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) = cookies.fastForEach {
|
||||||
|
if (it.name != KEY_UTMP_NAME) {
|
||||||
|
manager.setCookie(url.toString(), it.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun loadForRequest(url: HttpUrl): List<Cookie> {
|
override fun loadForRequest(url: HttpUrl): List<Cookie> {
|
||||||
val checkTips = EhUrl.DOMAIN_E in url.host
|
val checkTips = EhUrl.DOMAIN_E in url.host
|
||||||
return get(url).run {
|
return get(url).run {
|
||||||
if (checkTips) {
|
if (checkTips) {
|
||||||
filterNot { it.name == KEY_CONTENT_WARNING }.toMutableList().apply { add(sTipsCookie) }
|
fastFilter { it.name != KEY_CONTENT_WARNING }.toMutableList().apply { add(sTipsCookie) }
|
||||||
} else {
|
} else {
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,10 @@ import com.hippo.ehviewer.util.AppConfig
|
|||||||
import com.hippo.ehviewer.util.StatusCodeException
|
import com.hippo.ehviewer.util.StatusCodeException
|
||||||
import com.hippo.ehviewer.util.isCronetSupported
|
import com.hippo.ehviewer.util.isCronetSupported
|
||||||
import io.ktor.utils.io.pool.DirectByteBufferPool
|
import io.ktor.utils.io.pool.DirectByteBufferPool
|
||||||
|
import io.ktor.utils.io.pool.useInstance
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
import kotlin.math.ceil
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.serialization.json.add
|
import kotlinx.serialization.json.add
|
||||||
@@ -68,9 +72,6 @@ import okhttp3.Request
|
|||||||
import okhttp3.RequestBody.Companion.asRequestBody
|
import okhttp3.RequestBody.Companion.asRequestBody
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
import splitties.init.appCtx
|
import splitties.init.appCtx
|
||||||
import java.io.File
|
|
||||||
import java.nio.ByteBuffer
|
|
||||||
import kotlin.math.ceil
|
|
||||||
|
|
||||||
private const val TAG = "EhEngine"
|
private const val TAG = "EhEngine"
|
||||||
private const val MAX_REQUEST_SIZE = 25
|
private const val MAX_REQUEST_SIZE = 25
|
||||||
@@ -116,33 +117,27 @@ private fun rethrowExactly(code: Int, body: String, e: Throwable): Nothing {
|
|||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
|
|
||||||
val httpContentPool = DirectByteBufferPool(16, 16384)
|
val httpContentPool = DirectByteBufferPool(16, 0x20000)
|
||||||
|
|
||||||
suspend inline fun <T> fetchCompat(url: String, referer: String? = null, crossinline parser: (ByteBuffer) -> T): T {
|
suspend inline fun <T> fetchCompat(url: String, referer: String? = null, crossinline parser: (ByteBuffer) -> T): T {
|
||||||
return if (isCronetSupported) {
|
return if (isCronetSupported) {
|
||||||
cronetRequest(url, referer).execute {
|
cronetRequest(url, referer).execute {
|
||||||
val buffer = httpContentPool.borrow()
|
httpContentPool.useInstance { buffer ->
|
||||||
try {
|
|
||||||
awaitBodyFully {
|
awaitBodyFully {
|
||||||
buffer.put(it)
|
buffer.put(it)
|
||||||
}
|
}
|
||||||
buffer.flip()
|
buffer.flip()
|
||||||
parser(buffer)
|
parser(buffer)
|
||||||
} finally {
|
|
||||||
httpContentPool.recycle(buffer)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ehRequest(url, referer).execute {
|
ehRequest(url, referer).execute {
|
||||||
val buffer = httpContentPool.borrow()
|
httpContentPool.useInstance { buffer ->
|
||||||
try {
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (body.source().read(buffer) == -1) break
|
if (body.source().read(buffer) == -1) break
|
||||||
}
|
}
|
||||||
buffer.flip()
|
buffer.flip()
|
||||||
parser(buffer)
|
parser(buffer)
|
||||||
} finally {
|
|
||||||
httpContentPool.recycle(buffer)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ import android.util.Log
|
|||||||
import com.hippo.ehviewer.EhApplication.Companion.baseOkHttpClient
|
import com.hippo.ehviewer.EhApplication.Companion.baseOkHttpClient
|
||||||
import com.hippo.ehviewer.EhApplication.Companion.noRedirectOkHttpClient
|
import com.hippo.ehviewer.EhApplication.Companion.noRedirectOkHttpClient
|
||||||
import com.hippo.ehviewer.EhApplication.Companion.okHttpClient
|
import com.hippo.ehviewer.EhApplication.Companion.okHttpClient
|
||||||
|
import kotlin.contracts.InvocationKind
|
||||||
|
import kotlin.contracts.contract
|
||||||
|
import kotlin.coroutines.resume
|
||||||
|
import kotlin.coroutines.resumeWithException
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
@@ -38,10 +42,6 @@ import okhttp3.RequestBody.Companion.toRequestBody
|
|||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import okhttp3.executeAsync
|
import okhttp3.executeAsync
|
||||||
import okio.BufferedSource
|
import okio.BufferedSource
|
||||||
import kotlin.contracts.InvocationKind
|
|
||||||
import kotlin.contracts.contract
|
|
||||||
import kotlin.coroutines.resume
|
|
||||||
import kotlin.coroutines.resumeWithException
|
|
||||||
|
|
||||||
inline fun ehRequest(url: String, referer: String? = null, origin: String? = null, builder: Request.Builder.() -> Unit = {}) = Request.Builder().url(url).apply {
|
inline fun ehRequest(url: String, referer: String? = null, origin: String? = null, builder: Request.Builder.() -> Unit = {}) = Request.Builder().url(url).apply {
|
||||||
addHeader("User-Agent", CHROME_USER_AGENT)
|
addHeader("User-Agent", CHROME_USER_AGENT)
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ import com.hippo.ehviewer.R
|
|||||||
import com.hippo.ehviewer.util.AppConfig
|
import com.hippo.ehviewer.util.AppConfig
|
||||||
import com.hippo.ehviewer.util.FileUtils
|
import com.hippo.ehviewer.util.FileUtils
|
||||||
import com.hippo.ehviewer.util.copyToFile
|
import com.hippo.ehviewer.util.copyToFile
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.charset.StandardCharsets
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
@@ -42,8 +44,6 @@ import okio.blackholeSink
|
|||||||
import okio.buffer
|
import okio.buffer
|
||||||
import okio.source
|
import okio.source
|
||||||
import splitties.init.appCtx
|
import splitties.init.appCtx
|
||||||
import java.io.File
|
|
||||||
import java.nio.charset.StandardCharsets
|
|
||||||
|
|
||||||
object EhTagDatabase : CoroutineScope {
|
object EhTagDatabase : CoroutineScope {
|
||||||
private const val NAMESPACE_PREFIX = "n"
|
private const val NAMESPACE_PREFIX = "n"
|
||||||
|
|||||||
@@ -64,10 +64,14 @@ object EhUrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val apiUrl: String
|
val apiUrl: String
|
||||||
get() = when (Settings.gallerySite) {
|
get() = if (Settings.forceEhThumb) {
|
||||||
SITE_E -> API_E
|
API_E
|
||||||
SITE_EX -> API_EX
|
} else {
|
||||||
else -> API_E
|
when (Settings.gallerySite) {
|
||||||
|
SITE_E -> API_E
|
||||||
|
SITE_EX -> API_EX
|
||||||
|
else -> API_E
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val referer: String
|
val referer: String
|
||||||
|
|||||||
@@ -25,9 +25,9 @@ import com.hippo.ehviewer.dao.QuickSearch
|
|||||||
import com.hippo.ehviewer.ui.main.AdvanceTable
|
import com.hippo.ehviewer.ui.main.AdvanceTable
|
||||||
import com.hippo.ehviewer.util.encodeUTF8
|
import com.hippo.ehviewer.util.encodeUTF8
|
||||||
import com.hippo.ehviewer.util.toIntOrDefault
|
import com.hippo.ehviewer.util.toIntOrDefault
|
||||||
import kotlinx.parcelize.Parcelize
|
|
||||||
import java.io.UnsupportedEncodingException
|
import java.io.UnsupportedEncodingException
|
||||||
import java.net.URLDecoder
|
import java.net.URLDecoder
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class ListUrlBuilder(
|
data class ListUrlBuilder(
|
||||||
@@ -320,66 +320,67 @@ data class ListUrlBuilder(
|
|||||||
// Pages
|
// Pages
|
||||||
if (pageFrom != -1 || pageTo != -1) {
|
if (pageFrom != -1 || pageTo != -1) {
|
||||||
addQueryParameter("f_sp", "on")
|
addQueryParameter("f_sp", "on")
|
||||||
addQueryParameter(
|
addQueryParameterIfNotBlank(
|
||||||
"f_spf",
|
"f_spf",
|
||||||
(if (pageFrom != -1) pageFrom.toString() else "").toString(),
|
pageFrom.takeUnless { it == -1 }?.toString(),
|
||||||
)
|
)
|
||||||
addQueryParameter(
|
addQueryParameterIfNotBlank(
|
||||||
"f_spt",
|
"f_spt",
|
||||||
(if (pageTo != -1) pageTo.toString() else "").toString(),
|
pageTo.takeUnless { it == -1 }?.toString(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.toString()
|
}.toString()
|
||||||
|
|
||||||
MODE_UPLOADER -> {
|
MODE_UPLOADER -> {
|
||||||
val sb = StringBuilder(EhUrl.host)
|
buildString {
|
||||||
mKeyword?.let {
|
append(EhUrl.host)
|
||||||
sb.append("uploader/")
|
mKeyword?.let {
|
||||||
sb.append(encodeUTF8(it))
|
append("uploader/").append(encodeUTF8(it))
|
||||||
|
}
|
||||||
|
mPrev?.let {
|
||||||
|
append("?prev=").append(it)
|
||||||
|
}
|
||||||
|
mNext?.let {
|
||||||
|
append("?next=").append(it)
|
||||||
|
}
|
||||||
|
mJumpTo?.let {
|
||||||
|
append("&seek=").append(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mPrev?.let {
|
|
||||||
sb.append("?prev=").append(it)
|
|
||||||
}
|
|
||||||
mNext?.let {
|
|
||||||
sb.append("?next=").append(it)
|
|
||||||
}
|
|
||||||
mJumpTo?.let {
|
|
||||||
sb.append("&seek=").append(it)
|
|
||||||
}
|
|
||||||
sb.toString()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MODE_TAG -> {
|
MODE_TAG -> {
|
||||||
val sb = StringBuilder(EhUrl.host)
|
buildString {
|
||||||
mKeyword?.let {
|
append(EhUrl.host)
|
||||||
sb.append("tag/")
|
mKeyword?.let {
|
||||||
sb.append(encodeUTF8(it))
|
append("tag/").append(encodeUTF8(it))
|
||||||
|
}
|
||||||
|
mPrev?.let {
|
||||||
|
append("?prev=").append(it)
|
||||||
|
}
|
||||||
|
mNext?.let {
|
||||||
|
append("?next=").append(it)
|
||||||
|
}
|
||||||
|
mJumpTo?.let {
|
||||||
|
append("&seek=").append(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mPrev?.let {
|
|
||||||
sb.append("?prev=").append(it)
|
|
||||||
}
|
|
||||||
mNext?.let {
|
|
||||||
sb.append("?next=").append(it)
|
|
||||||
}
|
|
||||||
mJumpTo?.let {
|
|
||||||
sb.append("&seek=").append(it)
|
|
||||||
}
|
|
||||||
sb.toString()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MODE_WHATS_HOT -> EhUrl.popularUrl
|
MODE_WHATS_HOT -> EhUrl.popularUrl
|
||||||
MODE_IMAGE_SEARCH -> EhUrl.imageSearchUrl
|
MODE_IMAGE_SEARCH -> EhUrl.imageSearchUrl
|
||||||
MODE_TOPLIST -> {
|
MODE_TOPLIST -> {
|
||||||
val sb = StringBuilder(EhUrl.HOST_E)
|
buildString {
|
||||||
sb.append("toplist.php?tl=")
|
append(EhUrl.HOST_E)
|
||||||
mKeyword.orEmpty().let {
|
append("toplist.php?tl=")
|
||||||
sb.append(encodeUTF8(it))
|
mKeyword.orEmpty().let {
|
||||||
|
append(encodeUTF8(it))
|
||||||
|
}
|
||||||
|
mJumpTo?.let {
|
||||||
|
append("&p=").append(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mJumpTo?.let {
|
|
||||||
sb.append("&p=").append(it)
|
|
||||||
}
|
|
||||||
sb.toString()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> throw IllegalStateException("Unexpected value: $mode")
|
else -> throw IllegalStateException("Unexpected value: $mode")
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ import com.hippo.ehviewer.util.toFloatOrDefault
|
|||||||
import com.hippo.ehviewer.util.toIntOrDefault
|
import com.hippo.ehviewer.util.toIntOrDefault
|
||||||
import com.hippo.ehviewer.util.trimAnd
|
import com.hippo.ehviewer.util.trimAnd
|
||||||
import com.hippo.ehviewer.util.unescapeXml
|
import com.hippo.ehviewer.util.unescapeXml
|
||||||
|
import java.time.Instant
|
||||||
|
import java.time.ZoneOffset
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
import java.util.Locale
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
import org.jsoup.nodes.Document
|
import org.jsoup.nodes.Document
|
||||||
import org.jsoup.nodes.Element
|
import org.jsoup.nodes.Element
|
||||||
@@ -48,10 +52,6 @@ import org.jsoup.nodes.Node
|
|||||||
import org.jsoup.select.Elements
|
import org.jsoup.select.Elements
|
||||||
import org.jsoup.select.NodeTraversor
|
import org.jsoup.select.NodeTraversor
|
||||||
import org.jsoup.select.NodeVisitor
|
import org.jsoup.select.NodeVisitor
|
||||||
import java.time.Instant
|
|
||||||
import java.time.ZoneOffset
|
|
||||||
import java.time.format.DateTimeFormatter
|
|
||||||
import java.util.Locale
|
|
||||||
|
|
||||||
object GalleryDetailParser {
|
object GalleryDetailParser {
|
||||||
private val PATTERN_ERROR = Regex("<div class=\"d\">\n<p>([^<]+)</p>")
|
private val PATTERN_ERROR = Regex("<div class=\"d\">\n<p>([^<]+)</p>")
|
||||||
|
|||||||
@@ -31,10 +31,10 @@ object GalleryPageApiParser {
|
|||||||
val imageUrl = PATTERN_IMAGE_URL.find(res.imageUrl)?.run {
|
val imageUrl = PATTERN_IMAGE_URL.find(res.imageUrl)?.run {
|
||||||
groupValues[1].trim().unescapeXml()
|
groupValues[1].trim().unescapeXml()
|
||||||
}
|
}
|
||||||
val skipHathKey = PATTERN_SKIP_HATH_KEY.find(res.skipHathKey)?.run {
|
val skipHathKey = PATTERN_SKIP_HATH_KEY.find(res.skipHathKeyAndOriginImageUrl)?.run {
|
||||||
groupValues[1].trim().unescapeXml()
|
groupValues[1].trim().unescapeXml()
|
||||||
}
|
}
|
||||||
val originImageUrl = PATTERN_ORIGIN_IMAGE_URL.find(res.originImageUrl)?.run {
|
val originImageUrl = PATTERN_ORIGIN_IMAGE_URL.find(res.skipHathKeyAndOriginImageUrl)?.run {
|
||||||
groupValues[1].unescapeXml() + "fullimg.php" + groupValues[2].unescapeXml()
|
groupValues[1].unescapeXml() + "fullimg.php" + groupValues[2].unescapeXml()
|
||||||
}
|
}
|
||||||
check(!imageUrl.isNullOrEmpty())
|
check(!imageUrl.isNullOrEmpty())
|
||||||
@@ -49,9 +49,7 @@ object GalleryPageApiParser {
|
|||||||
@SerialName("i3")
|
@SerialName("i3")
|
||||||
val imageUrl: String,
|
val imageUrl: String,
|
||||||
@SerialName("i6")
|
@SerialName("i6")
|
||||||
val skipHathKey: String,
|
val skipHathKeyAndOriginImageUrl: String,
|
||||||
@SerialName("i7")
|
|
||||||
val originImageUrl: String,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
class Result(val imageUrl: String, val skipHathKey: String?, val originImageUrl: String?)
|
class Result(val imageUrl: String, val skipHathKey: String?, val originImageUrl: String?)
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ package com.hippo.ehviewer.client.parser
|
|||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import com.hippo.ehviewer.client.exception.InsufficientFundsException
|
import com.hippo.ehviewer.client.exception.InsufficientFundsException
|
||||||
import com.hippo.ehviewer.client.exception.ParseException
|
import com.hippo.ehviewer.client.exception.ParseException
|
||||||
import kotlinx.parcelize.Parcelize
|
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
object HomeParser {
|
object HomeParser {
|
||||||
private val PATTERN_FUNDS = Regex("Available: ([\\d,]+) Credits.*Available: ([\\d,]+) kGP", RegexOption.DOT_MATCHES_ALL)
|
private val PATTERN_FUNDS = Regex("Available: ([\\d,]+) Credits.*Available: ([\\d,]+) kGP", RegexOption.DOT_MATCHES_ALL)
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package com.hippo.ehviewer.coil
|
|||||||
import coil.intercept.Interceptor
|
import coil.intercept.Interceptor
|
||||||
import coil.request.ImageResult
|
import coil.request.ImageResult
|
||||||
import com.hippo.ehviewer.Settings
|
import com.hippo.ehviewer.Settings
|
||||||
|
import kotlin.coroutines.resume
|
||||||
|
import kotlin.coroutines.resumeWithException
|
||||||
import kotlinx.coroutines.CancellableContinuation
|
import kotlinx.coroutines.CancellableContinuation
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@@ -11,8 +13,6 @@ import kotlinx.coroutines.SupervisorJob
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlin.coroutines.resume
|
|
||||||
import kotlin.coroutines.resumeWithException
|
|
||||||
|
|
||||||
object MergeInterceptor : Interceptor {
|
object MergeInterceptor : Interceptor {
|
||||||
private val pendingContinuationMap: HashMap<String, MutableList<CancellableContinuation<Unit>>> = hashMapOf()
|
private val pendingContinuationMap: HashMap<String, MutableList<CancellableContinuation<Unit>>> = hashMapOf()
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.hippo.ehviewer.cronet
|
||||||
|
|
||||||
|
import com.hippo.ehviewer.EhApplication
|
||||||
|
import io.ktor.utils.io.pool.DirectByteBufferPool
|
||||||
|
import io.ktor.utils.io.pool.useInstance
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
import java.nio.channels.FileChannel
|
||||||
|
import kotlin.coroutines.resumeWithException
|
||||||
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
|
|
||||||
|
val pool = DirectByteBufferPool(32)
|
||||||
|
|
||||||
|
val cronetHttpClientExecutor = EhApplication.baseOkHttpClient.dispatcher.executorService
|
||||||
|
|
||||||
|
suspend inline fun CronetRequest.awaitBodyFully(crossinline callback: (ByteBuffer) -> Unit) {
|
||||||
|
return pool.useInstance { buffer ->
|
||||||
|
suspendCancellableCoroutine { cont ->
|
||||||
|
consumer = {
|
||||||
|
callback(it)
|
||||||
|
buffer.clear()
|
||||||
|
request.read(buffer)
|
||||||
|
}
|
||||||
|
onError = { readerCont.resumeWithException(it) }
|
||||||
|
readerCont = cont
|
||||||
|
request.read(buffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend inline fun CronetRequest.copyToChannel(chan: FileChannel, crossinline listener: ((Int) -> Unit) = {}) = awaitBodyFully {
|
||||||
|
val bytes = chan.write(it)
|
||||||
|
listener(bytes)
|
||||||
|
}
|
||||||
@@ -24,12 +24,12 @@ import com.hippo.ehviewer.Settings
|
|||||||
import com.hippo.ehviewer.client.EhCookieStore
|
import com.hippo.ehviewer.client.EhCookieStore
|
||||||
import com.hippo.ehviewer.client.EhEngine
|
import com.hippo.ehviewer.client.EhEngine
|
||||||
import eu.kanade.tachiyomi.util.lang.withIOContext
|
import eu.kanade.tachiyomi.util.lang.withIOContext
|
||||||
import splitties.init.appCtx
|
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.time.temporal.ChronoUnit
|
import java.time.temporal.ChronoUnit
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
import splitties.init.appCtx
|
||||||
|
|
||||||
private val signedIn
|
private val signedIn
|
||||||
get() = EhCookieStore.hasSignedIn()
|
get() = EhCookieStore.hasSignedIn()
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import com.hippo.ehviewer.util.runAssertingNotMainThread
|
|||||||
import com.hippo.unifile.UniFile
|
import com.hippo.unifile.UniFile
|
||||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||||
|
import java.util.LinkedList
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.channels.awaitClose
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
@@ -50,7 +51,6 @@ import kotlinx.coroutines.flow.shareIn
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import splitties.init.appCtx
|
import splitties.init.appCtx
|
||||||
import splitties.preferences.edit
|
import splitties.preferences.edit
|
||||||
import java.util.LinkedList
|
|
||||||
|
|
||||||
object DownloadManager : OnSpiderListener {
|
object DownloadManager : OnSpiderListener {
|
||||||
// All download info list
|
// All download info list
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import com.hippo.ehviewer.jni.providePassword
|
|||||||
import com.hippo.ehviewer.jni.releaseByteBuffer
|
import com.hippo.ehviewer.jni.releaseByteBuffer
|
||||||
import com.hippo.ehviewer.util.FileUtils
|
import com.hippo.ehviewer.util.FileUtils
|
||||||
import com.hippo.unifile.UniFile
|
import com.hippo.unifile.UniFile
|
||||||
|
import java.nio.ByteBuffer
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.CoroutineStart
|
import kotlinx.coroutines.CoroutineStart
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -48,7 +49,6 @@ import kotlinx.coroutines.sync.Semaphore
|
|||||||
import kotlinx.coroutines.sync.withPermit
|
import kotlinx.coroutines.sync.withPermit
|
||||||
import moe.tarsin.coroutines.NamedMutex
|
import moe.tarsin.coroutines.NamedMutex
|
||||||
import moe.tarsin.coroutines.withLock
|
import moe.tarsin.coroutines.withLock
|
||||||
import java.nio.ByteBuffer
|
|
||||||
|
|
||||||
class ArchivePageLoader(context: Context, private val uri: Uri, passwdFlow: Flow<String>) : PageLoader2(), CoroutineScope {
|
class ArchivePageLoader(context: Context, private val uri: Uri, passwdFlow: Flow<String>) : PageLoader2(), CoroutineScope {
|
||||||
override val coroutineContext = Dispatchers.IO + Job()
|
override val coroutineContext = Dispatchers.IO + Job()
|
||||||
|
|||||||
@@ -46,11 +46,11 @@ import com.hippo.ehviewer.util.isAtLeastP
|
|||||||
import com.hippo.ehviewer.util.isAtLeastU
|
import com.hippo.ehviewer.util.isAtLeastU
|
||||||
import com.hippo.unifile.UniFile
|
import com.hippo.unifile.UniFile
|
||||||
import com.hippo.unifile.openInputStream
|
import com.hippo.unifile.openInputStream
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
import kotlin.math.min
|
||||||
import okio.buffer
|
import okio.buffer
|
||||||
import okio.source
|
import okio.source
|
||||||
import splitties.init.appCtx
|
import splitties.init.appCtx
|
||||||
import java.nio.ByteBuffer
|
|
||||||
import kotlin.math.min
|
|
||||||
|
|
||||||
class Image private constructor(drawable: Drawable, private val src: AutoCloseable) {
|
class Image private constructor(drawable: Drawable, private val src: AutoCloseable) {
|
||||||
var mObtainedDrawable: Drawable? = drawable
|
var mObtainedDrawable: Drawable? = drawable
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ package com.hippo.ehviewer.legacy
|
|||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
|
|
||||||
private val OBSOLETE_CACHE_DIRS = arrayOf(
|
private val OBSOLETE_CACHE_DIRS = arrayOf(
|
||||||
"image",
|
"image",
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package com.hippo.ehviewer.legacy
|
package com.hippo.ehviewer.legacy
|
||||||
|
|
||||||
import com.hippo.ehviewer.spider.SpiderInfo
|
import com.hippo.ehviewer.spider.SpiderInfo
|
||||||
|
import java.io.InputStream
|
||||||
import okio.buffer
|
import okio.buffer
|
||||||
import okio.source
|
import okio.source
|
||||||
import java.io.InputStream
|
|
||||||
|
|
||||||
fun readLegacySpiderInfo(inputStream: InputStream): SpiderInfo {
|
fun readLegacySpiderInfo(inputStream: InputStream): SpiderInfo {
|
||||||
val source = inputStream.source().buffer()
|
val source = inputStream.source().buffer()
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ package com.hippo.ehviewer.spider
|
|||||||
|
|
||||||
import android.os.ParcelFileDescriptor
|
import android.os.ParcelFileDescriptor
|
||||||
import android.os.ParcelFileDescriptor.MODE_READ_WRITE
|
import android.os.ParcelFileDescriptor.MODE_READ_WRITE
|
||||||
|
import com.hippo.ehviewer.EhApplication.Companion.imageCache as sCache
|
||||||
import com.hippo.ehviewer.EhDB
|
import com.hippo.ehviewer.EhDB
|
||||||
import com.hippo.ehviewer.client.EhUtils.getSuitableTitle
|
import com.hippo.ehviewer.client.EhUtils.getSuitableTitle
|
||||||
import com.hippo.ehviewer.client.data.GalleryInfo
|
import com.hippo.ehviewer.client.data.GalleryInfo
|
||||||
@@ -38,14 +39,13 @@ import com.hippo.ehviewer.util.isCronetSupported
|
|||||||
import com.hippo.ehviewer.util.sendTo
|
import com.hippo.ehviewer.util.sendTo
|
||||||
import com.hippo.unifile.UniFile
|
import com.hippo.unifile.UniFile
|
||||||
import com.hippo.unifile.openOutputStream
|
import com.hippo.unifile.openOutputStream
|
||||||
|
import java.util.Locale
|
||||||
|
import kotlin.io.path.readText
|
||||||
import okhttp3.MediaType.Companion.toMediaType
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import okio.Buffer
|
import okio.Buffer
|
||||||
import okio.ForwardingSink
|
import okio.ForwardingSink
|
||||||
import okio.sink
|
import okio.sink
|
||||||
import java.util.Locale
|
|
||||||
import kotlin.io.path.readText
|
|
||||||
import com.hippo.ehviewer.EhApplication.Companion.imageCache as sCache
|
|
||||||
|
|
||||||
class SpiderDen(mGalleryInfo: GalleryInfo) {
|
class SpiderDen(mGalleryInfo: GalleryInfo) {
|
||||||
private val mGid = mGalleryInfo.gid
|
private val mGid = mGalleryInfo.gid
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ import com.hippo.ehviewer.legacy.readLegacySpiderInfo
|
|||||||
import com.hippo.unifile.UniFile
|
import com.hippo.unifile.UniFile
|
||||||
import com.hippo.unifile.openInputStream
|
import com.hippo.unifile.openInputStream
|
||||||
import com.hippo.unifile.openOutputStream
|
import com.hippo.unifile.openOutputStream
|
||||||
|
import java.io.File
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.cbor.Cbor
|
import kotlinx.serialization.cbor.Cbor
|
||||||
import kotlinx.serialization.decodeFromByteArray
|
import kotlinx.serialization.decodeFromByteArray
|
||||||
import kotlinx.serialization.encodeToByteArray
|
import kotlinx.serialization.encodeToByteArray
|
||||||
import moe.tarsin.coroutines.runSuspendCatching
|
import moe.tarsin.coroutines.runSuspendCatching
|
||||||
import splitties.init.appCtx
|
import splitties.init.appCtx
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class SpiderInfo(
|
class SpiderInfo(
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ import com.hippo.ehviewer.client.EhUrl.getGalleryMultiPageViewerUrl
|
|||||||
import com.hippo.ehviewer.client.EhUrl.referer
|
import com.hippo.ehviewer.client.EhUrl.referer
|
||||||
import com.hippo.ehviewer.client.data.GalleryInfo
|
import com.hippo.ehviewer.client.data.GalleryInfo
|
||||||
import com.hippo.ehviewer.client.ehRequest
|
import com.hippo.ehviewer.client.ehRequest
|
||||||
import com.hippo.ehviewer.client.exception.ParseException
|
|
||||||
import com.hippo.ehviewer.client.exception.QuotaExceededException
|
import com.hippo.ehviewer.client.exception.QuotaExceededException
|
||||||
import com.hippo.ehviewer.client.execute
|
import com.hippo.ehviewer.client.execute
|
||||||
import com.hippo.ehviewer.client.parser.GalleryDetailParser.parsePages
|
import com.hippo.ehviewer.client.parser.GalleryDetailParser.parsePages
|
||||||
@@ -42,6 +41,9 @@ import com.hippo.ehviewer.image.Image
|
|||||||
import com.hippo.ehviewer.util.ExceptionUtils
|
import com.hippo.ehviewer.util.ExceptionUtils
|
||||||
import com.hippo.unifile.UniFile
|
import com.hippo.unifile.UniFile
|
||||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||||
|
import java.time.Instant
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -59,9 +61,6 @@ import kotlinx.coroutines.sync.withPermit
|
|||||||
import kotlinx.coroutines.withTimeout
|
import kotlinx.coroutines.withTimeout
|
||||||
import moe.tarsin.coroutines.runSuspendCatching
|
import moe.tarsin.coroutines.runSuspendCatching
|
||||||
import splitties.init.appCtx
|
import splitties.init.appCtx
|
||||||
import java.time.Instant
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
|
||||||
import kotlin.time.Duration.Companion.seconds
|
|
||||||
|
|
||||||
class SpiderQueen private constructor(val galleryInfo: GalleryInfo) : CoroutineScope {
|
class SpiderQueen private constructor(val galleryInfo: GalleryInfo) : CoroutineScope {
|
||||||
override val coroutineContext = Dispatchers.IO + Job()
|
override val coroutineContext = Dispatchers.IO + Job()
|
||||||
@@ -650,7 +649,7 @@ class SpiderQueen private constructor(val galleryInfo: GalleryInfo) : CoroutineS
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (imageUrl == null) {
|
if (imageUrl == null) {
|
||||||
runCatching {
|
runSuspendCatching {
|
||||||
EhEngine.getGalleryPageApi(
|
EhEngine.getGalleryPageApi(
|
||||||
mSpiderInfo.gid,
|
mSpiderInfo.gid,
|
||||||
index,
|
index,
|
||||||
@@ -664,13 +663,8 @@ class SpiderQueen private constructor(val galleryInfo: GalleryInfo) : CoroutineS
|
|||||||
originImageUrl = it.originImageUrl
|
originImageUrl = it.originImageUrl
|
||||||
}
|
}
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
if (it is ParseException && "Key mismatch" == it.message) {
|
forceHtml = true
|
||||||
// Show key is wrong, enter a new loop to get the new show key
|
return@repeat
|
||||||
if (showKey == localShowKey) showKey = null
|
|
||||||
return@repeat
|
|
||||||
} else {
|
|
||||||
throw it
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -69,12 +69,12 @@ import com.hippo.ehviewer.util.getParcelableExtraCompat
|
|||||||
import com.hippo.ehviewer.util.getUrlFromClipboard
|
import com.hippo.ehviewer.util.getUrlFromClipboard
|
||||||
import eu.kanade.tachiyomi.util.lang.launchUI
|
import eu.kanade.tachiyomi.util.lang.launchUI
|
||||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import splitties.systemservices.clipboardManager
|
import splitties.systemservices.clipboardManager
|
||||||
import splitties.systemservices.connectivityManager
|
import splitties.systemservices.connectivityManager
|
||||||
import java.io.File
|
|
||||||
import java.io.FileOutputStream
|
|
||||||
|
|
||||||
class MainActivity : EhActivity() {
|
class MainActivity : EhActivity() {
|
||||||
private lateinit var binding: ActivityMainBinding
|
private lateinit var binding: ActivityMainBinding
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ import androidx.compose.foundation.rememberScrollState
|
|||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.ElevatedCard
|
import androidx.compose.material3.ElevatedCard
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.SecondaryTabRow
|
||||||
import androidx.compose.material3.Tab
|
import androidx.compose.material3.Tab
|
||||||
import androidx.compose.material3.TabRow
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -123,7 +123,7 @@ class SearchLayout @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TabRow(
|
SecondaryTabRow(
|
||||||
selectedTabIndex = if (vm.isNormalMode) 0 else 1,
|
selectedTabIndex = if (vm.isNormalMode) 0 else 1,
|
||||||
divider = {},
|
divider = {},
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -64,10 +64,10 @@ import com.hippo.ehviewer.util.ExceptionUtils
|
|||||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||||
import eu.kanade.tachiyomi.util.lang.withNonCancellableContext
|
import eu.kanade.tachiyomi.util.lang.withNonCancellableContext
|
||||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||||
|
import java.util.Locale
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import okhttp3.Cookie
|
import okhttp3.Cookie
|
||||||
import java.util.Locale
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CookieSignInScene(windowSizeClass: WindowSizeClass) {
|
fun CookieSignInScene(windowSizeClass: WindowSizeClass) {
|
||||||
@@ -101,7 +101,6 @@ fun CookieSignInScene(windowSizeClass: WindowSizeClass) {
|
|||||||
EhCookieStore.addCookie(newCookie(EhCookieStore.KEY_IPB_PASS_HASH, hash, EhUrl.DOMAIN_E))
|
EhCookieStore.addCookie(newCookie(EhCookieStore.KEY_IPB_PASS_HASH, hash, EhUrl.DOMAIN_E))
|
||||||
EhCookieStore.addCookie(newCookie(EhCookieStore.KEY_IPB_PASS_HASH, hash, EhUrl.DOMAIN_EX))
|
EhCookieStore.addCookie(newCookie(EhCookieStore.KEY_IPB_PASS_HASH, hash, EhUrl.DOMAIN_EX))
|
||||||
if (igneous.isNotBlank()) EhCookieStore.addCookie(newCookie(EhCookieStore.KEY_IGNEOUS, igneous, EhUrl.DOMAIN_EX))
|
if (igneous.isNotBlank()) EhCookieStore.addCookie(newCookie(EhCookieStore.KEY_IGNEOUS, igneous, EhUrl.DOMAIN_EX))
|
||||||
EhCookieStore.flush()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun login() {
|
fun login() {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ suspend fun postLogin() = coroutineScope {
|
|||||||
launch {
|
launch {
|
||||||
runCatching {
|
runCatching {
|
||||||
EhEngine.getUConfig(EhUrl.URL_UCONFIG_E)
|
EhEngine.getUConfig(EhUrl.URL_UCONFIG_E)
|
||||||
|
EhCookieStore.flush()
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
it.printStackTrace()
|
it.printStackTrace()
|
||||||
}
|
}
|
||||||
@@ -36,6 +37,7 @@ suspend fun postLogin() = coroutineScope {
|
|||||||
Settings.gallerySite = EhUrl.SITE_EX
|
Settings.gallerySite = EhUrl.SITE_EX
|
||||||
// Explicitly use ex url since https://github.com/Ehviewer-Overhauled/Ehviewer/issues/1239#issuecomment-1632584525
|
// Explicitly use ex url since https://github.com/Ehviewer-Overhauled/Ehviewer/issues/1239#issuecomment-1632584525
|
||||||
EhEngine.getUConfig(EhUrl.URL_UCONFIG_EX)
|
EhEngine.getUConfig(EhUrl.URL_UCONFIG_EX)
|
||||||
|
EhCookieStore.flush()
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
Settings.gallerySite = EhUrl.SITE_E
|
Settings.gallerySite = EhUrl.SITE_E
|
||||||
Settings.needSignIn = false
|
Settings.needSignIn = false
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ abstract class BaseScene : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open val showLeftDrawer = true
|
open val showLeftDrawer = false
|
||||||
|
|
||||||
private fun createDrawerView(savedInstanceState: Bundle?): View? {
|
private fun createDrawerView(savedInstanceState: Bundle?): View? {
|
||||||
drawerView = onCreateDrawerView(layoutInflater)
|
drawerView = onCreateDrawerView(layoutInflater)
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ import com.hippo.ehviewer.databinding.ItemDownloadBinding
|
|||||||
import com.hippo.ehviewer.databinding.ItemDrawerListBinding
|
import com.hippo.ehviewer.databinding.ItemDrawerListBinding
|
||||||
import com.hippo.ehviewer.databinding.SceneDownloadBinding
|
import com.hippo.ehviewer.databinding.SceneDownloadBinding
|
||||||
import com.hippo.ehviewer.download.DownloadManager
|
import com.hippo.ehviewer.download.DownloadManager
|
||||||
|
import com.hippo.ehviewer.download.DownloadManager as downloadManager
|
||||||
import com.hippo.ehviewer.download.DownloadService
|
import com.hippo.ehviewer.download.DownloadService
|
||||||
import com.hippo.ehviewer.download.DownloadService.Companion.clear
|
import com.hippo.ehviewer.download.DownloadService.Companion.clear
|
||||||
import com.hippo.ehviewer.download.downloadDir
|
import com.hippo.ehviewer.download.downloadDir
|
||||||
@@ -115,7 +116,6 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import rikka.core.res.resolveColor
|
import rikka.core.res.resolveColor
|
||||||
import com.hippo.ehviewer.download.DownloadManager as downloadManager
|
|
||||||
|
|
||||||
@SuppressLint("RtlHardcoded")
|
@SuppressLint("RtlHardcoded")
|
||||||
class DownloadsScene :
|
class DownloadsScene :
|
||||||
|
|||||||
@@ -88,17 +88,17 @@ import com.hippo.ehviewer.util.lazyMut
|
|||||||
import com.hippo.ehviewer.util.setValue
|
import com.hippo.ehviewer.util.setValue
|
||||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||||
import eu.kanade.tachiyomi.util.lang.withIOContext
|
import eu.kanade.tachiyomi.util.lang.withIOContext
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import moe.tarsin.coroutines.runSuspendCatching
|
|
||||||
import rikka.core.res.resolveColor
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.time.ZoneId
|
import java.time.ZoneId
|
||||||
import java.time.ZoneOffset
|
import java.time.ZoneOffset
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import moe.tarsin.coroutines.runSuspendCatching
|
||||||
|
import rikka.core.res.resolveColor
|
||||||
|
|
||||||
// Note that we do not really follow mvvm structure, just use it as ... storage
|
// Note that we do not really follow mvvm structure, just use it as ... storage
|
||||||
class VMStorage : ViewModel() {
|
class VMStorage : ViewModel() {
|
||||||
|
|||||||
@@ -74,31 +74,15 @@ class GalleryAdapter(
|
|||||||
layoutManager.setStrategy(STRATEGY_MIN_SIZE)
|
layoutManager.setStrategy(STRATEGY_MIN_SIZE)
|
||||||
if (null != mGirdDecoration) {
|
if (null != mGirdDecoration) {
|
||||||
recyclerView.removeItemDecoration(mGirdDecoration!!)
|
recyclerView.removeItemDecoration(mGirdDecoration!!)
|
||||||
val paddingH = resources.getDimensionPixelOffset(R.dimen.gallery_grid_margin_h)
|
|
||||||
val paddingV = resources.getDimensionPixelOffset(R.dimen.gallery_grid_margin_v)
|
|
||||||
recyclerView.setPadding(
|
|
||||||
recyclerView.paddingLeft - paddingH,
|
|
||||||
recyclerView.paddingTop - paddingV,
|
|
||||||
recyclerView.paddingRight - paddingH,
|
|
||||||
recyclerView.paddingBottom - paddingV,
|
|
||||||
)
|
|
||||||
mGirdDecoration = null
|
mGirdDecoration = null
|
||||||
}
|
}
|
||||||
if (null == mListDecoration) {
|
if (null == mListDecoration) {
|
||||||
val interval = resources.getDimensionPixelOffset(R.dimen.gallery_list_interval)
|
val interval = resources.getDimensionPixelOffset(R.dimen.gallery_list_interval)
|
||||||
val paddingH = resources.getDimensionPixelOffset(R.dimen.gallery_list_margin_h)
|
|
||||||
val paddingV = resources.getDimensionPixelOffset(R.dimen.gallery_list_margin_v)
|
|
||||||
mListDecoration = object : ItemDecoration() {
|
mListDecoration = object : ItemDecoration() {
|
||||||
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
|
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
|
||||||
outRect.set(0, interval / 2, 0, interval / 2)
|
outRect.set(0, interval / 2, 0, interval / 2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
recyclerView.setPadding(
|
|
||||||
recyclerView.paddingLeft + paddingH,
|
|
||||||
recyclerView.paddingTop + paddingV,
|
|
||||||
recyclerView.paddingRight + paddingH,
|
|
||||||
recyclerView.paddingBottom + paddingV,
|
|
||||||
)
|
|
||||||
recyclerView.addItemDecoration(mListDecoration!!)
|
recyclerView.addItemDecoration(mListDecoration!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,27 +93,11 @@ class GalleryAdapter(
|
|||||||
layoutManager.setStrategy(STRATEGY_SUITABLE_SIZE)
|
layoutManager.setStrategy(STRATEGY_SUITABLE_SIZE)
|
||||||
if (null != mListDecoration) {
|
if (null != mListDecoration) {
|
||||||
recyclerView.removeItemDecoration(mListDecoration!!)
|
recyclerView.removeItemDecoration(mListDecoration!!)
|
||||||
val paddingH = resources.getDimensionPixelOffset(R.dimen.gallery_list_margin_h)
|
|
||||||
val paddingV = resources.getDimensionPixelOffset(R.dimen.gallery_list_margin_v)
|
|
||||||
recyclerView.setPadding(
|
|
||||||
recyclerView.paddingLeft - paddingH,
|
|
||||||
recyclerView.paddingTop - paddingV,
|
|
||||||
recyclerView.paddingRight - paddingH,
|
|
||||||
recyclerView.paddingBottom - paddingV,
|
|
||||||
)
|
|
||||||
mListDecoration = null
|
mListDecoration = null
|
||||||
}
|
}
|
||||||
if (null == mGirdDecoration) {
|
if (null == mGirdDecoration) {
|
||||||
val interval = resources.getDimensionPixelOffset(R.dimen.gallery_grid_interval)
|
val interval = resources.getDimensionPixelOffset(R.dimen.gallery_grid_interval)
|
||||||
val paddingH = resources.getDimensionPixelOffset(R.dimen.gallery_grid_margin_h)
|
|
||||||
val paddingV = resources.getDimensionPixelOffset(R.dimen.gallery_grid_margin_v)
|
|
||||||
mGirdDecoration = MarginItemDecoration(interval, 0, 0, 0, 0)
|
mGirdDecoration = MarginItemDecoration(interval, 0, 0, 0, 0)
|
||||||
recyclerView.setPadding(
|
|
||||||
recyclerView.paddingLeft + paddingH,
|
|
||||||
recyclerView.paddingTop + paddingV,
|
|
||||||
recyclerView.paddingRight + paddingH,
|
|
||||||
recyclerView.paddingBottom + paddingV,
|
|
||||||
)
|
|
||||||
recyclerView.addItemDecoration(mGirdDecoration!!)
|
recyclerView.addItemDecoration(mGirdDecoration!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,9 +91,9 @@ import com.hippo.ehviewer.util.getParcelableCompat
|
|||||||
import com.hippo.ehviewer.util.toBBCode
|
import com.hippo.ehviewer.util.toBBCode
|
||||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||||
|
import kotlin.math.hypot
|
||||||
import moe.tarsin.coroutines.runSuspendCatching
|
import moe.tarsin.coroutines.runSuspendCatching
|
||||||
import rikka.core.res.resolveColor
|
import rikka.core.res.resolveColor
|
||||||
import kotlin.math.hypot
|
|
||||||
|
|
||||||
class GalleryCommentsScene : BaseToolbarScene(), View.OnClickListener, OnRefreshListener {
|
class GalleryCommentsScene : BaseToolbarScene(), View.OnClickListener, OnRefreshListener {
|
||||||
private var _binding: SceneGalleryCommentsBinding? = null
|
private var _binding: SceneGalleryCommentsBinding? = null
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ import com.hippo.ehviewer.databinding.DialogArchiveListBinding
|
|||||||
import com.hippo.ehviewer.databinding.DialogRateBinding
|
import com.hippo.ehviewer.databinding.DialogRateBinding
|
||||||
import com.hippo.ehviewer.databinding.DialogTorrentListBinding
|
import com.hippo.ehviewer.databinding.DialogTorrentListBinding
|
||||||
import com.hippo.ehviewer.databinding.ItemGalleryCommentBinding
|
import com.hippo.ehviewer.databinding.ItemGalleryCommentBinding
|
||||||
|
import com.hippo.ehviewer.download.DownloadManager as EhDownloadManager
|
||||||
import com.hippo.ehviewer.ktbuilder.imageRequest
|
import com.hippo.ehviewer.ktbuilder.imageRequest
|
||||||
import com.hippo.ehviewer.spider.SpiderQueen
|
import com.hippo.ehviewer.spider.SpiderQueen
|
||||||
import com.hippo.ehviewer.spider.SpiderQueen.Companion.MODE_READ
|
import com.hippo.ehviewer.spider.SpiderQueen.Companion.MODE_READ
|
||||||
@@ -172,6 +173,7 @@ import com.hippo.ehviewer.util.addTextToClipboard
|
|||||||
import com.hippo.ehviewer.util.getParcelableCompat
|
import com.hippo.ehviewer.util.getParcelableCompat
|
||||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||||
|
import kotlin.math.roundToInt
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
@@ -179,8 +181,6 @@ import kotlinx.coroutines.sync.withLock
|
|||||||
import moe.tarsin.coroutines.runSuspendCatching
|
import moe.tarsin.coroutines.runSuspendCatching
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
import splitties.systemservices.downloadManager
|
import splitties.systemservices.downloadManager
|
||||||
import kotlin.math.roundToInt
|
|
||||||
import com.hippo.ehviewer.download.DownloadManager as EhDownloadManager
|
|
||||||
|
|
||||||
class GalleryDetailScene : BaseScene() {
|
class GalleryDetailScene : BaseScene() {
|
||||||
private var mDownloadState = 0
|
private var mDownloadState = 0
|
||||||
|
|||||||
@@ -108,12 +108,6 @@ import com.hippo.ehviewer.util.setValue
|
|||||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||||
import eu.kanade.tachiyomi.util.lang.withIOContext
|
import eu.kanade.tachiyomi.util.lang.withIOContext
|
||||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.parcelize.Parcelize
|
|
||||||
import moe.tarsin.coroutines.runSuspendCatching
|
|
||||||
import rikka.core.res.resolveColor
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
@@ -121,6 +115,12 @@ import java.time.ZoneId
|
|||||||
import java.time.ZoneOffset
|
import java.time.ZoneOffset
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
import moe.tarsin.coroutines.runSuspendCatching
|
||||||
|
import rikka.core.res.resolveColor
|
||||||
|
|
||||||
class VMStorage1 : ViewModel() {
|
class VMStorage1 : ViewModel() {
|
||||||
var urlBuilder = ListUrlBuilder()
|
var urlBuilder = ListUrlBuilder()
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ import eu.kanade.tachiyomi.util.system.pxToDp
|
|||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
|
||||||
class HistoryScene : BaseScene() {
|
class HistoryScene : BaseScene() {
|
||||||
|
override val showLeftDrawer = true
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?) = ComposeView(inflater.context).apply {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?) = ComposeView(inflater.context).apply {
|
||||||
setMD3Content {
|
setMD3Content {
|
||||||
val dialogState = rememberDialogState()
|
val dialogState = rememberDialogState()
|
||||||
|
|||||||
@@ -34,8 +34,6 @@ import eu.kanade.tachiyomi.util.lang.withUIContext
|
|||||||
import moe.tarsin.coroutines.runSuspendCatching
|
import moe.tarsin.coroutines.runSuspendCatching
|
||||||
|
|
||||||
class ProgressScene : BaseScene() {
|
class ProgressScene : BaseScene() {
|
||||||
override val showLeftDrawer = false
|
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||||
return ComposeView(inflater.context).apply {
|
return ComposeView(inflater.context).apply {
|
||||||
setMD3Content {
|
setMD3Content {
|
||||||
|
|||||||
@@ -75,6 +75,8 @@ abstract class SearchBarScene : BaseScene(), ToolBarScene {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val showLeftDrawer = true
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||||
_binding = SceneSearchbarBinding.inflate(inflater, container, false)
|
_binding = SceneSearchbarBinding.inflate(inflater, container, false)
|
||||||
binding.appbar.statusBarForeground = MaterialShapeDrawable.createWithElevationOverlay(context)
|
binding.appbar.statusBarForeground = MaterialShapeDrawable.createWithElevationOverlay(context)
|
||||||
|
|||||||
@@ -43,10 +43,10 @@ import com.hippo.ehviewer.util.AppConfig
|
|||||||
import com.hippo.ehviewer.util.ExceptionUtils
|
import com.hippo.ehviewer.util.ExceptionUtils
|
||||||
import com.hippo.ehviewer.util.installPackage
|
import com.hippo.ehviewer.util.installPackage
|
||||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||||
|
import java.io.File
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import moe.tarsin.coroutines.runSuspendCatching
|
import moe.tarsin.coroutines.runSuspendCatching
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
private const val REPO_URL = "https://github.com/${BuildConfig.REPO_NAME}"
|
private const val REPO_URL = "https://github.com/${BuildConfig.REPO_NAME}"
|
||||||
private const val RELEASE_URL = "$REPO_URL/releases"
|
private const val RELEASE_URL = "$REPO_URL/releases"
|
||||||
|
|||||||
@@ -38,13 +38,13 @@ import com.hippo.ehviewer.ui.LocalNavController
|
|||||||
import com.hippo.ehviewer.ui.tools.observed
|
import com.hippo.ehviewer.ui.tools.observed
|
||||||
import com.hippo.ehviewer.util.AppConfig
|
import com.hippo.ehviewer.util.AppConfig
|
||||||
import com.hippo.ehviewer.util.ReadableTime
|
import com.hippo.ehviewer.util.ReadableTime
|
||||||
|
import java.io.File
|
||||||
|
import java.util.zip.ZipEntry
|
||||||
|
import java.util.zip.ZipOutputStream
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import moe.tarsin.coroutines.runSuspendCatching
|
import moe.tarsin.coroutines.runSuspendCatching
|
||||||
import java.io.File
|
|
||||||
import java.util.zip.ZipEntry
|
|
||||||
import java.util.zip.ZipOutputStream
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AdvancedScreen() {
|
fun AdvancedScreen() {
|
||||||
|
|||||||
@@ -50,13 +50,13 @@ import com.jamal.composeprefs3.ui.prefs.SliderPref
|
|||||||
import com.jamal.composeprefs3.ui.prefs.SpannedTextPref
|
import com.jamal.composeprefs3.ui.prefs.SpannedTextPref
|
||||||
import com.jamal.composeprefs3.ui.prefs.SwitchPref
|
import com.jamal.composeprefs3.ui.prefs.SwitchPref
|
||||||
import com.jamal.composeprefs3.ui.prefs.TextPref
|
import com.jamal.composeprefs3.ui.prefs.TextPref
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
import kotlin.coroutines.suspendCoroutine
|
import kotlin.coroutines.suspendCoroutine
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
import kotlin.reflect.KMutableProperty0
|
import kotlin.reflect.KMutableProperty0
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
object PreferenceTokens {
|
object PreferenceTokens {
|
||||||
val PreferenceHeaderHeight = 56.dp
|
val PreferenceHeaderHeight = 56.dp
|
||||||
|
|||||||
@@ -47,9 +47,9 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import com.jamal.composeprefs3.ui.ifNotNullThen
|
import com.jamal.composeprefs3.ui.ifNotNullThen
|
||||||
|
import kotlin.coroutines.resume
|
||||||
import kotlinx.coroutines.CancellableContinuation
|
import kotlinx.coroutines.CancellableContinuation
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
import kotlin.coroutines.resume
|
|
||||||
|
|
||||||
interface DialogScope<R> {
|
interface DialogScope<R> {
|
||||||
var expectedValue: R
|
var expectedValue: R
|
||||||
|
|||||||
@@ -46,14 +46,14 @@ import androidx.compose.ui.util.fastFirstOrNull
|
|||||||
import androidx.compose.ui.util.fastForEach
|
import androidx.compose.ui.util.fastForEach
|
||||||
import androidx.compose.ui.util.fastMaxBy
|
import androidx.compose.ui.util.fastMaxBy
|
||||||
import com.hippo.ehviewer.ui.tools.Scroller.STICKY_HEADER_KEY_PREFIX
|
import com.hippo.ehviewer.ui.tools.Scroller.STICKY_HEADER_KEY_PREFIX
|
||||||
import kotlinx.coroutines.channels.BufferOverflow
|
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
|
||||||
import kotlinx.coroutines.flow.sample
|
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
import kotlinx.coroutines.channels.BufferOverflow
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
import kotlinx.coroutines.flow.sample
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws vertical fast scroller to a lazy list
|
* Draws vertical fast scroller to a lazy list
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ import com.hippo.ehviewer.client.execute
|
|||||||
import com.hippo.ehviewer.client.executeAndParseAs
|
import com.hippo.ehviewer.client.executeAndParseAs
|
||||||
import eu.kanade.tachiyomi.util.system.logcat
|
import eu.kanade.tachiyomi.util.system.logcat
|
||||||
import io.ktor.util.encodeBase64
|
import io.ktor.util.encodeBase64
|
||||||
|
import java.io.File
|
||||||
|
import java.time.Instant
|
||||||
|
import java.time.temporal.ChronoUnit
|
||||||
|
import java.util.zip.ZipInputStream
|
||||||
import moe.tarsin.coroutines.runSuspendCatching
|
import moe.tarsin.coroutines.runSuspendCatching
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okio.sink
|
import okio.sink
|
||||||
@@ -14,10 +18,6 @@ import tachiyomi.data.release.GithubArtifacts
|
|||||||
import tachiyomi.data.release.GithubCommitComparison
|
import tachiyomi.data.release.GithubCommitComparison
|
||||||
import tachiyomi.data.release.GithubRelease
|
import tachiyomi.data.release.GithubRelease
|
||||||
import tachiyomi.data.release.GithubWorkflowRuns
|
import tachiyomi.data.release.GithubWorkflowRuns
|
||||||
import java.io.File
|
|
||||||
import java.time.Instant
|
|
||||||
import java.time.temporal.ChronoUnit
|
|
||||||
import java.util.zip.ZipInputStream
|
|
||||||
|
|
||||||
private const val API_URL = "https://api.github.com/repos/${BuildConfig.REPO_NAME}"
|
private const val API_URL = "https://api.github.com/repos/${BuildConfig.REPO_NAME}"
|
||||||
private const val LATEST_RELEASE_URL = "$API_URL/releases/latest"
|
private const val LATEST_RELEASE_URL = "$API_URL/releases/latest"
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ package com.hippo.ehviewer.util
|
|||||||
|
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import com.hippo.ehviewer.client.exception.ParseException
|
import com.hippo.ehviewer.client.exception.ParseException
|
||||||
import splitties.init.appCtx
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import splitties.init.appCtx
|
||||||
|
|
||||||
fun File.ensureDirectory() = if (exists()) isDirectory else mkdirs()
|
fun File.ensureDirectory() = if (exists()) isDirectory else mkdirs()
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ fun Spanned.toBBCode(): String {
|
|||||||
}
|
}
|
||||||
if (next < text.length) processOneSpanTransition(next)
|
if (next < text.length) processOneSpanTransition(next)
|
||||||
}
|
}
|
||||||
return StringBuilder().apply {
|
return buildString {
|
||||||
processOneSpanTransition()
|
processOneSpanTransition()
|
||||||
}.toString()
|
}
|
||||||
}
|
}
|
||||||
@@ -16,8 +16,6 @@
|
|||||||
package com.hippo.ehviewer.util
|
package com.hippo.ehviewer.util
|
||||||
|
|
||||||
import com.hippo.ehviewer.R
|
import com.hippo.ehviewer.R
|
||||||
import com.hippo.ehviewer.client.exception.EhException
|
|
||||||
import splitties.init.appCtx
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.net.MalformedURLException
|
import java.net.MalformedURLException
|
||||||
import java.net.ProtocolException
|
import java.net.ProtocolException
|
||||||
@@ -25,6 +23,7 @@ import java.net.SocketException
|
|||||||
import java.net.SocketTimeoutException
|
import java.net.SocketTimeoutException
|
||||||
import java.net.UnknownHostException
|
import java.net.UnknownHostException
|
||||||
import javax.net.ssl.SSLException
|
import javax.net.ssl.SSLException
|
||||||
|
import splitties.init.appCtx
|
||||||
|
|
||||||
object ExceptionUtils {
|
object ExceptionUtils {
|
||||||
fun getReadableString(e: Throwable): String {
|
fun getReadableString(e: Throwable): String {
|
||||||
@@ -43,12 +42,12 @@ object ExceptionUtils {
|
|||||||
is SocketTimeoutException -> appCtx.getString(R.string.error_timeout)
|
is SocketTimeoutException -> appCtx.getString(R.string.error_timeout)
|
||||||
is UnknownHostException -> appCtx.getString(R.string.error_unknown_host)
|
is UnknownHostException -> appCtx.getString(R.string.error_unknown_host)
|
||||||
is StatusCodeException -> {
|
is StatusCodeException -> {
|
||||||
val sb = StringBuilder()
|
buildString {
|
||||||
sb.append(appCtx.getString(R.string.error_bad_status_code, e.responseCode))
|
append(appCtx.getString(R.string.error_bad_status_code, e.responseCode))
|
||||||
if (e.isIdentifiedResponseCode) {
|
if (e.isIdentifiedResponseCode) {
|
||||||
sb.append(", ").append(e.message)
|
append(", ").append(e.message)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sb.toString()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
is ProtocolException -> {
|
is ProtocolException -> {
|
||||||
@@ -60,8 +59,7 @@ object ExceptionUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
is SocketException, is SSLException -> appCtx.getString(R.string.error_socket)
|
is SocketException, is SSLException -> appCtx.getString(R.string.error_socket)
|
||||||
is EhException -> e.message!!
|
else -> e.message ?: appCtx.getString(R.string.error_unknown)
|
||||||
else -> appCtx.getString(R.string.error_unknown)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package com.hippo.ehviewer.util
|
package com.hippo.ehviewer.util
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
import okhttp3.ResponseBody
|
import okhttp3.ResponseBody
|
||||||
import okio.buffer
|
import okio.buffer
|
||||||
import okio.sink
|
import okio.sink
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
fun ResponseBody.copyToFile(file: File) {
|
fun ResponseBody.copyToFile(file: File) {
|
||||||
file.outputStream().use { os ->
|
file.outputStream().use { os ->
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ import androidx.lifecycle.LifecycleEventObserver
|
|||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import arrow.atomic.AtomicInt
|
import arrow.atomic.AtomicInt
|
||||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||||
import rikka.core.util.ContextUtils.requireActivity
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
import kotlin.coroutines.suspendCoroutine
|
import kotlin.coroutines.suspendCoroutine
|
||||||
|
import rikka.core.util.ContextUtils.requireActivity
|
||||||
|
|
||||||
// Fuck off the silly Android launcher and callback :)
|
// Fuck off the silly Android launcher and callback :)
|
||||||
|
|
||||||
|
|||||||
@@ -16,11 +16,11 @@
|
|||||||
package com.hippo.ehviewer.util
|
package com.hippo.ehviewer.util
|
||||||
|
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import java.io.BufferedReader
|
import java.io.BufferedReader
|
||||||
import java.io.FileReader
|
import java.io.FileReader
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
object OSUtils {
|
object OSUtils {
|
||||||
private const val PROCFS_MEMFILE = "/proc/meminfo"
|
private const val PROCFS_MEMFILE = "/proc/meminfo"
|
||||||
|
|||||||
@@ -101,19 +101,19 @@ object ReadableTime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getShortTimeInterval(time: Long): String {
|
fun getShortTimeInterval(time: Long): String {
|
||||||
val sb = StringBuilder()
|
|
||||||
val resources = sResources
|
val resources = sResources
|
||||||
for (i in 0 until SIZE) {
|
return buildString {
|
||||||
val multiple = MULTIPLES[i]
|
for (i in 0 until SIZE) {
|
||||||
val quotient = time / multiple
|
val multiple = MULTIPLES[i]
|
||||||
if (time > multiple * 1.5 || i == SIZE - 1) {
|
val quotient = time / multiple
|
||||||
sb.append(quotient)
|
if (time > multiple * 1.5 || i == SIZE - 1) {
|
||||||
.append(" ")
|
append(quotient)
|
||||||
.append(resources!!.getQuantityString(UNITS[i], quotient.toInt()))
|
.append(" ")
|
||||||
break
|
.append(resources!!.getQuantityString(UNITS[i], quotient.toInt()))
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sb.toString()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getFilenamableTime(time: Long): String {
|
fun getFilenamableTime(time: Long): String {
|
||||||
|
|||||||
+2
-2
@@ -8,12 +8,12 @@ import android.webkit.WebViewClient
|
|||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import com.hippo.ehviewer.client.EhCookieStore
|
import com.hippo.ehviewer.client.EhCookieStore
|
||||||
import com.hippo.ehviewer.client.exception.CloudflareBypassException
|
import com.hippo.ehviewer.client.exception.CloudflareBypassException
|
||||||
|
import java.io.IOException
|
||||||
|
import java.util.concurrent.CountDownLatch
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
import okhttp3.Interceptor
|
import okhttp3.Interceptor
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import java.io.IOException
|
|
||||||
import java.util.concurrent.CountDownLatch
|
|
||||||
|
|
||||||
class CloudflareInterceptor(context: Context) : WebViewInterceptor(context) {
|
class CloudflareInterceptor(context: Context) : WebViewInterceptor(context) {
|
||||||
private val executor = ContextCompat.getMainExecutor(context)
|
private val executor = ContextCompat.getMainExecutor(context)
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ import android.content.Context
|
|||||||
import android.webkit.WebSettings
|
import android.webkit.WebSettings
|
||||||
import android.webkit.WebView
|
import android.webkit.WebView
|
||||||
import com.hippo.ehviewer.util.setDefaultSettings
|
import com.hippo.ehviewer.util.setDefaultSettings
|
||||||
|
import java.util.Locale
|
||||||
|
import java.util.concurrent.CountDownLatch
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
import okhttp3.Headers
|
import okhttp3.Headers
|
||||||
import okhttp3.Interceptor
|
import okhttp3.Interceptor
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import java.util.Locale
|
|
||||||
import java.util.concurrent.CountDownLatch
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
|
|
||||||
abstract class WebViewInterceptor(private val context: Context) : Interceptor {
|
abstract class WebViewInterceptor(private val context: Context) : Interceptor {
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -98,6 +98,12 @@ import eu.kanade.tachiyomi.util.view.copy
|
|||||||
import eu.kanade.tachiyomi.util.view.popupMenu
|
import eu.kanade.tachiyomi.util.view.popupMenu
|
||||||
import eu.kanade.tachiyomi.util.view.setTooltip
|
import eu.kanade.tachiyomi.util.view.setTooltip
|
||||||
import eu.kanade.tachiyomi.widget.listener.SimpleAnimationListener
|
import eu.kanade.tachiyomi.widget.listener.SimpleAnimationListener
|
||||||
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
|
import kotlin.coroutines.Continuation
|
||||||
|
import kotlin.coroutines.resume
|
||||||
|
import kotlin.math.abs
|
||||||
import kotlinx.coroutines.FlowPreview
|
import kotlinx.coroutines.FlowPreview
|
||||||
import kotlinx.coroutines.currentCoroutineContext
|
import kotlinx.coroutines.currentCoroutineContext
|
||||||
import kotlinx.coroutines.ensureActive
|
import kotlinx.coroutines.ensureActive
|
||||||
@@ -109,12 +115,6 @@ import kotlinx.coroutines.flow.onEach
|
|||||||
import kotlinx.coroutines.flow.sample
|
import kotlinx.coroutines.flow.sample
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
import splitties.systemservices.clipboardManager
|
import splitties.systemservices.clipboardManager
|
||||||
import java.io.File
|
|
||||||
import java.io.IOException
|
|
||||||
import java.util.concurrent.atomic.AtomicReference
|
|
||||||
import kotlin.coroutines.Continuation
|
|
||||||
import kotlin.coroutines.resume
|
|
||||||
import kotlin.math.abs
|
|
||||||
|
|
||||||
class ReaderActivity : EhActivity() {
|
class ReaderActivity : EhActivity() {
|
||||||
lateinit var binding: ReaderActivityBinding
|
lateinit var binding: ReaderActivityBinding
|
||||||
|
|||||||
+1
-1
@@ -46,7 +46,7 @@ fun CombinedCircularProgressIndicator(progress: Float) {
|
|||||||
label = "rotation",
|
label = "rotation",
|
||||||
)
|
)
|
||||||
CircularProgressIndicator(
|
CircularProgressIndicator(
|
||||||
progress = animatedProgress,
|
progress = { animatedProgress },
|
||||||
modifier = Modifier.rotate(rotation),
|
modifier = Modifier.rotate(rotation),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -27,6 +27,7 @@ class WebtoonLayoutManager(activity: ReaderActivity) : LinearLayoutManager(activ
|
|||||||
/**
|
/**
|
||||||
* Returns the custom extra layout space.
|
* Returns the custom extra layout space.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated("Deprecated in Java")
|
||||||
override fun getExtraLayoutSpace(state: RecyclerView.State): Int {
|
override fun getExtraLayoutSpace(state: RecyclerView.State): Int {
|
||||||
return extraLayoutSpace
|
return extraLayoutSpace
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,10 +17,10 @@ import androidx.core.graphics.blue
|
|||||||
import androidx.core.graphics.green
|
import androidx.core.graphics.green
|
||||||
import androidx.core.graphics.red
|
import androidx.core.graphics.red
|
||||||
import com.hippo.ehviewer.R
|
import com.hippo.ehviewer.R
|
||||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
|
||||||
import splitties.systemservices.windowManager
|
|
||||||
import kotlin.math.roundToInt
|
|
||||||
import com.hippo.ehviewer.Settings as AppSettings
|
import com.hippo.ehviewer.Settings as AppSettings
|
||||||
|
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
import splitties.systemservices.windowManager
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the color for the given attribute.
|
* Returns the color for the given attribute.
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package moe.tarsin.coroutines
|
package moe.tarsin.coroutines
|
||||||
|
|
||||||
import arrow.core.memoize
|
import arrow.core.memoize
|
||||||
import kotlinx.coroutines.sync.Mutex
|
|
||||||
import kotlin.contracts.InvocationKind
|
import kotlin.contracts.InvocationKind
|
||||||
import kotlin.contracts.contract
|
import kotlin.contracts.contract
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
|
||||||
class NamedMutex<T> {
|
class NamedMutex<T> {
|
||||||
val innerMutexGetter = { _: T -> Mutex() }.memoize()
|
val innerMutexGetter = { _: T -> Mutex() }.memoize()
|
||||||
|
|||||||
@@ -50,7 +50,6 @@
|
|||||||
<com.hippo.ehviewer.ui.legacy.FabLayout
|
<com.hippo.ehviewer.ui.legacy.FabLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:elevation="6dp"
|
|
||||||
android:paddingEnd="@dimen/corner_fab_margin"
|
android:paddingEnd="@dimen/corner_fab_margin"
|
||||||
android:paddingBottom="@dimen/corner_fab_margin"
|
android:paddingBottom="@dimen/corner_fab_margin"
|
||||||
app:fitsSystemWindowsInsets="top|bottom">
|
app:fitsSystemWindowsInsets="top|bottom">
|
||||||
|
|||||||
@@ -52,7 +52,10 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:clipChildren="false"
|
android:clipChildren="false"
|
||||||
android:clipToPadding="false" />
|
android:clipToPadding="false"
|
||||||
|
android:paddingHorizontal="@dimen/gallery_list_margin_h"
|
||||||
|
android:paddingVertical="@dimen/gallery_list_margin_v"
|
||||||
|
app:fitsSystemWindowsInsets="bottom" />
|
||||||
|
|
||||||
</androidx.swiperefreshlayout.widget.ThemedSwipeRefreshLayout>
|
</androidx.swiperefreshlayout.widget.ThemedSwipeRefreshLayout>
|
||||||
|
|
||||||
@@ -63,8 +66,8 @@
|
|||||||
android:layout_gravity="end"
|
android:layout_gravity="end"
|
||||||
android:layout_marginTop="@dimen/gallery_padding_top_search_bar"
|
android:layout_marginTop="@dimen/gallery_padding_top_search_bar"
|
||||||
android:paddingStart="20dp"
|
android:paddingStart="20dp"
|
||||||
android:paddingTop="8dp"
|
|
||||||
android:paddingEnd="4dp"
|
android:paddingEnd="4dp"
|
||||||
android:paddingBottom="8dp" />
|
android:paddingVertical="8dp"
|
||||||
|
app:fitsSystemWindowsInsets="bottom" />
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
@@ -329,8 +329,8 @@
|
|||||||
<string name="settings_eh_thumb_resolution">缩略图分辨率</string>
|
<string name="settings_eh_thumb_resolution">缩略图分辨率</string>
|
||||||
<string name="settings_eh_thumb_resolution_summary">%s,指定分辨率可能使获取缩略图失败</string>
|
<string name="settings_eh_thumb_resolution_summary">%s,指定分辨率可能使获取缩略图失败</string>
|
||||||
<string name="settings_eh_thumb_resolution_auto">自动</string>
|
<string name="settings_eh_thumb_resolution_auto">自动</string>
|
||||||
<string name="settings_eh_force_eh_thumb">强制使用 e-hentai 缩略图服务器</string>
|
<string name="settings_eh_force_eh_thumb">强制使用 e-hentai 缩略图/API服务器</string>
|
||||||
<string name="settings_eh_force_eh_thumb_summary">若 exhentai 缩略图加载失败可尝试此项</string>
|
<string name="settings_eh_force_eh_thumb_summary">若 exhentai 加载失败可尝试此项</string>
|
||||||
<string name="settings_eh_show_jpn_title">显示日文标题</string>
|
<string name="settings_eh_show_jpn_title">显示日文标题</string>
|
||||||
<string name="settings_eh_show_jpn_title_summary">需同时在 EHentai 网站设置中启用 Japanese Title</string>
|
<string name="settings_eh_show_jpn_title_summary">需同时在 EHentai 网站设置中启用 Japanese Title</string>
|
||||||
<string name="settings_eh_show_gallery_pages">显示画廊页数</string>
|
<string name="settings_eh_show_gallery_pages">显示画廊页数</string>
|
||||||
|
|||||||
@@ -371,8 +371,8 @@
|
|||||||
<string name="settings_eh_thumb_resolution">Thumb resolution</string>
|
<string name="settings_eh_thumb_resolution">Thumb resolution</string>
|
||||||
<string name="settings_eh_thumb_resolution_summary">Currently %s. Certain resolutions might cause getting thumb failed</string>
|
<string name="settings_eh_thumb_resolution_summary">Currently %s. Certain resolutions might cause getting thumb failed</string>
|
||||||
<string name="settings_eh_thumb_resolution_auto">Auto</string>
|
<string name="settings_eh_thumb_resolution_auto">Auto</string>
|
||||||
<string name="settings_eh_force_eh_thumb">Force e-hentai thumb server</string>
|
<string name="settings_eh_force_eh_thumb">Force e-hentai thumb/API server</string>
|
||||||
<string name="settings_eh_force_eh_thumb_summary">Try this if you have trouble loading thumbs from exhentai</string>
|
<string name="settings_eh_force_eh_thumb_summary">Try this if you have trouble loading from exhentai</string>
|
||||||
<string name="settings_eh_show_jpn_title">Show Japanese title</string>
|
<string name="settings_eh_show_jpn_title">Show Japanese title</string>
|
||||||
<string name="settings_eh_show_jpn_title_summary">Require enabling Japanese Title in Settings on EHentai website</string>
|
<string name="settings_eh_show_jpn_title_summary">Require enabling Japanese Title in Settings on EHentai website</string>
|
||||||
<string name="settings_eh_show_gallery_pages">Show gallery pages</string>
|
<string name="settings_eh_show_gallery_pages">Show gallery pages</string>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ jnix-macros = "0.4"
|
|||||||
catch_panic = { path = "catch_panic" }
|
catch_panic = { path = "catch_panic" }
|
||||||
catch_panic_macros = { path = "catch_panic_macros" }
|
catch_panic_macros = { path = "catch_panic_macros" }
|
||||||
jni_fn = "0.1.2"
|
jni_fn = "0.1.2"
|
||||||
quick-xml = { default-features = false, version = "0.30" }
|
quick-xml = { default-features = false, version = "0.31" }
|
||||||
tl = "0.7"
|
tl = "0.7"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
android_logger = { default-features = false, version = "0.13" }
|
android_logger = { default-features = false, version = "0.13" }
|
||||||
|
|||||||
@@ -8,29 +8,22 @@ import android.net.http.UrlRequest
|
|||||||
import android.net.http.UrlResponseInfo
|
import android.net.http.UrlResponseInfo
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.annotation.RequiresExtension
|
import androidx.annotation.RequiresExtension
|
||||||
import com.hippo.ehviewer.EhApplication
|
|
||||||
import com.hippo.ehviewer.client.CHROME_ACCEPT
|
import com.hippo.ehviewer.client.CHROME_ACCEPT
|
||||||
import com.hippo.ehviewer.client.CHROME_ACCEPT_LANGUAGE
|
import com.hippo.ehviewer.client.CHROME_ACCEPT_LANGUAGE
|
||||||
import com.hippo.ehviewer.client.CHROME_USER_AGENT
|
import com.hippo.ehviewer.client.CHROME_USER_AGENT
|
||||||
import com.hippo.ehviewer.client.EhCookieStore
|
import com.hippo.ehviewer.client.EhCookieStore
|
||||||
import eu.kanade.tachiyomi.util.system.logcat
|
import java.nio.ByteBuffer
|
||||||
import io.ktor.utils.io.pool.DirectByteBufferPool
|
import kotlin.contracts.InvocationKind
|
||||||
|
import kotlin.contracts.contract
|
||||||
|
import kotlin.coroutines.Continuation
|
||||||
|
import kotlin.coroutines.resume
|
||||||
|
import kotlin.coroutines.resumeWithException
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
import okio.Path.Companion.toOkioPath
|
import okio.Path.Companion.toOkioPath
|
||||||
import splitties.init.appCtx
|
import splitties.init.appCtx
|
||||||
import java.nio.ByteBuffer
|
|
||||||
import java.nio.channels.FileChannel
|
|
||||||
import kotlin.contracts.InvocationKind
|
|
||||||
import kotlin.contracts.contract
|
|
||||||
import kotlin.coroutines.Continuation
|
|
||||||
import kotlin.coroutines.resume
|
|
||||||
import kotlin.coroutines.resumeWithException
|
|
||||||
|
|
||||||
private const val TAG = "CronetRequest"
|
|
||||||
val pool = DirectByteBufferPool(32)
|
|
||||||
|
|
||||||
val cronetHttpClient: HttpEngine = HttpEngine.Builder(appCtx).apply {
|
val cronetHttpClient: HttpEngine = HttpEngine.Builder(appCtx).apply {
|
||||||
setEnableBrotli(true)
|
setEnableBrotli(true)
|
||||||
@@ -40,8 +33,6 @@ val cronetHttpClient: HttpEngine = HttpEngine.Builder(appCtx).apply {
|
|||||||
setUserAgent(CHROME_USER_AGENT)
|
setUserAgent(CHROME_USER_AGENT)
|
||||||
}.build()
|
}.build()
|
||||||
|
|
||||||
val cronetHttpClientExecutor = EhApplication.baseOkHttpClient.dispatcher.executorService
|
|
||||||
|
|
||||||
class CronetRequest {
|
class CronetRequest {
|
||||||
lateinit var consumer: (ByteBuffer) -> Unit
|
lateinit var consumer: (ByteBuffer) -> Unit
|
||||||
lateinit var onResponse: CronetRequest.(UrlResponseInfo) -> Unit
|
lateinit var onResponse: CronetRequest.(UrlResponseInfo) -> Unit
|
||||||
@@ -50,7 +41,6 @@ class CronetRequest {
|
|||||||
lateinit var readerCont: Continuation<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) {
|
override fun onRedirectReceived(req: UrlRequest, info: UrlResponseInfo, url: String) {
|
||||||
logcat(tag = TAG) { "Redirected to $url" }
|
|
||||||
req.followRedirect()
|
req.followRedirect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,29 +76,6 @@ inline fun cronetRequest(url: String, referer: String? = null, conf: UrlRequest.
|
|||||||
}.apply(conf).build()
|
}.apply(conf).build()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend inline fun CronetRequest.awaitBodyFully(crossinline callback: (ByteBuffer) -> Unit) {
|
|
||||||
val buffer = pool.borrow()
|
|
||||||
return try {
|
|
||||||
suspendCancellableCoroutine { cont ->
|
|
||||||
consumer = {
|
|
||||||
callback(it)
|
|
||||||
buffer.clear()
|
|
||||||
request.read(buffer)
|
|
||||||
}
|
|
||||||
onError = { readerCont.resumeWithException(it) }
|
|
||||||
readerCont = cont
|
|
||||||
request.read(buffer)
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
pool.recycle(buffer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend inline fun CronetRequest.copyToChannel(chan: FileChannel, crossinline listener: ((Int) -> Unit) = {}) = awaitBodyFully {
|
|
||||||
val bytes = chan.write(it)
|
|
||||||
listener(bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend inline fun <R> CronetRequest.execute(crossinline callback: suspend CronetRequest.(UrlResponseInfo) -> R): R {
|
suspend inline fun <R> CronetRequest.execute(crossinline callback: suspend CronetRequest.(UrlResponseInfo) -> R): R {
|
||||||
contract {
|
contract {
|
||||||
callsInPlace(callback, InvocationKind.EXACTLY_ONCE)
|
callsInPlace(callback, InvocationKind.EXACTLY_ONCE)
|
||||||
|
|||||||
@@ -9,3 +9,13 @@ plugins {
|
|||||||
alias(libs.plugins.rustAndroidPlugin) apply false
|
alias(libs.plugins.rustAndroidPlugin) apply false
|
||||||
alias(libs.plugins.composeCompilerReportGenerator) apply false
|
alias(libs.plugins.composeCompilerReportGenerator) apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.register("Delete", Delete::class) {
|
||||||
|
delete(rootProject.layout.buildDirectory)
|
||||||
|
}
|
||||||
|
|
||||||
|
buildscript {
|
||||||
|
dependencies {
|
||||||
|
classpath(libs.r8)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,5 +21,6 @@ org.gradle.caching=true
|
|||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
android.enableAppCompileTimeRClass=true
|
android.enableAppCompileTimeRClass=true
|
||||||
android.experimental.enableNewResourceShrinker.preciseShrinking=true
|
android.experimental.enableNewResourceShrinker.preciseShrinking=true
|
||||||
|
android.experimental.lint.version=8.3.0-alpha09
|
||||||
android.lint.useK2Uast=true
|
android.lint.useK2Uast=true
|
||||||
kotlin.experimental.tryK2=true
|
kotlin.experimental.tryK2=true
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
[versions]
|
[versions]
|
||||||
aboutlibraries = "10.9.1"
|
aboutlibraries = "10.9.1"
|
||||||
agp = "8.3.0-alpha09"
|
agp = "8.1.2"
|
||||||
|
android-tools-r8 = "8.3.12-dev"
|
||||||
androidx-activity = "1.8.0"
|
androidx-activity = "1.8.0"
|
||||||
androidx-appcompat = "1.6.1"
|
androidx-appcompat = "1.6.1"
|
||||||
androidx-biometric = "1.2.0-alpha05"
|
androidx-biometric = "1.2.0-alpha05"
|
||||||
@@ -121,6 +122,8 @@ okio-jvm = { module = "com.squareup.okio:okio-jvm", version.ref = "okio" }
|
|||||||
|
|
||||||
photoview = "com.github.chrisbanes:PhotoView:2.3.0"
|
photoview = "com.github.chrisbanes:PhotoView:2.3.0"
|
||||||
|
|
||||||
|
r8 = { module = "com.android.tools:r8", version.ref = "android-tools-r8" }
|
||||||
|
|
||||||
rikkax-core = "dev.rikka.rikkax.core:core-ktx:1.4.1"
|
rikkax-core = "dev.rikka.rikkax.core:core-ktx:1.4.1"
|
||||||
rikkax-insets = "dev.rikka.rikkax.insets:insets:1.3.0"
|
rikkax-insets = "dev.rikka.rikkax.insets:insets:1.3.0"
|
||||||
rikkax-layoutinflater = "dev.rikka.rikkax.layoutinflater:layoutinflater:1.3.0"
|
rikkax-layoutinflater = "dev.rikka.rikkax.layoutinflater:layoutinflater:1.3.0"
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ pluginManagement {
|
|||||||
gradlePluginPortal()
|
gradlePluginPortal()
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
maven("https://storage.googleapis.com/r8-releases/raw")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user