Skip to content

Commit 9402ca5

Browse files
committed
fix:add test case for huawei and implement the correct fix
1 parent 08c6f92 commit 9402ca5

6 files changed

Lines changed: 59 additions & 63 deletions

File tree

AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,14 +193,14 @@ import com.ichi2.utils.Permissions
193193
import com.ichi2.utils.VersionUtils
194194
import com.ichi2.utils.cancelable
195195
import com.ichi2.utils.checkBoxPrompt
196-
import com.ichi2.utils.checkWebviewVersion
197196
import com.ichi2.utils.configureView
198197
import com.ichi2.utils.customView
199198
import com.ichi2.utils.dp
200199
import com.ichi2.utils.message
201200
import com.ichi2.utils.negativeButton
202201
import com.ichi2.utils.positiveButton
203202
import com.ichi2.utils.show
203+
import com.ichi2.utils.showDialogIfWebViewOutdated
204204
import com.ichi2.utils.title
205205
import kotlinx.coroutines.CancellationException
206206
import kotlinx.coroutines.Dispatchers
@@ -598,7 +598,7 @@ open class DeckPicker :
598598

599599
shortAnimDuration = resources.getInteger(android.R.integer.config_shortAnimTime)
600600

601-
checkWebviewVersion(this)
601+
with(this) { showDialogIfWebViewOutdated() }
602602

603603
setFragmentResultListener(REQUEST_KEY) { _, bundle ->
604604
when (CustomStudyAction.fromBundle(bundle)) {

AnkiDroid/src/main/java/com/ichi2/anki/pages/AnkiPackageImporterFragment.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,15 @@ import com.ichi2.anki.CollectionManager
2525
import com.ichi2.anki.R
2626
import com.ichi2.anki.SingleFragmentActivity
2727
import com.ichi2.anki.hideShowButtonCss
28+
import com.ichi2.utils.OLDEST_WORKING_WEBVIEW_VERSION
2829

2930
class AnkiPackageImporterFragment : PageFragment() {
3031
override val pagePath: String by lazy {
3132
val filePath = requireArguments().getString(KEY_FILE_PATH)
3233
"import-anki-package$filePath"
3334
}
3435

35-
override fun requiresModernWebView() = true
36+
override val minimumWebViewVersion: Int = OLDEST_WORKING_WEBVIEW_VERSION
3637

3738
override fun onCreateWebViewClient(savedInstanceState: Bundle?): PageWebViewClient {
3839
// the back callback is only enabled when import is running and showing progress

AnkiDroid/src/main/java/com/ichi2/anki/pages/CsvImporter.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import com.ichi2.anki.CollectionManager
2727
import com.ichi2.anki.R
2828
import com.ichi2.anki.SingleFragmentActivity
2929
import com.ichi2.anki.hideShowButtonCss
30+
import com.ichi2.utils.OLDEST_WORKING_WEBVIEW_VERSION
3031

3132
/**
3233
* Anki page used to import text/csv files
@@ -37,7 +38,7 @@ class CsvImporter : PageFragment() {
3738
"import-csv$filePath"
3839
}
3940

40-
override fun requiresModernWebView() = true
41+
override val minimumWebViewVersion: Int = OLDEST_WORKING_WEBVIEW_VERSION
4142

4243
override fun onCreateWebViewClient(savedInstanceState: Bundle?): PageWebViewClient {
4344
// the back callback is only enabled when import is running and showing progress

AnkiDroid/src/main/java/com/ichi2/anki/pages/PageFragment.kt

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import com.ichi2.anki.R
3131
import com.ichi2.anki.workarounds.OnWebViewRecreatedListener
3232
import com.ichi2.anki.workarounds.SafeWebViewLayout
3333
import com.ichi2.themes.Themes
34-
import com.ichi2.utils.checkWebviewVersion
34+
import com.ichi2.utils.showDialogIfWebViewOutdated
3535
import timber.log.Timber
3636

3737
/**
@@ -66,7 +66,7 @@ abstract class PageFragment(
6666

6767
protected open fun onWebViewCreated() { }
6868

69-
protected open fun requiresModernWebView(): Boolean = false
69+
protected open val minimumWebViewVersion: Int? = null
7070

7171
/**
7272
* When the webview calls `BridgeCommand("foo")`, the PageFragment execute `bridgeCommands["foo"]`.
@@ -106,14 +106,20 @@ abstract class PageFragment(
106106
savedInstanceState: Bundle?,
107107
) {
108108
val ankiActivity = requireActivity() as AnkiActivity
109-
if (requiresModernWebView() && checkWebviewVersion(ankiActivity)) {
110-
Timber.w("${this::class.simpleName} requires modern WebView (Chrome 90+), aborting load")
111-
ankiActivity.onBackPressedDispatcher.onBackPressed()
112-
return
113-
}
114109
server = AnkiServer(this).also { it.start() }
115110
webViewLayout = view.findViewById(R.id.webview_layout)
116111

112+
val minVersion = minimumWebViewVersion
113+
if (minVersion != null &&
114+
with(ankiActivity) {
115+
showDialogIfWebViewOutdated(minVersion) {
116+
ankiActivity.onBackPressedDispatcher.onBackPressed()
117+
}
118+
}
119+
) {
120+
Timber.w("${this::class.simpleName} requires modern WebView (Chrome 90+), aborting load")
121+
return
122+
}
117123
view.findViewById<MaterialToolbar>(R.id.toolbar)?.setNavigationOnClickListener {
118124
requireActivity().onBackPressedDispatcher.onBackPressed()
119125
}

AnkiDroid/src/main/java/com/ichi2/utils/WebViewUtils.kt

Lines changed: 37 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -27,32 +27,40 @@ import androidx.annotation.VisibleForTesting
2727
import androidx.appcompat.app.AlertDialog
2828
import androidx.core.content.pm.PackageInfoCompat
2929
import androidx.webkit.WebViewCompat
30-
import com.ichi2.anki.AnkiActivity
3130
import com.ichi2.anki.CrashReportService
3231
import com.ichi2.anki.R
32+
import com.ichi2.anki.utils.openUrl
3333
import kotlinx.coroutines.Dispatchers
3434
import kotlinx.coroutines.withContext
3535
import timber.log.Timber
3636

37-
internal const val OLDEST_WORKING_WEBVIEW_VERSION_CODE = 443000000L
38-
internal const val OLDEST_WORKING_WEBVIEW_VERSION = 90
37+
/** The Android package version code which corresponds to the Webview version. */
38+
internal const val OLDEST_WORKING_WEBVIEW_VERSION_CODE = 418306960L
39+
40+
/** The minimum supported Webview version(Human readable). */
41+
internal const val OLDEST_WORKING_WEBVIEW_VERSION = 85
3942

4043
/**
4144
* Shows a dialog if the current WebView version is older than the last supported version.
4245
*/
43-
fun checkWebviewVersion(activity: AnkiActivity): Boolean {
44-
val userVisibleCode = getChromeLikeWebViewVersionIfOutdated(activity) ?: return false
46+
47+
context(context: Context)
48+
fun showDialogIfWebViewOutdated(
49+
minimumWebViewVersion: Int = OLDEST_WORKING_WEBVIEW_VERSION,
50+
onOutdated: () -> Unit = {},
51+
): Boolean {
52+
val userVisibleCode = getChromeLikeWebViewVersionIfOutdated(context, minimumWebViewVersion) ?: return false
4553

4654
// Provide guidance to the user if the WebView is outdated
47-
val webviewPackageInfo = getAndroidSystemWebViewPackageInfo(activity.packageManager)
48-
val legacyWebViewPackageInfo = getLegacyWebViewPackageInfo(activity.packageManager)
55+
val webviewPackageInfo = WebViewCompat.getCurrentWebViewPackage(context)
56+
val legacyWebViewPackageInfo = getLegacyWebViewPackageInfo(context.packageManager)
4957
// TODO modify the alert dialog text to handle the usage of developer builds for system WebView
5058
if (legacyWebViewPackageInfo != null) {
5159
Timber.w("WebView is outdated. %s: %s", legacyWebViewPackageInfo.packageName, legacyWebViewPackageInfo.versionName)
52-
showOutdatedWebViewDialog(activity, userVisibleCode, R.string.link_legacy_webview_update)
60+
showOutdatedWebViewDialog(context, userVisibleCode, R.string.link_legacy_webview_update, onOutdated)
5361
} else {
5462
Timber.w("WebView is outdated. %s: %s", webviewPackageInfo?.packageName, webviewPackageInfo?.versionName)
55-
showOutdatedWebViewDialog(activity, userVisibleCode, R.string.link_webview_update)
63+
showOutdatedWebViewDialog(context, userVisibleCode, R.string.link_webview_update, onOutdated)
5664
}
5765
return true
5866
}
@@ -78,16 +86,25 @@ fun getWebviewUserAgent(context: Context): String? {
7886
* Returns a Chrome-like WebView version if it is outdated, otherwise null if
7987
* cannot be determined at all or if okay
8088
*/
81-
private fun getChromeLikeWebViewVersionIfOutdated(activity: AnkiActivity): Int? {
89+
private fun getChromeLikeWebViewVersionIfOutdated(
90+
context: Context,
91+
minimumWebViewVersion: Int,
92+
): Int? {
8293
// If we cannot get the package information at all, return null
83-
val webviewPackageInfo = getAndroidSystemWebViewPackageInfo(activity.packageManager) ?: return null
94+
val webviewPackageInfo = WebViewCompat.getCurrentWebViewPackage(context) ?: return null
8495
val webviewVersion =
8596
webviewPackageInfo.versionName ?: run {
8697
Timber.w("Failed to obtain WebView version")
8798
return null
8899
}
89100
val versionCode = PackageInfoCompat.getLongVersionCode(webviewPackageInfo)
90-
return checkWebViewVersionComponents(webviewPackageInfo.packageName, webviewVersion, versionCode, getWebviewUserAgent(activity))
101+
return checkWebViewVersionComponents(
102+
webviewPackageInfo.packageName,
103+
webviewVersion,
104+
versionCode,
105+
getWebviewUserAgent(context),
106+
minimumWebViewVersion,
107+
)
91108
}
92109

93110
@VisibleForTesting
@@ -96,6 +113,7 @@ fun checkWebViewVersionComponents(
96113
webviewVersion: String,
97114
versionCode: Long,
98115
userAgent: String?,
116+
minimumWebViewVersion: Int = OLDEST_WORKING_WEBVIEW_VERSION,
99117
): Int? {
100118
// Sometimes the webview version code appears too old, and the package name does as well,
101119
// but it's a webview that advertises modern capabilities via User-Agent in "Chrome" section
@@ -104,7 +122,7 @@ fun checkWebViewVersionComponents(
104122
val chromeRegex = """Chrome/(\d+)""".toRegex()
105123
val matchResult = chromeRegex.find(userAgent)?.groupValues?.get(1)
106124
matchResult?.toInt()?.let {
107-
if (it >= OLDEST_WORKING_WEBVIEW_VERSION) {
125+
if (it >= minimumWebViewVersion) {
108126
// If the User-Agent says we are modern, trust it and skip further checks.
109127
return null
110128
} else {
@@ -154,15 +172,17 @@ suspend fun getWebViewInfo(context: Context): WebViewInfo =
154172
}
155173

156174
private fun showOutdatedWebViewDialog(
157-
activity: AnkiActivity,
175+
context: Context,
158176
installedVersion: Int,
159177
@StringRes learnMoreUrl: Int,
178+
onDismiss: () -> Unit = {},
160179
) {
161-
AlertDialog.Builder(activity).show {
162-
setMessage(activity.getString(R.string.webview_update_message, installedVersion, OLDEST_WORKING_WEBVIEW_VERSION))
180+
AlertDialog.Builder(context).show {
181+
setMessage(context.getString(R.string.webview_update_message, installedVersion, OLDEST_WORKING_WEBVIEW_VERSION))
163182
setPositiveButton(R.string.scoped_storage_learn_more) { _, _ ->
164-
activity.openUrl(learnMoreUrl)
183+
context.openUrl(learnMoreUrl)
165184
}
185+
setOnDismissListener { onDismiss() }
166186
}
167187
}
168188

@@ -173,26 +193,6 @@ private fun getLegacyWebViewPackageInfo(packageManager: PackageManager): Package
173193
null
174194
}
175195

176-
/**
177-
* Returns a [PackageInfo] from the current system WebView, or `null` if unavailable
178-
*/
179-
private fun getAndroidSystemWebViewPackageInfo(packageManager: PackageManager): PackageInfo? {
180-
fun getPackage(packageName: String): PackageInfo? =
181-
try {
182-
packageManager.getPackageInfo(packageName, 0)
183-
} catch (_: PackageManager.NameNotFoundException) {
184-
null
185-
}
186-
187-
// The WebView is called com.android.webview by default.
188-
// Partner devices which ship with Google applications ship the Google-specific version
189-
// of the WebView called com.google.android.webview.
190-
// https://issues.chromium.org/issues/40419837#comment10
191-
192-
return getPackage("com.google.android.webview")
193-
?: getPackage("com.android.webview") // com.android.webview is used on API 24
194-
}
195-
196196
/**
197197
* Enables debugging of web contents (HTML / CSS / JavaScript)
198198
* loaded into any WebViews of this application. This flag can be enabled

AnkiDroid/src/test/java/com/ichi2/utils/WebViewUtilsTest.kt

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -56,26 +56,14 @@ class WebViewUtilsTest {
5656
equalTo(null),
5757
)
5858
assertThat(
59-
"Should catch old engine (78) in Huawei package even with valid versionCode",
59+
"Known old huawei webview determined correctly",
6060
checkWebViewVersionComponents(
6161
"com.huawei.webview",
62-
"12.1.2.322",
63-
450000000L,
62+
"unknown",
63+
356L,
6464
"Mozilla/5.0 (Linux; Android 10; CDY-AN90 Build/HUAWEICDY-AN90; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 Mobile Safari/537.36",
6565
),
6666
equalTo(78),
6767
)
68-
// Link: https://www.apkmirror.com/apk/huawei/huawei-webview-2/huawei-webview-15-0-4-326-release/
69-
// verified version code is 2113L for 15.0.4.326 by analyzing the manifest
70-
assertThat(
71-
"Huawei v15 with code 21311 should be allowed if UA indicates modern engine (114)",
72-
checkWebViewVersionComponents(
73-
"com.huawei.webview",
74-
"15.0.4.326",
75-
21311L,
76-
"Mozilla/5.0 (Linux; Android 12; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.196 Mobile Safari/537.36",
77-
),
78-
equalTo(null),
79-
)
8068
}
8169
}

0 commit comments

Comments
 (0)