# Google Play Store — Android App Distribution Guide ## Google Play Console | Field | Value | | ----------------- | ---------------------------------------------------------- | | Console URL | [play.google.com/console](https://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: 1. Go to [play.google.com/console/signup](https://play.google.com/console/signup) 2. Pay the one-time $25 registration fee 3. Complete identity verification (can take 48 hours) ### 2. Create a Service Account (for CLI uploads) 1. Go to [Google Cloud Console → IAM → Service Accounts](https://console.cloud.google.com/iam-admin/serviceaccounts) 2. Select or create a project (e.g., `bytelyst-releases`) 3. Click **"+ Create Service Account"** - Name: `play-upload` - Role: (skip — we'll grant access in Play Console) 4. Click **"..."** → **"Manage keys"** → **"Add Key"** → **"Create new key"** → **JSON** 5. Save the JSON key file to this directory and `~/.google-play/` ```bash 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 1. Go to [Play Console → Settings → API access](https://play.google.com/console/developers/api-access) 2. Click **"Link"** next to Google Cloud project 3. Under Service Accounts, find `play-upload` → **"Grant access"** 4. Set permissions: **Release manager** (or **Admin** for full control) 5. Add the apps it can manage --- ## Creating a New App in Play Console 1. Go to [Play Console → All apps](https://play.google.com/console/developers) 2. Click **"Create app"** 3. Fill in: - **App name:** Product name (e.g., `ChronoMind`) - **Default language:** English (United States) - **App or Game:** App - **Free or Paid:** Free 4. 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) ```bash # Generate an upload keystore keytool -genkey -v \ -keystore ~/keystores/chronomind-upload.jks \ -keyalg RSA -keysize 2048 -validity 10000 \ -alias upload \ -storepass \ -keypass \ -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): ```properties storePassword= keyPassword= keyAlias=upload storeFile=/Users//keystores/chronomind-upload.jks ``` Reference in `android/app/build.gradle.kts`: ```kotlin 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 ```bash cd android ./gradlew :app:bundleRelease # Output: app/build/outputs/bundle/release/app-release.aab ``` ### Build a Release APK (for direct install) ```bash ./gradlew :app:assembleRelease # Output: app/build/outputs/apk/release/app-release.apk ``` --- ## Uploading to Play Console ### Manual Upload (Play Console UI) 1. Go to app → **Production** (or **Internal testing** / **Closed testing**) 2. Click **"Create new release"** 3. Upload the `.aab` file 4. Add release notes 5. Click **"Review release"** → **"Start rollout"** ### CLI Upload via bundletool + Google API ```bash 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`: ```kotlin 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: ```bash ./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 1. Go to app → **Testing** → **Internal testing** 2. Click **"Create new release"** 3. Upload `.aab` 4. Click **"Review release"** → **"Start rollout"** 5. Go to **Testers** tab → Create email list → Add tester emails 6. 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) 1. Go to App content → Content rating 2. Complete the IARC questionnaire 3. Receive ratings for all regions automatically --- ## Corporate Network / Gradle Proxy When building on the corporate network: ```bash # 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/-upload.jks` | Release signing | | keystore.properties | `android/keystore.properties` (gitignored) | Local signing config |