~ knotless Search/
↑↓ move↵ open⇧↵ all results

grain.social/grain-android

1 collaborator
0
1
main
96 lines · 3.8 KB Raw History

Releasing

One-time setup

1. Generate an upload key

Google holds the real app signing key under Play App Signing and re-signs every release. What you generate here is only the upload key — if it's lost, Play support can reset it, which is not true of the app signing key.

keytool -genkeypair -v \
  -keystore upload-keystore.jks \
  -alias upload \
  -keyalg RSA -keysize 2048 -validity 10000

Then create keystore.properties in the repo root:

storeFile=upload-keystore.jks
storePassword=<store password>
keyAlias=upload
keyPassword=<key password>

Both files are gitignored. Keep a backup somewhere other than this machine — without them you can still recover, but only by going through Play support.

Release builds without keystore.properties are left unsigned rather than failing, so a fresh clone and CI can still compile the release variant.

2. Play Console

  • Create the app. The package name social.grain is permanent once the first build is uploaded and cannot be changed afterwards.
  • Under Setup → App signing, opt into Play App Signing (the default for new apps) and let Google generate the app signing key.
  • Fill in the Data safety form. The app collects the account handle and DID, the photos a user chooses to publish, and their EXIF when the poster leaves camera data enabled. All of it goes to the user's own PDS via grain.social. It requests INTERNET and, for the story composer, CAMERA; photos taken there are only ever sent when the user posts them.
  • Content rating questionnaire, target audience, and a privacy policy URL are all required before any track can go live.

Note for personal developer accounts created after 13 Nov 2023: production is locked until a closed test has run with at least 12 testers opted in for 14 continuous days. Check Testing → Closed testing — a requirement card there means the rule applies.

Cutting a release

Bump versionCode and versionName in app/build.gradle.kts. versionCode must increase with every upload, even for a build that never ships.

./gradlew bundleRelease

The bundle lands at app/build/outputs/bundle/release/app-release.aab. Upload that, not the APK — Play requires the App Bundle format.

To sanity-check a release build on a device first, the APK variant can be signed with the debug key and installed locally:

./gradlew assembleRelease
zipalign -f 4 app/build/outputs/apk/release/app-release-unsigned.apk /tmp/aligned.apk
apksigner sign --ks ~/.android/debug.keystore --ks-pass pass:android \
  --ks-key-alias androiddebugkey --key-pass pass:android \
  --out /tmp/release-test.apk /tmp/aligned.apk
adb install -r /tmp/release-test.apk

This is worth doing at least once per dependency bump. R8 strips kotlinx.serialization's generated $$serializer classes without the keep rules in proguard-rules.pro, and the failure is release-only: every network decode throws and the feed simply comes back empty.

Store assets

  • Icon: store/play-store-icon-512.png (regenerate with python3 tools/generate_icons.py).

  • Feature graphic: store/play-feature-graphic-1024x500.png (regenerate with python3 tools/generate_feature_graphic.py).

  • Screenshots: store/play-screenshot-*.png. Capture with adb exec-out screencap -p > shot.png, then fit them to Play's rules with python3 tools/prepare_screenshots.py shot.png --out store/ — a raw capture is 2.06:1, which is past the 2:1 Play accepts, and the script pads each to 1080x1920 rather than cropping the app's own chrome away.

    Sign in on an emulator rather than using a phone: a locked screen returns a black screencap and blocks instrumented tests. Use your own galleries — the feed shows other people's photographs, which aren't yours to put in a store listing.