Skip to content

Implement native Android PebbleBridge#300

Draft
caco3 wants to merge 4 commits into
coredevices:masterfrom
caco3:feature/pebble-bridge
Draft

Implement native Android PebbleBridge#300
caco3 wants to merge 4 commits into
coredevices:masterfrom
caco3:feature/pebble-bridge

Conversation

@caco3

@caco3 caco3 commented Jul 18, 2026

Copy link
Copy Markdown

This PR introduces a native Android bridge (PebbleBridge) that lets watchapp config pages running in the mobile app’s WebView access capabilities that are normally blocked by WebView sandboxing, CORS, mixed-content rules, or JavaScript restrictions. See #301 for details.

When a watchapp declares the config_network_bridge capability, its config page receives a window.pebbleBridge object that delegates work to the native side. This allows a config page to:

  • Fetch from any HTTP/HTTPS endpoint (window.pebbleBridge.fetch) — including local or self-hosted services that would otherwise be unreachable from a mobile WebView.
  • Open WebSocket connections (window.pebbleBridge.WebSocket) — for streaming live data without WebSocket CORS issues.
  • Persist small amounts of data securely (window.pebbleBridge.storage) — using the app’s encrypted storage instead of localStorage, which is not shared across sessions or reliable in a WebView.
  • Close the config page and return data to the watchapp (window.pebbleBridge.close) — replacing the legacy pebblejs://close# URL scheme.

The bridge is opt-in per watchapp and only activates when the app declares the config_network_bridge capability, so existing watchapps are unaffected.

Example use cases

For easy testing, I created a minimal demo watchapp, see https://github.com/caco3/bridge-demo. It demonstrates internet access through the config page.

Key changes

  • Added NetworkBridge (config_network_bridge) capability detection in LockerAppScreen and PebbleRoutes.
  • Added a bridgeEnabled flag to WatchappSettingsRoute and passed it into WatchappSettingsScreen.
  • Implemented PebbleBridgeManager, which registers PebbleBridgeNativeInterface on the WebView and injects a JS shim that creates window.pebbleBridge.
  • Implemented native bridge APIs:
    • fetch(url, options) — HTTP via Ktor/OkHttp
    • WebSocket(url, protocols) — WebSocket via Ktor with message/close/error forwarding
    • storage.get/set/remove — Android EncryptedSharedPreferences
    • close(returnValue) — closes the config page and returns data to the app
  • Added BridgeConfigParser to read config values from the URL hash fragment.
  • Added BridgeModels, BridgeFetch, BridgeStorage, and BridgeWebSocket modules.
  • Updated the iOS webViewFactory signature for source compatibility (no iOS implementation yet).

Backward compatibility

Apps that do not declare config_network_bridge see no change.

Documentation

See coredevices/sdk-docs#17

High-level architecture

┌──────────────────────────────────────────────┐
│  Watch app config page (HTML/JS)             │
│  window.pebbleBridge.fetch(...)              │
│  window.pebbleBridge.WebSocket(...)          │
│  window.pebbleBridge.storage.get/set(...)    │
└──────────────────────────────────────────────┘
                       │
                       ▼
┌──────────────────────────────────────────────┐
│  JS shim (assets/bridge-shim.js)             │
│  Creates Promise-based API from native       │
└──────────────────────────────────────────────┘
                       │
                       ▼
┌──────────────────────────────────────────────┐
│  PebbleBridgeManager (Android)               │
│  - Shared Ktor/OkHttp HttpClient             │
│  - BridgeFetch, BridgeWebSocket              │
│  - BridgeStorage (EncryptedSharedPreferences)│
│  - PebbleBridgeNativeInterface (@JS)         │
└──────────────────────────────────────────────┘
                       │
      ┌────────────────┼────────────────┐
      ▼                ▼                ▼
  Ktor/OkHttp     Ktor/OkHttp     EncryptedShared
  fetch           WebSocket         Preferences

Notes

  • iOS support is intentionally out of scope; the iOS factory signature was updated only to keep the project compiling. I don't have access to iOS thus am unable to implement and test it for iOS.
  • No public API changes outside the Pebble module.
  • The code was generated using AI. My Kotlin skills are very limited, but I reviewed it am confident that it is ok.

- Add fetch, WebSocket, encrypted storage, and close() bridge APIs
- Inject window.pebbleBridge JS shim via WebView addJavascriptInterface
- Gate injection behind config_network_bridge app capability
- Maintain legacy pebblejs://close URL interception for compatibility
- Place bridge code in pebble module androidMain/commonMain source sets
@CLAassistant

CLAassistant commented Jul 18, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

…ine shim

- Add .open()/.message()/.error()/.close() event methods native expects
- Accept fetch response as either JSON string or already-parsed object
@caco3
caco3 force-pushed the feature/pebble-bridge branch 2 times, most recently from b932207 to bc22997 Compare July 18, 2026 14:18
- Move inline JS shim from PebbleBridgeManager to assets/bridge-shim.js
- Share a single Ktor/OkHttp HttpClient between BridgeFetch and BridgeWebSocket
- Add WebSocket subprotocol support via Sec-WebSocket-Protocol header
- Replace org.json.JSONObject.quote and manual escapeJson with kotlinx.serialization
- Add dispose() path that tears down without firing onClose
- Remove runBlocking(Dispatchers.Main) in Android factory
- Restore localStorage after page finishes loading (LoadingState.Finished)
@caco3
caco3 force-pushed the feature/pebble-bridge branch from 50dc77c to 2b489e5 Compare July 18, 2026 14:40
The HttpClient is shared with BridgeFetch and owned by PebbleBridgeManager.
Closing it in BridgeWebSocket.dispose would break fetch operations.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants