name: react-native-android-bridge-implementation
description: >
Implements the Android Kotlin bridge layer for a React-Native MoEngage SDK feature.
This is Step 2a of the React-Native feature pipeline — run AFTER plugin-base-feature-implementation
has completed. Produces the BridgeHandler, arch bridges (new/old),
Package, EventEmitterImpl, PayloadGenerator, Constants, and build.gradle for a new
sdk/
- name: "ticket_id" description: "JIRA ticket ID, e.g. 'MOEN-44072'. Extracted from command text if not supplied." optional: true
- name: "feature_description" description: "Natural language description of the feature. E.g. 'JWT authentication parity'."
- name: "contract_branch" description: "Branch in 'mobile-sdk-contracts' with the feature contract. E.g. 'MOEN-44072_jwt_contract'."
- name: "android_bom_version" description: "Target MoEngage Android BOM version. E.g. '2.2.1'."
- name: "android_plugin_base_pr_url" description: "URL of the android-plugin-base PR from plugin-base-feature-implementation."
Overview
Implements the Android Kotlin bridge inside the React-Native SDK repo (React-Native) for a
MoEngage feature whose plugin-base module already exists in ../android-plugin-base.
Prerequisite chain:
plugin-base-feature-implementation— creates/extends the Android plugin-base module ✅react-native-android-bridge-implementation← you are herereact-native-ts-implementation— TypeScript models, spec, Handler, public API
Architecture standard: Follow the Cards module pattern exactly. The Cards module is the canonical reference for all structural decisions.
Example files: Templates are in examples/ adjacent to this SKILL.md. Read each
template before generating the corresponding file.
Example Files Index
examples/
Constants.kt ← <rnPackage>/Constants.kt
BridgeHandler.kt ← <rnBridgeName>Handler.kt (all methods)
EventEmitterImpl.kt ← EventEmitterImpl.kt (nativeToHybrid / event methods only)
PayloadGenerator.kt ← PayloadGenerator.kt (nativeToHybrid / event methods only)
Package.kt ← MoEngage<featureNameCamel>Package.kt
NewArchBridge.kt ← src/newarch/ TurboModule bridge
OldArchBridge.kt ← src/oldarch/ ReactMethod bridge
Phase 0 — Clarify Inputs
0.1 Extract ticket ID
Scan the user's full command for MOEN-\d+ → ticketId.
If not found in the command or parameters, ask before proceeding.
Phase 1 — Parse Inputs & Derive All Identifiers
1.1 Extract from contract_branch
Strip everything up to and including the first / or _MOEN-XXXXX_ prefix:
feature/experience_contracts→contractSuffix=experience_contractsMOEN-44072_jwt_contract→contractSuffix=jwt_contract
1.2 Identifiers table
| Identifier | Example | Rule |
|---|---|---|
ticketId |
MOEN-44072 |
MOEN-\d+ from raw command or parameter |
contractSuffix |
jwt_contract |
branch name after first / or _MOEN-XXXXX_ |
featureName |
jwt |
lowercase slug from feature_description |
featureNameCamel |
Jwt |
PascalCase of featureName |
featureNameUpper |
JWT |
UPPER_SNAKE of featureName |
contractDir |
authentication |
subdirectory found in contracts json/ after checkout |
androidModuleName |
plugin-base-jwt |
plugin-base-<featureName> (or the actual module from plugin-base PR) |
rnSdkDir |
sdk/core |
see rule below |
rnPackage |
com.moengage.react.jwt |
com.moengage.react.<featureName> |
rnBridgeName |
MoEngageJwtBridge |
MoEngage<featureNameCamel>Bridge |
branchName |
feature/MOEN-44072-jwt_contract |
feature/<ticketId>-<contractSuffix> |
1.3 Resolve rnSdkDir
Scan feature_description for a framework keyword and map to the existing SDK module directory:
Keyword in feature_description |
rnSdkDir |
|---|---|
core, analytics, inapps, or messaging |
sdk/core |
cards |
sdk/cards |
geofence |
sdk/geofence |
inbox |
sdk/inbox |
personalize |
sdk/personalize |
| none of the above | ask the user which SDK module to add the feature to |
Examples:
"JWT authentication parity from core"→sdk/core"get clicked cards count"→sdk/cards"start geofence monitoring"→sdk/geofence
Phase 2 — Read Contracts
cd ../mobile-sdk-contracts
git fetch
git stash
git checkout <contract_branch>
- List
json/hybridToNative/to identifycontractDir- If no matching directory → list available dirs and ask the user
- For each
.jsoninjson/hybridToNative/<contractDir>/:- Filename (without
.json) = method name (camelCase) - File content = input payload shape
- Filename (without
- For each
.jsoninjson/nativeToHybrid/<contractDir>/:- File content = event/response payload shape
- Read all
.protofiles inprotos/<contractDir>/for field names and types
Method classification
| Condition | Type | Android return | Notes |
|---|---|---|---|
hybridToNative only |
fire-and-forget | Unit |
Delegates to plugin-base Helper |
| both directions, JS-initiated | promise | Promise param |
Delegates with listener |
nativeToHybrid only, SDK-initiated |
event | emit via EventEmitter | PayloadGenerator + EventEmitterImpl |
Build a complete method table before writing any code.
Phase 3 — Android Bridge Implementation
3.1 Create branch
cd React-Native
git fetch
git checkout -b feature/<ticketId>-<contractSuffix>
3.2 Check if SDK module already exists
ls <rnSdkDir>/android/src/main/ 2>/dev/null
- Not found → scaffold full module (Steps 3.3–3.9)
- Found → read existing files first, then add only the missing methods
3.3 Android build config
<rnSdkDir>/android/build.gradle
Copy sdk/cards/android/build.gradle exactly, then change:
moengageNativeBOMVersion→<android_bom_version>namespace→"<rnPackage>"api("com.moengage:cards")→api("com.moengage:<featureSdkArtifact>")(ask user if unknown)implementation("com.moengage:plugin-base-cards")→implementation("com.moengage:<androidModuleName>")
3.4 Constants.kt
→ See examples/android/Constants.kt
Generate at: <rnSdkDir>/android/src/main/java/<rnPackage>/Constants.kt
Constants must include:
- One string constant per method name (camelCase), matching the contract file names exactly
- One string constant per event name (for nativeToHybrid events)
3.5 BridgeHandler.kt
→ See examples/android/BridgeHandler.kt
Generate at: <rnSdkDir>/android/src/main/java/<rnPackage>/<rnBridgeName>Handler.kt
Rules:
- Fire-and-forget methods: call
PluginHelper(<featureNameCamel>Helper).methodName(context, payload)— no return - Promise methods: create listener, call helper, resolve/reject from callback
- Event methods: not in BridgeHandler — handled by EventEmitterImpl + PayloadGenerator
- Each method is annotated
@ReactMethod(old arch); new arch goes in NewArchBridge - Log all entry points using the module logger
3.6 EventEmitterImpl.kt (only if event methods exist)
→ See examples/android/EventEmitterImpl.kt
Generate at: <rnSdkDir>/android/src/main/java/<rnPackage>/EventEmitterImpl.kt
Implements the plugin-base EventEmitter interface. The emit(event) method switches on
event.eventType and calls the React-Native sendEvent for each known event type.
3.7 PayloadGenerator.kt (only if event methods exist)
→ See examples/android/PayloadGenerator.kt
Generate at: <rnSdkDir>/android/src/main/java/<rnPackage>/PayloadGenerator.kt
Contains one toWritableMap() extension function per nativeToHybrid event type. Converts the
plugin-base event's payload into a WritableMap for React-Native emission.
3.8 Package.kt
→ See examples/android/Package.kt
Generate at: <rnSdkDir>/android/src/main/java/<rnPackage>/MoEngage<featureNameCamel>Package.kt
3.9 Arch bridges
New arch → examples/android/NewArchBridge.kt
Path: <rnSdkDir>/android/src/newarch/java/<rnPackage>/MoEngage<featureNameCamel>Bridge.kt
Old arch → examples/android/OldArchBridge.kt
Path: <rnSdkDir>/android/src/oldarch/java/<rnPackage>/MoEngage<featureNameCamel>Bridge.kt
Rules:
- New arch: implements the TurboModule spec; delegates every method to the BridgeHandler
- Old arch: extends
ReactContextBaseJavaModule; delegates every method to the BridgeHandler - Both arches expose exactly the same public method signatures
3.10 Commit
git add <rnSdkDir>/android/
git commit -m "<ticketId>: Add React-Native Android bridge for <featureName>"
Phase 4 — Create Pull Request
git push -u origin feature/<ticketId>-<contractSuffix>
gh pr create \
--title "<ticketId>: Add React-Native Android bridge for <featureName>" \
--base development \
--body "$(cat <<'EOF'
## Summary
- Adds Android Kotlin bridge (`<rnSdkDir>/android/`) for the <featureName> feature
- BridgeHandler delegates to `<androidModuleName>` plugin-base helper
- New-arch (TurboModule) + old-arch (ReactMethod) bridges implemented
- Android BOM version: <android_bom_version>
## Related PRs
- android-plugin-base: <android_plugin_base_pr_url>
## Contract
Branch: `<contract_branch>` in mobile-sdk-contracts
## Methods
| Method | Type |
|---|---|
<table rows from method table>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
EOF
)"
Phase 5 — Report & Hand-off
Print:
- PR URL
- Full method table (name, type, Android delegate)
- All
// TODOitems left for manual verification - List of all files created or modified
Then ask the user:
"Android bridge for
<featureName>is done (PR:). Would you like to also run the TypeScript layer now ( react-native-ts-implementation)? It needs the same contract branch, BOM version, and both PR URLs."
If the user says yes, remind them to invoke:
/react-native-ts-implementation
ticket_id: <ticketId>
feature_description: <feature_description>
contract_branch: <contract_branch>
android_bom_version: <android_bom_version>
plugin_base_pr_url: <plugin_base_pr_url>
android_bridge_pr_url: <this PR URL>
Codebase Reference Files
Read these before generating the corresponding output — copy copyright headers, logging conventions, and structural patterns exactly.
| What | Codebase path |
|---|---|
| BridgeHandler reference | React-Native/sdk/cards/android/src/main/java/.../MoEngageCardsBridgeHandler.kt |
| EventEmitterImpl reference | React-Native/sdk/cards/android/src/main/java/.../EventEmitterImpl.kt |
| PayloadGenerator reference | React-Native/sdk/cards/android/src/main/java/.../PayloadGenerator.kt |
| Package reference | React-Native/sdk/cards/android/src/main/java/.../MoEngageCardsPackage.kt |
| New-arch bridge reference | React-Native/sdk/cards/android/src/newarch/.../MoEngageCardsBridge.kt |
| Old-arch bridge reference | React-Native/sdk/cards/android/src/oldarch/.../MoEngageCardsBridge.kt |
| build.gradle reference | React-Native/sdk/cards/android/build.gradle |
| plugin-base module reference | ../android-plugin-base/<androidModuleName>/ (output of plugin-base-feature-implementation) |
Error Handling Rules
contract_branchnot found in../mobile-sdk-contracts→ stop and tell the usercontractDirnot found injson/hybridToNative/→ list available dirs and askrnSdkDiralready hasandroid/src/main/→ read existing files, add only missing methods- SDK artifact name or plugin-base helper class name unknown → add
// TODO: verifyand continue - Push fails → report error and local branch name so the user can push manually