@symbiote-native/engine 1.2.0 → 1.3.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.
Files changed (71) hide show
  1. package/android/CMakeLists.txt +31 -1
  2. package/android/build.gradle +4 -1
  3. package/build/accessibility-info/index.android.js +21 -17
  4. package/build/accessibility-info/index.ios.js +2 -0
  5. package/build/accessibility-info/shared.d.ts +1 -1
  6. package/build/alert/shared.d.ts +1 -1
  7. package/build/alert/shared.js +3 -7
  8. package/build/app-state/index.d.ts +1 -1
  9. package/build/app-state/index.js +35 -27
  10. package/build/back-handler/index.d.ts +7 -2
  11. package/build/back-handler/index.js +19 -10
  12. package/build/events/index.js +10 -1
  13. package/build/fabric-props.js +1 -1
  14. package/build/fabric.d.ts +7 -0
  15. package/build/fabric.js +14 -0
  16. package/build/host-behavior.d.ts +1 -0
  17. package/build/image-loader.d.ts +4 -2
  18. package/build/image-loader.js +14 -25
  19. package/build/image-source-write.d.ts +1 -1
  20. package/build/image-source-write.js +14 -2
  21. package/build/index.d.ts +2 -2
  22. package/build/index.js +6 -6
  23. package/build/invariant.d.ts +1 -0
  24. package/build/invariant.js +10 -0
  25. package/build/keyboard/index.js +11 -32
  26. package/build/linking/index.android.js +5 -3
  27. package/build/linking/shared.d.ts +1 -1
  28. package/build/linking/shared.js +16 -19
  29. package/build/native-events.d.ts +5 -0
  30. package/build/native-events.js +21 -0
  31. package/build/node.js +18 -2
  32. package/build/permissions-android/index.android.d.ts +59 -0
  33. package/build/permissions-android/index.android.js +47 -0
  34. package/build/permissions-android/index.d.ts +1 -115
  35. package/build/permissions-android/index.ios.d.ts +59 -0
  36. package/build/permissions-android/index.ios.js +31 -0
  37. package/build/permissions-android/index.js +3 -184
  38. package/build/permissions-android/shared.d.ts +63 -0
  39. package/build/permissions-android/shared.js +66 -0
  40. package/build/platform/index.android.js +3 -5
  41. package/build/platform/index.ios.js +3 -3
  42. package/build/platform/shared.d.ts +4 -0
  43. package/build/platform/shared.js +9 -0
  44. package/build/platform-color/index.android.d.ts +4 -0
  45. package/build/platform-color/index.android.js +7 -0
  46. package/build/platform-color/index.d.ts +2 -20
  47. package/build/platform-color/index.js +1 -45
  48. package/build/platform-color/shared.d.ts +21 -0
  49. package/build/platform-color/shared.js +41 -0
  50. package/build/settings/index.android.d.ts +6 -0
  51. package/build/settings/index.android.js +21 -0
  52. package/build/settings/index.d.ts +1 -8
  53. package/build/settings/index.ios.d.ts +8 -0
  54. package/build/settings/index.ios.js +122 -0
  55. package/build/settings/index.js +3 -122
  56. package/build/share/index.android.js +9 -32
  57. package/build/share/index.ios.js +14 -15
  58. package/build/share/shared.d.ts +4 -2
  59. package/build/share/shared.js +7 -10
  60. package/build/status-bar/index.android.js +1 -1
  61. package/build/status-bar/index.ios.js +4 -3
  62. package/build/toast-android/index.android.d.ts +10 -0
  63. package/build/toast-android/index.android.js +108 -0
  64. package/build/toast-android/index.d.ts +1 -10
  65. package/build/toast-android/index.ios.d.ts +10 -0
  66. package/build/toast-android/index.ios.js +19 -0
  67. package/build/toast-android/index.js +3 -108
  68. package/cpp/SymbioteFabricProps.cpp +204 -120
  69. package/cpp/SymbioteFabricProps.h +5 -0
  70. package/cpp/SymbioteTree.cpp +20 -6
  71. package/package.json +2 -2
package/build/index.js CHANGED
@@ -44,7 +44,7 @@ export { SymbioteSurface, createSurface } from './surface.js';
44
44
  export { setEventDispatcher } from './dispatch.js';
45
45
  // `setColorProcessor` / `processColor` were only RE-exported by the old `commit.ts`; they point at
46
46
  // their real home now.
47
- export { setColorProcessor, processColor } from './platform-color/index.js';
47
+ export { setColorProcessor, processColor } from './platform-color';
48
48
  export { dispatchViewCommand, sendAccessibilityEvent, setNativeProps, getNativeTag, getNativeNode, whenCommitted, measure, measureInWindow, measureLayout, disposeRoot, requestCommitFor, } from './imperative.js';
49
49
  // The tree host: the seam a runtime installs to answer about the tree JS does not hold. `setTreeHost`
50
50
  // is what `installFabric()` (@symbiote-native/test-utils) calls with the TypeScript applier.
@@ -78,7 +78,7 @@ export { fabricProps } from './fabric-props.js';
78
78
  // createElement): the imperative measure/setNativeProps/focus API, on the shared node prototype.
79
79
  // toPublicInstance is the identity that names the seam — see ./host-instance.
80
80
  export { toPublicInstance } from './host-instance/index.js';
81
- export { PlatformColor, DynamicColorIOS, isOpaqueColorValue, } from './platform-color/index.js';
81
+ export { PlatformColor, DynamicColorIOS, isOpaqueColorValue, } from './platform-color';
82
82
  // CSS-style processors (boxShadow/filter): RN parses these in JS before native because
83
83
  // enableNativeCSSParsing() defaults to false. Exported so an adapter / test can reuse them.
84
84
  export { processBoxShadow } from './process-box-shadow/index.js';
@@ -117,9 +117,9 @@ export { setImageSourceResolver, resolveImageSource, } from './image-source-reso
117
117
  export { ActionSheetIOS } from './action-sheet-ios/index.js';
118
118
  export { Linking } from './linking';
119
119
  export { Vibration } from './vibration';
120
- export { ToastAndroid } from './toast-android/index.js';
120
+ export { ToastAndroid } from './toast-android';
121
121
  export { SoundManager } from './sound-manager/index.js';
122
- export { Settings } from './settings/index.js';
122
+ export { Settings } from './settings';
123
123
  export { I18nManager } from './i18n-manager/index.js';
124
124
  // Device-state / event modules (Dimensions, Appearance, AppState, Keyboard, …).
125
125
  export { Dimensions } from './dimensions/index.js';
@@ -135,8 +135,8 @@ export { createAppRegistry } from './app-registry/index.js';
135
135
  export { Keyboard, KEYBOARD_EVENT } from './keyboard/index.js';
136
136
  export { currentlyFocusedInput, setInputFocused, setInputBlurred, blurTextInput, focusTextInput, } from './text-input-state.js';
137
137
  export { LayoutAnimation } from './layout-animation/index.js';
138
- export { BackHandler } from './back-handler/index.js';
139
- export { PermissionsAndroid, PERMISSIONS, RESULTS, } from './permissions-android/index.js';
138
+ export { BackHandler, installBackHandler } from './back-handler/index.js';
139
+ export { PermissionsAndroid, PERMISSIONS, RESULTS, } from './permissions-android';
140
140
  export { AccessibilityInfo } from './accessibility-info';
141
141
  // StatusBar: values from the platform-selected './status-bar', types from '-shared' (the
142
142
  // .ios re-export would otherwise duplicate-export the type symbols).
@@ -0,0 +1 @@
1
+ export declare function invariant(condition: boolean, message: string): asserts condition;
@@ -0,0 +1,10 @@
1
+ // RN's `invariant(condition, message)` (the `invariant` package): a synchronous throw named
2
+ // 'Invariant Violation' carrying the message verbatim. Ports use it where RN does, so a failure is
3
+ // the same exception at the same moment.
4
+ export function invariant(condition, message) {
5
+ if (condition)
6
+ return;
7
+ const error = new Error(message);
8
+ error.name = 'Invariant Violation';
9
+ throw error;
10
+ }
@@ -33,25 +33,6 @@ function isKeyboardEvent(payload) {
33
33
  // Keyboard listens to its OWN show/hide events and caches the event so the synchronous
34
34
  // isVisible() / metrics() reads need no native round-trip.
35
35
  let currentlyShowing = null;
36
- // Every live subscription this module handed out, grouped by event type, so
37
- // removeAllListeners(eventType) can tear them all down. The shared NativeEventEmitter
38
- // exposes only per-listener remove(), so Keyboard tracks the set itself (mirrors RN's
39
- // _emitter.removeAllListeners, which we cannot reach through the shared emitter).
40
- const subscriptions = new Map();
41
- function trackSubscription(eventType, subscription) {
42
- let set = subscriptions.get(eventType);
43
- if (set === undefined) {
44
- set = new Set();
45
- subscriptions.set(eventType, set);
46
- }
47
- set.add(subscription);
48
- return {
49
- remove() {
50
- subscription.remove();
51
- set.delete(subscription);
52
- },
53
- };
54
- }
55
36
  // Lazily resolved so importing this module has no native side effect: a headless
56
37
  // run without a fake __turboModuleProxy still loads it; resolution happens on the
57
38
  // first addListener. Null when the module isn't linked.
@@ -73,42 +54,40 @@ const deviceEventModule = createDeviceEventModule({
73
54
  });
74
55
  },
75
56
  });
57
+ // RN builds KeyboardImpl, and with it the didShow/didHide tracking, the first time Keyboard is
58
+ // touched. Every method goes through here so the tracking starts at that same moment.
76
59
  function getEmitter() {
77
60
  return deviceEventModule.getEmitter();
78
61
  }
79
62
  export const Keyboard = {
80
63
  addListener(eventType, listener) {
81
64
  dlog(`Keyboard.addListener -> ${eventType}`);
82
- return trackSubscription(eventType, getEmitter().addListener(eventType, listener));
65
+ return getEmitter().addListener(eventType, listener);
83
66
  },
84
- // Tear down every listener this module added for one event type. The self-subscription
85
- // that feeds the cache is untracked, so it survives (RN parity: removeAllListeners only
86
- // clears caller subscriptions). No-op when nobody's listening for that event.
67
+ // RN's `_emitter.removeAllListeners(eventType)`: every listener of the event on the device bus,
68
+ // Keyboard's own show/hide tracking included — a later show is then no longer cached.
87
69
  removeAllListeners(eventType) {
88
70
  dlog(`Keyboard.removeAllListeners -> ${eventType}`);
89
- const set = subscriptions.get(eventType);
90
- if (set === undefined)
91
- return;
92
- for (const subscription of set)
93
- subscription.remove();
94
- set.clear();
71
+ getEmitter().removeAllListeners(eventType);
95
72
  },
96
73
  // Whether the keyboard is last known to be visible. Reads the cached show event.
97
74
  isVisible() {
75
+ getEmitter();
98
76
  return currentlyShowing !== null;
99
77
  },
100
78
  // The soft-keyboard frame if visible (the cached event's endCoordinates), else
101
79
  // undefined. RN's metrics().
102
80
  metrics() {
81
+ getEmitter();
103
82
  return currentlyShowing?.endCoordinates;
104
83
  },
105
84
  // Syncs an accessory view's layout with the keyboard transition: configure the next
106
85
  // commit to animate over the keyboard's own duration/easing. RN's
107
- // scheduleLayoutAnimation (Keyboard.js:193). Skipped when duration is absent or 0,
108
- // since a zero-length animation is a no-op.
86
+ // scheduleLayoutAnimation (Keyboard.js:193): only when `duration != null && duration !== 0`.
109
87
  scheduleLayoutAnimation(event) {
88
+ getEmitter();
110
89
  const { duration, easing } = event;
111
- if (duration === 0)
90
+ if (duration == null || duration === 0)
112
91
  return;
113
92
  dlog(`Keyboard.scheduleLayoutAnimation -> duration ${duration}, easing ${easing}`);
114
93
  LayoutAnimation.configureNext({
@@ -9,9 +9,11 @@
9
9
  import { createLinking } from './shared.js';
10
10
  export const Linking = createLinking({
11
11
  moduleName: 'IntentAndroid',
12
- sendIntent: (module, action, extras) => {
13
- if (module === null || module.sendIntent === undefined) {
14
- return Promise.reject(new Error('Linking: IntentAndroid native module unavailable'));
12
+ // RN: `nullthrows(NativeIntentAndroid).sendIntent(action, extras)`.
13
+ sendIntent: (requireModule, action, extras) => {
14
+ const module = requireModule();
15
+ if (module.sendIntent === undefined) {
16
+ throw new TypeError('NativeIntentAndroid.sendIntent is not a function');
15
17
  }
16
18
  return module.sendIntent(action, extras);
17
19
  },
@@ -26,7 +26,7 @@ export interface ILinkingStatic {
26
26
  }
27
27
  export interface ILinkingPlatform {
28
28
  moduleName: string;
29
- sendIntent(module: INativeLinkingModule | null, action: string, extras?: IIntentExtra[]): Promise<void>;
29
+ sendIntent(requireModule: () => INativeLinkingModule, action: string, extras?: IIntentExtra[]): Promise<void>;
30
30
  }
31
31
  export declare function createLinking(platform: ILinkingPlatform): ILinkingStatic;
32
32
  export {};
@@ -12,6 +12,8 @@ import { getNativeModule } from '../native-modules/index.js';
12
12
  import { dlog } from '../debug.js';
13
13
  // The one event symbiote observes: an incoming deep link. RN's LinkingEventDefinitions.
14
14
  const URL_EVENT = 'url';
15
+ // The `nullthrows` package's message, which RN's Linking surfaces for a missing native module.
16
+ const NULLTHROWS_MESSAGE = 'Got unexpected null or undefined';
15
17
  // RN's _validateURL: a typo / empty URL must fail loudly at the call, not reach native.
16
18
  function validateUrl(url) {
17
19
  if (typeof url !== 'string' || url.length === 0) {
@@ -50,8 +52,15 @@ export function createLinking(platform) {
50
52
  }
51
53
  return emitter;
52
54
  }
53
- function moduleUnavailable() {
54
- return new Error(`Linking: ${platform.moduleName} native module unavailable`);
55
+ // RN's `nullthrows(NativeModule)`: a missing module throws SYNCHRONOUSLY, with that package's
56
+ // message, from every method (Linking.js).
57
+ function requireModule() {
58
+ const module = getModule();
59
+ if (module === null) {
60
+ dlog(`Linking: ${platform.moduleName} unavailable -> throw`);
61
+ throw new Error(NULLTHROWS_MESSAGE);
62
+ }
63
+ return module;
55
64
  }
56
65
  return {
57
66
  addEventListener(eventType, listener) {
@@ -63,36 +72,24 @@ export function createLinking(platform) {
63
72
  openURL(url) {
64
73
  validateUrl(url);
65
74
  dlog(`Linking.openURL -> ${url}`);
66
- const module = getModule();
67
- if (module === null)
68
- return Promise.reject(moduleUnavailable());
69
- return module.openURL(url);
75
+ return requireModule().openURL(url);
70
76
  },
71
77
  canOpenURL(url) {
72
78
  validateUrl(url);
73
79
  dlog(`Linking.canOpenURL -> ${url}`);
74
- const module = getModule();
75
- if (module === null)
76
- return Promise.reject(moduleUnavailable());
77
- return module.canOpenURL(url);
80
+ return requireModule().canOpenURL(url);
78
81
  },
79
82
  getInitialURL() {
80
83
  dlog('Linking.getInitialURL');
81
- const module = getModule();
82
- if (module === null)
83
- return Promise.resolve(null);
84
- return module.getInitialURL();
84
+ return requireModule().getInitialURL();
85
85
  },
86
86
  openSettings() {
87
87
  dlog('Linking.openSettings');
88
- const module = getModule();
89
- if (module === null)
90
- return Promise.reject(moduleUnavailable());
91
- return module.openSettings();
88
+ return requireModule().openSettings();
92
89
  },
93
90
  sendIntent(action, extras) {
94
91
  dlog(`Linking.sendIntent -> ${action}`);
95
- return platform.sendIntent(getModule(), action, extras);
92
+ return platform.sendIntent(requireModule, action, extras);
96
93
  },
97
94
  };
98
95
  }
@@ -10,6 +10,9 @@ export interface IEventSubscription {
10
10
  export declare function installDeviceEventHub(): void;
11
11
  export interface IDeviceEventSource {
12
12
  addListener(eventType: string, listener: (payload: unknown) => void): IEventSubscription;
13
+ removeAllListeners?(eventType: string): void;
14
+ listenerCount?(eventType: string): number;
15
+ emit?(eventType: string, ...args: unknown[]): void;
13
16
  }
14
17
  export declare function setDeviceEventSource(source: IDeviceEventSource): void;
15
18
  export interface IEventEmitterModule {
@@ -21,4 +24,6 @@ export declare class NativeEventEmitter {
21
24
  private readonly module?;
22
25
  constructor(module?: IEventEmitterModule);
23
26
  addListener(eventType: string, listener: INativeEventListener): IEventSubscription;
27
+ emit(eventType: string, ...args: unknown[]): void;
28
+ removeAllListeners(eventType: string): void;
24
29
  }
@@ -9,6 +9,7 @@
9
9
  // hub below stays as the fallback bus for headless/non-RN runs.
10
10
  import { dlog } from './debug.js';
11
11
  import { runWrapped } from './dispatch.js';
12
+ import { invariant } from './invariant.js';
12
13
  // The single device-event bus. Native pushes into `emit`; subscribers (wrapped by
13
14
  // NativeEventEmitter) live in `listeners`, keyed by event name.
14
15
  const listeners = new Map();
@@ -127,4 +128,24 @@ export class NativeEventEmitter {
127
128
  },
128
129
  };
129
130
  }
131
+ // RN's NativeEventEmitter.emit: a JS-side emit onto the same device bus native feeds.
132
+ emit(eventType, ...args) {
133
+ if (injectedSource !== undefined) {
134
+ injectedSource.emit?.(eventType, ...args);
135
+ return;
136
+ }
137
+ emit(eventType, ...args);
138
+ }
139
+ // RN's NativeEventEmitter.removeAllListeners: EVERY listener of the event on the device bus goes,
140
+ // not only this emitter's, and the module counter drops by that many.
141
+ removeAllListeners(eventType) {
142
+ invariant(eventType != null, '`NativeEventEmitter.removeAllListener()` requires a non-null argument.');
143
+ if (injectedSource !== undefined) {
144
+ this.module?.removeListeners(injectedSource.listenerCount?.(eventType) ?? 0);
145
+ injectedSource.removeAllListeners?.(eventType);
146
+ return;
147
+ }
148
+ this.module?.removeListeners(listeners.get(eventType)?.size ?? 0);
149
+ listeners.delete(eventType);
150
+ }
130
151
  }
package/build/node.js CHANGED
@@ -15,6 +15,7 @@ import { appListenerFor, attachHostBehavior, claimModeFor, hasAttachedBehaviors,
15
15
  import { configPayloadFold } from './registry.js';
16
16
  import { resolveStructuredStyle } from './structured-style.js';
17
17
  import { IMAGE_SOURCE_PROPS, IMAGE_LOAD_EVENT_NAMES, anyImageLoadEventListenerWired, resolveImageSourceProp, } from './image-source-write.js';
18
+ import { Platform } from './platform';
18
19
  // A cycle, deliberately: `imperative.ts` imports this module for the node shape, and the prototype
19
20
  // methods below call back into it. Neither side touches the other at module-evaluation time - only
20
21
  // inside a function body - so every loader (tsc, vitest, Metro) resolves it fine. The alternative
@@ -485,7 +486,7 @@ export function writeProp(node, key, value) {
485
486
  written = resolveStructuredStyle(value);
486
487
  }
487
488
  else if (node.resolvesImageSources && IMAGE_SOURCE_PROPS.has(key)) {
488
- written = resolveImageSourceProp(value);
489
+ written = resolveImageSourceProp(value, key === 'source' && Platform.OS === 'android');
489
490
  }
490
491
  if (typeof written === 'function') {
491
492
  let bag = functionProps.get(node);
@@ -591,6 +592,12 @@ export function setBehaviorListener(node, name, listener) {
591
592
  if (flagProp !== undefined)
592
593
  setProp(node, flagProp, listener === undefined ? undefined : true);
593
594
  }
595
+ // The unowned names whose presence a platform rule reads. See `setEventListener`.
596
+ const PRESSABILITY_NAMES = new Set([
597
+ 'press',
598
+ 'longPress',
599
+ 'startShouldSetResponder',
600
+ ]);
594
601
  export function setEventListener(node, name, value) {
595
602
  const isHandler = typeof value === 'function';
596
603
  // A name a host behavior OWNS never reaches `node.listeners` — the behavior's dispatcher holds
@@ -619,6 +626,14 @@ export function setEventListener(node, name, value) {
619
626
  setProp(node, flagged, isHandler ? true : undefined);
620
627
  return;
621
628
  }
629
+ // A plain `<text>` presses through the engine's own synthesis, with no behavior owning the
630
+ // names, yet its payload depends on whether it is pressable (Text.js:145-163: Android
631
+ // `accessible`, the `link` role). Same bit as the owned path, on the flip only.
632
+ if (PRESSABILITY_NAMES.has(name)) {
633
+ const wasWired = node.listeners?.has(name) === true;
634
+ if (wasWired !== isHandler)
635
+ recordSetOwnedListener(node, name, isHandler);
636
+ }
622
637
  if (isHandler) {
623
638
  const handler = value;
624
639
  const listeners = (node.listeners ??= new Map());
@@ -1165,7 +1180,8 @@ export function routeProp(node, key, value) {
1165
1180
  // `style` holding a string, which is not a style and is dropped with nothing red. React's
1166
1181
  // wrapper resolves the name itself (components/scroll-view/shared.ts), so this gap could
1167
1182
  // only ever show on the tag path.
1168
- routeProp(node.childHost, slotKey === 'style' && typeof value === 'string' ? 'class' : slotKey, value);
1183
+ const slotValueFor = node.hostBehavior?.slotValueFor;
1184
+ routeProp(node.childHost, slotKey === 'style' && typeof value === 'string' ? 'class' : slotKey, slotValueFor === undefined ? value : slotValueFor(slotKey, value));
1169
1185
  return;
1170
1186
  }
1171
1187
  }
@@ -0,0 +1,59 @@
1
+ import { type IPermission, type IPermissionStatus, type IRationale } from './shared';
2
+ export { PERMISSIONS, RESULTS, type IPermission, type IPermissionStatus, type IRationale, } from './shared';
3
+ export declare const PermissionsAndroid: {
4
+ PERMISSIONS: Readonly<{
5
+ readonly READ_CALENDAR: "android.permission.READ_CALENDAR";
6
+ readonly WRITE_CALENDAR: "android.permission.WRITE_CALENDAR";
7
+ readonly CAMERA: "android.permission.CAMERA";
8
+ readonly READ_CONTACTS: "android.permission.READ_CONTACTS";
9
+ readonly WRITE_CONTACTS: "android.permission.WRITE_CONTACTS";
10
+ readonly GET_ACCOUNTS: "android.permission.GET_ACCOUNTS";
11
+ readonly ACCESS_FINE_LOCATION: "android.permission.ACCESS_FINE_LOCATION";
12
+ readonly ACCESS_COARSE_LOCATION: "android.permission.ACCESS_COARSE_LOCATION";
13
+ readonly ACCESS_BACKGROUND_LOCATION: "android.permission.ACCESS_BACKGROUND_LOCATION";
14
+ readonly RECORD_AUDIO: "android.permission.RECORD_AUDIO";
15
+ readonly READ_PHONE_STATE: "android.permission.READ_PHONE_STATE";
16
+ readonly CALL_PHONE: "android.permission.CALL_PHONE";
17
+ readonly READ_CALL_LOG: "android.permission.READ_CALL_LOG";
18
+ readonly WRITE_CALL_LOG: "android.permission.WRITE_CALL_LOG";
19
+ readonly ADD_VOICEMAIL: "com.android.voicemail.permission.ADD_VOICEMAIL";
20
+ readonly READ_VOICEMAIL: "com.android.voicemail.permission.READ_VOICEMAIL";
21
+ readonly WRITE_VOICEMAIL: "com.android.voicemail.permission.WRITE_VOICEMAIL";
22
+ readonly USE_SIP: "android.permission.USE_SIP";
23
+ readonly PROCESS_OUTGOING_CALLS: "android.permission.PROCESS_OUTGOING_CALLS";
24
+ readonly BODY_SENSORS: "android.permission.BODY_SENSORS";
25
+ readonly BODY_SENSORS_BACKGROUND: "android.permission.BODY_SENSORS_BACKGROUND";
26
+ readonly SEND_SMS: "android.permission.SEND_SMS";
27
+ readonly RECEIVE_SMS: "android.permission.RECEIVE_SMS";
28
+ readonly READ_SMS: "android.permission.READ_SMS";
29
+ readonly RECEIVE_WAP_PUSH: "android.permission.RECEIVE_WAP_PUSH";
30
+ readonly RECEIVE_MMS: "android.permission.RECEIVE_MMS";
31
+ readonly READ_EXTERNAL_STORAGE: "android.permission.READ_EXTERNAL_STORAGE";
32
+ readonly READ_MEDIA_IMAGES: "android.permission.READ_MEDIA_IMAGES";
33
+ readonly READ_MEDIA_VIDEO: "android.permission.READ_MEDIA_VIDEO";
34
+ readonly READ_MEDIA_AUDIO: "android.permission.READ_MEDIA_AUDIO";
35
+ readonly READ_MEDIA_VISUAL_USER_SELECTED: "android.permission.READ_MEDIA_VISUAL_USER_SELECTED";
36
+ readonly WRITE_EXTERNAL_STORAGE: "android.permission.WRITE_EXTERNAL_STORAGE";
37
+ readonly BLUETOOTH_CONNECT: "android.permission.BLUETOOTH_CONNECT";
38
+ readonly BLUETOOTH_SCAN: "android.permission.BLUETOOTH_SCAN";
39
+ readonly BLUETOOTH_ADVERTISE: "android.permission.BLUETOOTH_ADVERTISE";
40
+ readonly ACCESS_MEDIA_LOCATION: "android.permission.ACCESS_MEDIA_LOCATION";
41
+ readonly ACCEPT_HANDOVER: "android.permission.ACCEPT_HANDOVER";
42
+ readonly ACTIVITY_RECOGNITION: "android.permission.ACTIVITY_RECOGNITION";
43
+ readonly ANSWER_PHONE_CALLS: "android.permission.ANSWER_PHONE_CALLS";
44
+ readonly READ_PHONE_NUMBERS: "android.permission.READ_PHONE_NUMBERS";
45
+ readonly UWB_RANGING: "android.permission.UWB_RANGING";
46
+ readonly POST_NOTIFICATIONS: "android.permission.POST_NOTIFICATIONS";
47
+ readonly NEARBY_WIFI_DEVICES: "android.permission.NEARBY_WIFI_DEVICES";
48
+ }>;
49
+ RESULTS: Readonly<{
50
+ readonly GRANTED: "granted";
51
+ readonly DENIED: "denied";
52
+ readonly NEVER_ASK_AGAIN: "never_ask_again";
53
+ }>;
54
+ checkPermission(permission: IPermission): Promise<boolean>;
55
+ check(permission: IPermission): Promise<boolean>;
56
+ requestPermission(permission: IPermission, rationale?: IRationale): Promise<boolean>;
57
+ request(permission: IPermission, rationale?: IRationale): Promise<IPermissionStatus>;
58
+ requestMultiple(permissions: IPermission[]): Promise<Record<string, IPermissionStatus>>;
59
+ };
@@ -0,0 +1,47 @@
1
+ // PermissionsAndroid, Android build: RN's PermissionsAndroid.js with `Platform.OS === 'android'`.
2
+ // Every call reaches the native module and returns what it returns; a missing module is RN's
3
+ // invariant. request() shows the rationale through DialogManagerAndroid first when native says to.
4
+ import { dlog } from '../debug.js';
5
+ import { invariant } from '../invariant.js';
6
+ import { getNativeModule } from '../native-modules/index.js';
7
+ import { CHECK_PERMISSION_DEPRECATED, PERMISSIONS, PERMISSIONS_ANDROID_MODULE, REQUEST_PERMISSION_DEPRECATED, RESULTS, } from './shared.js';
8
+ export { PERMISSIONS, RESULTS, } from './shared.js';
9
+ const NOT_INSTALLED = 'PermissionsAndroid is not installed correctly.';
10
+ function requireModule() {
11
+ const module = getNativeModule(PERMISSIONS_ANDROID_MODULE);
12
+ dlog(`PermissionsAndroid: module ${module ? 'resolved' : 'NOT resolved (null)'}`);
13
+ invariant(module !== null, NOT_INSTALLED);
14
+ return module;
15
+ }
16
+ export const PermissionsAndroid = {
17
+ PERMISSIONS,
18
+ RESULTS,
19
+ checkPermission(permission) {
20
+ console.warn(CHECK_PERMISSION_DEPRECATED);
21
+ return requireModule().checkPermission(permission);
22
+ },
23
+ check(permission) {
24
+ return requireModule().checkPermission(permission);
25
+ },
26
+ async requestPermission(permission, rationale) {
27
+ console.warn(REQUEST_PERMISSION_DEPRECATED);
28
+ const response = await PermissionsAndroid.request(permission, rationale);
29
+ return response === RESULTS.GRANTED;
30
+ },
31
+ async request(permission, rationale) {
32
+ const module = requireModule();
33
+ if (rationale) {
34
+ const shouldShowRationale = await module.shouldShowRequestPermissionRationale(permission);
35
+ const dialog = getNativeModule('DialogManagerAndroid');
36
+ if (shouldShowRationale && dialog !== null) {
37
+ return new Promise((resolve, reject) => {
38
+ dialog.showAlert({ ...rationale }, () => reject(new Error('Error showing rationale')), () => resolve(module.requestPermission(permission)));
39
+ });
40
+ }
41
+ }
42
+ return module.requestPermission(permission);
43
+ },
44
+ requestMultiple(permissions) {
45
+ return requireModule().requestMultiplePermissions(permissions);
46
+ },
47
+ };
@@ -1,115 +1 @@
1
- export declare const RESULTS: Readonly<{
2
- readonly GRANTED: "granted";
3
- readonly DENIED: "denied";
4
- readonly NEVER_ASK_AGAIN: "never_ask_again";
5
- }>;
6
- export type IPermissionStatus = (typeof RESULTS)[keyof typeof RESULTS];
7
- export declare const PERMISSIONS: Readonly<{
8
- readonly READ_CALENDAR: "android.permission.READ_CALENDAR";
9
- readonly WRITE_CALENDAR: "android.permission.WRITE_CALENDAR";
10
- readonly CAMERA: "android.permission.CAMERA";
11
- readonly READ_CONTACTS: "android.permission.READ_CONTACTS";
12
- readonly WRITE_CONTACTS: "android.permission.WRITE_CONTACTS";
13
- readonly GET_ACCOUNTS: "android.permission.GET_ACCOUNTS";
14
- readonly ACCESS_FINE_LOCATION: "android.permission.ACCESS_FINE_LOCATION";
15
- readonly ACCESS_COARSE_LOCATION: "android.permission.ACCESS_COARSE_LOCATION";
16
- readonly ACCESS_BACKGROUND_LOCATION: "android.permission.ACCESS_BACKGROUND_LOCATION";
17
- readonly RECORD_AUDIO: "android.permission.RECORD_AUDIO";
18
- readonly READ_PHONE_STATE: "android.permission.READ_PHONE_STATE";
19
- readonly CALL_PHONE: "android.permission.CALL_PHONE";
20
- readonly READ_CALL_LOG: "android.permission.READ_CALL_LOG";
21
- readonly WRITE_CALL_LOG: "android.permission.WRITE_CALL_LOG";
22
- readonly ADD_VOICEMAIL: "com.android.voicemail.permission.ADD_VOICEMAIL";
23
- readonly READ_VOICEMAIL: "com.android.voicemail.permission.READ_VOICEMAIL";
24
- readonly WRITE_VOICEMAIL: "com.android.voicemail.permission.WRITE_VOICEMAIL";
25
- readonly USE_SIP: "android.permission.USE_SIP";
26
- readonly PROCESS_OUTGOING_CALLS: "android.permission.PROCESS_OUTGOING_CALLS";
27
- readonly BODY_SENSORS: "android.permission.BODY_SENSORS";
28
- readonly BODY_SENSORS_BACKGROUND: "android.permission.BODY_SENSORS_BACKGROUND";
29
- readonly SEND_SMS: "android.permission.SEND_SMS";
30
- readonly RECEIVE_SMS: "android.permission.RECEIVE_SMS";
31
- readonly READ_SMS: "android.permission.READ_SMS";
32
- readonly RECEIVE_WAP_PUSH: "android.permission.RECEIVE_WAP_PUSH";
33
- readonly RECEIVE_MMS: "android.permission.RECEIVE_MMS";
34
- readonly READ_EXTERNAL_STORAGE: "android.permission.READ_EXTERNAL_STORAGE";
35
- readonly READ_MEDIA_IMAGES: "android.permission.READ_MEDIA_IMAGES";
36
- readonly READ_MEDIA_VIDEO: "android.permission.READ_MEDIA_VIDEO";
37
- readonly READ_MEDIA_AUDIO: "android.permission.READ_MEDIA_AUDIO";
38
- readonly READ_MEDIA_VISUAL_USER_SELECTED: "android.permission.READ_MEDIA_VISUAL_USER_SELECTED";
39
- readonly WRITE_EXTERNAL_STORAGE: "android.permission.WRITE_EXTERNAL_STORAGE";
40
- readonly BLUETOOTH_CONNECT: "android.permission.BLUETOOTH_CONNECT";
41
- readonly BLUETOOTH_SCAN: "android.permission.BLUETOOTH_SCAN";
42
- readonly BLUETOOTH_ADVERTISE: "android.permission.BLUETOOTH_ADVERTISE";
43
- readonly ACCESS_MEDIA_LOCATION: "android.permission.ACCESS_MEDIA_LOCATION";
44
- readonly ACCEPT_HANDOVER: "android.permission.ACCEPT_HANDOVER";
45
- readonly ACTIVITY_RECOGNITION: "android.permission.ACTIVITY_RECOGNITION";
46
- readonly ANSWER_PHONE_CALLS: "android.permission.ANSWER_PHONE_CALLS";
47
- readonly READ_PHONE_NUMBERS: "android.permission.READ_PHONE_NUMBERS";
48
- readonly UWB_RANGING: "android.permission.UWB_RANGING";
49
- readonly POST_NOTIFICATIONS: "android.permission.POST_NOTIFICATIONS";
50
- readonly NEARBY_WIFI_DEVICES: "android.permission.NEARBY_WIFI_DEVICES";
51
- }>;
52
- export type IPermission = (typeof PERMISSIONS)[keyof typeof PERMISSIONS];
53
- export type IRationale = {
54
- title: string;
55
- message: string;
56
- buttonPositive?: string;
57
- buttonNegative?: string;
58
- buttonNeutral?: string;
59
- };
60
- export declare const PermissionsAndroid: {
61
- PERMISSIONS: Readonly<{
62
- readonly READ_CALENDAR: "android.permission.READ_CALENDAR";
63
- readonly WRITE_CALENDAR: "android.permission.WRITE_CALENDAR";
64
- readonly CAMERA: "android.permission.CAMERA";
65
- readonly READ_CONTACTS: "android.permission.READ_CONTACTS";
66
- readonly WRITE_CONTACTS: "android.permission.WRITE_CONTACTS";
67
- readonly GET_ACCOUNTS: "android.permission.GET_ACCOUNTS";
68
- readonly ACCESS_FINE_LOCATION: "android.permission.ACCESS_FINE_LOCATION";
69
- readonly ACCESS_COARSE_LOCATION: "android.permission.ACCESS_COARSE_LOCATION";
70
- readonly ACCESS_BACKGROUND_LOCATION: "android.permission.ACCESS_BACKGROUND_LOCATION";
71
- readonly RECORD_AUDIO: "android.permission.RECORD_AUDIO";
72
- readonly READ_PHONE_STATE: "android.permission.READ_PHONE_STATE";
73
- readonly CALL_PHONE: "android.permission.CALL_PHONE";
74
- readonly READ_CALL_LOG: "android.permission.READ_CALL_LOG";
75
- readonly WRITE_CALL_LOG: "android.permission.WRITE_CALL_LOG";
76
- readonly ADD_VOICEMAIL: "com.android.voicemail.permission.ADD_VOICEMAIL";
77
- readonly READ_VOICEMAIL: "com.android.voicemail.permission.READ_VOICEMAIL";
78
- readonly WRITE_VOICEMAIL: "com.android.voicemail.permission.WRITE_VOICEMAIL";
79
- readonly USE_SIP: "android.permission.USE_SIP";
80
- readonly PROCESS_OUTGOING_CALLS: "android.permission.PROCESS_OUTGOING_CALLS";
81
- readonly BODY_SENSORS: "android.permission.BODY_SENSORS";
82
- readonly BODY_SENSORS_BACKGROUND: "android.permission.BODY_SENSORS_BACKGROUND";
83
- readonly SEND_SMS: "android.permission.SEND_SMS";
84
- readonly RECEIVE_SMS: "android.permission.RECEIVE_SMS";
85
- readonly READ_SMS: "android.permission.READ_SMS";
86
- readonly RECEIVE_WAP_PUSH: "android.permission.RECEIVE_WAP_PUSH";
87
- readonly RECEIVE_MMS: "android.permission.RECEIVE_MMS";
88
- readonly READ_EXTERNAL_STORAGE: "android.permission.READ_EXTERNAL_STORAGE";
89
- readonly READ_MEDIA_IMAGES: "android.permission.READ_MEDIA_IMAGES";
90
- readonly READ_MEDIA_VIDEO: "android.permission.READ_MEDIA_VIDEO";
91
- readonly READ_MEDIA_AUDIO: "android.permission.READ_MEDIA_AUDIO";
92
- readonly READ_MEDIA_VISUAL_USER_SELECTED: "android.permission.READ_MEDIA_VISUAL_USER_SELECTED";
93
- readonly WRITE_EXTERNAL_STORAGE: "android.permission.WRITE_EXTERNAL_STORAGE";
94
- readonly BLUETOOTH_CONNECT: "android.permission.BLUETOOTH_CONNECT";
95
- readonly BLUETOOTH_SCAN: "android.permission.BLUETOOTH_SCAN";
96
- readonly BLUETOOTH_ADVERTISE: "android.permission.BLUETOOTH_ADVERTISE";
97
- readonly ACCESS_MEDIA_LOCATION: "android.permission.ACCESS_MEDIA_LOCATION";
98
- readonly ACCEPT_HANDOVER: "android.permission.ACCEPT_HANDOVER";
99
- readonly ACTIVITY_RECOGNITION: "android.permission.ACTIVITY_RECOGNITION";
100
- readonly ANSWER_PHONE_CALLS: "android.permission.ANSWER_PHONE_CALLS";
101
- readonly READ_PHONE_NUMBERS: "android.permission.READ_PHONE_NUMBERS";
102
- readonly UWB_RANGING: "android.permission.UWB_RANGING";
103
- readonly POST_NOTIFICATIONS: "android.permission.POST_NOTIFICATIONS";
104
- readonly NEARBY_WIFI_DEVICES: "android.permission.NEARBY_WIFI_DEVICES";
105
- }>;
106
- RESULTS: Readonly<{
107
- readonly GRANTED: "granted";
108
- readonly DENIED: "denied";
109
- readonly NEVER_ASK_AGAIN: "never_ask_again";
110
- }>;
111
- check(permission: IPermission): Promise<boolean>;
112
- request(permission: IPermission, rationale?: IRationale): Promise<IPermissionStatus>;
113
- requestMultiple(permissions: IPermission[]): Promise<Record<string, IPermissionStatus>>;
114
- shouldShowRequestPermissionRationale(permission: IPermission): Promise<boolean>;
115
- };
1
+ export * from './index.ios';
@@ -0,0 +1,59 @@
1
+ import { type IPermission, type IPermissionStatus, type IRationale } from './shared';
2
+ export { PERMISSIONS, RESULTS, type IPermission, type IPermissionStatus, type IRationale, } from './shared';
3
+ export declare const PermissionsAndroid: {
4
+ PERMISSIONS: Readonly<{
5
+ readonly READ_CALENDAR: "android.permission.READ_CALENDAR";
6
+ readonly WRITE_CALENDAR: "android.permission.WRITE_CALENDAR";
7
+ readonly CAMERA: "android.permission.CAMERA";
8
+ readonly READ_CONTACTS: "android.permission.READ_CONTACTS";
9
+ readonly WRITE_CONTACTS: "android.permission.WRITE_CONTACTS";
10
+ readonly GET_ACCOUNTS: "android.permission.GET_ACCOUNTS";
11
+ readonly ACCESS_FINE_LOCATION: "android.permission.ACCESS_FINE_LOCATION";
12
+ readonly ACCESS_COARSE_LOCATION: "android.permission.ACCESS_COARSE_LOCATION";
13
+ readonly ACCESS_BACKGROUND_LOCATION: "android.permission.ACCESS_BACKGROUND_LOCATION";
14
+ readonly RECORD_AUDIO: "android.permission.RECORD_AUDIO";
15
+ readonly READ_PHONE_STATE: "android.permission.READ_PHONE_STATE";
16
+ readonly CALL_PHONE: "android.permission.CALL_PHONE";
17
+ readonly READ_CALL_LOG: "android.permission.READ_CALL_LOG";
18
+ readonly WRITE_CALL_LOG: "android.permission.WRITE_CALL_LOG";
19
+ readonly ADD_VOICEMAIL: "com.android.voicemail.permission.ADD_VOICEMAIL";
20
+ readonly READ_VOICEMAIL: "com.android.voicemail.permission.READ_VOICEMAIL";
21
+ readonly WRITE_VOICEMAIL: "com.android.voicemail.permission.WRITE_VOICEMAIL";
22
+ readonly USE_SIP: "android.permission.USE_SIP";
23
+ readonly PROCESS_OUTGOING_CALLS: "android.permission.PROCESS_OUTGOING_CALLS";
24
+ readonly BODY_SENSORS: "android.permission.BODY_SENSORS";
25
+ readonly BODY_SENSORS_BACKGROUND: "android.permission.BODY_SENSORS_BACKGROUND";
26
+ readonly SEND_SMS: "android.permission.SEND_SMS";
27
+ readonly RECEIVE_SMS: "android.permission.RECEIVE_SMS";
28
+ readonly READ_SMS: "android.permission.READ_SMS";
29
+ readonly RECEIVE_WAP_PUSH: "android.permission.RECEIVE_WAP_PUSH";
30
+ readonly RECEIVE_MMS: "android.permission.RECEIVE_MMS";
31
+ readonly READ_EXTERNAL_STORAGE: "android.permission.READ_EXTERNAL_STORAGE";
32
+ readonly READ_MEDIA_IMAGES: "android.permission.READ_MEDIA_IMAGES";
33
+ readonly READ_MEDIA_VIDEO: "android.permission.READ_MEDIA_VIDEO";
34
+ readonly READ_MEDIA_AUDIO: "android.permission.READ_MEDIA_AUDIO";
35
+ readonly READ_MEDIA_VISUAL_USER_SELECTED: "android.permission.READ_MEDIA_VISUAL_USER_SELECTED";
36
+ readonly WRITE_EXTERNAL_STORAGE: "android.permission.WRITE_EXTERNAL_STORAGE";
37
+ readonly BLUETOOTH_CONNECT: "android.permission.BLUETOOTH_CONNECT";
38
+ readonly BLUETOOTH_SCAN: "android.permission.BLUETOOTH_SCAN";
39
+ readonly BLUETOOTH_ADVERTISE: "android.permission.BLUETOOTH_ADVERTISE";
40
+ readonly ACCESS_MEDIA_LOCATION: "android.permission.ACCESS_MEDIA_LOCATION";
41
+ readonly ACCEPT_HANDOVER: "android.permission.ACCEPT_HANDOVER";
42
+ readonly ACTIVITY_RECOGNITION: "android.permission.ACTIVITY_RECOGNITION";
43
+ readonly ANSWER_PHONE_CALLS: "android.permission.ANSWER_PHONE_CALLS";
44
+ readonly READ_PHONE_NUMBERS: "android.permission.READ_PHONE_NUMBERS";
45
+ readonly UWB_RANGING: "android.permission.UWB_RANGING";
46
+ readonly POST_NOTIFICATIONS: "android.permission.POST_NOTIFICATIONS";
47
+ readonly NEARBY_WIFI_DEVICES: "android.permission.NEARBY_WIFI_DEVICES";
48
+ }>;
49
+ RESULTS: Readonly<{
50
+ readonly GRANTED: "granted";
51
+ readonly DENIED: "denied";
52
+ readonly NEVER_ASK_AGAIN: "never_ask_again";
53
+ }>;
54
+ checkPermission(_permission: IPermission): Promise<boolean>;
55
+ check(_permission: IPermission): Promise<boolean>;
56
+ requestPermission(_permission: IPermission, _rationale?: IRationale): Promise<boolean>;
57
+ request(_permission: IPermission, _rationale?: IRationale): Promise<IPermissionStatus>;
58
+ requestMultiple(_permissions: IPermission[]): Promise<Record<string, IPermissionStatus>>;
59
+ };