Play Console Recommendations¶
Google Play's console raises automated "recommendations" against the uploaded bundle. They are produced by static analysis of the shipped bytecode, so they fire on code paths that exist in a dependency even when the app can never reach them. This page records the ones we have already investigated, so nobody has to work through the same deobfuscation twice.
Raw recommendation text is archived alongside the crash reports in
dev/crashes/<version>/recommendations.txt.
Bitmap downsampling — BitmapFactory without BitmapFactory.Options (v1.0.2)¶
Your app uses BitmapFactory without downsampling at:
f3.j.b
Status: not actionable. Unreachable in this app.
Deobfuscated against release/V1.0.2/mapping.txt:
com.mr.flutter.plugin.filepicker.FileUtils -> f3.j
android.net.Uri compressImage(android.net.Uri, int, android.content.Context) -> b
So the finding is FileUtils.compressImage() in the file_picker plugin, not
our code. In the plugin it is guarded:
private fun processUri(activity: Activity, uri: Uri, compressionQuality: Int): Uri {
return if (compressionQuality > 0 && isImage(activity.applicationContext, uri)) {
compressImage(uri, compressionQuality, activity.applicationContext)
} else {
uri
}
}
compressionQuality defaults to 0 in file_picker's Dart API, and both of our
call sites leave it at the default while restricting the picker to non-image
types:
lib/features/file_analysis/file_analysis_screen.dart— audio extensionslib/features/survey/survey_setup_screen.dart—txt/csv
Both conditions therefore fail and compressImage never runs. R8 keeps the
method because it is reachable from the plugin's method-channel dispatch, which
is why the analyzer still sees it.
If this ever changes: the moment we pass a non-zero compressionQuality or
allow image types through the picker, this stops being theoretical and the fix
belongs upstream in file_picker (it should set BitmapFactory.Options.inSampleSize).
R8 — optimized resource shrinking (v1.0.2)¶
Optimized resource shrinking is not enabled. Update your Android Gradle Plugin to version 9.0 or higher.
Status: fixed (AGP 9 upgrade), pending device testing.
The recommendation is correct and cannot be worked around by adding a flag. The
property that controls it is android.r8.optimizedResourceShrinking, and it
exists only in AGP 9.x. Verified by scanning the plugin jars directly:
| Property | AGP 8.11.1 | AGP 9.0.1 |
|---|---|---|
android.r8.optimizedShrinking |
yes | yes |
android.r8.integratedResourceShrinking |
yes | yes |
android.r8.optimizedResourceShrinking |
no | yes |
The app already sets minifyEnabled true and shrinkResources true; this is a
further R8 mode, not a missing basic setting.
What the upgrade involved¶
AGP 9 pulls two other tools with it. These are the versions now in the repo:
| Tool | Was | Now |
|---|---|---|
| Android Gradle Plugin | 8.11.1 | 9.0.1 |
| Gradle | 8.14 | 9.1.0 |
| Kotlin Gradle Plugin | 2.2.20 | 2.3.20 |
Gradle 9.1.0 is the floor, not 9.0.0 — AGP 9.0.1 rejects 9.0.0 outright:
Failed to apply plugin 'com.android.internal.version-check'.
> Minimum supported Gradle version is 9.1.0. Current version is 9.0.0.
Result: flutter build appbundle --release succeeds, and the bundle went from
230.7 MB to 229.5 MB, which is the optimized resource shrinking doing its
job (the bulk of the bundle is ONNX models, which are noCompress, so a 1.2 MB
drop comes entirely out of the much smaller resource section).
Notes on our specific configuration:
- Proguard file is already correct. AGP 9 rejects
getDefaultProguardFile('proguard-android.txt')because it implies-dontoptimize. We already useproguard-android-optimize.txt, which is the replacement AGP 9 asks for. android.newDsl=falseinandroid/gradle.propertiesopts out of the new DSL, which becomes the default in AGP 9 and is the only option in AGP 10. It is still honoured in 9.x, so it does not block the upgrade, but it is borrowed time.com.android.asset-packmust move in lockstep with AGP — we build amodels_packasset pack.- Flutter 3.44 supports AGP up to 9.1 and its own project template already defaults to AGP 9.0.1 + Gradle 9.1.0, so this is a well-trodden path rather than bleeding edge.
Still outstanding: Built-in Kotlin¶
The upgrade surfaced a separate warning that is not fixed:
WARNING: Your app uses the following plugins that apply Kotlin Gradle Plugin (KGP):
dynamic_color, flutter_foreground_task, flutter_onnxruntime, flutter_tts
Future versions of Flutter will fail to build if your app uses plugins that apply KGP.
android.builtInKotlin=false is therefore load-bearing: we cannot migrate to
Built-in Kotlin until those four plugins stop applying KGP themselves. One of
them, flutter_onnxruntime, is deliberately pinned to an old version (see
ONNX Runtime Pin), so that plugin is blocked on two fronts
at once. This is a "watch the plugin changelogs" item, not something we can fix
locally.
Ship it separately from a crash hotfix¶
The build is green, but a toolchain bump changes how every class is compiled and
shrunk, and optimized resource shrinking rewrites resources — breakage there
shows up at runtime, not at build time. A successful bundleRelease is not
evidence the app still works.
Do not fold this into a hotfix release. Land it on its own, and smoke-test on real devices across a couple of Android versions first — with attention to anything resource-driven (icons, notification channels, widget layouts, the Quick Listen home-screen widget). Bundling an unvalidated toolchain change into a crash fix is exactly the failure mode that produced the v1.0.2 crash.