Improve WebView multi-window UX (#2662)
- Navigation history for lower windows is preserved when a popup is opened - Back gesture will close a popup window rather than the entire WebView activity when there is no previous page - The leftmost close button closes the entire activity as before - When a popup window is shown, a new button appears to close just that window (cherry picked from commit 855eea2ada8c09404dee99a965325913b246618f)
This commit is contained in:
committed by
NGB-Was-Taken
parent
208d291b3c
commit
edcf939611
@@ -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<String, String> = 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,
|
||||
|
||||
Reference in New Issue
Block a user