Skip to content

Commit 3ce47f9

Browse files
authored
fix(android): Fixed class protection in builds to resolve the screen freezing issue in release builds (#1110)
* fix(android): Fixed class protection in builds to resolve the screen freezing issue in release builds Allowed the use of debug signatures as a fallback during local verification Updated build configurations to correctly handle missing signatures Added documentation detailing signature requirements for release builds * fix(android): Check signature configuration only when a release signature is required Check the release signature configuration only when the task name contains ‘release’ to avoid unnecessary checks during non-release builds * refactor(android): Optimize signature configuration validation logic and extract variables Rewrite the signature configuration validation logic, extract properties as separate variables, and add null checks Add validation of signature requirements for aggregate build tasks * refactor(android): Optimize the detection logic for the release signature task Break down complex conditional checks into clearer combinations of multiple conditions to improve code readability and maintainability * fix(android): Fixed an issue with the normalization of build task names Added normalization for task names containing colons during processing to ensure that signature verification logic executes correctly * refactor(android): Optimize the logic for processing Gradle task names Improve the logic for collecting and processing Gradle task names to ensure more accurate identification of project-related tasks and standardized handling. This prevents signature configuration errors caused by issues with task name formats.
1 parent 75c1dd3 commit 3ce47f9

4 files changed

Lines changed: 70 additions & 16 deletions

File tree

CLAUDE.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
77
### Development
88

99
- `flutter run` - Run the app in development mode
10+
- `flutter run --release -PallowDebugReleaseSigning=true` - Run Android release locally with explicit debug-signing fallback for verification only
1011
- `dart run fl_build -p PLATFORM` - Build the app for specific platform (see fl_build package)
1112
- `dart run build_runner build --delete-conflicting-outputs` - Generate code for models with annotations (json_serializable, freezed, hive, riverpod)
1213
- Every time you change model files, run this command to regenerate code (Hive adapters, Riverpod providers, etc.)
@@ -93,3 +94,11 @@ This is a Flutter application for managing Linux servers with the following key
9394
- Before adding new strings, check if it already exists in `libL10n`.
9495
- Prioritize using strings from `libL10n` to avoid duplication, even if the meaning is not 100% exact, just use the substitution of `libL10n`.
9596
- Split UI into Widget build, Actions, Utils. use `extension on` to achieve this
97+
- Android release signing:
98+
- Normal release builds must use the real release keystore from `key.properties`.
99+
- Debug-signing fallback is for local verification only and must be enabled explicitly with `-PallowDebugReleaseSigning=true`.
100+
- Do not use debug-signing fallback for formal release artifacts.
101+
- Android release artifacts:
102+
- CI release builds for Android are split per ABI, not a single fat APK.
103+
- Do not judge release size from a local `flutter build apk --release` fat APK.
104+
- To reproduce CI-style Android artifacts locally, use `dart run fl_build -p android` or `flutter build apk --release --split-per-abi`.

android/app/build.gradle

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,55 @@ def keystorePropertiesFile = rootProject.file('key.properties')
1717
if (keystorePropertiesFile.exists()) {
1818
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
1919
} else {
20-
System.err.printf(" [!] key.properties not found in %s (%s). Build will fail. \n", rootProject, rootProject.file('.'))
20+
System.err.printf(" [!] key.properties not found in %s (%s). \n", rootProject, rootProject.file('.'))
2121
}
2222

23-
if (keystoreProperties['storeFile'] == null || !file(keystoreProperties['storeFile']).exists()) {
24-
System.err.printf(" [!] storeFile defined in key.properties does not exist in %s. Build will fail. \n", file('.'))
23+
def storeFilePath = keystoreProperties['storeFile']?.toString()?.trim()
24+
def storePassword = keystoreProperties['storePassword']?.toString()?.trim()
25+
def keyAlias = keystoreProperties['keyAlias']?.toString()?.trim()
26+
def keyPassword = keystoreProperties['keyPassword']?.toString()?.trim()
27+
28+
def hasReleaseSigning =
29+
storeFilePath &&
30+
file(storeFilePath).exists() &&
31+
storePassword &&
32+
keyAlias &&
33+
keyPassword
34+
35+
def allowDebugReleaseSigning = project.findProperty('allowDebugReleaseSigning') == 'true'
36+
def requestedTaskNames = gradle.startParameter.taskNames
37+
.findAll {
38+
!it.contains(':') ||
39+
(it.startsWith(':') && it.indexOf(':', 1) == -1) ||
40+
it.startsWith("${project.path}:") ||
41+
it.startsWith(":${project.name}:")
42+
}
43+
.collect {
44+
def normalizedTaskName =
45+
(it.startsWith("${project.path}:") || it.startsWith(":${project.name}:"))
46+
? it.substring(it.lastIndexOf(':') + 1)
47+
: it.startsWith(':') ? it.substring(1) : it
48+
normalizedTaskName.toLowerCase()
49+
}
50+
def aggregateSigningTasks = ['assemble', 'build', 'bundle', 'install']
51+
def requiresReleaseSigning = requestedTaskNames.any {
52+
aggregateSigningTasks.contains(it) ||
53+
it == 'release' ||
54+
(it.startsWith('assemble') && it.contains('release')) ||
55+
(it.startsWith('bundle') && it.contains('release')) ||
56+
(it.startsWith('install') && it.contains('release')) ||
57+
(it.startsWith('build') && it.contains('release'))
58+
}
59+
60+
if (requiresReleaseSigning && !hasReleaseSigning) {
61+
if (allowDebugReleaseSigning) {
62+
System.err.printf(" [!] Release signing is unavailable. Falling back to debug signing because -PallowDebugReleaseSigning=true was provided. \n")
63+
} else {
64+
throw new GradleException(
65+
'Release signing is not configured. Add key.properties with a valid storeFile, ' +
66+
'or build with -PallowDebugReleaseSigning=true for local verification only.'
67+
)
68+
}
2569
}
2670

2771
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
@@ -70,16 +114,16 @@ android {
70114

71115
signingConfigs {
72116
release {
73-
keyAlias keystoreProperties['keyAlias']
74-
keyPassword keystoreProperties['keyPassword']
75-
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
76-
storePassword keystoreProperties['storePassword']
117+
keyAlias keyAlias
118+
keyPassword keyPassword
119+
storeFile storeFilePath ? file(storeFilePath) : null
120+
storePassword storePassword
77121
}
78122
}
79123

80124
buildTypes {
81125
release {
82-
signingConfig signingConfigs.release
126+
signingConfig hasReleaseSigning ? signingConfigs.release : signingConfigs.debug
83127
minifyEnabled true
84128
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
85129
}

android/app/proguard-rules.pro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
-keep class com.jcraft.** { *; }
2+
-keep class io.flutter.util.PathUtils { *; }

pubspec.lock

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -918,10 +918,10 @@ packages:
918918
dependency: transitive
919919
description:
920920
name: matcher
921-
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
921+
sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6"
922922
url: "https://pub.dev"
923923
source: hosted
924-
version: "0.12.19"
924+
version: "0.12.18"
925925
material_color_utilities:
926926
dependency: transitive
927927
description:
@@ -1506,26 +1506,26 @@ packages:
15061506
dependency: "direct dev"
15071507
description:
15081508
name: test
1509-
sha256: "280d6d890011ca966ad08df7e8a4ddfab0fb3aa49f96ed6de56e3521347a9ae7"
1509+
sha256: "54c516bbb7cee2754d327ad4fca637f78abfc3cbcc5ace83b3eda117e42cd71a"
15101510
url: "https://pub.dev"
15111511
source: hosted
1512-
version: "1.30.0"
1512+
version: "1.29.0"
15131513
test_api:
15141514
dependency: transitive
15151515
description:
15161516
name: test_api
1517-
sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
1517+
sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636"
15181518
url: "https://pub.dev"
15191519
source: hosted
1520-
version: "0.7.10"
1520+
version: "0.7.9"
15211521
test_core:
15221522
dependency: transitive
15231523
description:
15241524
name: test_core
1525-
sha256: "0381bd1585d1a924763c308100f2138205252fb90c9d4eeaf28489ee65ccde51"
1525+
sha256: "394f07d21f0f2255ec9e3989f21e54d3c7dc0e6e9dbce160e5a9c1a6be0e2943"
15261526
url: "https://pub.dev"
15271527
source: hosted
1528-
version: "0.6.16"
1528+
version: "0.6.15"
15291529
tuple:
15301530
dependency: transitive
15311531
description:

0 commit comments

Comments
 (0)