expo-invoke 1.3.0 → 2.1.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 (123) hide show
  1. package/android/src/main/java/expo/modules/invoke/InteractiveWidgetReceiver.kt +47 -0
  2. package/android/src/main/java/expo/modules/invoke/InvokeSliceProvider.kt +83 -0
  3. package/android/src/main/java/expo/modules/invoke/ShortcutsHelper.kt +38 -32
  4. package/build/plugin/android/withAndroidInvoke.d.ts +3 -1
  5. package/build/plugin/android/withAndroidInvoke.js +71 -15
  6. package/build/plugin/android/withAndroidInvoke.js.map +1 -1
  7. package/build/plugin/codegen/generateSwiftAppClip.d.ts +2 -0
  8. package/build/plugin/codegen/generateSwiftAppClip.js +20 -0
  9. package/build/plugin/codegen/generateSwiftAppClip.js.map +1 -0
  10. package/build/plugin/codegen/generateSwiftEntities.d.ts +2 -0
  11. package/build/plugin/codegen/generateSwiftEntities.js +91 -0
  12. package/build/plugin/codegen/generateSwiftEntities.js.map +1 -0
  13. package/build/plugin/codegen/generateSwiftFocusFilters.d.ts +2 -0
  14. package/build/plugin/codegen/generateSwiftFocusFilters.js +42 -0
  15. package/build/plugin/codegen/generateSwiftFocusFilters.js.map +1 -0
  16. package/build/plugin/codegen/generateSwiftIntents.d.ts +2 -0
  17. package/build/plugin/codegen/generateSwiftIntents.js +43 -13
  18. package/build/plugin/codegen/generateSwiftIntents.js.map +1 -1
  19. package/build/plugin/codegen/generateSwiftWidget.d.ts +35 -0
  20. package/build/plugin/codegen/generateSwiftWidget.js +379 -0
  21. package/build/plugin/codegen/generateSwiftWidget.js.map +1 -0
  22. package/build/plugin/ios/withIOSInvoke.d.ts +25 -1
  23. package/build/plugin/ios/withIOSInvoke.js +83 -10
  24. package/build/plugin/ios/withIOSInvoke.js.map +1 -1
  25. package/build/plugin/ios/withWidgetExtension.d.ts +14 -0
  26. package/build/plugin/ios/withWidgetExtension.js +185 -0
  27. package/build/plugin/ios/withWidgetExtension.js.map +1 -0
  28. package/build/plugin/src/withInvoke.d.ts +45 -3
  29. package/build/plugin/src/withInvoke.js +30 -4
  30. package/build/plugin/src/withInvoke.js.map +1 -1
  31. package/build/src/components/AddToSiriButton.d.ts +1 -1
  32. package/build/src/components/AddToSiriButton.d.ts.map +1 -1
  33. package/build/src/components/InvokeDebugger.d.ts +1 -1
  34. package/build/src/components/InvokeDebugger.d.ts.map +1 -1
  35. package/build/src/components/ShortcutChip.d.ts +1 -1
  36. package/build/src/components/ShortcutChip.d.ts.map +1 -1
  37. package/build/src/hooks/useBackgroundIntent.d.ts +15 -0
  38. package/build/src/hooks/useBackgroundIntent.d.ts.map +1 -0
  39. package/build/src/hooks/useBackgroundIntent.js +41 -0
  40. package/build/src/hooks/useBackgroundIntent.js.map +1 -0
  41. package/build/src/hooks/useEntityQuery.d.ts +26 -0
  42. package/build/src/hooks/useEntityQuery.d.ts.map +1 -0
  43. package/build/src/hooks/useEntityQuery.js +47 -0
  44. package/build/src/hooks/useEntityQuery.js.map +1 -0
  45. package/build/src/hooks/useFocusFilter.d.ts +1 -1
  46. package/build/src/hooks/useFocusFilter.d.ts.map +1 -1
  47. package/build/src/hooks/useFocusFilter.js.map +1 -1
  48. package/build/src/hooks/useFocusFilterState.d.ts +28 -0
  49. package/build/src/hooks/useFocusFilterState.d.ts.map +1 -0
  50. package/build/src/hooks/useFocusFilterState.js +47 -0
  51. package/build/src/hooks/useFocusFilterState.js.map +1 -0
  52. package/build/src/hooks/useInvoke.d.ts.map +1 -1
  53. package/build/src/hooks/useInvoke.js +3 -0
  54. package/build/src/hooks/useInvoke.js.map +1 -1
  55. package/build/src/hooks/useInvokeAnalytics.d.ts +10 -14
  56. package/build/src/hooks/useInvokeAnalytics.d.ts.map +1 -1
  57. package/build/src/hooks/useInvokeAnalytics.js +21 -14
  58. package/build/src/hooks/useInvokeAnalytics.js.map +1 -1
  59. package/build/src/hooks/useInvokeQueue.d.ts +32 -0
  60. package/build/src/hooks/useInvokeQueue.d.ts.map +1 -0
  61. package/build/src/hooks/useInvokeQueue.js +77 -0
  62. package/build/src/hooks/useInvokeQueue.js.map +1 -0
  63. package/build/src/hooks/useLiveActivity.d.ts +37 -0
  64. package/build/src/hooks/useLiveActivity.d.ts.map +1 -0
  65. package/build/src/hooks/useLiveActivity.js +74 -0
  66. package/build/src/hooks/useLiveActivity.js.map +1 -0
  67. package/build/src/hooks/useOTAConfig.d.ts +24 -0
  68. package/build/src/hooks/useOTAConfig.d.ts.map +1 -0
  69. package/build/src/hooks/useOTAConfig.js +40 -0
  70. package/build/src/hooks/useOTAConfig.js.map +1 -0
  71. package/build/src/hooks/usePushToTalk.d.ts +35 -0
  72. package/build/src/hooks/usePushToTalk.d.ts.map +1 -0
  73. package/build/src/hooks/usePushToTalk.js +101 -0
  74. package/build/src/hooks/usePushToTalk.js.map +1 -0
  75. package/build/src/hooks/useSlice.d.ts +19 -0
  76. package/build/src/hooks/useSlice.d.ts.map +1 -0
  77. package/build/src/hooks/useSlice.js +22 -0
  78. package/build/src/hooks/useSlice.js.map +1 -0
  79. package/build/src/index.d.ts +19 -3
  80. package/build/src/index.d.ts.map +1 -1
  81. package/build/src/index.js +16 -1
  82. package/build/src/index.js.map +1 -1
  83. package/build/src/module.d.ts +18 -3
  84. package/build/src/module.d.ts.map +1 -1
  85. package/build/src/module.js +77 -0
  86. package/build/src/module.js.map +1 -1
  87. package/build/src/types/index.d.ts +117 -4
  88. package/build/src/types/index.d.ts.map +1 -1
  89. package/build/src/utils/analytics.d.ts +1 -7
  90. package/build/src/utils/analytics.d.ts.map +1 -1
  91. package/build/src/utils/analytics.js +9 -0
  92. package/build/src/utils/analytics.js.map +1 -1
  93. package/build/src/utils/invokeError.d.ts +28 -0
  94. package/build/src/utils/invokeError.d.ts.map +1 -0
  95. package/build/src/utils/invokeError.js +57 -0
  96. package/build/src/utils/invokeError.js.map +1 -0
  97. package/build/src/utils/otaConfig.d.ts +9 -0
  98. package/build/src/utils/otaConfig.d.ts.map +1 -0
  99. package/build/src/utils/otaConfig.js +45 -0
  100. package/build/src/utils/otaConfig.js.map +1 -0
  101. package/build/src/utils/persistentAnalytics.d.ts +5 -0
  102. package/build/src/utils/persistentAnalytics.d.ts.map +1 -0
  103. package/build/src/utils/persistentAnalytics.js +52 -0
  104. package/build/src/utils/persistentAnalytics.js.map +1 -0
  105. package/build/src/utils/validation.d.ts +12 -0
  106. package/build/src/utils/validation.d.ts.map +1 -1
  107. package/build/src/utils/validation.js +81 -1
  108. package/build/src/utils/validation.js.map +1 -1
  109. package/build/src/web/index.d.ts +39 -0
  110. package/build/src/web/index.d.ts.map +1 -0
  111. package/build/src/web/index.js +37 -0
  112. package/build/src/web/index.js.map +1 -0
  113. package/build/src/web/module.d.ts +40 -0
  114. package/build/src/web/module.d.ts.map +1 -0
  115. package/build/src/web/module.js +46 -0
  116. package/build/src/web/module.js.map +1 -0
  117. package/ios/AppClip/AppClipHandler.swift +41 -0
  118. package/ios/Background/BackgroundIntentBridge.swift +56 -0
  119. package/ios/Entities/EntityQueryBridge.swift +40 -0
  120. package/ios/FocusFilter/FocusFilterRegistrar.swift +40 -0
  121. package/ios/LiveActivity/LiveActivityManager.swift +83 -0
  122. package/ios/PTT/PTTManager.swift +51 -0
  123. package/package.json +2 -1
@@ -0,0 +1,47 @@
1
+ package expo.modules.invoke
2
+
3
+ import android.app.PendingIntent
4
+ import android.content.BroadcastReceiver
5
+ import android.content.Context
6
+ import android.content.Intent
7
+ import android.content.SharedPreferences
8
+
9
+ /**
10
+ * BroadcastReceiver for interactive widget button taps (Android 12+).
11
+ * Widget buttons use PendingIntent pointing to this receiver.
12
+ * On receive, writes a pending intent so the app (or background handler) can process it.
13
+ */
14
+ class InteractiveWidgetReceiver : BroadcastReceiver() {
15
+
16
+ companion object {
17
+ const val ACTION = "expo.modules.invoke.WIDGET_ACTION"
18
+ const val EXTRA_INTENT_ID = "intentId"
19
+ const val EXTRA_PARAMETERS = "parameters"
20
+
21
+ fun buildPendingIntent(
22
+ context: Context,
23
+ intentId: String,
24
+ parametersJson: String = "{}",
25
+ requestCode: Int = intentId.hashCode()
26
+ ): PendingIntent {
27
+ val intent = Intent(ACTION).apply {
28
+ setPackage(context.packageName)
29
+ putExtra(EXTRA_INTENT_ID, intentId)
30
+ putExtra(EXTRA_PARAMETERS, parametersJson)
31
+ }
32
+ return PendingIntent.getBroadcast(
33
+ context,
34
+ requestCode,
35
+ intent,
36
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
37
+ )
38
+ }
39
+ }
40
+
41
+ override fun onReceive(context: Context, intent: Intent?) {
42
+ if (intent?.action != ACTION) return
43
+ val intentId = intent.getStringExtra(EXTRA_INTENT_ID) ?: return
44
+ val prefs: SharedPreferences = context.getSharedPreferences("expo.invoke", Context.MODE_PRIVATE)
45
+ ExpoInvokeModule.writePendingIntent(prefs, intentId, "widget")
46
+ }
47
+ }
@@ -0,0 +1,83 @@
1
+ package expo.modules.invoke
2
+
3
+ import android.app.PendingIntent
4
+ import android.content.ContentResolver
5
+ import android.content.Context
6
+ import android.content.Intent
7
+ import android.net.Uri
8
+ import androidx.slice.Slice
9
+ import androidx.slice.SliceProvider
10
+ import androidx.slice.builders.ListBuilder
11
+ import androidx.slice.builders.SliceAction
12
+ import org.json.JSONArray
13
+ import org.json.JSONException
14
+
15
+ /**
16
+ * Android Slices provider for expo-invoke.
17
+ * Surfaces live app content inside Google Search and Google Assistant responses.
18
+ *
19
+ * Register in AndroidManifest via withAndroidInvoke plugin when slices[] is defined.
20
+ * Authority: {packageName}.invoke.slices
21
+ *
22
+ * Each SliceDefinition maps a URI path to an intent + widget data.
23
+ */
24
+ class InvokeSliceProvider : SliceProvider() {
25
+
26
+ override fun onCreateSliceProvider(): Boolean = true
27
+
28
+ override fun onBindSlice(sliceUri: Uri): Slice? {
29
+ val context = context ?: return null
30
+ val prefs = context.getSharedPreferences("expo.invoke", Context.MODE_PRIVATE)
31
+
32
+ val sliceConfigJson = prefs.getString("slices", null) ?: return null
33
+ val sliceConfig = findSliceForUri(sliceConfigJson, sliceUri.path ?: "") ?: return null
34
+
35
+ val title = sliceConfig.optString("title", "")
36
+ val intentId = sliceConfig.optString("intentId", "")
37
+ val subtitleKey = sliceConfig.optString("subtitleKey", null)
38
+ val subtitle = subtitleKey?.let { prefs.getString("widget.$it", null) }
39
+
40
+ val tapIntent = buildTapIntent(context, intentId)
41
+ val action = SliceAction.create(tapIntent, null, ListBuilder.ICON_IMAGE, title)
42
+
43
+ return ListBuilder(context, sliceUri, ListBuilder.INFINITY)
44
+ .addRow { row ->
45
+ row.setTitle(title)
46
+ if (subtitle != null) row.setSubtitle(subtitle.trim('"'))
47
+ row.setPrimaryAction(action)
48
+ }
49
+ .build()
50
+ }
51
+
52
+ override fun onMapIntentToUri(intent: Intent?): Uri {
53
+ val prefs = context?.getSharedPreferences("expo.invoke", Context.MODE_PRIVATE)
54
+ val pkg = context?.packageName ?: "unknown"
55
+ return Uri.parse("content://$pkg.invoke.slices/default")
56
+ }
57
+
58
+ private fun findSliceForUri(json: String, path: String): org.json.JSONObject? {
59
+ return try {
60
+ val arr = JSONArray(json)
61
+ for (i in 0 until arr.length()) {
62
+ val obj = arr.getJSONObject(i)
63
+ if (obj.optString("uriPath") == path) return obj
64
+ }
65
+ null
66
+ } catch (_: JSONException) { null }
67
+ }
68
+
69
+ private fun buildTapIntent(context: Context, intentId: String): PendingIntent {
70
+ val pkg = context.packageName
71
+ val deepLink = "$pkg://invoke/$intentId"
72
+ val launchIntent = Intent(Intent.ACTION_VIEW, Uri.parse(deepLink)).apply {
73
+ setPackage(pkg)
74
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
75
+ }
76
+ return PendingIntent.getActivity(
77
+ context,
78
+ intentId.hashCode(),
79
+ launchIntent,
80
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
81
+ )
82
+ }
83
+ }
@@ -6,6 +6,7 @@ import android.content.pm.ShortcutInfo
6
6
  import android.content.pm.ShortcutManager
7
7
  import android.graphics.drawable.Icon
8
8
  import android.os.Build
9
+ import android.util.Log
9
10
  import androidx.core.content.getSystemService
10
11
  import org.json.JSONArray
11
12
  import org.json.JSONObject
@@ -29,44 +30,49 @@ object ShortcutsHelper {
29
30
 
30
31
  @TargetApi(Build.VERSION_CODES.N_MR1)
31
32
  private fun parseShortcuts(context: Context, json: String): List<ShortcutInfo> {
32
- val arr = JSONArray(json)
33
- val result = mutableListOf<ShortcutInfo>()
33
+ val arr = try {
34
+ JSONArray(json)
35
+ } catch (e: Exception) {
36
+ Log.w("ExpoInvoke", "parseShortcuts: could not parse JSON array: ${e.message}")
37
+ return emptyList()
38
+ }
34
39
 
35
- for (i in 0 until arr.length()) {
36
- val obj = arr.getJSONObject(i)
37
- val id = obj.getString("id")
38
- val title = obj.getString("title")
39
- val subtitle = obj.optString("subtitle", "")
40
- val intentId = obj.optString("intentId", id)
40
+ return (0 until arr.length()).mapNotNull { i ->
41
+ runCatching {
42
+ val obj = arr.getJSONObject(i)
43
+ val id = obj.getString("id")
44
+ val title = obj.getString("title")
45
+ val subtitle = obj.optString("subtitle", "")
46
+ val intentId = obj.optString("intentId", id)
41
47
 
42
- val launchIntent = context.packageManager
43
- .getLaunchIntentForPackage(context.packageName)
44
- ?.apply {
45
- action = "expo.invoke.SHORTCUT"
46
- putExtra("intentId", intentId)
47
- // Encode optional parameters
48
- if (obj.has("parameters")) {
49
- putExtra("parameters", obj.getJSONObject("parameters").toString())
48
+ val launchIntent = context.packageManager
49
+ .getLaunchIntentForPackage(context.packageName)
50
+ ?.apply {
51
+ action = "expo.invoke.SHORTCUT"
52
+ putExtra("intentId", intentId)
53
+ if (obj.has("parameters")) {
54
+ putExtra("parameters", obj.getJSONObject("parameters").toString())
55
+ }
50
56
  }
51
- }
52
- ?: continue
57
+ ?: return@runCatching null
53
58
 
54
- val builder = ShortcutInfo.Builder(context, id)
55
- .setShortLabel(title.take(10))
56
- .setLongLabel(if (subtitle.isNotEmpty()) subtitle.take(25) else title.take(25))
57
- .setIntent(launchIntent)
59
+ val builder = ShortcutInfo.Builder(context, id)
60
+ .setShortLabel(title.take(10))
61
+ .setLongLabel(if (subtitle.isNotEmpty()) subtitle.take(25) else title.take(25))
62
+ .setIntent(launchIntent)
58
63
 
59
- // Use adaptive icon from the app launcher icon
60
- try {
61
- val iconRes = context.resources.getIdentifier("ic_launcher", "mipmap", context.packageName)
62
- if (iconRes != 0) {
63
- builder.setIcon(Icon.createWithResource(context, iconRes))
64
- }
65
- } catch (_: Exception) { /* icon is optional */ }
64
+ try {
65
+ val iconRes = context.resources.getIdentifier("ic_launcher", "mipmap", context.packageName)
66
+ if (iconRes != 0) {
67
+ builder.setIcon(Icon.createWithResource(context, iconRes))
68
+ }
69
+ } catch (_: Exception) { /* icon is optional */ }
66
70
 
67
- result.add(builder.build())
71
+ builder.build()
72
+ }.getOrElse { e ->
73
+ Log.w("ExpoInvoke", "parseShortcuts: skipping shortcut at index $i: ${e.message}")
74
+ null
75
+ }
68
76
  }
69
-
70
- return result
71
77
  }
72
78
  }
@@ -1,8 +1,10 @@
1
1
  import { type ConfigPlugin } from '@expo/config-plugins';
2
- import type { IntentDefinition, NotificationCategoryDefinition } from '../../src/types';
2
+ import type { IntentDefinition, NotificationCategoryDefinition, SliceDefinition } from '../../src/types';
3
3
  interface AndroidInvokeOptions {
4
4
  intents: IntentDefinition[];
5
5
  notificationActions?: NotificationCategoryDefinition[];
6
+ slices?: SliceDefinition[];
7
+ enableInteractiveWidgets?: boolean;
6
8
  }
7
9
  export declare const withAndroidInvoke: ConfigPlugin<AndroidInvokeOptions>;
8
10
  export {};
@@ -40,17 +40,18 @@ const path = __importStar(require("path"));
40
40
  const generateActionsXml_1 = require("../codegen/generateActionsXml");
41
41
  const generateShortcutsXml_1 = require("../codegen/generateShortcutsXml");
42
42
  const withAndroidInvoke = (config, options) => {
43
- const { intents } = options;
44
- // 1. AndroidManifest.xml — add QuickTileService + intent filter for App Actions
43
+ const { intents, slices = [], enableInteractiveWidgets = false } = options;
44
+ // 1. AndroidManifest.xml
45
45
  config = (0, config_plugins_1.withAndroidManifest)(config, (cfg) => {
46
- var _a, _b, _c, _d, _e;
46
+ var _a, _b, _c, _d, _e, _f, _g, _h;
47
47
  const app = (_a = cfg.modResults.manifest.application) === null || _a === void 0 ? void 0 : _a[0];
48
48
  if (!app)
49
49
  return cfg;
50
50
  app.service = (_b = app.service) !== null && _b !== void 0 ? _b : [];
51
51
  app.receiver = (_c = app.receiver) !== null && _c !== void 0 ? _c : [];
52
- // QuickTileService use `as any` because @expo/config-plugins types don't model
53
- // all valid Android manifest service attributes (android:icon, android:permission, etc.)
52
+ app.provider = (_d = app.provider) !== null && _d !== void 0 ? _d : [];
53
+ const packageName = (_f = (_e = cfg.android) === null || _e === void 0 ? void 0 : _e.package) !== null && _f !== void 0 ? _f : 'com.example.app';
54
+ // QuickTileService
54
55
  const hasQts = app.service.some((s) => { var _a; return ((_a = s.$) === null || _a === void 0 ? void 0 : _a['android:name']) === 'expo.modules.invoke.QuickTileService'; });
55
56
  if (!hasQts) {
56
57
  app.service.push({
@@ -62,16 +63,45 @@ const withAndroidInvoke = (config, options) => {
62
63
  'android:exported': 'true',
63
64
  },
64
65
  'intent-filter': [
65
- {
66
- action: [{ $: { 'android:name': 'android.service.quicksettings.action.QS_TILE' } }],
67
- },
66
+ { action: [{ $: { 'android:name': 'android.service.quicksettings.action.QS_TILE' } }] },
68
67
  ],
69
68
  });
70
69
  }
71
- // App Actions meta-data on MainActivity — ManifestActivity doesn't type 'meta-data', use `as any`
72
- const mainActivity = ((_d = app.activity) !== null && _d !== void 0 ? _d : []).find((a) => { var _a, _b; return (_b = (_a = a.$) === null || _a === void 0 ? void 0 : _a['android:name']) === null || _b === void 0 ? void 0 : _b.endsWith('.MainActivity'); });
70
+ // InteractiveWidgetReceiver
71
+ if (enableInteractiveWidgets) {
72
+ const hasReceiver = app.receiver.some((r) => { var _a; return ((_a = r.$) === null || _a === void 0 ? void 0 : _a['android:name']) === 'expo.modules.invoke.InteractiveWidgetReceiver'; });
73
+ if (!hasReceiver) {
74
+ app.receiver.push({
75
+ $: {
76
+ 'android:name': 'expo.modules.invoke.InteractiveWidgetReceiver',
77
+ 'android:exported': 'false',
78
+ },
79
+ 'intent-filter': [
80
+ { action: [{ $: { 'android:name': 'expo.modules.invoke.WIDGET_ACTION' } }] },
81
+ ],
82
+ });
83
+ }
84
+ }
85
+ // InvokeSliceProvider
86
+ if (slices.length > 0) {
87
+ const hasSlice = app.provider.some((p) => { var _a; return ((_a = p.$) === null || _a === void 0 ? void 0 : _a['android:name']) === 'expo.modules.invoke.InvokeSliceProvider'; });
88
+ if (!hasSlice) {
89
+ app.provider.push({
90
+ $: {
91
+ 'android:name': 'expo.modules.invoke.InvokeSliceProvider',
92
+ 'android:authorities': `${packageName}.invoke.slices`,
93
+ 'android:exported': 'true',
94
+ },
95
+ 'intent-filter': [
96
+ { action: [{ $: { 'android:name': 'android.intent.action.VIEW' } }] },
97
+ ],
98
+ });
99
+ }
100
+ }
101
+ // App Actions + Shortcuts meta-data on MainActivity
102
+ const mainActivity = ((_g = app.activity) !== null && _g !== void 0 ? _g : []).find((a) => { var _a, _b; return (_b = (_a = a.$) === null || _a === void 0 ? void 0 : _a['android:name']) === null || _b === void 0 ? void 0 : _b.endsWith('.MainActivity'); });
73
103
  if (mainActivity) {
74
- mainActivity['meta-data'] = (_e = mainActivity['meta-data']) !== null && _e !== void 0 ? _e : [];
104
+ mainActivity['meta-data'] = (_h = mainActivity['meta-data']) !== null && _h !== void 0 ? _h : [];
75
105
  const hasMeta = mainActivity['meta-data'].some((m) => { var _a; return ((_a = m.$) === null || _a === void 0 ? void 0 : _a['android:name']) === 'android.app.shortcuts'; });
76
106
  if (!hasMeta) {
77
107
  mainActivity['meta-data'].push({
@@ -87,16 +117,42 @@ const withAndroidInvoke = (config, options) => {
87
117
  }
88
118
  return cfg;
89
119
  });
90
- // 2. Generate res/xml/actions.xml and shortcuts.xml
120
+ // 2. Generate res/xml files + slice strings
91
121
  config = (0, config_plugins_1.withDangerousMod)(config, [
92
122
  'android',
93
123
  async (cfg) => {
94
124
  var _a, _b;
95
125
  const packageName = (_b = (_a = cfg.android) === null || _a === void 0 ? void 0 : _a.package) !== null && _b !== void 0 ? _b : 'com.example.app';
96
126
  const resXmlDir = path.join(cfg.modRequest.platformProjectRoot, 'app', 'src', 'main', 'res', 'xml');
97
- fs.mkdirSync(resXmlDir, { recursive: true });
98
- fs.writeFileSync(path.join(resXmlDir, 'actions.xml'), (0, generateActionsXml_1.generateActionsXml)(intents, packageName), 'utf8');
99
- fs.writeFileSync(path.join(resXmlDir, 'shortcuts.xml'), (0, generateShortcutsXml_1.generateShortcutsXml)(intents, packageName), 'utf8');
127
+ const resValuesDir = path.join(cfg.modRequest.platformProjectRoot, 'app', 'src', 'main', 'res', 'values');
128
+ for (const dir of [resXmlDir, resValuesDir]) {
129
+ try {
130
+ fs.mkdirSync(dir, { recursive: true });
131
+ }
132
+ catch (e) {
133
+ throw new Error(`[expo-invoke] Could not create Android resource directory at "${dir}".\n` +
134
+ `Cause: ${e instanceof Error ? e.message : e}\n` +
135
+ `Fix: Ensure the path is writable. Try running \`npx expo prebuild --clean\`.`);
136
+ }
137
+ }
138
+ const writeXml = (filePath, content) => {
139
+ try {
140
+ fs.writeFileSync(filePath, content, 'utf8');
141
+ }
142
+ catch (e) {
143
+ throw new Error(`[expo-invoke] Failed to write "${path.basename(filePath)}".\n` +
144
+ `Cause: ${e instanceof Error ? e.message : e}\n` +
145
+ `Fix: Ensure the Android resource directory is writable. Run \`npx expo prebuild --clean\`.`);
146
+ }
147
+ };
148
+ writeXml(path.join(resXmlDir, 'actions.xml'), (0, generateActionsXml_1.generateActionsXml)(intents, packageName));
149
+ writeXml(path.join(resXmlDir, 'shortcuts.xml'), (0, generateShortcutsXml_1.generateShortcutsXml)(intents, packageName));
150
+ // Slice string resources
151
+ if (slices.length > 0) {
152
+ const sliceStrings = slices.map((s) => ` <string name="slice_${s.id}_title">${s.title}</string>`).join('\n');
153
+ const xml = `<?xml version="1.0" encoding="utf-8"?>\n<resources>\n${sliceStrings}\n</resources>`;
154
+ writeXml(path.join(resValuesDir, 'slice_strings.xml'), xml);
155
+ }
100
156
  return cfg;
101
157
  },
102
158
  ]);
@@ -1 +1 @@
1
- {"version":3,"file":"withAndroidInvoke.js","sourceRoot":"","sources":["../../../plugin/android/withAndroidInvoke.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"withAndroidInvoke.js","sourceRoot":"","sources":["../../../plugin/android/withAndroidInvoke.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { AppClipDefinition } from '../../src/types';
2
+ export declare function generateSwiftAppClip(clips: AppClipDefinition[]): string;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateSwiftAppClip = generateSwiftAppClip;
4
+ function generateSwiftAppClip(clips) {
5
+ if (clips.length === 0)
6
+ return '';
7
+ const registrations = clips.map((c) => ` AppClipHandler.register(intentId: "${c.intentId}", urlPrefix: "${c.urlPrefix}")`).join('\n');
8
+ return `// AUTO-GENERATED by expo-invoke config plugin. Do not edit manually.
9
+ import Foundation
10
+
11
+ // Called once during app startup to register App Clip URL → intent mappings.
12
+ // ExpoInvokeModule.configure() calls this automatically.
13
+ @objc public class InvokeAppClipRegistrations: NSObject {
14
+ @objc public static func registerAll() {
15
+ ${registrations}
16
+ }
17
+ }
18
+ `;
19
+ }
20
+ //# sourceMappingURL=generateSwiftAppClip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generateSwiftAppClip.js","sourceRoot":"","sources":["../../../plugin/codegen/generateSwiftAppClip.ts"],"names":[],"mappings":";;AAEA,oDAkBC;AAlBD,SAAgB,oBAAoB,CAAC,KAA0B;IAC7D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAElC,MAAM,aAAa,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACpC,wCAAwC,CAAC,CAAC,QAAQ,kBAAkB,CAAC,CAAC,SAAS,IAAI,CACpF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEb,OAAO;;;;;;;EAOP,aAAa;;;CAGd,CAAC;AACF,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { AppEntityDefinition } from '../../src/types';
2
+ export declare function generateSwiftEntities(entities: AppEntityDefinition[]): string;
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateSwiftEntities = generateSwiftEntities;
4
+ function toPascalCase(id) {
5
+ return id.split(/[_\s-]+/).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join('');
6
+ }
7
+ function swiftPropertyType(type) {
8
+ switch (type) {
9
+ case 'text': return 'String';
10
+ case 'number': return 'Double';
11
+ case 'date': return 'Date';
12
+ }
13
+ }
14
+ function generateEntityStruct(entity) {
15
+ var _a, _b;
16
+ const name = `${toPascalCase(entity.id)}Entity`;
17
+ const queryName = `${toPascalCase(entity.id)}EntityQuery`;
18
+ const propDecls = entity.properties.map((p) => ` var ${p.name}: ${swiftPropertyType(p.type)}`).join('\n');
19
+ const toJSONLines = entity.properties.map((p) => {
20
+ if (p.type === 'date') {
21
+ return ` dict["${p.name}"] = ${p.name}.timeIntervalSince1970 * 1000`;
22
+ }
23
+ return ` dict["${p.name}"] = ${p.name}`;
24
+ }).join('\n');
25
+ const fromJSONLines = entity.properties.map((p) => {
26
+ if (p.type === 'number') {
27
+ return ` self.${p.name} = dict["${p.name}"] as? Double ?? 0`;
28
+ }
29
+ else if (p.type === 'date') {
30
+ return ` self.${p.name} = (dict["${p.name}"] as? Double).map { Date(timeIntervalSince1970: $0 / 1000) } ?? Date()`;
31
+ }
32
+ return ` self.${p.name} = dict["${p.name}"] as? String ?? ""`;
33
+ }).join('\n');
34
+ const displayRep = (_b = (_a = entity.properties[0]) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : 'id';
35
+ return `@available(iOS 16.4, *)
36
+ struct ${name}: AppEntity {
37
+ static var typeDisplayRepresentation: TypeDisplayRepresentation = "${entity.title}"
38
+ static var defaultQuery = ${queryName}()
39
+
40
+ var id: String
41
+ ${propDecls}
42
+
43
+ var displayRepresentation: DisplayRepresentation {
44
+ DisplayRepresentation(title: LocalizedStringResource(stringLiteral: ${displayRep}))
45
+ }
46
+
47
+ func toJSON() -> [String: Any] {
48
+ var dict: [String: Any] = ["id": id]
49
+ ${toJSONLines}
50
+ return dict
51
+ }
52
+
53
+ init(id: String, dict: [String: Any]) {
54
+ self.id = id
55
+ ${fromJSONLines}
56
+ }
57
+ }
58
+
59
+ @available(iOS 16.4, *)
60
+ struct ${queryName}: EntityQuery {
61
+ func entities(for ids: [${name}.ID]) async throws -> [${name}] { [] }
62
+
63
+ func suggestedEntities() async throws -> [${name}] {
64
+ let results = await EntityQueryBridge.shared.queryEntities(entityId: "${entity.id}", query: "")
65
+ return results.compactMap { dict in
66
+ guard let id = dict["id"] as? String else { return nil }
67
+ return ${name}(id: id, dict: dict)
68
+ }
69
+ }
70
+
71
+ func entities(matching query: String) async throws -> [${name}] {
72
+ let results = await EntityQueryBridge.shared.queryEntities(entityId: "${entity.id}", query: query)
73
+ return results.compactMap { dict in
74
+ guard let id = dict["id"] as? String else { return nil }
75
+ return ${name}(id: id, dict: dict)
76
+ }
77
+ }
78
+ }`;
79
+ }
80
+ function generateSwiftEntities(entities) {
81
+ if (entities.length === 0)
82
+ return '';
83
+ const structs = entities.map(generateEntityStruct).join('\n\n');
84
+ return `// AUTO-GENERATED by expo-invoke config plugin. Do not edit manually.
85
+ import AppIntents
86
+ import Foundation
87
+
88
+ ${structs}
89
+ `;
90
+ }
91
+ //# sourceMappingURL=generateSwiftEntities.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generateSwiftEntities.js","sourceRoot":"","sources":["../../../plugin/codegen/generateSwiftEntities.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { FocusFilterDefinition } from '../../src/types';
2
+ export declare function generateSwiftFocusFilters(filters: FocusFilterDefinition[]): string;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateSwiftFocusFilters = generateSwiftFocusFilters;
4
+ function toPascalCase(id) {
5
+ return id.split(/[_\s-]+/).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join('');
6
+ }
7
+ function swiftParamType(type) {
8
+ return type === 'boolean' ? 'Bool' : 'String';
9
+ }
10
+ function generateFilterStruct(filter) {
11
+ var _a;
12
+ const name = `${toPascalCase(filter.id)}FocusFilter`;
13
+ const params = (_a = filter.parameters) !== null && _a !== void 0 ? _a : [];
14
+ const paramDecls = params.map((p) => ` @Parameter(title: "${p.title}")\n var ${p.name}: ${swiftParamType(p.type)}${p.type === 'boolean' ? '' : '?'}`).join('\n\n');
15
+ const paramsDict = params.map((p) => ` params["${p.name}"] = ${p.name}${p.type !== 'boolean' ? ' ?? ""' : ''}`).join('\n');
16
+ return `@available(iOS 16.0, *)
17
+ struct ${name}: SetFocusFilterIntent {
18
+ static var title: LocalizedStringResource = "${filter.title}"
19
+ static var authorizationPolicy: IntentAuthorizationPolicy = .alwaysAllowed
20
+
21
+ ${paramDecls || ' // No parameters'}
22
+
23
+ func perform() async throws -> some IntentResult {
24
+ var params: [String: Any] = [:]
25
+ ${paramsDict}
26
+ FocusFilterRegistrar.shared.apply(filterId: "${filter.id}", parameters: params)
27
+ return .result()
28
+ }
29
+ }`;
30
+ }
31
+ function generateSwiftFocusFilters(filters) {
32
+ if (filters.length === 0)
33
+ return '';
34
+ const structs = filters.map(generateFilterStruct).join('\n\n');
35
+ return `// AUTO-GENERATED by expo-invoke config plugin. Do not edit manually.
36
+ import AppIntents
37
+ import Foundation
38
+
39
+ ${structs}
40
+ `;
41
+ }
42
+ //# sourceMappingURL=generateSwiftFocusFilters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generateSwiftFocusFilters.js","sourceRoot":"","sources":["../../../plugin/codegen/generateSwiftFocusFilters.ts"],"names":[],"mappings":";;AAsCA,8DASC;AA7CD,SAAS,YAAY,CAAC,EAAU;IAC9B,OAAO,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACzF,CAAC;AAED,SAAS,cAAc,CAAC,IAAwB;IAC9C,OAAO,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;AAChD,CAAC;AAED,SAAS,oBAAoB,CAAC,MAA6B;;IACzD,MAAM,IAAI,GAAG,GAAG,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,aAAa,CAAC;IACrD,MAAM,MAAM,GAAG,MAAA,MAAM,CAAC,UAAU,mCAAI,EAAE,CAAC;IAEvC,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAClC,wBAAwB,CAAC,CAAC,KAAK,aAAa,CAAC,CAAC,IAAI,KAAK,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAClH,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAEf,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAClC,eAAe,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CAC7E,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEb,OAAO;SACA,IAAI;iDACoC,MAAM,CAAC,KAAK;;;EAG3D,UAAU,IAAI,oBAAoB;;;;EAIlC,UAAU;mDACuC,MAAM,CAAC,EAAE;;;EAG1D,CAAC;AACH,CAAC;AAED,SAAgB,yBAAyB,CAAC,OAAgC;IACxE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACpC,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC/D,OAAO;;;;EAIP,OAAO;CACR,CAAC;AACF,CAAC"}
@@ -1,2 +1,4 @@
1
1
  import type { IntentDefinition } from '../../src/types';
2
+ /** Maps a WidgetSize string to its Swift WidgetFamily case */
3
+ export declare function swiftWidgetFamily(size: string): string;
2
4
  export declare function generateSwiftIntents(intents: IntentDefinition[]): string;