diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b66d8974..81f11f38c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ The format is a modified version of [Keep a Changelog](https://keepachangelog.co - `Other` - for technical stuff. ## [Unreleased] +### Improved +- Improved various aspects of the WebView multi window support added in 0.19.2 ([@TheUnlocked](https://github.com/TheUnlocked)) ([#2662](https://github.com/mihonapp/mihon/pull/2662)) ## [v0.19.3] - 2025-11-04 ### Fixed diff --git a/app/src/main/java/eu/kanade/presentation/webview/WebViewScreenContent.kt b/app/src/main/java/eu/kanade/presentation/webview/WebViewScreenContent.kt index 5ce838164..48c4606d3 100644 --- a/app/src/main/java/eu/kanade/presentation/webview/WebViewScreenContent.kt +++ b/app/src/main/java/eu/kanade/presentation/webview/WebViewScreenContent.kt @@ -5,6 +5,7 @@ import android.graphics.Bitmap import android.os.Message import android.webkit.WebResourceRequest import android.webkit.WebView +import androidx.activity.compose.BackHandler import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -28,7 +29,9 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalUriHandler +import androidx.compose.ui.res.vectorResource import androidx.compose.ui.unit.dp import cafe.adriel.voyager.core.stack.mutableStateStackOf import com.kevinnzou.web.AccompanistWebChromeClient @@ -36,12 +39,13 @@ import com.kevinnzou.web.AccompanistWebViewClient import com.kevinnzou.web.LoadingState import com.kevinnzou.web.WebContent import com.kevinnzou.web.WebView +import com.kevinnzou.web.WebViewNavigator import com.kevinnzou.web.WebViewState -import com.kevinnzou.web.rememberWebViewNavigator 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.collections.immutable.persistentListOf @@ -50,13 +54,13 @@ import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.i18n.stringResource -class WebViewWindow(webContent: WebContent) { +class WebViewWindow(webContent: WebContent, val navigator: WebViewNavigator) { var state by mutableStateOf(WebViewState(webContent)) var popupMessage: Message? = null private set var webView: WebView? = null - constructor(popupMessage: Message) : this(WebContent.NavigatorOnly) { + constructor(popupMessage: Message, navigator: WebViewNavigator) : this(WebContent.NavigatorOnly, navigator) { this.popupMessage = popupMessage } } @@ -72,27 +76,20 @@ fun WebViewScreenContent( headers: Map = emptyMap(), onUrlChange: (String) -> Unit = {}, ) { + val coroutineScope = rememberCoroutineScope() + val windowStack = remember { mutableStateStackOf( WebViewWindow( WebContent.Url(url = url, additionalHttpHeaders = headers), + WebViewNavigator(coroutineScope), ), ) } val currentWindow = windowStack.lastItemOrNull!! + val navigator = currentWindow.navigator - val popState: (() -> Unit) = remember { - { - if (windowStack.size == 1) { - onNavigateUp() - } else { - windowStack.pop() - } - } - } - - val navigator = rememberWebViewNavigator() val uriHandler = LocalUriHandler.current val scope = rememberCoroutineScope() @@ -161,7 +158,7 @@ fun WebViewScreenContent( ): Boolean { // if it wasn't initiated by a user gesture, we should ignore it like a normal browser would if (isUserGesture) { - windowStack.push(WebViewWindow(resultMsg)) + windowStack.push(WebViewWindow(resultMsg, WebViewNavigator(coroutineScope))) return true } return false @@ -176,6 +173,18 @@ fun WebViewScreenContent( return webView } + val popState = remember<() -> Unit> { + { + if (windowStack.size == 1) { + onNavigateUp() + } else { + windowStack.pop() + } + } + } + + BackHandler(windowStack.size > 1, popState) + Scaffold( topBar = { Box { @@ -183,7 +192,7 @@ fun WebViewScreenContent( AppBar( title = currentWindow.state.pageTitle ?: initialTitle, subtitle = currentUrl, - navigateUp = popState, + navigateUp = onNavigateUp, navigationIcon = Icons.Outlined.Close, actions = { AppBarActions( @@ -224,7 +233,18 @@ fun WebViewScreenContent( title = stringResource(MR.strings.pref_clear_cookies), onClick = { onClearCookies(currentUrl) }, ), - ), + ).builder().apply { + if (windowStack.size > 1) { + add( + 0, + AppBar.Action( + title = stringResource(MR.strings.action_webview_close_tab), + icon = ImageVector.vectorResource(R.drawable.ic_tab_close_24px), + onClick = popState, + ), + ) + } + }.build(), ) }, ) @@ -297,7 +317,7 @@ fun WebViewScreenContent( // The composable is being disposed but the WebView object is not. // When the WebView element is recomposed, we will want the WebView to resume from its state // before it was unmounted, we won't want it to reset back to its original target. - window.state = WebViewState(WebContent.NavigatorOnly) + window.state.content = WebContent.NavigatorOnly } }, client = webClient, diff --git a/app/src/main/res/drawable/ic_tab_close_24px.xml b/app/src/main/res/drawable/ic_tab_close_24px.xml new file mode 100644 index 000000000..e14d35158 --- /dev/null +++ b/app/src/main/res/drawable/ic_tab_close_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/i18n/src/commonMain/moko-resources/base/strings.xml b/i18n/src/commonMain/moko-resources/base/strings.xml index 7bb1aaa8b..9b5718ee8 100644 --- a/i18n/src/commonMain/moko-resources/base/strings.xml +++ b/i18n/src/commonMain/moko-resources/base/strings.xml @@ -164,6 +164,7 @@ Back Forward Refresh + Close tab Start downloading now Not now Add anyway