- APPLE/README.md: API key details, App Store Connect setup, TestFlight workflow, submission checklist, provisioning, screenshots/metadata requirements - GOOGLE/README.md: Play Console setup, service account config, signing keys, AAB upload, testing tracks, data safety, content rating, submission checklist
12 KiB
12 KiB
Google Play Store — Android App Distribution Guide
Google Play Console
| Field | Value |
|---|---|
| Console URL | play.google.com/console |
| Developer Account | ByteLyst / saravanakumardb1@gmail.com |
Registered Apps
| Product | Package Name | Play Console | Status |
|---|---|---|---|
| LysnrAI | com.bytelyst.lysnrai |
❌ Needs creation | Gradle builds, pending setup |
| ChronoMind | com.chronomind.app |
❌ Needs creation | Gradle builds, pending setup |
| MindLyst | com.mindlyst.android |
❌ Needs creation | KMP shared module, pending setup |
| JarvisJr | com.jarvisjr.app |
❌ Needs creation | Jetpack Compose, pending setup |
One-Time Setup
1. Google Play Console Account
If you don't have one yet:
- Go to play.google.com/console/signup
- Pay the one-time $25 registration fee
- Complete identity verification (can take 48 hours)
2. Create a Service Account (for CLI uploads)
- Go to Google Cloud Console → IAM → Service Accounts
- Select or create a project (e.g.,
bytelyst-releases) - Click "+ Create Service Account"
- Name:
play-upload - Role: (skip — we'll grant access in Play Console)
- Name:
- Click "..." → "Manage keys" → "Add Key" → "Create new key" → JSON
- Save the JSON key file to this directory and
~/.google-play/
mkdir -p ~/.google-play
cp service-account-key.json ~/.google-play/
chmod 600 ~/.google-play/service-account-key.json
3. Link Service Account to Play Console
- Go to Play Console → Settings → API access
- Click "Link" next to Google Cloud project
- Under Service Accounts, find
play-upload→ "Grant access" - Set permissions: Release manager (or Admin for full control)
- Add the apps it can manage
Creating a New App in Play Console
- Go to Play Console → All apps
- Click "Create app"
- Fill in:
- App name: Product name (e.g.,
ChronoMind) - Default language: English (United States)
- App or Game: App
- Free or Paid: Free
- App name: Product name (e.g.,
- Accept declarations and click "Create app"
After Creation — Complete the Store Listing
Navigate through the Dashboard checklist:
| Section | Required For |
|---|---|
| App access | If app requires login → provide test credentials |
| Ads | Declare if app contains ads |
| Content rating | Complete IARC questionnaire |
| Target audience | Select age groups |
| News apps | Declare if it's a news app |
| COVID-19 apps | Declare if related |
| Data safety | Privacy questionnaire (like Apple's App Privacy) |
| Government apps | Declare if government-related |
Building an Android Release
Signing Key Setup (one-time per app)
# Generate an upload keystore
keytool -genkey -v \
-keystore ~/keystores/chronomind-upload.jks \
-keyalg RSA -keysize 2048 -validity 10000 \
-alias upload \
-storepass <PASSWORD> \
-keypass <PASSWORD> \
-dname "CN=ByteLyst, O=ByteLyst, L=Dallas, ST=TX, C=US"
⚠️ CRITICAL: Back up the keystore file and passwords. If you lose them, you cannot update the app. Store a copy in this directory.
Configure signing in the app
Create or edit android/keystore.properties (gitignored):
storePassword=<PASSWORD>
keyPassword=<PASSWORD>
keyAlias=upload
storeFile=/Users/<you>/keystores/chronomind-upload.jks
Reference in android/app/build.gradle.kts:
val keystoreProperties = Properties()
val keystoreFile = rootProject.file("keystore.properties")
if (keystoreFile.exists()) keystoreProperties.load(keystoreFile.inputStream())
android {
signingConfigs {
create("release") {
keyAlias = keystoreProperties["keyAlias"] as? String
keyPassword = keystoreProperties["keyPassword"] as? String
storeFile = file(keystoreProperties["storeFile"] as? String ?: "")
storePassword = keystoreProperties["storePassword"] as? String
}
}
buildTypes {
release {
signingConfig = signingConfigs.getByName("release")
}
}
}
Build the Release AAB
cd android
./gradlew :app:bundleRelease
# Output: app/build/outputs/bundle/release/app-release.aab
Build a Release APK (for direct install)
./gradlew :app:assembleRelease
# Output: app/build/outputs/apk/release/app-release.apk
Uploading to Play Console
Manual Upload (Play Console UI)
- Go to app → Production (or Internal testing / Closed testing)
- Click "Create new release"
- Upload the
.aabfile - Add release notes
- Click "Review release" → "Start rollout"
CLI Upload via bundletool + Google API
pip install google-api-python-client google-auth
python3 -c "
from googleapiclient.discovery import build
from google.oauth2 import service_account
creds = service_account.Credentials.from_service_account_file(
'$HOME/.google-play/service-account-key.json',
scopes=['https://www.googleapis.com/auth/androidpublisher']
)
service = build('androidpublisher', 'v3', credentials=creds)
package = 'com.chronomind.app'
edit = service.edits().insert(body={}, packageName=package).execute()
edit_id = edit['id']
service.edits().bundles().upload(
editId=edit_id, packageName=package,
media_body='app/build/outputs/bundle/release/app-release.aab',
media_mime_type='application/octet-stream'
).execute()
service.edits().tracks().update(
editId=edit_id, packageName=package, track='internal',
body={'releases': [{'status': 'completed', 'versionCodes': ['1']}]}
).execute()
service.edits().commit(editId=edit_id, packageName=package).execute()
print('Upload complete!')
"
Using Gradle Play Publisher Plugin (recommended for CI)
Add to android/build.gradle.kts:
plugins {
id("com.github.triplet.play") version "3.11.0"
}
play {
serviceAccountCredentials.set(file("$HOME/.google-play/service-account-key.json"))
track.set("internal") // internal, alpha, beta, production
defaultToAppBundles.set(true)
}
Then:
./gradlew publishBundle # Upload AAB to configured track
./gradlew publishInternalBundle # Upload to internal testing
./gradlew publishAlphaBundle # Upload to closed testing
./gradlew publishBundle --track production # Upload to production
Testing Tracks
| Track | Testers | Review Required | Purpose |
|---|---|---|---|
| Internal testing | Up to 100 (by email) | No | Quick dev testing |
| Closed testing (Alpha) | Invite-only | No (first time: yes) | Wider beta |
| Open testing (Beta) | Anyone with link | Yes (first time) | Public beta |
| Production | Everyone | Yes | Full release |
Internal Testing Setup
- Go to app → Testing → Internal testing
- Click "Create new release"
- Upload
.aab - Click "Review release" → "Start rollout"
- Go to Testers tab → Create email list → Add tester emails
- Share the opt-in link with testers
Play Store Submission Checklist
Required Assets
| Asset | Spec | Notes |
|---|---|---|
| App Icon | 512×512 PNG | 32-bit, no alpha |
| Feature Graphic | 1024×500 PNG or JPG | Required for store listing |
| Screenshots (Phone) | 320-3840px, 16:9 or 9:16 | Min 2, max 8 per device type |
| Screenshots (7" Tablet) | Same as phone | Required if targeting tablets |
| Screenshots (10" Tablet) | Same as phone | Required if targeting tablets |
| Promo Video | YouTube URL | Optional |
Required Metadata
| Field | Notes |
|---|---|
| App name | Max 30 characters |
| Short description | Max 80 characters |
| Full description | Max 4000 characters |
| App category | Select from predefined list |
| Contact email | Required, public |
| Privacy Policy URL | Required |
| App type & category | App or Game + category |
Data Safety Declaration
In Play Console → App content → Data safety:
- Declare all data types collected
- Whether data is encrypted in transit
- Whether users can request deletion
- Whether data is shared with third parties
- Purpose of each data type
Content Rating (IARC)
- Go to App content → Content rating
- Complete the IARC questionnaire
- Receive ratings for all regions automatically
Corporate Network / Gradle Proxy
When building on the corporate network:
# Ensure NETWORK=corp is set (switch-network.sh)
echo $GRADLE_OPTS
# Should show -Djavax.net.ssl.trustStore=~/.gradle/ssl/gradle-cacerts.jks
# If not set:
export GRADLE_OPTS="-Djavax.net.ssl.trustStore=$HOME/.gradle/ssl/gradle-cacerts.jks -Djavax.net.ssl.trustStorePassword=changeit"
See learning_ai_common_plat/AGENTS.md § 9 for full proxy setup docs.
Troubleshooting
| Error | Fix |
|---|---|
Keystore was tampered with |
Wrong password or corrupted keystore |
No key with alias 'upload' |
Check keyAlias in keystore.properties |
Version code already exists |
Bump versionCode in build.gradle.kts |
APK/AAB not signed |
Check signingConfig in release buildType |
Deobfuscation file missing |
Upload mapping.txt alongside the AAB |
Data safety form incomplete |
Complete all sections in App content → Data safety |
Target API level |
Google requires targeting latest Android API (currently 34+) |
| Gradle proxy failure | Set GRADLE_OPTS with truststore (see above) |
SDK not found |
Set sdk.dir in local.properties |
Key Files
| File | Location | Purpose |
|---|---|---|
| Service Account Key | ~/.google-play/service-account-key.json |
CLI uploads |
| Upload Keystore | ~/keystores/<app>-upload.jks |
Release signing |
| keystore.properties | android/keystore.properties (gitignored) |
Local signing config |