mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-30 22:07:57 +01:00 
			
		
		
		
	chore: merge upstream.
This commit is contained in:
		| @@ -39,7 +39,9 @@ class NetworkHelper( | ||||
|             builder.addNetworkInterceptor(httpLoggingInterceptor) | ||||
|         } | ||||
|  | ||||
|         builder.addInterceptor(CloudflareInterceptor(context, cookieJar, ::defaultUserAgentProvider)) | ||||
|         builder.addInterceptor( | ||||
|             CloudflareInterceptor(context, cookieJar, ::defaultUserAgentProvider), | ||||
|         ) | ||||
|  | ||||
|         when (preferences.dohProvider().get()) { | ||||
|             PREF_DOH_CLOUDFLARE -> builder.dohCloudflare() | ||||
|   | ||||
| @@ -17,6 +17,9 @@ class NetworkPreferences( | ||||
|     } | ||||
|  | ||||
|     fun defaultUserAgent(): Preference<String> { | ||||
|         return preferenceStore.getString("default_user_agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/114.0") | ||||
|         return preferenceStore.getString( | ||||
|             "default_user_agent", | ||||
|             "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/114.0", | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -58,6 +58,15 @@ fun Call.asObservable(): Observable<Response> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| fun Call.asObservableSuccess(): Observable<Response> { | ||||
|     return asObservable().doOnNext { response -> | ||||
|         if (!response.isSuccessful) { | ||||
|             response.close() | ||||
|             throw HttpException(response.code) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| // Based on https://github.com/gildor/kotlin-coroutines-okhttp | ||||
| @OptIn(ExperimentalCoroutinesApi::class) | ||||
| private suspend fun Call.await(callStack: Array<StackTraceElement>): Response { | ||||
| @@ -95,6 +104,9 @@ suspend fun Call.await(): Response { | ||||
|     return await(callStack) | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @since extensions-lib 1.5 | ||||
|  */ | ||||
| suspend fun Call.awaitSuccess(): Response { | ||||
|     val callStack = Exception().stackTrace.run { copyOfRange(1, size) } | ||||
|     val response = await(callStack) | ||||
| @@ -105,15 +117,6 @@ suspend fun Call.awaitSuccess(): Response { | ||||
|     return response | ||||
| } | ||||
|  | ||||
| fun Call.asObservableSuccess(): Observable<Response> { | ||||
|     return asObservable().doOnNext { response -> | ||||
|         if (!response.isSuccessful) { | ||||
|             response.close() | ||||
|             throw HttpException(response.code) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| fun OkHttpClient.newCachelessCallWithProgress(request: Request, listener: ProgressListener): Call { | ||||
|     val progressClient = newBuilder() | ||||
|         .cache(null) | ||||
|   | ||||
| @@ -9,7 +9,10 @@ import okio.Source | ||||
| import okio.buffer | ||||
| import java.io.IOException | ||||
|  | ||||
| class ProgressResponseBody(private val responseBody: ResponseBody, private val progressListener: ProgressListener) : ResponseBody() { | ||||
| class ProgressResponseBody( | ||||
|     private val responseBody: ResponseBody, | ||||
|     private val progressListener: ProgressListener, | ||||
| ) : ResponseBody() { | ||||
|  | ||||
|     private val bufferedSource: BufferedSource by lazy { | ||||
|         source(responseBody.source()).buffer() | ||||
| @@ -36,7 +39,11 @@ class ProgressResponseBody(private val responseBody: ResponseBody, private val p | ||||
|                 val bytesRead = super.read(sink, byteCount) | ||||
|                 // read() returns the number of bytes read, or -1 if this source is exhausted. | ||||
|                 totalBytesRead += if (bytesRead != -1L) bytesRead else 0 | ||||
|                 progressListener.update(totalBytesRead, responseBody.contentLength(), bytesRead == -1L) | ||||
|                 progressListener.update( | ||||
|                     totalBytesRead, | ||||
|                     responseBody.contentLength(), | ||||
|                     bytesRead == -1L, | ||||
|                 ) | ||||
|                 return bytesRead | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -31,7 +31,11 @@ class CloudflareInterceptor( | ||||
|         return response.code in ERROR_CODES && response.header("Server") in SERVER_CHECK | ||||
|     } | ||||
|  | ||||
|     override fun intercept(chain: Interceptor.Chain, request: Request, response: Response): Response { | ||||
|     override fun intercept( | ||||
|         chain: Interceptor.Chain, | ||||
|         request: Request, | ||||
|         response: Response, | ||||
|     ): Response { | ||||
|         try { | ||||
|             response.close() | ||||
|             cookieManager.remove(request.url, COOKIE_NAMES, 0) | ||||
|   | ||||
| @@ -33,7 +33,9 @@ fun OkHttpClient.Builder.rateLimitHost( | ||||
|     permits: Int, | ||||
|     period: Long = 1, | ||||
|     unit: TimeUnit = TimeUnit.SECONDS, | ||||
| ) = addInterceptor(RateLimitInterceptor(httpUrl.host, permits, period.toDuration(unit.toDurationUnit()))) | ||||
| ) = addInterceptor( | ||||
|     RateLimitInterceptor(httpUrl.host, permits, period.toDuration(unit.toDurationUnit())), | ||||
| ) | ||||
|  | ||||
| /** | ||||
|  * An OkHttp interceptor that handles given url host's rate limiting. | ||||
| @@ -69,8 +71,5 @@ fun OkHttpClient.Builder.rateLimitHost( | ||||
|  * @param permits [Int]     Number of requests allowed within a period of units. | ||||
|  * @param period [Duration] The limiting duration. Defaults to 1.seconds. | ||||
|  */ | ||||
| fun OkHttpClient.Builder.rateLimitHost( | ||||
|     url: String, | ||||
|     permits: Int, | ||||
|     period: Duration = 1.seconds, | ||||
| ) = addInterceptor(RateLimitInterceptor(url.toHttpUrlOrNull()?.host, permits, period)) | ||||
| fun OkHttpClient.Builder.rateLimitHost(url: String, permits: Int, period: Duration = 1.seconds) = | ||||
|     addInterceptor(RateLimitInterceptor(url.toHttpUrlOrNull()?.host, permits, period)) | ||||
|   | ||||
| @@ -10,7 +10,11 @@ import androidx.annotation.StringRes | ||||
|  * @param resource the text resource. | ||||
|  * @param duration the duration of the toast. Defaults to short. | ||||
|  */ | ||||
| fun Context.toast(@StringRes resource: Int, duration: Int = Toast.LENGTH_SHORT, block: (Toast) -> Unit = {}): Toast { | ||||
| fun Context.toast( | ||||
|     @StringRes resource: Int, | ||||
|     duration: Int = Toast.LENGTH_SHORT, | ||||
|     block: (Toast) -> Unit = {}, | ||||
| ): Toast { | ||||
|     return toast(getString(resource), duration, block) | ||||
| } | ||||
|  | ||||
| @@ -20,7 +24,11 @@ fun Context.toast(@StringRes resource: Int, duration: Int = Toast.LENGTH_SHORT, | ||||
|  * @param text the text to display. | ||||
|  * @param duration the duration of the toast. Defaults to short. | ||||
|  */ | ||||
| fun Context.toast(text: String?, duration: Int = Toast.LENGTH_SHORT, block: (Toast) -> Unit = {}): Toast { | ||||
| fun Context.toast( | ||||
|     text: String?, | ||||
|     duration: Int = Toast.LENGTH_SHORT, | ||||
|     block: (Toast) -> Unit = {}, | ||||
| ): Toast { | ||||
|     return Toast.makeText(applicationContext, text.orEmpty(), duration).also { | ||||
|         block(it) | ||||
|         it.show() | ||||
|   | ||||
| @@ -47,10 +47,7 @@ abstract class WebViewClientCompat : WebViewClient() { | ||||
|         return shouldInterceptRequestCompat(view, request.url.toString()) | ||||
|     } | ||||
|  | ||||
|     final override fun shouldInterceptRequest( | ||||
|         view: WebView, | ||||
|         url: String, | ||||
|     ): WebResourceResponse? { | ||||
|     final override fun shouldInterceptRequest(view: WebView, url: String): WebResourceResponse? { | ||||
|         return shouldInterceptRequestCompat(view, url) | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -14,7 +14,24 @@ import kotlin.coroutines.resume | ||||
| object WebViewUtil { | ||||
|     const val SPOOF_PACKAGE_NAME = "org.chromium.chrome" | ||||
|  | ||||
|     const val MINIMUM_WEBVIEW_VERSION = 111 | ||||
|     const val MINIMUM_WEBVIEW_VERSION = 114 | ||||
|  | ||||
|     /** | ||||
|      * Uses the WebView's user agent string to create something similar to what Chrome on Android | ||||
|      * would return. | ||||
|      * | ||||
|      * Example of WebView user agent string: | ||||
|      *   Mozilla/5.0 (Linux; Android 13; Pixel 7 Build/TQ3A.230901.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/116.0.0.0 Mobile Safari/537.36 | ||||
|      * | ||||
|      * Example of Chrome on Android: | ||||
|      *   Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Mobile Safari/537.3 | ||||
|      */ | ||||
|     fun getInferredUserAgent(context: Context): String { | ||||
|         return WebView(context) | ||||
|             .getDefaultUserAgentString() | ||||
|             .replace("; Android .*?\\)".toRegex(), "; Android 10; K)") | ||||
|             .replace("Version/.* Chrome/".toRegex(), "Chrome/") | ||||
|     } | ||||
|  | ||||
|     fun supportsWebView(context: Context): Boolean { | ||||
|         try { | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package tachiyomi.core | ||||
|  | ||||
| object Constants { | ||||
|     const val URL_HELP = "https://tachiyomi.org/help/" | ||||
|     const val URL_HELP = "https://tachiyomi.org/docs/guides/troubleshooting/" | ||||
|  | ||||
|     const val MANGA_EXTRA = "manga" | ||||
|  | ||||
|   | ||||
| @@ -68,7 +68,11 @@ sealed class AndroidPreference<T>( | ||||
|         key: String, | ||||
|         defaultValue: String, | ||||
|     ) : AndroidPreference<String>(preferences, keyFlow, key, defaultValue) { | ||||
|         override fun read(preferences: SharedPreferences, key: String, defaultValue: String): String { | ||||
|         override fun read( | ||||
|             preferences: SharedPreferences, | ||||
|             key: String, | ||||
|             defaultValue: String, | ||||
|         ): String { | ||||
|             return preferences.getString(key, defaultValue) ?: defaultValue | ||||
|         } | ||||
|  | ||||
| @@ -128,7 +132,11 @@ sealed class AndroidPreference<T>( | ||||
|         key: String, | ||||
|         defaultValue: Boolean, | ||||
|     ) : AndroidPreference<Boolean>(preferences, keyFlow, key, defaultValue) { | ||||
|         override fun read(preferences: SharedPreferences, key: String, defaultValue: Boolean): Boolean { | ||||
|         override fun read( | ||||
|             preferences: SharedPreferences, | ||||
|             key: String, | ||||
|             defaultValue: Boolean, | ||||
|         ): Boolean { | ||||
|             return preferences.getBoolean(key, defaultValue) | ||||
|         } | ||||
|  | ||||
| @@ -143,7 +151,11 @@ sealed class AndroidPreference<T>( | ||||
|         key: String, | ||||
|         defaultValue: Set<String>, | ||||
|     ) : AndroidPreference<Set<String>>(preferences, keyFlow, key, defaultValue) { | ||||
|         override fun read(preferences: SharedPreferences, key: String, defaultValue: Set<String>): Set<String> { | ||||
|         override fun read( | ||||
|             preferences: SharedPreferences, | ||||
|             key: String, | ||||
|             defaultValue: Set<String>, | ||||
|         ): Set<String> { | ||||
|             return preferences.getStringSet(key, defaultValue) ?: defaultValue | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -64,7 +64,11 @@ class AndroidPreferenceStore( | ||||
|  | ||||
| private val SharedPreferences.keyFlow | ||||
|     get() = callbackFlow { | ||||
|         val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key: String? -> trySend(key) } | ||||
|         val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key: String? -> | ||||
|             trySend( | ||||
|                 key, | ||||
|             ) | ||||
|         } | ||||
|         registerOnSharedPreferenceChangeListener(listener) | ||||
|         awaitClose { | ||||
|             unregisterOnSharedPreferenceChangeListener(listener) | ||||
|   | ||||
| @@ -23,7 +23,9 @@ interface Preference<T> { | ||||
|     fun stateIn(scope: CoroutineScope): StateFlow<T> | ||||
| } | ||||
|  | ||||
| inline fun <reified T, R : T> Preference<T>.getAndSet(crossinline block: (T) -> R) = set(block(get())) | ||||
| inline fun <reified T, R : T> Preference<T>.getAndSet(crossinline block: (T) -> R) = set( | ||||
|     block(get()), | ||||
| ) | ||||
|  | ||||
| operator fun <T> Preference<Set<T>>.plusAssign(item: T) { | ||||
|     set(get() + item) | ||||
|   | ||||
| @@ -52,9 +52,15 @@ fun CoroutineScope.launchIO(block: suspend CoroutineScope.() -> Unit): Job = | ||||
| fun CoroutineScope.launchNonCancellable(block: suspend CoroutineScope.() -> Unit): Job = | ||||
|     launchIO { withContext(NonCancellable, block) } | ||||
|  | ||||
| suspend fun <T> withUIContext(block: suspend CoroutineScope.() -> T) = withContext(Dispatchers.Main, block) | ||||
| suspend fun <T> withUIContext(block: suspend CoroutineScope.() -> T) = withContext( | ||||
|     Dispatchers.Main, | ||||
|     block, | ||||
| ) | ||||
|  | ||||
| suspend fun <T> withIOContext(block: suspend CoroutineScope.() -> T) = withContext(Dispatchers.IO, block) | ||||
| suspend fun <T> withIOContext(block: suspend CoroutineScope.() -> T) = withContext( | ||||
|     Dispatchers.IO, | ||||
|     block, | ||||
| ) | ||||
|  | ||||
| suspend fun <T> withNonCancellableContext(block: suspend CoroutineScope.() -> T) = | ||||
|     withContext(NonCancellable, block) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user