@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.
Files changed (41) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +266 -0
  3. package/build/angular/index.d.ts +1 -0
  4. package/build/angular/index.js +4 -0
  5. package/build/core/device.d.ts +138 -0
  6. package/build/core/device.js +194 -0
  7. package/build/core/index.d.ts +2 -0
  8. package/build/core/index.js +2 -0
  9. package/build/core/native-module.d.ts +29 -0
  10. package/build/core/native-module.js +4 -0
  11. package/build/core/types.d.ts +13 -0
  12. package/build/core/types.js +17 -0
  13. package/build/react/index.d.ts +1 -0
  14. package/build/react/index.js +6 -0
  15. package/build/vue/index.d.ts +1 -0
  16. package/build/vue/index.js +4 -0
  17. package/build-ngc/angular/index.d.ts +1 -0
  18. package/build-ngc/angular/index.js +5 -0
  19. package/build-ngc/angular/index.js.map +1 -0
  20. package/build-ngc/core/device.d.ts +138 -0
  21. package/build-ngc/core/device.js +195 -0
  22. package/build-ngc/core/device.js.map +1 -0
  23. package/build-ngc/core/index.d.ts +2 -0
  24. package/build-ngc/core/index.js +3 -0
  25. package/build-ngc/core/index.js.map +1 -0
  26. package/build-ngc/core/native-module.d.ts +29 -0
  27. package/build-ngc/core/native-module.js +5 -0
  28. package/build-ngc/core/native-module.js.map +1 -0
  29. package/build-ngc/core/types.d.ts +13 -0
  30. package/build-ngc/core/types.js +18 -0
  31. package/build-ngc/core/types.js.map +1 -0
  32. package/native-link.json +12 -0
  33. package/package.json +107 -0
  34. package/src/angular/index.ts +4 -0
  35. package/src/core/device.test.ts +202 -0
  36. package/src/core/device.ts +222 -0
  37. package/src/core/index.ts +28 -0
  38. package/src/core/native-module.ts +42 -0
  39. package/src/core/types.ts +17 -0
  40. package/src/react/index.ts +6 -0
  41. package/src/vue/index.ts +4 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 A. Prokopenko
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,266 @@
1
+ # @symbiote-native/device
2
+
3
+ A wrapper package for [SymbioteNative](../../README.md) that makes
4
+ [`expo-device`](https://github.com/expo/expo/tree/main/packages/expo-device)
5
+ — physical device information: brand/model/OS constants, uptime, max-memory,
6
+ root/jailbreak detection, side-loading detection, and platform-feature queries — usable from
7
+ **every** adapter, React, Vue, and Angular, not just React. Like
8
+ [`@symbiote-native/local-auth`](../local-auth) and unlike this repo's stateful Expo wrapper
9
+ ([`@symbiote-native/sensors`](../sensors), an `EventEmitter` + live-subscription surface), every
10
+ export here is either an eagerly-resolved constant or a one-shot async call with no per-instance
11
+ state, so there is no hook/composable/service to wrap — the React, Vue, and Angular entry points
12
+ are plain re-exports of the same `core`.
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ npm install @symbiote-native/device
18
+ ```
19
+
20
+ `expo-device` and `expo-modules-core` come along as regular dependencies, pinned to exact
21
+ versions — never install either yourself, and never add the `expo` meta-package to your project
22
+ (it bundles its own Metro/Babel pipeline, which conflicts with this project's own).
23
+
24
+ ## Required one-time step: native autolinking wiring
25
+
26
+ Unlike a plain RN native module, `expo-device`'s native code is discovered by
27
+ `expo-modules-autolinking`, not RN's own `react-native.config.cjs` mechanism — this needs wiring
28
+ into the native host app **once**, covering this package and every other `expo-modules-core`
29
+ package with zero further changes:
30
+
31
+ | Platform | Touches |
32
+ |---|---|
33
+ | iOS | `ios/Podfile` — add `use_expo_modules!` |
34
+ | iOS | `AppDelegate.swift` — Expo's runtime-bootstrap hook |
35
+ | Android | `settings.gradle` / `app/build.gradle` — resolve and include the Expo Gradle projects |
36
+ | Android | `MainApplication.kt` — Expo's bootstrap hook, plus a hand-written native-module name map (there's no `expo` meta-package here to auto-generate one) |
37
+
38
+ Full mechanics live in the `symbiote-expo-native-module` project skill. Reference
39
+ implementation: `examples/expo-react/ios/Podfile` and
40
+ `examples/expo-react/android/app/src/main/java/com/canaryexpo/MainApplication.kt`.
41
+
42
+ `expo-device` needs no runtime permission on either platform — every constant and function here
43
+ reads plain system/build information, nothing gated by a permission prompt.
44
+
45
+ ## Shape
46
+
47
+ ```
48
+ src/core/ Eager constants (isDevice, brand, manufacturer, modelId, modelName, designName,
49
+ productName, deviceType, deviceYearClass, totalMemory,
50
+ supportedCpuArchitectures, osName, osVersion, osBuildId, osInternalBuildId,
51
+ osBuildFingerprint, platformApiLevel, deviceName), plus getDeviceTypeAsync /
52
+ getUptimeAsync / getMaxMemoryAsync / isRootedExperimentalAsync /
53
+ isSideLoadingEnabledAsync / getPlatformFeaturesAsync / hasPlatformFeatureAsync,
54
+ and the DeviceType enum. native-module.ts resolves the native module via
55
+ expo-modules-core's requireNativeModule.
56
+ src/react/ @symbiote-native/device/react — export * from '../core'
57
+ src/vue/ @symbiote-native/device/vue — export * from '../core'
58
+ src/angular/ @symbiote-native/device/angular — export * from '../core'
59
+ ```
60
+
61
+ No per-adapter lifecycle wrapper exists because there's nothing to subscribe to or clean up —
62
+ each adapter entry is a single-file re-export.
63
+
64
+ ## Use it
65
+
66
+ ```tsx
67
+ // React
68
+ import { useEffect, useState } from 'react';
69
+ import { Text, View } from '@symbiote-native/react';
70
+ import {
71
+ brand,
72
+ deviceName,
73
+ getMaxMemoryAsync,
74
+ getUptimeAsync,
75
+ isDevice,
76
+ modelName,
77
+ osName,
78
+ osVersion,
79
+ } from '@symbiote-native/device/react';
80
+
81
+ function DeviceScreen() {
82
+ const [uptime, setUptime] = useState<number | null>(null);
83
+ const [maxMemory, setMaxMemory] = useState<number | null>(null);
84
+
85
+ useEffect(() => {
86
+ getUptimeAsync().then(setUptime);
87
+ getMaxMemoryAsync().then(setMaxMemory);
88
+ }, []);
89
+
90
+ return (
91
+ <View>
92
+ <Text>{isDevice ? 'Real device' : 'Simulator/emulator'}</Text>
93
+ <Text>{`${brand ?? 'unknown'} ${modelName ?? ''}`}</Text>
94
+ <Text>{`${osName ?? 'unknown OS'} ${osVersion ?? ''}`}</Text>
95
+ <Text>{deviceName ?? 'unnamed device'}</Text>
96
+ <Text>{uptime === null ? 'checking uptime…' : `Uptime: ${uptime}ms`}</Text>
97
+ <Text>{maxMemory === null ? 'checking memory…' : `Max memory: ${maxMemory} bytes`}</Text>
98
+ </View>
99
+ );
100
+ }
101
+ ```
102
+
103
+ ```vue
104
+ <!-- Vue -->
105
+ <script setup lang="ts">
106
+ import { onMounted, ref } from 'vue';
107
+ import { Text, View } from '@symbiote-native/vue';
108
+ import {
109
+ brand,
110
+ deviceName,
111
+ getMaxMemoryAsync,
112
+ getUptimeAsync,
113
+ isDevice,
114
+ modelName,
115
+ osName,
116
+ osVersion,
117
+ } from '@symbiote-native/device/vue';
118
+
119
+ const uptime = ref<number | null>(null);
120
+ const maxMemory = ref<number | null>(null);
121
+
122
+ onMounted(() => {
123
+ void getUptimeAsync().then(value => (uptime.value = value));
124
+ void getMaxMemoryAsync().then(value => (maxMemory.value = value));
125
+ });
126
+ </script>
127
+
128
+ <template>
129
+ <View>
130
+ <Text>{{ isDevice ? 'Real device' : 'Simulator/emulator' }}</Text>
131
+ <Text>{{ `${brand ?? 'unknown'} ${modelName ?? ''}` }}</Text>
132
+ <Text>{{ `${osName ?? 'unknown OS'} ${osVersion ?? ''}` }}</Text>
133
+ <Text>{{ deviceName ?? 'unnamed device' }}</Text>
134
+ <Text>{{ uptime === null ? 'checking uptime…' : `Uptime: ${uptime}ms` }}</Text>
135
+ <Text>{{ maxMemory === null ? 'checking memory…' : `Max memory: ${maxMemory} bytes` }}</Text>
136
+ </View>
137
+ </template>
138
+ ```
139
+
140
+ ```ts
141
+ // Angular
142
+ import { Component, signal } from '@angular/core';
143
+ import { Text, View } from '@symbiote-native/angular';
144
+ import {
145
+ brand,
146
+ deviceName,
147
+ getMaxMemoryAsync,
148
+ getUptimeAsync,
149
+ isDevice,
150
+ modelName,
151
+ osName,
152
+ osVersion,
153
+ } from '@symbiote-native/device/angular';
154
+
155
+ @Component({
156
+ standalone: true,
157
+ imports: [Text, View],
158
+ template: `
159
+ <View>
160
+ <Text>{{ isDevice ? 'Real device' : 'Simulator/emulator' }}</Text>
161
+ <Text>{{ brand ?? 'unknown' }} {{ modelName ?? '' }}</Text>
162
+ <Text>{{ osName ?? 'unknown OS' }} {{ osVersion ?? '' }}</Text>
163
+ <Text>{{ deviceName ?? 'unnamed device' }}</Text>
164
+ <Text>{{ uptime() === null ? 'checking uptime…' : 'Uptime: ' + uptime() + 'ms' }}</Text>
165
+ <Text>{{ maxMemory() === null ? 'checking memory…' : 'Max memory: ' + maxMemory() + ' bytes' }}</Text>
166
+ </View>
167
+ `,
168
+ })
169
+ export class DeviceScreen {
170
+ readonly isDevice = isDevice;
171
+ readonly brand = brand;
172
+ readonly modelName = modelName;
173
+ readonly osName = osName;
174
+ readonly osVersion = osVersion;
175
+ readonly deviceName = deviceName;
176
+
177
+ readonly uptime = signal<number | null>(null);
178
+ readonly maxMemory = signal<number | null>(null);
179
+
180
+ constructor() {
181
+ getUptimeAsync().then(value => this.uptime.set(value));
182
+ getMaxMemoryAsync().then(value => this.maxMemory.set(value));
183
+ }
184
+ }
185
+ ```
186
+
187
+ There's no per-instance service to `inject()` in the Angular case — every constant/function is a
188
+ plain export off the core package, read straight in the constructor or class-field initializer.
189
+ These snippets are not yet demoed in a real canary screen — no `DeviceScreen.*` exists yet in
190
+ `examples/expo-*` — so treat them as realistic reference code, not a trimmed-down copy of a
191
+ running demo.
192
+
193
+ ## API
194
+
195
+ Eagerly-resolved constants, plus a handful of one-shot async functions — no event stream, no
196
+ per-instance state — so the React/Vue/Angular entry points above are plain re-exports of `core`
197
+ with nothing adapter-specific to add.
198
+
199
+ ```ts
200
+ // Constants — resolved once, at import time, straight off the native module:
201
+ isDevice: boolean
202
+ brand: string | null
203
+ manufacturer: string | null
204
+ modelId: string | null // iOS only
205
+ modelName: string | null
206
+ designName: string | null // Android only
207
+ productName: string | null // Android only
208
+ deviceType: DeviceType | null
209
+ deviceYearClass: number | null
210
+ totalMemory: number | null
211
+ supportedCpuArchitectures: string[] | null
212
+ osName: string | null
213
+ osVersion: string | null
214
+ osBuildId: string | null
215
+ osInternalBuildId: string | null
216
+ osBuildFingerprint: string | null // Android only
217
+ platformApiLevel: number | null // Android only
218
+ deviceName: string | null
219
+
220
+ // Functions:
221
+ getDeviceTypeAsync(): Promise<DeviceType>
222
+ getUptimeAsync(): Promise<number> // Android + iOS
223
+ getMaxMemoryAsync(): Promise<number> // Android only; -1 sentinel normalized to Number.MAX_SAFE_INTEGER
224
+ isRootedExperimentalAsync(): Promise<boolean> // best-effort root/jailbreak check
225
+ isSideLoadingEnabledAsync(): Promise<boolean> // Android only
226
+ getPlatformFeaturesAsync(): Promise<string[]> // Android only; [] elsewhere, never throws
227
+ hasPlatformFeatureAsync(feature: string): Promise<boolean> // Android only; false elsewhere, never throws
228
+ ```
229
+
230
+ Plus `DeviceType` (`UNKNOWN`/`PHONE`/`TABLET`/`DESKTOP`/`TV`) — ported from upstream's
231
+ `Device.types.ts`.
232
+
233
+ ```ts
234
+ import { getDeviceTypeAsync, isDevice } from '@symbiote-native/device';
235
+ // or the framework-scoped entry points — identical surface, re-exported verbatim:
236
+ import { getDeviceTypeAsync } from '@symbiote-native/device/react';
237
+ import { getDeviceTypeAsync } from '@symbiote-native/device/vue';
238
+ import { getDeviceTypeAsync } from '@symbiote-native/device/angular';
239
+ ```
240
+
241
+ ## Notes
242
+
243
+ - **Every function except `getPlatformFeaturesAsync`/`hasPlatformFeatureAsync` throws an
244
+ `UnavailabilityError` when the native method is missing.** Those two are the deliberate
245
+ exceptions — they resolve to `[]`/`false` instead, matching upstream, since a platform-feature
246
+ query on a platform with no such concept (iOS) is a normal "no" answer, not an error.
247
+ - **`getMaxMemoryAsync`'s `-1` native sentinel means "no inherent limit"** and is normalized to
248
+ `Number.MAX_SAFE_INTEGER` before it reaches your code — you never see the raw `-1`.
249
+ - **`isRootedExperimentalAsync` is a best-effort check, not a guarantee** — root/jailbreak
250
+ detection bypasses exist on both platforms; a `false` result does not prove the device is
251
+ unmodified.
252
+
253
+ ## Test it
254
+
255
+ No Fabric/Descriptor angle at all — every export here is a pure constant or async-function
256
+ surface, never a view or per-instance state. Tests inject a fake native-module object in place of
257
+ the real `requireNativeModule` resolution (`src/core/device.test.ts`, `vitest`) — no
258
+ `installFabric()`, no ViewConfig. Native rendering itself is verified on-device (see the parent
259
+ [README](../../README.md) for the project's testing model).
260
+
261
+ Native autolinking wiring for `expo-modules-core` packages is already done in the four Expo
262
+ canary apps (`examples/expo-react`, `examples/expo-vue-sfc`, `examples/expo-vue-tsx`,
263
+ `examples/expo-angular`) via `@symbiote-native/local-auth`/`@symbiote-native/sensors` — this
264
+ package reuses that same wiring with zero further app-side changes, since
265
+ `expo-modules-autolinking` discovers any `expo-modules-core` package already present in
266
+ `node_modules`. A dedicated `DeviceScreen` demo has not been wired into those canaries yet.
@@ -0,0 +1 @@
1
+ export * from '../core';
@@ -0,0 +1,4 @@
1
+ // @symbiote-native/device/angular: the Angular entry over the framework-agnostic core. Same
2
+ // reasoning as the React/Vue entries — no per-instance state or event stream to wrap in a
3
+ // service, so this is a plain re-export.
4
+ export * from '../core';
@@ -0,0 +1,138 @@
1
+ import { DeviceType } from './types';
2
+ /**
3
+ * `true` if the app is running on a real device and `false` if running in a simulator or
4
+ * emulator.
5
+ */
6
+ export declare const isDevice: boolean;
7
+ /**
8
+ * The device brand. The consumer-visible brand of the product/hardware.
9
+ * @example Device.brand; // Android: "google", "xiaomi"; iOS: "Apple"
10
+ */
11
+ export declare const brand: string | null;
12
+ /**
13
+ * The actual device manufacturer of the product or hardware. May be `null` if it cannot be
14
+ * determined.
15
+ */
16
+ export declare const manufacturer: string | null;
17
+ /**
18
+ * The internal model ID of the device — useful for programmatically identifying the type of
19
+ * device, not a human-friendly string.
20
+ * @platform ios
21
+ */
22
+ export declare const modelId: string | null;
23
+ /**
24
+ * The human-friendly name of the device model. May be `null` if it cannot be determined.
25
+ * @example Device.modelName; // Android: "Pixel 2"; iOS: "iPhone XS Max"
26
+ */
27
+ export declare const modelName: string | null;
28
+ /**
29
+ * The specific configuration or name of the industrial design — the device's name when it was
30
+ * designed during manufacturing into mass production. Corresponds to `Build.DEVICE`.
31
+ * @platform android
32
+ */
33
+ export declare const designName: string | null;
34
+ /**
35
+ * The device's overall product name chosen by the device implementer, containing the
36
+ * development name or code name of the device. Corresponds to `Build.PRODUCT`.
37
+ * @platform android
38
+ */
39
+ export declare const productName: string | null;
40
+ /**
41
+ * The type of the device as a {@link DeviceType} enum value.
42
+ */
43
+ export declare const deviceType: DeviceType | null;
44
+ /** The device year class (https://github.com/facebook/device-year-class) of this device. */
45
+ export declare const deviceYearClass: number | null;
46
+ /**
47
+ * The device's total memory, in bytes — the total memory accessible to the kernel, but not
48
+ * necessarily to a single app.
49
+ */
50
+ export declare const totalMemory: number | null;
51
+ /**
52
+ * A list of supported processor architecture versions. `null` if the supported architectures
53
+ * could not be determined.
54
+ */
55
+ export declare const supportedCpuArchitectures: string[] | null;
56
+ /**
57
+ * The name of the OS running on the device.
58
+ * @example Device.osName; // Android: "Android" or a build fingerprint string; iOS: "iOS"/"iPadOS"
59
+ */
60
+ export declare const osName: string | null;
61
+ /**
62
+ * The human-readable OS version string. Note that the version string may not always contain
63
+ * three numbers separated by dots.
64
+ */
65
+ export declare const osVersion: string | null;
66
+ /**
67
+ * The build ID of the OS that more precisely identifies the version of the OS. On Android this
68
+ * corresponds to `Build.DISPLAY` (not `Build.ID`); on iOS to `kern.osversion`.
69
+ */
70
+ export declare const osBuildId: string | null;
71
+ /**
72
+ * The internal build ID of the OS running on the device. On Android this corresponds to
73
+ * `Build.ID`; on iOS it's the same value as {@link osBuildId}.
74
+ */
75
+ export declare const osInternalBuildId: string | null;
76
+ /**
77
+ * A string that uniquely identifies the build of the currently running system OS.
78
+ * @platform android
79
+ */
80
+ export declare const osBuildFingerprint: string | null;
81
+ /**
82
+ * The Android SDK version of the software currently running on this hardware device.
83
+ * @platform android
84
+ */
85
+ export declare const platformApiLevel: number | null;
86
+ /**
87
+ * The human-readable name of the device, which may be set by the device's user. `null` if the
88
+ * device name is unavailable.
89
+ */
90
+ export declare const deviceName: string | null;
91
+ /**
92
+ * Checks the type of the device as a {@link DeviceType} enum value.
93
+ *
94
+ * On Android, for devices other than TVs, the device type is determined by the screen
95
+ * resolution (screen diagonal size), so the result may not be completely accurate. If the
96
+ * screen diagonal length is between 3" and 6.9", the method returns `DeviceType.PHONE`. For
97
+ * lengths between 7" and 18", the method returns `DeviceType.TABLET`. Otherwise it returns
98
+ * `DeviceType.UNKNOWN`.
99
+ */
100
+ export declare function getDeviceTypeAsync(): Promise<DeviceType>;
101
+ /**
102
+ * Gets the uptime since the last reboot of the device, in milliseconds. Android devices do not
103
+ * count time spent in deep sleep.
104
+ * @platform android
105
+ * @platform ios
106
+ */
107
+ export declare function getUptimeAsync(): Promise<number>;
108
+ /**
109
+ * Returns the maximum amount of memory that the Java VM will attempt to use. If there is no
110
+ * inherent limit, `Number.MAX_SAFE_INTEGER` is returned instead of the native `-1` sentinel.
111
+ * @platform android
112
+ */
113
+ export declare function getMaxMemoryAsync(): Promise<number>;
114
+ /**
115
+ * Checks whether the device has been rooted (Android) or jailbroken (iOS). This is a best-effort
116
+ * check — root/jailbreak-detection bypasses exist on both platforms, so a `false` result is not
117
+ * a guarantee.
118
+ */
119
+ export declare function isRootedExperimentalAsync(): Promise<boolean>;
120
+ /**
121
+ * Returns whether applications can be installed for this user via the system's
122
+ * `ACTION_INSTALL_PACKAGE` mechanism, rather than through the OS's default app store. Requires
123
+ * the `REQUEST_INSTALL_PACKAGES` permission.
124
+ * @platform android
125
+ */
126
+ export declare function isSideLoadingEnabledAsync(): Promise<boolean>;
127
+ /**
128
+ * Gets a list of platform-specific feature names available on the system. Resolves to an empty
129
+ * array rather than throwing when the native method is absent (e.g. on iOS), matching upstream.
130
+ * @platform android
131
+ */
132
+ export declare function getPlatformFeaturesAsync(): Promise<string[]>;
133
+ /**
134
+ * Tells if the device has a specific system feature. Resolves to `false` rather than throwing
135
+ * when the native method is absent (e.g. on iOS), matching upstream.
136
+ * @platform android
137
+ */
138
+ export declare function hasPlatformFeatureAsync(feature: string): Promise<boolean>;
@@ -0,0 +1,194 @@
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
+ import { expoDevice } from './native-module';
19
+ import { DeviceType } from './types';
20
+ const NATIVE_MODULE_NAME = 'expo-device';
21
+ const MAX_MEMORY_UNLIMITED_SENTINEL = -1;
22
+ /**
23
+ * `true` if the app is running on a real device and `false` if running in a simulator or
24
+ * emulator.
25
+ */
26
+ export const isDevice = expoDevice.isDevice ?? true;
27
+ /**
28
+ * The device brand. The consumer-visible brand of the product/hardware.
29
+ * @example Device.brand; // Android: "google", "xiaomi"; iOS: "Apple"
30
+ */
31
+ export const brand = expoDevice.brand ?? null;
32
+ /**
33
+ * The actual device manufacturer of the product or hardware. May be `null` if it cannot be
34
+ * determined.
35
+ */
36
+ export const manufacturer = expoDevice.manufacturer ?? null;
37
+ /**
38
+ * The internal model ID of the device — useful for programmatically identifying the type of
39
+ * device, not a human-friendly string.
40
+ * @platform ios
41
+ */
42
+ export const modelId = expoDevice.modelId ?? null;
43
+ /**
44
+ * The human-friendly name of the device model. May be `null` if it cannot be determined.
45
+ * @example Device.modelName; // Android: "Pixel 2"; iOS: "iPhone XS Max"
46
+ */
47
+ export const modelName = expoDevice.modelName ?? null;
48
+ /**
49
+ * The specific configuration or name of the industrial design — the device's name when it was
50
+ * designed during manufacturing into mass production. Corresponds to `Build.DEVICE`.
51
+ * @platform android
52
+ */
53
+ export const designName = expoDevice.designName ?? null;
54
+ /**
55
+ * The device's overall product name chosen by the device implementer, containing the
56
+ * development name or code name of the device. Corresponds to `Build.PRODUCT`.
57
+ * @platform android
58
+ */
59
+ export const productName = expoDevice.productName ?? null;
60
+ /**
61
+ * The type of the device as a {@link DeviceType} enum value.
62
+ */
63
+ export const deviceType = expoDevice.deviceType ?? null;
64
+ /** The device year class (https://github.com/facebook/device-year-class) of this device. */
65
+ export const deviceYearClass = expoDevice.deviceYearClass ?? null;
66
+ /**
67
+ * The device's total memory, in bytes — the total memory accessible to the kernel, but not
68
+ * necessarily to a single app.
69
+ */
70
+ export const totalMemory = expoDevice.totalMemory ?? null;
71
+ /**
72
+ * A list of supported processor architecture versions. `null` if the supported architectures
73
+ * could not be determined.
74
+ */
75
+ export const supportedCpuArchitectures = expoDevice.supportedCpuArchitectures ?? null;
76
+ /**
77
+ * The name of the OS running on the device.
78
+ * @example Device.osName; // Android: "Android" or a build fingerprint string; iOS: "iOS"/"iPadOS"
79
+ */
80
+ export const osName = expoDevice.osName ?? null;
81
+ /**
82
+ * The human-readable OS version string. Note that the version string may not always contain
83
+ * three numbers separated by dots.
84
+ */
85
+ export const osVersion = expoDevice.osVersion ?? null;
86
+ /**
87
+ * The build ID of the OS that more precisely identifies the version of the OS. On Android this
88
+ * corresponds to `Build.DISPLAY` (not `Build.ID`); on iOS to `kern.osversion`.
89
+ */
90
+ export const osBuildId = expoDevice.osBuildId ?? null;
91
+ /**
92
+ * The internal build ID of the OS running on the device. On Android this corresponds to
93
+ * `Build.ID`; on iOS it's the same value as {@link osBuildId}.
94
+ */
95
+ export const osInternalBuildId = expoDevice.osInternalBuildId ?? null;
96
+ /**
97
+ * A string that uniquely identifies the build of the currently running system OS.
98
+ * @platform android
99
+ */
100
+ export const osBuildFingerprint = expoDevice.osBuildFingerprint ?? null;
101
+ /**
102
+ * The Android SDK version of the software currently running on this hardware device.
103
+ * @platform android
104
+ */
105
+ export const platformApiLevel = expoDevice.platformApiLevel ?? null;
106
+ /**
107
+ * The human-readable name of the device, which may be set by the device's user. `null` if the
108
+ * device name is unavailable.
109
+ */
110
+ export const deviceName = expoDevice.deviceName ?? null;
111
+ /**
112
+ * Checks the type of the device as a {@link DeviceType} enum value.
113
+ *
114
+ * On Android, for devices other than TVs, the device type is determined by the screen
115
+ * resolution (screen diagonal size), so the result may not be completely accurate. If the
116
+ * screen diagonal length is between 3" and 6.9", the method returns `DeviceType.PHONE`. For
117
+ * lengths between 7" and 18", the method returns `DeviceType.TABLET`. Otherwise it returns
118
+ * `DeviceType.UNKNOWN`.
119
+ */
120
+ export async function getDeviceTypeAsync() {
121
+ if (!expoDevice.getDeviceTypeAsync) {
122
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getDeviceTypeAsync');
123
+ }
124
+ return expoDevice.getDeviceTypeAsync();
125
+ }
126
+ /**
127
+ * Gets the uptime since the last reboot of the device, in milliseconds. Android devices do not
128
+ * count time spent in deep sleep.
129
+ * @platform android
130
+ * @platform ios
131
+ */
132
+ export async function getUptimeAsync() {
133
+ if (!expoDevice.getUptimeAsync) {
134
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getUptimeAsync');
135
+ }
136
+ return expoDevice.getUptimeAsync();
137
+ }
138
+ /**
139
+ * Returns the maximum amount of memory that the Java VM will attempt to use. If there is no
140
+ * inherent limit, `Number.MAX_SAFE_INTEGER` is returned instead of the native `-1` sentinel.
141
+ * @platform android
142
+ */
143
+ export async function getMaxMemoryAsync() {
144
+ if (!expoDevice.getMaxMemoryAsync) {
145
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getMaxMemoryAsync');
146
+ }
147
+ const maxMemory = await expoDevice.getMaxMemoryAsync();
148
+ return maxMemory === MAX_MEMORY_UNLIMITED_SENTINEL ? Number.MAX_SAFE_INTEGER : maxMemory;
149
+ }
150
+ /**
151
+ * Checks whether the device has been rooted (Android) or jailbroken (iOS). This is a best-effort
152
+ * check — root/jailbreak-detection bypasses exist on both platforms, so a `false` result is not
153
+ * a guarantee.
154
+ */
155
+ export async function isRootedExperimentalAsync() {
156
+ if (!expoDevice.isRootedExperimentalAsync) {
157
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'isRootedExperimentalAsync');
158
+ }
159
+ return expoDevice.isRootedExperimentalAsync();
160
+ }
161
+ /**
162
+ * Returns whether applications can be installed for this user via the system's
163
+ * `ACTION_INSTALL_PACKAGE` mechanism, rather than through the OS's default app store. Requires
164
+ * the `REQUEST_INSTALL_PACKAGES` permission.
165
+ * @platform android
166
+ */
167
+ export async function isSideLoadingEnabledAsync() {
168
+ if (!expoDevice.isSideLoadingEnabledAsync) {
169
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'isSideLoadingEnabledAsync');
170
+ }
171
+ return expoDevice.isSideLoadingEnabledAsync();
172
+ }
173
+ /**
174
+ * Gets a list of platform-specific feature names available on the system. Resolves to an empty
175
+ * array rather than throwing when the native method is absent (e.g. on iOS), matching upstream.
176
+ * @platform android
177
+ */
178
+ export async function getPlatformFeaturesAsync() {
179
+ if (!expoDevice.getPlatformFeaturesAsync) {
180
+ return [];
181
+ }
182
+ return expoDevice.getPlatformFeaturesAsync();
183
+ }
184
+ /**
185
+ * Tells if the device has a specific system feature. Resolves to `false` rather than throwing
186
+ * when the native method is absent (e.g. on iOS), matching upstream.
187
+ * @platform android
188
+ */
189
+ export async function hasPlatformFeatureAsync(feature) {
190
+ if (!expoDevice.hasPlatformFeatureAsync) {
191
+ return false;
192
+ }
193
+ return expoDevice.hasPlatformFeatureAsync(feature);
194
+ }
@@ -0,0 +1,2 @@
1
+ export { isDevice, brand, manufacturer, modelId, modelName, designName, productName, deviceType, deviceYearClass, totalMemory, supportedCpuArchitectures, osName, osVersion, osBuildId, osInternalBuildId, osBuildFingerprint, platformApiLevel, deviceName, getDeviceTypeAsync, getUptimeAsync, getMaxMemoryAsync, isRootedExperimentalAsync, isSideLoadingEnabledAsync, getPlatformFeaturesAsync, hasPlatformFeatureAsync, } from './device';
2
+ export { DeviceType } from './types';
@@ -0,0 +1,2 @@
1
+ export { isDevice, brand, manufacturer, modelId, modelName, designName, productName, deviceType, deviceYearClass, totalMemory, supportedCpuArchitectures, osName, osVersion, osBuildId, osInternalBuildId, osBuildFingerprint, platformApiLevel, deviceName, getDeviceTypeAsync, getUptimeAsync, getMaxMemoryAsync, isRootedExperimentalAsync, isSideLoadingEnabledAsync, getPlatformFeaturesAsync, hasPlatformFeatureAsync, } from './device';
2
+ export { DeviceType } from './types';
@@ -0,0 +1,29 @@
1
+ import { DeviceType } from './types';
2
+ export type INativeDeviceModule = {
3
+ isDevice?: boolean;
4
+ brand?: string | null;
5
+ manufacturer?: string | null;
6
+ modelId?: string | null;
7
+ modelName?: string | null;
8
+ designName?: string | null;
9
+ productName?: string | null;
10
+ deviceType?: DeviceType | null;
11
+ deviceYearClass?: number | null;
12
+ totalMemory?: number | null;
13
+ supportedCpuArchitectures?: string[] | null;
14
+ osName?: string | null;
15
+ osVersion?: string | null;
16
+ osBuildId?: string | null;
17
+ osInternalBuildId?: string | null;
18
+ osBuildFingerprint?: string | null;
19
+ platformApiLevel?: number | null;
20
+ deviceName?: string | null;
21
+ getDeviceTypeAsync?(): Promise<DeviceType>;
22
+ getUptimeAsync?(): Promise<number>;
23
+ getMaxMemoryAsync?(): Promise<number>;
24
+ isRootedExperimentalAsync?(): Promise<boolean>;
25
+ isSideLoadingEnabledAsync?(): Promise<boolean>;
26
+ getPlatformFeaturesAsync?(): Promise<string[]>;
27
+ hasPlatformFeatureAsync?(feature: string): Promise<boolean>;
28
+ };
29
+ export declare const expoDevice: INativeDeviceModule;
@@ -0,0 +1,4 @@
1
+ import { requireNativeModule } from 'expo-modules-core';
2
+ import { DeviceType } from './types';
3
+ const EXPO_DEVICE_MODULE_NAME = 'ExpoDevice';
4
+ export const expoDevice = requireNativeModule(EXPO_DEVICE_MODULE_NAME);