-
-
Notifications
You must be signed in to change notification settings - Fork 5
feat(android): add WebView upgrade plugin #115
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,4 +33,5 @@ src-tauri/gen/* | |
|
|
||
| *.node | ||
|
|
||
| content | ||
| content | ||
| debugging | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,4 @@ | ||
| #[cfg(not(target_os = "android"))] | ||
| pub mod paths; | ||
| #[cfg(not(target_os = "android"))] | ||
| pub mod time; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| /android/.gradle | ||
| /android/build | ||
| /android/.tauri | ||
| /android/local.properties | ||
| /android/src/main/java/app/risuko/webview_upgrade/WebViewUpgradeConfig.kt |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| [package] | ||
| name = "risuko-webview-upgrade" | ||
| description = "Swap the in-process Android WebView kernel to a newer installed Chrome / Google WebView when the system WebView is too old to render Risuko." | ||
| version.workspace = true | ||
| authors.workspace = true | ||
| edition.workspace = true | ||
| rust-version.workspace = true | ||
| # `links` is the hook that makes the host app's `tauri-build` discover this | ||
| # plugin's Android library and wire it into the generated Gradle project. It | ||
| # must be unique across the whole dependency graph. | ||
| links = "risuko-webview-upgrade" | ||
|
|
||
| [dependencies] | ||
| tauri = { version = "2" } | ||
|
|
||
| [build-dependencies] | ||
| tauri-plugin = { version = "2", features = ["build"] } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,132 @@ | ||
| import groovy.json.JsonSlurper | ||
| import org.jetbrains.kotlin.gradle.tasks.KotlinCompile | ||
|
|
||
| plugins { | ||
| id("com.android.library") | ||
| id("org.jetbrains.kotlin.android") | ||
| } | ||
|
|
||
| android { | ||
| namespace = "app.risuko.webview_upgrade" | ||
| compileSdk = 36 | ||
|
|
||
| defaultConfig { | ||
| minSdk = 24 | ||
| consumerProguardFiles("consumer-rules.pro") | ||
| } | ||
|
|
||
| buildTypes { | ||
| release { | ||
| isMinifyEnabled = false | ||
| proguardFiles( | ||
| getDefaultProguardFile("proguard-android-optimize.txt"), | ||
| "proguard-rules.pro" | ||
| ) | ||
| } | ||
| } | ||
| compileOptions { | ||
| sourceCompatibility = JavaVersion.VERSION_1_8 | ||
| targetCompatibility = JavaVersion.VERSION_1_8 | ||
| } | ||
| kotlinOptions { | ||
| jvmTarget = "1.8" | ||
| } | ||
|
|
||
| sourceSets["main"].kotlin.srcDir( | ||
| layout.buildDirectory.dir("generated/source/webview-upgrade") | ||
| ) | ||
| } | ||
|
|
||
| dependencies { | ||
| implementation(project(":tauri-android")) | ||
| } | ||
|
|
||
| val defaultMinUpgradeMajor = 106 | ||
| val defaultMinSupportedMajor = 75 | ||
|
|
||
| @Suppress("UNCHECKED_CAST") | ||
| fun parsePluginBlock(block: Map<String, Any?>?): Pair<Int, Int> { | ||
| if (block == null) return defaultMinUpgradeMajor to defaultMinSupportedMajor | ||
| val upgrade = (block["minUpgradeMajor"] as? Number)?.toInt() ?: defaultMinUpgradeMajor | ||
|
cubic-dev-ai[bot] marked this conversation as resolved.
|
||
| val supported = (block["minSupportedMajor"] as? Number)?.toInt() ?: defaultMinSupportedMajor | ||
| require(upgrade > 0) { "minUpgradeMajor must be > 0, got $upgrade" } | ||
| require(supported > 0) { "minSupportedMajor must be > 0, got $supported" } | ||
| require(supported <= upgrade) { "minSupportedMajor ($supported) must be <= minUpgradeMajor ($upgrade)" } | ||
| return upgrade to supported | ||
| } | ||
|
Comment on lines
+48
to
+56
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Fail fast on invalid threshold config instead of silently defaulting. Line 50-Line 52 and Line 95 currently coerce malformed/invalid explicit config into defaults, which can silently ship wrong swap thresholds. Treat malformed env/plugin values as build errors, and validate bounds ( Suggested fix+import org.gradle.api.GradleException
import groovy.json.JsonSlurper
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
@@
`@Suppress`("UNCHECKED_CAST")
fun parsePluginBlock(block: Map<String, Any?>?): Pair<Int, Int> {
if (block == null) return defaultMinUpgradeMajor to defaultMinSupportedMajor
- val upgrade = (block["minUpgradeMajor"] as? Number)?.toInt() ?: defaultMinUpgradeMajor
- val supported = (block["minSupportedMajor"] as? Number)?.toInt() ?: defaultMinSupportedMajor
+ val upgrade = (block["minUpgradeMajor"] as? Number)?.toInt() ?: defaultMinUpgradeMajor
+ val supported = (block["minSupportedMajor"] as? Number)?.toInt() ?: defaultMinSupportedMajor
+ if (upgrade <= 0 || supported <= 0) {
+ throw GradleException("webview-upgrade config values must be positive: minUpgradeMajor=$upgrade, minSupportedMajor=$supported")
+ }
+ if (supported > upgrade) {
+ throw GradleException("webview-upgrade config is invalid: minSupportedMajor ($supported) must be <= minUpgradeMajor ($upgrade)")
+ }
return upgrade to supported
}
@@
val (minUpgrade, minSupported, source) = when {
- !envJson.isNullOrBlank() ->
- readFromEnv(envJson)?.let { Triple(it.first, it.second, "TAURI_WEBVIEW_UPGRADE_PLUGIN_CONFIG env var") }
- ?: Triple(defaultMinUpgradeMajor, defaultMinSupportedMajor, "defaults (env var unparseable)")
+ !envJson.isNullOrBlank() -> {
+ val parsed = readFromEnv(envJson)
+ ?: throw GradleException("TAURI_WEBVIEW_UPGRADE_PLUGIN_CONFIG is not valid JSON object for webview-upgrade config")
+ Triple(parsed.first, parsed.second, "TAURI_WEBVIEW_UPGRADE_PLUGIN_CONFIG env var")
+ }
tauriConfFile != null ->
readFromTauriConf(tauriConfFile)?.let { Triple(it.first, it.second, "tauri.conf.json at $tauriConfFile") }
?: Triple(defaultMinUpgradeMajor, defaultMinSupportedMajor, "defaults (tauri.conf.json had no block)")Also applies to: 92-99 🤖 Prompt for AI Agents |
||
|
|
||
| @Suppress("UNCHECKED_CAST") | ||
| fun readFromEnv(jsonText: String?): Pair<Int, Int>? { | ||
| if (jsonText.isNullOrBlank()) return null | ||
| return try { | ||
| val parsed = JsonSlurper().parseText(jsonText) as? Map<String, Any?> ?: return null | ||
| parsePluginBlock(parsed) | ||
| } catch (e: Exception) { | ||
| logger.warn("TAURI_WEBVIEW_UPGRADE_PLUGIN_CONFIG parse error: ${e.message}; using defaults") | ||
| null | ||
| } | ||
| } | ||
|
|
||
| @Suppress("UNCHECKED_CAST") | ||
| fun readFromTauriConf(file: File): Pair<Int, Int>? { | ||
| val root = JsonSlurper().parse(file) as? Map<String, Any?> ?: return null | ||
| val plugins = root["plugins"] as? Map<String, Any?> ?: return null | ||
| val block = plugins["webview-upgrade"] as? Map<String, Any?> ?: return null | ||
| return parsePluginBlock(block) | ||
| } | ||
|
|
||
| fun findTauriConf(start: File): File? { | ||
| var current: File? = start | ||
| while (current != null) { | ||
| val candidate = File(current, "tauri.conf.json") | ||
| if (candidate.exists()) return candidate | ||
| current = current.parentFile | ||
| } | ||
| return null | ||
| } | ||
|
|
||
| val generatedConfigDir = layout.buildDirectory.dir("generated/source/webview-upgrade") | ||
|
|
||
| val generateWebViewUpgradeConfig = tasks.register("generateWebViewUpgradeConfig") { | ||
| val envJson = System.getenv("TAURI_WEBVIEW_UPGRADE_PLUGIN_CONFIG") | ||
| val tauriConfFile = findTauriConf(projectDir) | ||
| if (envJson.isNullOrBlank() && tauriConfFile != null) { | ||
| inputs.file(tauriConfFile) | ||
| } | ||
| inputs.property("envJson", envJson ?: "") | ||
| outputs.dir(generatedConfigDir) | ||
|
|
||
| doLast { | ||
| val (minUpgrade, minSupported, source) = when { | ||
| !envJson.isNullOrBlank() -> | ||
| readFromEnv(envJson)?.let { Triple(it.first, it.second, "TAURI_WEBVIEW_UPGRADE_PLUGIN_CONFIG env var") } | ||
| ?: Triple(defaultMinUpgradeMajor, defaultMinSupportedMajor, "defaults (env var unparseable)") | ||
| tauriConfFile != null -> | ||
| readFromTauriConf(tauriConfFile)?.let { Triple(it.first, it.second, "tauri.conf.json") } | ||
| ?: Triple(defaultMinUpgradeMajor, defaultMinSupportedMajor, "defaults (tauri.conf.json had no block)") | ||
| else -> Triple(defaultMinUpgradeMajor, defaultMinSupportedMajor, "defaults (no config found)") | ||
| } | ||
| val outDir = generatedConfigDir.get().asFile | ||
| val pkgDir = File(outDir, "app/risuko/webview_upgrade") | ||
| pkgDir.mkdirs() | ||
| File(pkgDir, "WebViewUpgradeConfig.kt").writeText( | ||
| """ | ||
| // Generated by build.gradle.kts at build time. Do not edit by hand. | ||
| // Source: $source | ||
| package app.risuko.webview_upgrade | ||
|
|
||
| internal object WebViewUpgradeConfig { | ||
| const val MIN_UPGRADE_MAJOR: Int = $minUpgrade | ||
| const val MIN_SUPPORTED_MAJOR: Int = $minSupported | ||
| } | ||
| """.trimIndent() + "\n" | ||
| ) | ||
| logger.lifecycle( | ||
| "WebViewUpgradeConfig: minUpgradeMajor=$minUpgrade, minSupportedMajor=$minSupported (from $source)" | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| tasks.withType<KotlinCompile>().configureEach { | ||
| dependsOn(generateWebViewUpgradeConfig) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| # Applied automatically to the host app's R8 step | ||
| -keep class app.risuko.webview_upgrade.** { *; } | ||
| -keepattributes RuntimeVisibleAnnotations,RuntimeVisibleParameterAnnotations,AnnotationDefault |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| -keep class app.risuko.webview_upgrade.** { *; } | ||
| -keepattributes RuntimeVisibleAnnotations,RuntimeVisibleParameterAnnotations,AnnotationDefault |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P3: Missing
[lib]section - all other workspace library crates explicitly declare one.Prompt for AI agents