diff --git a/app/src/main/java/me/ash/reader/infrastructure/preference/ReadingRendererPreference.kt b/app/src/main/java/me/ash/reader/infrastructure/preference/ReadingRendererPreference.kt index 30b6a6e56..2294de986 100644 --- a/app/src/main/java/me/ash/reader/infrastructure/preference/ReadingRendererPreference.kt +++ b/app/src/main/java/me/ash/reader/infrastructure/preference/ReadingRendererPreference.kt @@ -2,31 +2,33 @@ package me.ash.reader.infrastructure.preference import android.content.Context import androidx.compose.runtime.Stable +import androidx.compose.runtime.compositionLocalOf import androidx.datastore.preferences.core.Preferences import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import me.ash.reader.R -import me.ash.reader.ui.ext.DataStoreKeys +import me.ash.reader.ui.ext.DataStoreKey +import me.ash.reader.ui.ext.DataStoreKey.Companion.readingRenderer import me.ash.reader.ui.ext.dataStore import me.ash.reader.ui.ext.put +val LocalReadingRenderer = + compositionLocalOf { ReadingRendererPreference.default } + sealed class ReadingRendererPreference(val value: Int) : Preference() { object WebView : ReadingRendererPreference(0) object NativeComponent : ReadingRendererPreference(1) override fun put(context: Context, scope: CoroutineScope) { scope.launch { - context.dataStore.put( - DataStoreKeys.ReadingRenderer, - value - ) + context.dataStore.put(DataStoreKey.readingRenderer, value) } } @Stable fun toDesc(context: Context): String = when (this) { - WebView -> context.getString(R.string.web_view) + WebView -> context.getString(R.string.webview) NativeComponent -> context.getString(R.string.native_component) } @@ -35,8 +37,8 @@ sealed class ReadingRendererPreference(val value: Int) : Preference() { val default = WebView val values = listOf(WebView, NativeComponent) - fun fromPreferences(preferences: Preferences): ReadingRendererPreference = - when (preferences[DataStoreKeys.ReadingRenderer.key]) { + fun fromPreferences(preferences: Preferences) = + when (preferences[DataStoreKey.keys[readingRenderer]?.key as Preferences.Key]) { 0 -> WebView 1 -> NativeComponent else -> default diff --git a/app/src/main/java/me/ash/reader/infrastructure/preference/ReadingSubheadAlignPreference.kt b/app/src/main/java/me/ash/reader/infrastructure/preference/ReadingSubheadAlignPreference.kt index 5b1d51bb8..32eafd084 100644 --- a/app/src/main/java/me/ash/reader/infrastructure/preference/ReadingSubheadAlignPreference.kt +++ b/app/src/main/java/me/ash/reader/infrastructure/preference/ReadingSubheadAlignPreference.kt @@ -48,8 +48,8 @@ sealed class ReadingSubheadAlignPreference(val value: Int) : Preference() { fun toTextAlignCSS(): String = when (this) { - Left -> "left" - Right -> "right" + Start -> "left" + End -> "right" Center -> "center" Justify -> "justify" } diff --git a/app/src/main/java/me/ash/reader/infrastructure/preference/ReadingTextAlignPreference.kt b/app/src/main/java/me/ash/reader/infrastructure/preference/ReadingTextAlignPreference.kt index c0d77ff20..39ba1e578 100644 --- a/app/src/main/java/me/ash/reader/infrastructure/preference/ReadingTextAlignPreference.kt +++ b/app/src/main/java/me/ash/reader/infrastructure/preference/ReadingTextAlignPreference.kt @@ -49,8 +49,8 @@ sealed class ReadingTextAlignPreference(val value: Int) : Preference() { fun toTextAlignCSS(): String = when (this) { - Left -> "left" - Right -> "right" + Start -> "left" + End -> "right" Center -> "center" Justify -> "justify" } diff --git a/app/src/main/java/me/ash/reader/infrastructure/preference/Settings.kt b/app/src/main/java/me/ash/reader/infrastructure/preference/Settings.kt index c7d8b8459..56de1ac3e 100644 --- a/app/src/main/java/me/ash/reader/infrastructure/preference/Settings.kt +++ b/app/src/main/java/me/ash/reader/infrastructure/preference/Settings.kt @@ -51,6 +51,7 @@ data class Settings( val flowArticleListReadIndicator: FlowArticleReadIndicatorPreference = FlowArticleReadIndicatorPreference.default, // Reading page + val readingRenderer: ReadingRendererPreference = ReadingRendererPreference.default, val readingTheme: ReadingThemePreference = ReadingThemePreference.default, val readingDarkTheme: ReadingDarkThemePreference = ReadingDarkThemePreference.default, val readingPageTonalElevation: ReadingPageTonalElevationPreference = ReadingPageTonalElevationPreference.default, @@ -140,6 +141,7 @@ fun SettingsProvider( LocalFlowArticleListReadIndicator provides settings.flowArticleListReadIndicator, // Reading page + LocalReadingRenderer provides settings.readingRenderer, LocalReadingTheme provides settings.readingTheme, LocalReadingDarkTheme provides settings.readingDarkTheme, LocalReadingPageTonalElevation provides settings.readingPageTonalElevation, diff --git a/app/src/main/java/me/ash/reader/ui/component/base/RYWebView.kt b/app/src/main/java/me/ash/reader/ui/component/base/RYWebView.kt index ed07fd3b5..48f86dc86 100644 --- a/app/src/main/java/me/ash/reader/ui/component/base/RYWebView.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/RYWebView.kt @@ -1,7 +1,9 @@ package me.ash.reader.ui.component.base import android.content.Context +import android.graphics.Color import android.net.http.SslError +import android.os.Build import android.util.Log import android.webkit.SslErrorHandler import android.webkit.WebResourceError @@ -25,7 +27,6 @@ import me.ash.reader.infrastructure.preference.LocalOpenLink import me.ash.reader.infrastructure.preference.LocalOpenLinkSpecificBrowser import me.ash.reader.infrastructure.preference.LocalReadingImageHorizontalPadding import me.ash.reader.infrastructure.preference.LocalReadingImageRoundedCorners -import me.ash.reader.infrastructure.preference.LocalReadingLetterSpacing import me.ash.reader.infrastructure.preference.LocalReadingPageTonalElevation import me.ash.reader.infrastructure.preference.LocalReadingSubheadAlign import me.ash.reader.infrastructure.preference.LocalReadingSubheadBold @@ -33,6 +34,7 @@ import me.ash.reader.infrastructure.preference.LocalReadingTextAlign import me.ash.reader.infrastructure.preference.LocalReadingTextBold import me.ash.reader.infrastructure.preference.LocalReadingTextFontSize import me.ash.reader.infrastructure.preference.LocalReadingTextHorizontalPadding +import me.ash.reader.infrastructure.preference.LocalReadingTextLetterSpacing import me.ash.reader.ui.ext.openURL import me.ash.reader.ui.ext.surfaceColorAtElevation import kotlin.math.absoluteValue @@ -60,7 +62,7 @@ fun RYWebView( val textBold: Boolean = LocalReadingTextBold.current.value val textAlign: String = LocalReadingTextAlign.current.toTextAlignCSS() val textFontSize: Int = LocalReadingTextFontSize.current - val textLetterSpacing: Double = LocalReadingLetterSpacing.current + val textLetterSpacing: Float = LocalReadingTextLetterSpacing.current val imageHorizontalPadding: Int = LocalReadingImageHorizontalPadding.current val textHorizontalPadding: Int = LocalReadingTextHorizontalPadding.current val imageShape: Int = LocalReadingImageRoundedCorners.current @@ -69,11 +71,11 @@ fun RYWebView( .surfaceColorAtElevation((tonalElevation.value + 6).dp).toArgb() val webViewClient by remember { mutableStateOf(object : WebViewClient() { - override fun shouldInterceptRequest( view: WebView?, - url: String?, + request: WebResourceRequest?, ): WebResourceResponse? { + val url = request?.url?.toString() if (url != null && url.contains(INJECTION_TOKEN)) { try { val assetPath = url.substring( @@ -89,7 +91,7 @@ fun RYWebView( Log.e("RLog", "WebView shouldInterceptRequest: $e") } } - return super.shouldInterceptRequest(view, url); + return super.shouldInterceptRequest(view, request); } override fun onPageFinished(view: WebView?, url: String?) { @@ -135,11 +137,22 @@ fun RYWebView( } val webView by remember(backgroundColor) { - mutableStateOf(WebView(context).apply { + mutableStateOf(WebView(context).run { this.webViewClient = webViewClient - setBackgroundColor(backgroundColor) + setBackgroundColor(Color.TRANSPARENT) + scrollBarSize = 0 isHorizontalScrollBarEnabled = false isVerticalScrollBarEnabled = true + with(this.settings) { + domStorageEnabled = true + javaScriptEnabled = true // Do we need JS? + setSupportZoom(false) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + isAlgorithmicDarkeningAllowed = true + } + } + // this.loadUrl(url) + this }) } @@ -147,12 +160,18 @@ fun RYWebView( modifier = modifier, factory = { webView }, update = { + // if (isRefreshing) { + // it.reload() + // setRefreshed() + // } it.apply { Log.i("RLog", "maxWidth: ${maxWidth}") Log.i("RLog", "readingFont: ${context.filesDir.absolutePath}") Log.i("RLog", "CustomWebView: ${content}") settings.javaScriptEnabled = true settings.defaultFontSize = textFontSize + setBackgroundColor(Color.TRANSPARENT) + scrollBarSize = 0 loadDataWithBaseURL( null, getStyle( @@ -196,7 +215,7 @@ fun getStyle( textBold: Boolean, textAlign: String, textFontSize: Int, - textLetterSpacing: Double, + textLetterSpacing: Float, imageHorizontalPadding: Int, textHorizontalPadding: Int, imageShape: Int, diff --git a/app/src/main/java/me/ash/reader/ui/ext/DataStoreExt.kt b/app/src/main/java/me/ash/reader/ui/ext/DataStoreExt.kt index 4205d288a..dddadc5c3 100644 --- a/app/src/main/java/me/ash/reader/ui/ext/DataStoreExt.kt +++ b/app/src/main/java/me/ash/reader/ui/ext/DataStoreExt.kt @@ -134,6 +134,7 @@ data class DataStoreKey( const val flowArticleListReadIndicator = "flowArticleListReadIndicator" // Reading page + const val readingRenderer = "readingRender" const val readingDarkTheme = "readingDarkTheme" const val readingPageTonalElevation = "readingPageTonalElevation" const val readingTextFontSize = "readingTextFontSize" @@ -207,6 +208,7 @@ data class DataStoreKey( flowArticleListTonalElevation to DataStoreKey(intPreferencesKey(flowArticleListTonalElevation), Int::class.java), flowArticleListReadIndicator to DataStoreKey(booleanPreferencesKey(flowArticleListReadIndicator), Boolean::class.java), // Reading page + readingRenderer to DataStoreKey(intPreferencesKey(readingRenderer), Int::class.java), readingDarkTheme to DataStoreKey(intPreferencesKey(readingDarkTheme), Int::class.java), readingPageTonalElevation to DataStoreKey(intPreferencesKey(readingPageTonalElevation), Int::class.java), readingTextFontSize to DataStoreKey(intPreferencesKey(readingTextFontSize), Int::class.java), diff --git a/app/src/main/java/me/ash/reader/ui/page/home/reading/Content.kt b/app/src/main/java/me/ash/reader/ui/page/home/reading/Content.kt index ee10dd0e5..101e0858e 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/reading/Content.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/reading/Content.kt @@ -1,44 +1,34 @@ package me.ash.reader.ui.page.home.reading -import android.util.Log -import androidx.compose.animation.AnimatedContent -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.core.tween -import androidx.compose.animation.expandVertically -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut -import androidx.compose.animation.scaleIn -import androidx.compose.animation.shrinkVertically -import androidx.compose.animation.togetherWith -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.navigationBars +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.windowInsetsBottomHeight import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.text.selection.DisableSelection import androidx.compose.foundation.text.selection.SelectionContainer -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.KeyboardArrowDown -import androidx.compose.material.icons.outlined.KeyboardArrowUp import androidx.compose.material3.CircularProgressIndicator -import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp -import androidx.compose.ui.zIndex import me.ash.reader.infrastructure.preference.LocalOpenLink import me.ash.reader.infrastructure.preference.LocalOpenLinkSpecificBrowser +import me.ash.reader.infrastructure.preference.LocalReadingRenderer import me.ash.reader.infrastructure.preference.LocalReadingSubheadUpperCase +import me.ash.reader.infrastructure.preference.ReadingRendererPreference +import me.ash.reader.ui.component.base.RYWebView import me.ash.reader.ui.component.reader.Reader import me.ash.reader.ui.ext.drawVerticalScrollbar import me.ash.reader.ui.ext.openURL -import me.ash.reader.ui.ext.pagerAnimate -import java.util.* -import kotlin.math.abs +import java.util.Date @Composable fun Content( @@ -57,6 +47,7 @@ fun Content( val subheadUpperCase = LocalReadingSubheadUpperCase.current val openLink = LocalOpenLink.current val openLinkSpecificBrowser = LocalOpenLinkSpecificBrowser.current + val renderer = LocalReadingRenderer.current if (isLoading) { Column { @@ -95,16 +86,32 @@ fun Content( } } } - Reader( - context = context, - subheadUpperCase = subheadUpperCase.value, - link = link ?: "", - content = content, - onImageClick = onImageClick, - onLinkClick = { - context.openURL(it, openLink, openLinkSpecificBrowser) + + when (renderer) { + ReadingRendererPreference.WebView -> { + item { + RYWebView( + content = content, + onReceivedError = { + // throw RuntimeException("errorCode: ${it?.errorCode}, description: ${it?.description}") + } + ) + } } - ) + + ReadingRendererPreference.NativeComponent -> { + Reader( + context = context, + subheadUpperCase = subheadUpperCase.value, + link = link ?: "", + content = content, + onImageClick = onImageClick, + onLinkClick = { + context.openURL(it, openLink, openLinkSpecificBrowser) + } + ) + } + } item { Spacer(modifier = Modifier.height(128.dp)) diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/reading/ReadingStylePage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/reading/ReadingStylePage.kt index cf687784b..93518e10d 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/color/reading/ReadingStylePage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/reading/ReadingStylePage.kt @@ -23,7 +23,6 @@ import androidx.compose.material.icons.automirrored.rounded.ArrowBack import androidx.compose.material.icons.automirrored.rounded.Segment import androidx.compose.material.icons.outlined.Image import androidx.compose.material.icons.outlined.Movie -import androidx.compose.material.icons.rounded.Segment import androidx.compose.material.icons.rounded.Title import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable @@ -46,9 +45,11 @@ import me.ash.reader.infrastructure.preference.LocalReadingAutoHideToolbar import me.ash.reader.infrastructure.preference.LocalReadingDarkTheme import me.ash.reader.infrastructure.preference.LocalReadingFonts import me.ash.reader.infrastructure.preference.LocalReadingPageTonalElevation +import me.ash.reader.infrastructure.preference.LocalReadingRenderer import me.ash.reader.infrastructure.preference.LocalReadingTheme import me.ash.reader.infrastructure.preference.ReadingFontsPreference import me.ash.reader.infrastructure.preference.ReadingPageTonalElevationPreference +import me.ash.reader.infrastructure.preference.ReadingRendererPreference import me.ash.reader.infrastructure.preference.ReadingThemePreference import me.ash.reader.infrastructure.preference.not import me.ash.reader.ui.component.ReadingThemePrev @@ -80,9 +81,11 @@ fun ReadingStylePage( val fonts = LocalReadingFonts.current val autoHideToolbar = LocalReadingAutoHideToolbar.current val pullToSwitchArticle = LocalPullToSwitchArticle.current + val renderer = LocalReadingRenderer.current var tonalElevationDialogVisible by remember { mutableStateOf(false) } + var rendererDialogVisible by remember { mutableStateOf(false) } var fontsDialogVisible by remember { mutableStateOf(false) } val launcher = rememberLauncherForActivityResult(ActivityResultContracts.OpenDocument()) { uri -> @@ -152,6 +155,11 @@ fun ReadingStylePage( modifier = Modifier.padding(horizontal = 24.dp), text = stringResource(R.string.general) ) + SettingItem( + title = stringResource(R.string.content_renderer), + desc = renderer.toDesc(context), + onClick = { rendererDialogVisible = true }, + ) {} SettingItem( title = stringResource(R.string.reading_fonts), desc = fonts.toDesc(context), @@ -291,6 +299,21 @@ fun ReadingStylePage( tonalElevationDialogVisible = false } + RadioDialog( + visible = rendererDialogVisible, + title = stringResource(R.string.content_renderer), + options = ReadingRendererPreference.values.map { + RadioDialogOption( + text = it.toDesc(context), + selected = it == renderer, + ) { + it.put(context, scope) + } + } + ) { + rendererDialogVisible = false + } + RadioDialog( visible = fontsDialogVisible, title = stringResource(R.string.reading_fonts), diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e20b70c5d..0af108c3a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -445,4 +445,7 @@ Import from JSON Export as JSON This file may not be a valid JSON file. Importing it could potentially corrupt the app and result in the loss of current preferences. Are you sure you want to proceed? + Webview + Native Component + Content renderer