# Android Release Process ## Keystore Generation ### Create Release Keystore ```bash # Generate a new keystore keytool -genkeypair -v -storetype PKCS12 -keystore saayam-release-key.keystore -alias saayam-key-alias -keyalg RSA -keysize 2048 -validity 10000 # Verify keystore keytool -list -v -keystore saayam-release-key.keystore ``` ### Keystore Security ```bash # Store keystore securely mkdir -p ~/.android/keystores mv saayam-release-key.keystore ~/.android/keystores/ # Set proper permissions chmod 600 ~/.android/keystores/saayam-release-key.keystore ``` ## Gradle Configuration ### Configure Signing **android/gradle.properties:** ```properties # Keystore configuration SAAYAM_UPLOAD_STORE_FILE=saayam-release-key.keystore SAAYAM_UPLOAD_KEY_ALIAS=saayam-key-alias SAAYAM_UPLOAD_STORE_PASSWORD=your_keystore_password SAAYAM_UPLOAD_KEY_PASSWORD=your_key_password # Build optimization org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 org.gradle.parallel=true org.gradle.configureondemand=true org.gradle.daemon=true # Android configuration android.useAndroidX=true android.enableJetifier=true android.enableR8.fullMode=true ``` **android/app/build.gradle:** ```gradle android { compileSdkVersion rootProject.ext.compileSdkVersion defaultConfig { applicationId "com.saayam.app" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 1 versionName "1.0.0" multiDexEnabled true } signingConfigs { debug { storeFile file('debug.keystore') storePassword 'android' keyAlias 'androiddebugkey' keyPassword 'android' } release { if (project.hasProperty('SAAYAM_UPLOAD_STORE_FILE')) { storeFile file(SAAYAM_UPLOAD_STORE_FILE) storePassword SAAYAM_UPLOAD_STORE_PASSWORD keyAlias SAAYAM_UPLOAD_KEY_ALIAS keyPassword SAAYAM_UPLOAD_KEY_PASSWORD } } } buildTypes { debug { signingConfig signingConfigs.debug applicationIdSuffix ".debug" debuggable true minifyEnabled false proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" } release { signingConfig signingConfigs.release minifyEnabled true proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" debuggable false zipAlignEnabled true } } splits { abi { reset() enable true universalApk false include "armeabi-v7a", "x86", "arm64-v8a", "x86_64" } } } ``` ## ProGuard Configuration **android/app/proguard-rules.pro:** ```proguard # React Native -keep class com.facebook.react.** { *; } -keep class com.facebook.hermes.** { *; } -keep class com.facebook.jni.** { *; } # Keep our application class -keep class com.saayam.** { *; } # Keep native methods -keepclassmembers class * { native ; } # Keep enums -keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); } # Keep Parcelable implementations -keep class * implements android.os.Parcelable { public static final android.os.Parcelable$Creator *; } # Keep serializable classes -keepnames class * implements java.io.Serializable -keepclassmembers class * implements java.io.Serializable { static final long serialVersionUID; private static final java.io.ObjectStreamField[] serialPersistentFields; !static !transient ; private void writeObject(java.io.ObjectOutputStream); private void readObject(java.io.ObjectInputStream); java.lang.Object writeReplace(); java.lang.Object readResolve(); } # Firebase -keep class com.google.firebase.** { *; } -keep class com.google.android.gms.** { *; } # OkHttp -dontwarn okhttp3.** -dontwarn okio.** -dontwarn javax.annotation.** -keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase # Retrofit -dontwarn retrofit2.** -keep class retrofit2.** { *; } -keepattributes Signature -keepattributes Exceptions # Gson -keepattributes Signature -keepattributes *Annotation* -dontwarn sun.misc.** -keep class com.google.gson.** { *; } -keep class * implements com.google.gson.TypeAdapterFactory -keep class * implements com.google.gson.JsonSerializer -keep class * implements com.google.gson.JsonDeserializer ``` ## Build Process ### Build Commands ```bash # Clean build cd android && ./gradlew clean # Build debug APK ./gradlew assembleDebug # Build release APK ./gradlew assembleRelease # Build release AAB (recommended for Play Store) ./gradlew bundleRelease # Install debug APK ./gradlew installDebug # Install release APK ./gradlew installRelease ``` ### Build Variants ```gradle android { flavorDimensions "version" productFlavors { development { dimension "version" applicationIdSuffix ".dev" versionNameSuffix "-dev" buildConfigField "String", "API_BASE_URL", '"https://dev-api.saayam.com"' resValue "string", "app_name", "Saayam Dev" } staging { dimension "version" applicationIdSuffix ".staging" versionNameSuffix "-staging" buildConfigField "String", "API_BASE_URL", '"https://staging-api.saayam.com"' resValue "string", "app_name", "Saayam Staging" } production { dimension "version" buildConfigField "String", "API_BASE_URL", '"https://api.saayam.com"' resValue "string", "app_name", "Saayam" } } } ``` ## Google Play Console Setup ### Create Application 1. **Go to Google Play Console** 2. **Create Application** - App name: Saayam - Default language: English - App or game: App - Free or paid: Free 3. **App Content** - Privacy Policy URL - App category - Content rating questionnaire - Target audience - Data safety form ### Upload Release ```bash # Build release AAB cd android && ./gradlew bundleRelease # AAB file location # android/app/build/outputs/bundle/release/app-release.aab ``` ### Release Tracks 1. **Internal Testing** - Upload AAB - Add internal testers - Release to internal testing 2. **Closed Testing** - Create closed testing track - Add test users via email lists - Release to closed testing 3. **Open Testing** - Create open testing track - Set country availability - Release to open testing 4. **Production** - Review release - Set rollout percentage - Release to production ## Version Management ### Automated Version Bumping **scripts/bump-android-version.sh:** ```bash #!/bin/bash VERSION_TYPE=${1:-patch} # major, minor, patch BUILD_GRADLE="android/app/build.gradle" # Get current version CURRENT_VERSION=$(grep "versionName" $BUILD_GRADLE | sed 's/.*versionName "\(.*\)"/\1/') CURRENT_CODE=$(grep "versionCode" $BUILD_GRADLE | sed 's/.*versionCode \(.*\)/\1/') echo "Current version: $CURRENT_VERSION ($CURRENT_CODE)" # Calculate new version IFS='.' read -ra VERSION_PARTS <<< "$CURRENT_VERSION" MAJOR=${VERSION_PARTS[0]} MINOR=${VERSION_PARTS[1]} PATCH=${VERSION_PARTS[2]} case $VERSION_TYPE in major) MAJOR=$((MAJOR + 1)) MINOR=0 PATCH=0 ;; minor) MINOR=$((MINOR + 1)) PATCH=0 ;; patch) PATCH=$((PATCH + 1)) ;; esac NEW_VERSION="$MAJOR.$MINOR.$PATCH" NEW_CODE=$((CURRENT_CODE + 1)) echo "New version: $NEW_VERSION ($NEW_CODE)" # Update build.gradle sed -i "s/versionCode $CURRENT_CODE/versionCode $NEW_CODE/" $BUILD_GRADLE sed -i "s/versionName \"$CURRENT_VERSION\"/versionName \"$NEW_VERSION\"/" $BUILD_GRADLE echo "Version updated successfully!" ``` ### Git Tagging ```bash # Create and push tag git tag -a v1.0.0 -m "Release version 1.0.0" git push origin v1.0.0 # List tags git tag -l # Delete tag git tag -d v1.0.0 git push origin --delete v1.0.0 ``` ## Release Checklist ### Pre-Release Checklist - [ ] Update version number - [ ] Update changelog - [ ] Run all tests - [ ] Test on multiple devices - [ ] Check ProGuard configuration - [ ] Verify signing configuration - [ ] Test release build locally - [ ] Update app store metadata - [ ] Prepare release notes ### Post-Release Checklist - [ ] Monitor crash reports - [ ] Check app store reviews - [ ] Monitor performance metrics - [ ] Update documentation - [ ] Create git tag - [ ] Notify team of release - [ ] Plan next release ## Troubleshooting ### Common Build Issues ```bash # Clean and rebuild cd android ./gradlew clean ./gradlew assembleRelease # Clear React Native cache npx react-native start --reset-cache # Clear Metro cache npx react-native start --reset-cache # Reset Android build cd android rm -rf build/ rm -rf app/build/ ./gradlew clean ``` ### Keystore Issues ```bash # Verify keystore keytool -list -v -keystore your-keystore.keystore # Check keystore alias keytool -list -keystore your-keystore.keystore # Export certificate keytool -export -alias your-alias -keystore your-keystore.keystore -file certificate.crt ```