6.4 KiB
6.4 KiB
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
- Application id chosen:
de.softwareapp_hb.filemanager(applicationIdinapp/build.gradle.kts). It can never be changed after the first release, so treat it as locked.namespaceand the Kotlin package of the same name are aligned with it; debug builds append.debug. - [code] Upload keystore generated —
filemanager-upload.jksin 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 - [code]
keystore.propertieswritten (git-ignored, mode600). 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
versionName[code] —1.0.0inapp/build.gradle.kts.versionCode[code] —1. Play orders uploads by it, so every later upload must be higher.
3. Build the release artefacts
./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
- [code] verified 2026-10-14.
bundleReleaseandassembleReleaseboth succeed with R8 minification and resource shrinking on. -app-release.aabis 4 682 814 bytes (sha256 9014627a8596397623404cb9d986af5019c5e33cf331c5d363427c140f121788). -app-release.apkis 2 081 955 bytes, signed by the upload key above. - Both reportversionCode=1 versionName=1.0.0 targetSdk=36and exactly the permission set in step 4 — noINTERNET, 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 findsbase/lib/<abi>/libandroidx.graphics.path.so, which arrives transitively throughandroidx.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 nondk/externalNativeBuild/jniLibsconfiguration and no native sources of its own. - Keep
app/build/outputs/mapping/release/mapping.txtfor 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_PACKAGESorINTERNETwithout declaring them. The app currently requests none of them. - Provide the privacy policy URL (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:
$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.
- 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.BILLINGpermission in the APK. The price lives on the listing, not in the build.