customerio-expo-plugin 2.5.0 → 2.7.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 (162) hide show
  1. package/package.json +13 -31
  2. package/plugin/app.plugin.js +1 -1
  3. package/plugin/lib/commonjs/android/withAndroidManifestUpdates.js +56 -15
  4. package/plugin/lib/commonjs/android/withAndroidManifestUpdates.js.map +1 -1
  5. package/plugin/lib/commonjs/android/withCIOAndroid.js +7 -2
  6. package/plugin/lib/commonjs/android/withCIOAndroid.js.map +1 -1
  7. package/plugin/lib/commonjs/android/withGoogleServicesJSON.js +1 -1
  8. package/plugin/lib/commonjs/android/withGoogleServicesJSON.js.map +1 -1
  9. package/plugin/lib/commonjs/android/withMainApplicationModifications.js +45 -0
  10. package/plugin/lib/commonjs/android/withMainApplicationModifications.js.map +1 -0
  11. package/plugin/lib/commonjs/android/withNotificationChannelMetadata.js +1 -1
  12. package/plugin/lib/commonjs/android/withNotificationChannelMetadata.js.map +1 -1
  13. package/plugin/lib/commonjs/android/withProjectStrings.js +14 -7
  14. package/plugin/lib/commonjs/android/withProjectStrings.js.map +1 -1
  15. package/plugin/lib/commonjs/helpers/constants/android.js +7 -1
  16. package/plugin/lib/commonjs/helpers/constants/android.js.map +1 -1
  17. package/plugin/lib/commonjs/helpers/constants/common.js +18 -0
  18. package/plugin/lib/commonjs/helpers/constants/common.js.map +1 -0
  19. package/plugin/lib/commonjs/helpers/constants/ios.js +7 -7
  20. package/plugin/lib/commonjs/helpers/constants/ios.js.map +1 -1
  21. package/plugin/lib/commonjs/helpers/native-files/android/CustomerIOSDKInitializer.kt +64 -0
  22. package/plugin/lib/commonjs/helpers/native-files/ios/CustomerIOSDKInitializer.swift +54 -0
  23. package/plugin/lib/commonjs/helpers/utils/injectCIOPodfileCode.js +2 -2
  24. package/plugin/lib/commonjs/helpers/utils/injectCIOPodfileCode.js.map +1 -1
  25. package/plugin/lib/commonjs/helpers/utils/patchPluginNativeCode.js +62 -0
  26. package/plugin/lib/commonjs/helpers/utils/patchPluginNativeCode.js.map +1 -0
  27. package/plugin/lib/commonjs/index.js +13 -2
  28. package/plugin/lib/commonjs/index.js.map +1 -1
  29. package/plugin/lib/commonjs/ios/utils.js +1 -1
  30. package/plugin/lib/commonjs/ios/utils.js.map +1 -1
  31. package/plugin/lib/commonjs/ios/withAppDelegateModifications.js +1 -1
  32. package/plugin/lib/commonjs/ios/withAppDelegateModifications.js.map +1 -1
  33. package/plugin/lib/commonjs/ios/withCIOIos.js +17 -11
  34. package/plugin/lib/commonjs/ios/withCIOIos.js.map +1 -1
  35. package/plugin/lib/commonjs/ios/withCIOIosSwift.js +133 -49
  36. package/plugin/lib/commonjs/ios/withCIOIosSwift.js.map +1 -1
  37. package/plugin/lib/commonjs/ios/withGoogleServicesJsonFile.js +1 -1
  38. package/plugin/lib/commonjs/ios/withGoogleServicesJsonFile.js.map +1 -1
  39. package/plugin/lib/commonjs/ios/withNotificationsXcodeProject.js +17 -19
  40. package/plugin/lib/commonjs/ios/withNotificationsXcodeProject.js.map +1 -1
  41. package/plugin/lib/commonjs/ios/withXcodeProject.js +1 -1
  42. package/plugin/lib/commonjs/ios/withXcodeProject.js.map +1 -1
  43. package/plugin/lib/commonjs/postInstallHelper.js.map +1 -1
  44. package/plugin/lib/commonjs/types/cio-types.js.map +1 -1
  45. package/plugin/lib/commonjs/utils/android.js +109 -0
  46. package/plugin/lib/commonjs/utils/android.js.map +1 -0
  47. package/plugin/lib/commonjs/utils/config.js +43 -0
  48. package/plugin/lib/commonjs/utils/config.js.map +1 -0
  49. package/plugin/lib/commonjs/utils/plugin.js +49 -0
  50. package/plugin/lib/commonjs/utils/plugin.js.map +1 -0
  51. package/plugin/lib/commonjs/utils/validation.js +44 -0
  52. package/plugin/lib/commonjs/utils/validation.js.map +1 -0
  53. package/plugin/lib/commonjs/utils/xcode.js +67 -0
  54. package/plugin/lib/commonjs/utils/xcode.js.map +1 -0
  55. package/plugin/lib/module/android/withAndroidManifestUpdates.js +55 -14
  56. package/plugin/lib/module/android/withAndroidManifestUpdates.js.map +1 -1
  57. package/plugin/lib/module/android/withCIOAndroid.js +7 -2
  58. package/plugin/lib/module/android/withCIOAndroid.js.map +1 -1
  59. package/plugin/lib/module/android/withGoogleServicesJSON.js +1 -1
  60. package/plugin/lib/module/android/withGoogleServicesJSON.js.map +1 -1
  61. package/plugin/lib/module/android/withMainApplicationModifications.js +38 -0
  62. package/plugin/lib/module/android/withMainApplicationModifications.js.map +1 -0
  63. package/plugin/lib/module/android/withNotificationChannelMetadata.js +1 -1
  64. package/plugin/lib/module/android/withNotificationChannelMetadata.js.map +1 -1
  65. package/plugin/lib/module/android/withProjectStrings.js +13 -6
  66. package/plugin/lib/module/android/withProjectStrings.js.map +1 -1
  67. package/plugin/lib/module/helpers/constants/android.js +6 -0
  68. package/plugin/lib/module/helpers/constants/android.js.map +1 -1
  69. package/plugin/lib/module/helpers/constants/common.js +12 -0
  70. package/plugin/lib/module/helpers/constants/common.js.map +1 -0
  71. package/plugin/lib/module/helpers/constants/ios.js +6 -6
  72. package/plugin/lib/module/helpers/constants/ios.js.map +1 -1
  73. package/plugin/lib/module/helpers/native-files/android/CustomerIOSDKInitializer.kt +64 -0
  74. package/plugin/lib/module/helpers/native-files/ios/CustomerIOSDKInitializer.swift +54 -0
  75. package/plugin/lib/module/helpers/utils/injectCIOPodfileCode.js +2 -2
  76. package/plugin/lib/module/helpers/utils/injectCIOPodfileCode.js.map +1 -1
  77. package/plugin/lib/module/helpers/utils/patchPluginNativeCode.js +57 -0
  78. package/plugin/lib/module/helpers/utils/patchPluginNativeCode.js.map +1 -0
  79. package/plugin/lib/module/index.js +14 -2
  80. package/plugin/lib/module/index.js.map +1 -1
  81. package/plugin/lib/module/ios/utils.js +1 -2
  82. package/plugin/lib/module/ios/utils.js.map +1 -1
  83. package/plugin/lib/module/ios/withAppDelegateModifications.js +3 -3
  84. package/plugin/lib/module/ios/withAppDelegateModifications.js.map +1 -1
  85. package/plugin/lib/module/ios/withCIOIos.js +17 -11
  86. package/plugin/lib/module/ios/withCIOIos.js.map +1 -1
  87. package/plugin/lib/module/ios/withCIOIosSwift.js +134 -50
  88. package/plugin/lib/module/ios/withCIOIosSwift.js.map +1 -1
  89. package/plugin/lib/module/ios/withGoogleServicesJsonFile.js +2 -2
  90. package/plugin/lib/module/ios/withGoogleServicesJsonFile.js.map +1 -1
  91. package/plugin/lib/module/ios/withNotificationsXcodeProject.js +18 -20
  92. package/plugin/lib/module/ios/withNotificationsXcodeProject.js.map +1 -1
  93. package/plugin/lib/module/ios/withXcodeProject.js +1 -1
  94. package/plugin/lib/module/ios/withXcodeProject.js.map +1 -1
  95. package/plugin/lib/module/postInstallHelper.js.map +1 -1
  96. package/plugin/lib/module/types/cio-types.js.map +1 -1
  97. package/plugin/lib/module/utils/android.js +100 -0
  98. package/plugin/lib/module/utils/android.js.map +1 -0
  99. package/plugin/lib/module/utils/config.js +38 -0
  100. package/plugin/lib/module/utils/config.js.map +1 -0
  101. package/plugin/lib/module/utils/plugin.js +38 -0
  102. package/plugin/lib/module/utils/plugin.js.map +1 -0
  103. package/plugin/lib/module/utils/validation.js +39 -0
  104. package/plugin/lib/module/utils/validation.js.map +1 -0
  105. package/plugin/lib/module/utils/xcode.js +60 -0
  106. package/plugin/lib/module/utils/xcode.js.map +1 -0
  107. package/plugin/lib/typescript/android/withAndroidManifestUpdates.d.ts +1 -0
  108. package/plugin/lib/typescript/android/withCIOAndroid.d.ts +2 -2
  109. package/plugin/lib/typescript/android/withMainApplicationModifications.d.ts +3 -0
  110. package/plugin/lib/typescript/android/withProjectStrings.d.ts +2 -1
  111. package/plugin/lib/typescript/helpers/constants/android.d.ts +3 -0
  112. package/plugin/lib/typescript/helpers/constants/common.d.ts +11 -0
  113. package/plugin/lib/typescript/helpers/constants/ios.d.ts +3 -1
  114. package/plugin/lib/typescript/helpers/utils/patchPluginNativeCode.d.ts +7 -0
  115. package/plugin/lib/typescript/ios/utils.d.ts +2 -2
  116. package/plugin/lib/typescript/ios/withCIOIos.d.ts +2 -2
  117. package/plugin/lib/typescript/ios/withCIOIosSwift.d.ts +3 -3
  118. package/plugin/lib/typescript/types/cio-types.d.ts +46 -6
  119. package/plugin/lib/typescript/utils/android.d.ts +5 -0
  120. package/plugin/lib/typescript/utils/config.d.ts +8 -0
  121. package/plugin/lib/typescript/utils/plugin.d.ts +4 -0
  122. package/plugin/lib/typescript/utils/validation.d.ts +3 -0
  123. package/plugin/lib/typescript/utils/xcode.d.ts +28 -0
  124. package/plugin/src/android/withAndroidManifestUpdates.ts +70 -21
  125. package/plugin/src/android/withCIOAndroid.ts +8 -2
  126. package/plugin/src/android/withGoogleServicesJSON.ts +2 -2
  127. package/plugin/src/android/withMainApplicationModifications.ts +50 -0
  128. package/plugin/src/android/withNotificationChannelMetadata.ts +7 -3
  129. package/plugin/src/android/withProjectStrings.ts +20 -10
  130. package/plugin/src/helpers/constants/android.ts +7 -0
  131. package/plugin/src/helpers/constants/common.ts +12 -0
  132. package/plugin/src/helpers/constants/ios.ts +11 -13
  133. package/plugin/src/helpers/native-files/android/CustomerIOSDKInitializer.kt +64 -0
  134. package/plugin/src/helpers/native-files/ios/CustomerIOSDKInitializer.swift +54 -0
  135. package/plugin/src/helpers/utils/injectCIOPodfileCode.ts +8 -7
  136. package/plugin/src/helpers/utils/patchPluginNativeCode.ts +97 -0
  137. package/plugin/src/index.ts +18 -2
  138. package/plugin/src/ios/utils.ts +5 -5
  139. package/plugin/src/ios/withAppDelegateModifications.ts +11 -8
  140. package/plugin/src/ios/withCIOIos.ts +19 -11
  141. package/plugin/src/ios/withCIOIosSwift.ts +195 -73
  142. package/plugin/src/ios/withGoogleServicesJsonFile.ts +7 -10
  143. package/plugin/src/ios/withNotificationsXcodeProject.ts +25 -26
  144. package/plugin/src/ios/withXcodeProject.ts +1 -1
  145. package/plugin/src/postInstallHelper.js +1 -1
  146. package/plugin/src/types/cio-types.ts +48 -8
  147. package/plugin/src/utils/android.ts +112 -0
  148. package/plugin/src/utils/config.ts +53 -0
  149. package/plugin/src/utils/plugin.ts +46 -0
  150. package/plugin/src/utils/validation.ts +54 -0
  151. package/plugin/src/utils/xcode.ts +74 -0
  152. package/plugin/lib/commonjs/helpers/constants/globals.d.js +0 -2
  153. package/plugin/lib/commonjs/helpers/constants/globals.d.js.map +0 -1
  154. package/plugin/lib/commonjs/helpers/utils/pluginUtils.js +0 -26
  155. package/plugin/lib/commonjs/helpers/utils/pluginUtils.js.map +0 -1
  156. package/plugin/lib/module/helpers/constants/globals.d.js +0 -2
  157. package/plugin/lib/module/helpers/constants/globals.d.js.map +0 -1
  158. package/plugin/lib/module/helpers/utils/pluginUtils.js +0 -19
  159. package/plugin/lib/module/helpers/utils/pluginUtils.js.map +0 -1
  160. package/plugin/lib/typescript/helpers/utils/pluginUtils.d.ts +0 -4
  161. package/plugin/src/helpers/constants/globals.d.ts +0 -8
  162. package/plugin/src/helpers/utils/pluginUtils.ts +0 -22
@@ -0,0 +1,7 @@
1
+ import type { NativeSDKConfig } from '../../types/cio-types';
2
+ import { type Platform } from '../constants/common';
3
+ /**
4
+ * Shared utility function to perform common SDK config replacements
5
+ * for both iOS and Android template files
6
+ */
7
+ export declare function patchNativeSDKInitializer(rawContent: string, platform: Platform, sdkConfig: NativeSDKConfig): string;
@@ -1,7 +1,7 @@
1
- import type { CustomerIOPluginOptionsIOS } from '../types/cio-types';
2
1
  import type { ExpoConfig } from '@expo/config-types';
2
+ import type { CustomerIOPluginOptionsIOS } from '../types/cio-types';
3
3
  /**
4
- * Returns t
4
+ * Returns true if FCM is configured to be used as push provider
5
5
  * @param iosOptions The plugin iOS configuration options
6
6
  * @returns true if FCM is configured to be used as push provider
7
7
  */
@@ -1,3 +1,3 @@
1
1
  import type { ExpoConfig } from '@expo/config-types';
2
- import type { CustomerIOPluginOptionsIOS } from '../types/cio-types';
3
- export declare function withCIOIos(config: ExpoConfig, props: CustomerIOPluginOptionsIOS): ExpoConfig;
2
+ import type { CustomerIOPluginOptionsIOS, NativeSDKConfig } from '../types/cio-types';
3
+ export declare function withCIOIos(config: ExpoConfig, sdkConfig: NativeSDKConfig | undefined, props: CustomerIOPluginOptionsIOS): ExpoConfig;
@@ -1,3 +1,3 @@
1
- import type { ConfigPlugin } from '@expo/config-plugins';
2
- import type { CustomerIOPluginOptionsIOS } from '../types/cio-types';
3
- export declare const withCIOIosSwift: ConfigPlugin<CustomerIOPluginOptionsIOS>;
1
+ import type { ExpoConfig } from '@expo/config-types';
2
+ import type { CustomerIOPluginOptionsIOS, NativeSDKConfig } from '../types/cio-types';
3
+ export declare const withCIOIosSwift: (configOuter: ExpoConfig, sdkConfig: NativeSDKConfig | undefined, props: CustomerIOPluginOptionsIOS) => ExpoConfig;
@@ -1,7 +1,15 @@
1
+ /**
2
+ * Properties set by the user in their app config file (e.g: app.json or app.plugin.js)
3
+ * @public
4
+ */
1
5
  export type CustomerIOPluginProperties = {
2
6
  devTeam: string;
3
7
  iosDeploymentTarget: string;
4
8
  };
9
+ /**
10
+ * Plugin options for iOS platform configuration
11
+ * @public
12
+ */
5
13
  export type CustomerIOPluginOptionsIOS = {
6
14
  iosPath: string;
7
15
  devTeam?: string;
@@ -38,6 +46,10 @@ export type CustomerIOPluginOptionsIOS = {
38
46
  */
39
47
  disableNotificationRegistration?: boolean;
40
48
  };
49
+ /**
50
+ * Plugin options for Android platform configuration
51
+ * @public
52
+ */
41
53
  export type CustomerIOPluginOptionsAndroid = {
42
54
  androidPath: string;
43
55
  googleServicesFile?: string;
@@ -50,10 +62,41 @@ export type CustomerIOPluginOptionsAndroid = {
50
62
  };
51
63
  };
52
64
  };
65
+ /**
66
+ * SDK configuration options for auto initialization
67
+ * @public
68
+ */
69
+ export type NativeSDKConfig = {
70
+ cdpApiKey: string;
71
+ region?: 'US' | 'EU';
72
+ autoTrackDeviceAttributes?: boolean;
73
+ trackApplicationLifecycleEvents?: boolean;
74
+ screenViewUse?: 'all' | 'inapp';
75
+ logLevel?: 'none' | 'error' | 'info' | 'debug';
76
+ siteId?: string;
77
+ migrationSiteId?: string;
78
+ };
79
+ /**
80
+ * Combined plugin options for both iOS and Android platforms
81
+ * @public
82
+ */
53
83
  export type CustomerIOPluginOptions = {
84
+ config?: NativeSDKConfig;
54
85
  android: CustomerIOPluginOptionsAndroid;
55
86
  ios: CustomerIOPluginOptionsIOS;
56
87
  };
88
+ /**
89
+ * Rich push configuration used to initialize Notification Service Extension (NSE) on the native side
90
+ * @public
91
+ */
92
+ export type RichPushConfig = {
93
+ cdpApiKey: string;
94
+ region?: string;
95
+ };
96
+ /**
97
+ * Push notification configuration options
98
+ * @public
99
+ */
57
100
  export type CustomerIOPluginPushNotificationOptions = {
58
101
  provider?: 'apn' | 'fcm';
59
102
  googleServicesFile?: string;
@@ -64,11 +107,8 @@ export type CustomerIOPluginPushNotificationOptions = {
64
107
  disableNotificationRegistration?: boolean;
65
108
  handleDeeplinkInKilledState?: boolean;
66
109
  /**
67
- * These values will be used to initialize the Notification Service Extension (NSE) on the native side.
68
- * They should match the values you use to initialize the SDK in your app
110
+ * Rich push config should match the values used to initialize SDK in the app.
111
+ * Optional if `config` is provided at the top level.
69
112
  */
70
- env: {
71
- cdpApiKey: string;
72
- region: string;
73
- };
113
+ env?: RichPushConfig;
74
114
  };
@@ -0,0 +1,5 @@
1
+ import type { ExportedConfigWithProps } from '@expo/config-plugins';
2
+ import type { ApplicationProjectFile } from '@expo/config-plugins/build/android/Paths';
3
+ export declare const addImportToFile: (content: string, importStatement: string) => string;
4
+ export declare const addCodeToMethod: (content: string, methodRegex: RegExp, codeToAdd: string) => string;
5
+ export declare const copyTemplateFile: (expoConfig: ExportedConfigWithProps<ApplicationProjectFile>, filename: string, classPackage: string, patchContent: (content: string) => string) => void;
@@ -0,0 +1,8 @@
1
+ import type { CustomerIOPluginOptionsIOS, NativeSDKConfig, RichPushConfig } from '../types/cio-types';
2
+ /**
3
+ * Merges config values with env values for backward compatibility.
4
+ * If env is provided, it takes precedence. If nativeConfig is provided but env is not,
5
+ * nativeConfig values are used. This prioritizes existing env configuration for backward compatibility.
6
+ */
7
+ declare function mergeConfigWithEnvValues(props: CustomerIOPluginOptionsIOS, nativeConfig?: NativeSDKConfig): RichPushConfig | undefined;
8
+ export { mergeConfigWithEnvValues };
@@ -0,0 +1,4 @@
1
+ export declare const getNativeFilesPath: () => string;
2
+ export declare const getAndroidNativeFilesPath: () => string;
3
+ export declare const getIosNativeFilesPath: () => string;
4
+ export declare const getPluginVersion: () => string;
@@ -0,0 +1,3 @@
1
+ import type { NativeSDKConfig } from '../types/cio-types';
2
+ declare function validateNativeSDKConfig(config: NativeSDKConfig): void;
3
+ export { validateNativeSDKConfig };
@@ -0,0 +1,28 @@
1
+ import type { XcodeProject } from "@expo/config-plugins";
2
+ /**
3
+ * Gets an existing CustomerIO group or creates a new one in the Xcode project
4
+ * @param xcodeProject The Xcode project instance
5
+ * @param projectName The iOS project name
6
+ * @returns The CustomerIO group reference
7
+ */
8
+ export declare function getOrCreateCustomerIOGroup(xcodeProject: XcodeProject, projectName: string): XcodeProject['pbxCreateGroup'];
9
+ /**
10
+ * Copies template file to iOS project, applies transformations, and registers with Xcode
11
+ * @param params.xcodeProject Xcode project instance
12
+ * @param params.iosProjectRoot iOS project root path
13
+ * @param params.projectName iOS project name
14
+ * @param params.sourceFilePath Source template file path
15
+ * @param params.targetFileName Target file name
16
+ * @param params.transform Content transformation function
17
+ * @param params.customerIOGroup CustomerIO group (auto-created if not provided)
18
+ * @returns Destination file path
19
+ */
20
+ export declare function copyFileToXcode({ xcodeProject, iosProjectRoot, projectName, sourceFilePath, targetFileName, transform, customerIOGroup, }: {
21
+ xcodeProject: XcodeProject;
22
+ iosProjectRoot: string;
23
+ projectName: string;
24
+ sourceFilePath: string;
25
+ targetFileName: string;
26
+ transform: (content: string) => string;
27
+ customerIOGroup?: XcodeProject['pbxCreateGroup'];
28
+ }): string;
@@ -4,44 +4,93 @@ import type { ManifestApplication } from '@expo/config-plugins/build/android/Man
4
4
 
5
5
  import type { CustomerIOPluginOptionsAndroid } from '../types/cio-types';
6
6
 
7
+ // Default low priority for Firebase messaging service when setHighPriorityPushHandler is false
8
+ export const DEFAULT_LOW_PRIORITY = -10;
9
+
10
+
7
11
  export const withAndroidManifestUpdates: ConfigPlugin<
8
12
  CustomerIOPluginOptionsAndroid
9
- > = (configOuter) => {
13
+ > = (configOuter, options) => {
10
14
  return withAndroidManifest(configOuter, (props) => {
11
15
  const application = props.modResults.manifest
12
16
  .application as ManifestApplication[];
13
17
  const customerIOMessagingpush =
14
18
  'io.customer.messagingpush.CustomerIOFirebaseMessagingService';
15
19
 
16
- if (!application[0]['service']) {
17
- application[0]['service'] = [];
20
+ if (!application[0].service) {
21
+ application[0].service = [];
18
22
  }
19
23
 
20
- const hasService = application[0]['service'].some(
21
- (service) => service['$']['android:name'] === customerIOMessagingpush
24
+ const existingServiceIndex = application[0].service.findIndex(
25
+ (service) => service.$['android:name'] === customerIOMessagingpush
22
26
  );
23
27
 
24
- if (!hasService) {
25
- application[0]['service'].push({
28
+ if (existingServiceIndex === -1) {
29
+ // Intent filter structure for Firebase messaging service
30
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
31
+ const intentFilter: any = {
32
+ action: [
33
+ {
34
+ $: {
35
+ 'android:name': 'com.google.firebase.MESSAGING_EVENT',
36
+ },
37
+ },
38
+ ],
39
+ };
40
+
41
+ // Handle priority based on setHighPriorityPushHandler value
42
+ if (options.setHighPriorityPushHandler === true) {
43
+ // High priority - no priority attribute means default high priority
44
+ console.log(
45
+ 'Successfully set CustomerIO push handler as high priority in AndroidManifest.xml'
46
+ );
47
+ } else if (options.setHighPriorityPushHandler === false) {
48
+ // Low priority - set fixed priority
49
+ intentFilter.$ = {
50
+ 'android:priority': DEFAULT_LOW_PRIORITY.toString(),
51
+ };
52
+ console.log(
53
+ `Successfully set CustomerIO push handler as low priority (${DEFAULT_LOW_PRIORITY}) in AndroidManifest.xml`
54
+ );
55
+ }
56
+
57
+ application[0].service.push({
26
58
  '$': {
27
59
  'android:name': customerIOMessagingpush,
28
60
  'android:exported': 'false',
29
61
  },
30
- 'intent-filter': [
31
- {
32
- action: [
33
- {
34
- $: {
35
- 'android:name': 'com.google.firebase.MESSAGING_EVENT',
36
- },
37
- },
38
- ],
39
- },
40
- ],
62
+ 'intent-filter': [intentFilter],
41
63
  });
42
- console.log(
43
- 'Successfully set CustomerIO push handler as priority in AndroidManifest.xml'
44
- );
64
+ } else if (options.setHighPriorityPushHandler === true) {
65
+ // Service exists, need to ensure it becomes high priority (remove priority attribute)
66
+ const existingService = application[0].service[existingServiceIndex];
67
+
68
+ if (existingService['intent-filter'] && existingService['intent-filter'].length > 0) {
69
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
70
+ const intentFilter = existingService['intent-filter'][0] as any;
71
+ if (intentFilter.$ && intentFilter.$['android:priority']) {
72
+ delete intentFilter.$['android:priority'];
73
+ console.log(
74
+ 'Successfully updated existing CustomerIO push handler to high priority in AndroidManifest.xml'
75
+ );
76
+ }
77
+ }
78
+ } else if (options.setHighPriorityPushHandler === false) {
79
+ // Service exists, update to low priority
80
+ const existingService = application[0].service[existingServiceIndex];
81
+
82
+ // Update existing service intent-filter with fixed priority
83
+ if (existingService['intent-filter'] && existingService['intent-filter'].length > 0) {
84
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
85
+ const intentFilter = existingService['intent-filter'][0] as any;
86
+ if (!intentFilter.$) {
87
+ intentFilter.$ = {};
88
+ }
89
+ intentFilter.$['android:priority'] = DEFAULT_LOW_PRIORITY.toString();
90
+ console.log(
91
+ `Successfully updated existing CustomerIO push handler to low priority (${DEFAULT_LOW_PRIORITY}) in AndroidManifest.xml`
92
+ );
93
+ }
45
94
  }
46
95
 
47
96
  props.modResults.manifest.application = application;
@@ -1,16 +1,18 @@
1
1
  import type { ExpoConfig } from '@expo/config-types';
2
2
 
3
- import type { CustomerIOPluginOptionsAndroid } from '../types/cio-types';
3
+ import type { CustomerIOPluginOptionsAndroid, NativeSDKConfig } from '../types/cio-types';
4
4
  import { withAndroidManifestUpdates } from './withAndroidManifestUpdates';
5
5
  import { withAppGoogleServices } from './withAppGoogleServices';
6
6
  import { withGistMavenRepository } from './withGistMavenRepository';
7
7
  import { withGoogleServicesJSON } from './withGoogleServicesJSON';
8
+ import { withMainApplicationModifications } from './withMainApplicationModifications';
8
9
  import { withNotificationChannelMetadata } from './withNotificationChannelMetadata';
9
10
  import { withProjectGoogleServices } from './withProjectGoogleServices';
10
11
  import { withProjectStrings } from './withProjectStrings';
11
12
 
12
13
  export function withCIOAndroid(
13
14
  config: ExpoConfig,
15
+ sdkConfig: NativeSDKConfig | undefined,
14
16
  props: CustomerIOPluginOptionsAndroid
15
17
  ): ExpoConfig {
16
18
  config = withGistMavenRepository(config, props);
@@ -18,12 +20,16 @@ export function withCIOAndroid(
18
20
  config = withAppGoogleServices(config, props);
19
21
  config = withGoogleServicesJSON(config, props);
20
22
  config = withProjectStrings(config);
21
- if (props.setHighPriorityPushHandler) {
23
+ if (props.setHighPriorityPushHandler !== undefined) {
22
24
  config = withAndroidManifestUpdates(config, props);
23
25
  }
24
26
  if (props.pushNotification?.channel) {
25
27
  config = withNotificationChannelMetadata(config, props);
26
28
  }
29
+ // Add auto initialization if sdkConfig is provided
30
+ if (sdkConfig) {
31
+ config = withMainApplicationModifications(config, sdkConfig);
32
+ }
27
33
 
28
34
  return config;
29
35
  }
@@ -1,5 +1,5 @@
1
- import { withProjectBuildGradle } from '@expo/config-plugins';
2
1
  import type { ConfigPlugin } from '@expo/config-plugins';
2
+ import { withProjectBuildGradle } from '@expo/config-plugins';
3
3
 
4
4
  import { FileManagement } from './../helpers/utils/fileManagement';
5
5
  import type { CustomerIOPluginOptionsAndroid } from './../types/cio-types';
@@ -20,7 +20,7 @@ export const withGoogleServicesJSON: ConfigPlugin<
20
20
  googleServicesFile,
21
21
  `${androidPath}/app/google-services.json`
22
22
  );
23
- } catch (e) {
23
+ } catch {
24
24
  console.log(
25
25
  `There was an error copying your google-services.json file. You can copy it manually into ${androidPath}/app/google-services.json`
26
26
  );
@@ -0,0 +1,50 @@
1
+ import type { ConfigPlugin, ExportedConfigWithProps } from '@expo/config-plugins';
2
+ import { withMainApplication } from '@expo/config-plugins';
3
+ import type { ApplicationProjectFile } from '@expo/config-plugins/build/android/Paths';
4
+ import { CIO_MAINAPPLICATION_ONCREATE_REGEX, CIO_NATIVE_SDK_INITIALIZE_CALL, CIO_NATIVE_SDK_INITIALIZE_SNIPPET } from '../helpers/constants/android';
5
+ import { PLATFORM } from '../helpers/constants/common';
6
+ import { patchNativeSDKInitializer } from '../helpers/utils/patchPluginNativeCode';
7
+ import type { NativeSDKConfig } from '../types/cio-types';
8
+ import { addCodeToMethod, addImportToFile, copyTemplateFile } from '../utils/android';
9
+
10
+ export const withMainApplicationModifications: ConfigPlugin<NativeSDKConfig> = (configOuter, sdkConfig) => {
11
+ return withMainApplication(configOuter, async (config) => {
12
+ const content = setupCustomerIOSDKInitializer(config, sdkConfig);
13
+ config.modResults.contents = content;
14
+ return config;
15
+ });
16
+ };
17
+
18
+ /**
19
+ * Setup CustomerIOSDKInitializer for Android auto initialization
20
+ */
21
+ const setupCustomerIOSDKInitializer = (
22
+ config: ExportedConfigWithProps<ApplicationProjectFile>,
23
+ sdkConfig: NativeSDKConfig,
24
+ ): string => {
25
+ const SDK_INITIALIZER_CLASS = 'CustomerIOSDKInitializer';
26
+ const SDK_INITIALIZER_PACKAGE = 'io.customer.sdk.expo';
27
+
28
+ const SDK_INITIALIZER_FILE = `${SDK_INITIALIZER_CLASS}.kt`;
29
+ const SDK_INITIALIZER_IMPORT = `import ${SDK_INITIALIZER_PACKAGE}.${SDK_INITIALIZER_CLASS}`;
30
+
31
+ let content = config.modResults.contents;
32
+
33
+ try {
34
+ // Always regenerate the CustomerIOSDKInitializer file to reflect config changes
35
+ copyTemplateFile(config, SDK_INITIALIZER_FILE, SDK_INITIALIZER_PACKAGE, (content) =>
36
+ patchNativeSDKInitializer(content, PLATFORM.ANDROID, sdkConfig)
37
+ );
38
+ // Add import if not already present
39
+ content = addImportToFile(content, SDK_INITIALIZER_IMPORT);
40
+ // Add initialization code to onCreate if not already present
41
+ if (!content.includes(CIO_NATIVE_SDK_INITIALIZE_CALL)) {
42
+ content = addCodeToMethod(content, CIO_MAINAPPLICATION_ONCREATE_REGEX, CIO_NATIVE_SDK_INITIALIZE_SNIPPET);
43
+ }
44
+ } catch (error) {
45
+ console.warn(`Could not setup ${SDK_INITIALIZER_CLASS}:`, error);
46
+ return config.modResults.contents;
47
+ }
48
+
49
+ return content;
50
+ };
@@ -19,7 +19,7 @@ const addMetadataIfNotExists = (
19
19
 
20
20
  // Check if metadata already exists
21
21
  const hasMetadata = application['meta-data'].some(
22
- (metadata) => metadata['$']['android:name'] === name
22
+ (metadata) => metadata.$['android:name'] === name
23
23
  );
24
24
 
25
25
  // Add metadata if it doesn't exist
@@ -37,11 +37,15 @@ export const withNotificationChannelMetadata: ConfigPlugin<
37
37
  CustomerIOPluginOptionsAndroid
38
38
  > = (config, props) => {
39
39
  return withAndroidManifest(config, (manifestProps) => {
40
- const application = manifestProps.modResults.manifest.application as ManifestApplication[];
40
+ const application = manifestProps.modResults.manifest
41
+ .application as ManifestApplication[];
41
42
  const channel = props.pushNotification?.channel;
42
43
 
43
44
  // Only proceed if channel configuration exists
44
- if (channel && (channel.id || channel.name || channel.importance !== undefined)) {
45
+ if (
46
+ channel &&
47
+ (channel.id || channel.name || channel.importance !== undefined)
48
+ ) {
45
49
  if (channel.id) {
46
50
  addMetadataIfNotExists(
47
51
  application[0],
@@ -1,12 +1,13 @@
1
1
  import type { ConfigPlugin } from '@expo/config-plugins';
2
2
  import { withStringsXml } from '@expo/config-plugins';
3
- import { getPluginVersion } from '../helpers/utils/pluginUtils';
3
+ import type { ResourceXML } from '@expo/config-plugins/build/android/Resources';
4
+ import { getPluginVersion } from '../utils/plugin';
4
5
 
5
6
  /**
6
7
  * Adds or updates string resources in Android's strings.xml required by the plugin
7
8
  */
8
- export const withProjectStrings: ConfigPlugin = (config) => {
9
- return withStringsXml(config, (config) => {
9
+ export const withProjectStrings: ConfigPlugin = (configOuter) => {
10
+ return withStringsXml(configOuter, (config) => {
10
11
  const stringsXml = config.modResults;
11
12
  const pluginVersion = getPluginVersion();
12
13
 
@@ -17,7 +18,10 @@ export const withProjectStrings: ConfigPlugin = (config) => {
17
18
  // can be generated correctly for Expo apps
18
19
  addStringsToXml(stringsXml, [
19
20
  { name: 'customer_io_react_native_sdk_client_source', value: 'Expo' },
20
- { name: 'customer_io_react_native_sdk_client_version', value: pluginVersion },
21
+ {
22
+ name: 'customer_io_react_native_sdk_client_version',
23
+ value: pluginVersion,
24
+ },
21
25
  ]);
22
26
 
23
27
  return config;
@@ -31,25 +35,31 @@ export const withProjectStrings: ConfigPlugin = (config) => {
31
35
  * @returns Updated strings.xml object
32
36
  */
33
37
  export function addStringsToXml(
34
- stringsXml: any,
35
- stringResources: { name: string, value: string }[]
38
+ stringsXml: ResourceXML,
39
+ stringResources: { name: string; value: string }[]
36
40
  ) {
37
41
  // Ensure the resource exists
38
42
  if (!stringsXml.resources) {
39
43
  stringsXml.resources = { string: [] };
40
44
  }
45
+ // Ensure the string array exists
46
+ if (!stringsXml.resources.string) {
47
+ stringsXml.resources.string = [];
48
+ }
41
49
 
50
+ // Get a reference to the string array after ensuring it exists
51
+ const stringArray = stringsXml.resources.string;
42
52
  stringResources.forEach(({ name, value }) => {
43
- const existingStringIndex = stringsXml.resources.string.findIndex(
44
- (item: { $: { name: string } }) => item.$?.name === name
53
+ const existingStringIndex = stringArray.findIndex(
54
+ (item) => item.$?.name === name
45
55
  );
46
56
 
47
57
  if (existingStringIndex !== -1) {
48
58
  // Update the existing string
49
- stringsXml.resources.string[existingStringIndex]._ = value;
59
+ stringArray[existingStringIndex]._ = value;
50
60
  } else {
51
61
  // Add a new string resource
52
- stringsXml.resources.string.push({
62
+ stringArray.push({
53
63
  $: { name },
54
64
  _: value,
55
65
  });
@@ -12,3 +12,10 @@ export const CIO_APP_GOOGLE_SNIPPET =
12
12
  'apply plugin: "com.google.gms.google-services" // Google Services plugin';
13
13
  export const CIO_PROJECT_GOOGLE_SNIPPET =
14
14
  ' classpath "com.google.gms:google-services:4.3.13" // Google Services plugin';
15
+
16
+ export const CIO_MAINAPPLICATION_ONCREATE_REGEX = /override\s+fun\s+onCreate\s*\(\s*\)\s*\{[\s\S]*?\}/;
17
+ // Actual method call, also used to detect if Customer.io auto initialization is already present
18
+ export const CIO_NATIVE_SDK_INITIALIZE_CALL = 'CustomerIOSDKInitializer.initialize(this)';
19
+ // Complete code snippet to inject into MainActivity.onCreate()
20
+ export const CIO_NATIVE_SDK_INITIALIZE_SNIPPET = `// Auto Initialize Native Customer.io SDK
21
+ ${CIO_NATIVE_SDK_INITIALIZE_CALL}`;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Platform constants for native SDK initialization
3
+ */
4
+ export const PLATFORM = {
5
+ IOS: 'ios',
6
+ ANDROID: 'android',
7
+ } as const;
8
+
9
+ /**
10
+ * Platform type definition
11
+ */
12
+ export type Platform = typeof PLATFORM[keyof typeof PLATFORM];
@@ -1,23 +1,15 @@
1
- const finder = require('find-package-json');
2
1
  const path = require('path');
3
2
  const resolveFrom = require('resolve-from');
4
3
 
5
- const f = finder(__dirname);
6
- let pluginPackageRoot = f.next().filename;
7
- // This is the path to the root of the customerio-expo-plugin package
8
- pluginPackageRoot = path.dirname(pluginPackageRoot);
9
-
10
- export const LOCAL_PATH_TO_CIO_NSE_FILES = path.join(
11
- pluginPackageRoot,
12
- 'plugin/src/helpers/native-files/ios'
13
- );
14
-
15
4
  export function getRelativePathToRNSDK(iosPath: string) {
16
5
  // Root path of the Expo project
17
6
  const rootAppPath = path.dirname(iosPath);
18
7
 
19
8
  // Path of the cio RN package.json file. Example: test-app/node_modules/customerio-reactnative/package.json
20
- const pluginPackageJsonPath = resolveFrom.silent(rootAppPath, `customerio-reactnative/package.json`);
9
+ const pluginPackageJsonPath = resolveFrom.silent(
10
+ rootAppPath,
11
+ `customerio-reactnative/package.json`
12
+ );
21
13
 
22
14
  // Example: ../node_modules/customerio-reactnative
23
15
  return path.relative(iosPath, path.dirname(pluginPackageJsonPath));
@@ -170,4 +162,10 @@ export const CIO_REGISTER_PUSHNOTIFICATION_SNIPPET_v2 = `
170
162
  }
171
163
  }`;
172
164
 
173
- export const CIO_REGISTER_PUSH_NOTIFICATION_PLACEHOLDER = /\{\{REGISTER_SNIPPET\}\}/;
165
+ export const CIO_REGISTER_PUSH_NOTIFICATION_PLACEHOLDER = /\{\{REGISTER_SNIPPET\}\}/;
166
+ // Regex to match MessagingPush initialization in AppDelegate (different from NSE initialization)
167
+ export const CIO_MESSAGING_PUSH_APP_DELEGATE_INIT_REGEX = /(MessagingPush(?:APN|FCM)\.initialize)/;
168
+ export const CIO_NATIVE_SDK_INITIALIZE_CALL = 'CustomerIOSDKInitializer.initialize()';
169
+ export const CIO_NATIVE_SDK_INITIALIZE_SNIPPET = `// Auto Initialize Native Customer.io SDK
170
+ ${CIO_NATIVE_SDK_INITIALIZE_CALL}
171
+ `;
@@ -0,0 +1,64 @@
1
+ package io.customer.sdk.expo
2
+
3
+ import android.app.Application
4
+ import io.customer.datapipelines.config.ScreenView
5
+ import io.customer.messaginginapp.MessagingInAppModuleConfig
6
+ import io.customer.messaginginapp.ModuleMessagingInApp
7
+ import io.customer.messagingpush.MessagingPushModuleConfig
8
+ import io.customer.messagingpush.ModuleMessagingPushFCM
9
+ import io.customer.reactnative.sdk.messaginginapp.ReactInAppEventListener
10
+ import io.customer.sdk.CustomerIOBuilder
11
+ import io.customer.sdk.core.util.CioLogLevel
12
+ import io.customer.sdk.data.model.Region
13
+
14
+ object CustomerIOSDKInitializer {
15
+ fun initialize(application: Application) = with(
16
+ CustomerIOBuilder(application, "{{CDP_API_KEY}}")
17
+ ) {
18
+ val siteId: String? = {{SITE_ID}}
19
+ val migrationSiteId: String? = {{MIGRATION_SITE_ID}}
20
+ val region = Region.getRegion({{REGION}})
21
+
22
+ setIfDefined({{LOG_LEVEL}}, CustomerIOBuilder::logLevel) { CioLogLevel.getLogLevel(it) }
23
+ setIfDefined(region, CustomerIOBuilder::region)
24
+ setIfDefined({{AUTO_TRACK_DEVICE_ATTRIBUTES}}, CustomerIOBuilder::autoTrackDeviceAttributes)
25
+ setIfDefined({{TRACK_APPLICATION_LIFECYCLE_EVENTS}}, CustomerIOBuilder::trackApplicationLifecycleEvents)
26
+ setIfDefined({{SCREEN_VIEW_USE}}, CustomerIOBuilder::screenViewUse) { ScreenView.getScreenView(it) }
27
+ setIfDefined(migrationSiteId, CustomerIOBuilder::migrationSiteId)
28
+
29
+ // Add messaging modules if siteId is provided
30
+ if (!(siteId.isNullOrBlank())) {
31
+ addCustomerIOModule(
32
+ ModuleMessagingInApp(
33
+ MessagingInAppModuleConfig.Builder(siteId, region)
34
+ .setEventListener(ReactInAppEventListener())
35
+ .build()
36
+ )
37
+ )
38
+ }
39
+ addCustomerIOModule(
40
+ ModuleMessagingPushFCM(
41
+ MessagingPushModuleConfig.Builder().build()
42
+ )
43
+ )
44
+
45
+ build()
46
+ }
47
+ }
48
+
49
+ // Apply a value after transforming it, only if both the original and transformed values are non-nil
50
+ private inline fun <R, T> CustomerIOBuilder.setIfDefined(
51
+ value: R?,
52
+ block: CustomerIOBuilder.(T) -> CustomerIOBuilder,
53
+ transform: (R) -> T,
54
+ ): CustomerIOBuilder = value?.let { block(transform(it)) } ?: this
55
+
56
+ // Apply a value to a setter only if it's non-nil
57
+ private inline fun <T> CustomerIOBuilder.setIfDefined(
58
+ value: T?,
59
+ block: CustomerIOBuilder.(T) -> CustomerIOBuilder,
60
+ ): CustomerIOBuilder = setIfDefined(
61
+ value = value,
62
+ block = block,
63
+ transform = { it },
64
+ )