merge23.5

Last commit merged: 6d69caf59e
This commit is contained in:
LuftVerbot 2023-11-17 20:12:49 +01:00
parent 72a54cbb6e
commit 4cbf9a813e
11 changed files with 119 additions and 83 deletions

View file

@ -4,8 +4,9 @@ import android.content.pm.ApplicationInfo
import android.graphics.Bitmap
import android.webkit.WebResourceRequest
import android.webkit.WebView
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
@ -17,9 +18,11 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.stringResource
import com.google.accompanist.web.AccompanistWebViewClient
import com.google.accompanist.web.LoadingState
@ -28,9 +31,12 @@ import com.google.accompanist.web.rememberWebViewNavigator
import com.google.accompanist.web.rememberWebViewState
import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.AppBarActions
import eu.kanade.presentation.components.WarningBanner
import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.system.getHtml
import eu.kanade.tachiyomi.util.system.setDefaultSettings
import kotlinx.coroutines.launch
import tachiyomi.presentation.core.components.material.Scaffold
@Composable
@ -46,7 +52,53 @@ fun WebViewScreenContent(
) {
val state = rememberWebViewState(url = url, additionalHttpHeaders = headers)
val navigator = rememberWebViewNavigator()
val uriHandler = LocalUriHandler.current
val scope = rememberCoroutineScope()
var currentUrl by remember { mutableStateOf(url) }
var showCloudflareHelp by remember { mutableStateOf(false) }
val webClient = remember {
object : AccompanistWebViewClient() {
override fun onPageStarted(view: WebView, url: String?, favicon: Bitmap?) {
super.onPageStarted(view, url, favicon)
url?.let {
currentUrl = it
onUrlChange(it)
}
}
override fun onPageFinished(view: WebView, url: String?) {
super.onPageFinished(view, url)
scope.launch {
val html = view.getHtml()
showCloudflareHelp = "Checking if the site connection is secure" in html
}
}
override fun doUpdateVisitedHistory(
view: WebView,
url: String?,
isReload: Boolean,
) {
super.doUpdateVisitedHistory(view, url, isReload)
url?.let {
currentUrl = it
onUrlChange(it)
}
}
override fun shouldOverrideUrlLoading(
view: WebView?,
request: WebResourceRequest?,
): Boolean {
request?.let {
view?.loadUrl(it.url.toString(), headers)
}
return super.shouldOverrideUrlLoading(view, request)
}
}
}
Scaffold(
topBar = {
@ -116,67 +168,38 @@ fun WebViewScreenContent(
}
},
) { contentPadding ->
val webClient = remember {
object : AccompanistWebViewClient() {
override fun onPageStarted(view: WebView, url: String?, favicon: Bitmap?) {
super.onPageStarted(view, url, favicon)
url?.let {
currentUrl = it
onUrlChange(it)
}
}
override fun doUpdateVisitedHistory(
view: WebView,
url: String?,
isReload: Boolean,
) {
super.doUpdateVisitedHistory(view, url, isReload)
url?.let {
currentUrl = it
onUrlChange(it)
}
}
override fun shouldOverrideUrlLoading(
view: WebView?,
request: WebResourceRequest?,
): Boolean {
request?.let {
// Don't attempt to open blobs as webpages
if (it.url.toString().startsWith("blob:http")) {
return false
}
// Continue with request, but with custom headers
view?.loadUrl(it.url.toString(), headers)
}
return super.shouldOverrideUrlLoading(view, request)
}
Column(
modifier = Modifier.padding(contentPadding),
) {
if (showCloudflareHelp) {
WarningBanner(
textRes = R.string.information_cloudflare_help,
modifier = Modifier.clickable {
uriHandler.openUri("https://aniyomi.org/docs/guides/troubleshooting/#solving-cloudflare-issues")
},
)
}
WebView(
state = state,
modifier = Modifier.weight(1f),
navigator = navigator,
onCreated = { webView ->
webView.setDefaultSettings()
// Debug mode (chrome://inspect/#devices)
if (BuildConfig.DEBUG &&
0 != webView.context.applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE
) {
WebView.setWebContentsDebuggingEnabled(true)
}
headers["user-agent"]?.let {
webView.settings.userAgentString = it
}
},
client = webClient,
)
}
WebView(
state = state,
modifier = Modifier
.padding(contentPadding)
.fillMaxSize(),
navigator = navigator,
onCreated = { webView ->
webView.setDefaultSettings()
// Debug mode (chrome://inspect/#devices)
if (BuildConfig.DEBUG &&
0 != webView.context.applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE
) {
WebView.setWebContentsDebuggingEnabled(true)
}
headers["user-agent"]?.let {
webView.settings.userAgentString = it
}
},
client = webClient,
)
}
}

View file

@ -6,7 +6,6 @@ import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.drawable.BitmapDrawable
import android.net.Uri
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
@ -24,6 +23,7 @@ import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.util.lang.chop
import eu.kanade.tachiyomi.util.system.cancelNotification
import eu.kanade.tachiyomi.util.system.getBitmapOrNull
import eu.kanade.tachiyomi.util.system.notificationBuilder
import eu.kanade.tachiyomi.util.system.notify
import tachiyomi.core.util.lang.launchUI
@ -274,7 +274,7 @@ class AnimeLibraryUpdateNotifier(private val context: Context) {
.size(NOTIF_ANIME_ICON_SIZE)
.build()
val drawable = context.imageLoader.execute(request).drawable
return (drawable as? BitmapDrawable)?.bitmap
return drawable?.getBitmapOrNull()
}
private fun getNewEpisodesDescription(episodes: Array<Episode>): String {

View file

@ -6,7 +6,6 @@ import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.drawable.BitmapDrawable
import android.net.Uri
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
@ -24,6 +23,7 @@ import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.util.lang.chop
import eu.kanade.tachiyomi.util.system.cancelNotification
import eu.kanade.tachiyomi.util.system.getBitmapOrNull
import eu.kanade.tachiyomi.util.system.notificationBuilder
import eu.kanade.tachiyomi.util.system.notify
import tachiyomi.core.util.lang.launchUI
@ -283,7 +283,7 @@ class MangaLibraryUpdateNotifier(private val context: Context) {
.size(NOTIF_MANGA_ICON_SIZE)
.build()
val drawable = context.imageLoader.execute(request).drawable
return (drawable as? BitmapDrawable)?.bitmap
return drawable?.getBitmapOrNull()
}
private fun getNewChaptersDescription(chapters: Array<Chapter>): String {

View file

@ -1,7 +1,6 @@
package eu.kanade.tachiyomi.ui.entries.anime
import android.content.Context
import android.graphics.drawable.BitmapDrawable
import android.net.Uri
import androidx.compose.material3.SnackbarHostState
import cafe.adriel.voyager.core.model.StateScreenModel
@ -16,6 +15,7 @@ import eu.kanade.tachiyomi.data.saver.Image
import eu.kanade.tachiyomi.data.saver.ImageSaver
import eu.kanade.tachiyomi.data.saver.Location
import eu.kanade.tachiyomi.util.editCover
import eu.kanade.tachiyomi.util.system.getBitmapOrNull
import eu.kanade.tachiyomi.util.system.toShareIntent
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
@ -98,7 +98,7 @@ class AnimeCoverScreenModel(
val result = context.imageLoader.execute(req).drawable
// TODO: Handle animated cover
val bitmap = (result as? BitmapDrawable)?.bitmap ?: return@withIOContext null
val bitmap = result?.getBitmapOrNull() ?: return@withIOContext null
imageSaver.save(
Image.Cover(
bitmap = bitmap,

View file

@ -1,7 +1,6 @@
package eu.kanade.tachiyomi.ui.entries.manga
import android.content.Context
import android.graphics.drawable.BitmapDrawable
import android.net.Uri
import androidx.compose.material3.SnackbarHostState
import cafe.adriel.voyager.core.model.StateScreenModel
@ -16,6 +15,7 @@ import eu.kanade.tachiyomi.data.saver.Image
import eu.kanade.tachiyomi.data.saver.ImageSaver
import eu.kanade.tachiyomi.data.saver.Location
import eu.kanade.tachiyomi.util.editCover
import eu.kanade.tachiyomi.util.system.getBitmapOrNull
import eu.kanade.tachiyomi.util.system.toShareIntent
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
@ -98,7 +98,7 @@ class MangaCoverScreenModel(
val result = context.imageLoader.execute(req).drawable
// TODO: Handle animated cover
val bitmap = (result as? BitmapDrawable)?.bitmap ?: return@withIOContext null
val bitmap = result?.getBitmapOrNull() ?: return@withIOContext null
imageSaver.save(
Image.Cover(
bitmap = bitmap,

View file

@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.ui.reader
import android.content.Context
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.net.Uri
import androidx.core.app.NotificationCompat
import coil.imageLoader
@ -13,6 +12,7 @@ import eu.kanade.tachiyomi.data.notification.NotificationHandler
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.util.system.cancelNotification
import eu.kanade.tachiyomi.util.system.getBitmapOrNull
import eu.kanade.tachiyomi.util.system.notificationBuilder
import eu.kanade.tachiyomi.util.system.notify
@ -35,12 +35,8 @@ class SaveImageNotifier(private val context: Context) {
.memoryCachePolicy(CachePolicy.DISABLED)
.size(720, 1280)
.target(
onSuccess = { result ->
showCompleteNotification(uri, (result as BitmapDrawable).bitmap)
},
onError = {
onError(null)
},
onSuccess = { showCompleteNotification(uri, it.getBitmapOrNull()) },
onError = { onError(null) },
)
.build()
context.imageLoader.enqueue(request)
@ -67,11 +63,11 @@ class SaveImageNotifier(private val context: Context) {
updateNotification()
}
private fun showCompleteNotification(uri: Uri, image: Bitmap) {
private fun showCompleteNotification(uri: Uri, image: Bitmap?) {
with(notificationBuilder) {
setContentTitle(context.getString(R.string.picture_saved))
setSmallIcon(R.drawable.ic_photo_24dp)
setStyle(NotificationCompat.BigPictureStyle().bigPicture(image))
image?.let { setStyle(NotificationCompat.BigPictureStyle().bigPicture(it)) }
setLargeIcon(image)
setAutoCancel(true)

View file

@ -277,10 +277,7 @@ open class ReaderPageImageView @JvmOverloads constructor(
)
when (image) {
is Drawable -> {
val bitmap = (image as BitmapDrawable).bitmap
setImage(ImageSource.bitmap(bitmap))
}
is BitmapDrawable -> setImage(ImageSource.bitmap(image.bitmap))
is InputStream -> setImage(ImageSource.inputStream(image))
else -> throw IllegalArgumentException("Not implemented for class ${image::class.simpleName}")
}

View file

@ -0,0 +1,13 @@
package eu.kanade.tachiyomi.util.system
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import androidx.core.graphics.drawable.toBitmap
import coil.drawable.ScaleDrawable
fun Drawable.getBitmapOrNull(): Bitmap? = when (this) {
is BitmapDrawable -> bitmap
is ScaleDrawable -> child.toBitmap()
else -> null
}

View file

@ -44,7 +44,7 @@ class CloudflareInterceptor(
// Because OkHttp's enqueue only handles IOExceptions, wrap the exception so that
// we don't crash the entire app
catch (e: CloudflareBypassException) {
throw IOException(context.getString(R.string.information_cloudflare_bypass_failure))
throw IOException(context.getString(R.string.information_cloudflare_bypass_failure), e)
} catch (e: Exception) {
throw IOException(e)
}

View file

@ -6,8 +6,10 @@ import android.content.pm.PackageManager
import android.webkit.CookieManager
import android.webkit.WebSettings
import android.webkit.WebView
import kotlinx.coroutines.suspendCancellableCoroutine
import logcat.LogPriority
import tachiyomi.core.util.system.logcat
import kotlin.coroutines.resume
object WebViewUtil {
const val SPOOF_PACKAGE_NAME = "org.chromium.chrome"
@ -32,6 +34,10 @@ fun WebView.isOutdated(): Boolean {
return getWebViewMajorVersion() < WebViewUtil.MINIMUM_WEBVIEW_VERSION
}
suspend fun WebView.getHtml(): String = suspendCancellableCoroutine {
evaluateJavascript("document.documentElement.outerHTML") { html -> it.resume(html) }
}
@SuppressLint("SetJavaScriptEnabled")
fun WebView.setDefaultSettings() {
with(settings) {

View file

@ -905,6 +905,7 @@
<string name="information_empty_category">You have no categories. Tap the plus button to create one for organizing your library.</string>
<string name="information_empty_category_dialog">You don\'t have any categories yet.</string>
<string name="information_cloudflare_bypass_failure">Failed to bypass Cloudflare</string>
<string name="information_cloudflare_help">Tap here for help with Cloudflare</string>
<string name="information_required_plain">*required</string>
<!-- Do not translate "WebView" -->
<string name="information_webview_outdated">Please update the WebView app for better compatibility</string>