Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 61 additions & 8 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,15 +173,11 @@ Add to `Info.plist`:
<string>fb-viewapp</string>
</array>

<key>UISupportedExternalAccessoryProtocols</key>
<array>
<string>com.meta.ar.wearable</string>
</array>

<!-- Camera transport (Wi-Fi or Bluetooth Classic) is configured separately —
see "Choose your camera transport" below. -->
<key>UIBackgroundModes</key>
<array>
<string>bluetooth-peripheral</string>
<string>external-accessory</string>
</array>

<!-- Deep link callback from Meta AI app -->
Expand Down Expand Up @@ -213,6 +209,60 @@ Add to `Info.plist`:
</dict>
```

#### Choose your camera transport: Wi‑Fi (recommended) or Bluetooth Classic

The SDK streams over one of two high-bandwidth links; on iOS you pick which **purely via `Info.plist` + entitlements** (no runtime switch). Configure exactly one.

**Wi‑Fi (recommended)** — higher bandwidth. Triggers a one-time *"Join Wi‑Fi Network"* prompt and joins the glasses' AP (phone internet then rides cellular). Add to `Info.plist`:

```xml
<key>NSLocalNetworkUsageDescription</key>
<string>Allows your phone to find and connect to your glasses over Wi-Fi.</string>
<key>NSBonjourServices</key>
<array>
<string>_bonjour._tcp</string>
</array>
```

plus a `.entitlements` file wired to the target via `CODE_SIGN_ENTITLEMENTS` (Xcode → Signing & Capabilities → add **Access Wi‑Fi Information** + **Hotspot Configuration** does this automatically; a hand-created file is silently ignored until that build setting points at it in every Runner configuration):

```xml
<key>com.apple.developer.networking.HotspotConfiguration</key>
<true/>
<key>com.apple.developer.networking.wifi-info</key>
<true/>
```

Do **not** add the ExternalAccessory keys below — while present, the SDK uses Bluetooth Classic and never brings up Wi‑Fi.

**Bluetooth Classic** — no prompt, keeps the phone on its normal Wi‑Fi, works offline, but lower bandwidth. Add to `Info.plist`:

```xml
<key>UISupportedExternalAccessoryProtocols</key>
<array>
<string>com.meta.ar.wearable</string>
</array>

<key>UIBackgroundModes</key>
<array>
<string>bluetooth-peripheral</string>
<string>external-accessory</string>
</array>
```

> **App Store note:** transport does not affect App Store eligibility — the DAT SDK links `ExternalAccessory.framework` regardless of transport (an MFi binary-scanner concern), and Meta limits public publishing to select partners until GA. Wi‑Fi's advantage is bandwidth, not publishability.

#### Migrating or switching camera transport

Switching transport is a **config-only change** — no plugin update or Dart code involved. Two scenarios:

- **Upgrading an app configured before this transport choice was documented** — it already has `com.meta.ar.wearable` + `external-accessory` (Bluetooth Classic) and keeps working unchanged; migrating to Wi‑Fi is optional.
- **One transport isn't working** — Wi‑Fi never prompts / won't join the AP, or Bluetooth Classic streaming is unreliable — switch to the other the same way.

**To switch to Wi‑Fi:** remove `UISupportedExternalAccessoryProtocols` + `external-accessory`, add the Wi‑Fi `Info.plist` keys + entitlements above, then **verify the entitlements wiring** — a `.entitlements` file is silently ignored unless `CODE_SIGN_ENTITLEMENTS` points at it in every Runner build configuration (Xcode → Signing & Capabilities → add the two capabilities does this automatically; check by hand with `grep CODE_SIGN_ENTITLEMENTS ios/Runner.xcodeproj/project.pbxproj`). Delete the app from the test device before reinstalling to clear stale Bluetooth Classic accessory state, then rebuild.

**To switch to Bluetooth Classic:** remove the Wi‑Fi keys + entitlements, add the ExternalAccessory keys above, rebuild.

### 3. Android configuration

**AndroidManifest.xml:**
Expand Down Expand Up @@ -394,15 +444,15 @@ await MetaWearablesDat.stopStreamSession(null);
await MetaWearablesDat.disableBackgroundStreaming();
```

**Additional iOS `Info.plist` entries** (required only if you call `enableBackgroundStreaming`): add `audio` and `bluetooth-central` alongside the existing `bluetooth-peripheral` and `external-accessory`:
**Additional iOS `Info.plist` entries** (required only if you call `enableBackgroundStreaming`): add `audio` and `bluetooth-central` alongside `bluetooth-peripheral` (and `external-accessory` only if you use the Bluetooth Classic transport):

```xml
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
<string>bluetooth-central</string>
<string>bluetooth-peripheral</string>
<string>external-accessory</string>
<string>external-accessory</string> <!-- only if using the Bluetooth Classic transport -->
</array>
```

Expand Down Expand Up @@ -525,6 +575,9 @@ Stream not starting?
├── On Android: MainActivity extends FlutterFragmentActivity?
├── On Android: GitHub token configured for Maven dependency?
└── Try restarting glasses: power off → hold capture button → power on → release when LED turns red

iOS: Wi-Fi never prompts, or one transport streams unreliably?
└── Switch transports — see "Migrating or switching camera transport" (config-only, no code change)
```

## Links
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## 0.7.1
* Docs: document the iOS Wi‑Fi vs Bluetooth Classic camera transport choice, with migration/switching steps.

## 0.7.0
**BREAKING CHANGES**
* Update to Meta Wearables DAT **0.8.0** on both platforms.
Expand Down
73 changes: 66 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ A Flutter plugin that provides a bridge to Meta's Wearables Device Access Toolki
- [Setup](#setup)
- [Glasses setup (Developer mode)](#glasses-setup-developer-mode)
- [iOS Configuration](#ios-configuration)
- [Choose your camera transport: Wi‑Fi (recommended) or Bluetooth Classic](#choose-your-camera-transport-wifi-recommended-or-bluetooth-classic)
- [Migrating or switching camera transport](#migrating-or-switching-camera-transport)
- [Android Configuration](#android-configuration)
- [1. AndroidManifest.xml](#1-androidmanifestxml)
- [2. Repository Configuration](#2-repository-configuration)
Expand Down Expand Up @@ -82,15 +84,11 @@ Add the following to your `Info.plist`:
<string>fb-viewapp</string>
</array>

<key>UISupportedExternalAccessoryProtocols</key>
<array>
<string>com.meta.ar.wearable</string>
</array>

<!-- Camera transport (Wi-Fi or Bluetooth Classic) is configured separately —
see "Choose your camera transport" below. -->
<key>UIBackgroundModes</key>
<array>
<string>bluetooth-peripheral</string>
<string>external-accessory</string>
</array>

<!-- Deep link callback from Meta AI app - scheme must match AppLinkURLScheme below -->
Expand Down Expand Up @@ -127,6 +125,66 @@ Add the following to your `Info.plist`:
</dict>
```

#### Choose your camera transport: Wi‑Fi (recommended) or Bluetooth Classic

The DAT SDK streams video over one of two high-bandwidth links to the glasses, and on iOS **you select which one purely through `Info.plist` + entitlements — there is no runtime switch.** Configure exactly one.

**Wi‑Fi (recommended).** Higher bandwidth (better resolution / frame rate, faster photo capture). On the first stream the SDK joins the glasses' Wi‑Fi access point, so iOS shows a one-time *"Join Wi‑Fi Network"* prompt and the phone associates to that AP — its internet then rides cellular, which matters if you also upload frames to the cloud. Add to `Info.plist`:

```xml
<key>NSLocalNetworkUsageDescription</key>
<string>Allows your phone to find and connect to your glasses over Wi-Fi.</string>
<key>NSBonjourServices</key>
<array>
<string>_bonjour._tcp</string>
</array>
```

and add these to your app's `.entitlements` (in Xcode: **Signing & Capabilities → + Capability → Access Wi‑Fi Information** and **Hotspot Configuration** — which wires the file to the target via `CODE_SIGN_ENTITLEMENTS` and provisions the capabilities on your App ID; a hand-created `.entitlements` file is silently ignored until that build setting points at it in every Runner build configuration):

```xml
<key>com.apple.developer.networking.HotspotConfiguration</key>
<true/>
<key>com.apple.developer.networking.wifi-info</key>
<true/>
```

Do **not** add the ExternalAccessory keys below — while they are present the SDK fills its high-bandwidth lease over Bluetooth Classic and never brings up Wi‑Fi.

**Bluetooth Classic.** No Wi‑Fi prompt, keeps the phone on its normal Wi‑Fi, and needs no Wi‑Fi infrastructure — but lower bandwidth. Add to `Info.plist`:

```xml
<key>UISupportedExternalAccessoryProtocols</key>
<array>
<string>com.meta.ar.wearable</string>
</array>

<key>UIBackgroundModes</key>
<array>
<string>bluetooth-peripheral</string>
<string>external-accessory</string>
</array>
```

> The example app is configured for **Wi‑Fi** — see [`example/ios/Runner/Info.plist`](example/ios/Runner/Info.plist) and [`example/ios/Runner/Runner.entitlements`](example/ios/Runner/Runner.entitlements).

> **App Store note.** Transport choice does **not** affect App Store eligibility. The DAT SDK links `ExternalAccessory.framework` regardless of transport (Apple's binary scanner flags this MFi dependency), and Meta currently limits public publishing to select partners (see [Publishing disclaimer](#publishing-disclaimer)) until GA. Wi‑Fi's advantage is bandwidth, not publishability.

#### Migrating or switching camera transport

Switching transport — whether upgrading an older app or troubleshooting one that isn't working — is a **config-only change**: remove one recipe's keys above, add the other's, rebuild. No plugin update or Dart code change is involved.

- **Upgrading an app configured before this transport choice was documented.** Your `Info.plist` likely already has `UISupportedExternalAccessoryProtocols` (`com.meta.ar.wearable`) and `external-accessory` — that's the Bluetooth Classic recipe above, and it keeps working unchanged. Wi‑Fi is optional; migrate whenever it's convenient using the steps below.
- **One transport isn't working.** Wi‑Fi never shows the join prompt, the phone won't associate to the glasses' AP, or Bluetooth Classic streaming is unreliable: switch to the other transport with the same steps — the two are otherwise interchangeable.

**To switch to Wi‑Fi:**
1. Remove `UISupportedExternalAccessoryProtocols` and `external-accessory` (from `UIBackgroundModes`) from `Info.plist`.
2. Add the two Wi‑Fi `Info.plist` keys and the two entitlements from the Wi‑Fi recipe above.
3. **Don't skip the entitlements wiring** — the most common reason this migration silently "does nothing" is an `.entitlements` file that was created but never attached to the target. Add the capabilities via Xcode (**Signing & Capabilities → + Capability → Access Wi‑Fi Information** + **Hotspot Configuration**), which wires `CODE_SIGN_ENTITLEMENTS` automatically — or if editing `project.pbxproj` by hand, confirm `CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;` is set in **every** Runner build configuration (Debug, Release, Profile): `grep CODE_SIGN_ENTITLEMENTS ios/Runner.xcodeproj/project.pbxproj`.
4. Delete the app from your test device before reinstalling — this clears any stale Bluetooth Classic accessory pairing state — then rebuild. The first stream should show the "Join Wi‑Fi Network" prompt.

**To switch to Bluetooth Classic:** remove the Wi‑Fi `Info.plist` keys and entitlements, add the ExternalAccessory keys from the Bluetooth Classic recipe above, rebuild. No prompt, no entitlements needed.

### Android Configuration

#### 1. AndroidManifest.xml
Expand Down Expand Up @@ -371,7 +429,7 @@ await MetaWearablesDat.disableBackgroundStreaming();
<string>audio</string>
<string>bluetooth-central</string>
<string>bluetooth-peripheral</string>
<string>external-accessory</string>
<string>external-accessory</string> <!-- only if using the Bluetooth Classic transport -->
</array>
```

Expand Down Expand Up @@ -515,6 +573,7 @@ If you run into issues, try these steps first:
- **From official docs**: See [Known Issues](https://wearables.developer.meta.com/docs/knownissues), [FAQ](https://developers.meta.com/wearables/faq/) and [Report a bug](https://wearables.developer.meta.com/devcenter/feedback/).

Common issues:
- **Wi‑Fi never prompts, or one transport streams unreliably** — see [Migrating or switching camera transport](#migrating-or-switching-camera-transport); switching between Wi‑Fi and Bluetooth Classic is a config-only change, no code required.
- **Registration deep link not returning** — If registration opens the Meta AI app but the callback does not return to your app, verify that your URL scheme matches the one registered in the [Meta Wearables Developer Center](https://wearables.developer.meta.com/devcenter). On iOS, ensure `CFBundleURLSchemes` in `Info.plist` (and `AppLinkURLScheme` in the `MWDAT` dict) use the same scheme. On Android, ensure the `data android:scheme` in your activity's intent-filter matches that scheme.

**Still having issues?** — Open a [GitHub issue](https://github.com/rodcone/flutter_meta_wearables_dat/issues) with all the details you can provide. This helps us pinpoint the problem and assist you more efficiently.
Expand Down
13 changes: 11 additions & 2 deletions agent/claude/commands/setup-dat.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,20 @@ Audit and configure this Flutter project for flutter_meta_wearables_dat integrat
3. **iOS audit** — Read `ios/Runner/Info.plist` and verify these keys exist:
- `NSBluetoothAlwaysUsageDescription`
- `LSApplicationQueriesSchemes` containing `fb-viewapp`
- `UISupportedExternalAccessoryProtocols` containing `com.meta.ar.wearable`
- `UIBackgroundModes` containing `bluetooth-peripheral` and `external-accessory`
- `UIBackgroundModes` containing `bluetooth-peripheral`
- `CFBundleURLTypes` with a URL scheme
- `MWDAT` dict with `AppLinkURLScheme`, `MetaAppID`, `ClientToken`, `TeamID`, `Analytics`

Then check the **camera transport** (exactly one must be configured):
- **Wi‑Fi (recommended):** in `Info.plist`, `NSLocalNetworkUsageDescription` + `NSBonjourServices` (`_bonjour._tcp`); the `com.apple.developer.networking.HotspotConfiguration` + `com.apple.developer.networking.wifi-info` entitlements in the app's `.entitlements` file; and the ExternalAccessory keys **absent** (while present they force Bluetooth Classic and disable Wi‑Fi). **An `.entitlements` file is ignored unless it is wired to the target** — checking its contents is not enough. Confirm `CODE_SIGN_ENTITLEMENTS` points at it in *every* Runner app build configuration: grep `ios/Runner.xcodeproj/project.pbxproj` for `CODE_SIGN_ENTITLEMENTS` (it must appear in the Debug, Release, and Profile configs of the Runner app target), or run `cd ios && xcodebuild -showBuildSettings -target Runner | grep CODE_SIGN_ENTITLEMENTS`. If the file is missing or unwired, the audit must **not** pass — create the file and add `CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;` to each Runner config (or add the two capabilities via Xcode → Signing & Capabilities, which wires it automatically).
- **Bluetooth Classic:** `UISupportedExternalAccessoryProtocols` containing `com.meta.ar.wearable`, and `external-accessory` in `UIBackgroundModes`.

(Transport does not affect App Store eligibility — the SDK links `ExternalAccessory.framework` regardless, and Meta limits public publishing to select partners until GA.)

**If neither transport is configured** (fresh project), do not silently default to one — ask the user which they'd prefer (mention Wi‑Fi is recommended for bandwidth; Bluetooth Classic needs no Wi‑Fi prompt and works offline) before adding either recipe.

**If one transport is already configured** and the user wants to switch (migrating an older app to Wi‑Fi, or troubleshooting a transport that isn't working — e.g. Wi‑Fi never prompts, or streaming is unreliable), this is a config-only change: remove that transport's keys/entitlements, add the other's from the recipes above, and — if switching to Wi‑Fi — verify the `CODE_SIGN_ENTITLEMENTS` wiring per the Wi‑Fi check above before declaring it done. No Dart/plugin code changes are needed either way.

Report any missing keys and offer to add them.

4. **Android audit** — Read `android/app/src/main/AndroidManifest.xml` and verify:
Expand Down
18 changes: 12 additions & 6 deletions agent/claude/skills/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,10 @@ Add to `ios/Runner/Info.plist`:
<string>fb-viewapp</string>
</array>

<key>UISupportedExternalAccessoryProtocols</key>
<array>
<string>com.meta.ar.wearable</string>
</array>

<!-- Camera transport is configured separately — see "iOS camera transport" below. -->
<key>UIBackgroundModes</key>
<array>
<string>bluetooth-peripheral</string>
<string>external-accessory</string>
<!-- Optional: required only if you call MetaWearablesDat.enableBackgroundStreaming()
to keep the stream alive while backgrounded or the phone is locked. -->
<!-- <string>audio</string> -->
Expand Down Expand Up @@ -67,6 +62,17 @@ Add to `ios/Runner/Info.plist`:

Replace `myexampleapp` with your app's URL scheme. Use `0` for MetaAppID in Developer Mode, or your real ID from the Meta Wearables Developer Center for production.

### iOS camera transport: Wi‑Fi (recommended) or Bluetooth Classic

Pick one (selected purely via `Info.plist` + entitlements — no runtime switch):

- **Wi‑Fi (recommended)** — higher bandwidth. Add `NSLocalNetworkUsageDescription` and `NSBonjourServices` (`_bonjour._tcp`) to `Info.plist`, plus the `com.apple.developer.networking.HotspotConfiguration` and `com.apple.developer.networking.wifi-info` entitlements (Xcode → Signing & Capabilities → **Access Wi‑Fi Information** + **Hotspot Configuration**). The first stream shows a one-time "Join Wi‑Fi Network" prompt. Do **not** add the ExternalAccessory keys — they force Bluetooth Classic.
- **Bluetooth Classic** — no prompt, works offline, lower bandwidth. Add `UISupportedExternalAccessoryProtocols` (`com.meta.ar.wearable`) and `external-accessory` to `UIBackgroundModes`.

Transport does not affect App Store eligibility — the SDK links `ExternalAccessory.framework` either way, and Meta limits public publishing to select partners until GA.

**Migrating or troubleshooting:** switching transport is config-only — remove one recipe's keys, add the other's, rebuild; no Dart/plugin change. If migrating an older app to Wi‑Fi, don't skip the entitlements wiring: a `.entitlements` file is ignored unless `CODE_SIGN_ENTITLEMENTS` points at it in every Runner build configuration (Xcode → Signing & Capabilities → adding the two capabilities wires this automatically; check by hand with `grep CODE_SIGN_ENTITLEMENTS ios/Runner.xcodeproj/project.pbxproj`). If Wi‑Fi never prompts or streaming misbehaves on either transport, switch to the other the same way.

## Android configuration

**android/app/src/main/AndroidManifest.xml:**
Expand Down
Loading
Loading