Skip to content

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 Android AndroidManifest.xml and strings.xml at 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, and android:windowSoftInputMode="adjustPan" on the main activity. It runs during expo prebuild / expo run:android and 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:
    # From main/app/
    npx expo run:android
    # or for a full manifest regeneration
    npx expo prebuild --platform android
    
  • Notes: Changes to this plugin take effect on the next expo prebuild or expo run:android. The generated AndroidManifest.xml lives in main/app/android/app/src/main/AndroidManifest.xml after prebuild; do not edit it by hand as it will be overwritten. windowSoftInputMode="adjustPan" is required to match the manual keyboard-offset spacer in chat.tsxadjustResize would double-count keyboard height and push the InputDock to the top of the screen.