@symbiote-native/device 0.0.1
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/LICENSE +21 -0
- package/README.md +266 -0
- package/build/angular/index.d.ts +1 -0
- package/build/angular/index.js +4 -0
- package/build/core/device.d.ts +138 -0
- package/build/core/device.js +194 -0
- package/build/core/index.d.ts +2 -0
- package/build/core/index.js +2 -0
- package/build/core/native-module.d.ts +29 -0
- package/build/core/native-module.js +4 -0
- package/build/core/types.d.ts +13 -0
- package/build/core/types.js +17 -0
- package/build/react/index.d.ts +1 -0
- package/build/react/index.js +6 -0
- package/build/vue/index.d.ts +1 -0
- package/build/vue/index.js +4 -0
- package/build-ngc/angular/index.d.ts +1 -0
- package/build-ngc/angular/index.js +5 -0
- package/build-ngc/angular/index.js.map +1 -0
- package/build-ngc/core/device.d.ts +138 -0
- package/build-ngc/core/device.js +195 -0
- package/build-ngc/core/device.js.map +1 -0
- package/build-ngc/core/index.d.ts +2 -0
- package/build-ngc/core/index.js +3 -0
- package/build-ngc/core/index.js.map +1 -0
- package/build-ngc/core/native-module.d.ts +29 -0
- package/build-ngc/core/native-module.js +5 -0
- package/build-ngc/core/native-module.js.map +1 -0
- package/build-ngc/core/types.d.ts +13 -0
- package/build-ngc/core/types.js +18 -0
- package/build-ngc/core/types.js.map +1 -0
- package/native-link.json +12 -0
- package/package.json +107 -0
- package/src/angular/index.ts +4 -0
- package/src/core/device.test.ts +202 -0
- package/src/core/device.ts +222 -0
- package/src/core/index.ts +28 -0
- package/src/core/native-module.ts +42 -0
- package/src/core/types.ts +17 -0
- package/src/react/index.ts +6 -0
- package/src/vue/index.ts +4 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
const FAKE_NATIVE_DEVICE = {
|
|
4
|
+
isDevice: true,
|
|
5
|
+
brand: 'Apple',
|
|
6
|
+
manufacturer: 'Apple',
|
|
7
|
+
modelId: 'iPhone14,2',
|
|
8
|
+
modelName: 'iPhone 13 Pro',
|
|
9
|
+
designName: null,
|
|
10
|
+
productName: null,
|
|
11
|
+
deviceType: 1,
|
|
12
|
+
deviceYearClass: 2_021,
|
|
13
|
+
totalMemory: 6_442_450_944,
|
|
14
|
+
supportedCpuArchitectures: ['arm64'],
|
|
15
|
+
osName: 'iOS',
|
|
16
|
+
osVersion: '17.4',
|
|
17
|
+
osBuildId: '21E219',
|
|
18
|
+
osInternalBuildId: '21E219',
|
|
19
|
+
osBuildFingerprint: null,
|
|
20
|
+
platformApiLevel: null,
|
|
21
|
+
deviceName: "Andrey's iPhone",
|
|
22
|
+
getDeviceTypeAsync: vi.fn(async () => 1),
|
|
23
|
+
getUptimeAsync: vi.fn(async () => 4_371_054),
|
|
24
|
+
getMaxMemoryAsync: vi.fn(async () => 402_653_184),
|
|
25
|
+
isRootedExperimentalAsync: vi.fn(async () => false),
|
|
26
|
+
isSideLoadingEnabledAsync: vi.fn(async () => false),
|
|
27
|
+
getPlatformFeaturesAsync: vi.fn(async () => ['android.hardware.sensor.accelerometer']),
|
|
28
|
+
hasPlatformFeatureAsync: vi.fn(async () => true),
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// The real ExpoDevice native module only exists on device — resolving it via
|
|
32
|
+
// requireNativeModule() at import time would throw in this headless test run, so the
|
|
33
|
+
// module-lookup file is faked in place of expo-modules-core's runtime resolution, the same
|
|
34
|
+
// pattern packages/local-auth/src/core/local-authentication.test.ts uses.
|
|
35
|
+
vi.mock('./native-module', () => ({
|
|
36
|
+
expoDevice: FAKE_NATIVE_DEVICE,
|
|
37
|
+
}));
|
|
38
|
+
|
|
39
|
+
// expo-modules-core's real entry transitively imports 'react-native' for Platform/
|
|
40
|
+
// TurboModuleRegistry, whose Flow-typed source Vitest's Oxc transform can't parse — same fake
|
|
41
|
+
// packages/local-auth/src/core/local-authentication.test.ts uses.
|
|
42
|
+
vi.mock('expo-modules-core', () => ({
|
|
43
|
+
UnavailabilityError: class UnavailabilityError extends Error {
|
|
44
|
+
constructor(moduleName: string, propertyName: string) {
|
|
45
|
+
super(`${propertyName} is not available on ${moduleName}`);
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
}));
|
|
49
|
+
|
|
50
|
+
const {
|
|
51
|
+
isDevice,
|
|
52
|
+
brand,
|
|
53
|
+
manufacturer,
|
|
54
|
+
modelId,
|
|
55
|
+
modelName,
|
|
56
|
+
designName,
|
|
57
|
+
productName,
|
|
58
|
+
deviceType,
|
|
59
|
+
deviceYearClass,
|
|
60
|
+
totalMemory,
|
|
61
|
+
supportedCpuArchitectures,
|
|
62
|
+
osName,
|
|
63
|
+
osVersion,
|
|
64
|
+
osBuildId,
|
|
65
|
+
osInternalBuildId,
|
|
66
|
+
osBuildFingerprint,
|
|
67
|
+
platformApiLevel,
|
|
68
|
+
deviceName,
|
|
69
|
+
getDeviceTypeAsync,
|
|
70
|
+
getUptimeAsync,
|
|
71
|
+
getMaxMemoryAsync,
|
|
72
|
+
isRootedExperimentalAsync,
|
|
73
|
+
isSideLoadingEnabledAsync,
|
|
74
|
+
getPlatformFeaturesAsync,
|
|
75
|
+
hasPlatformFeatureAsync,
|
|
76
|
+
} = await import('./device');
|
|
77
|
+
|
|
78
|
+
afterEach(() => {
|
|
79
|
+
vi.clearAllMocks();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe('eagerly resolved constants', () => {
|
|
83
|
+
it('read straight off the native module at import time', () => {
|
|
84
|
+
expect(isDevice).toBe(true);
|
|
85
|
+
expect(brand).toBe('Apple');
|
|
86
|
+
expect(manufacturer).toBe('Apple');
|
|
87
|
+
expect(modelId).toBe('iPhone14,2');
|
|
88
|
+
expect(modelName).toBe('iPhone 13 Pro');
|
|
89
|
+
expect(designName).toBeNull();
|
|
90
|
+
expect(productName).toBeNull();
|
|
91
|
+
expect(deviceType).toBe(1);
|
|
92
|
+
expect(deviceYearClass).toBe(2_021);
|
|
93
|
+
expect(totalMemory).toBe(6_442_450_944);
|
|
94
|
+
expect(supportedCpuArchitectures).toEqual(['arm64']);
|
|
95
|
+
expect(osName).toBe('iOS');
|
|
96
|
+
expect(osVersion).toBe('17.4');
|
|
97
|
+
expect(osBuildId).toBe('21E219');
|
|
98
|
+
expect(osInternalBuildId).toBe('21E219');
|
|
99
|
+
expect(osBuildFingerprint).toBeNull();
|
|
100
|
+
expect(platformApiLevel).toBeNull();
|
|
101
|
+
expect(deviceName).toBe("Andrey's iPhone");
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe('getDeviceTypeAsync', () => {
|
|
106
|
+
it('delegates to the native module', async () => {
|
|
107
|
+
await expect(getDeviceTypeAsync()).resolves.toBe(1);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('throws an UnavailabilityError-shaped error when the native method is absent', async () => {
|
|
111
|
+
const { getDeviceTypeAsync: native } = FAKE_NATIVE_DEVICE;
|
|
112
|
+
// @ts-expect-error -- simulating a platform where the native module has no such method
|
|
113
|
+
FAKE_NATIVE_DEVICE.getDeviceTypeAsync = undefined;
|
|
114
|
+
|
|
115
|
+
await expect(getDeviceTypeAsync()).rejects.toThrow(
|
|
116
|
+
'getDeviceTypeAsync is not available on expo-device',
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
FAKE_NATIVE_DEVICE.getDeviceTypeAsync = native;
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
describe('getUptimeAsync', () => {
|
|
124
|
+
it('delegates to the native module', async () => {
|
|
125
|
+
await expect(getUptimeAsync()).resolves.toBe(4_371_054);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
describe('getMaxMemoryAsync', () => {
|
|
130
|
+
it('delegates to the native module', async () => {
|
|
131
|
+
await expect(getMaxMemoryAsync()).resolves.toBe(402_653_184);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('normalizes the -1 unlimited sentinel to Number.MAX_SAFE_INTEGER', async () => {
|
|
135
|
+
const { getMaxMemoryAsync: native } = FAKE_NATIVE_DEVICE;
|
|
136
|
+
FAKE_NATIVE_DEVICE.getMaxMemoryAsync = vi.fn(async () => -1);
|
|
137
|
+
|
|
138
|
+
await expect(getMaxMemoryAsync()).resolves.toBe(Number.MAX_SAFE_INTEGER);
|
|
139
|
+
|
|
140
|
+
FAKE_NATIVE_DEVICE.getMaxMemoryAsync = native;
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('throws an UnavailabilityError-shaped error when the native method is absent', async () => {
|
|
144
|
+
const { getMaxMemoryAsync: native } = FAKE_NATIVE_DEVICE;
|
|
145
|
+
// @ts-expect-error -- simulating a platform where the native module has no such method
|
|
146
|
+
FAKE_NATIVE_DEVICE.getMaxMemoryAsync = undefined;
|
|
147
|
+
|
|
148
|
+
await expect(getMaxMemoryAsync()).rejects.toThrow(
|
|
149
|
+
'getMaxMemoryAsync is not available on expo-device',
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
FAKE_NATIVE_DEVICE.getMaxMemoryAsync = native;
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
describe('isRootedExperimentalAsync', () => {
|
|
157
|
+
it('delegates to the native module', async () => {
|
|
158
|
+
await expect(isRootedExperimentalAsync()).resolves.toBe(false);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
describe('isSideLoadingEnabledAsync', () => {
|
|
163
|
+
it('delegates to the native module', async () => {
|
|
164
|
+
await expect(isSideLoadingEnabledAsync()).resolves.toBe(false);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
describe('getPlatformFeaturesAsync', () => {
|
|
169
|
+
it('delegates to the native module', async () => {
|
|
170
|
+
await expect(getPlatformFeaturesAsync()).resolves.toEqual([
|
|
171
|
+
'android.hardware.sensor.accelerometer',
|
|
172
|
+
]);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('resolves an empty array instead of throwing when the native method is absent', async () => {
|
|
176
|
+
const { getPlatformFeaturesAsync: native } = FAKE_NATIVE_DEVICE;
|
|
177
|
+
// @ts-expect-error -- simulating iOS, where getPlatformFeaturesAsync has no native implementation
|
|
178
|
+
FAKE_NATIVE_DEVICE.getPlatformFeaturesAsync = undefined;
|
|
179
|
+
|
|
180
|
+
await expect(getPlatformFeaturesAsync()).resolves.toEqual([]);
|
|
181
|
+
|
|
182
|
+
FAKE_NATIVE_DEVICE.getPlatformFeaturesAsync = native;
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
describe('hasPlatformFeatureAsync', () => {
|
|
187
|
+
it('delegates to the native module', async () => {
|
|
188
|
+
await expect(hasPlatformFeatureAsync('android.hardware.sensor.accelerometer')).resolves.toBe(
|
|
189
|
+
true,
|
|
190
|
+
);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('resolves false instead of throwing when the native method is absent', async () => {
|
|
194
|
+
const { hasPlatformFeatureAsync: native } = FAKE_NATIVE_DEVICE;
|
|
195
|
+
// @ts-expect-error -- simulating iOS, where hasPlatformFeatureAsync has no native implementation
|
|
196
|
+
FAKE_NATIVE_DEVICE.hasPlatformFeatureAsync = undefined;
|
|
197
|
+
|
|
198
|
+
await expect(hasPlatformFeatureAsync('any')).resolves.toBe(false);
|
|
199
|
+
|
|
200
|
+
FAKE_NATIVE_DEVICE.hasPlatformFeatureAsync = native;
|
|
201
|
+
});
|
|
202
|
+
});
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
// Hand-ported from .vendors/expo/packages/expo-device/src/Device.ts (sdk-57). Every constant
|
|
2
|
+
// below is resolved eagerly, once, at import time — matching upstream's own top-level `export
|
|
3
|
+
// const` shape — from the native module's optional fields (native-module.ts), never imported
|
|
4
|
+
// from expo-device's own JS.
|
|
5
|
+
//
|
|
6
|
+
// Deviation from upstream: upstream guards each constant with `ExpoDevice ? field : fallback`,
|
|
7
|
+
// i.e. a truthiness check on the WHOLE module, because its own requireNativeModule call could in
|
|
8
|
+
// principle resolve to a falsy value on some legacy/web target. Every sibling package in this
|
|
9
|
+
// repo (local-auth, cellular, battery) instead resolves its native module unconditionally via
|
|
10
|
+
// expo-modules-core's requireNativeModule, which throws rather than returning null/undefined —
|
|
11
|
+
// so "the whole module is absent" already fails at import time here, same as every other
|
|
12
|
+
// package. What upstream's ternary is actually doing field-by-field IS still real and worth
|
|
13
|
+
// keeping: several constants are `undefined` on one platform by design (modelId is iOS-only;
|
|
14
|
+
// designName/productName/osBuildFingerprint/platformApiLevel are Android-only), so each constant
|
|
15
|
+
// below falls back with `??`, matching that per-field behavior without a redundant whole-module
|
|
16
|
+
// check the rest of this codebase's convention doesn't otherwise use.
|
|
17
|
+
import { UnavailabilityError } from 'expo-modules-core';
|
|
18
|
+
|
|
19
|
+
import { expoDevice } from './native-module';
|
|
20
|
+
import { DeviceType } from './types';
|
|
21
|
+
|
|
22
|
+
const NATIVE_MODULE_NAME = 'expo-device';
|
|
23
|
+
const MAX_MEMORY_UNLIMITED_SENTINEL = -1;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* `true` if the app is running on a real device and `false` if running in a simulator or
|
|
27
|
+
* emulator.
|
|
28
|
+
*/
|
|
29
|
+
export const isDevice: boolean = expoDevice.isDevice ?? true;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The device brand. The consumer-visible brand of the product/hardware.
|
|
33
|
+
* @example Device.brand; // Android: "google", "xiaomi"; iOS: "Apple"
|
|
34
|
+
*/
|
|
35
|
+
export const brand: string | null = expoDevice.brand ?? null;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The actual device manufacturer of the product or hardware. May be `null` if it cannot be
|
|
39
|
+
* determined.
|
|
40
|
+
*/
|
|
41
|
+
export const manufacturer: string | null = expoDevice.manufacturer ?? null;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The internal model ID of the device — useful for programmatically identifying the type of
|
|
45
|
+
* device, not a human-friendly string.
|
|
46
|
+
* @platform ios
|
|
47
|
+
*/
|
|
48
|
+
export const modelId: string | null = expoDevice.modelId ?? null;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The human-friendly name of the device model. May be `null` if it cannot be determined.
|
|
52
|
+
* @example Device.modelName; // Android: "Pixel 2"; iOS: "iPhone XS Max"
|
|
53
|
+
*/
|
|
54
|
+
export const modelName: string | null = expoDevice.modelName ?? null;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The specific configuration or name of the industrial design — the device's name when it was
|
|
58
|
+
* designed during manufacturing into mass production. Corresponds to `Build.DEVICE`.
|
|
59
|
+
* @platform android
|
|
60
|
+
*/
|
|
61
|
+
export const designName: string | null = expoDevice.designName ?? null;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The device's overall product name chosen by the device implementer, containing the
|
|
65
|
+
* development name or code name of the device. Corresponds to `Build.PRODUCT`.
|
|
66
|
+
* @platform android
|
|
67
|
+
*/
|
|
68
|
+
export const productName: string | null = expoDevice.productName ?? null;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The type of the device as a {@link DeviceType} enum value.
|
|
72
|
+
*/
|
|
73
|
+
export const deviceType: DeviceType | null = expoDevice.deviceType ?? null;
|
|
74
|
+
|
|
75
|
+
/** The device year class (https://github.com/facebook/device-year-class) of this device. */
|
|
76
|
+
export const deviceYearClass: number | null = expoDevice.deviceYearClass ?? null;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The device's total memory, in bytes — the total memory accessible to the kernel, but not
|
|
80
|
+
* necessarily to a single app.
|
|
81
|
+
*/
|
|
82
|
+
export const totalMemory: number | null = expoDevice.totalMemory ?? null;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* A list of supported processor architecture versions. `null` if the supported architectures
|
|
86
|
+
* could not be determined.
|
|
87
|
+
*/
|
|
88
|
+
export const supportedCpuArchitectures: string[] | null =
|
|
89
|
+
expoDevice.supportedCpuArchitectures ?? null;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* The name of the OS running on the device.
|
|
93
|
+
* @example Device.osName; // Android: "Android" or a build fingerprint string; iOS: "iOS"/"iPadOS"
|
|
94
|
+
*/
|
|
95
|
+
export const osName: string | null = expoDevice.osName ?? null;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* The human-readable OS version string. Note that the version string may not always contain
|
|
99
|
+
* three numbers separated by dots.
|
|
100
|
+
*/
|
|
101
|
+
export const osVersion: string | null = expoDevice.osVersion ?? null;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* The build ID of the OS that more precisely identifies the version of the OS. On Android this
|
|
105
|
+
* corresponds to `Build.DISPLAY` (not `Build.ID`); on iOS to `kern.osversion`.
|
|
106
|
+
*/
|
|
107
|
+
export const osBuildId: string | null = expoDevice.osBuildId ?? null;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* The internal build ID of the OS running on the device. On Android this corresponds to
|
|
111
|
+
* `Build.ID`; on iOS it's the same value as {@link osBuildId}.
|
|
112
|
+
*/
|
|
113
|
+
export const osInternalBuildId: string | null = expoDevice.osInternalBuildId ?? null;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* A string that uniquely identifies the build of the currently running system OS.
|
|
117
|
+
* @platform android
|
|
118
|
+
*/
|
|
119
|
+
export const osBuildFingerprint: string | null = expoDevice.osBuildFingerprint ?? null;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* The Android SDK version of the software currently running on this hardware device.
|
|
123
|
+
* @platform android
|
|
124
|
+
*/
|
|
125
|
+
export const platformApiLevel: number | null = expoDevice.platformApiLevel ?? null;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* The human-readable name of the device, which may be set by the device's user. `null` if the
|
|
129
|
+
* device name is unavailable.
|
|
130
|
+
*/
|
|
131
|
+
export const deviceName: string | null = expoDevice.deviceName ?? null;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Checks the type of the device as a {@link DeviceType} enum value.
|
|
135
|
+
*
|
|
136
|
+
* On Android, for devices other than TVs, the device type is determined by the screen
|
|
137
|
+
* resolution (screen diagonal size), so the result may not be completely accurate. If the
|
|
138
|
+
* screen diagonal length is between 3" and 6.9", the method returns `DeviceType.PHONE`. For
|
|
139
|
+
* lengths between 7" and 18", the method returns `DeviceType.TABLET`. Otherwise it returns
|
|
140
|
+
* `DeviceType.UNKNOWN`.
|
|
141
|
+
*/
|
|
142
|
+
export async function getDeviceTypeAsync(): Promise<DeviceType> {
|
|
143
|
+
if (!expoDevice.getDeviceTypeAsync) {
|
|
144
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getDeviceTypeAsync');
|
|
145
|
+
}
|
|
146
|
+
return expoDevice.getDeviceTypeAsync();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Gets the uptime since the last reboot of the device, in milliseconds. Android devices do not
|
|
151
|
+
* count time spent in deep sleep.
|
|
152
|
+
* @platform android
|
|
153
|
+
* @platform ios
|
|
154
|
+
*/
|
|
155
|
+
export async function getUptimeAsync(): Promise<number> {
|
|
156
|
+
if (!expoDevice.getUptimeAsync) {
|
|
157
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getUptimeAsync');
|
|
158
|
+
}
|
|
159
|
+
return expoDevice.getUptimeAsync();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Returns the maximum amount of memory that the Java VM will attempt to use. If there is no
|
|
164
|
+
* inherent limit, `Number.MAX_SAFE_INTEGER` is returned instead of the native `-1` sentinel.
|
|
165
|
+
* @platform android
|
|
166
|
+
*/
|
|
167
|
+
export async function getMaxMemoryAsync(): Promise<number> {
|
|
168
|
+
if (!expoDevice.getMaxMemoryAsync) {
|
|
169
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getMaxMemoryAsync');
|
|
170
|
+
}
|
|
171
|
+
const maxMemory = await expoDevice.getMaxMemoryAsync();
|
|
172
|
+
return maxMemory === MAX_MEMORY_UNLIMITED_SENTINEL ? Number.MAX_SAFE_INTEGER : maxMemory;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Checks whether the device has been rooted (Android) or jailbroken (iOS). This is a best-effort
|
|
177
|
+
* check — root/jailbreak-detection bypasses exist on both platforms, so a `false` result is not
|
|
178
|
+
* a guarantee.
|
|
179
|
+
*/
|
|
180
|
+
export async function isRootedExperimentalAsync(): Promise<boolean> {
|
|
181
|
+
if (!expoDevice.isRootedExperimentalAsync) {
|
|
182
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'isRootedExperimentalAsync');
|
|
183
|
+
}
|
|
184
|
+
return expoDevice.isRootedExperimentalAsync();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Returns whether applications can be installed for this user via the system's
|
|
189
|
+
* `ACTION_INSTALL_PACKAGE` mechanism, rather than through the OS's default app store. Requires
|
|
190
|
+
* the `REQUEST_INSTALL_PACKAGES` permission.
|
|
191
|
+
* @platform android
|
|
192
|
+
*/
|
|
193
|
+
export async function isSideLoadingEnabledAsync(): Promise<boolean> {
|
|
194
|
+
if (!expoDevice.isSideLoadingEnabledAsync) {
|
|
195
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'isSideLoadingEnabledAsync');
|
|
196
|
+
}
|
|
197
|
+
return expoDevice.isSideLoadingEnabledAsync();
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Gets a list of platform-specific feature names available on the system. Resolves to an empty
|
|
202
|
+
* array rather than throwing when the native method is absent (e.g. on iOS), matching upstream.
|
|
203
|
+
* @platform android
|
|
204
|
+
*/
|
|
205
|
+
export async function getPlatformFeaturesAsync(): Promise<string[]> {
|
|
206
|
+
if (!expoDevice.getPlatformFeaturesAsync) {
|
|
207
|
+
return [];
|
|
208
|
+
}
|
|
209
|
+
return expoDevice.getPlatformFeaturesAsync();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Tells if the device has a specific system feature. Resolves to `false` rather than throwing
|
|
214
|
+
* when the native method is absent (e.g. on iOS), matching upstream.
|
|
215
|
+
* @platform android
|
|
216
|
+
*/
|
|
217
|
+
export async function hasPlatformFeatureAsync(feature: string): Promise<boolean> {
|
|
218
|
+
if (!expoDevice.hasPlatformFeatureAsync) {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
return expoDevice.hasPlatformFeatureAsync(feature);
|
|
222
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export {
|
|
2
|
+
isDevice,
|
|
3
|
+
brand,
|
|
4
|
+
manufacturer,
|
|
5
|
+
modelId,
|
|
6
|
+
modelName,
|
|
7
|
+
designName,
|
|
8
|
+
productName,
|
|
9
|
+
deviceType,
|
|
10
|
+
deviceYearClass,
|
|
11
|
+
totalMemory,
|
|
12
|
+
supportedCpuArchitectures,
|
|
13
|
+
osName,
|
|
14
|
+
osVersion,
|
|
15
|
+
osBuildId,
|
|
16
|
+
osInternalBuildId,
|
|
17
|
+
osBuildFingerprint,
|
|
18
|
+
platformApiLevel,
|
|
19
|
+
deviceName,
|
|
20
|
+
getDeviceTypeAsync,
|
|
21
|
+
getUptimeAsync,
|
|
22
|
+
getMaxMemoryAsync,
|
|
23
|
+
isRootedExperimentalAsync,
|
|
24
|
+
isSideLoadingEnabledAsync,
|
|
25
|
+
getPlatformFeaturesAsync,
|
|
26
|
+
hasPlatformFeatureAsync,
|
|
27
|
+
} from './device';
|
|
28
|
+
export { DeviceType } from './types';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { requireNativeModule } from 'expo-modules-core';
|
|
2
|
+
import { DeviceType } from './types';
|
|
3
|
+
|
|
4
|
+
const EXPO_DEVICE_MODULE_NAME = 'ExpoDevice';
|
|
5
|
+
|
|
6
|
+
// Constants are optional fields (not just the async methods) because several of them are
|
|
7
|
+
// genuinely absent on one platform — modelId is iOS-only, designName/productName/
|
|
8
|
+
// osBuildFingerprint/platformApiLevel are Android-only — so the native module itself may simply
|
|
9
|
+
// never populate that field rather than the whole module being unavailable. Each async method is
|
|
10
|
+
// optional for the same reason every sibling package's native module type is (local-auth,
|
|
11
|
+
// cellular, battery): each call site in device.ts checks for the method's presence itself and
|
|
12
|
+
// throws an UnavailabilityError, matching upstream's own per-platform capability checks rather
|
|
13
|
+
// than assuming the native module implements the whole surface.
|
|
14
|
+
export type INativeDeviceModule = {
|
|
15
|
+
isDevice?: boolean;
|
|
16
|
+
brand?: string | null;
|
|
17
|
+
manufacturer?: string | null;
|
|
18
|
+
modelId?: string | null;
|
|
19
|
+
modelName?: string | null;
|
|
20
|
+
designName?: string | null;
|
|
21
|
+
productName?: string | null;
|
|
22
|
+
deviceType?: DeviceType | null;
|
|
23
|
+
deviceYearClass?: number | null;
|
|
24
|
+
totalMemory?: number | null;
|
|
25
|
+
supportedCpuArchitectures?: string[] | null;
|
|
26
|
+
osName?: string | null;
|
|
27
|
+
osVersion?: string | null;
|
|
28
|
+
osBuildId?: string | null;
|
|
29
|
+
osInternalBuildId?: string | null;
|
|
30
|
+
osBuildFingerprint?: string | null;
|
|
31
|
+
platformApiLevel?: number | null;
|
|
32
|
+
deviceName?: string | null;
|
|
33
|
+
getDeviceTypeAsync?(): Promise<DeviceType>;
|
|
34
|
+
getUptimeAsync?(): Promise<number>;
|
|
35
|
+
getMaxMemoryAsync?(): Promise<number>;
|
|
36
|
+
isRootedExperimentalAsync?(): Promise<boolean>;
|
|
37
|
+
isSideLoadingEnabledAsync?(): Promise<boolean>;
|
|
38
|
+
getPlatformFeaturesAsync?(): Promise<string[]>;
|
|
39
|
+
hasPlatformFeatureAsync?(feature: string): Promise<boolean>;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const expoDevice = requireNativeModule<INativeDeviceModule>(EXPO_DEVICE_MODULE_NAME);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Hand-ported from .vendors/expo/packages/expo-device/src/Device.types.ts (sdk-57). The enum
|
|
2
|
+
// itself carries no platform-specific logic (unlike local-auth's SecurityLevel.BIOMETRIC alias),
|
|
3
|
+
// so it's a verbatim port with no expo-modules-core dependency at all.
|
|
4
|
+
|
|
5
|
+
/** The type of device the app is currently running on. */
|
|
6
|
+
export enum DeviceType {
|
|
7
|
+
/** An unrecognized device type. */
|
|
8
|
+
UNKNOWN = 0,
|
|
9
|
+
/** Mobile phone handsets, typically with a touch screen and held in one hand. */
|
|
10
|
+
PHONE,
|
|
11
|
+
/** Tablet computers, typically with a touch screen that is larger than a usual phone. */
|
|
12
|
+
TABLET,
|
|
13
|
+
/** Desktop or laptop computers, typically with a keyboard and mouse. */
|
|
14
|
+
DESKTOP,
|
|
15
|
+
/** Device with TV-based interfaces. */
|
|
16
|
+
TV,
|
|
17
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// @symbiote-native/device/react: the React entry over the framework-agnostic core. Upstream
|
|
2
|
+
// ships eager constants and free async functions, no per-instance state and no event stream
|
|
3
|
+
// (unlike @symbiote-native/sensors) — there is nothing for a hook to wrap, so this is a plain
|
|
4
|
+
// re-export, mirroring how @symbiote-native/local-auth's React entry passes its own free
|
|
5
|
+
// functions through untouched.
|
|
6
|
+
export * from '../core';
|
package/src/vue/index.ts
ADDED