Google Play Listing
Purpose
Guide the Android signing and Google Play Store publication process — from keystore generation through to internal testing and CI automation — for any Android app that produces an APK or AAB regardless of the build framework used.
When to use
After the app builds successfully and produces an unsigned APK or AAB. Apply when setting up code signing for the first time, when preparing a first Play Store release, or when automating AAB uploads from CI. For iOS/App Store submission, enrol at developer.apple.com/programs/enroll ($99 USD/year) — that process is not covered here.
Inputs expected
- Working unsigned APK or AAB from any Android build pipeline
- Google Play Developer account (existing or to be created)
- App name, package name (bundle ID), version, and build number confirmed
- Privacy policy URL (live and publicly accessible) — required before any release is promoted
- App description, screenshots, and app icon prepared
Guiding principles
- The Android upload keystore is permanent. Once uploaded to Google Play, the keystore must be kept forever — losing it means creating a new Play Store listing. Store the base64 value, passwords, and alias in a password manager immediately after generation.
- Required GitHub Secrets for signed builds:
ANDROID_KEYSTORE_BASE64,ANDROID_KEY_ALIAS,ANDROID_KEY_PASSWORD,ANDROID_STORE_PASSWORD. These must be set before the signed build workflow runs. PKCS12 format uses one password for store and key — set them identically. - Upload a signed AAB, not APK, to Google Play. Gate the AAB build on
workflow_dispatch— it should only run when intentionally preparing a release, not on every push. - Google Play org accounts do not require a D-U-N-S number. Unlike Apple, Google does not require D-U-N-S for organisation accounts. The one-time $25 USD fee is the only requirement. The "developer name" is the public-facing name shown on every listing — use your trading name exactly as customers should see it.
- Privacy policy must be a publicly accessible URL. Play Console will not accept a file upload or email address. Host it on your website or use a privacy policy generator service.
- Account deletion must be offered both in-app and online, and declared in Data Safety. For any app with account creation, Google Play requires both an in-app deletion path and an online (web) deletion request URL, and you must declare account deletion in the Data Safety form. The Play listing publicly surfaces whether the app offers account deletion, so a missing declaration is visible to users.
- Use dedicated reviewer credentials, not a real user account. Create a shared mailbox (e.g.
app_reviewer@yourdomain.com) and a dedicated app account using that email for Google's review team. - GitHub free tier artifact storage fills quickly with APK/AAB builds. Use
gh release createto publish binaries as GitHub Releases instead — Release assets do not count against the Actions artifact storage quota. - When migrating frameworks, match the bundle ID exactly and prepare testers for a full reinstall. Replacing one build framework (e.g. Flet) with another (e.g. Expo) in an existing Play Store listing requires the new app's bundle ID to match the original exactly. The signing key will differ between frameworks — existing device installs require a full uninstall before the new build can be installed. The Play Store listing itself is unaffected.
- Resetting the upload key triggers a mandatory ~2 day server-enforced wait. After requesting an upload key reset, the new key is not valid until the waiting period passes. There is no workaround — both manual AAB uploads and automated CI deploys fail until the window closes. Plan key rotations around this delay.
- A draft release with no AAB shows −100% device support — this is an artefact, not a regression. The figure comes from comparing an empty draft against the previous release. Discard the empty draft and return once a valid AAB is attached.
- Chrome DevTools device emulation produces store-ready screenshots without a physical device. Set a custom device using CSS pixel dimensions (not physical pixels) and a DPR that multiplies up to the required output resolution, then Ctrl+Shift+P → "Capture screenshot" exports at full physical resolution. Setting the viewport to physical pixels at DPR 1 renders content tiny — CSS pixels × DPR = physical output is the rule.
- Promoting Internal Testing → Production reuses the tested bundle — no re-upload. The "Create production release" page pre-populates with the tested bundle. Countries/regions are configured at the track level (Production → Countries/regions), not per release — the release page errors if no countries are set at track level.
- The
r0adkll/upload-google-playaction requires awhatsNewDirectory. Point it at a directory containing release-notes files (e.g.whatsnew/whatsnew-en-AU). If the directory does not exist, the deploy job fails — create it with at least one locale file before the first deploy. - The 512×512 store-listing icon must be full-bleed square art with no baked rounding, bevel or shadow. Google Play applies its OWN corner-rounding mask, so artwork with a baked rounded tile renders as an icon-within-an-icon. If only the tiled artwork exists, composite it full-bleed on a solid ground and zoom (~1.25x) so the baked corner/shadow edges fall outside the frame; the adaptive icon layers (foreground/background) are separate and handled differently.
- The 1024×500 feature graphic is a REQUIRED store-listing asset (the listing won't complete without it) even though Play only displays it when the app is featured/promoted. A simple programmatic composition — brand gradient, app icon tile, wordmark, tagline matching the short description — is perfectly acceptable and can be generated in minutes rather than commissioned.
- Play phone screenshots must be 16:9 or 9:16 (sides 320–3840px) — modern tall-phone captures (19.5:9) and 3:4 tablet-ish captures don't qualify, and promotion eligibility additionally needs at least 4 shots at 1080px+. Capture exact 1080×1920 from a web build with Chrome DevTools at CSS dimensions = target ÷ DPR, picking a DPR that preserves PHONE density — 540×960@2 renders airy/tablet-like; 360×640@3 or 432×768@2.5 look like a real phone.
- Label AI-generated imagery per asset in the review step's AI declaration. Label every listing asset containing photorealistic AI-generated imagery — including AI-generated photos APPEARING INSIDE otherwise-genuine UI screenshots — while plain UI captures with no photographic content need no label. Over-labelling has no user-visible penalty; under-labelling risks a mislabelling flag during review, so when provenance is uncertain, label.
- For a sensitive-health app, keep condition-specific language out of store assets even when it appears in-app: frame/crop screenshots so UI naming the condition stays out of shot, keep the supportive-tool disclaimer and a crisis line in the description, and keep listing copy to the neutral framing — this serves claim/regulatory discipline AND user privacy (a store listing is shoulder-surfable). Store screenshots come from seeded demo data, never real accounts.
- "Send app for review" stays locked until a release is SAVED on a reviewable track with at least one country selected. For a new app, Publishing overview shows "complete the required steps in the app dashboard" until then — internal-testing uploads don't count, and a draft release left on an error (e.g. no countries) keeps the lock on. Once saved, submission bundles the release, listing and all App Content declarations into one review package, preceded by ~15 min of automatic "quick checks".
- Turn Managed publishing ON before submitting a release queued to "Start full rollout". With it off, Google's approval auto-publishes to the store; with it on, the approved release parks under "Changes ready to publish" until you click — Play's equivalent of Apple's "Manually release this version" for holding go-live behind sign-off.
- Promote an uploaded AAB via "Add from library", never re-upload. An AAB uploaded to any track lives in the app's bundle library; re-uploading the same version code is rejected as a duplicate. The "no deobfuscation file" warning on a first Expo/EAS bundle is advisory only, not a submission blocker.
Process
Android signing setup
Generate the upload keystore in CI to avoid requiring Java/keytool locally. Add
ANDROID_STORE_PASSWORDandANDROID_KEY_PASSWORDas repo secrets first, then run a one-timeworkflow_dispatchworkflow: See Generate-keystore workflow inreference.md. Copy the base64 output → add asANDROID_KEYSTORE_BASE64secret. Also addANDROID_KEY_ALIAS(e.g.upload). Store all four secrets in a password manager before closing the browser tab. Delete the workflow file from the repo after use.Decode the keystore and pass signing env vars in the release workflow — see Signed-build step in
reference.md.
Google Play Console setup
Create a Google Play Developer account at play.google.com/console ($25 USD one-off, no annual fee). Register as an Organisation account if publishing under a company name — no D-U-N-S number required. The "developer name" is public-facing; the "experience description" (how you plan to use the Play Store) is not visible to users.
Set up Internal Testing: go to Testing → Internal testing, create a release, upload the signed AAB. Under the Testers tab, create an email list and add tester addresses (must be Google accounts). Copy the opt-in URL and share it — testers must open it on their Android device before the app appears in their Play Store. Internal Testing has no review process — the release goes live to testers immediately after promotion.
Store listing
- Complete the store listing — key fields and character limits:
- App name: 50 characters max — title shown on the Play Store
- Short description: 80 characters max — shown in search results and top of listing
- Full description: 4,000 characters max — shown under "About this app"
- App icon: 512×512 PNG, max 1 MB
- Feature graphic: 1024×500 PNG or JPEG — shown at top of listing when promoted; required even if not featured
- Screenshots: minimum 2 phone screenshots; 7-inch and 10-inch tablet screenshots can reuse phone screenshots — Play Console accepts this
App Content declarations
Complete all App Content declarations before promoting any release:
- Privacy policy: public URL only — required; not a file or email
- Ads: declare whether the app contains ads; "No ads" requires no justification
- App access: if login is required to access core functionality, provide reviewer credentials (see Step 7)
- Content ratings: complete the IARC questionnaire — for a general lifestyle/entertainment app: category Entertainment, Violence: None, Sexual content: None. IARC generates ratings for all regions automatically; individual regional ratings cannot be overridden.
- Target audience: declare minimum age. Selecting 18+ excludes the app from children's content policies.
- Data safety: declare data collected. For an app with Supabase authentication: Email address (required, account management), Name (optional, personalisation), User IDs (required, analytics), App interactions (required, analytics). Data shared with third parties: None — Supabase is your backend, not a third party. Encrypted in transit: Yes (Supabase uses TLS). Include a data deletion URL if any account data is collected.
Add reviewer credentials under App access → Restricted access → Manage instructions: create a shared mailbox (e.g.
app_reviewer@yourdomain.com), create a dedicated app account using that email, and enter the credentials. Avoid backticks and quotes in the password — they can break copy-paste in the Play Console form.
CI automation
- Automate AAB uploads to Play Console using
r0adkll/upload-google-play:- In Play Console → Setup → API access, link a Google Cloud project and create a service account with "Release manager" role. Download the JSON key file.
- Add the JSON as
GOOGLE_PLAY_SERVICE_ACCOUNT_JSONin GitHub Secrets. - Grant the service account email access in Play Console under Users and permissions with "Release apps to testing tracks" permission at minimum.
Workflow step: see Upload-to-Play step in
reference.md.trackacceptsinternal,alpha,beta, orproduction.status: completedmakes it live immediately;status: draftrequires manual promotion in Play Console.
Output format
- Signing setup summary — keystore alias, GitHub Secrets list, workflow changes needed
- Signed build workflow snippet — AAB build step with keystore decode and env vars
- Play Console checklist — store listing fields completed, App Content declarations, reviewer credentials
- CI automation config — r0adkll/upload-google-play workflow step with correct track and package name
Quality checklist
-
ANDROID_KEYSTORE_BASE64,ANDROID_KEY_ALIAS,ANDROID_KEY_PASSWORD,ANDROID_STORE_PASSWORDall set in GitHub Secrets - Keystore base64, passwords, and alias stored in password manager immediately after generation
- Generate-keystore workflow deleted from repo after use
- AAB build gated on
workflow_dispatch— not triggered on every push - App name uses trading name exactly as customers should see it (50 chars max)
- Privacy policy URL is live and publicly accessible (not an email or file)
- Feature graphic (1024×500) uploaded — even if not promoting
- All App Content declarations completed before first release promotion
- Account deletion offered both in-app and online, and declared in the Data Safety form (if the app has account creation)
- Reviewer credentials are for a dedicated account, not a real user account
- Internal Testing opt-in URL shared with testers before expecting them to see the app
- Service account email granted access in Play Console before CI automation runs
- Store icon is full-bleed square (no baked rounding/shadow); screenshots 16:9/9:16 with 4+ at 1080px+
- AI-generated imagery labelled per asset in the AI declaration; health-condition language kept out of store assets
- Release SAVED on a reviewable track with countries selected before expecting "Send app for review" to unlock
- Managed publishing ON before submitting when go-live needs sign-off
Avoid
- Losing the Android upload keystore — it cannot be recovered; losing it means a new Play Store listing
- Uploading an unsigned APK or APK at all to Google Play — upload a signed AAB
- Building AAB on every push — gate it to
workflow_dispatch; artifact storage fills quickly on the free tier - Assuming Google Play org accounts require a D-U-N-S number — they do not (only Apple does)
- Using a real user account as reviewer credentials — use a dedicated account with a shared mailbox
- Providing a privacy policy as a file upload or email address — Play Console requires a public URL
- Skipping the Feature graphic — Play Console requires it even if the app is not being promoted
- Setting
ANDROID_STORE_PASSWORDandANDROID_KEY_PASSWORDto different values — PKCS12 format uses one password for both; mismatches cause hard-to-diagnose signing failures - Expecting testers to update over the air when the signing key has changed (e.g. after a framework migration) — a changed signing key requires a full uninstall on the device before the new build can be installed
- Resetting the upload key right before a release — the ~2 day server-enforced validation wait blocks all uploads until it passes
- Panicking at a −100% device support figure on an empty draft release — it is an artefact of comparing against zero devices; attach a valid AAB
- Configuring an
r0adkll/upload-google-playdeploy without awhatsNewDirectoryand locale file — the job fails - Uploading a store-listing icon with baked-in rounded corners — Google double-rounds it into an icon-within-an-icon; use full-bleed square 512×512
- Submitting 19.5:9 tall-phone or 3:4 captures as phone screenshots — Play requires 16:9 / 9:16
- Leaving Managed publishing off on a full-rollout release that still needs sign-off — approval auto-publishes
- Re-uploading an AAB to promote it across tracks — use "Add from library"; the same version code is rejected
- Blocking a submission on the "no deobfuscation file" warning — it is advisory only
Example usage
"The Android build pipeline produces a signed AAB on manual trigger. Set up the Google Play Console listing, complete all App Content declarations, configure Internal Testing, and automate AAB uploads from CI."
Source: This skill is sourced from the Matrix Skills library. Learn more at the AI Agent Skills Library.