doopush-react-native-sdk 0.1.2 → 0.5.2

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 (41) hide show
  1. package/README.md +71 -34
  2. package/android/build.gradle +9 -7
  3. package/android/src/main/java/com/doopush/reactnative/DooPushReactNativeSDKModule.kt +188 -28
  4. package/build/DooPush.d.ts +44 -7
  5. package/build/DooPush.d.ts.map +1 -1
  6. package/build/DooPush.js +76 -1
  7. package/build/DooPush.js.map +1 -1
  8. package/build/events.d.ts +7 -4
  9. package/build/events.d.ts.map +1 -1
  10. package/build/events.js +5 -2
  11. package/build/events.js.map +1 -1
  12. package/build/hooks.d.ts +34 -0
  13. package/build/hooks.d.ts.map +1 -0
  14. package/build/hooks.js +138 -0
  15. package/build/hooks.js.map +1 -0
  16. package/build/index.d.ts +1 -0
  17. package/build/index.d.ts.map +1 -1
  18. package/build/index.js +1 -0
  19. package/build/index.js.map +1 -1
  20. package/build/types.d.ts +28 -1
  21. package/build/types.d.ts.map +1 -1
  22. package/build/types.js.map +1 -1
  23. package/ios/DooPushReactNativeSDK.podspec +1 -3
  24. package/ios/DooPushReactNativeSDKModule.swift +169 -4
  25. package/package.json +13 -8
  26. package/plugin/build/android/withAndroid.js +6 -0
  27. package/plugin/build/android/withAppBuildGradle.js +71 -30
  28. package/plugin/build/android/withGoogleServices.js +74 -12
  29. package/plugin/build/android/withGradleProperties.d.ts +3 -0
  30. package/plugin/build/android/withGradleProperties.js +26 -0
  31. package/plugin/build/android/withOppoManifest.d.ts +3 -0
  32. package/plugin/build/android/withOppoManifest.js +61 -0
  33. package/plugin/build/android/withRootBuildGradle.d.ts +2 -2
  34. package/plugin/build/android/withRootBuildGradle.js +65 -10
  35. package/plugin/build/android/withSettingsGradle.d.ts +8 -0
  36. package/plugin/build/android/withSettingsGradle.js +33 -0
  37. package/plugin/build/ios/withIOS.js +2 -0
  38. package/plugin/build/ios/withPodfile.d.ts +8 -0
  39. package/plugin/build/ios/withPodfile.js +48 -0
  40. package/plugin/build/schema.d.ts +327 -1
  41. package/plugin/build/schema.js +82 -1
@@ -4,10 +4,10 @@ import UserNotifications
4
4
 
5
5
  /**
6
6
  * DooPush React Native SDK — iOS bridge
7
- * v0.1.0 alpha
7
+ * v0.5.0
8
8
  *
9
9
  * Mode: ACTIVE (default) — DooPush owns UNUserNotificationCenterDelegate via the
10
- * delegate-forwarding mechanism added in DooPushSDK v1.1.0. Coexists with
10
+ * delegate-forwarding mechanism in DooPushSDK. Coexists with
11
11
  * expo-notifications because both delegates forward to each other.
12
12
  */
13
13
  public class DooPushReactNativeSDKModule: Module, DooPushDelegate {
@@ -15,7 +15,16 @@ public class DooPushReactNativeSDKModule: Module, DooPushDelegate {
15
15
  public func definition() -> ModuleDefinition {
16
16
  Name("DooPushReactNativeSDK")
17
17
 
18
- Events("onRegister", "onRegisterError", "onMessage")
18
+ Events(
19
+ "onRegister",
20
+ "onRegisterError",
21
+ "onMessage",
22
+ "onNotificationClick",
23
+ "onNotificationOpen",
24
+ "onGatewayOpen",
25
+ "onGatewayClosed",
26
+ "onGatewayError"
27
+ )
19
28
 
20
29
  OnCreate {
21
30
  // Wire DooPush's delegate to this module so we can forward events to JS.
@@ -61,7 +70,12 @@ public class DooPushReactNativeSDKModule: Module, DooPushDelegate {
61
70
 
62
71
  // ── registerWithToken ───────────────────────────────────────────
63
72
  AsyncFunction("registerWithToken") { (token: String, vendor: String, promise: Promise) in
64
- DooPushManager.shared.registerDevice(withToken: token, vendor: vendor) { deviceId, error in
73
+ let allowedVendors: Set<String> = ["apns", "fcm", "hms", "honor", "xiaomi", "oppo", "vivo", "meizu"]
74
+ guard allowedVendors.contains(vendor.lowercased()) else {
75
+ promise.reject("E_INVALID_VENDOR", "vendor must be one of: \(allowedVendors.sorted().joined(separator: ", "))")
76
+ return
77
+ }
78
+ DooPushManager.shared.registerDevice(withToken: token, vendor: vendor.lowercased()) { deviceId, error in
65
79
  if let error = error {
66
80
  promise.reject("E_REGISTER", error.localizedDescription)
67
81
  return
@@ -78,6 +92,97 @@ public class DooPushReactNativeSDKModule: Module, DooPushDelegate {
78
92
  AsyncFunction("getDeviceId") { () -> String? in
79
93
  return DooPushManager.shared.getDeviceId()
80
94
  }
95
+
96
+ AsyncFunction("getDeviceInfo") { () -> [String: Any] in
97
+ return normalizeDeviceInfo(DooPushManager.shared.getDeviceInfo())
98
+ }
99
+
100
+ AsyncFunction("updateDeviceInfo") { (promise: Promise) in
101
+ DooPushManager.shared.updateDeviceInfo()
102
+ promise.resolve(nil)
103
+ }
104
+
105
+ AsyncFunction("reportStatistics") { (promise: Promise) in
106
+ DooPushManager.shared.reportStatistics()
107
+ promise.resolve(nil)
108
+ }
109
+
110
+ AsyncFunction("checkPermissionStatus") { (promise: Promise) in
111
+ DooPushManager.shared.checkPushPermissionStatus { status in
112
+ promise.resolve(self.normalizePermissionStatus(status))
113
+ }
114
+ }
115
+
116
+ AsyncFunction("setBadge") { (count: Int, promise: Promise) in
117
+ guard count >= 0 else {
118
+ promise.reject("E_BADGE", "badge count must be >= 0")
119
+ return
120
+ }
121
+ DooPushManager.shared.setBadgeNumber(count) { error in
122
+ if let error = error {
123
+ promise.reject("E_BADGE", error.localizedDescription)
124
+ } else {
125
+ promise.resolve(true)
126
+ }
127
+ }
128
+ }
129
+
130
+ AsyncFunction("clearBadge") { (promise: Promise) in
131
+ DooPushManager.shared.clearBadge { error in
132
+ if let error = error {
133
+ promise.reject("E_BADGE", error.localizedDescription)
134
+ } else {
135
+ promise.resolve(true)
136
+ }
137
+ }
138
+ }
139
+
140
+ AsyncFunction("getBadge") { () -> Int in
141
+ return DooPushManager.shared.getCurrentBadgeNumber()
142
+ }
143
+
144
+ // ── notification management / coexistence ───────────────────────
145
+ Function("setNotificationManagementMode") { (mode: String) in
146
+ switch mode.lowercased() {
147
+ case "active":
148
+ DooPushManager.shared.setNotificationManagementMode(.active)
149
+ DooPushManager.shared.enableAutomaticNotificationTracking()
150
+ case "passive":
151
+ DooPushManager.shared.setNotificationManagementMode(.passive)
152
+ DooPushManager.shared.disableAutomaticNotificationTracking()
153
+ default:
154
+ throw NSError(
155
+ domain: "DooPushReactNativeSDK",
156
+ code: -2,
157
+ userInfo: [NSLocalizedDescriptionKey: "mode must be 'active' or 'passive'"]
158
+ )
159
+ }
160
+ }
161
+
162
+ Function("setExpoNotificationRelayEnabled") { (_ enabled: Bool) in
163
+ // iOS uses UNUserNotificationCenter delegate forwarding; no explicit relay flag is needed.
164
+ }
165
+
166
+ Function("setNotificationDisplayEnabled") { (enabled: Bool) in
167
+ // iOS notification presentation is controlled by UNUserNotificationCenter and APNs.
168
+ // Keep this API as a no-op for cross-platform compatibility; use
169
+ // setNotificationManagementMode('passive') to disable DooPush delegate tracking.
170
+ NSLog("[DooPushReactNativeSDK] setNotificationDisplayEnabled(\(enabled)) is a no-op on iOS")
171
+ }
172
+
173
+ AsyncFunction("connectGateway") { (promise: Promise) in
174
+ guard DooPushManager.shared.getDeviceToken()?.isEmpty == false else {
175
+ promise.reject("E_GATEWAY", "device token is required before connecting gateway")
176
+ return
177
+ }
178
+ DooPushManager.shared.connectWebSocket()
179
+ promise.resolve(nil)
180
+ }
181
+
182
+ AsyncFunction("disconnectGateway") { (promise: Promise) in
183
+ DooPushManager.shared.disconnectWebSocket()
184
+ promise.resolve(nil)
185
+ }
81
186
  }
82
187
 
83
188
  // MARK: - DooPushDelegate
@@ -101,8 +206,68 @@ public class DooPushReactNativeSDKModule: Module, DooPushDelegate {
101
206
  ])
102
207
  }
103
208
 
209
+ public func dooPush(_ manager: DooPushManager, didClickNotification userInfo: [AnyHashable: Any]) {
210
+ sendEvent("onNotificationClick", normalizeMessage(userInfo))
211
+ }
212
+
213
+ public func dooPush(_ manager: DooPushManager, didOpenNotification userInfo: [AnyHashable: Any]) {
214
+ sendEvent("onNotificationOpen", normalizeMessage(userInfo))
215
+ }
216
+
217
+ public func dooPushGatewayDidOpen(_ manager: DooPushManager) {
218
+ sendEvent("onGatewayOpen", ["connected": true])
219
+ }
220
+
221
+ public func dooPush(_ manager: DooPushManager, gatewayDidCloseWithCode code: Int, reason: String?) {
222
+ var payload: [String: Any] = ["code": code]
223
+ if let reason = reason {
224
+ payload["reason"] = reason
225
+ }
226
+ sendEvent("onGatewayClosed", payload)
227
+ }
228
+
229
+ public func dooPush(_ manager: DooPushManager, gatewayDidFailWithError error: Error) {
230
+ sendEvent("onGatewayError", [
231
+ "code": "E_GATEWAY",
232
+ "message": error.localizedDescription
233
+ ])
234
+ }
235
+
104
236
  // MARK: - Helpers
105
237
 
238
+
239
+ private func normalizeDeviceInfo(_ deviceInfo: DeviceInfo) -> [String: Any] {
240
+ return [
241
+ "platform": deviceInfo.platform,
242
+ "channel": deviceInfo.channel,
243
+ "bundleId": deviceInfo.bundleId,
244
+ "brand": deviceInfo.brand,
245
+ "model": deviceInfo.model,
246
+ "systemVersion": deviceInfo.systemVersion,
247
+ "appVersion": deviceInfo.appVersion,
248
+ "userAgent": deviceInfo.userAgent
249
+ ]
250
+ }
251
+
252
+ private func normalizePermissionStatus(_ status: UNAuthorizationStatus) -> String {
253
+ if #available(iOS 14.0, *), status == .ephemeral {
254
+ return "ephemeral"
255
+ }
256
+
257
+ switch status {
258
+ case .authorized:
259
+ return "authorized"
260
+ case .denied:
261
+ return "denied"
262
+ case .notDetermined:
263
+ return "notDetermined"
264
+ case .provisional:
265
+ return "provisional"
266
+ @unknown default:
267
+ return "unknown"
268
+ }
269
+ }
270
+
106
271
  /// Convert APNs userInfo into the JS-side DooPushMessage shape.
107
272
  private func normalizeMessage(_ userInfo: [AnyHashable: Any]) -> [String: Any] {
108
273
  var data: [String: String] = [:]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "doopush-react-native-sdk",
3
- "version": "0.1.2",
3
+ "version": "0.5.2",
4
4
  "description": "React Native SDK for DooPush push notification service. Built with Expo Modules API; works in Expo (managed/prebuild) and bare RN.",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -13,6 +13,14 @@
13
13
  "app.plugin.js",
14
14
  "README.md"
15
15
  ],
16
+ "scripts": {
17
+ "build": "tsc && pnpm --filter ./plugin build",
18
+ "build:plugin": "tsc -p plugin/tsconfig.json",
19
+ "test": "pnpm --filter ./plugin test",
20
+ "clean": "rm -rf build plugin/build",
21
+ "prepare": "pnpm build",
22
+ "prepublishOnly": "pnpm clean && pnpm build && pnpm test"
23
+ },
16
24
  "keywords": [
17
25
  "react-native",
18
26
  "expo",
@@ -26,6 +34,9 @@
26
34
  "url": "https://github.com/doopush/doopush-react-native-sdk"
27
35
  },
28
36
  "license": "MIT",
37
+ "dependencies": {
38
+ "zod": "^3.22.0"
39
+ },
29
40
  "peerDependencies": {
30
41
  "expo": "*",
31
42
  "react": "*",
@@ -38,11 +49,5 @@
38
49
  "react": "18.2.0",
39
50
  "react-native": "0.73.0",
40
51
  "typescript": "^5.3.0"
41
- },
42
- "scripts": {
43
- "build": "tsc && pnpm --filter ./plugin build",
44
- "build:plugin": "tsc -p plugin/tsconfig.json",
45
- "test": "pnpm --filter ./plugin test",
46
- "clean": "rm -rf build plugin/build"
47
52
  }
48
- }
53
+ }
@@ -2,12 +2,18 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.withAndroid = void 0;
4
4
  const withRootBuildGradle_1 = require("./withRootBuildGradle");
5
+ const withSettingsGradle_1 = require("./withSettingsGradle");
5
6
  const withAppBuildGradle_1 = require("./withAppBuildGradle");
7
+ const withGradleProperties_1 = require("./withGradleProperties");
6
8
  const withGoogleServices_1 = require("./withGoogleServices");
9
+ const withOppoManifest_1 = require("./withOppoManifest");
7
10
  const withAndroid = (config, validated) => {
11
+ config = (0, withSettingsGradle_1.withDooPushSettingsGradle)(config, validated);
12
+ config = (0, withGradleProperties_1.withDooPushGradleProperties)(config, validated);
8
13
  config = (0, withRootBuildGradle_1.withDooPushRootBuildGradle)(config, validated);
9
14
  config = (0, withAppBuildGradle_1.withDooPushAppBuildGradle)(config, validated);
10
15
  config = (0, withGoogleServices_1.withDooPushGoogleServices)(config, validated);
16
+ config = (0, withOppoManifest_1.withDooPushOppoManifest)(config, validated);
11
17
  return config;
12
18
  };
13
19
  exports.withAndroid = withAndroid;
@@ -2,39 +2,80 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.withDooPushAppBuildGradle = void 0;
4
4
  const config_plugins_1 = require("@expo/config-plugins");
5
- const VENDOR_PLACEHOLDER_KEYS = [
6
- 'DOOPUSH_MI_APP_ID',
7
- 'DOOPUSH_MI_APP_KEY',
8
- 'DOOPUSH_OPPO_APP_KEY',
9
- 'DOOPUSH_OPPO_APP_SECRET',
10
- 'DOOPUSH_VIVO_APP_ID',
11
- 'DOOPUSH_VIVO_API_KEY',
12
- 'DOOPUSH_MEIZU_APP_ID',
13
- 'DOOPUSH_MEIZU_APP_KEY',
14
- 'DOOPUSH_HONOR_APP_ID',
15
- 'DOOPUSH_HONOR_DEVELOPER_ID',
16
- ];
5
+ function q(value) {
6
+ return `"${(value !== null && value !== void 0 ? value : '').replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
7
+ }
8
+ function addApplyPlugin(contents, pluginId) {
9
+ const line = `apply plugin: '${pluginId}'`;
10
+ if (contents.includes(line) || contents.includes(`id '${pluginId}'`) || contents.includes(`id("${pluginId}")`)) {
11
+ return contents;
12
+ }
13
+ return `${contents.trimEnd()}\n${line}\n`;
14
+ }
15
+ function addDependency(contents, dependency) {
16
+ var _a, _b;
17
+ const artifact = (_b = (_a = dependency.match(/'([^:]+:[^:]+):/)) === null || _a === void 0 ? void 0 : _a[1]) !== null && _b !== void 0 ? _b : dependency;
18
+ if (contents.includes(artifact))
19
+ return contents;
20
+ return contents.replace(/dependencies\s*{/, `dependencies {\n ${dependency}`);
21
+ }
22
+ function mergeManifestPlaceholders(contents, placeholderValues) {
23
+ const entries = Object.entries(placeholderValues)
24
+ .map(([key, value]) => `${key}: ${q(value)}`);
25
+ if (entries.length === 0)
26
+ return contents;
27
+ const doopushMap = entries.join(', ');
28
+ const doopushLine = `manifestPlaceholders += [${doopushMap}]`;
29
+ // Idempotency: if all DooPush keys already exist somewhere, do not inject again.
30
+ if (entries.every((entry) => contents.includes(entry.split(':')[0]))) {
31
+ return contents;
32
+ }
33
+ const defaultConfigMatch = contents.match(/defaultConfig\s*{/);
34
+ if (!defaultConfigMatch || defaultConfigMatch.index === undefined) {
35
+ return contents;
36
+ }
37
+ const insertAt = defaultConfigMatch.index + defaultConfigMatch[0].length;
38
+ return `${contents.slice(0, insertAt)}\n ${doopushLine}${contents.slice(insertAt)}`;
39
+ }
17
40
  const withDooPushAppBuildGradle = (config, validated) => {
18
41
  return (0, config_plugins_1.withAppBuildGradle)(config, (cfg) => {
42
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
19
43
  let contents = cfg.modResults.contents;
20
- // 1. Apply google-services plugin if FCM enabled
21
- if (validated.android.vendors.fcm) {
22
- if (!contents.includes("apply plugin: 'com.google.gms.google-services'")) {
23
- contents += `\napply plugin: 'com.google.gms.google-services'\n`;
24
- }
25
- }
26
- // 2. Inject manifestPlaceholders block (defaultConfig).
27
- // For v0.1 alpha, all OEM vendors get empty string placeholders.
28
- const placeholders = VENDOR_PLACEHOLDER_KEYS
29
- .map((k) => ` ${k}: ""`)
30
- .join(',\n');
31
- const placeholderBlock = `manifestPlaceholders = [\n${placeholders}\n ]`;
32
- if (!contents.includes('manifestPlaceholders')) {
33
- contents = contents.replace(/defaultConfig\s*{/, `defaultConfig {\n ${placeholderBlock}`);
34
- }
35
- // 3. Inject DooPush Android SDK dependency if not present
36
- if (!contents.includes('com.github.doopush:doopush-android-sdk')) {
37
- contents = contents.replace(/dependencies\s*{/, `dependencies {\n implementation 'com.github.doopush:doopush-android-sdk:1.1.+'`);
44
+ const v = validated.android.vendors;
45
+ // 1. Apply vendor Gradle plugins where required by the upstream SDKs.
46
+ if (v.fcm)
47
+ contents = addApplyPlugin(contents, 'com.google.gms.google-services');
48
+ if (v.hms)
49
+ contents = addApplyPlugin(contents, 'com.huawei.agconnect');
50
+ if (v.honor)
51
+ contents = addApplyPlugin(contents, 'com.hihonor.mcs.asplugin');
52
+ // 2. Merge DooPush manifest placeholders into defaultConfig, even if the host
53
+ // project or another plugin already defines manifestPlaceholders.
54
+ contents = mergeManifestPlaceholders(contents, {
55
+ DOOPUSH_MI_APP_ID: (_a = v.xiaomi) === null || _a === void 0 ? void 0 : _a.appId,
56
+ DOOPUSH_MI_APP_KEY: (_b = v.xiaomi) === null || _b === void 0 ? void 0 : _b.appKey,
57
+ DOOPUSH_OPPO_APP_KEY: (_c = v.oppo) === null || _c === void 0 ? void 0 : _c.appKey,
58
+ DOOPUSH_OPPO_APP_SECRET: (_d = v.oppo) === null || _d === void 0 ? void 0 : _d.appSecret,
59
+ DOOPUSH_VIVO_APP_ID: (_e = v.vivo) === null || _e === void 0 ? void 0 : _e.appId,
60
+ DOOPUSH_VIVO_API_KEY: (_f = v.vivo) === null || _f === void 0 ? void 0 : _f.apiKey,
61
+ DOOPUSH_MEIZU_APP_ID: (_g = v.meizu) === null || _g === void 0 ? void 0 : _g.appId,
62
+ DOOPUSH_MEIZU_APP_KEY: (_h = v.meizu) === null || _h === void 0 ? void 0 : _h.appKey,
63
+ DOOPUSH_HONOR_APP_ID: (_j = v.honor) === null || _j === void 0 ? void 0 : _j.appId,
64
+ DOOPUSH_HONOR_DEVELOPER_ID: (_k = v.honor) === null || _k === void 0 ? void 0 : _k.developerId,
65
+ });
66
+ // 3. Inject DooPush Android SDK dependency if not present.
67
+ contents = addDependency(contents, "implementation 'com.doopush:android-sdk:1.2.0'");
68
+ const vendorDependencies = [
69
+ [!!v.hms, "implementation 'com.huawei.hms:push:6.11.0.300'"],
70
+ [!!v.honor, "implementation 'com.hihonor.mcs:push:8.0.12.307'"],
71
+ [!!v.xiaomi, "implementation 'com.umeng.umsdk:xiaomi-push:6.0.1'"],
72
+ [!!v.oppo, "implementation 'com.umeng.umsdk:oppo-push:3.5.3'"],
73
+ [!!v.vivo, "implementation 'com.umeng.umsdk:vivo-push:4.0.6.0'"],
74
+ [!!v.meizu, "implementation 'com.umeng.umsdk:meizu-push:5.0.3'"],
75
+ ];
76
+ for (const [enabled, dependency] of vendorDependencies) {
77
+ if (enabled)
78
+ contents = addDependency(contents, dependency);
38
79
  }
39
80
  cfg.modResults.contents = contents;
40
81
  return cfg;
@@ -37,25 +37,87 @@ exports.withDooPushGoogleServices = void 0;
37
37
  const config_plugins_1 = require("@expo/config-plugins");
38
38
  const fs = __importStar(require("fs"));
39
39
  const path = __importStar(require("path"));
40
+ function addCopy(jobs, source, destRelative, label) {
41
+ if (source)
42
+ jobs.push({ source, destRelative, label });
43
+ }
44
+ function addWrite(jobs, contents, destRelative, label) {
45
+ const filtered = Object.fromEntries(Object.entries(contents).filter(([, value]) => value !== undefined && value !== ''));
46
+ if (Object.keys(filtered).length > 0)
47
+ jobs.push({ contents: filtered, destRelative, label });
48
+ }
40
49
  const withDooPushGoogleServices = (config, validated) => {
41
- if (!validated.android.vendors.fcm) {
42
- return config; // No FCM vendor → no file to copy
50
+ var _a, _b, _c, _d, _e, _f, _g;
51
+ const vendors = validated.android.vendors;
52
+ const copyJobs = [];
53
+ const writeJobs = [];
54
+ addCopy(copyJobs, (_a = vendors.fcm) === null || _a === void 0 ? void 0 : _a.googleServicesFile, 'google-services.json', 'google-services.json');
55
+ if ((_b = vendors.hms) === null || _b === void 0 ? void 0 : _b.agconnectServicesFile) {
56
+ addCopy(copyJobs, vendors.hms.agconnectServicesFile, 'agconnect-services.json', 'agconnect-services.json');
57
+ addCopy(copyJobs, vendors.hms.agconnectServicesFile, path.join('src', 'main', 'assets', 'agconnect-services.json'), 'agconnect-services.json');
58
+ }
59
+ if ((_c = vendors.honor) === null || _c === void 0 ? void 0 : _c.mcsServicesFile) {
60
+ addCopy(copyJobs, vendors.honor.mcsServicesFile, 'mcs-services.json', 'mcs-services.json');
61
+ addCopy(copyJobs, vendors.honor.mcsServicesFile, path.join('src', 'main', 'assets', 'mcs-services.json'), 'mcs-services.json');
62
+ }
63
+ else if (vendors.honor) {
64
+ const honorJson = {
65
+ client_id: vendors.honor.clientId,
66
+ client_secret: vendors.honor.clientSecret,
67
+ app_id: vendors.honor.appId,
68
+ developer_id: vendors.honor.developerId,
69
+ };
70
+ addWrite(writeJobs, honorJson, 'mcs-services.json', 'mcs-services.json');
71
+ addWrite(writeJobs, honorJson, path.join('src', 'main', 'assets', 'mcs-services.json'), 'mcs-services.json');
72
+ }
73
+ if ((_d = vendors.xiaomi) === null || _d === void 0 ? void 0 : _d.servicesFile) {
74
+ addCopy(copyJobs, vendors.xiaomi.servicesFile, path.join('src', 'main', 'assets', 'xiaomi-services.json'), 'xiaomi-services.json');
75
+ }
76
+ else if (vendors.xiaomi) {
77
+ addWrite(writeJobs, { app_id: vendors.xiaomi.appId, app_key: vendors.xiaomi.appKey }, path.join('src', 'main', 'assets', 'xiaomi-services.json'), 'xiaomi-services.json');
78
+ }
79
+ if ((_e = vendors.oppo) === null || _e === void 0 ? void 0 : _e.servicesFile) {
80
+ addCopy(copyJobs, vendors.oppo.servicesFile, path.join('src', 'main', 'assets', 'oppo-services.json'), 'oppo-services.json');
81
+ }
82
+ else if (vendors.oppo) {
83
+ addWrite(writeJobs, { app_key: vendors.oppo.appKey, app_secret: vendors.oppo.appSecret }, path.join('src', 'main', 'assets', 'oppo-services.json'), 'oppo-services.json');
84
+ }
85
+ if ((_f = vendors.vivo) === null || _f === void 0 ? void 0 : _f.servicesFile) {
86
+ addCopy(copyJobs, vendors.vivo.servicesFile, path.join('src', 'main', 'assets', 'vivo-services.json'), 'vivo-services.json');
87
+ }
88
+ else if (vendors.vivo) {
89
+ addWrite(writeJobs, { app_id: vendors.vivo.appId, api_key: vendors.vivo.apiKey }, path.join('src', 'main', 'assets', 'vivo-services.json'), 'vivo-services.json');
90
+ }
91
+ if ((_g = vendors.meizu) === null || _g === void 0 ? void 0 : _g.servicesFile) {
92
+ addCopy(copyJobs, vendors.meizu.servicesFile, path.join('src', 'main', 'assets', 'meizu-services.json'), 'meizu-services.json');
93
+ }
94
+ else if (vendors.meizu) {
95
+ addWrite(writeJobs, { app_id: vendors.meizu.appId, app_key: vendors.meizu.appKey }, path.join('src', 'main', 'assets', 'meizu-services.json'), 'meizu-services.json');
96
+ }
97
+ if (copyJobs.length === 0 && writeJobs.length === 0) {
98
+ return config;
43
99
  }
44
- const sourcePath = validated.android.vendors.fcm.googleServicesFile;
45
100
  return (0, config_plugins_1.withDangerousMod)(config, [
46
101
  'android',
47
102
  async (cfg) => {
48
103
  const projectRoot = cfg.modRequest.projectRoot;
49
- const absSource = path.isAbsolute(sourcePath)
50
- ? sourcePath
51
- : path.resolve(projectRoot, sourcePath);
52
- if (!fs.existsSync(absSource)) {
53
- throw new Error(`[doopush-react-native-sdk] google-services.json not found at: ${absSource}`);
104
+ const appDir = path.join(cfg.modRequest.platformProjectRoot, 'app');
105
+ for (const job of copyJobs) {
106
+ const absSource = path.isAbsolute(job.source)
107
+ ? job.source
108
+ : path.resolve(projectRoot, job.source);
109
+ if (!fs.existsSync(absSource)) {
110
+ throw new Error(`[doopush-react-native-sdk] ${job.label} not found at: ${absSource}`);
111
+ }
112
+ const dest = path.join(appDir, job.destRelative);
113
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
114
+ fs.copyFileSync(absSource, dest);
115
+ }
116
+ for (const job of writeJobs) {
117
+ const dest = path.join(appDir, job.destRelative);
118
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
119
+ fs.writeFileSync(dest, `${JSON.stringify(job.contents, null, 2)}\n`);
54
120
  }
55
- const destDir = path.join(cfg.modRequest.platformProjectRoot, 'app');
56
- fs.mkdirSync(destDir, { recursive: true });
57
- const dest = path.join(destDir, 'google-services.json');
58
- fs.copyFileSync(absSource, dest);
59
121
  return cfg;
60
122
  },
61
123
  ]);
@@ -0,0 +1,3 @@
1
+ import { ConfigPlugin } from '@expo/config-plugins';
2
+ import type { PluginConfig } from '../schema';
3
+ export declare const withDooPushGradleProperties: ConfigPlugin<PluginConfig>;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withDooPushGradleProperties = void 0;
4
+ const config_plugins_1 = require("@expo/config-plugins");
5
+ const MIN_SDK_KEY = 'android.minSdkVersion';
6
+ const DOOPUSH_MIN_SDK = 26;
7
+ const withDooPushGradleProperties = (config) => {
8
+ return (0, config_plugins_1.withGradleProperties)(config, (cfg) => {
9
+ const props = cfg.modResults;
10
+ const existing = props.find((item) => item.type === 'property' && item.key === MIN_SDK_KEY);
11
+ if ((existing === null || existing === void 0 ? void 0 : existing.type) === 'property') {
12
+ const parsed = Number.parseInt(existing.value, 10);
13
+ if (!Number.isNaN(parsed) && parsed >= DOOPUSH_MIN_SDK)
14
+ return cfg;
15
+ existing.value = String(DOOPUSH_MIN_SDK);
16
+ return cfg;
17
+ }
18
+ props.push({
19
+ type: 'property',
20
+ key: MIN_SDK_KEY,
21
+ value: String(DOOPUSH_MIN_SDK),
22
+ });
23
+ return cfg;
24
+ });
25
+ };
26
+ exports.withDooPushGradleProperties = withDooPushGradleProperties;
@@ -0,0 +1,3 @@
1
+ import { ConfigPlugin } from '@expo/config-plugins';
2
+ import type { PluginConfig } from '../schema';
3
+ export declare const withDooPushOppoManifest: ConfigPlugin<PluginConfig>;
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withDooPushOppoManifest = void 0;
4
+ const config_plugins_1 = require("@expo/config-plugins");
5
+ /**
6
+ * HeyTap MCS(OPPO/ColorOS 推送)要求在 AndroidManifest 声明的权限与回调 service。
7
+ *
8
+ * umeng `oppo-push` 的 aar 自带 manifest 为空(只声明 package),不会贡献这些节点;
9
+ * 其余 vendor 的 aar 自带完整 manifest,所以只有 OPPO 需要 plugin 主动补。缺这些节点时,
10
+ * 系统推送服务收到注册后无处回传 RegisterId,DooPush.register() 会静默超时、拿不到 token。
11
+ */
12
+ const MCS_PERMISSIONS = [
13
+ 'com.coloros.mcs.permission.RECIEVE_MCS_MESSAGE',
14
+ 'com.heytap.mcs.permission.RECIEVE_MCS_MESSAGE',
15
+ ];
16
+ const MCS_SERVICES = [
17
+ {
18
+ name: 'com.heytap.msp.push.service.CompatibleDataMessageCallbackService',
19
+ permission: 'com.coloros.mcs.permission.SEND_MCS_MESSAGE',
20
+ actions: ['com.coloros.mcs.action.RECEIVE_MCS_MESSAGE'],
21
+ },
22
+ {
23
+ name: 'com.heytap.msp.push.service.DataMessageCallbackService',
24
+ permission: 'com.heytap.mcs.permission.SEND_PUSH_MESSAGE',
25
+ actions: [
26
+ 'com.heytap.mcs.action.RECEIVE_MCS_MESSAGE',
27
+ 'com.heytap.msp.push.RECEIVE_MCS_MESSAGE',
28
+ ],
29
+ },
30
+ ];
31
+ const withDooPushOppoManifest = (config, validated) => {
32
+ // service 类来自 oppo-push 依赖,仅在启用 OPPO vendor 时注入。
33
+ if (!validated.android.vendors.oppo) {
34
+ return config;
35
+ }
36
+ return (0, config_plugins_1.withAndroidManifest)(config, (cfg) => {
37
+ var _a;
38
+ config_plugins_1.AndroidConfig.Permissions.ensurePermissions(cfg.modResults, MCS_PERMISSIONS);
39
+ const application = config_plugins_1.AndroidConfig.Manifest.getMainApplication(cfg.modResults);
40
+ if (!application)
41
+ return cfg;
42
+ application.service = (_a = application.service) !== null && _a !== void 0 ? _a : [];
43
+ for (const svc of MCS_SERVICES) {
44
+ const exists = application.service.some((s) => { var _a; return ((_a = s.$) === null || _a === void 0 ? void 0 : _a['android:name']) === svc.name; });
45
+ if (exists)
46
+ continue;
47
+ application.service.push({
48
+ $: {
49
+ 'android:name': svc.name,
50
+ 'android:permission': svc.permission,
51
+ 'android:exported': 'true',
52
+ },
53
+ 'intent-filter': [
54
+ { action: svc.actions.map((a) => ({ $: { 'android:name': a } })) },
55
+ ],
56
+ });
57
+ }
58
+ return cfg;
59
+ });
60
+ };
61
+ exports.withDooPushOppoManifest = withDooPushOppoManifest;
@@ -1,7 +1,7 @@
1
1
  import { ConfigPlugin } from '@expo/config-plugins';
2
2
  import type { PluginConfig } from '../schema';
3
3
  /**
4
- * Adds Maven repositories (jitpack + mavenLocal during dev) to root build.gradle.
5
- * Idempotent.
4
+ * Adds Maven repositories and vendor Gradle plugin classpaths to root build.gradle.
5
+ * Idempotent. Supports the Groovy build.gradle generated by Expo prebuild.
6
6
  */
7
7
  export declare const withDooPushRootBuildGradle: ConfigPlugin<PluginConfig>;