Android Development Setup
Repository:
ngwenya-android
Submodule:ngwenya-front/mobile/android
Requirements: Android Studio Ladybug 2024.2+, JDK 17, Apple Silicon Mac
Tested on: M1 Max, 64 GB RAM, macOS 15
Quick Start
# 1. Clone (or init submodule)
cd /path/to/ngwenya-front
git submodule update --init mobile/android
cd mobile/android
# 2. Open in Android Studio
open -a "Android Studio" .
# 3. Wait for Gradle sync โ Select emulator โ โถ Run
Prerequisites
| Tool | Minimum | Download |
|---|---|---|
| Android Studio | Ladybug 2024.2+ | developer.android.com/studio |
| JDK | 17+ | Bundled with Android Studio |
| Git | 2.30+ | git --version |
Install Android Studio
- Download the Mac (Apple Silicon)
.dmgfrom developer.android.com/studio - Drag to
/Applicationsโ Launch - Complete the Setup Wizard:
- Install Type: Standard
- Accept all SDK licenses
- Skip Intel HAXM (Apple Silicon uses Hypervisor.framework natively)
Configure PATH
Add to ~/.zshrc:
export ANDROID_HOME=$HOME/Library/Android/sdk
export PATH=$ANDROID_HOME/emulator:$ANDROID_HOME/platform-tools:$PATH
Then source ~/.zshrc and verify:
adb --version # Android Debug Bridge version 1.0.41+
emulator -version # Android emulator version 35.x
Install SDK Components
Settings (โ,) โ Languages & Frameworks โ Android SDK:
| Tab | Component | Required |
|---|---|---|
| SDK Platforms | Android 15.0 (API 35) | โ Yes |
| SDK Platforms | Android 14.0 (API 34) | Recommended |
| SDK Tools | Build-Tools 35 | โ Yes |
| SDK Tools | Android Emulator | โ Yes |
| SDK Tools | Platform-Tools | โ Yes |
| SDK Tools | Google Play services | โ Yes (for FCM) |
Open the Project
Step 1: Clone
# Via submodule
cd /path/to/ngwenya-front
git submodule update --init mobile/android
# Or standalone
git clone https://github.com/mall-dev/ngwenya-android.git
Step 2: Open in Android Studio
open -a "Android Studio" /path/to/mobile/android
Click Trust Project when prompted.
Step 3: Gradle Sync
Android Studio triggers Gradle sync automatically. Look for:
- โ "Gradle sync finished" in the status bar
- ~200 MB of dependencies downloaded on first run
If it doesn't auto-trigger: File โ Sync Project with Gradle Files (โโงO)
Step 4: Verify Project Structure
The Project pane (Android view) should show:
app/
โโโ manifests/
โ โโโ AndroidManifest.xml
โโโ java/com.mallnline.ngwenya/
โ โโโ MainActivity.kt
โ โโโ NgwenyaApp.kt
โ โโโ bridge/WebViewBridge.kt
โ โโโ services/NgwenyaFirebaseService.kt
โ โโโ ui/Screens.kt
โโโ res/values/
โ โโโ themes.xml
โโโ build.gradle.kts
Build Configuration
The project uses Gradle Kotlin DSL with these key settings:
| Property | Value | Notes |
|---|---|---|
compileSdk |
35 | Android 15 |
minSdk |
26 | Android 8.0 (Oreo) |
targetSdk |
35 | Android 15 |
jvmTarget |
17 | JDK 17 |
| Compose BOM | 2024.12.01 | Material3 |
| Firebase BOM | 33.7.0 | FCM |
Key Dependencies
| Library | Purpose |
|---|---|
androidx.compose.material3 |
Material Design 3 UI |
androidx.navigation:navigation-compose |
Tab navigation |
androidx.webkit:webkit |
Modern WebView API |
com.google.firebase:firebase-messaging-ktx |
Push notifications |
com.squareup.okhttp3:okhttp |
HTTP client for GraphQL |
Gradle Performance (M1 Max)
The gradle.properties is tuned for your hardware:
org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8
org.gradle.parallel=true
org.gradle.caching=true
Expected build times on M1 Max / 64 GB:
- First build: ~90 seconds
- Incremental builds: ~5-10 seconds
- Clean rebuild: ~45 seconds
Configure Firebase (FCM)
Firebase Cloud Messaging requires a google-services.json file.
Step 1: Create Firebase Project
- Go to Firebase Console
- Add Project โ Name:
Ngwenyaโ Create
Step 2: Register Android App
- Click Add App โ Android
- Package name:
com.mallnline.ngwenya - App nickname:
Ngwenya Android - Click Register App
Step 3: Download Config
Download google-services.json โ place in app/ directory:
cp ~/Downloads/google-services.json app/
Step 4: Add Plugin
Update build.gradle.kts (root):
plugins {
// ...existing plugins...
id("com.google.gms.google-services") version "4.4.2" apply false
}
Update app/build.gradle.kts:
plugins {
// ...existing plugins...
id("com.google.gms.google-services")
}
Step 5: Re-sync Gradle
File โ Sync Project with Gradle Files
Running Without Firebase
To build without push notifications (e.g., for pure UI development), comment out the Firebase dependencies in app/build.gradle.kts:
// implementation(platform("com.google.firebase:firebase-bom:33.7.0"))
// implementation("com.google.firebase:firebase-messaging-ktx")
The app compiles and runs โ push features are non-functional.
Backend Configuration
Once FCM is configured, set these environment variables in the alerts subgraph:
FCM_PROJECT_ID=your-firebase-project-id
FCM_CLIENT_EMAIL=firebase-adminsdk-xxx@your-project.iam.gserviceaccount.com
FCM_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
See Mobile Push Infrastructure for the full backend dispatch architecture.
Create an Emulator
Important: Use ARM64 Images on Apple Silicon
Critical: Select arm64-v8a system images. x86_64 images use Rosetta 2 translation and run 5-10x slower.
Step-by-Step
- Tools โ Device Manager โ Create Virtual Device
- Select hardware:
| Recommended | Screen | Density |
|---|---|---|
| Pixel 8 Pro | 6.7" | 560 dpi |
| Pixel 8 | 6.2" | 420 dpi |
- Select system image: VanillaIceCream (API 35) โ ABI must show arm64-v8a โ
- Configure:
| Setting | Value |
|---|---|
| RAM | 4096 MB |
| VM Heap | 512 MB |
| Internal Storage | 4096 MB |
- Click Finish โ โถ to launch
With 64 GB RAM, you can run 2-3 emulators simultaneously. Each instance uses ~2-3 GB.
Run the App
On Emulator
- Select emulator from device dropdown
- Press โถ Run (โR) or
./gradlew :app:installDebug
On Physical Device
- Enable Developer Options: Settings โ About phone โ tap Build number 7 times
- Enable USB Debugging in Developer Options
- Connect via USB-C โ Accept debugging prompt โ select device โ โถ Run
Wireless Debugging
adb tcpip 5555
adb connect 192.168.1.XXX:5555
# Disconnect USB โ you're wireless
Verify
You should see:
- โ Material3 bottom navigation with 6 tabs
- โ Notification permission dialog (Android 13+)
- โ
Logcat:
[NgwenyaApp] Notification channel created: ngwenya_default
Local Backend Development
Emulator โ Host Machine
The Android Emulator maps 10.0.2.2 to your Mac's localhost:
// In NgwenyaApp.kt or a config file:
const val API_URL = "http://10.0.2.2:30000/graphql" // Gateway
const val WEB_URL = "http://10.0.2.2:5173" // Vite dev server
Physical Device โ Mac
Both must be on the same Wi-Fi. Use your Mac's IP:
const val API_URL = "http://192.168.1.XXX:30000/graphql"
const val WEB_URL = "http://192.168.1.XXX:5173"
Cleartext Traffic (HTTP)
For local development with HTTP, create app/src/main/res/xml/network_security_config.xml:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="false">10.0.2.2</domain>
<domain includeSubdomains="false">192.168.1.0</domain>
</domain-config>
</network-security-config>
Reference in AndroidManifest.xml:
<application android:networkSecurityConfig="@xml/network_security_config" ... >
WebView Bridge Testing
Enable WebView Debugging
Already enabled in debug builds via:
WebView.setWebContentsDebuggingEnabled(BuildConfig.DEBUG)
Chrome DevTools
- Open Chrome on Mac:
chrome://inspect/#devices - Your emulator/device WebView appears under Remote Target
- Click inspect to open DevTools
Test Injected Globals
In Chrome DevTools console:
window.__NGWENYA_TOKEN__ // โ JWT string
window.__NGWENYA_PLATFORM__ // โ "android"
Test Bridge Interface
NgwenyaBridge.navigate(JSON.stringify({ route: "/cart" }));
// Check Logcat for: [WebViewBridge] Navigate: /cart
The frontend detects this context via mobileUtils.ts โ isMobileWebView(), getMobilePlatform(), and sendBridgeMessage().
Build Variants
| Variant | applicationId |
Debuggable | Minified |
|---|---|---|---|
| debug | com.mallnline.ngwenya.debug |
โ | โ |
| release | com.mallnline.ngwenya |
โ | โ (R8) |
# Debug APK
./gradlew :app:assembleDebug
# โ app/build/outputs/apk/debug/app-debug.apk
# Release AAB (Play Store)
./gradlew :app:bundleRelease
Useful Commands
# Clean + rebuild
./gradlew clean assembleDebug
# Run unit tests
./gradlew test
# Run instrumented tests (requires emulator)
./gradlew connectedAndroidTest
# Lint report
./gradlew lint
# โ app/build/reports/lint-results-debug.html
# View logs (filtered to our app)
adb logcat --pid=$(adb shell pidof com.mallnline.ngwenya.debug)
# Direct install
adb install app/build/outputs/apk/debug/app-debug.apk
Troubleshooting
| Problem | Solution |
|---|---|
| Gradle sync: "Could not resolve gradle" | Invalidate caches (File โ Invalidate Caches), delete ~/.gradle/caches/, re-sync |
| "Failed to find target android-35" | Settings โ Android SDK โ check "Android 15.0" โ Apply |
| Emulator extremely slow | Delete AVD โ recreate with arm64-v8a image (not x86_64) |
google-services.json not found |
Download from Firebase Console โ place in app/ directory, or comment out plugin |
| FCM token is null | Use emulator with "Google APIs" system image |
net::ERR_CLEARTEXT_NOT_PERMITTED |
Add network_security_config.xml (see Local Backend section) |
| "Unsupported class file major version 65" | Settings โ Build โ Gradle โ Gradle JDK โ select 17 |
Related
- Mobile Push Infrastructure โ In-house APNs/FCM dispatch and backend TCP event wiring
- Mobile SDK Architecture โ KMP shared module and OIDC/PKCE auth adapters
- iOS Development Setup โ Companion guide for the iOS app
- MFA & Passkeys โ Additional auth factors for native apps
- User Guide: Notifications โ Visitor-facing notification management