homeresume
 
   

Internal testing React Native Android apps

January 21, 2023

Internal testing on Google Play Console is used for testing new app versions before releasing them to the end users. This post covers the main notes from setting up the app (on the Google Play Console) to automatic uploads.

Prerequisites

  • bootstrapped app
  • installed Android studio
  • verified developer account on Google Play Console
  • paid one-time fee (25\$)

Google Play Console setup

Create an app with the essential details, such as app name, default language, and app type, and choose if it is paid or free.

Testers

Add email addresses to the email list.

Release

Signing config

Generate a private signing key with a password using keytool

sudo keytool -genkey -v -keystore my-upload-key.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000

Move the generated file to android/app directory.

Edit ~/.gradle/gradle.properties to add the following keys and replace the alias and password with the correct values.

MYAPP_UPLOAD_STORE_FILE=my-upload-key.keystore
MYAPP_UPLOAD_KEY_ALIAS=my-key-alias
MYAPP_UPLOAD_STORE_PASSWORD=*****
MYAPP_UPLOAD_KEY_PASSWORD=*****

Edit android/app/build.gradle to add the release signing config, which uses the generated key.

android {
...
defaultConfig { ... }
signingConfigs {
...
release {
if (project.hasProperty('MYAPP_UPLOAD_STORE_FILE')) {
storeFile file(MYAPP_UPLOAD_STORE_FILE)
storePassword MYAPP_UPLOAD_STORE_PASSWORD
keyAlias MYAPP_UPLOAD_KEY_ALIAS
keyPassword MYAPP_UPLOAD_KEY_PASSWORD
}
}
}
buildTypes {
...
release {
...
signingConfig signingConfigs.release
}
}
}

Versioning

Update versionCode and versionName fields in android/app/build.gradle file before generating the bundle. The version code should be incremented by 1.

android {
...
defaultConfig {
...
versionCode 2
versionName "1.1.0"
...
}
...

Android App Bundle (aab file)

Generate the Android app bundle with the following command.

cd android
./gradlew bundleRelease

android/app/build/outputs/bundle/release/app-release.aab is the path for the generated file.

Manual uploads

Upload the aab file and write the release name and notes. The link for downloading the app should be available on the Testers tab.

Automatic uploads

This section configures the necessary steps for the pipeline with Github actions.

Versioning

Version apps with np and react-native-version packages running np script.

// package.json
{
"scripts": {
"np": "np --no-publish",
"postversion": "react-native-version -t android"
},
"repository": {
"type": "git",
"url": "<REPOSITORY_URL>"
}
}

Signing config

Remove the release signing config from android/app/build.gradle file. An app will be bundled first and signed after that.

Credentials

Get the signing key with the following command and set it as ANDROID_SIGNING_KEY Github action secret in the Settings Secrets Actions page.

cd android/app
openssl base64 < my-upload-key.keystore | tr -d '\n' | tee my-upload-key.keystore.base64.txt

Reuse credentials from ~/.gradle/gradle.properties file and set them as Github secrets.

  • MYAPP_UPLOAD_STORE_PASSWORD ANDROID_KEY_STORE_PASSWORD
  • MYAPP_UPLOAD_KEY_ALIAS ANDROID_ALIAS
  • MYAPP_UPLOAD_KEY_PASSWORD ANDROID_KEY_PASSWORD

For automatic uploads, create a service account by following the next steps.

  • go to Google Play console Setup API Access Google Cloud project, create a Google Cloud project, and link it
  • on the same page, go to Credentials Service accounts heading and click on Learn how to create service accounts, follow the mentioned steps
    • create a service account with a service account user role
    • click on Actions Manage keys button and create JSON key, which will be downloaded
  • set downloaded JSON file as ANDROID_SERVICE_ACCOUNT_JSON_TEXT Github secret

Pipeline

The following pipeline sets up the necessary tools, does CI checks (linting, testing, audit), generates the app bundle, signs it, and uploads it to the Google Play console.

name: Android Build
on:
push:
branches:
- release
jobs:
android-build:
name: Android Build
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@v3
- name: Set up JDK
uses: actions/setup-java@v3
with:
java-version: 18
distribution: temurin
- name: Set up Android SDK
uses: android-actions/setup-android@v2
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: 18
- run: npm ci
- run: npm run lint
- run: npm test
- run: npm audit
- name: Make Gradlew Executable
run: cd android && chmod +x ./gradlew
- name: Generate App Bundle
run: |
cd android && ./gradlew clean && \
./gradlew bundleRelease --no-daemon
- name: Sign App Bundle
id: sign_aab
uses: r0adkll/sign-android-release@v1
with:
releaseDirectory: android/app/build/outputs/bundle/release
signingKeyBase64: ${{ secrets.ANDROID_SIGNING_KEY }}
alias: ${{ secrets.ANDROID_ALIAS }}
keyStorePassword: ${{ secrets.ANDROID_KEY_STORE_PASSWORD }}
keyPassword: ${{ secrets.ANDROID_KEY_PASSWORD }}
- name: Upload App Bundle to Google Play
uses: r0adkll/upload-google-play@v1
with:
serviceAccountJsonPlainText: ${{ secrets.ANDROID_SERVICE_ACCOUNT_JSON_TEXT }}
packageName: com.flatmeapp
releaseFiles: android/app/build/outputs/bundle/release/*.aab
track: internal
status: draft
inAppUpdatePriority: 2

Troubleshooting

In case of a problem with signatures not matching the previously installed version, uninstall the app with the following commands.

adb devices
# adb -s <DEVICE_KEY> uninstall <PACKAGE_NAME>
adb -s emulator-5554 uninstall "com.yourapp"
2022

Splash screen with React Native

October 30, 2022

A splash screen is the first thing user sees after opening the app, and it usually shows an app logo with optional animations. This post covers setup for Android devices and iOS setup will be covered additionally.

Prerequisites

  • installed Android studio

  • bootstrapped app

  • react-native-splash-screen package installed

npx react-native init SplashScreenApp
cd SplashScreenApp
npm i react-native-splash-screen

JavaScript setup

Hide the splash screen when the app is ready

// App.js
// ...
useEffect(() => SplashScreen.hide(), []);
// ...

Native setup

Extending MainActivity class with onCreate method

// android/app/src/main/java/com/splashscreenapp/MainActivity.java
import org.devio.rn.splashscreen.SplashScreen;
import android.os.Bundle;
// ...
@Override
protected void onCreate(Bundle savedInstanceState) {
SplashScreen.show(this, R.style.SplashTheme, true);
super.onCreate(savedInstanceState);
}
// ...

Adding splash screen activity with the theme into the Android manifest

file: android/app/src/main/AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.splashscreenapp">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".MainApplication"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:allowBackup="false"
android:theme="@style/AppTheme">
<activity
android:name=".SplashActivity"
android:theme="@style/SplashTheme"
android:label="@string/app_name"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:windowSoftInputMode="adjustResize"
android:exported="true"/>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
</application>
</manifest>

Implementing SplashActivity class

// android/app/src/main/java/SplashActivity.java
package com.splashscreenapp;
import android.content.Intent;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
public class SplashActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
finish();
}
}

Themes setup

file: android/app/src/main/res/values/styles.xml

<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:statusBarColor">@color/theme_bg</item>
<item name="android:windowBackground">@drawable/background_splash</item>
</style>
<style name="SplashTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:background">@drawable/background_splash</item>
<item name="android:windowDisablePreview">true</item>
<item name="colorPrimaryDark">@color/theme_bg</item>
</style>
</resources>

Colors setup

file: android/app/src/main/res/values/colors.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="theme_bg">#2F3C7E</color>
</resources>

Adding config for background splash drawable

file: android/app/src/main/res/drawable/background_splash.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" android:opacity="opaque">
<item android:drawable="@color/theme_bg"/>
<item
android:width="300dp"
android:height="300dp"
android:drawable="@mipmap/splash_icon"
android:gravity="center" />
</layer-list>

Adding layout

file: android/app/src/main/res/layout/launch_screen.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/background_splash"
android:orientation="vertical">
</LinearLayout>

Images for splash screen

App Icon Generator can be used for generating the images. Open the Image Sets tab, choose 4X design base size, insert an image and generate corresponding images for the splash screen. Downloaded images should be stored in mipmap (android/app/src/main/res/mipmap-*hdpi/splash_icon.png) folders.

Demo

Run the following commands

npx react-native start
npx react-native run-android

Further reading

 

© 2023