Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
84eea056c6 | ||
|
|
d0ca1be15e | ||
|
|
f09e82d941 |
No files matched your search
@@ -1,8 +1,6 @@
|
|||||||
name: Build Release AAB
|
name: Build Release AAB
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
|
||||||
tags: ['v*']
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -22,7 +20,7 @@ jobs:
|
|||||||
- name: Setup Flutter
|
- name: Setup Flutter
|
||||||
uses: subosito/flutter-action@v2
|
uses: subosito/flutter-action@v2
|
||||||
with:
|
with:
|
||||||
flutter-version: '3.27.1'
|
flutter-version: '3.47.4'
|
||||||
channel: 'stable'
|
channel: 'stable'
|
||||||
cache: true
|
cache: true
|
||||||
|
|
||||||
|
|||||||
@@ -42,3 +42,8 @@ app.*.map.json
|
|||||||
/android/app/debug
|
/android/app/debug
|
||||||
/android/app/profile
|
/android/app/profile
|
||||||
/android/app/release
|
/android/app/release
|
||||||
|
|
||||||
|
# Keystore and signing (local-only, never commit)
|
||||||
|
*.keystore
|
||||||
|
*.jks
|
||||||
|
android/key.properties
|
||||||
@@ -5,10 +5,13 @@ plugins {
|
|||||||
id("dev.flutter.flutter-gradle-plugin")
|
id("dev.flutter.flutter-gradle-plugin")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import java.util.Properties
|
||||||
|
import java.io.FileInputStream
|
||||||
|
|
||||||
val keystorePropertiesFile = rootProject.file("key.properties")
|
val keystorePropertiesFile = rootProject.file("key.properties")
|
||||||
val keystoreProperties = java.util.Properties()
|
val keystoreProperties = Properties()
|
||||||
if (keystorePropertiesFile.exists()) {
|
if (keystorePropertiesFile.exists()) {
|
||||||
keystoreProperties.load(java.io.FileInputStream(keystorePropertiesFile))
|
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
|||||||
Vendored
+3
-17
@@ -1,23 +1,9 @@
|
|||||||
# Flutter specific
|
|
||||||
-keep class io.flutter.app.** { *; }
|
|
||||||
-keep class io.flutter.plugin.** { *; }
|
|
||||||
-keep class io.flutter.util.** { *; }
|
|
||||||
-keep class io.flutter.view.** { *; }
|
|
||||||
-keep class io.flutter.** { *; }
|
|
||||||
-keep class io.flutter.plugins.** { *; }
|
|
||||||
|
|
||||||
# Play Core deferred components
|
# Play Core deferred components
|
||||||
-dontwarn com.google.android.play.core.**
|
-dontwarn com.google.android.play.core.**
|
||||||
-dontwarn com.google.android.play.core.splitcompat.**
|
-dontwarn com.google.android.play.core.splitcompat.**
|
||||||
-dontwarn com.google.android.play.core.splitinstall.**
|
-dontwarn com.google.android.play.core.splitinstall.**
|
||||||
-dontwarn com.google.android.play.core.tasks.**
|
-dontwarn com.google.android.play.core.tasks.**
|
||||||
|
|
||||||
# SIAB Scale app
|
# Aggressive R8 optimization (missing from default configs)
|
||||||
-keep class id.ptsemangat.siab_scale.** { *; }
|
-allowaccessmodification
|
||||||
|
-repackageclasses ''
|
||||||
# flutter_blue_plus
|
|
||||||
-keep class com.lib.flutter_blue_plus.** { *; }
|
|
||||||
|
|
||||||
# sqflite
|
|
||||||
-keep class com.tekartik.sqflite.** { *; }
|
|
||||||
|
|
||||||
+1
-1
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
|
|||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-all.zip
|
||||||
@@ -8,4 +8,5 @@
|
|||||||
storePassword=CHANGE_ME_STORE_PASSWORD
|
storePassword=CHANGE_ME_STORE_PASSWORD
|
||||||
keyPassword=CHANGE_ME_KEY_PASSWORD
|
keyPassword=CHANGE_ME_KEY_PASSWORD
|
||||||
keyAlias=siab
|
keyAlias=siab
|
||||||
storeFile=../siab-release.keystore
|
# NOTE: storeFile is resolved relative to android/app/, so ../../ points to the repo root.
|
||||||
|
storeFile=../../siab-release.keystore
|
||||||
@@ -19,8 +19,8 @@ pluginManagement {
|
|||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
|
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
|
||||||
id("com.android.application") version "8.4.0" apply false
|
id("com.android.application") version "9.0.0" apply false
|
||||||
id("org.jetbrains.kotlin.android") version "2.0.0" apply false
|
id("org.jetbrains.kotlin.android") version "2.2.20" apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
include(":app")
|
include(":app")
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
# DEX Optimization (Play Console metrics) Implementation Plan
|
||||||
|
|
||||||
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
|
||||||
|
**Goal:** Raise Play Console DEX metrics (shrink/obfuscation/optimization currently 40-42%, level Medium) by removing redundant blanket ProGuard keep rules, then verify the app still works end-to-end on a physical device.
|
||||||
|
|
||||||
|
**Architecture:** Flutter Android app whose DEX (1.1MB, 1165 classes) is ~40% pinned by legacy `io.flutter.** { *; }` blanket keep rules. Flutter's built-in `flutter_proguard_rules.pro` (auto-applied from the Flutter SDK gradle package) already keeps `FlutterPlugin` implementations with `allowobfuscation`, plus manifest keeps and `@Keep` annotations cover the rest. Strip the redundant rules, add standard R8 hardening flags, rebuild, verify DEX size reduction, smoke-test on a physical device (Redmi A5 over adb/usbipd), then commit.
|
||||||
|
|
||||||
|
**Tech Stack:** Flutter 3.47.4, AGP 9.0.0, Gradle 9.1.0, R8 (full mode default), Android SDK 36, adb via usbipd WSL passthrough.
|
||||||
|
|
||||||
|
**Spec:** In-conversation analysis (2026-09-17) — Play Console reported: DEX code optimization Medium, optimization 40%, obfuscation 42%, shrinking 41%. Root cause: blanket keep rules. Expected outcome: shrink/obfuscation ~65-75%, never 100% (remaining DEX is genuinely used plugin code). Baseline-profile work explicitly OUT OF SCOPE.
|
||||||
|
|
||||||
|
## Global Constraints
|
||||||
|
|
||||||
|
- Working dir: `/mnt/d/Flutter_Projects/TimbanganSiab` (git branch `main`; user explicitly authorized committing directly to `main`)
|
||||||
|
- Release build command (exact): `flutter build appbundle --release --obfuscate --split-debug-info=build/debug-info --dart-define=SIAB_DEVICE_NAME=SIAB`
|
||||||
|
- `flutter install` takes NO `--dart-define` flag; build APK with the flag first, then `adb install -r`
|
||||||
|
- PATH required: `export PATH="/home/ifitworks/flutter/bin:/mnt/d/android-sdk/platform-tools:$PATH"`
|
||||||
|
- Device: Redmi A5 (serial `863d0058304831323851054b1c83ab`), app id `id.ptsemangat.siab_scale`, installed copy is signed with release key
|
||||||
|
- If device missing from `adb devices`: `"/mnt/c/Program Files/usbipd-win/usbipd.exe" attach --wsl --busid 1-2`
|
||||||
|
- Baseline DEX size (pre-change): `classes.dex` = 1097572 bytes
|
||||||
|
- No new dependencies. No `git push` (user runs push themselves).
|
||||||
|
- Do NOT touch: `gradle.properties` migrator flags (`android.builtInKotlin=false`, `android.newDsl=false`), settings.gradle.kts versions, workflow file.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 1: Strip redundant ProGuard keep rules
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `android/app/proguard-rules.pro` (full rewrite)
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Consumes: nothing (config file only)
|
||||||
|
- Produces: rules consumed by R8 during release build in Task 2
|
||||||
|
|
||||||
|
- [ ] **Step 1: Replace entire contents of `android/app/proguard-rules.pro` with:**
|
||||||
|
|
||||||
|
```proguard
|
||||||
|
# Play Core deferred components
|
||||||
|
-dontwarn com.google.android.play.core.**
|
||||||
|
-dontwarn com.google.android.play.core.splitcompat.**
|
||||||
|
-dontwarn com.google.android.play.core.splitinstall.**
|
||||||
|
-dontwarn com.google.android.play.core.tasks.**
|
||||||
|
|
||||||
|
# Aggressive R8 optimization (missing from default configs)
|
||||||
|
-allowaccessmodification
|
||||||
|
-repackageclasses ''
|
||||||
|
```
|
||||||
|
|
||||||
|
Removed (verified redundant): all 5 `io.flutter.**` keep lines (Flutter SDK auto-applies `flutter_proguard_rules.pro` which keeps `FlutterPlugin` implementors with `allowobfuscation`; JNI natives use `@Keep`; app methods are `@Keep`d by GeneratedPluginRegistrant), `-keep class id.ptsemangat.siab_scale.** { *; }` (manifest keeps `MainActivity`, `@Keep` on `GeneratedPluginRegistrant`), `-keep class com.lib.flutter_blue_plus.**`, `-keep class com.tekartik.sqflite.**` (both instantiated statically in `GeneratedPluginRegistrant` + covered by Flutter's FlutterPlugin rule).
|
||||||
|
|
||||||
|
- [ ] **Step 2: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add android/app/proguard-rules.pro
|
||||||
|
git commit -m "perf: strip redundant blanket proguard keeps for DEX shrink/obfuscation"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 2: Rebuild AAB and verify R8 output
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create/replace: `build/app/outputs/bundle/release/app-release.aab`, `build/app/outputs/mapping/release/mapping.txt`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Consumes: Task 1 rules
|
||||||
|
- Produces: smaller AAB + mapping for Task 3/4
|
||||||
|
|
||||||
|
- [ ] **Step 1: Build**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export PATH="/home/ifitworks/flutter/bin:/mnt/d/android-sdk/platform-tools:$PATH"
|
||||||
|
cd /mnt/d/Flutter_Projects/TimbanganSiab
|
||||||
|
flutter build appbundle --release --obfuscate --split-debug-info=build/debug-info --dart-define=SIAB_DEVICE_NAME=SIAB
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: `✓ Built ... app-release.aab`. Fail if build fails.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Verify DEX shrank and mapping regenerated**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
unzip -p build/app/outputs/bundle/release/app-release.aab base/dex/classes.dex | wc -c
|
||||||
|
# must be < 1097572 (baseline)
|
||||||
|
ls -la build/app/outputs/mapping/release/mapping.txt
|
||||||
|
# must exist, freshly created
|
||||||
|
grep -c "^io.flutter" build/app/outputs/mapping/release/mapping.txt || true
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: Check build log for R8 missing-class warnings** — if any `Missing class` warnings appear, add ONLY `-dontwarn <exact.class>` lines for those classes (append to proguard-rules.pro), never `-keep`.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Record actual DEX size in report; no commit needed (build outputs are gitignored).**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 3: On-device smoke test (Redmi A5)
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Installs `app-release.apk` to device
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Consumes: Task 2 build config; same flags for APK build
|
||||||
|
- Produces: pass/fail runtime evidence for Task 4
|
||||||
|
|
||||||
|
- [ ] **Step 1: Ensure device present** — if `adb devices` empty or `no permissions`: `"/mnt/c/Program Files/usbipd-win/usbipd.exe" attach --wsl --busid 1-2; sleep 5; adb kill-server; adb devices -l`. Need status `device`.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Build + install**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export PATH="/home/ifitworks/flutter/bin:/mnt/d/android-sdk/platform-tools:$PATH"
|
||||||
|
cd /mnt/d/Flutter_Projects/TimbanganSiab
|
||||||
|
flutter build apk --release --obfuscate --split-debug-info=build/debug-info --dart-define=SIAB_DEVICE_NAME=SIAB
|
||||||
|
adb install -r build/app/outputs/flutter-apk/app-release.apk
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: `Success`. If `INSTALL_FAILED_UPDATE_INCOMPATIBLE` → `adb uninstall id.ptsemangat.siab_scale` then retry.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Launch + crash scan**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
adb logcat -c
|
||||||
|
adb shell monkey -p id.ptsemangat.siab_scale -c android.intent.category.LAUNCHER 1
|
||||||
|
sleep 12
|
||||||
|
adb shell pidof id.ptsemangat.siab_scale # must print PID (app alive)
|
||||||
|
adb logcat -d | grep -iE "FATAL|NoClassDef|NoSuchMethod|UnsatisfiedLink|ClassNotFoundException" | head -20
|
||||||
|
# expected: no matches
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: Manual checks by user** (blocks Task 4): BLE scan lists devices; weighing screen loads; history screen loads (sqflite); CSV share works (share_plus). Record user confirmation in report.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 4: Commit and hand off push
|
||||||
|
|
||||||
|
**Files:** none beyond Task 1 commit (verify clean `git status` except gitignored build/)
|
||||||
|
|
||||||
|
- [ ] **Step 1:** `git status` — confirm only build/ artifacts untracked; commit anything from Task 2 Step 3 if proguard file changed again.
|
||||||
|
- [ ] **Step 2:** Report: baseline vs new DEX sizes, mapping exists, device test result, user manual-check confirmation.
|
||||||
|
- [ ] **Step 3:** Print push command for user (does NOT run push — needs user auth):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git push origin main
|
||||||
|
```
|
||||||
Binary file not shown.
Reference in new issue
Block a user