Fix(WebView): Support check WebView version

This commit is contained in:
2024-11-08 15:39:29 +08:00
parent 3f6fcc0106
commit de28107287
13 changed files with 113 additions and 6 deletions

View File

@@ -192,6 +192,7 @@ dependencies {
implementation(libs.lifecycle.viewmodel.compose) implementation(libs.lifecycle.viewmodel.compose)
implementation(libs.androidx.core.splashscreen) implementation(libs.androidx.core.splashscreen)
implementation(libs.hilt.android) implementation(libs.hilt.android)
implementation(libs.androidx.web.kit)
ksp(libs.hilt.android) ksp(libs.hilt.android)
ksp(libs.hilt.compiler) ksp(libs.hilt.compiler)
implementation(libs.coil.kt) implementation(libs.coil.kt)

View File

@@ -8,6 +8,9 @@ import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -16,11 +19,14 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
import androidx.webkit.WebViewCompat
import dagger.hilt.EntryPoint import dagger.hilt.EntryPoint
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
@@ -86,6 +92,9 @@ class MainActivity : ComponentActivity() {
LaunchedEffect(locale) { LaunchedEffect(locale) {
LocaleUtils.switchLocale(this@MainActivity, locale) LocaleUtils.switchLocale(this@MainActivity, locale)
} }
UseIsFirstLaunch(viewModel) {
CheckWebView(it)
}
} }
val darkTheme = shouldUseDarkTheme(uiState) val darkTheme = shouldUseDarkTheme(uiState)
@@ -154,6 +163,57 @@ class MainActivity : ComponentActivity() {
} }
} }
@Composable
private fun UseIsFirstLaunch(viewModel: MainActivityViewModel, callback: @Composable (ondDismiss: () -> Unit) -> Unit) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
if (!whatIsFirstLaunch(uiState)) {
return
}
callback {
viewModel.updateIsNotFirstLaunch()
}
}
@Composable
private fun whatIsFirstLaunch(uiState: MainActivityUiState): Boolean =
when (uiState) {
MainActivityUiState.Loading -> false
is MainActivityUiState.Success ->
!uiState.userData.isNotFirstLaunch
}
@Composable
private fun CheckWebView(onDismiss: () -> Unit) {
val versionName = WebViewCompat.getCurrentWebViewPackage(LocalContext.current)?.versionName
if (versionName == null) {
AlertDialog(
onDismissRequest = onDismiss,
title = {Text(text = stringResource(R.string.core_web_view_warning))},
text = { Text(text = stringResource(R.string.core_cannot_load_web_view_version)) },
confirmButton = {
TextButton(onClick = onDismiss) {
Text(text = stringResource(R.string.core_dismiss))
}
}
)
return
}
if (versionName.split(".").first().toInt() < 80) {
AlertDialog(
onDismissRequest = onDismiss,
title = {Text(text = stringResource(R.string.core_web_view_warning))},
text = { Text(text = stringResource(R.string.core_web_view_version_too_low)) },
confirmButton = {
TextButton(onClick = onDismiss) {
Text(text = stringResource(R.string.core_dismiss))
}
}
)
return
}
}
@Composable @Composable
private fun shouldUseDarkTheme(uiState: MainActivityUiState): Boolean = private fun shouldUseDarkTheme(uiState: MainActivityUiState): Boolean =
when (uiState) { when (uiState) {

View File

@@ -7,6 +7,7 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import top.fatweb.oxygen.toolbox.model.userdata.UserData import top.fatweb.oxygen.toolbox.model.userdata.UserData
import top.fatweb.oxygen.toolbox.repository.userdata.UserDataRepository import top.fatweb.oxygen.toolbox.repository.userdata.UserDataRepository
import javax.inject.Inject import javax.inject.Inject
@@ -14,7 +15,7 @@ import kotlin.time.Duration.Companion.seconds
@HiltViewModel @HiltViewModel
class MainActivityViewModel @Inject constructor( class MainActivityViewModel @Inject constructor(
userDataRepository: UserDataRepository private val userDataRepository: UserDataRepository
) : ViewModel() { ) : ViewModel() {
val uiState: StateFlow<MainActivityUiState> = userDataRepository.userData.map { val uiState: StateFlow<MainActivityUiState> = userDataRepository.userData.map {
MainActivityUiState.Success(it) MainActivityUiState.Success(it)
@@ -23,6 +24,12 @@ class MainActivityViewModel @Inject constructor(
initialValue = MainActivityUiState.Loading, initialValue = MainActivityUiState.Loading,
started = SharingStarted.WhileSubscribed(stopTimeoutMillis = 5.seconds.inWholeMilliseconds) started = SharingStarted.WhileSubscribed(stopTimeoutMillis = 5.seconds.inWholeMilliseconds)
) )
fun updateIsNotFirstLaunch() {
viewModelScope.launch {
userDataRepository.updateIsNotFirstLaunch()
}
}
} }
sealed interface MainActivityUiState { sealed interface MainActivityUiState {

View File

@@ -69,7 +69,8 @@ class OxygenPreferencesDataSource @Inject constructor(
DarkThemeConfigProto.DARK_THEME_CONFIG_DARK DarkThemeConfigProto.DARK_THEME_CONFIG_DARK
-> DarkThemeConfig.Dark -> DarkThemeConfig.Dark
}, },
useDynamicColor = it.useDynamicColor useDynamicColor = it.useDynamicColor,
isNotFirstLaunch = it.isNotFirstLaunch
) )
} }
@@ -126,4 +127,12 @@ class OxygenPreferencesDataSource @Inject constructor(
} }
} }
} }
suspend fun updateIsNotFirstLaunch() {
userPreferences.updateData {
it.copy {
this.isNotFirstLaunch = true
}
}
}
} }

View File

@@ -5,5 +5,6 @@ data class UserData(
val launchPageConfig: LaunchPageConfig, val launchPageConfig: LaunchPageConfig,
val themeBrandConfig: ThemeBrandConfig, val themeBrandConfig: ThemeBrandConfig,
val darkThemeConfig: DarkThemeConfig, val darkThemeConfig: DarkThemeConfig,
val useDynamicColor: Boolean val useDynamicColor: Boolean,
val isNotFirstLaunch: Boolean
) )

View File

@@ -19,4 +19,6 @@ interface UserDataRepository {
suspend fun setDarkThemeConfig(darkThemeConfig: DarkThemeConfig) suspend fun setDarkThemeConfig(darkThemeConfig: DarkThemeConfig)
suspend fun setUseDynamicColor(useDynamicColor: Boolean) suspend fun setUseDynamicColor(useDynamicColor: Boolean)
suspend fun updateIsNotFirstLaunch()
} }

View File

@@ -35,4 +35,8 @@ internal class LocalUserDataRepository @Inject constructor(
override suspend fun setUseDynamicColor(useDynamicColor: Boolean) { override suspend fun setUseDynamicColor(useDynamicColor: Boolean) {
oxygenPreferencesDataSource.setUseDynamicColor(useDynamicColor) oxygenPreferencesDataSource.setUseDynamicColor(useDynamicColor)
} }
override suspend fun updateIsNotFirstLaunch() {
oxygenPreferencesDataSource.updateIsNotFirstLaunch()
}
} }

View File

@@ -43,6 +43,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.webkit.WebViewCompat
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import top.fatweb.oxygen.toolbox.R import top.fatweb.oxygen.toolbox.R
import top.fatweb.oxygen.toolbox.icon.OxygenIcons import top.fatweb.oxygen.toolbox.icon.OxygenIcons
@@ -126,6 +127,7 @@ internal fun AboutScreen(
private fun AboutAppInfo( private fun AboutAppInfo(
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val context = LocalContext.current
val logo = AnimatedImageVector.animatedVectorResource(R.drawable.ic_launcher) val logo = AnimatedImageVector.animatedVectorResource(R.drawable.ic_launcher)
var atEnd by remember { mutableStateOf(false) } var atEnd by remember { mutableStateOf(false) }
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
@@ -169,6 +171,15 @@ private fun AboutAppInfo(
) )
})" })"
) )
Text(
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.outline,
text = "WebView: ${
WebViewCompat.getCurrentWebViewPackage(context)?.versionName ?: stringResource(
R.string.core_unknown
)
}"
)
} }
} }

View File

@@ -271,7 +271,8 @@ private fun SettingDialogPreview() {
launchPageConfig = LaunchPageConfig.Tools, launchPageConfig = LaunchPageConfig.Tools,
themeBrandConfig = ThemeBrandConfig.Default, themeBrandConfig = ThemeBrandConfig.Default,
darkThemeConfig = DarkThemeConfig.FollowSystem, darkThemeConfig = DarkThemeConfig.FollowSystem,
useDynamicColor = true useDynamicColor = true,
isNotFirstLaunch = true
) )
), ),
onChangeLanguageConfig = {}, onChangeLanguageConfig = {},

View File

@@ -14,4 +14,5 @@ message UserPreferences {
ThemeBrandConfigProto theme_brand_config = 3; ThemeBrandConfigProto theme_brand_config = 3;
DarkThemeConfigProto dark_theme_config = 4; DarkThemeConfigProto dark_theme_config = 4;
bool use_dynamic_color = 5; bool use_dynamic_color = 5;
bool is_not_first_launch = 6;
} }

View File

@@ -26,6 +26,7 @@
<string name="core_unstar">取消收藏</string> <string name="core_unstar">取消收藏</string>
<string name="core_cancel">取消</string> <string name="core_cancel">取消</string>
<string name="core_undo">撤消</string> <string name="core_undo">撤消</string>
<string name="core_dismiss">忽略</string>
<string name="core_nothing">空空如也</string> <string name="core_nothing">空空如也</string>
<string name="core_nothing_found">未找到相关内容</string> <string name="core_nothing_found">未找到相关内容</string>
<string name="core_file_select_one">选择一个文件</string> <string name="core_file_select_one">选择一个文件</string>
@@ -33,6 +34,9 @@
<string name="core_file_select_one_text">选择一个文本文件</string> <string name="core_file_select_one_text">选择一个文本文件</string>
<string name="core_file_select_multi_text">选择文本文件</string> <string name="core_file_select_multi_text">选择文本文件</string>
<string name="core_can_only_download_http_https">只能下载 HTTP/HTTPS URI%1$s</string> <string name="core_can_only_download_http_https">只能下载 HTTP/HTTPS URI%1$s</string>
<string name="core_web_view_warning">⚠️ WebView 警告 ⚠️</string>
<string name="core_cannot_load_web_view_version">无法获取 WebView 版本信息WebView 版本过低可能会导致工具使用异常,请知悉。</string>
<string name="core_web_view_version_too_low">当前 WebView 版本过低,可能会导致工具使用异常,请知悉。</string>
<string name="feature_store_title">商店</string> <string name="feature_store_title">商店</string>
<string name="feature_store_retry">重试</string> <string name="feature_store_retry">重试</string>

View File

@@ -25,6 +25,7 @@
<string name="core_unstar">Unstar</string> <string name="core_unstar">Unstar</string>
<string name="core_cancel">Cancel</string> <string name="core_cancel">Cancel</string>
<string name="core_undo">Undo</string> <string name="core_undo">Undo</string>
<string name="core_dismiss">Dismiss</string>
<string name="core_nothing">Nothing</string> <string name="core_nothing">Nothing</string>
<string name="core_nothing_found">Nothing found</string> <string name="core_nothing_found">Nothing found</string>
<string name="core_file_select_one">Select a file</string> <string name="core_file_select_one">Select a file</string>
@@ -32,6 +33,9 @@
<string name="core_file_select_one_text">Select a text file</string> <string name="core_file_select_one_text">Select a text file</string>
<string name="core_file_select_multi_text">Select text files</string> <string name="core_file_select_multi_text">Select text files</string>
<string name="core_can_only_download_http_https">Can only download HTTP/HTTPS URIs: %1$s</string> <string name="core_can_only_download_http_https">Can only download HTTP/HTTPS URIs: %1$s</string>
<string name="core_web_view_warning">⚠️ WebView warning ⚠️</string>
<string name="core_cannot_load_web_view_version">The WebView version information cannot be obtained. If the WebView version is too low, it may cause abnormal tool usage. Please be informed.</string>
<string name="core_web_view_version_too_low">The current WebView version is too low, which may cause abnormal use of the tool. Please be informed.</string>
<string name="feature_store_title">Store</string> <string name="feature_store_title">Store</string>
<string name="feature_store_retry">try again</string> <string name="feature_store_retry">try again</string>

View File

@@ -1,6 +1,6 @@
[versions] [versions]
agp = "8.7.2" agp = "8.7.2"
kotlin = "2.0.20" kotlin = "2.0.21"
ksp = "2.0.20-1.0.25" ksp = "2.0.20-1.0.25"
aboutlibraries = "11.2.3" aboutlibraries = "11.2.3"
protobufPlugin = "0.9.4" protobufPlugin = "0.9.4"
@@ -8,7 +8,7 @@ kotlinxSerialization = "2.0.20"
secrets = "2.0.1" secrets = "2.0.1"
paging = "3.3.2" paging = "3.3.2"
desugarJdkLibs = "2.1.2" desugarJdkLibs = "2.1.3"
composeBom = "2024.10.01" composeBom = "2024.10.01"
junit = "4.13.2" junit = "4.13.2"
coreKtx = "1.15.0" coreKtx = "1.15.0"
@@ -19,6 +19,7 @@ activityCompose = "1.9.3"
appcompat = "1.7.0" appcompat = "1.7.0"
androidxLifecycle = "2.8.7" androidxLifecycle = "2.8.7"
androidxCoreSplashscreen = "1.0.1" androidxCoreSplashscreen = "1.0.1"
androidxWebKit = "1.12.1"
hilt = "2.52" hilt = "2.52"
coil = "2.7.0" coil = "2.7.0"
kotlinxDatetime = "0.6.1" kotlinxDatetime = "0.6.1"
@@ -79,6 +80,7 @@ lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-ru
lifecycle-runtime-testing = { group = "androidx.lifecycle", name = "lifecycle-runtime-testing", version.ref = "androidxLifecycle" } lifecycle-runtime-testing = { group = "androidx.lifecycle", name = "lifecycle-runtime-testing", version.ref = "androidxLifecycle" }
lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "androidxLifecycle" } lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "androidxLifecycle" }
androidx-core-splashscreen = { group = "androidx.core", name = "core-splashscreen", version.ref = "androidxCoreSplashscreen" } androidx-core-splashscreen = { group = "androidx.core", name = "core-splashscreen", version.ref = "androidxCoreSplashscreen" }
androidx-web-kit = { group = "androidx.webkit", name = "webkit", version.ref = "androidxWebKit" }
dagger-compiler = { group = "com.google.dagger", name = "dagger-compiler", version.ref = "hilt" } dagger-compiler = { group = "com.google.dagger", name = "dagger-compiler", version.ref = "hilt" }
hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" } hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }
hilt-android-testing = { group = "com.google.dagger", name = "hilt-android-testing", version.ref = "hilt" } hilt-android-testing = { group = "com.google.dagger", name = "hilt-android-testing", version.ref = "hilt" }