Android Config Plugin (with-meta-wearables-android)
Metadata
- System type:
library
System Intent
- What this is: An Expo config plugin (
main/app/plugins/with-meta-wearables-android.js) that patches the AndroidAndroidManifest.xmlandstrings.xmlat build time to support Meta wearables SDK integration and correct Android app behaviour. It injects required permissions, Meta SDK meta-data entries, a URL-scheme intent filter for the Meta View callback, andandroid:windowSoftInputMode="adjustPan"on the main activity. It runs duringexpo prebuild/expo run:androidand produces no runtime artefacts.
Mermaid Diagram
flowchart TD
ExpoConfig[Expo config / app.config.js] -->|plugin entry| withMetaWearablesAndroid
withMetaWearablesAndroid -->|withStringsXml| setMetaWearablesStrings
setMetaWearablesStrings -->|applicationId present| InjectString["strings.xml\nmeta_wearable_application_id = applicationId"]
withMetaWearablesAndroid -->|withAndroidManifest| setMetaWearablesConfig
setMetaWearablesConfig --> ensureUsesPermissions["AndroidManifest\nuses-permission ×6"]
setMetaWearablesConfig --> ensureMetaApplicationId["main application\nmeta-data: APPLICATION_ID"]
setMetaWearablesConfig --> ensureMetaClientToken["main application\nmeta-data: CLIENT_TOKEN"]
setMetaWearablesConfig --> ensureCallbackIntentFilter["main activity\nintent-filter: VIEW + scheme"]
setMetaWearablesConfig --> ensureWindowSoftInputMode["main activity\nandroid:windowSoftInputMode=adjustPan"] Flows
Flow: setMetaWearablesConfig
- Core files:
main/app/plugins/with-meta-wearables-android.js
Types
Props {
applicationId?: string (Meta wearables application ID; omit in devMode)
clientToken?: string (Meta wearables client token)
callbackScheme?: string (URL scheme for Meta View callback; falls back to config.scheme, then "myexampleapp")
devMode?: boolean (if true, applicationId is hard-coded to "0")
}
Paths
| path | input | output | path-type | notes |
|---|---|---|---|---|
setMetaWearablesConfig.full | all props present | permissions injected, META_APPLICATION_ID + META_CLIENT_TOKEN meta-data written, intent filter added, windowSoftInputMode set | happy path | standard production build |
setMetaWearablesConfig.devMode | devMode: true | same as full but applicationId = "0" | happy path | for development / simulator builds |
setMetaWearablesConfig.noApplicationId | no applicationId and no devMode | permissions and intent filter injected; META_APPLICATION_ID meta-data skipped; strings.xml entry skipped | degraded | plugin guards on null applicationId |
setMetaWearablesConfig.noClientToken | no clientToken | META_CLIENT_TOKEN meta-data skipped | degraded | plugin guards on null clientToken |
Pseudocode
// Permissions injected into <manifest>
REQUIRED_PERMISSIONS = [
BLUETOOTH, BLUETOOTH_CONNECT, INTERNET,
POST_NOTIFICATIONS, FOREGROUND_SERVICE, FOREGROUND_SERVICE_DATA_SYNC
]
for each permission → add <uses-permission> if not already present
// Meta SDK meta-data on <application>
if applicationId → addMetaDataItem(APPLICATION_ID, @string/meta_wearable_application_id)
if clientToken → addMetaDataItem(CLIENT_TOKEN, clientToken)
// URL-scheme intent filter on <activity android:name=".MainActivity">
if scheme and filter not already present:
add intent-filter { VIEW action, DEFAULT + BROWSABLE categories, scheme data }
// Keyboard behaviour on main activity
mainActivity.$["android:windowSoftInputMode"] = "adjustPan"
// strings.xml
if applicationId → set string item meta_wearable_application_id = applicationId
Key invariant: ensureWindowSoftInputMode sets adjustPan unconditionally on the main activity. This tells Android to pan (scroll) the visible area rather than resizing the window when the keyboard appears.
chat.tsx uses a manual keyboard-offset spacer (keyboardOffset state + <View height={keyboardOffset+20}>) that assumes the window stays full-height. With adjustPan, the window is NOT resized, so the spacer correctly lifts the InputDock above the keyboard. With adjustResize, the window would shrink by keyboard height AND the spacer would add keyboard height again — double-counting — pushing InputDock to the top of the screen.
See docs/bugs/2026-05-17-chat-inputdock-jumps-to-top-on-keyboard-open.md for the full regression analysis.
Logs
| Source | Location |
|---|---|
| Expo prebuild | stdout during npx expo prebuild or npx expo run:android |
Deployment
- Mechanism:
local only(Expo config plugin — runs at build/prebuild time, not at runtime) - Deploy command:
- Notes: Changes to this plugin take effect on the next
expo prebuildorexpo run:android. The generatedAndroidManifest.xmllives inmain/app/android/app/src/main/AndroidManifest.xmlafter prebuild; do not edit it by hand as it will be overwritten.windowSoftInputMode="adjustPan"is required to match the manual keyboard-offset spacer inchat.tsx—adjustResizewould double-count keyboard height and push the InputDock to the top of the screen.