@ouidesigner/ouider-device
v0.0.7
Published
Typed device feature modules for the OUID bridge.
Readme
OUIDer Device
@ouidesigner/ouider-device adds typed, modular device APIs on top of the OUID bridge. Importing the package installs device modules on the default OUID instance without changing the low-level bridge behavior.
import '@ouidesigner/ouider-device'
const photo = await OUID.camera.takePicture({ quality: 80 })
const samePhoto = await OUID.camera().takePicture()
const file = await OUID.filePicker.pickFile({ mimeTypes: ['application/pdf'] })
const images = await OUID.imagePicker.pickImages({ maxWidth: 1200, includeDataUrl: true })
await OUID.fileSystem.writeText('notes/today.txt', 'Hello from OUIDDevice')
const note = await OUID.fileSystem.readText('notes/today.txt')
const entries = await OUID.fileSystem.readdir('notes')
const camera = await OUID.camera.createSession({ width: 320, height: 240, fps: 8, forScan: true })
await camera?.setTorch(true)
await camera?.setZoom(2)
const offQr = await camera?.onQRCode(result => console.log(result.rawValue))
const offFrame = await camera?.onFrame((frame) => {
canvasContext.drawImage(dataUrlToImage(frame.dataUrl), 0, 0)
})
await camera?.start()
const savedImage = await camera?.saveFrame()
await camera?.startRecording()
const video = await camera?.stopRecording()
await offFrame?.()
await offQr?.()
await camera?.close()
const stopGyroscope = await OUID.gyroscope.listen((reading) => {
console.log(reading.x, reading.y, reading.z)
})
const heading = await OUID.compass.read()
const auth = await OUID.biometry.authenticate({ title: 'Unlock', reason: 'Confirm your identity' })
const offBluetooth = await OUID.bluetooth.ble.onDeviceFound(device => console.log(device.id, device.name))
await OUID.bluetooth.ble.startScan()
const offBeacon = await OUID.bluetooth.beacon.onFound(beacon => console.log(beacon.type, beacon.rssi))
await OUID.bluetooth.beacon.startScan()
const offNfc = await OUID.nfc.onTag(tag => console.log(tag.id, tag.techTypes))
await OUID.nfc.startScan({ techs: ['ndef', 'nfcisodep'] })
const ndef = await OUID.nfc.ndef.read()
const apdu = await OUID.nfc.nfcisodep.transceive({ hex: '00A4040000' })
const offWifi = await OUID.wifi.onChange(state => console.log(state.connected))
const networks = await OUID.wifi.scanResults()
const position = await OUID.location.current()
const memory = await OUID.memory.read()
const qr = await OUID.qrScan.scan({ timeoutMs: 15000 })
await OUID.keyboard.hide()
await OUID.screen.setCaptureProtection(true)
await OUID.screen.setBrightness(0.8)
await OUID.screen.lockOrientation('landscape')
await OUID.screen.setImmersiveMode(true)
await OUID.media.audio.play('/assets/click.mp3')
const preview = OUID.media.video('#video-preview')
await preview.setSrc('/assets/intro.mp4')
await preview.play()
const nativeVideo = OUID.media.nativeVideo('#native-video')
await nativeVideo.attach({ src: 'assets/intro.mp4', fit: 'contain' })
await nativeVideo.play()
await nativeVideo.sync()
await nativeVideo.load({
url: 'https://example.com/live/stream.m3u8',
fit: 'cover',
headers: { Authorization: 'Bearer token' },
minBufferMs: 15000,
maxBufferMs: 50000
})
const recording = await OUID.recording.manager({ bitRate: 128000 })
await recording?.start()
const audioFile = await recording?.stop()
const player = await OUID.audioPlayer.play({ path: audioFile?.path, volume: 0.8 })
await OUID.audioPlayer.release(player?.playerId)
const pickedVideo = await OUID.media.pickVideo({ copyToCache: true, compress: true })
const compressedVideo = await OUID.media.compressVideo({ path: pickedVideo?.file?.path, preset: 'medium' })
const image = await OUID.imagePicker.pickImage({ copyToCache: true })
const compressedImage = await OUID.media.compressImage({ path: image?.file?.path, maxWidth: 1200, quality: 82 })
await OUID.clipboard.writeText('copied from mini app')
const pickedContact = await OUID.contact.pick()
await OUID.contacts.add({ givenName: 'Awa', familyName: 'Traore', phone: '+22370000000', mode: 'auto' })
await OUID.calendar.createEvent({
title: 'Customer visit',
start: Date.now() + 3600000,
end: Date.now() + 7200000
})
await OUID.accessibility.announce('Saved')
await OUID.share.text('Receipt ready')
await OUID.share.file({ path: 'receipts/latest.pdf', directory: 'documents', mimeType: 'application/pdf' })
await offWifi()The host runtime must provide the matching native implementation. For example, OUID.camera.takePicture() calls the host method _ouid_camera with the takePicture action, OUID.filePicker.pick() calls _ouid_filePicker, and OUID.accelerometer.listen() subscribes through OUID.onHostEvent('accelerometerchange', ...).
Manifest Permissions
Mini apps must ship a manifest.json next to index.html, main.bundle.js, and preload.bundle.js. The host SDK reads it before exposing native module calls and host events. Missing permissions are rejected by the bridge before the native module runs.
{
"schemaVersion": 1,
"id": "com.example.app",
"name": "Example App",
"version": "1.0.0",
"permissions": {
"camera": { "reason": "Capture receipts" },
"biometry": { "reason": "Protect sensitive actions", "access": ["authenticate"] },
"contacts": { "reason": "Pick or create a customer", "access": ["pick", "add"] },
"wifi": { "reason": "Show connectivity", "access": ["read", "scan", "events"] },
"location": { "reason": "Show nearby service points", "access": ["read", "events"] },
"calendar": { "reason": "Create appointment reminders", "access": ["createEvent"] },
"memory": { "reason": "React to memory pressure", "access": ["read", "events"] },
"qrScan": { "reason": "Scan check-in QR codes", "access": ["scan", "decode", "events"] },
"keyboard": { "reason": "Adjust forms around the keyboard", "access": ["events", "hide"] },
"screen": { "reason": "Protect sensitive content", "access": ["setCaptureProtection", "setBrightness", "events"] },
"media": { "reason": "Record audio and process local media", "access": ["audioContext", "recording", "nativeVideo", "pickVideo", "compressImage", "events"] },
"share": { "reason": "Share generated files", "access": ["text", "file", "files"] }
}
}The schema is available at schemas/ouid-manifest.schema.json. OS-level permissions are still requested by the native module; the manifest only declares which bridge capabilities the mini app is allowed to use.
Device APIs
OUID.wifi: read Wi-Fi status, request/open settings, scan/connect on Android, and listen to Wi-Fi events.OUID.location: read the current position, start/stop location updates, and listen tolocationchange.OUID.calendar: list calendars, list events, create events, update events, and delete events.OUID.memory: read memory state and listen tomemorywarn.OUID.qrScan/OUID.qr: scan QR codes from the camera or decode from image paths/base64.OUID.keyboard: observe keyboard height, hide the keyboard, and read or set focused input selection range.OUID.screen: read orientation, lock/unlock orientation, toggle immersive mode, set per-window brightness, keep the screen awake, protect sensitive content from capture where the OS allows it, and listen to screenshot/recording state events. On Android, screenshot events require API 34+, theDETECT_SCREEN_CAPTUREpermission, and a real user screenshot; ADB or emulator-tool screenshots do not trigger the callback.OUID.media: use WebViewAudioContext, control HTML video, render native underlay video, stream remote video, pick videos, compress images/videos, play native audio files, and record audio.OUID.recording: create managed audio recording sessions or use unmanagedstart(),pause(),resume(), andstop()calls.OUID.audioPlayer: play, pause, seek, stop, and release native file audio players.OUID.bluetooth: read Bluetooth status, use Android classic discovery and paired-device checks, scan/connect/read/write BLE devices, and scan BLE beacons.OUID.nfc: scan NFC tags, read/write NDEF, and transceive through NFC-A/B/F/V, ISO-DEP, and MiFare helpers (nfcmifireis accepted as a MiFare alias).OUID.biometry: check biometric availability and authenticate with Face ID, Touch ID, fingerprint, or device credential fallback.OUID.compass: start/read/stop heading updates and listen tocompasschange.OUID.clipboard: read, write, check, and clear text clipboard content.OUID.contact/OUID.contacts: request permission, list/search contacts, open the native picker, and create contacts.OUID.accessibility: read accessibility state, announce text, open settings, and listen toaccessibilitychange.OUID.share: open the native share sheet for text, URLs, and local files. Remote image/file fetching is intentionally not performed by the module.
Bluetooth Platform Notes
Android supports classic Bluetooth paired-device listing, classic discovery, bond-change events, BLE scanning/GATT operations, and beacon scanning. iOS supports BLE scanning/GATT operations and beacon scanning through CoreBluetooth, but iOS does not expose classic Bluetooth discovery, Bluetooth MAC addresses, a general paired-device list, or pairing state to apps. On iOS, pairedDevices(), isPaired(), and classic discovery helpers return an unsupported success payload or an empty list; use OUID.bluetooth.ble or OUID.bluetooth.beacon for cross-platform behavior.
Android-only classic Bluetooth APIs: pairedDevices(), bondedDevices(), isPaired(), isBonded(), getDevice(), startDiscovery(), discover(), stopDiscovery(), foundDevices(), discoveredDevices(), isDiscovering(), onDeviceFound(), onDevicesFound(), onDiscoveryChange(), and onBondChange(). The related classic events bluetoothdevicefound, bluetoothdevicesfound, bluetoothdiscoverychange, and bluetoothbondchange are not emitted on iOS. Use BLE scan/connection APIs for shared Android/iOS behavior.
Native Video Notes
OUID.media.nativeVideo() accepts local bundle paths, file:// or content:// URIs, and http:// or https:// URLs. Android uses Media3 ExoPlayer and supports progressive streams, HLS .m3u8, DASH .mpd, and SmoothStreaming manifests. iOS uses AVPlayer and supports the formats provided by AVFoundation, including HLS. For remote sources, the host app must declare INTERNET on Android. Plain http:// streams also require Android cleartext network policy and iOS App Transport Security exceptions when the host allows them.
Use headers and userAgent for authenticated Android HTTP streams. Buffer options are in milliseconds: minBufferMs, maxBufferMs, bufferForPlaybackMs, and bufferForPlaybackAfterRebufferMs.
NFC Platform Notes
Android uses NFC reader mode and supports NDEF, NFC-A, NFC-B, NFC-F, NFC-V, ISO-DEP, and MiFare transceive when the device chipset exposes the tag technology. iOS uses CoreNFC, requires NFCReaderUsageDescription plus NFC entitlements in the host app, and always displays the system NFC sheet. iOS supports NDEF, MiFare command packets, FeliCa/NFC-F commands, ISO-DEP APDU commands, and ISO15693 custom commands; it does not expose raw NFC-B transceive, and raw NFC-A is only available when CoreNFC reports the tag as MiFare.
FileSystem
OUID.fileSystem provides app-private file access without requiring broad storage permissions. OUID.file and OUID.fs are aliases. Relative paths are resolved under a directory root:
data/files: private app filesdocuments: app document storagecache: app cachetmp/temp: temporary filesexternal/externalCache: Android app-specific external storage; mapped to app document/cache roots on iOS
Supported operations include readFile, writeFile, appendFile, deleteFile, mkdir, rmdir, readdir, stat, exists, rename, copy, and getUri. Use encoding: 'base64' for binary payloads, or includeDataUrl: true when a read result should include a data URL.
On Android, UI-backed features such as camera, image picker, file picker, and runtime permission prompts need an activity host. Keep the runtime context as an application context if desired, then install the native module with the current FragmentActivity:
OUI.setup(applicationContext, ...)
OUIDDevice.install(this)If the host manages activity lifecycles separately, install with a provider instead:
OUIDDevice.install { currentActivity }For Flutter hosts, use FlutterFragmentActivity and pass that activity to the native OUIDDevice.install(...).
For custom bridge instances:
import { installOUIDevice } from '@ouidesigner/ouider-device'
const device = installOUIDevice(customBridge)
await device.vibration.vibrate(120)