packwise-skills 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursorrules +23 -23
- package/CLAUDE.md +25 -25
- package/LICENSE +21 -0
- package/README.md +404 -295
- package/audit.md +224 -224
- package/bin/packwise.js +322 -155
- package/install.sh +123 -0
- package/package.json +32 -31
- package/skill.md +944 -719
- package/sub-skills/ai/local-llm.md +183 -183
- package/sub-skills/ai/python-ml.md +164 -164
- package/sub-skills/backend/go-server.md +184 -184
- package/sub-skills/backend/java-spring.md +241 -241
- package/sub-skills/backend/node-server.md +164 -164
- package/sub-skills/backend/php-laravel.md +175 -175
- package/sub-skills/backend/python-server.md +164 -164
- package/sub-skills/backend/rust-backend.md +118 -118
- package/sub-skills/cli/python-cli.md +236 -236
- package/sub-skills/cli/sdk-library.md +497 -497
- package/sub-skills/cloud/ci-cd-pipelines.md +350 -350
- package/sub-skills/cloud/docker.md +191 -191
- package/sub-skills/cloud/kubernetes.md +277 -277
- package/sub-skills/cloud/payment-integration.md +307 -307
- package/sub-skills/cross-platform/multiplatform.md +252 -252
- package/sub-skills/desktop/electron.md +783 -783
- package/sub-skills/desktop/game-dev.md +443 -443
- package/sub-skills/desktop/native-app.md +123 -123
- package/sub-skills/desktop/scenarios.md +443 -443
- package/sub-skills/desktop/smart-platforms.md +324 -324
- package/sub-skills/desktop/tauri.md +428 -428
- package/sub-skills/desktop/vr-ar.md +252 -252
- package/sub-skills/desktop/web-to-desktop.md +153 -153
- package/sub-skills/embedded/car-infotainment.md +129 -129
- package/sub-skills/embedded/esp32.md +184 -184
- package/sub-skills/embedded/ros.md +150 -150
- package/sub-skills/embedded/stm32.md +160 -160
- package/sub-skills/mobile/android.md +322 -322
- package/sub-skills/mobile/capacitor.md +232 -232
- package/sub-skills/mobile/flutter-mobile.md +138 -138
- package/sub-skills/mobile/harmonyos.md +150 -150
- package/sub-skills/mobile/ios.md +245 -245
- package/sub-skills/mobile/react-native.md +443 -443
- package/sub-skills/mobile/wearables.md +230 -230
- package/sub-skills/plugins/browser-extension.md +308 -308
- package/sub-skills/plugins/jetbrains-plugin.md +226 -226
- package/sub-skills/plugins/vscode-extension.md +204 -204
- package/sub-skills/security/security-tools.md +174 -174
- package/sub-skills/web/monorepo.md +274 -274
- package/sub-skills/web/pwa.md +220 -220
- package/sub-skills/web/serverless-edge.md +295 -295
- package/sub-skills/web/spa.md +266 -266
- package/sub-skills/web/ssr.md +228 -228
- package/sub-skills/web/wasm.md +243 -243
|
@@ -1,322 +1,322 @@
|
|
|
1
|
-
# Android Native Build Sub-Skill
|
|
2
|
-
|
|
3
|
-
Build and publish Kotlin/Java native Android applications.
|
|
4
|
-
|
|
5
|
-
**Current version**: Android 15 (API 35) / AGP 8.7.x / Gradle 8.10+ / JDK 17 (2025-2026)
|
|
6
|
-
|
|
7
|
-
## When to Use
|
|
8
|
-
|
|
9
|
-
- Native Android applications (Kotlin recommended, Java supported)
|
|
10
|
-
- Apps requiring deep Android platform integration
|
|
11
|
-
- Apps published to Google Play, Huawei AppGallery, or Xiaomi GetApps
|
|
12
|
-
|
|
13
|
-
## Prerequisites
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
# JDK 17 (REQUIRED for AGP 8.x)
|
|
17
|
-
# Android Studio (includes JDK 17 bundled as JBR)
|
|
18
|
-
# OR: standalone JDK 17
|
|
19
|
-
|
|
20
|
-
# Android SDK 35 (via Android Studio SDK Manager)
|
|
21
|
-
# NDK (if using native code)
|
|
22
|
-
|
|
23
|
-
# Verify
|
|
24
|
-
java -version # Should show 17.x
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
## Build
|
|
28
|
-
|
|
29
|
-
```bash
|
|
30
|
-
./gradlew assembleDebug # Debug APK
|
|
31
|
-
./gradlew assembleRelease # Release APK
|
|
32
|
-
./gradlew bundleRelease # AAB (required by Google Play)
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## Required Build Configuration (AGP 8.x)
|
|
36
|
-
|
|
37
|
-
```kotlin
|
|
38
|
-
// build.gradle.kts (app-level)
|
|
39
|
-
android {
|
|
40
|
-
namespace = "com.example.myapp" // REQUIRED since AGP 8.0 (replaces package in AndroidManifest)
|
|
41
|
-
compileSdk = 35 // Should match latest API level
|
|
42
|
-
|
|
43
|
-
defaultConfig {
|
|
44
|
-
applicationId = "com.example.myapp"
|
|
45
|
-
minSdk = 24 // Android 7.0
|
|
46
|
-
targetSdk = 35 // REQUIRED: Google Play mandates API 35 as of 2025
|
|
47
|
-
versionCode = 1
|
|
48
|
-
versionName = "1.0.0"
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
```xml
|
|
54
|
-
<!-- AndroidManifest.xml — REMOVE package attribute (AGP 8.x) -->
|
|
55
|
-
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
56
|
-
<!-- NO package="..." attribute here -->
|
|
57
|
-
</manifest>
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
### gradle.properties (Modern Defaults)
|
|
61
|
-
|
|
62
|
-
```properties
|
|
63
|
-
# gradle.properties
|
|
64
|
-
android.useAndroidX=true
|
|
65
|
-
android.nonTransitiveRClass=true
|
|
66
|
-
org.gradle.jvmargs=-Xmx4g -XX:+UseG1GC
|
|
67
|
-
org.gradle.parallel=true
|
|
68
|
-
org.gradle.caching=true
|
|
69
|
-
org.gradle.configuration-cache=true
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
## Signing (Secure Approach)
|
|
73
|
-
|
|
74
|
-
### Generate Upload Key
|
|
75
|
-
|
|
76
|
-
```bash
|
|
77
|
-
keytool -genkey -v -keystore upload-key.jks \
|
|
78
|
-
-keyalg RSA -keysize 2048 -validity 10000 \
|
|
79
|
-
-alias upload
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
### Secure Signing Configuration
|
|
83
|
-
|
|
84
|
-
```kotlin
|
|
85
|
-
// build.gradle.kts — NEVER hardcode passwords
|
|
86
|
-
signingConfigs {
|
|
87
|
-
create("release") {
|
|
88
|
-
storeFile = file(System.getenv("KEYSTORE_PATH") ?: "../keystore/upload-key.jks")
|
|
89
|
-
storePassword = System.getenv("KEYSTORE_PASSWORD") ?: ""
|
|
90
|
-
keyAlias = System.getenv("KEY_ALIAS") ?: ""
|
|
91
|
-
keyPassword = System.getenv("KEY_PASSWORD") ?: ""
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
buildTypes {
|
|
96
|
-
release {
|
|
97
|
-
signingConfig = signingConfigs.getByName("release")
|
|
98
|
-
isMinifyEnabled = true
|
|
99
|
-
isShrinkResources = true
|
|
100
|
-
proguardFiles(
|
|
101
|
-
getDefaultProguardFile("proguard-android-optimize.txt"),
|
|
102
|
-
"proguard-rules.pro"
|
|
103
|
-
)
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
### Play App Signing (Recommended)
|
|
109
|
-
|
|
110
|
-
Google Play App Signing separates the **upload key** (you hold) from the **signing key** (Google holds):
|
|
111
|
-
|
|
112
|
-
- **Upload key**: Signs your AAB before uploading. If lost, can be reset in Play Console.
|
|
113
|
-
- **Signing key**: Signs the final APK delivered to users. Google manages this securely.
|
|
114
|
-
- Enroll in Google Play Console → Release → Setup → App signing
|
|
115
|
-
|
|
116
|
-
## 16KB Page Alignment (Android 15 Requirement)
|
|
117
|
-
|
|
118
|
-
Android 15 devices may use 16KB memory pages. All native libraries (.so files) must be 16KB aligned.
|
|
119
|
-
|
|
120
|
-
```kotlin
|
|
121
|
-
// build.gradle.kts
|
|
122
|
-
android {
|
|
123
|
-
packaging {
|
|
124
|
-
jniLibs {
|
|
125
|
-
pageAlignSharedLibs = true // AGP 8.5+
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
```bash
|
|
132
|
-
# Check alignment
|
|
133
|
-
unzip -l app.apk | grep "\.so$"
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
**Apps without native code are NOT affected.**
|
|
137
|
-
|
|
138
|
-
## Network Security Config
|
|
139
|
-
|
|
140
|
-
```xml
|
|
141
|
-
<!-- res/xml/network_security_config.xml -->
|
|
142
|
-
<?xml version="1.0" encoding="utf-8"?>
|
|
143
|
-
<network-security-config>
|
|
144
|
-
<base-config cleartextTrafficPermitted="false">
|
|
145
|
-
<trust-anchors>
|
|
146
|
-
<certificates src="system" />
|
|
147
|
-
</trust-anchors>
|
|
148
|
-
</base-config>
|
|
149
|
-
</network-security-config>
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
```xml
|
|
153
|
-
<!-- AndroidManifest.xml -->
|
|
154
|
-
<application
|
|
155
|
-
android:networkSecurityConfig="@xml/network_security_config"
|
|
156
|
-
... >
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
## Android Keystore (Runtime Key Storage)
|
|
160
|
-
|
|
161
|
-
For storing sensitive data at runtime (tokens, credentials), use Android Keystore — NOT JKS files:
|
|
162
|
-
|
|
163
|
-
```kotlin
|
|
164
|
-
// EncryptedSharedPreferences (recommended for key-value storage)
|
|
165
|
-
val masterKey = MasterKey.Builder(context)
|
|
166
|
-
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
|
|
167
|
-
.build()
|
|
168
|
-
|
|
169
|
-
val securePrefs = EncryptedSharedPreferences.create(
|
|
170
|
-
context, "secret_prefs", masterKey,
|
|
171
|
-
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
|
172
|
-
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
|
173
|
-
)
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
## Foreground Service Types (Android 14+)
|
|
177
|
-
|
|
178
|
-
Starting with Android 14 (API 34), every foreground service MUST declare a type:
|
|
179
|
-
|
|
180
|
-
```xml
|
|
181
|
-
<service
|
|
182
|
-
android:name=".MyService"
|
|
183
|
-
android:foregroundServiceType="dataSync" />
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
Valid types: `camera`, `connectedDevice`, `dataSync`, `health`, `location`, `mediaPlayback`, `microphone`, `phoneCall`, `shortService`, `specialUse`.
|
|
187
|
-
|
|
188
|
-
## Permissions Policy
|
|
189
|
-
|
|
190
|
-
```xml
|
|
191
|
-
<!-- Android 13+ notification permission -->
|
|
192
|
-
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
|
193
|
-
|
|
194
|
-
<!-- Android 13+ granular media permissions (replaces READ_EXTERNAL_STORAGE) -->
|
|
195
|
-
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
|
196
|
-
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
|
|
197
|
-
|
|
198
|
-
<!-- Use system photo picker instead of READ_MEDIA_IMAGES when possible -->
|
|
199
|
-
<!-- No permission needed: ActivityResultContracts.PickVisualMedia() -->
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
## Store Publishing Checklist
|
|
203
|
-
|
|
204
|
-
| Step | Description |
|
|
205
|
-
|------|-------------|
|
|
206
|
-
| 1. Register developer | Google Play ($25 one-time), Huawei (free), Xiaomi (free) |
|
|
207
|
-
| 2. Generate signed AAB | `./gradlew bundleRelease` |
|
|
208
|
-
| 3. Privacy policy | Required URL — must be accessible without login |
|
|
209
|
-
| 4. Data Safety section | Declare all data collection in Play Console (Mandatory) |
|
|
210
|
-
| 5. Data deletion | Apps with account creation MUST provide data deletion option |
|
|
211
|
-
| 6. App icon | 512x512 PNG |
|
|
212
|
-
| 7. Screenshots | Phone + tablet |
|
|
213
|
-
| 8. Description | App name, short description, full description |
|
|
214
|
-
| 9. Content rating | IARC rating questionnaire |
|
|
215
|
-
| 10. Target SDK | Must be API 35 (as of 2025/2026) |
|
|
216
|
-
| 11. Submit review | Google Play typically 1–3 days |
|
|
217
|
-
|
|
218
|
-
## Build Optimization
|
|
219
|
-
|
|
220
|
-
### ABI Splits (Reduce APK Size)
|
|
221
|
-
|
|
222
|
-
```kotlin
|
|
223
|
-
// build.gradle.kts
|
|
224
|
-
android {
|
|
225
|
-
splits {
|
|
226
|
-
abi {
|
|
227
|
-
isEnable = true
|
|
228
|
-
reset()
|
|
229
|
-
include("arm64-v8a", "armeabi-v7a", "x86_64")
|
|
230
|
-
isUniversalApk = false
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
### R8 Code Shrinking
|
|
237
|
-
|
|
238
|
-
```kotlin
|
|
239
|
-
// build.gradle.kts
|
|
240
|
-
android {
|
|
241
|
-
buildTypes {
|
|
242
|
-
release {
|
|
243
|
-
isMinifyEnabled = true
|
|
244
|
-
isShrinkResources = true
|
|
245
|
-
proguardFiles(
|
|
246
|
-
getDefaultProguardFile("proguard-android-optimize.txt"),
|
|
247
|
-
"proguard-rules.pro"
|
|
248
|
-
)
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
### Build Variant Management
|
|
255
|
-
|
|
256
|
-
```kotlin
|
|
257
|
-
// build.gradle.kts
|
|
258
|
-
android {
|
|
259
|
-
flavorDimensions += "env"
|
|
260
|
-
productFlavors {
|
|
261
|
-
create("dev") {
|
|
262
|
-
dimension = "env"
|
|
263
|
-
applicationIdSuffix = ".dev"
|
|
264
|
-
versionNameSuffix = "-dev"
|
|
265
|
-
}
|
|
266
|
-
create("staging") {
|
|
267
|
-
dimension = "env"
|
|
268
|
-
applicationIdSuffix = ".staging"
|
|
269
|
-
}
|
|
270
|
-
create("prod") {
|
|
271
|
-
dimension = "env"
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
## CI/CD — GitHub Actions
|
|
278
|
-
|
|
279
|
-
```yaml
|
|
280
|
-
name: Android Build
|
|
281
|
-
on:
|
|
282
|
-
push:
|
|
283
|
-
tags: ['v*']
|
|
284
|
-
jobs:
|
|
285
|
-
build:
|
|
286
|
-
runs-on: ubuntu-latest
|
|
287
|
-
steps:
|
|
288
|
-
- uses: actions/checkout@v4
|
|
289
|
-
- uses: actions/setup-java@v4
|
|
290
|
-
with:
|
|
291
|
-
distribution: 'temurin'
|
|
292
|
-
java-version: '17'
|
|
293
|
-
- name: Decode Keystore
|
|
294
|
-
run: echo "${{ secrets.RELEASE_KEYSTORE_BASE64 }}" | base64 --decode > app/release.jks
|
|
295
|
-
- name: Build AAB
|
|
296
|
-
env:
|
|
297
|
-
KEYSTORE_PATH: release.jks
|
|
298
|
-
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
|
|
299
|
-
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
|
|
300
|
-
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
|
|
301
|
-
run: ./gradlew bundleRelease
|
|
302
|
-
- uses: actions/upload-artifact@v4
|
|
303
|
-
with:
|
|
304
|
-
name: aab
|
|
305
|
-
path: app/build/outputs/bundle/release/*.aab
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
## Common Pitfalls
|
|
309
|
-
|
|
310
|
-
| Issue | Fix |
|
|
311
|
-
|-------|-----|
|
|
312
|
-
| "Namespace not specified" | Add `namespace = "com.example.app"` in build.gradle (AGP 8.x requires it) |
|
|
313
|
-
| Signing error | Check environment variables; never hardcode passwords |
|
|
314
|
-
| ProGuard crash | Add `-keep` rules for models, native modules, serialization |
|
|
315
|
-
| Target SDK rejected | Google Play requires `targetSdk = 35` as of 2025 |
|
|
316
|
-
| 16KB alignment error | Add `pageAlignSharedLibs = true` in build.gradle |
|
|
317
|
-
| Foreground service crash | Declare `foregroundServiceType` in AndroidManifest (API 34+) |
|
|
318
|
-
| Data Safety rejection | Complete Data Safety section in Play Console |
|
|
319
|
-
| Privacy policy rejected | URL must be live, accessible without login, match Data Safety declaration |
|
|
320
|
-
| Cleartext traffic blocked | Android 9+ blocks HTTP by default; use HTTPS or configure network_security_config |
|
|
321
|
-
| `READ_EXTERNAL_STORAGE` not working | Android 13+ uses `READ_MEDIA_IMAGES`/`READ_MEDIA_VIDEO` instead |
|
|
322
|
-
| Play App Signing key lost | Upload key can be reset in Play Console; signing key is managed by Google |
|
|
1
|
+
# Android Native Build Sub-Skill
|
|
2
|
+
|
|
3
|
+
Build and publish Kotlin/Java native Android applications.
|
|
4
|
+
|
|
5
|
+
**Current version**: Android 15 (API 35) / AGP 8.7.x / Gradle 8.10+ / JDK 17 (2025-2026)
|
|
6
|
+
|
|
7
|
+
## When to Use
|
|
8
|
+
|
|
9
|
+
- Native Android applications (Kotlin recommended, Java supported)
|
|
10
|
+
- Apps requiring deep Android platform integration
|
|
11
|
+
- Apps published to Google Play, Huawei AppGallery, or Xiaomi GetApps
|
|
12
|
+
|
|
13
|
+
## Prerequisites
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# JDK 17 (REQUIRED for AGP 8.x)
|
|
17
|
+
# Android Studio (includes JDK 17 bundled as JBR)
|
|
18
|
+
# OR: standalone JDK 17
|
|
19
|
+
|
|
20
|
+
# Android SDK 35 (via Android Studio SDK Manager)
|
|
21
|
+
# NDK (if using native code)
|
|
22
|
+
|
|
23
|
+
# Verify
|
|
24
|
+
java -version # Should show 17.x
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Build
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
./gradlew assembleDebug # Debug APK
|
|
31
|
+
./gradlew assembleRelease # Release APK
|
|
32
|
+
./gradlew bundleRelease # AAB (required by Google Play)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Required Build Configuration (AGP 8.x)
|
|
36
|
+
|
|
37
|
+
```kotlin
|
|
38
|
+
// build.gradle.kts (app-level)
|
|
39
|
+
android {
|
|
40
|
+
namespace = "com.example.myapp" // REQUIRED since AGP 8.0 (replaces package in AndroidManifest)
|
|
41
|
+
compileSdk = 35 // Should match latest API level
|
|
42
|
+
|
|
43
|
+
defaultConfig {
|
|
44
|
+
applicationId = "com.example.myapp"
|
|
45
|
+
minSdk = 24 // Android 7.0
|
|
46
|
+
targetSdk = 35 // REQUIRED: Google Play mandates API 35 as of 2025
|
|
47
|
+
versionCode = 1
|
|
48
|
+
versionName = "1.0.0"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
```xml
|
|
54
|
+
<!-- AndroidManifest.xml — REMOVE package attribute (AGP 8.x) -->
|
|
55
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
56
|
+
<!-- NO package="..." attribute here -->
|
|
57
|
+
</manifest>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### gradle.properties (Modern Defaults)
|
|
61
|
+
|
|
62
|
+
```properties
|
|
63
|
+
# gradle.properties
|
|
64
|
+
android.useAndroidX=true
|
|
65
|
+
android.nonTransitiveRClass=true
|
|
66
|
+
org.gradle.jvmargs=-Xmx4g -XX:+UseG1GC
|
|
67
|
+
org.gradle.parallel=true
|
|
68
|
+
org.gradle.caching=true
|
|
69
|
+
org.gradle.configuration-cache=true
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Signing (Secure Approach)
|
|
73
|
+
|
|
74
|
+
### Generate Upload Key
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
keytool -genkey -v -keystore upload-key.jks \
|
|
78
|
+
-keyalg RSA -keysize 2048 -validity 10000 \
|
|
79
|
+
-alias upload
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Secure Signing Configuration
|
|
83
|
+
|
|
84
|
+
```kotlin
|
|
85
|
+
// build.gradle.kts — NEVER hardcode passwords
|
|
86
|
+
signingConfigs {
|
|
87
|
+
create("release") {
|
|
88
|
+
storeFile = file(System.getenv("KEYSTORE_PATH") ?: "../keystore/upload-key.jks")
|
|
89
|
+
storePassword = System.getenv("KEYSTORE_PASSWORD") ?: ""
|
|
90
|
+
keyAlias = System.getenv("KEY_ALIAS") ?: ""
|
|
91
|
+
keyPassword = System.getenv("KEY_PASSWORD") ?: ""
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
buildTypes {
|
|
96
|
+
release {
|
|
97
|
+
signingConfig = signingConfigs.getByName("release")
|
|
98
|
+
isMinifyEnabled = true
|
|
99
|
+
isShrinkResources = true
|
|
100
|
+
proguardFiles(
|
|
101
|
+
getDefaultProguardFile("proguard-android-optimize.txt"),
|
|
102
|
+
"proguard-rules.pro"
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Play App Signing (Recommended)
|
|
109
|
+
|
|
110
|
+
Google Play App Signing separates the **upload key** (you hold) from the **signing key** (Google holds):
|
|
111
|
+
|
|
112
|
+
- **Upload key**: Signs your AAB before uploading. If lost, can be reset in Play Console.
|
|
113
|
+
- **Signing key**: Signs the final APK delivered to users. Google manages this securely.
|
|
114
|
+
- Enroll in Google Play Console → Release → Setup → App signing
|
|
115
|
+
|
|
116
|
+
## 16KB Page Alignment (Android 15 Requirement)
|
|
117
|
+
|
|
118
|
+
Android 15 devices may use 16KB memory pages. All native libraries (.so files) must be 16KB aligned.
|
|
119
|
+
|
|
120
|
+
```kotlin
|
|
121
|
+
// build.gradle.kts
|
|
122
|
+
android {
|
|
123
|
+
packaging {
|
|
124
|
+
jniLibs {
|
|
125
|
+
pageAlignSharedLibs = true // AGP 8.5+
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# Check alignment
|
|
133
|
+
unzip -l app.apk | grep "\.so$"
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**Apps without native code are NOT affected.**
|
|
137
|
+
|
|
138
|
+
## Network Security Config
|
|
139
|
+
|
|
140
|
+
```xml
|
|
141
|
+
<!-- res/xml/network_security_config.xml -->
|
|
142
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
143
|
+
<network-security-config>
|
|
144
|
+
<base-config cleartextTrafficPermitted="false">
|
|
145
|
+
<trust-anchors>
|
|
146
|
+
<certificates src="system" />
|
|
147
|
+
</trust-anchors>
|
|
148
|
+
</base-config>
|
|
149
|
+
</network-security-config>
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
```xml
|
|
153
|
+
<!-- AndroidManifest.xml -->
|
|
154
|
+
<application
|
|
155
|
+
android:networkSecurityConfig="@xml/network_security_config"
|
|
156
|
+
... >
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Android Keystore (Runtime Key Storage)
|
|
160
|
+
|
|
161
|
+
For storing sensitive data at runtime (tokens, credentials), use Android Keystore — NOT JKS files:
|
|
162
|
+
|
|
163
|
+
```kotlin
|
|
164
|
+
// EncryptedSharedPreferences (recommended for key-value storage)
|
|
165
|
+
val masterKey = MasterKey.Builder(context)
|
|
166
|
+
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
|
|
167
|
+
.build()
|
|
168
|
+
|
|
169
|
+
val securePrefs = EncryptedSharedPreferences.create(
|
|
170
|
+
context, "secret_prefs", masterKey,
|
|
171
|
+
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
|
172
|
+
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
|
173
|
+
)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Foreground Service Types (Android 14+)
|
|
177
|
+
|
|
178
|
+
Starting with Android 14 (API 34), every foreground service MUST declare a type:
|
|
179
|
+
|
|
180
|
+
```xml
|
|
181
|
+
<service
|
|
182
|
+
android:name=".MyService"
|
|
183
|
+
android:foregroundServiceType="dataSync" />
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Valid types: `camera`, `connectedDevice`, `dataSync`, `health`, `location`, `mediaPlayback`, `microphone`, `phoneCall`, `shortService`, `specialUse`.
|
|
187
|
+
|
|
188
|
+
## Permissions Policy
|
|
189
|
+
|
|
190
|
+
```xml
|
|
191
|
+
<!-- Android 13+ notification permission -->
|
|
192
|
+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
|
193
|
+
|
|
194
|
+
<!-- Android 13+ granular media permissions (replaces READ_EXTERNAL_STORAGE) -->
|
|
195
|
+
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
|
196
|
+
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
|
|
197
|
+
|
|
198
|
+
<!-- Use system photo picker instead of READ_MEDIA_IMAGES when possible -->
|
|
199
|
+
<!-- No permission needed: ActivityResultContracts.PickVisualMedia() -->
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Store Publishing Checklist
|
|
203
|
+
|
|
204
|
+
| Step | Description |
|
|
205
|
+
|------|-------------|
|
|
206
|
+
| 1. Register developer | Google Play ($25 one-time), Huawei (free), Xiaomi (free) |
|
|
207
|
+
| 2. Generate signed AAB | `./gradlew bundleRelease` |
|
|
208
|
+
| 3. Privacy policy | Required URL — must be accessible without login |
|
|
209
|
+
| 4. Data Safety section | Declare all data collection in Play Console (Mandatory) |
|
|
210
|
+
| 5. Data deletion | Apps with account creation MUST provide data deletion option |
|
|
211
|
+
| 6. App icon | 512x512 PNG |
|
|
212
|
+
| 7. Screenshots | Phone + tablet |
|
|
213
|
+
| 8. Description | App name, short description, full description |
|
|
214
|
+
| 9. Content rating | IARC rating questionnaire |
|
|
215
|
+
| 10. Target SDK | Must be API 35 (as of 2025/2026) |
|
|
216
|
+
| 11. Submit review | Google Play typically 1–3 days |
|
|
217
|
+
|
|
218
|
+
## Build Optimization
|
|
219
|
+
|
|
220
|
+
### ABI Splits (Reduce APK Size)
|
|
221
|
+
|
|
222
|
+
```kotlin
|
|
223
|
+
// build.gradle.kts
|
|
224
|
+
android {
|
|
225
|
+
splits {
|
|
226
|
+
abi {
|
|
227
|
+
isEnable = true
|
|
228
|
+
reset()
|
|
229
|
+
include("arm64-v8a", "armeabi-v7a", "x86_64")
|
|
230
|
+
isUniversalApk = false
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### R8 Code Shrinking
|
|
237
|
+
|
|
238
|
+
```kotlin
|
|
239
|
+
// build.gradle.kts
|
|
240
|
+
android {
|
|
241
|
+
buildTypes {
|
|
242
|
+
release {
|
|
243
|
+
isMinifyEnabled = true
|
|
244
|
+
isShrinkResources = true
|
|
245
|
+
proguardFiles(
|
|
246
|
+
getDefaultProguardFile("proguard-android-optimize.txt"),
|
|
247
|
+
"proguard-rules.pro"
|
|
248
|
+
)
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Build Variant Management
|
|
255
|
+
|
|
256
|
+
```kotlin
|
|
257
|
+
// build.gradle.kts
|
|
258
|
+
android {
|
|
259
|
+
flavorDimensions += "env"
|
|
260
|
+
productFlavors {
|
|
261
|
+
create("dev") {
|
|
262
|
+
dimension = "env"
|
|
263
|
+
applicationIdSuffix = ".dev"
|
|
264
|
+
versionNameSuffix = "-dev"
|
|
265
|
+
}
|
|
266
|
+
create("staging") {
|
|
267
|
+
dimension = "env"
|
|
268
|
+
applicationIdSuffix = ".staging"
|
|
269
|
+
}
|
|
270
|
+
create("prod") {
|
|
271
|
+
dimension = "env"
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## CI/CD — GitHub Actions
|
|
278
|
+
|
|
279
|
+
```yaml
|
|
280
|
+
name: Android Build
|
|
281
|
+
on:
|
|
282
|
+
push:
|
|
283
|
+
tags: ['v*']
|
|
284
|
+
jobs:
|
|
285
|
+
build:
|
|
286
|
+
runs-on: ubuntu-latest
|
|
287
|
+
steps:
|
|
288
|
+
- uses: actions/checkout@v4
|
|
289
|
+
- uses: actions/setup-java@v4
|
|
290
|
+
with:
|
|
291
|
+
distribution: 'temurin'
|
|
292
|
+
java-version: '17'
|
|
293
|
+
- name: Decode Keystore
|
|
294
|
+
run: echo "${{ secrets.RELEASE_KEYSTORE_BASE64 }}" | base64 --decode > app/release.jks
|
|
295
|
+
- name: Build AAB
|
|
296
|
+
env:
|
|
297
|
+
KEYSTORE_PATH: release.jks
|
|
298
|
+
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
|
|
299
|
+
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
|
|
300
|
+
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
|
|
301
|
+
run: ./gradlew bundleRelease
|
|
302
|
+
- uses: actions/upload-artifact@v4
|
|
303
|
+
with:
|
|
304
|
+
name: aab
|
|
305
|
+
path: app/build/outputs/bundle/release/*.aab
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
## Common Pitfalls
|
|
309
|
+
|
|
310
|
+
| Issue | Fix |
|
|
311
|
+
|-------|-----|
|
|
312
|
+
| "Namespace not specified" | Add `namespace = "com.example.app"` in build.gradle (AGP 8.x requires it) |
|
|
313
|
+
| Signing error | Check environment variables; never hardcode passwords |
|
|
314
|
+
| ProGuard crash | Add `-keep` rules for models, native modules, serialization |
|
|
315
|
+
| Target SDK rejected | Google Play requires `targetSdk = 35` as of 2025 |
|
|
316
|
+
| 16KB alignment error | Add `pageAlignSharedLibs = true` in build.gradle |
|
|
317
|
+
| Foreground service crash | Declare `foregroundServiceType` in AndroidManifest (API 34+) |
|
|
318
|
+
| Data Safety rejection | Complete Data Safety section in Play Console |
|
|
319
|
+
| Privacy policy rejected | URL must be live, accessible without login, match Data Safety declaration |
|
|
320
|
+
| Cleartext traffic blocked | Android 9+ blocks HTTP by default; use HTTPS or configure network_security_config |
|
|
321
|
+
| `READ_EXTERNAL_STORAGE` not working | Android 13+ uses `READ_MEDIA_IMAGES`/`READ_MEDIA_VIDEO` instead |
|
|
322
|
+
| Play App Signing key lost | Upload key can be reset in Play Console; signing key is managed by Google |
|