@ng-native/expo 0.12.0 → 0.14.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/android/build.gradle +2 -0
- package/android/src/main/java/dev/ngnative/expo/SearchFieldModule.kt +111 -0
- package/dist/app-info.d.ts +18 -15
- package/dist/app-info.js +17 -5
- package/dist/app-info.js.map +1 -1
- package/dist/apple-sign-in.js +5 -4
- package/dist/apple-sign-in.js.map +1 -1
- package/dist/assets.js +2 -2
- package/dist/assets.js.map +1 -1
- package/dist/async-storage.js +4 -1
- package/dist/async-storage.js.map +1 -1
- package/dist/audio.js +2 -2
- package/dist/audio.js.map +1 -1
- package/dist/battery.js +4 -1
- package/dist/battery.js.map +1 -1
- package/dist/biometrics.js +5 -4
- package/dist/biometrics.js.map +1 -1
- package/dist/brightness.js +7 -2
- package/dist/brightness.js.map +1 -1
- package/dist/camera.js +4 -1
- package/dist/camera.js.map +1 -1
- package/dist/clipboard.js +5 -2
- package/dist/clipboard.js.map +1 -1
- package/dist/database.js +2 -2
- package/dist/database.js.map +1 -1
- package/dist/expo-ui-components.d.ts +101 -9
- package/dist/expo-ui-components.js +305 -43
- package/dist/expo-ui-components.js.map +1 -1
- package/dist/expo-ui.js +7 -1
- package/dist/expo-ui.js.map +1 -1
- package/dist/foldable.d.ts +2 -0
- package/dist/foldable.js +15 -5
- package/dist/foldable.js.map +1 -1
- package/dist/fonts.d.ts +2 -0
- package/dist/fonts.js +12 -14
- package/dist/fonts.js.map +1 -1
- package/dist/haptics.js +2 -2
- package/dist/haptics.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/language-model.js +7 -3
- package/dist/language-model.js.map +1 -1
- package/dist/native.d.ts +17 -2
- package/dist/native.js +61 -3
- package/dist/native.js.map +1 -1
- package/dist/network.js +4 -1
- package/dist/network.js.map +1 -1
- package/dist/notifications.js +35 -8
- package/dist/notifications.js.map +1 -1
- package/dist/observed.js +5 -1
- package/dist/observed.js.map +1 -1
- package/dist/orientation.js +4 -1
- package/dist/orientation.js.map +1 -1
- package/dist/register-expo-view.d.ts +19 -0
- package/dist/register-expo-view.js +1 -0
- package/dist/register-expo-view.js.map +1 -1
- package/dist/screen-capture.js +2 -2
- package/dist/screen-capture.js.map +1 -1
- package/dist/search-field.d.ts +83 -0
- package/dist/search-field.js +147 -0
- package/dist/search-field.js.map +1 -0
- package/dist/secure-store.js +4 -1
- package/dist/secure-store.js.map +1 -1
- package/dist/sensors.js +6 -3
- package/dist/sensors.js.map +1 -1
- package/dist/splash-screen.js +2 -2
- package/dist/splash-screen.js.map +1 -1
- package/dist/store.js +4 -0
- package/dist/store.js.map +1 -1
- package/dist/tracking.js +2 -2
- package/dist/tracking.js.map +1 -1
- package/dist/ui-font-names.d.ts +16 -0
- package/dist/ui-font-names.js +71 -0
- package/dist/ui-font-names.js.map +1 -0
- package/dist/updates.js +4 -1
- package/dist/updates.js.map +1 -1
- package/dist/video.js +2 -2
- package/dist/video.js.map +1 -1
- package/dist/watch.js +6 -5
- package/dist/watch.js.map +1 -1
- package/expo-module.config.json +5 -2
- package/ios/ExpoUIModifiersModule.swift +25 -0
- package/ios/InlineNavigationTitle.swift +12 -0
- package/ios/NgNativeExpo.podspec +3 -0
- package/ios/SearchField.swift +130 -0
- package/ios/SegmentedFont.swift +113 -0
- package/package.json +6 -2
package/android/build.gradle
CHANGED
|
@@ -17,4 +17,6 @@ android {
|
|
|
17
17
|
dependencies {
|
|
18
18
|
// The first version with ProgressStyle and promoted ongoing notifications in NotificationCompat.
|
|
19
19
|
implementation 'androidx.core:core:1.17.0'
|
|
20
|
+
// `<search-field>`'s SearchView. React Native depends on AppCompat already.
|
|
21
|
+
implementation 'androidx.appcompat:appcompat:1.7.0'
|
|
20
22
|
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
package dev.ngnative.expo
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.view.inputmethod.InputMethodManager
|
|
5
|
+
import androidx.appcompat.widget.SearchView
|
|
6
|
+
import expo.modules.kotlin.AppContext
|
|
7
|
+
import expo.modules.kotlin.modules.Module
|
|
8
|
+
import expo.modules.kotlin.modules.ModuleDefinition
|
|
9
|
+
import expo.modules.kotlin.viewevent.EventDispatcher
|
|
10
|
+
import expo.modules.kotlin.views.ExpoView
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* `<search-field>`: AndroidX's search view, always open, for a search field outside a toolbar. The
|
|
14
|
+
* text is controlled from JavaScript, and a text older than what has been typed since is skipped,
|
|
15
|
+
* as React Native's text input skips one. Android's search field has no Cancel button.
|
|
16
|
+
*/
|
|
17
|
+
class SearchFieldModule : Module() {
|
|
18
|
+
override fun definition() = ModuleDefinition {
|
|
19
|
+
Name("NgNativeSearchField")
|
|
20
|
+
|
|
21
|
+
View(SearchFieldView::class) {
|
|
22
|
+
Events("onChangeText", "onSubmitSearch", "onPressCancel", "onBeginEditing", "onEndEditing")
|
|
23
|
+
|
|
24
|
+
Prop("text") { view: SearchFieldView, text: String? -> view.text = text ?: "" }
|
|
25
|
+
Prop("mostRecentEventCount") { view: SearchFieldView, count: Int? ->
|
|
26
|
+
view.mostRecentEventCount = count ?: 0
|
|
27
|
+
}
|
|
28
|
+
Prop("enabled") { view: SearchFieldView, enabled: Boolean? -> view.fieldEnabled = enabled ?: true }
|
|
29
|
+
Prop("placeholder") { view: SearchFieldView, placeholder: String? ->
|
|
30
|
+
view.search.queryHint = placeholder
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
AsyncFunction("focus") { view: SearchFieldView -> view.focus() }.runOnQueue(expo.modules.kotlin.functions.Queues.MAIN)
|
|
34
|
+
AsyncFunction("blur") { view: SearchFieldView -> view.search.clearFocus() }.runOnQueue(expo.modules.kotlin.functions.Queues.MAIN)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
class SearchFieldView(context: Context, appContext: AppContext) : ExpoView(context, appContext) {
|
|
40
|
+
override val shouldUseAndroidLayout = true
|
|
41
|
+
|
|
42
|
+
private val onChangeText by EventDispatcher()
|
|
43
|
+
private val onSubmitSearch by EventDispatcher()
|
|
44
|
+
private val onBeginEditing by EventDispatcher()
|
|
45
|
+
private val onEndEditing by EventDispatcher()
|
|
46
|
+
|
|
47
|
+
/** Changes the user has made, counted; JavaScript sends back the count its text answers. */
|
|
48
|
+
private var eventCount = 0
|
|
49
|
+
/** Set while JavaScript's text goes in, so it is not reported back as typing. */
|
|
50
|
+
private var applying = false
|
|
51
|
+
|
|
52
|
+
var mostRecentEventCount = 0
|
|
53
|
+
set(value) {
|
|
54
|
+
field = value
|
|
55
|
+
applyText()
|
|
56
|
+
}
|
|
57
|
+
var text = ""
|
|
58
|
+
set(value) {
|
|
59
|
+
field = value
|
|
60
|
+
applyText()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
val search = SearchView(context).apply {
|
|
64
|
+
setIconifiedByDefault(false)
|
|
65
|
+
setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
|
66
|
+
override fun onQueryTextChange(newText: String): Boolean {
|
|
67
|
+
if (applying) return true
|
|
68
|
+
eventCount += 1
|
|
69
|
+
onChangeText(mapOf("text" to newText, "eventCount" to eventCount))
|
|
70
|
+
return true
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
override fun onQueryTextSubmit(query: String): Boolean {
|
|
74
|
+
onSubmitSearch(mapOf("text" to query))
|
|
75
|
+
clearFocus()
|
|
76
|
+
return true
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
setOnQueryTextFocusChangeListener { _, focused ->
|
|
80
|
+
if (focused) onBeginEditing(mapOf()) else onEndEditing(mapOf())
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
init {
|
|
85
|
+
addView(search, LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT))
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Puts JavaScript's text in the field, unless it answers a keystroke older than the last one. */
|
|
89
|
+
private fun applyText() {
|
|
90
|
+
if (mostRecentEventCount < eventCount || search.query.toString() == text) return
|
|
91
|
+
applying = true
|
|
92
|
+
search.setQuery(text, false)
|
|
93
|
+
applying = false
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
var fieldEnabled = true
|
|
97
|
+
set(value) {
|
|
98
|
+
field = value
|
|
99
|
+
search.isEnabled = value
|
|
100
|
+
// The text box and its clear button take touches of their own, so each is turned off too.
|
|
101
|
+
search.findViewById<android.view.View>(androidx.appcompat.R.id.search_src_text)?.isEnabled = value
|
|
102
|
+
search.findViewById<android.view.View>(androidx.appcompat.R.id.search_close_btn)?.isEnabled = value
|
|
103
|
+
search.alpha = if (value) 1f else 0.5f
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
fun focus() {
|
|
107
|
+
search.requestFocus()
|
|
108
|
+
val input = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
|
109
|
+
input.showSoftInput(search.findFocus(), InputMethodManager.SHOW_IMPLICIT)
|
|
110
|
+
}
|
|
111
|
+
}
|
package/dist/app-info.d.ts
CHANGED
|
@@ -35,24 +35,27 @@ export declare class AppInfo {
|
|
|
35
35
|
static readonly SOURCE: InjectionToken<NativeAppInfo>;
|
|
36
36
|
private readonly native;
|
|
37
37
|
/** The version people see: `CFBundleShortVersionString`, or Android's `versionName`. */
|
|
38
|
-
|
|
38
|
+
get version(): string | null;
|
|
39
39
|
/** The build number: `CFBundleVersion`, or Android's `versionCode`. */
|
|
40
|
-
|
|
40
|
+
get build(): string | null;
|
|
41
41
|
/** The bundle identifier on iOS, the package name on Android. */
|
|
42
|
-
|
|
42
|
+
get id(): string | null;
|
|
43
43
|
/** The name under the icon. */
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
readonly model: string | null;
|
|
48
|
-
readonly brand: string | null;
|
|
49
|
-
/** `iOS`, `iPadOS`, `Android`. */
|
|
50
|
-
readonly os: string | null;
|
|
51
|
-
readonly osVersion: string | null;
|
|
52
|
-
/** False in a simulator or emulator. */
|
|
53
|
-
readonly physical: boolean | null;
|
|
54
|
-
readonly type: DeviceKind | null;
|
|
55
|
-
};
|
|
44
|
+
get name(): string | null;
|
|
45
|
+
get device(): ReturnType<typeof deviceOf>;
|
|
46
|
+
private deviceInfo?;
|
|
56
47
|
static ɵfac: i0.ɵɵFactoryDeclaration<AppInfo, never>;
|
|
57
48
|
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
|
|
58
49
|
}
|
|
50
|
+
declare function deviceOf(native: NativeAppInfo['device']): {
|
|
51
|
+
/** `iPhone 17 Pro`, `Pixel 9`. */
|
|
52
|
+
readonly model: string | null;
|
|
53
|
+
readonly brand: string | null;
|
|
54
|
+
/** `iOS`, `iPadOS`, `Android`. */
|
|
55
|
+
readonly os: string | null;
|
|
56
|
+
readonly osVersion: string | null;
|
|
57
|
+
/** False in a simulator or emulator. */
|
|
58
|
+
readonly physical: boolean | null;
|
|
59
|
+
readonly type: DeviceKind | null;
|
|
60
|
+
};
|
|
61
|
+
export {};
|
package/dist/app-info.js
CHANGED
|
@@ -31,14 +31,26 @@ export class AppInfo {
|
|
|
31
31
|
});
|
|
32
32
|
native = inject(AppInfo.SOURCE);
|
|
33
33
|
/** The version people see: `CFBundleShortVersionString`, or Android's `versionName`. */
|
|
34
|
-
|
|
34
|
+
// Read when asked, so a module missing from the build says so then, not as the service is made.
|
|
35
|
+
get version() {
|
|
36
|
+
return this.native.application?.nativeApplicationVersion ?? null;
|
|
37
|
+
}
|
|
35
38
|
/** The build number: `CFBundleVersion`, or Android's `versionCode`. */
|
|
36
|
-
build
|
|
39
|
+
get build() {
|
|
40
|
+
return this.native.application?.nativeBuildVersion ?? null;
|
|
41
|
+
}
|
|
37
42
|
/** The bundle identifier on iOS, the package name on Android. */
|
|
38
|
-
id
|
|
43
|
+
get id() {
|
|
44
|
+
return this.native.application?.applicationId ?? null;
|
|
45
|
+
}
|
|
39
46
|
/** The name under the icon. */
|
|
40
|
-
name
|
|
41
|
-
|
|
47
|
+
get name() {
|
|
48
|
+
return this.native.application?.applicationName ?? null;
|
|
49
|
+
}
|
|
50
|
+
get device() {
|
|
51
|
+
return (this.deviceInfo ??= deviceOf(this.native.device));
|
|
52
|
+
}
|
|
53
|
+
deviceInfo;
|
|
42
54
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: AppInfo, deps: [], target: i0.ɵɵFactoryTarget.Service });
|
|
43
55
|
static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.2.0", ngImport: i0, type: AppInfo });
|
|
44
56
|
}
|
package/dist/app-info.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-info.js","sourceRoot":"","sources":["../src/app-info.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;;AAIzC,gEAAgE;AAChE,MAAM,KAAK,GAA+B;IACxC,CAAC,EAAE,SAAS;IACZ,CAAC,EAAE,OAAO;IACV,CAAC,EAAE,QAAQ;IACX,CAAC,EAAE,SAAS;IACZ,CAAC,EAAE,IAAI;CACR,CAAC;AAqBF,MAAM,OAAO,OAAO;IAClB,uDAAuD;IACvD,MAAM,CAAU,MAAM,GAAG,IAAI,cAAc,CAAgB,8BAA8B,EAAE;QACzF,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;YACd,WAAW,EAAE,UAAU,CACrB,kBAAkB,EAClB,GAAG,EAAE,CAAC,OAAO,CAAC,kBAAkB,CAA8C,CAC/E;YACD,MAAM,EAAE,UAAU,CAChB,aAAa,EACb,GAAG,EAAE,CAAC,OAAO,CAAC,aAAa,CAAyC,CACrE;SACF,CAAC;KACH,CAAC,CAAC;IAEc,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAEjD,wFAAwF;
|
|
1
|
+
{"version":3,"file":"app-info.js","sourceRoot":"","sources":["../src/app-info.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;;AAIzC,gEAAgE;AAChE,MAAM,KAAK,GAA+B;IACxC,CAAC,EAAE,SAAS;IACZ,CAAC,EAAE,OAAO;IACV,CAAC,EAAE,QAAQ;IACX,CAAC,EAAE,SAAS;IACZ,CAAC,EAAE,IAAI;CACR,CAAC;AAqBF,MAAM,OAAO,OAAO;IAClB,uDAAuD;IACvD,MAAM,CAAU,MAAM,GAAG,IAAI,cAAc,CAAgB,8BAA8B,EAAE;QACzF,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;YACd,WAAW,EAAE,UAAU,CACrB,kBAAkB,EAClB,GAAG,EAAE,CAAC,OAAO,CAAC,kBAAkB,CAA8C,CAC/E;YACD,MAAM,EAAE,UAAU,CAChB,aAAa,EACb,GAAG,EAAE,CAAC,OAAO,CAAC,aAAa,CAAyC,CACrE;SACF,CAAC;KACH,CAAC,CAAC;IAEc,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAEjD,wFAAwF;IACxF,gGAAgG;IAChG,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,wBAAwB,IAAI,IAAI,CAAC;IACnE,CAAC;IACD,uEAAuE;IACvE,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,kBAAkB,IAAI,IAAI,CAAC;IAC7D,CAAC;IACD,iEAAiE;IACjE,IAAI,EAAE;QACJ,OAAO,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,aAAa,IAAI,IAAI,CAAC;IACxD,CAAC;IACD,+BAA+B;IAC/B,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,eAAe,IAAI,IAAI,CAAC;IAC1D,CAAC;IAED,IAAI,MAAM;QACR,OAAO,CAAC,IAAI,CAAC,UAAU,KAAK,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5D,CAAC;IACO,UAAU,CAA+B;uGAtCtC,OAAO;wGAAP,OAAO;;2FAAP,OAAO;kBADnB,OAAO;;AA0CR,mEAAmE;AACnE,MAAM,cAAc,GAAG;IACrB,SAAS,EAAE,IAAI;IACf,KAAK,EAAE,IAAI;IACX,MAAM,EAAE,IAAI;IACZ,SAAS,EAAE,IAAI;IACf,QAAQ,EAAE,IAAI;IACd,UAAU,EAAE,IAAI;CACjB,CAAC;AAEF,SAAS,QAAQ,CAAC,MAA+B;IAC/C,MAAM,MAAM,GAAG,MAAM,IAAI,cAAc,CAAC;IACxC,OAAO;QACL,kCAAkC;QAClC,KAAK,EAAE,MAAM,CAAC,SAAS;QACvB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,kCAAkC;QAClC,EAAE,EAAE,MAAM,CAAC,MAAM;QACjB,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,wCAAwC;QACxC,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,IAAI,EAAE,MAAM,CAAC,UAAU,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,SAAS,CAAC;KACzE,CAAC;AACb,CAAC","sourcesContent":["/**\n * `AppInfo`, bound to `expo-application` and `expo-device`: the app's version and build, and the\n * device it is running on.\n *\n * ```ts\n * protected readonly info = inject(AppInfo);\n * // {{ info.version }} ({{ info.build }}) on {{ info.device.model }}\n * ```\n *\n * Constants, read once, rather than signals: none of them changes while the app runs. Anything the\n * platform does not say is null, and so is everything from a module that is not installed.\n */\nimport { InjectionToken, Service, inject } from '@angular/core';\nimport { expoModule } from './native.ts';\n\nexport type DeviceKind = 'phone' | 'tablet' | 'desktop' | 'tv' | 'unknown';\n\n/** Expo's `DeviceType` enum, whose values are these numbers. */\nconst KINDS: Record<number, DeviceKind> = {\n 0: 'unknown',\n 1: 'phone',\n 2: 'tablet',\n 3: 'desktop',\n 4: 'tv',\n};\n\n/** The constants this reads, from each module. */\nexport interface NativeAppInfo {\n readonly application: {\n readonly nativeApplicationVersion: string | null;\n readonly nativeBuildVersion: string | null;\n readonly applicationId: string | null;\n readonly applicationName: string | null;\n } | null;\n readonly device: {\n readonly modelName: string | null;\n readonly brand: string | null;\n readonly osName: string | null;\n readonly osVersion: string | null;\n readonly isDevice: boolean;\n readonly deviceType: number | null;\n } | null;\n}\n\n@Service()\nexport class AppInfo {\n /** Overridden in a test to be any app on any phone. */\n static readonly SOURCE = new InjectionToken<NativeAppInfo>('angular-native.appInfoSource', {\n factory: () => ({\n application: expoModule(\n 'expo-application',\n () => require('expo-application') as NonNullable<NativeAppInfo['application']>,\n ),\n device: expoModule(\n 'expo-device',\n () => require('expo-device') as NonNullable<NativeAppInfo['device']>,\n ),\n }),\n });\n\n private readonly native = inject(AppInfo.SOURCE);\n\n /** The version people see: `CFBundleShortVersionString`, or Android's `versionName`. */\n // Read when asked, so a module missing from the build says so then, not as the service is made.\n get version(): string | null {\n return this.native.application?.nativeApplicationVersion ?? null;\n }\n /** The build number: `CFBundleVersion`, or Android's `versionCode`. */\n get build(): string | null {\n return this.native.application?.nativeBuildVersion ?? null;\n }\n /** The bundle identifier on iOS, the package name on Android. */\n get id(): string | null {\n return this.native.application?.applicationId ?? null;\n }\n /** The name under the icon. */\n get name(): string | null {\n return this.native.application?.applicationName ?? null;\n }\n\n get device(): ReturnType<typeof deviceOf> {\n return (this.deviceInfo ??= deviceOf(this.native.device));\n }\n private deviceInfo?: ReturnType<typeof deviceOf>;\n}\n\n/** What a device reads as without `expo-device`: nothing known. */\nconst UNKNOWN_DEVICE = {\n modelName: null,\n brand: null,\n osName: null,\n osVersion: null,\n isDevice: null,\n deviceType: null,\n};\n\nfunction deviceOf(native: NativeAppInfo['device']) {\n const device = native ?? UNKNOWN_DEVICE;\n return {\n /** `iPhone 17 Pro`, `Pixel 9`. */\n model: device.modelName,\n brand: device.brand,\n /** `iOS`, `iPadOS`, `Android`. */\n os: device.osName,\n osVersion: device.osVersion,\n /** False in a simulator or emulator. */\n physical: device.isDevice,\n type: device.deviceType === null ? null : (KINDS[device.deviceType] ?? 'unknown'),\n } as const;\n}\n"]}
|
package/dist/apple-sign-in.js
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* from a real error itself.
|
|
25
25
|
*/
|
|
26
26
|
import { DestroyRef, InjectionToken, Service, inject, signal } from '@angular/core';
|
|
27
|
-
import { expoModule } from "./native.js";
|
|
27
|
+
import { expoModule, present } from "./native.js";
|
|
28
28
|
import * as i0 from "@angular/core";
|
|
29
29
|
/*
|
|
30
30
|
* The module's enums, as plain numbers typed as the enums themselves. Importing an enum from the
|
|
@@ -64,18 +64,19 @@ export class AppleSignIn {
|
|
|
64
64
|
revocations = signal(0, /* @ts-ignore */
|
|
65
65
|
...(ngDevMode ? [{ debugName: "revocations" }] : /* istanbul ignore next */ []));
|
|
66
66
|
constructor() {
|
|
67
|
-
|
|
67
|
+
const native = present(this.native);
|
|
68
|
+
if (!native)
|
|
68
69
|
return;
|
|
69
70
|
// The user can revoke the app's access in Settings while it runs; the session it signed in
|
|
70
71
|
// with is then no longer theirs to use.
|
|
71
|
-
const subscription =
|
|
72
|
+
const subscription = native.addRevokeListener(() => this.revocations.update((count) => count + 1));
|
|
72
73
|
inject(DestroyRef).onDestroy(() => subscription.remove());
|
|
73
74
|
}
|
|
74
75
|
/** How many times the user has revoked the app's Apple ID credential while it ran. */
|
|
75
76
|
revoked = this.revocations.asReadonly();
|
|
76
77
|
/** Whether Sign in with Apple can be offered: iOS 13 and later, with the module installed. */
|
|
77
78
|
async available() {
|
|
78
|
-
return (await this.native?.isAvailableAsync()) ?? false;
|
|
79
|
+
return (await present(this.native)?.isAvailableAsync()) ?? false;
|
|
79
80
|
}
|
|
80
81
|
/**
|
|
81
82
|
* Shows Apple's sign-in sheet. The name and email in the credential are only there the first
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apple-sign-in.js","sourceRoot":"","sources":["../src/apple-sign-in.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAe,MAAM,eAAe,CAAC;AAQjG,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;;
|
|
1
|
+
{"version":3,"file":"apple-sign-in.js","sourceRoot":"","sources":["../src/apple-sign-in.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `AppleSignIn`, bound to `expo-apple-authentication`: Sign in with Apple, which the App Store\n * requires of an app that offers another social sign-in. iOS only; everywhere else, and without\n * the module installed, `available()` answers false and every call answers null.\n *\n * ```ts\n * import { AppleAuthenticationScope, AppleSignIn } from '@ng-native/expo/apple-sign-in';\n *\n * private readonly apple = inject(AppleSignIn);\n *\n * async signIn(): Promise<void> {\n * const credential = await this.apple.signIn({\n * requestedScopes: [AppleAuthenticationScope.FULL_NAME, AppleAuthenticationScope.EMAIL],\n * });\n * if (credential) await this.session.start(credential.identityToken);\n * }\n * ```\n *\n * The button Apple's guidelines ask for is `<apple-sign-in-button>`, in\n * `./apple-sign-in-button.ts`.\n *\n * A sign-in the user cancels answers null rather than throwing: closing the sheet is an ordinary\n * answer, not a failure, and every caller would otherwise have to tell `ERR_REQUEST_CANCELED` apart\n * from a real error itself.\n */\nimport { DestroyRef, InjectionToken, Service, inject, signal, type Signal } from '@angular/core';\nimport type {\n AppleAuthenticationButtonStyle as ExpoButtonStyle,\n AppleAuthenticationButtonType as ExpoButtonType,\n AppleAuthenticationCredential,\n AppleAuthenticationCredentialState as ExpoCredentialState,\n AppleAuthenticationScope as ExpoScope,\n} from 'expo-apple-authentication';\nimport { expoModule, present } from './native.ts';\n\ntype Expo = typeof import('expo-apple-authentication');\n\n/** The module's functions this service calls. The real module is one; a test provides a fake. */\nexport type NativeAppleAuthentication = Pick<\n Expo,\n | 'isAvailableAsync'\n | 'signInAsync'\n | 'refreshAsync'\n | 'signOutAsync'\n | 'getCredentialStateAsync'\n | 'formatFullName'\n | 'addRevokeListener'\n>;\n\ntype Args<K extends keyof NativeAppleAuthentication> = Parameters<NativeAppleAuthentication[K]>;\n\n/*\n * The module's enums, as plain numbers typed as the enums themselves. Importing an enum from the\n * module loads the module, which a test in Node cannot; these are the same values, so either is\n * accepted wherever the module's types are.\n */\n\n/** What `signIn` asks the user to share. */\nexport const AppleAuthenticationScope = {\n FULL_NAME: 0,\n EMAIL: 1,\n} as unknown as typeof ExpoScope;\n\n/** What `credentialState` answers. */\nexport const AppleAuthenticationCredentialState = {\n REVOKED: 0,\n AUTHORIZED: 1,\n NOT_FOUND: 2,\n TRANSFERRED: 3,\n} as unknown as typeof ExpoCredentialState;\n\n/** The button's wording, as native takes it. `<apple-sign-in-button>` takes the names instead. */\nexport const AppleAuthenticationButtonType = {\n SIGN_IN: 0,\n CONTINUE: 1,\n SIGN_UP: 2,\n} as unknown as typeof ExpoButtonType;\n\n/** The button's colours, as native takes them. */\nexport const AppleAuthenticationButtonStyle = {\n WHITE: 0,\n WHITE_OUTLINE: 1,\n BLACK: 2,\n} as unknown as typeof ExpoButtonStyle;\n\n@Service()\nexport class AppleSignIn {\n /** Overridden in a test to sign in without Apple. */\n static readonly SOURCE = new InjectionToken<NativeAppleAuthentication | null>(\n 'angular-native.appleSignInSource',\n {\n factory: () =>\n expoModule(\n 'expo-apple-authentication',\n () => require('expo-apple-authentication') as Expo,\n ['ios'],\n ),\n },\n );\n\n private readonly native = inject(AppleSignIn.SOURCE);\n private readonly revocations = signal(0);\n\n constructor() {\n const native = present(this.native);\n if (!native) return;\n // The user can revoke the app's access in Settings while it runs; the session it signed in\n // with is then no longer theirs to use.\n const subscription = native.addRevokeListener(() =>\n this.revocations.update((count) => count + 1),\n );\n inject(DestroyRef).onDestroy(() => subscription.remove());\n }\n\n /** How many times the user has revoked the app's Apple ID credential while it ran. */\n readonly revoked: Signal<number> = this.revocations.asReadonly();\n\n /** Whether Sign in with Apple can be offered: iOS 13 and later, with the module installed. */\n async available(): Promise<boolean> {\n return (await present(this.native)?.isAvailableAsync()) ?? false;\n }\n\n /**\n * Shows Apple's sign-in sheet. The name and email in the credential are only there the first\n * time a user signs in to the app, so store them then. Null when the user cancels, or without\n * the module.\n */\n async signIn(...options: Args<'signInAsync'>): Promise<AppleAuthenticationCredential | null> {\n if (!this.native) return null;\n try {\n return await this.native.signInAsync(...options);\n } catch (error) {\n if ((error as { code?: unknown } | null)?.code === 'ERR_REQUEST_CANCELED') return null;\n throw error;\n }\n }\n\n /** A fresh credential for a user already signed in. Null without the module. */\n async refresh(...options: Args<'refreshAsync'>): Promise<AppleAuthenticationCredential | null> {\n return (await this.native?.refreshAsync(...options)) ?? null;\n }\n\n /** Signs the user out with Apple. Null without the module. */\n async signOut(...options: Args<'signOutAsync'>): Promise<AppleAuthenticationCredential | null> {\n return (await this.native?.signOutAsync(...options)) ?? null;\n }\n\n /** Whether a user's credential is still authorised, for a check at launch. Null without the module. */\n async credentialState(user: string): Promise<ExpoCredentialState | null> {\n return (await this.native?.getCredentialStateAsync(user)) ?? null;\n }\n\n /** A credential's name, formatted for the user's locale. Empty without the module. */\n formatName(...name: Args<'formatFullName'>): string {\n return this.native?.formatFullName(...name) ?? '';\n }\n}\n\nexport {\n AppleSignInButton,\n type AppleButtonStyle,\n type AppleButtonType,\n} from './apple-sign-in-button.ts';\n"]}
|
package/dist/assets.js
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* would have the second screen's load answer the first's. An app that wants assets warmed before
|
|
19
19
|
* there is an injector at all calls `Asset.loadAsync` itself - there is nothing here to add.
|
|
20
20
|
*/
|
|
21
|
-
import {
|
|
21
|
+
import { expoModuleNow } from "./native.js";
|
|
22
22
|
import { resource } from '@angular/core';
|
|
23
23
|
/**
|
|
24
24
|
* The modules, downloaded, as a resource.
|
|
@@ -35,7 +35,7 @@ export function assetResource(native, modules, options = {}) {
|
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
37
|
function expoAssets() {
|
|
38
|
-
const expo =
|
|
38
|
+
const expo = expoModuleNow('expo-asset', () => require('expo-asset'));
|
|
39
39
|
if (!expo)
|
|
40
40
|
return null;
|
|
41
41
|
return { load: async (modules) => expo.Asset.loadAsync(modules) };
|
package/dist/assets.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assets.js","sourceRoot":"","sources":["../src/assets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"assets.js","sourceRoot":"","sources":["../src/assets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,EAAE,QAAQ,EAAmC,MAAM,eAAe,CAAC;AAa1E;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAC3B,MAA2B,EAC3B,OAA2C,EAC3C,UAAmC,EAAE;IAErC,OAAO,QAAQ,CAAC;QACd,MAAM,EAAE,OAAO;QACf,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACnE,QAAQ,EAAE,OAAO,CAAC,QAAQ;KAC3B,CAAC,CAAC;AACL,CAAC;AAED,SAAS,UAAU;IACjB,MAAM,IAAI,GAAG,aAAa,CACxB,YAAY,EACZ,GAAG,EAAE,CAAC,OAAO,CAAC,YAAY,CAAgC,CAC3D,CAAC;IACF,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,OAA8B,CAAC,EAAE,CAAC;AAC3F,CAAC;AAED,sGAAsG;AACtG,MAAM,UAAU,MAAM,CACpB,OAA2C,EAC3C,UAAmC,EAAE;IAErC,OAAO,aAAa,CAAC,UAAU,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AACvD,CAAC","sourcesContent":["/**\n * `assets()`, bound to `expo-asset`. Images downloaded before they are needed.\n *\n * ```ts\n * protected readonly hero = assets(() => [require('./hero.png')]);\n *\n * // <image [source]=\"hero.value()?.[0]\" />\n * // @if (hero.isLoading()) { <skeleton /> }\n * ```\n *\n * `useAssets()` is a hook around `Asset.loadAsync`, which is already a plain promise - so what is\n * worth having is not a wrapper but the reason the hook exists: a screen that shows an image\n * bundled with the app still has to wait for it the first time, and a splash screen or a skeleton\n * is only useful if something says when the wait is over.\n *\n * That is `resource()`, exactly. It is per call site for the same reason the hook is: two screens\n * loading different pictures are two separate waits, and a singleton holding one `loaded` signal\n * would have the second screen's load answer the first's. An app that wants assets warmed before\n * there is an injector at all calls `Asset.loadAsync` itself - there is nothing here to add.\n */\nimport { expoModuleNow } from './native.ts';\n\nimport { resource, type Injector, type ResourceRef } from '@angular/core';\n\n/** What `require('./x.png')` becomes, and what a downloaded asset knows about itself. */\nexport interface AssetLike {\n readonly uri: string;\n readonly width: number | null;\n readonly height: number | null;\n}\n\nexport interface NativeAssets {\n load(modules: readonly (number | string)[]): Promise<readonly AssetLike[]>;\n}\n\n/**\n * The modules, downloaded, as a resource.\n *\n * `modules` is reactive: a signal read inside it re-runs the load, which is what a screen showing\n * one of several pictures wants. `value()` is undefined until the first load resolves, and a\n * failure lands in `error()` rather than being thrown at a caller who has nothing to do with it.\n */\nexport function assetResource(\n native: NativeAssets | null,\n modules: () => readonly (number | string)[],\n options: { injector?: Injector } = {},\n): ResourceRef<readonly AssetLike[] | undefined> {\n return resource({\n params: modules,\n loader: ({ params }) => native?.load(params) ?? Promise.resolve([]),\n injector: options.injector,\n });\n}\n\nfunction expoAssets(): NativeAssets | null {\n const expo = expoModuleNow(\n 'expo-asset',\n () => require('expo-asset') as typeof import('expo-asset'),\n );\n if (!expo) return null;\n return { load: async (modules) => expo.Asset.loadAsync(modules as number[] | string[]) };\n}\n\n/** Download the modules the reactive `modules` function asks for. Created in an injection context. */\nexport function assets(\n modules: () => readonly (number | string)[],\n options: { injector?: Injector } = {},\n): ResourceRef<readonly AssetLike[] | undefined> {\n return assetResource(expoAssets(), modules, options);\n}\n"]}
|
package/dist/async-storage.js
CHANGED
|
@@ -9,10 +9,13 @@
|
|
|
9
9
|
* See `Store` in `@ng-native/expo/store` for what the signal does.
|
|
10
10
|
*/
|
|
11
11
|
import { InjectionToken } from '@angular/core';
|
|
12
|
-
import { expoModule } from "./native.js";
|
|
12
|
+
import { expoModule, present } from "./native.js";
|
|
13
13
|
import { Store } from "./store.js";
|
|
14
14
|
const plain = () => {
|
|
15
15
|
const module = expoModule('@react-native-async-storage/async-storage', () => require('@react-native-async-storage/async-storage'));
|
|
16
|
+
// Missing from the build: the stand-in, to say so when it is used.
|
|
17
|
+
if (module && !present(module))
|
|
18
|
+
return module;
|
|
16
19
|
const store = module?.default;
|
|
17
20
|
if (!store)
|
|
18
21
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"async-storage.js","sourceRoot":"","sources":["../src/async-storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"async-storage.js","sourceRoot":"","sources":["../src/async-storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,KAAK,EAAoB,MAAM,YAAY,CAAC;AAErD,MAAM,KAAK,GAAG,GAAuB,EAAE;IACrC,MAAM,MAAM,GAAG,UAAU,CACvB,2CAA2C,EAC3C,GAAG,EAAE,CACH,OAAO,CAAC,2CAA2C,CAElD,CACJ,CAAC;IACF,mEAAmE;IACnE,IAAI,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,MAAe,CAAC;IACvD,MAAM,KAAK,GAAG,MAAM,EAAE,OAAO,CAAC;IAC9B,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,OAAO;QACL,GAAG,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;QAChC,GAAG,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC;QAC9C,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;KACvC,CAAC;AACJ,CAAC,CAAC;AAEF,oFAAoF;AACpF,MAAM,CAAC,MAAM,OAAO,GAAG,IAAI,cAAc,CAAQ,wBAAwB,EAAE;IACzE,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;CAClC,CAAC,CAAC","sourcesContent":["/**\n * `Storage`, a `Store` over `@react-native-async-storage/async-storage`: a plain key-value file.\n *\n * ```ts\n * private readonly store = inject(Storage);\n * protected readonly theme = this.store.signal<'light' | 'dark'>('theme', 'light');\n * ```\n *\n * See `Store` in `@ng-native/expo/store` for what the signal does.\n */\nimport { InjectionToken } from '@angular/core';\nimport { expoModule, present } from './native.ts';\nimport { Store, type NativeStore } from './store.ts';\n\nconst plain = (): NativeStore | null => {\n const module = expoModule(\n '@react-native-async-storage/async-storage',\n () =>\n require('@react-native-async-storage/async-storage') as {\n default: typeof import('@react-native-async-storage/async-storage').default;\n },\n );\n // Missing from the build: the stand-in, to say so when it is used.\n if (module && !present(module)) return module as never;\n const store = module?.default;\n if (!store) return null;\n return {\n get: (key) => store.getItem(key),\n set: (key, value) => store.setItem(key, value),\n remove: (key) => store.removeItem(key),\n };\n};\n\n/** A plain key-value file. Bigger and faster than the keychain, and not private. */\nexport const Storage = new InjectionToken<Store>('angular-native.storage', {\n factory: () => new Store(plain()),\n});\nexport type Storage = Store;\n"]}
|
package/dist/audio.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* // {{ player.state().currentTime }}
|
|
8
8
|
* ```
|
|
9
9
|
*/
|
|
10
|
-
import {
|
|
10
|
+
import { expoModuleNow, unavailable } from "./native.js";
|
|
11
11
|
import { ownPlayer, watchAudioPlayer } from "./player.js";
|
|
12
12
|
/**
|
|
13
13
|
* An audio player owned by the current component, released when it is destroyed.
|
|
@@ -16,7 +16,7 @@ import { ownPlayer, watchAudioPlayer } from "./player.js";
|
|
|
16
16
|
* an option to `createAudioPlayer` itself, in milliseconds rather than `videoPlayer`'s seconds.
|
|
17
17
|
*/
|
|
18
18
|
export function audioPlayer(source, options = {}) {
|
|
19
|
-
const expo =
|
|
19
|
+
const expo = expoModuleNow('expo-audio', () => require('expo-audio'), ['ios', 'android', 'web']);
|
|
20
20
|
if (!expo)
|
|
21
21
|
throw unavailable('expo-audio', 'Stand in for the player in the test.');
|
|
22
22
|
const updateInterval = options.timeUpdate === undefined ? undefined : options.timeUpdate * 1000;
|
package/dist/audio.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audio.js","sourceRoot":"","sources":["../src/audio.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"audio.js","sourceRoot":"","sources":["../src/audio.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAe,MAAM,aAAa,CAAC;AAEvE;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CACzB,MAAwC,EACxC,UAAmC,EAAE;IAErC,MAAM,IAAI,GAAG,aAAa,CACxB,YAAY,EACZ,GAAG,EAAE,CAAC,OAAO,CAAC,YAAY,CAAgC,EAC1D,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,CAAC,CAC1B,CAAC;IACF,IAAI,CAAC,IAAI;QAAE,MAAM,WAAW,CAAC,YAAY,EAAE,sCAAsC,CAAC,CAAC;IACnF,MAAM,cAAc,GAAG,OAAO,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAChG,MAAM,MAAM,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,EAAE,EAAE,cAAc,EAAE,CAAC,CAAC;IAClE,OAAO,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC;AAC3D,CAAC","sourcesContent":["/**\n * `audioPlayer`, an `expo-audio` player owned by the component that makes it.\n *\n * ```ts\n * protected readonly player = audioPlayer(this.source, { timeUpdate: 0.5 });\n *\n * // {{ player.state().currentTime }}\n * ```\n */\nimport { expoModuleNow, unavailable } from './native.ts';\nimport { ownPlayer, watchAudioPlayer, type Player } from './player.ts';\n\n/**\n * An audio player owned by the current component, released when it is destroyed.\n *\n * `expo-audio` has no `timeUpdateEventInterval` property to set after the fact - the interval is\n * an option to `createAudioPlayer` itself, in milliseconds rather than `videoPlayer`'s seconds.\n */\nexport function audioPlayer(\n source: import('expo-audio').AudioSource,\n options: { timeUpdate?: number } = {},\n): Player<import('expo-audio').AudioPlayer> {\n const expo = expoModuleNow(\n 'expo-audio',\n () => require('expo-audio') as typeof import('expo-audio'),\n ['ios', 'android', 'web'],\n );\n if (!expo) throw unavailable('expo-audio', 'Stand in for the player in the test.');\n const updateInterval = options.timeUpdate === undefined ? undefined : options.timeUpdate * 1000;\n const native = expo.createAudioPlayer(source, { updateInterval });\n return ownPlayer(native, () => watchAudioPlayer(native));\n}\n"]}
|
package/dist/battery.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { InjectionToken, Service, computed, inject } from '@angular/core';
|
|
5
5
|
import { observed, observedFrom } from "./observed.js";
|
|
6
|
-
import { expoModule } from "./native.js";
|
|
6
|
+
import { expoModule, present } from "./native.js";
|
|
7
7
|
import * as i0 from "@angular/core";
|
|
8
8
|
const NOTHING = { level: null, state: null, saving: null };
|
|
9
9
|
export class Battery {
|
|
@@ -13,6 +13,9 @@ export class Battery {
|
|
|
13
13
|
const expo = expoModule('expo-battery', () => require('expo-battery'));
|
|
14
14
|
if (!expo)
|
|
15
15
|
return NOTHING;
|
|
16
|
+
// Missing from the build: stand-ins, to say so when they are read.
|
|
17
|
+
if (!present(expo))
|
|
18
|
+
return { level: expo, state: expo, saving: expo };
|
|
16
19
|
/**
|
|
17
20
|
* Expo's states are numbers, and one of them is Android-only.
|
|
18
21
|
*
|
package/dist/battery.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"battery.js","sourceRoot":"","sources":["../src/battery.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAe,MAAM,eAAe,CAAC;AACvF,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAiB,MAAM,eAAe,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;;
|
|
1
|
+
{"version":3,"file":"battery.js","sourceRoot":"","sources":["../src/battery.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `Battery`, bound to `expo-battery`. For the work an app should not be doing on 5%.\n */\nimport { InjectionToken, Service, computed, inject, type Signal } from '@angular/core';\nimport { observed, observedFrom, type Observed } from './observed.ts';\nimport { expoModule, present } from './native.ts';\n\n/** What the battery is doing. `unknown` is a real answer on a simulator. */\nexport type BatteryState = 'unknown' | 'unplugged' | 'charging' | 'full';\n\nexport interface BatterySources {\n level: Observed<number> | null;\n state: Observed<BatteryState> | null;\n saving: Observed<boolean> | null;\n}\n\nconst NOTHING: BatterySources = { level: null, state: null, saving: null };\n\n@Service()\nexport class Battery {\n /** Overridden in a test to drain a battery that is not there. */\n static readonly SOURCE = new InjectionToken<BatterySources>('angular-native.batterySource', {\n factory: () => {\n const expo = expoModule(\n 'expo-battery',\n () => require('expo-battery') as typeof import('expo-battery'),\n );\n if (!expo) return NOTHING;\n // Missing from the build: stand-ins, to say so when they are read.\n if (!present(expo)) return { level: expo, state: expo, saving: expo } as never;\n\n /**\n * Expo's states are numbers, and one of them is Android-only.\n *\n * `NOT_CHARGING` means plugged in and holding, which for every purpose an app has is\n * charging: the battery is not going down.\n */\n const STATES: Record<number, BatteryState> = {\n [expo.BatteryState.UNKNOWN]: 'unknown',\n [expo.BatteryState.UNPLUGGED]: 'unplugged',\n [expo.BatteryState.CHARGING]: 'charging',\n [expo.BatteryState.FULL]: 'full',\n [expo.BatteryState.NOT_CHARGING]: 'full',\n };\n\n return {\n level: observedFrom(\n () => expo.getBatteryLevelAsync(),\n (listener) => expo.addBatteryLevelListener(({ batteryLevel }) => listener(batteryLevel)),\n ),\n state: observedFrom(\n async () => STATES[await expo.getBatteryStateAsync()] ?? 'unknown',\n (listener) =>\n expo.addBatteryStateListener(({ batteryState }) =>\n listener(STATES[batteryState] ?? 'unknown'),\n ),\n ),\n saving: observedFrom(\n () => expo.isLowPowerModeEnabledAsync(),\n (listener) => expo.addLowPowerModeListener(({ lowPowerMode }) => listener(lowPowerMode)),\n ),\n };\n },\n });\n\n private readonly sources = inject(Battery.SOURCE);\n private readonly reported: Signal<number> = observed(this.sources.level, 1);\n\n /**\n * Nought to one. One until the platform says otherwise: an app should not open dimmed.\n *\n * A negative reading is not a level. iOS answers `-1` where the battery level is unavailable -\n * every simulator, and a device that will not say - and Expo passes it through. Taken at face\n * value it is below every threshold an app has, so `low` is true and an app starts shedding\n * work on the one device that has no battery to save.\n */\n readonly level: Signal<number> = computed(() => {\n const level = this.reported();\n return level < 0 ? 1 : level;\n });\n\n /** Whether the platform will say. False on a simulator, and on a device that answers -1. */\n readonly known: Signal<boolean> = computed(() => this.reported() >= 0);\n readonly state: Signal<BatteryState> = observed(this.sources.state, 'unknown');\n /**\n * Low Power Mode on iOS, Battery Saver on Android. The signal to skip the background refresh,\n * drop the frame rate of a decoration, or stop prefetching.\n */\n readonly saving: Signal<boolean> = observed(this.sources.saving, false);\n\n readonly charging: Signal<boolean> = computed(\n () => this.state() === 'charging' || this.state() === 'full',\n );\n\n /**\n * Under a fifth and not charging: the point at which an app should start doing less.\n *\n * False whenever the level is unknown. \"I cannot tell\" and \"almost flat\" are not the same\n * answer, and only one of them is a reason to degrade.\n */\n readonly low: Signal<boolean> = computed(\n () => this.known() && this.level() <= 0.2 && !this.charging(),\n );\n}\n"]}
|
package/dist/biometrics.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* it is asked for; `expo-local-authentication`'s config plugin writes it.
|
|
11
11
|
*/
|
|
12
12
|
import { InjectionToken, Service, inject } from '@angular/core';
|
|
13
|
-
import { expoModule } from "./native.js";
|
|
13
|
+
import { expoModule, present } from "./native.js";
|
|
14
14
|
import * as i0 from "@angular/core";
|
|
15
15
|
/** Expo's `AuthenticationType` enum, whose values are these numbers. */
|
|
16
16
|
const KINDS = { 1: 'fingerprint', 2: 'face', 3: 'iris' };
|
|
@@ -22,11 +22,12 @@ export class Biometrics {
|
|
|
22
22
|
native = inject(Biometrics.SOURCE);
|
|
23
23
|
/** Whether there is a sensor and something enrolled on it: whether asking can succeed. */
|
|
24
24
|
async available() {
|
|
25
|
-
|
|
25
|
+
const native = present(this.native);
|
|
26
|
+
if (!native)
|
|
26
27
|
return false;
|
|
27
28
|
const [hardware, enrolled] = await Promise.all([
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
native.hasHardwareAsync(),
|
|
30
|
+
native.isEnrolledAsync(),
|
|
30
31
|
]);
|
|
31
32
|
return hardware && enrolled;
|
|
32
33
|
}
|
package/dist/biometrics.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"biometrics.js","sourceRoot":"","sources":["../src/biometrics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;;
|
|
1
|
+
{"version":3,"file":"biometrics.js","sourceRoot":"","sources":["../src/biometrics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;;AAIlD,wEAAwE;AACxE,MAAM,KAAK,GAAkC,EAAE,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;AAqBxF,MAAM,OAAO,UAAU;IACrB,2DAA2D;IAC3D,MAAM,CAAU,MAAM,GAAG,IAAI,cAAc,CACzC,iCAAiC,EACjC;QACE,OAAO,EAAE,GAAG,EAAE,CACZ,UAAU,CACR,2BAA2B,EAC3B,GAAG,EAAE,CAAC,OAAO,CAAC,2BAA2B,CAAqB,CAC/D,IAAI,IAAI;KACZ,CACF,CAAC;IAEe,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IAEpD,0FAA0F;IAC1F,KAAK,CAAC,SAAS;QACb,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACpC,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC1B,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YAC7C,MAAM,CAAC,gBAAgB,EAAE;YACzB,MAAM,CAAC,eAAe,EAAE;SACzB,CAAC,CAAC;QACH,OAAO,QAAQ,IAAI,QAAQ,CAAC;IAC9B,CAAC;IAED,mGAAmG;IACnG,KAAK,CAAC,KAAK;QACT,MAAM,KAAK,GAAG,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,iCAAiC,EAAE,CAAC,IAAI,EAAE,CAAC;QAC7E,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IACpD,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,YAAY,CAChB,OAAe,EACf,UAA+B,EAAE;QAEjC,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC;QACpE,OAAO,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,EAAE,aAAa,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;IAC/E,CAAC;uGA1CU,UAAU;wGAAV,UAAU;;2FAAV,UAAU;kBADtB,OAAO","sourcesContent":["/**\n * `Biometrics`, bound to `expo-local-authentication`.\n *\n * ```ts\n * private readonly biometrics = inject(Biometrics);\n * const { success } = await this.biometrics.authenticate('Unlock your wallet');\n * ```\n *\n * Face ID needs `NSFaceIDUsageDescription` in the app config, or iOS ends the app the first time\n * it is asked for; `expo-local-authentication`'s config plugin writes it.\n */\nimport { InjectionToken, Service, inject } from '@angular/core';\nimport { expoModule, present } from './native.ts';\n\nexport type BiometricKind = 'fingerprint' | 'face' | 'iris';\n\n/** Expo's `AuthenticationType` enum, whose values are these numbers. */\nconst KINDS: Record<number, BiometricKind> = { 1: 'fingerprint', 2: 'face', 3: 'iris' };\n\n/** Passed, or the platform's reason it did not: `user_cancel`, `lockout`, `not_enrolled`... */\nexport type AuthenticationResult = { success: true } | { success: false; error: string };\n\nexport type AuthenticateOptions = Omit<\n import('expo-local-authentication').LocalAuthenticationOptions,\n 'promptMessage'\n>;\n\n/** The slice of `expo-local-authentication` this needs. */\nexport interface NativeBiometrics {\n hasHardwareAsync(): Promise<boolean>;\n isEnrolledAsync(): Promise<boolean>;\n supportedAuthenticationTypesAsync(): Promise<number[]>;\n authenticateAsync(\n options?: import('expo-local-authentication').LocalAuthenticationOptions,\n ): Promise<AuthenticationResult>;\n}\n\n@Service()\nexport class Biometrics {\n /** Overridden in a test to pass or fail without a face. */\n static readonly SOURCE = new InjectionToken<NativeBiometrics | null>(\n 'angular-native.biometricsSource',\n {\n factory: () =>\n expoModule(\n 'expo-local-authentication',\n () => require('expo-local-authentication') as NativeBiometrics,\n ) ?? null,\n },\n );\n\n private readonly native = inject(Biometrics.SOURCE);\n\n /** Whether there is a sensor and something enrolled on it: whether asking can succeed. */\n async available(): Promise<boolean> {\n const native = present(this.native);\n if (!native) return false;\n const [hardware, enrolled] = await Promise.all([\n native.hasHardwareAsync(),\n native.isEnrolledAsync(),\n ]);\n return hardware && enrolled;\n }\n\n /** The kinds the device has, for a button that says \"Use Face ID\" rather than \"Use biometrics\". */\n async kinds(): Promise<BiometricKind[]> {\n const types = (await this.native?.supportedAuthenticationTypesAsync()) ?? [];\n return types.flatMap((type) => KINDS[type] ?? []);\n }\n\n /**\n * Show the system prompt. Without the module this fails rather than passes: a lock that opens\n * when its sensor is missing is not a lock.\n */\n async authenticate(\n message: string,\n options: AuthenticateOptions = {},\n ): Promise<AuthenticationResult> {\n if (!this.native) return { success: false, error: 'not_available' };\n return this.native.authenticateAsync({ promptMessage: message, ...options });\n }\n}\n"]}
|
package/dist/brightness.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Expo's own API rather than to reach through a convenience here.
|
|
9
9
|
*/
|
|
10
10
|
import { InjectionToken, Service, inject, signal } from '@angular/core';
|
|
11
|
-
import { expoModule } from "./native.js";
|
|
11
|
+
import { expoModule, present } from "./native.js";
|
|
12
12
|
import * as i0 from "@angular/core";
|
|
13
13
|
export class Brightness {
|
|
14
14
|
/** Overridden in a test to watch the level without changing a screen. */
|
|
@@ -17,6 +17,9 @@ export class Brightness {
|
|
|
17
17
|
const expo = expoModule('expo-brightness', () => require('expo-brightness'));
|
|
18
18
|
if (!expo)
|
|
19
19
|
return null;
|
|
20
|
+
// Missing from the build: the stand-in, to say so when it is used.
|
|
21
|
+
if (!present(expo))
|
|
22
|
+
return expo;
|
|
20
23
|
return {
|
|
21
24
|
get: () => expo.getBrightnessAsync(),
|
|
22
25
|
set: (level) => expo.setBrightnessAsync(level),
|
|
@@ -30,7 +33,9 @@ export class Brightness {
|
|
|
30
33
|
/** Nought to one. One until the platform answers, since that is what a screen already shows. */
|
|
31
34
|
level = this.current.asReadonly();
|
|
32
35
|
constructor() {
|
|
33
|
-
void this.native
|
|
36
|
+
void present(this.native)
|
|
37
|
+
?.get()
|
|
38
|
+
.then((level) => this.current.set(level), () => { });
|
|
34
39
|
}
|
|
35
40
|
/**
|
|
36
41
|
* Set it, and return the function that puts it back.
|
package/dist/brightness.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"brightness.js","sourceRoot":"","sources":["../src/brightness.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAe,MAAM,eAAe,CAAC;AACrF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;;
|
|
1
|
+
{"version":3,"file":"brightness.js","sourceRoot":"","sources":["../src/brightness.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `Brightness`, bound to `expo-brightness`.\n *\n * For the screens that have a reason to change it: a boarding pass, a QR code.\n *\n * The app's own brightness only. The system one is a global setting an app can leave changed\n * after it is gone, and needs a permission on Android - a decision to make deliberately against\n * Expo's own API rather than to reach through a convenience here.\n */\nimport { InjectionToken, Service, inject, signal, type Signal } from '@angular/core';\nimport { expoModule, present } from './native.ts';\n\nexport interface NativeBrightness {\n get(): Promise<number>;\n set(level: number): Promise<void>;\n restore(): Promise<void>;\n}\n\n@Service()\nexport class Brightness {\n /** Overridden in a test to watch the level without changing a screen. */\n static readonly SOURCE = new InjectionToken<NativeBrightness | null>(\n 'angular-native.brightnessSource',\n {\n factory: () => {\n const expo = expoModule(\n 'expo-brightness',\n () => require('expo-brightness') as typeof import('expo-brightness'),\n );\n if (!expo) return null;\n // Missing from the build: the stand-in, to say so when it is used.\n if (!present(expo)) return expo as never;\n return {\n get: () => expo.getBrightnessAsync(),\n set: (level) => expo.setBrightnessAsync(level),\n restore: () => expo.restoreSystemBrightnessAsync(),\n };\n },\n },\n );\n\n private readonly native = inject(Brightness.SOURCE);\n private readonly current = signal(1);\n\n /** Nought to one. One until the platform answers, since that is what a screen already shows. */\n readonly level: Signal<number> = this.current.asReadonly();\n\n constructor() {\n void present(this.native)\n ?.get()\n .then(\n (level) => this.current.set(level),\n () => {},\n );\n }\n\n /**\n * Set it, and return the function that puts it back.\n *\n * Always paired, because the alternative is an app that leaves the screen at full brightness\n * after the boarding pass has been shown - which the user experiences as a battery fault.\n */\n set(level: number): () => void {\n const clamped = Math.min(1, Math.max(0, level));\n // A stack, as the status bar's is: a screen pushed over one that set a level of its own puts\n // back the level beneath it, not the system's, when it goes.\n const entry = { level: clamped };\n this.levels.push(entry);\n this.apply(clamped);\n return () => {\n const at = this.levels.indexOf(entry);\n if (at === -1) return;\n this.levels.splice(at, 1);\n if (at < this.levels.length) return;\n const below = this.levels.at(-1);\n if (below) this.apply(below.level);\n else this.restore();\n };\n }\n\n /** Every level set and not yet put back, the one in force last. */\n private readonly levels: { readonly level: number }[] = [];\n\n private apply(level: number): void {\n this.current.set(level);\n void this.native?.set(level);\n }\n\n restore(): void {\n // Everything set so far is put back, so a release still to come has nothing left to undo.\n this.levels.length = 0;\n void this.native\n ?.restore()\n .then(() => this.native?.get().then((level) => this.current.set(level)));\n }\n}\n"]}
|
package/dist/camera.js
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
*/
|
|
25
25
|
import { Directive, ElementRef, InjectionToken, inject } from '@angular/core';
|
|
26
26
|
import { Engine } from '@ng-native/fabric';
|
|
27
|
-
import { expoModule, optional } from "./native.js";
|
|
27
|
+
import { expoModule, optional, present } from "./native.js";
|
|
28
28
|
import * as i0 from "@angular/core";
|
|
29
29
|
export class Camera {
|
|
30
30
|
/** Overridden in a test to take pictures without a camera. */
|
|
@@ -32,6 +32,9 @@ export class Camera {
|
|
|
32
32
|
factory: () => {
|
|
33
33
|
const core = optional(() => require('expo-modules-core'));
|
|
34
34
|
const module = expoModule('expo-camera', () => core?.requireOptionalNativeModule('ExpoCamera'));
|
|
35
|
+
// Missing, the stand-in is the functions: taking a picture says what to install.
|
|
36
|
+
if (module && !present(module))
|
|
37
|
+
return module;
|
|
35
38
|
// The camera is the module's first view, so its functions are under the module's name.
|
|
36
39
|
return module?.ViewPrototypes?.['ExpoCamera'] ?? null;
|
|
37
40
|
},
|