expo-accessory-setup 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/LICENSE +21 -0
- package/README.md +267 -6
- package/android/build.gradle +24 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/expo/modules/accessorysetup/AccessorySetupException.kt +20 -0
- package/android/src/main/java/expo/modules/accessorysetup/CompanionAssociations.kt +152 -0
- package/android/src/main/java/expo/modules/accessorysetup/ExpoAccessorySetupModule.kt +214 -0
- package/android/src/main/java/expo/modules/accessorysetup/Records.kt +31 -0
- package/app.plugin.js +1 -0
- package/build/ExpoAccessorySetup.types.d.ts +63 -0
- package/build/ExpoAccessorySetup.types.d.ts.map +1 -0
- package/build/ExpoAccessorySetup.types.js +2 -0
- package/build/ExpoAccessorySetup.types.js.map +1 -0
- package/build/ExpoAccessorySetupModule.d.ts +24 -0
- package/build/ExpoAccessorySetupModule.d.ts.map +1 -0
- package/build/ExpoAccessorySetupModule.js +4 -0
- package/build/ExpoAccessorySetupModule.js.map +1 -0
- package/build/errors.d.ts +12 -0
- package/build/errors.d.ts.map +1 -0
- package/build/errors.js +45 -0
- package/build/errors.js.map +1 -0
- package/build/index.d.ts +20 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +61 -0
- package/build/index.js.map +1 -0
- package/build/pickerOptions.d.ts +20 -0
- package/build/pickerOptions.d.ts.map +1 -0
- package/build/pickerOptions.js +64 -0
- package/build/pickerOptions.js.map +1 -0
- package/expo-module.config.json +9 -0
- package/ios/AccessorySessionController.swift +290 -0
- package/ios/AccessorySetupException.swift +50 -0
- package/ios/AccessorySetupRecords.swift +32 -0
- package/ios/ExpoAccessorySetup.podspec +31 -0
- package/ios/ExpoAccessorySetupModule.swift +67 -0
- package/mocks/ExpoAccessorySetup.ts +13 -0
- package/package.json +63 -4
- package/plugin/build/index.d.ts +8 -0
- package/plugin/build/index.js +85 -0
- package/plugin/build/options.d.ts +29 -0
- package/plugin/build/options.js +88 -0
- package/plugin/src/index.ts +122 -0
- package/plugin/src/options.ts +142 -0
- package/plugin/tsconfig.json +18 -0
- package/src/ExpoAccessorySetup.types.ts +69 -0
- package/src/ExpoAccessorySetupModule.ts +20 -0
- package/src/errors.ts +59 -0
- package/src/index.ts +87 -0
- package/src/pickerOptions.ts +100 -0
- package/tsconfig.json +28 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { Image } from 'react-native';
|
|
2
|
+
|
|
3
|
+
import type { AccessoryPlatform, ImageSource, ShowPickerOptions } from './ExpoAccessorySetup.types';
|
|
4
|
+
import { AccessorySetupError } from './errors';
|
|
5
|
+
|
|
6
|
+
type NativeFilter = {
|
|
7
|
+
/** Upper case. 128-bit form on Android, as given on iOS. */
|
|
8
|
+
serviceUUID?: string;
|
|
9
|
+
companyIdentifier?: number;
|
|
10
|
+
nameSubstring?: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type IosNativePickerOptions = NativeFilter & {
|
|
14
|
+
displayName: string;
|
|
15
|
+
productImageUri: string;
|
|
16
|
+
range: 'default' | 'immediate';
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type AndroidNativePickerOptions = NativeFilter & {
|
|
20
|
+
singleDevice: boolean;
|
|
21
|
+
namePattern?: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type NativePickerOptions = IosNativePickerOptions | AndroidNativePickerOptions;
|
|
25
|
+
|
|
26
|
+
const UUID_16 = /^[0-9A-F]{4}$/;
|
|
27
|
+
const UUID_32 = /^[0-9A-F]{8}$/;
|
|
28
|
+
const UUID_128 = /^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/;
|
|
29
|
+
const BLUETOOTH_BASE_UUID_SUFFIX = '-0000-1000-8000-00805F9B34FB';
|
|
30
|
+
|
|
31
|
+
function invalid(message: string): never {
|
|
32
|
+
throw new AccessorySetupError('INVALID_OPTIONS', message);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function parseServiceUUID(value: string, platform: AccessoryPlatform): string {
|
|
36
|
+
const uuid = value.trim().toUpperCase();
|
|
37
|
+
if (!UUID_16.test(uuid) && !UUID_32.test(uuid) && !UUID_128.test(uuid)) {
|
|
38
|
+
invalid(`filter.serviceUUID "${value}" is not a 16-bit, 32-bit, or 128-bit UUID.`);
|
|
39
|
+
}
|
|
40
|
+
if (platform === 'ios' || UUID_128.test(uuid)) {
|
|
41
|
+
return uuid;
|
|
42
|
+
}
|
|
43
|
+
return `${uuid.padStart(8, '0')}${BLUETOOTH_BASE_UUID_SUFFIX}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function parseCompanyIdentifier(value: number): number {
|
|
47
|
+
if (!Number.isInteger(value) || value < 0 || value > 0xffff) {
|
|
48
|
+
invalid(`filter.companyIdentifier ${value} is not an integer between 0 and 0xFFFF.`);
|
|
49
|
+
}
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function nonEmpty(value: string, field: string): string {
|
|
54
|
+
if (value.trim().length === 0) {
|
|
55
|
+
invalid(`${field} must not be empty.`);
|
|
56
|
+
}
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function resolveImageUri(source: ImageSource): string {
|
|
61
|
+
const uri = typeof source === 'number' ? Image.resolveAssetSource(source)?.uri : source.uri;
|
|
62
|
+
if (!uri) {
|
|
63
|
+
invalid('ios.productImage could not be resolved to a URI.');
|
|
64
|
+
}
|
|
65
|
+
return uri;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function toNativePickerOptions(
|
|
69
|
+
options: ShowPickerOptions,
|
|
70
|
+
platform: AccessoryPlatform
|
|
71
|
+
): NativePickerOptions {
|
|
72
|
+
const { serviceUUID, companyIdentifier, nameSubstring } = options.filter;
|
|
73
|
+
if (serviceUUID === undefined && companyIdentifier === undefined) {
|
|
74
|
+
invalid('filter needs a serviceUUID or a companyIdentifier.');
|
|
75
|
+
}
|
|
76
|
+
const filter: NativeFilter = {
|
|
77
|
+
serviceUUID: serviceUUID === undefined ? undefined : parseServiceUUID(serviceUUID, platform),
|
|
78
|
+
companyIdentifier:
|
|
79
|
+
companyIdentifier === undefined ? undefined : parseCompanyIdentifier(companyIdentifier),
|
|
80
|
+
nameSubstring:
|
|
81
|
+
nameSubstring === undefined ? undefined : nonEmpty(nameSubstring, 'filter.nameSubstring'),
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
if (platform === 'ios') {
|
|
85
|
+
return {
|
|
86
|
+
...filter,
|
|
87
|
+
displayName: nonEmpty(options.ios.displayName, 'ios.displayName'),
|
|
88
|
+
productImageUri: resolveImageUri(options.ios.productImage),
|
|
89
|
+
range: options.ios.range ?? 'default',
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const { singleDevice = true, namePattern } = options.android ?? {};
|
|
94
|
+
return {
|
|
95
|
+
...filter,
|
|
96
|
+
singleDevice,
|
|
97
|
+
namePattern:
|
|
98
|
+
namePattern === undefined ? undefined : nonEmpty(namePattern, 'android.namePattern'),
|
|
99
|
+
};
|
|
100
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"lib": ["dom", "DOM.Iterable", "esnext"],
|
|
4
|
+
"types": ["jest"],
|
|
5
|
+
"typeRoots": ["./ts-declarations", "./node_modules/@types"],
|
|
6
|
+
"jsx": "react-native",
|
|
7
|
+
"target": "esnext",
|
|
8
|
+
"moduleResolution": "bundler",
|
|
9
|
+
"module": "esnext",
|
|
10
|
+
"moduleDetection": "force",
|
|
11
|
+
"esModuleInterop": true,
|
|
12
|
+
"sourceMap": true,
|
|
13
|
+
"declaration": true,
|
|
14
|
+
"declarationMap": true,
|
|
15
|
+
"inlineSources": true,
|
|
16
|
+
"skipLibCheck": true,
|
|
17
|
+
"strict": true,
|
|
18
|
+
"noFallthroughCasesInSwitch": true,
|
|
19
|
+
"noPropertyAccessFromIndexSignature": false,
|
|
20
|
+
"noImplicitReturns": true,
|
|
21
|
+
"noUnusedLocals": true,
|
|
22
|
+
"noUnusedParameters": false,
|
|
23
|
+
"rootDir": "./src",
|
|
24
|
+
"outDir": "./build"
|
|
25
|
+
},
|
|
26
|
+
"include": ["./src"],
|
|
27
|
+
"exclude": ["**/__mocks__/*", "**/__tests__/*"]
|
|
28
|
+
}
|