|
@@ -1,24 +1,38 @@
|
|
|
package io.legado.app.ui.dict
|
|
package io.legado.app.ui.dict
|
|
|
|
|
|
|
|
|
|
+import android.annotation.SuppressLint
|
|
|
|
|
+import android.net.Uri
|
|
|
|
|
+import android.webkit.WebChromeClient
|
|
|
|
|
+import android.webkit.WebResourceRequest
|
|
|
|
|
+import android.webkit.WebSettings
|
|
|
|
|
+import android.webkit.WebView
|
|
|
|
|
+import android.webkit.WebViewClient
|
|
|
import androidx.compose.animation.core.FastOutSlowInEasing
|
|
import androidx.compose.animation.core.FastOutSlowInEasing
|
|
|
import androidx.compose.animation.core.spring
|
|
import androidx.compose.animation.core.spring
|
|
|
import androidx.compose.animation.core.tween
|
|
import androidx.compose.animation.core.tween
|
|
|
import androidx.compose.foundation.layout.Box
|
|
import androidx.compose.foundation.layout.Box
|
|
|
import androidx.compose.foundation.layout.Column
|
|
import androidx.compose.foundation.layout.Column
|
|
|
|
|
+import androidx.compose.foundation.layout.fillMaxSize
|
|
|
import androidx.compose.foundation.layout.fillMaxWidth
|
|
import androidx.compose.foundation.layout.fillMaxWidth
|
|
|
import androidx.compose.foundation.layout.height
|
|
import androidx.compose.foundation.layout.height
|
|
|
|
|
+import androidx.compose.foundation.layout.heightIn
|
|
|
import androidx.compose.foundation.layout.padding
|
|
import androidx.compose.foundation.layout.padding
|
|
|
import androidx.compose.foundation.pager.HorizontalPager
|
|
import androidx.compose.foundation.pager.HorizontalPager
|
|
|
import androidx.compose.foundation.pager.rememberPagerState
|
|
import androidx.compose.foundation.pager.rememberPagerState
|
|
|
import androidx.compose.foundation.rememberScrollState
|
|
import androidx.compose.foundation.rememberScrollState
|
|
|
import androidx.compose.foundation.verticalScroll
|
|
import androidx.compose.foundation.verticalScroll
|
|
|
|
|
+import androidx.compose.material.icons.Icons
|
|
|
|
|
+import androidx.compose.material.icons.filled.OpenInBrowser
|
|
|
import androidx.compose.runtime.Composable
|
|
import androidx.compose.runtime.Composable
|
|
|
import androidx.compose.runtime.LaunchedEffect
|
|
import androidx.compose.runtime.LaunchedEffect
|
|
|
import androidx.compose.runtime.getValue
|
|
import androidx.compose.runtime.getValue
|
|
|
|
|
+import androidx.compose.runtime.mutableIntStateOf
|
|
|
import androidx.compose.runtime.mutableStateMapOf
|
|
import androidx.compose.runtime.mutableStateMapOf
|
|
|
|
|
+import androidx.compose.runtime.mutableStateOf
|
|
|
import androidx.compose.runtime.remember
|
|
import androidx.compose.runtime.remember
|
|
|
import androidx.compose.runtime.rememberCoroutineScope
|
|
import androidx.compose.runtime.rememberCoroutineScope
|
|
|
import androidx.compose.runtime.rememberUpdatedState
|
|
import androidx.compose.runtime.rememberUpdatedState
|
|
|
|
|
+import androidx.compose.runtime.setValue
|
|
|
import androidx.compose.runtime.snapshotFlow
|
|
import androidx.compose.runtime.snapshotFlow
|
|
|
import androidx.compose.ui.Alignment
|
|
import androidx.compose.ui.Alignment
|
|
|
import androidx.compose.ui.Modifier
|
|
import androidx.compose.ui.Modifier
|
|
@@ -28,14 +42,17 @@ import androidx.compose.ui.res.stringResource
|
|
|
import androidx.compose.ui.unit.dp
|
|
import androidx.compose.ui.unit.dp
|
|
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|
|
import io.legado.app.R
|
|
import io.legado.app.R
|
|
|
|
|
+import io.legado.app.ui.rss.read.VisibleWebViewCompose
|
|
|
import io.legado.app.ui.widget.components.EmptyMessage
|
|
import io.legado.app.ui.widget.components.EmptyMessage
|
|
|
|
|
+import io.legado.app.ui.widget.components.button.series.MediumTonalButton
|
|
|
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
|
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
|
|
import io.legado.app.ui.widget.components.pager.pagerHeight
|
|
import io.legado.app.ui.widget.components.pager.pagerHeight
|
|
|
import io.legado.app.ui.widget.components.pager.rememberPagerAnimatedHeight
|
|
import io.legado.app.ui.widget.components.pager.rememberPagerAnimatedHeight
|
|
|
import io.legado.app.ui.widget.components.progressIndicator.AppCircularProgressIndicator
|
|
import io.legado.app.ui.widget.components.progressIndicator.AppCircularProgressIndicator
|
|
|
|
|
+import io.legado.app.ui.widget.components.progressIndicator.AppLinearProgressIndicator
|
|
|
import io.legado.app.ui.widget.components.tabRow.AppTabRow
|
|
import io.legado.app.ui.widget.components.tabRow.AppTabRow
|
|
|
import io.legado.app.ui.widget.components.text.HtmlContent
|
|
import io.legado.app.ui.widget.components.text.HtmlContent
|
|
|
-import kotlinx.coroutines.flow.Flow
|
|
|
|
|
|
|
+import io.legado.app.utils.openUrl
|
|
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
|
|
import kotlinx.coroutines.flow.drop
|
|
import kotlinx.coroutines.flow.drop
|
|
|
import kotlinx.coroutines.launch
|
|
import kotlinx.coroutines.launch
|
|
@@ -49,6 +66,7 @@ fun DictSheet(
|
|
|
viewModel: DictViewModel = koinViewModel(),
|
|
viewModel: DictViewModel = koinViewModel(),
|
|
|
) {
|
|
) {
|
|
|
val state by viewModel.uiState.collectAsStateWithLifecycle()
|
|
val state by viewModel.uiState.collectAsStateWithLifecycle()
|
|
|
|
|
+ var webPage by remember { mutableStateOf<DictWebPage?>(null) }
|
|
|
|
|
|
|
|
LaunchedEffect(show, word) {
|
|
LaunchedEffect(show, word) {
|
|
|
if (show) {
|
|
if (show) {
|
|
@@ -56,39 +74,50 @@ fun DictSheet(
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ LaunchedEffect(viewModel.effects) {
|
|
|
|
|
+ viewModel.effects.collect { effect ->
|
|
|
|
|
+ when (effect) {
|
|
|
|
|
+ is DictEffect.ShowToast -> Unit
|
|
|
|
|
+ is DictEffect.OpenWebPage -> webPage = DictWebPage(effect.title, effect.url)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
AppModalBottomSheet(
|
|
AppModalBottomSheet(
|
|
|
- show = show,
|
|
|
|
|
|
|
+ show = show && webPage == null,
|
|
|
onDismissRequest = onDismissRequest,
|
|
onDismissRequest = onDismissRequest,
|
|
|
title = word,
|
|
title = word,
|
|
|
|
|
+ endAction = {
|
|
|
|
|
+ MediumTonalButton(
|
|
|
|
|
+ onClick = { viewModel.onIntent(DictIntent.OpenSelectedRuleInWebView) },
|
|
|
|
|
+ icon = Icons.Default.OpenInBrowser,
|
|
|
|
|
+ contentDescription = stringResource(R.string.source_tab_web_view),
|
|
|
|
|
+ )
|
|
|
|
|
+ },
|
|
|
contentPaddingEnabled = false,
|
|
contentPaddingEnabled = false,
|
|
|
animateContentSize = false,
|
|
animateContentSize = false,
|
|
|
) {
|
|
) {
|
|
|
DictSheetContent(
|
|
DictSheetContent(
|
|
|
state = state,
|
|
state = state,
|
|
|
onIntent = viewModel::onIntent,
|
|
onIntent = viewModel::onIntent,
|
|
|
- effects = viewModel.effects,
|
|
|
|
|
modifier = Modifier
|
|
modifier = Modifier
|
|
|
.fillMaxWidth()
|
|
.fillMaxWidth()
|
|
|
.padding(bottom = 16.dp),
|
|
.padding(bottom = 16.dp),
|
|
|
)
|
|
)
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ DictWebSheet(
|
|
|
|
|
+ page = webPage,
|
|
|
|
|
+ onDismissRequest = { webPage = null },
|
|
|
|
|
+ )
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@Composable
|
|
@Composable
|
|
|
private fun DictSheetContent(
|
|
private fun DictSheetContent(
|
|
|
state: DictUiState,
|
|
state: DictUiState,
|
|
|
onIntent: (DictIntent) -> Unit,
|
|
onIntent: (DictIntent) -> Unit,
|
|
|
- effects: Flow<DictEffect>,
|
|
|
|
|
modifier: Modifier = Modifier,
|
|
modifier: Modifier = Modifier,
|
|
|
) {
|
|
) {
|
|
|
- LaunchedEffect(effects) {
|
|
|
|
|
- effects.collect { effect ->
|
|
|
|
|
- when (effect) {
|
|
|
|
|
- is DictEffect.ShowToast -> Unit
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
DictContent(
|
|
DictContent(
|
|
|
state = state,
|
|
state = state,
|
|
|
onIntent = onIntent,
|
|
onIntent = onIntent,
|
|
@@ -96,6 +125,82 @@ private fun DictSheetContent(
|
|
|
)
|
|
)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+private data class DictWebPage(
|
|
|
|
|
+ val title: String,
|
|
|
|
|
+ val url: String,
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+@SuppressLint("SetJavaScriptEnabled")
|
|
|
|
|
+@Composable
|
|
|
|
|
+private fun DictWebSheet(
|
|
|
|
|
+ page: DictWebPage?,
|
|
|
|
|
+ onDismissRequest: () -> Unit,
|
|
|
|
|
+) {
|
|
|
|
|
+ page ?: return
|
|
|
|
|
+ val context = androidx.compose.ui.platform.LocalContext.current
|
|
|
|
|
+ var progress by remember(page.url) { mutableIntStateOf(0) }
|
|
|
|
|
+
|
|
|
|
|
+ AppModalBottomSheet(
|
|
|
|
|
+ show = true,
|
|
|
|
|
+ onDismissRequest = onDismissRequest,
|
|
|
|
|
+ title = page.title,
|
|
|
|
|
+ contentPaddingEnabled = false,
|
|
|
|
|
+ sheetGesturesEnabled = false,
|
|
|
|
|
+ ) {
|
|
|
|
|
+ Box(
|
|
|
|
|
+ Modifier
|
|
|
|
|
+ .fillMaxWidth()
|
|
|
|
|
+ .heightIn(min = 520.dp)
|
|
|
|
|
+ ) {
|
|
|
|
|
+ VisibleWebViewCompose(
|
|
|
|
|
+ modifier = Modifier.fillMaxSize(),
|
|
|
|
|
+ onCreated = { webView ->
|
|
|
|
|
+ webView.apply {
|
|
|
|
|
+ setBackgroundColor(android.graphics.Color.WHITE)
|
|
|
|
|
+ settings.apply {
|
|
|
|
|
+ mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
|
|
|
|
|
+ domStorageEnabled = true
|
|
|
|
|
+ useWideViewPort = true
|
|
|
|
|
+ loadWithOverviewMode = true
|
|
|
|
|
+ builtInZoomControls = true
|
|
|
|
|
+ displayZoomControls = false
|
|
|
|
|
+ javaScriptEnabled = true
|
|
|
|
|
+ }
|
|
|
|
|
+ webViewClient = object : WebViewClient() {
|
|
|
|
|
+ override fun shouldOverrideUrlLoading(
|
|
|
|
|
+ view: WebView,
|
|
|
|
|
+ request: WebResourceRequest,
|
|
|
|
|
+ ): Boolean = handleUrl(request.url)
|
|
|
|
|
+
|
|
|
|
|
+ @Suppress("DEPRECATION", "OVERRIDE_DEPRECATION")
|
|
|
|
|
+ override fun shouldOverrideUrlLoading(
|
|
|
|
|
+ view: WebView,
|
|
|
|
|
+ url: String
|
|
|
|
|
+ ): Boolean =
|
|
|
|
|
+ handleUrl(Uri.parse(url))
|
|
|
|
|
+
|
|
|
|
|
+ private fun handleUrl(uri: Uri): Boolean {
|
|
|
|
|
+ if (uri.scheme == "http" || uri.scheme == "https") return false
|
|
|
|
|
+ context.openUrl(uri)
|
|
|
|
|
+ return true
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ webChromeClient = object : WebChromeClient() {
|
|
|
|
|
+ override fun onProgressChanged(view: WebView?, newProgress: Int) {
|
|
|
|
|
+ progress = newProgress
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ loadUrl(page.url)
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ )
|
|
|
|
|
+ if (progress in 0..99) {
|
|
|
|
|
+ AppLinearProgressIndicator(Modifier.fillMaxWidth())
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
@Composable
|
|
@Composable
|
|
|
private fun DictContent(
|
|
private fun DictContent(
|
|
|
state: DictUiState,
|
|
state: DictUiState,
|