Android Build Guide

This guide walks you through setting up and building the zmNinjaNg Android app from scratch.

Prerequisites

Before you begin, ensure you have the following installed:

  • Node.js ^20.19.0 || >=22.12.0 and npm - Download here

  • Android Studio - Download here

  • Android SDK (installed via Android Studio)

  • Java JDK 17+ (usually bundled with Android Studio)

Environment Setup

1. Configure Android SDK

After installing Android Studio, set up the ANDROID_HOME environment variable:

macOS/Linux:

# Add to ~/.zshrc or ~/.bashrc
export ANDROID_HOME=$HOME/Library/Android/sdk
export PATH=$PATH:$ANDROID_HOME/emulator
export PATH=$PATH:$ANDROID_HOME/tools
export PATH=$PATH:$ANDROID_HOME/tools/bin
export PATH=$PATH:$ANDROID_HOME/platform-tools

Windows:

ANDROID_HOME=C:\Users\YourUsername\AppData\Local\Android\Sdk

Verify the installation:

echo $ANDROID_HOME  # Should show the SDK path
adb --version       # Should show ADB version

2. Create an Android Virtual Device (AVD)

For push notifications to work, you need an emulator with Google Play Services:

  1. Open Android Studio

  2. Go to Tools > Device Manager

  3. Click Create Device

  4. Select a device (e.g., Pixel 5)

  5. Choose a system image with the Play Store icon (e.g., Android 13 with Google APIs)

  6. Click Finish

Project Setup

1. Clone the Repository

git clone https://github.com/ZoneMinder/zmNinjaNg
cd zmNinjaNg/app

2. Install Dependencies

npm install

Push Notifications Setup

To enable push notifications, you need to configure Firebase Cloud Messaging (FCM):

1. Create Firebase Project

  1. Go to Firebase Console

  2. Click Add project

  3. Follow the setup wizard to create your project (call it anything, I called mine zmNinjaNg)

2. Add Android App to Firebase

  1. In Firebase Console, click Add app and select Android

  2. Enter the package name: com.zoneminder.zmNinjaNG

  3. (Optional) Enter an app nickname: “zmNinjaNg Android”

  4. Click Register app

3. Download Configuration File

  1. Download the google-services.json file

  2. Place it in the following location:

    zmNinjaNg/app/android/app/google-services.json
    

4. Verify Firebase Configuration

The project already includes the necessary Firebase dependencies. The google-services.json file is all you need to enable push notifications.

Note: Push notifications will only work on emulators/devices with Google Play Services installed.

5. Configure ZoneMinder Event Notification Server

zmNinjaNg works out of the box with zmES web socket notifications. If you want push notification support, the ZoneMinder event notification server (zmeventnotification.pl) needs to be configured to work with your Firebase project. You will need to use a modified version that I provided here. Follow its README

Building and Running

Debug Build

To build and run the app on an emulator or connected device:

# Build web assets, sync to Android, and run
npm run android

# Open in Android Studio
npm run android:open

This command will: 1. Build the React web app 2. Sync files to the Android project 3. Launch the app on a connected device/emulator

View logs:

npm run android:logs

Check connected devices:

npm run android:devices

Production Build

To create a release build for distribution or the Google Play Store:

1. Generate a Signing Key

If you don’t already have a keystore, create one:

keytool -genkey -v -keystore zmninja-ng-upload.jks -keyalg RSA -keysize 2048 -validity 10000 -alias zmninja-ng-upload

You’ll be prompted to enter a keystore password and identity fields.

Keep your keystore file and passwords safe! You’ll need them for all future app updates. Store the keystore outside the repo (e.g., ~/.signing-keys/).

2. Configure Signing

The build reads signing configuration from environment variables. Add these to your ~/.zshrc or ~/.bashrc:

export ZMNINJA_KEYSTORE_PATH="$HOME/.signing-keys/zmninja-ng-upload.jks"
export ZMNINJA_KEYSTORE_PASSWORD="your_password"
export ZMNINJA_KEY_ALIAS="zmninja-ng-upload"
export ZMNINJA_KEY_PASSWORD="your_password"

Then reload your shell: source ~/.zshrc

3. Build Release APK

cd app
npx cap sync android
cd android && ./gradlew assembleRelease

Output location: android/app/build/outputs/apk/release/app-release.apk

4. Build App Bundle (AAB) for Play Store

For publishing to Google Play Store, use the App Bundle format:

cd app/android && ./gradlew bundleRelease

Output location: android/app/build/outputs/bundle/release/app-release.aab

5. Upload to Google Play Console

  1. Go to Google Play Console

  2. Select zmNinjaNg

  3. Go to Testing > Internal testing (or Production) and click Create new release

  4. Upload the .aab file

  5. Add release notes and roll out

CI/CD

The GitHub Actions workflow (build-android.yml) handles signed release builds automatically. It uses these repository secrets:

  • ANDROID_KEYSTORE_BASE64: base64-encoded keystore file

  • ANDROID_KEYSTORE_PASSWORD: keystore password

  • ANDROID_KEY_ALIAS: key alias

  • ANDROID_KEY_PASSWORD: key password

To generate the base64 keystore value:

base64 -i ~/.signing-keys/zmninja-ng-upload.jks | pbcopy

Paste the clipboard contents as the ANDROID_KEYSTORE_BASE64 secret in GitHub > Settings > Secrets and variables > Actions.

Troubleshooting

Build Fails with “ANDROID_HOME not set”

Make sure you’ve set the ANDROID_HOME environment variable and restarted your terminal.

Push Notifications Don’t Work

  • Ensure you’re using an emulator/device with Google Play Services

  • Verify google-services.json is in the correct location

  • Check that the package name in Firebase matches com.zoneminder.zmNinjaNG

  • If using your own Firebase project, ensure the ZoneMinder server is configured with your FCM credentials

  • Check for “sender ID mismatch” errors in logcat - this means your app’s Firebase project doesn’t match the server’s

FCM Image Errors (“Failed to decode image”)

If you see this error in the Android logs:

Failed to download image: java.io.IOException: Failed to decode image

This means the ZoneMinder server is sending an incorrectly formatted image URL. Fix it by: 1. Editing /etc/zm/zmeventnotification.ini on your ZoneMinder server 2. Setting picture_url to use view=image instead of view=frame 3. Removing username/password from the URL (use tokens instead) 4. Example: picture_url = http://your-server/index.php?view=image&eid=EVENTID&fid=snapshot&width=600

App Won’t Connect to ZoneMinder Server (HTTP)

  • Android 9+ blocks cleartext HTTP by default

  • The app includes network_security_config.xml to allow HTTP

  • If you modified the config, ensure it allows cleartext traffic

Gradle Build Errors

Try cleaning the build:

cd android
./gradlew clean
cd ..
npm run android

Next Steps

  • Testing: Run the app and connect to your ZoneMinder server

  • Customization: Update app icons, splash screens, and branding

  • Distribution: Upload AAB to Google Play Console for release

For more information, see: - Capacitor Android Documentation - Android Developer Guide - Firebase Cloud Messaging