Files
file-manager/docs/PLAY_RELEASE_CHECKLIST.md
T

130 lines
6.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Google Play release checklist
Work top to bottom. Items marked **[code]** are already handled in this repository; the
rest are things you must do in the Play Console or with your own accounts.
## 1. Identity and signing
- [x] **Application id** chosen: `de.softwareapp_hb.filemanager`
(`applicationId` in `app/build.gradle.kts`). It can **never be changed after the first
release**, so treat it as locked. `namespace` and the Kotlin package of the same name
are aligned with it; debug builds append `.debug`.
- [x] **[code] Upload keystore generated** — `filemanager-upload.jks` in the project root
(git-ignored, `alias upload`, 4096-bit RSA, 10 000 days, `CN=Hadrian Burkhardt,
L=Potsdam, C=DE`). Certificate SHA-256 fingerprint, to confirm the right key is
registered when Play App Signing is enabled:
`49:A9:FC:13:F5:13:24:48:AE:74:5A:AE:7E:05:D9:2F:67:55:1F:EE:71:A5:64:A1:10:13:57:4D:0E:AA:0E:4E`
- [x] **[code] `keystore.properties` written** (git-ignored, mode `600`). The Gradle build
picks it up automatically and signs release builds; without it the release APK stays
unsigned.
- [ ] **Store the keystore and its password in a password manager and back it up.** Losing
the upload key means you cannot ship updates.
- [ ] Enable **Play App Signing** in the Play Console with the first upload. Google then
holds the app signing key and this one only signs uploads:
```bash
keytool -exportcert -alias upload -keystore filemanager-upload.jks -rfc > upload_cert.pem
```
## 2. Versioning
- [x] `versionName` **[code]** — `1.0.0` in `app/build.gradle.kts`.
- [x] `versionCode` **[code]** — `1`. Play orders uploads by it, so every later upload must
be higher.
## 3. Build the release artefacts
```bash
./gradlew :app:bundleRelease # -> app/build/outputs/bundle/release/app-release.aab
./gradlew :app:assembleRelease # -> APK, for local checks
./gradlew :app:testDebugUnitTest # JVM unit tests
./gradlew :app:lintDebug # Android Lint
```
- [x] **[code] verified 2026-10-14.** `bundleRelease` and `assembleRelease` both succeed
with R8 minification and resource shrinking on.
- `app-release.aab` is **4 682 814 bytes**
(`sha256 9014627a8596397623404cb9d986af5019c5e33cf331c5d363427c140f121788`).
- `app-release.apk` is **2 081 955 bytes**, signed by the upload key above.
- Both report `versionCode=1 versionName=1.0.0 targetSdk=36` and exactly the
permission set in step 4 — **no `INTERNET`**, so "nothing leaves the device" holds
in the shipped artefact and not just in the source manifest.
- [ ] Upload the **`.aab`** (not the APK) to the Play Console.
⚠️ **Expect a native-debug-symbols warning and ignore it.** Play finds
`base/lib/<abi>/libandroidx.graphics.path.so`, which arrives transitively through
`androidx.compose.ui:ui-graphics` → `androidx.graphics:graphics-path`. It is a
recommendation, not an error: it does not block upload, review or rollout. This
project has **no** `ndk`/`externalNativeBuild`/`jniLibs` configuration and no native
sources of its own.
- [ ] Keep `app/build/outputs/mapping/release/mapping.txt` for every release so Play can
de-obfuscate crash reports.
## 4. Sensitive permissions — the Permissions Declaration Form
This is the step that can block a file manager, and the one to get right.
- [ ] In the Play Console open **App content → Sensitive app permissions** (older UI:
*Permissions Declaration Form*) and declare **All files access**
(`MANAGE_EXTERNAL_STORAGE`).
- [ ] Select the **file manager** use case. Google's policy — *Use of All files access
(MANAGE_EXTERNAL_STORAGE) permission* — names file managers explicitly:
*"intended and permitted use includes file managers, backup and restore apps,
anti-virus apps, and document management apps."*
- [ ] Make sure the **store description states the core purpose** (browsing, organising and
managing files on the device). The policy requires the core functionality to be
"prominently documented and promoted in the app's description" — the listing copy in
`docs/STORE_LISTING.md` (M10) is written to satisfy that.
- [ ] **Do not** add `QUERY_ALL_PACKAGES`, `REQUEST_INSTALL_PACKAGES` or `INTERNET` without
declaring them. The app currently requests none of them.
- [ ] Provide the **privacy policy URL** ([PRIVACY.md](../PRIVACY.md) — it must be a public
URL, not a file in the repo).
- [ ] Keep the permission set honest: declaring a permission the app does not use is itself
a policy violation.
**Verify the shipped permission set** after every release build:
```bash
$ANDROID_HOME/build-tools/36.0.0/aapt2 dump permissions \
app/build/outputs/apk/release/app-release.apk
```
Expected, and nothing else:
```
MANAGE_EXTERNAL_STORAGE
READ_EXTERNAL_STORAGE (maxSdkVersion=32)
READ_MEDIA_IMAGES
READ_MEDIA_VIDEO
READ_MEDIA_AUDIO
READ_MEDIA_VISUAL_USER_SELECTED
WRITE_EXTERNAL_STORAGE (maxSdkVersion=29)
FOREGROUND_SERVICE
FOREGROUND_SERVICE_DATA_SYNC
POST_NOTIFICATIONS
<applicationId>.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION (added by AndroidX)
```
## 5. Data safety, content rating, audience
- [ ] **Data safety** form: the app collects **nothing** and shares **nothing**. Answer "No"
to collection, "No" to sharing, and state that data is not transmitted off the device.
- [ ] Confirm the answers match [PRIVACY.md](../PRIVACY.md).
- [ ] Content rating questionnaire: utility, no user-generated content, no ads.
- [ ] Target audience: not directed at children.
## 6. Store listing
Full copy and the generated graphics land in **M10** (`docs/STORE_LISTING.md`). Console
fields needed: app name, short description, full description, app icon 512×512, feature
graphic 1024×500, and at least 4 phone screenshots (Play asks for 4+ per large-screen form
factor too).
## 7. Release tracks and pricing
- [ ] Create the app, complete *App content*, then roll out
**internal → closed → production**, checking the pre-launch report on the way.
- [ ] ⚠️ **Set the price before the first upload** (M9). Once an app has been offered for
free it can never be made paid, and this app is a **one-time paid** app: no ads, no
subscription, no in-app purchases — and therefore no Billing code and no
`com.android.vending.BILLING` permission in the APK. The price lives on the listing,
not in the build.