@livx.cc/appwrap 0.26.0 → 0.28.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/package.json +1 -1
  2. package/runtime/App_Resources/Android/src/main/AndroidManifest.xml +3 -0
  3. package/runtime/app/app.ts +57 -39
  4. package/runtime/app/shell/android-helpers.ts +6 -6
  5. package/runtime/app/shell/background-bootstrap.generated.ts +8 -0
  6. package/runtime/app/shell/background-context.ts +20 -0
  7. package/runtime/app/shell/banner.ts +9 -4
  8. package/runtime/app/shell/bridge.ts +2 -0
  9. package/runtime/app/shell/capabilities.manifest.ts +22 -3
  10. package/runtime/app/shell/custom-webview.android.ts +50 -23
  11. package/runtime/app/shell/custom-webview.ios.ts +90 -78
  12. package/runtime/app/shell/devmenu.ts +2 -2
  13. package/runtime/app/shell/env.ts +1 -3
  14. package/runtime/app/shell/events.ts +25 -4
  15. package/runtime/app/shell/fcm-firebase.d.ts +2 -1
  16. package/runtime/app/shell/handlers-android.ts +16 -12
  17. package/runtime/app/shell/handlers-background.ts +290 -0
  18. package/runtime/app/shell/handlers-billing.ts +90 -82
  19. package/runtime/app/shell/handlers-extended.ts +141 -110
  20. package/runtime/app/shell/handlers-fs.ts +53 -41
  21. package/runtime/app/shell/handlers-health.ts +27 -19
  22. package/runtime/app/shell/handlers-media.ts +8 -14
  23. package/runtime/app/shell/handlers-oauth.ts +19 -19
  24. package/runtime/app/shell/handlers-parity.ts +118 -89
  25. package/runtime/app/shell/handlers-push.ts +5 -15
  26. package/runtime/app/shell/handlers-reviews.ts +5 -5
  27. package/runtime/app/shell/handlers-scanner.ts +36 -38
  28. package/runtime/app/shell/handlers-speech.ts +43 -49
  29. package/runtime/app/shell/handlers-system.ts +122 -1
  30. package/runtime/app/shell/handlers.ts +10 -3
  31. package/runtime/app/shell/ios-frameworks.d.ts +9 -44
  32. package/runtime/app/shell/ios-ui-delegate.ts +112 -108
  33. package/runtime/app/shell/native-log.ts +1 -3
  34. package/runtime/app/shell/status-bar.ts +7 -4
  35. package/runtime/app/shell/toast.ts +1 -1
  36. package/runtime/references.d.ts +21 -0
  37. package/runtime/tsconfig.json +1 -0
  38. package/src/cli.ts +74 -18
  39. package/src/config.ts +20 -0
  40. package/src/derive.ts +75 -0
@@ -2,23 +2,25 @@ import { Utils, isAndroid, isIOS } from '@nativescript/core';
2
2
  import { bridge } from './bridge';
3
3
  import { requestPermissions } from './android-helpers';
4
4
 
5
- // AVFoundation / Speech globals (NS auto-links the frameworks via metadata when referenced).
6
- declare const AVSpeechSynthesizer: any;
7
- declare const AVSpeechUtterance: any;
8
- declare const AVSpeechSynthesisVoice: any;
9
- declare const AVAudioEngine: any;
10
- declare const AVAudioSession: any;
11
- declare const AVAudioSessionCategoryPlayAndRecord: any;
12
- declare const AVAudioSessionModeMeasurement: any;
13
- declare const SFSpeechRecognizer: any;
14
- declare const SFSpeechAudioBufferRecognitionRequest: any;
15
- declare const SFSpeechRecognizerAuthorizationStatusAuthorized: any;
16
- declare const NSLocale: any;
17
- declare const android: any;
18
- declare const java: any;
19
-
20
5
  const err = (code: string, message: string) => Object.assign(new Error(message), { code });
21
6
 
7
+ // iOS speech-synthesizer delegate — both finish & cancel settle the pending speak (the JS
8
+ // `speakPending` is captured via the `onSettle` instance field, assigned after `new()`).
9
+ @NativeClass()
10
+ class SpeakDelegate extends NSObject implements AVSpeechSynthesizerDelegate {
11
+ static ObjCProtocols = [AVSpeechSynthesizerDelegate];
12
+ onSettle?: () => void;
13
+ static new(): SpeakDelegate {
14
+ return <SpeakDelegate>super.new();
15
+ }
16
+ speechSynthesizerDidFinishSpeechUtterance(_s: AVSpeechSynthesizer, _u: AVSpeechUtterance): void {
17
+ this.onSettle?.();
18
+ }
19
+ speechSynthesizerDidCancelSpeechUtterance(_s: AVSpeechSynthesizer, _u: AVSpeechUtterance): void {
20
+ this.onSettle?.();
21
+ }
22
+ }
23
+
22
24
  /**
23
25
  * Voice I/O. TTS: iOS AVSpeechSynthesizer / Android TextToSpeech — speak resolves on finish.
24
26
  * STT: iOS SFSpeechRecognizer + AVAudioEngine mic tap / Android SpeechRecognizer — listen resolves
@@ -37,15 +39,15 @@ export function registerSpeechHandlers(): void {
37
39
  function registerIos(): void {
38
40
  // Strong refs while a session is live — ARC would otherwise free the synthesizer/engine/delegate
39
41
  // the moment the JS locals fall out of scope (same hazard as handlers-scanner/oauth).
40
- let synth: any = null;
41
- let speakDelegate: any = null;
42
+ let synth: AVSpeechSynthesizer | null = null;
43
+ let speakDelegate: SpeakDelegate | null = null;
42
44
  let speakPending: { resolve: () => void } | null = null;
43
45
 
44
- let engine: any = null;
45
- let recognizer: any = null;
46
- let request: any = null;
47
- let task: any = null;
48
- let listenPending: { resolve: (v: string) => void; reject: (e: any) => void } | null = null;
46
+ let engine: AVAudioEngine | null = null;
47
+ let recognizer: SFSpeechRecognizer | null = null;
48
+ let request: SFSpeechAudioBufferRecognitionRequest | null = null;
49
+ let task: SFSpeechRecognitionTask | null = null;
50
+ let listenPending: { resolve: (v: string) => void; reject: (e: Error) => void } | null = null;
49
51
  let best = '';
50
52
 
51
53
  const teardownListen = () => {
@@ -71,18 +73,8 @@ function registerIos(): void {
71
73
  try {
72
74
  if (!synth) {
73
75
  synth = AVSpeechSynthesizer.new();
74
- const DelegateClass = (NSObject as any).extend(
75
- {
76
- speechSynthesizerDidFinishSpeechUtterance(_s: any, _u: any) {
77
- const p = speakPending; speakPending = null; p?.resolve();
78
- },
79
- speechSynthesizerDidCancelSpeechUtterance(_s: any, _u: any) {
80
- const p = speakPending; speakPending = null; p?.resolve();
81
- },
82
- },
83
- { protocols: [] }
84
- );
85
- speakDelegate = DelegateClass.new();
76
+ speakDelegate = SpeakDelegate.new();
77
+ speakDelegate.onSettle = () => { const p = speakPending; speakPending = null; p?.resolve(); };
86
78
  synth.delegate = speakDelegate;
87
79
  }
88
80
  // A new speak resolves any prior pending (its utterance is being superseded).
@@ -136,8 +128,8 @@ function registerIos(): void {
136
128
  best = '';
137
129
  listenPending = { resolve, reject };
138
130
 
139
- SFSpeechRecognizer.requestAuthorization((status: any) => {
140
- if (status !== SFSpeechRecognizerAuthorizationStatusAuthorized) {
131
+ SFSpeechRecognizer.requestAuthorization((status: SFSpeechRecognizerAuthorizationStatus) => {
132
+ if (status !== SFSpeechRecognizerAuthorizationStatus.Authorized) {
141
133
  const p = listenPending; listenPending = null;
142
134
  p?.reject(err('DENIED', 'Speech recognition not authorized'));
143
135
  return;
@@ -167,17 +159,18 @@ function registerIos(): void {
167
159
 
168
160
  const session = AVAudioSession.sharedInstance();
169
161
  session.setCategoryModeOptionsError(AVAudioSessionCategoryPlayAndRecord, AVAudioSessionModeMeasurement, 1 /* duckOthers */, null);
170
- session.setActiveWithOptionsError(true, 0, null);
162
+ // 0 = no activation flags; not a named member of the options enum, so cast (preserve value).
163
+ session.setActiveWithOptionsError(true, 0 as AVAudioSessionSetActiveOptions, null);
171
164
 
172
165
  engine = AVAudioEngine.new();
173
166
  request = SFSpeechAudioBufferRecognitionRequest.new();
174
167
  request.shouldReportPartialResults = !!params?.partial;
175
168
 
176
- task = recognizer.recognitionTaskWithRequestResultHandler(request, (result: any, error: any) => {
169
+ task = recognizer.recognitionTaskWithRequestResultHandler(request, (result: SFSpeechRecognitionResult, error: NSError) => {
177
170
  if (result) {
178
171
  best = String(result.bestTranscription?.formattedString ?? best);
179
- if (params?.partial && !result.isFinal) bridge.emit('speech.partial', { transcript: best });
180
- if (result.isFinal && listenPending) settleListen();
172
+ if (params?.partial && !result.final) bridge.emit('speech.partial', { transcript: best });
173
+ if (result.final && listenPending) settleListen();
181
174
  }
182
175
  if (error && listenPending) {
183
176
  const p = listenPending; listenPending = null;
@@ -188,7 +181,7 @@ function registerIos(): void {
188
181
 
189
182
  const input = engine.inputNode;
190
183
  const format = input.outputFormatForBus(0);
191
- input.installTapOnBusBufferSizeFormatBlock(0, 1024, format, (buffer: any, _when: any) => {
184
+ input.installTapOnBusBufferSizeFormatBlock(0, 1024, format, (buffer: AVAudioPCMBuffer, _when: AVAudioTime) => {
192
185
  request?.appendAudioPCMBuffer(buffer);
193
186
  });
194
187
  engine.prepare();
@@ -206,11 +199,11 @@ function registerIos(): void {
206
199
 
207
200
  function registerAndroid(): void {
208
201
  // TextToSpeech + SpeechRecognizer are plain Java → direct interop, no kotlin flag, no gradle dep.
209
- let tts: any = null;
202
+ let tts: android.speech.tts.TextToSpeech | null = null;
210
203
  let ttsReady = false;
211
204
  const ctx = () => Utils.android.getApplicationContext();
212
205
 
213
- const ensureTts = (): Promise<any> =>
206
+ const ensureTts = (): Promise<android.speech.tts.TextToSpeech | null> =>
214
207
  new Promise((resolve) => {
215
208
  if (tts && ttsReady) { resolve(tts); return; }
216
209
  tts = new android.speech.tts.TextToSpeech(
@@ -236,7 +229,8 @@ function registerAndroid(): void {
236
229
  const id = 'u' + Date.now();
237
230
  return new Promise<void>((resolve) => {
238
231
  engine.setOnUtteranceProgressListener(
239
- new android.speech.tts.UtteranceProgressListener({
232
+ // NS interop: subclass the abstract Java class via object-literal ctor (untypeable — cast base).
233
+ new (android.speech.tts.UtteranceProgressListener as any)({
240
234
  onStart() {},
241
235
  onDone() { resolve(); },
242
236
  onError() { resolve(); }, // resolve regardless — speak() promises completion, not success
@@ -264,8 +258,8 @@ function registerAndroid(): void {
264
258
  });
265
259
 
266
260
  // STT — SpeechRecognizer must be created + driven on the main (UI) thread.
267
- let recognizer: any = null;
268
- let listenPending: { resolve: (v: string) => void; reject: (e: any) => void } | null = null;
261
+ let recognizer: android.speech.SpeechRecognizer | null = null;
262
+ let listenPending: { resolve: (v: string) => void; reject: (e: Error) => void } | null = null;
269
263
  let best = '';
270
264
 
271
265
  const settle = () => {
@@ -295,7 +289,7 @@ function registerAndroid(): void {
295
289
  return;
296
290
  }
297
291
  recognizer = android.speech.SpeechRecognizer.createSpeechRecognizer(ctx());
298
- const pickText = (b: any): string => {
292
+ const pickText = (b: android.os.Bundle): string => {
299
293
  const arr = b?.getStringArrayList(android.speech.SpeechRecognizer.RESULTS_RECOGNITION);
300
294
  return arr && arr.size() ? String(arr.get(0)) : '';
301
295
  };
@@ -303,12 +297,12 @@ function registerAndroid(): void {
303
297
  new android.speech.RecognitionListener({
304
298
  onReadyForSpeech() {}, onBeginningOfSpeech() {}, onRmsChanged() {},
305
299
  onBufferReceived() {}, onEndOfSpeech() {}, onEvent() {},
306
- onPartialResults(b: any) {
300
+ onPartialResults(b: android.os.Bundle) {
307
301
  if (!params?.partial) return;
308
302
  const t = pickText(b);
309
303
  if (t) { best = t; bridge.emit('speech.partial', { transcript: t }); }
310
304
  },
311
- onResults(b: any) {
305
+ onResults(b: android.os.Bundle) {
312
306
  const t = pickText(b);
313
307
  if (t) best = t;
314
308
  if (listenPending) settle();
@@ -1,7 +1,7 @@
1
1
  import { Application, ApplicationSettings, Color, Utils, isAndroid, isIOS } from '@nativescript/core';
2
2
  import { bridge } from './bridge';
3
3
 
4
- declare const android: any, androidx: any, java: any;
4
+ declare const androidx: any; // no NS types: androidx
5
5
 
6
6
  const err = (code: string, message: string) => Object.assign(new Error(message), { code });
7
7
 
@@ -119,6 +119,70 @@ export function registerSystemHandlers(): void {
119
119
 
120
120
  bridge.register('app.environment', () => appEnvironment());
121
121
 
122
+ // Probe whether the OS can open a URL. Custom schemes need declaring up-front (iOS
123
+ // LSApplicationQueriesSchemes via appwrap.json.queryUrlSchemes; Android <queries> via
124
+ // queryPackages) — common schemes (http/https/tel/mailto/sms) resolve without declaration.
125
+ bridge.register('app.canOpenUrl', ({ url }: { url: string }) => {
126
+ const target = String(url ?? '');
127
+ if (!target) return false;
128
+ if (isIOS) {
129
+ const nsUrl = NSURL.URLWithString(target);
130
+ return !!nsUrl && UIApplication.sharedApplication.canOpenURL(nsUrl);
131
+ }
132
+ if (isAndroid) {
133
+ const pm = Utils.android.getApplicationContext().getPackageManager();
134
+ const intent = new android.content.Intent(
135
+ android.content.Intent.ACTION_VIEW,
136
+ android.net.Uri.parse(target)
137
+ );
138
+ return pm.resolveActivity(intent, 0) != null;
139
+ }
140
+ return false;
141
+ });
142
+
143
+ // Home-screen long-press quick actions. iOS: UIApplication.shortcutItems. Android: dynamic
144
+ // ShortcutManager shortcuts (API 25+; no-op below) whose launch intent carries appwrap_shortcut=<id>.
145
+ bridge.register('app.setShortcuts', ({ items }: { items: Array<{ id: string; title: string; subtitle?: string }> }) => {
146
+ const list = (items ?? []).filter((i) => i && i.id && i.title);
147
+ if (isIOS) {
148
+ Utils.dispatchToMainThread(() => {
149
+ const arr = NSMutableArray.alloc().init();
150
+ for (const i of list) {
151
+ const item = UIApplicationShortcutItem.alloc().initWithTypeLocalizedTitleLocalizedSubtitleIconUserInfo(
152
+ String(i.id), String(i.title), i.subtitle ? String(i.subtitle) : null, null, null
153
+ );
154
+ arr.addObject(item);
155
+ }
156
+ UIApplication.sharedApplication.shortcutItems = arr as any;
157
+ });
158
+ } else if (isAndroid) {
159
+ if (android.os.Build.VERSION.SDK_INT < 25) return; // ShortcutManager is API 25+
160
+ const ctx = Utils.android.getApplicationContext();
161
+ const sm = ctx.getSystemService(android.content.Context.SHORTCUT_SERVICE); // API 25+ string const
162
+ if (!sm) return;
163
+ const launchClass = (Application.android.foregroundActivity ?? Application.android.startActivity)?.getClass();
164
+ const shortcuts = new java.util.ArrayList();
165
+ for (const i of list) {
166
+ const intent = new android.content.Intent(android.content.Intent.ACTION_VIEW);
167
+ if (launchClass) intent.setClassName(ctx, launchClass.getName());
168
+ intent.putExtra('appwrap_shortcut', String(i.id));
169
+ intent.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK | android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP);
170
+ const b = new android.content.pm.ShortcutInfo.Builder(ctx, String(i.id))
171
+ .setShortLabel(String(i.title))
172
+ .setLongLabel(String(i.subtitle ?? i.title))
173
+ .setIntent(intent);
174
+ shortcuts.add(b.build());
175
+ }
176
+ sm.setDynamicShortcuts(shortcuts);
177
+ }
178
+ });
179
+
180
+ // Privacy screen — hide content in the app-switcher / block screenshots. iOS: cover the key window
181
+ // with a blur while inactive/backgrounded (wired via the app lifecycle below). Android: FLAG_SECURE.
182
+ bridge.register('screen.setPrivacy', ({ enabled }: { enabled: boolean }) => {
183
+ setPrivacyScreen(!!enabled);
184
+ });
185
+
122
186
  bridge.register('browser.open', ({ url, toolbarColor }: { url: string; toolbarColor?: string }) => {
123
187
  const target = String(url ?? '');
124
188
  if (!target) throw err('NATIVE_ERROR', 'browser.open: empty url');
@@ -139,3 +203,60 @@ export function registerSystemHandlers(): void {
139
203
  }
140
204
  });
141
205
  }
206
+
207
+ // ── privacy screen ────────────────────────────────────────────────────
208
+ // iOS keeps the enabled flag + lazily wires the lifecycle hooks once. The cover MUST go on at
209
+ // `willResignActive` — that fires BEFORE the app-switcher snapshot, whereas NS's suspendEvent
210
+ // (≈ didEnterBackground) fires AFTER it, so suspendEvent alone would leak the first snapshot. We
211
+ // observe the raw UIApplicationWillResignActiveNotification for the cover, removing it on
212
+ // becomeActive. Android flips FLAG_SECURE immediately (also blocks screenshots).
213
+ let privacyEnabled = false;
214
+ let privacyCover: UIView | null = null;
215
+ let privacyLifecycleWired = false;
216
+
217
+ function setPrivacyScreen(enabled: boolean): void {
218
+ privacyEnabled = enabled;
219
+ if (isIOS) {
220
+ wireIosPrivacyLifecycle();
221
+ if (!enabled) Utils.dispatchToMainThread(removeIosPrivacyCover); // disabling mid-foreground
222
+ } else if (isAndroid) {
223
+ Utils.dispatchToMainThread(() => {
224
+ const activity = Application.android.foregroundActivity ?? Application.android.startActivity;
225
+ const FLAG_SECURE = android.view.WindowManager.LayoutParams.FLAG_SECURE;
226
+ const window = activity?.getWindow();
227
+ if (!window) return;
228
+ if (enabled) window.setFlags(FLAG_SECURE, FLAG_SECURE);
229
+ else window.clearFlags(FLAG_SECURE);
230
+ });
231
+ }
232
+ }
233
+
234
+ function wireIosPrivacyLifecycle(): void {
235
+ if (privacyLifecycleWired) return;
236
+ privacyLifecycleWired = true;
237
+ const center = NSNotificationCenter.defaultCenter;
238
+ // willResignActive → cover BEFORE the OS captures the app-switcher snapshot (the critical timing).
239
+ center.addObserverForNameObjectQueueUsingBlock(
240
+ UIApplicationWillResignActiveNotification, null, null, () => { if (privacyEnabled) addIosPrivacyCover(); }
241
+ );
242
+ // didBecomeActive → reveal again once the user returns.
243
+ center.addObserverForNameObjectQueueUsingBlock(
244
+ UIApplicationDidBecomeActiveNotification, null, null, () => removeIosPrivacyCover()
245
+ );
246
+ }
247
+
248
+ function addIosPrivacyCover(): void {
249
+ const window = UIApplication.sharedApplication.keyWindow;
250
+ if (!window || privacyCover) return;
251
+ const effect = UIBlurEffect.effectWithStyle(UIBlurEffectStyle.SystemMaterial);
252
+ const blur = UIVisualEffectView.alloc().initWithEffect(effect);
253
+ blur.frame = window.bounds;
254
+ blur.autoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight;
255
+ window.addSubview(blur);
256
+ privacyCover = blur;
257
+ }
258
+
259
+ function removeIosPrivacyCover(): void {
260
+ privacyCover?.removeFromSuperview();
261
+ privacyCover = null;
262
+ }
@@ -7,6 +7,7 @@ import { showBanner, dismissBanner } from './banner';
7
7
  import { setStatusBarStyle } from './status-bar';
8
8
  import { buildCapabilityMap } from './capabilities.manifest';
9
9
  import { ACTIVE_MODULE_NAMES } from './active-modules.generated';
10
+ import { consumePendingBackgroundTaskId } from './background-context';
10
11
 
11
12
  /** Build identifier for the native shell bundle — bump per deploy to spot stale bundles. */
12
13
  export const SHELL_BUILD = 'updates-devmenu-3';
@@ -27,12 +28,17 @@ export function registerHandlers(): void {
27
28
  // kit degrades gracefully. Push is special — gated by per-platform build config, not the manifest.
28
29
  const capabilities = buildCapabilityMap(new Set(ACTIVE_MODULE_NAMES), isIOS ? 'ios' : 'android') as Record<string, 'native' | 'none'>;
29
30
  capabilities.push = (isIOS ? SHELL_CONFIG.pushIos : SHELL_CONFIG.pushAndroid) ? 'native' : 'none';
31
+ // Background launch: the headless runner (handlers-background) set the wake id before loading this
32
+ // (offscreen) WebView. Report it so `kit.backgroundTask` dispatches the registered handler. Consumed
33
+ // (read-once) so a later foreground handshake in the same process never re-reports a stale wake.
34
+ const backgroundTaskId = consumePendingBackgroundTaskId();
30
35
  return {
31
36
  protocol: 1,
32
37
  platform: isIOS ? 'ios' : 'android',
33
38
  app: { id: SHELL_CONFIG.appId, name: SHELL_CONFIG.name, version: SHELL_CONFIG.version, build: SHELL_BUILD, loader: SHELL_CONFIG.loader },
34
39
  debug: { lastNotifTap: safeJson(ApplicationSettings.getString('kit:__notifTap', '')) },
35
40
  capabilities,
41
+ ...(backgroundTaskId ? { backgroundTaskId } : {}),
36
42
  };
37
43
  });
38
44
 
@@ -74,7 +80,7 @@ export function registerHandlers(): void {
74
80
  const items = NSMutableArray.new();
75
81
  if (content) items.addObject(content);
76
82
  const controller = UIActivityViewController.alloc().initWithActivityItemsApplicationActivities(
77
- items as any,
83
+ items,
78
84
  null
79
85
  );
80
86
  const rootVC = Utils.ios.getRootViewController();
@@ -102,6 +108,7 @@ export function registerHandlers(): void {
102
108
  if (text) items.addObject(text);
103
109
  const tmp = NSTemporaryDirectory();
104
110
  (files ?? []).forEach((f, i) => {
111
+ // 0 = no decoding options; the enum has no zero member, so cast the raw bitmask value.
105
112
  const data = NSData.alloc().initWithBase64EncodedStringOptions(f.base64, 0 as unknown as NSDataBase64DecodingOptions);
106
113
  if (!data) return;
107
114
  const filePath = tmp + i + '-' + (f.name || 'file'); // index-prefix: avoid same-name collisions in one call
@@ -109,7 +116,7 @@ export function registerHandlers(): void {
109
116
  items.addObject(NSURL.fileURLWithPath(filePath));
110
117
  });
111
118
  const controller = UIActivityViewController.alloc().initWithActivityItemsApplicationActivities(
112
- items as any,
119
+ items,
113
120
  null
114
121
  );
115
122
  const rootVC = Utils.ios.getRootViewController();
@@ -181,6 +188,6 @@ function safeJson(s: string): unknown {
181
188
 
182
189
  function vibrateAndroid(ms: number): void {
183
190
  const context = Utils.android.getApplicationContext();
184
- const vibrator = context.getSystemService(android.content.Context.VIBRATOR_SERVICE);
191
+ const vibrator = context.getSystemService(android.content.Context.VIBRATOR_SERVICE) as android.os.Vibrator;
185
192
  vibrator?.vibrate(ms);
186
193
  }
@@ -1,44 +1,9 @@
1
- /**
2
- * Ambient declarations for iOS frameworks not covered by the default
3
- * @nativescript/types-ios reference set (LocalAuthentication, CoreLocation,
4
- * PhotosUI). Marshalled at runtime by the NativeScript iOS runtime regardless.
5
- */
6
- declare const LAContext: any;
7
- declare const LAPolicy: any;
8
- declare const LABiometryType: any;
9
- declare const CLLocationManager: any;
10
- declare const CLAuthorizationStatus: any;
11
- declare const CLLocationManagerDelegate: any;
12
- declare const kCLLocationAccuracyHundredMeters: number;
13
- declare type CLLocation = any;
14
- declare type CLLocationManager = any;
15
- declare const PHPickerConfiguration: any;
16
- declare const PHPickerViewController: any;
17
- declare const PHPickerViewControllerDelegate: any;
18
- declare type PHPickerResult = any;
19
- declare type PHPickerViewController = any;
20
- // StoreKit / CoreMotion / ContactsUI / EventKit (parity handlers)
21
- declare const SKStoreReviewController: any;
22
- declare const CMMotionManager: any;
23
- declare const CMPedometer: any;
24
- // HealthKit (health/steps module)
25
- declare const HKHealthStore: any;
26
- declare const HKObjectType: any;
27
- declare const HKQuery: any;
28
- declare const HKStatisticsQuery: any;
29
- declare const HKStatisticsOptions: any;
30
- declare const HKUnit: any;
31
- declare const CNContactPickerViewController: any;
32
- declare const CNContactPickerDelegate: any;
33
- // Contacts framework (contacts.getAll bulk read — CNContactStore, vs the ContactsUI picker above)
34
- declare const CNContactStore: any;
35
- declare const CNEntityType: any;
36
- declare const CNContactFetchRequest: any;
37
- declare const CNContactGivenNameKey: any;
38
- declare const CNContactFamilyNameKey: any;
39
- declare const CNContactPhoneNumbersKey: any;
40
- declare const CNContactEmailAddressesKey: any;
41
- declare const EKEventStore: any;
42
- declare const EKEvent: any;
43
- declare const EKSpan: any;
44
- declare const EKEntityType: any;
1
+ // Neutralizer stub — intentionally EMPTY. Do NOT add `declare const X: any` here.
2
+ //
3
+ // Real iOS framework types come from the @nativescript/types-ios references in `references.d.ts`.
4
+ // This file exists ONLY so `appwrap sync` copies it over `native/app/shell/ios-frameworks.d.ts`,
5
+ // overwriting a stale PRE-`references.d.ts` generated relic of the same name. That relic declared
6
+ // every framework symbol as `declare const X: any` (a VALUE); once handlers annotate params with the
7
+ // real types (e.g. `mm: CMMotionManager`), the value-shadow makes the build throw TS2749 ("refers to
8
+ // a value, used as a type"). Shipping this empty stub self-heals any native/ on the next sync.
9
+ export {};