From e59a6b8ec065e1bdfa5cb1f93809f7ca2e9338f6 Mon Sep 17 00:00:00 2001 From: HapeLee <63206378+HapeLee@users.noreply.github.com> Date: Sun, 26 Apr 2026 14:31:18 +0800 Subject: [PATCH] =?UTF-8?q?[=E4=BC=98=E5=8C=96]=20=E8=AE=A2=E9=98=85?= =?UTF-8?q?=E7=95=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/ui/rss/read/RssReadRouteScreen.kt | 177 ++++++------------ .../legado/app/ui/rss/read/VisibleWebView.kt | 19 +- 2 files changed, 66 insertions(+), 130 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/rss/read/RssReadRouteScreen.kt b/app/src/main/java/io/legado/app/ui/rss/read/RssReadRouteScreen.kt index da4f1f667..f5e43f7e8 100644 --- a/app/src/main/java/io/legado/app/ui/rss/read/RssReadRouteScreen.kt +++ b/app/src/main/java/io/legado/app/ui/rss/read/RssReadRouteScreen.kt @@ -6,7 +6,6 @@ import android.annotation.SuppressLint import android.net.Uri import android.net.http.SslError import android.os.SystemClock -import android.view.MotionEvent import android.view.View import android.webkit.JavascriptInterface import android.webkit.SslErrorHandler @@ -19,20 +18,14 @@ import android.webkit.WebViewClient import androidx.activity.compose.BackHandler import androidx.activity.compose.LocalActivity import androidx.appcompat.app.AlertDialog -import androidx.compose.animation.animateContentSize +import androidx.appcompat.app.AppCompatActivity import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.statusBarsPadding -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.clickable import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.Login import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.CleaningServices @@ -44,17 +37,18 @@ import androidx.compose.material.icons.filled.Star import androidx.compose.material.icons.filled.Stop import androidx.compose.material.icons.filled.VolumeUp import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon import androidx.compose.material3.LinearProgressIndicator +import androidx.compose.material3.Icon +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf 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.LocalContext import androidx.compose.ui.res.stringResource @@ -74,20 +68,16 @@ import io.legado.app.model.Download import io.legado.app.ui.association.OnLineImportActivity import io.legado.app.ui.config.otherConfig.OtherConfig import io.legado.app.ui.login.SourceLoginActivity -import io.legado.app.ui.theme.LegadoTheme import io.legado.app.ui.widget.components.AppTextField import io.legado.app.ui.widget.components.button.MediumIconButton import io.legado.app.ui.widget.components.card.GlassCard -import io.legado.app.ui.widget.components.card.NormalCard import io.legado.app.ui.widget.components.button.SmallIconButton -import io.legado.app.ui.widget.components.button.SmallTonalIconButton import io.legado.app.ui.widget.components.button.TopBarActionButton -import io.legado.app.ui.widget.components.list.TopFloatingStickyItem +import io.legado.app.ui.widget.components.button.TopBarNavigationButton import io.legado.app.ui.widget.components.menuItem.MenuItemIcon import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet -import io.legado.app.ui.widget.components.text.AppText import io.legado.app.utils.NetworkUtils import io.legado.app.utils.isTrue import io.legado.app.utils.keepScreenOn @@ -97,9 +87,6 @@ import io.legado.app.utils.setDarkeningAllowed import io.legado.app.utils.share import io.legado.app.utils.startActivity import io.legado.app.utils.toastOnUi -import kotlinx.coroutines.Job -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch import org.apache.commons.text.StringEscapeUtils import org.jsoup.Jsoup import java.net.URLDecoder @@ -118,8 +105,7 @@ fun RssReadRouteScreen( ) { val context = LocalContext.current val activity = LocalActivity.current - val appCompatActivity = activity as? androidx.appcompat.app.AppCompatActivity - val scope = rememberCoroutineScope() + val appCompatActivity = activity as? AppCompatActivity val defaultTopBarTitle = stringResource(R.string.rss) var pageTitle by remember(title, defaultTopBarTitle) { @@ -128,9 +114,6 @@ fun RssReadRouteScreen( var webProgress by remember { mutableIntStateOf(100) } var showMenu by remember { mutableStateOf(false) } var showRedirectMenu by remember { mutableStateOf(false) } - var showFloatingCard by remember { mutableStateOf(true) } - var showFloatingCardJob by remember { mutableStateOf(null) } - var webView by remember { mutableStateOf(null) } var redirectPolicy by remember { mutableStateOf(RedirectPolicy.ALLOW_ALL) } @@ -189,102 +172,26 @@ fun RssReadRouteScreen( } } - Box(modifier = Modifier.fillMaxSize()) { - VisibleWebViewCompose( - modifier = Modifier.fillMaxSize(), - onCreated = { createdWebView -> - webView = createdWebView - configureRssReadWebView( - webView = createdWebView, - context = context, - activity = activity, - appCompatActivity = appCompatActivity, - viewModel = viewModel, - initialTitle = title, - redirectPolicyProvider = { redirectPolicy }, - callbacks = RssReadWebControllerCallbacks( - onProgressChanged = { webProgress = it }, - onPageTitleResolved = { resolved -> - pageTitle = resolved.ifBlank { defaultTopBarTitle } - } - ) - ) - createdWebView.setOnTouchListener { _, event -> - when (event.actionMasked) { - MotionEvent.ACTION_DOWN, - MotionEvent.ACTION_MOVE -> { - showFloatingCardJob?.cancel() - showFloatingCard = false - } - - MotionEvent.ACTION_UP, - MotionEvent.ACTION_CANCEL -> { - createdWebView.performClick() - showFloatingCardJob?.cancel() - showFloatingCardJob = scope.launch { - delay(180L) - showFloatingCard = true - } - } - } - false - } - } - ) - if (webProgress in 0..99) { - LinearProgressIndicator( - progress = { webProgress / 100f }, - modifier = Modifier.fillMaxWidth() - ) - } - TopFloatingStickyItem( - item = if (showFloatingCard) { - RssReadFloatingSummary( - titleText = pageTitle.ifBlank { defaultTopBarTitle }, - progressText = if (webProgress in 0..99) " ยท ${webProgress}%" else null - ) - } else { - null - }, - modifier = Modifier - .align(Alignment.TopCenter) - .statusBarsPadding() - .padding(top = 6.dp), - ) { summary -> - NormalCard( - modifier = Modifier.animateContentSize(), - cornerRadius = 32.dp, - containerColor = LegadoTheme.colorScheme.surfaceContainer, - contentColor = LegadoTheme.colorScheme.onCardContainer - ) { - Row( - modifier = Modifier.padding(horizontal = 12.dp, vertical = 8.dp), - verticalAlignment = Alignment.CenterVertically - ) { - SmallTonalIconButton( - imageVector = Icons.AutoMirrored.Filled.ArrowBack, - contentDescription = stringResource(R.string.back), - onClick = onBackClick - ) - Spacer(modifier = Modifier.width(8.dp)) - AppText( - text = summary.titleText, - style = LegadoTheme.typography.labelSmallEmphasized, + Scaffold( + topBar = { + TopAppBar( + title = { + Text( + text = pageTitle.ifBlank { defaultTopBarTitle }, maxLines = 1, - overflow = TextOverflow.Ellipsis, - modifier = Modifier.padding(horizontal = 12.dp) + overflow = TextOverflow.Ellipsis ) - summary.progressText?.let { text -> - AppText(text = text, style = LegadoTheme.typography.labelMediumEmphasized) - Spacer(modifier = Modifier.width(6.dp)) - } - SmallTonalIconButton( + }, + navigationIcon = { + TopBarNavigationButton(onClick = onBackClick) + }, + actions = { + TopBarActionButton( imageVector = Icons.Default.Refresh, contentDescription = stringResource(R.string.refresh), onClick = { viewModel.refresh { webView?.reload() } } ) - Spacer(modifier = Modifier.width(8.dp)) - SmallTonalIconButton( + TopBarActionButton( imageVector = Icons.Default.Star, contentDescription = stringResource(R.string.favorite), onClick = { @@ -294,9 +201,8 @@ fun RssReadRouteScreen( showFavoriteSheet = true } ) - Spacer(modifier = Modifier.width(8.dp)) Box { - SmallTonalIconButton( + TopBarActionButton( imageVector = Icons.Default.MoreVert, contentDescription = "Menu", onClick = { showMenu = true } @@ -391,6 +297,40 @@ fun RssReadRouteScreen( } } } + ) + } + ) { paddingValues -> + Box( + modifier = Modifier + .fillMaxSize() + .padding(paddingValues) + ) { + VisibleWebViewCompose( + modifier = Modifier.fillMaxSize(), + onCreated = { createdWebView -> + webView = createdWebView + configureRssReadWebView( + webView = createdWebView, + context = context, + activity = activity, + appCompatActivity = appCompatActivity, + viewModel = viewModel, + initialTitle = title, + redirectPolicyProvider = { redirectPolicy }, + callbacks = RssReadWebControllerCallbacks( + onProgressChanged = { webProgress = it }, + onPageTitleResolved = { resolved -> + pageTitle = resolved.ifBlank { defaultTopBarTitle } + } + ) + ) + } + ) + if (webProgress in 0..99) { + LinearProgressIndicator( + progress = { webProgress / 100f }, + modifier = Modifier.fillMaxWidth() + ) } } } @@ -413,11 +353,6 @@ fun RssReadRouteScreen( ) } -private data class RssReadFloatingSummary( - val titleText: String, - val progressText: String?, -) - @Composable private fun FavoriteEditSheet( show: Boolean, diff --git a/app/src/main/java/io/legado/app/ui/rss/read/VisibleWebView.kt b/app/src/main/java/io/legado/app/ui/rss/read/VisibleWebView.kt index 6c1afb55f..44334c885 100644 --- a/app/src/main/java/io/legado/app/ui/rss/read/VisibleWebView.kt +++ b/app/src/main/java/io/legado/app/ui/rss/read/VisibleWebView.kt @@ -11,6 +11,7 @@ import android.webkit.WebView import androidx.appcompat.app.AppCompatActivity import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.viewinterop.AndroidView import io.legado.app.R @@ -47,10 +48,6 @@ class VisibleWebView( evaluateJavascript(js, null) } - override fun onWindowVisibilityChanged(visibility: Int) { - super.onWindowVisibilityChanged(VISIBLE) - } - override fun performClick(): Boolean { return super.performClick() } @@ -143,24 +140,28 @@ fun VisibleWebViewCompose( onCreated: (VisibleWebView) -> Unit, onDestroyed: (() -> Unit)? = null ) { - var webViewRef: VisibleWebView? = null + val webViewHolder = remember { WebViewHolder() } AndroidView( modifier = modifier, factory = { context -> VisibleWebView(context).also { - webViewRef = it + webViewHolder.webView = it onCreated(it) } }, update = { - webViewRef = it + webViewHolder.webView = it } ) DisposableEffect(Unit) { onDispose { onDestroyed?.invoke() - webViewRef?.destroy() - webViewRef = null + webViewHolder.webView?.destroy() + webViewHolder.webView = null } } } + +private class WebViewHolder { + var webView: VisibleWebView? = null +}