native-update 1.0.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 (184) hide show
  1. package/CapacitorNativeUpdate.podspec +18 -0
  2. package/LICENSE +21 -0
  3. package/Readme.md +451 -0
  4. package/android/build.gradle +92 -0
  5. package/android/gradle/wrapper/gradle-wrapper.properties +8 -0
  6. package/android/gradle.properties +17 -0
  7. package/android/proguard-rules.pro +29 -0
  8. package/android/settings.gradle +2 -0
  9. package/android/src/main/AndroidManifest.xml +34 -0
  10. package/android/src/main/java/com/aoneahsan/nativeupdate/AppReviewPlugin.kt +153 -0
  11. package/android/src/main/java/com/aoneahsan/nativeupdate/AppUpdatePlugin.kt +275 -0
  12. package/android/src/main/java/com/aoneahsan/nativeupdate/BackgroundNotificationManager.kt +390 -0
  13. package/android/src/main/java/com/aoneahsan/nativeupdate/BackgroundUpdateManager.kt +46 -0
  14. package/android/src/main/java/com/aoneahsan/nativeupdate/BackgroundUpdatePlugin.kt +333 -0
  15. package/android/src/main/java/com/aoneahsan/nativeupdate/BackgroundUpdateWorker.kt +251 -0
  16. package/android/src/main/java/com/aoneahsan/nativeupdate/CapacitorNativeUpdatePlugin.kt +265 -0
  17. package/android/src/main/java/com/aoneahsan/nativeupdate/LiveUpdatePlugin.kt +526 -0
  18. package/android/src/main/java/com/aoneahsan/nativeupdate/NotificationActionReceiver.kt +99 -0
  19. package/android/src/main/java/com/aoneahsan/nativeupdate/SecurityManager.kt +249 -0
  20. package/dist/esm/__tests__/bundle-manager.test.d.ts +1 -0
  21. package/dist/esm/__tests__/bundle-manager.test.js +123 -0
  22. package/dist/esm/__tests__/bundle-manager.test.js.map +1 -0
  23. package/dist/esm/__tests__/config.test.d.ts +1 -0
  24. package/dist/esm/__tests__/config.test.js +69 -0
  25. package/dist/esm/__tests__/config.test.js.map +1 -0
  26. package/dist/esm/__tests__/integration.test.d.ts +1 -0
  27. package/dist/esm/__tests__/integration.test.js +78 -0
  28. package/dist/esm/__tests__/integration.test.js.map +1 -0
  29. package/dist/esm/__tests__/security.test.d.ts +1 -0
  30. package/dist/esm/__tests__/security.test.js +54 -0
  31. package/dist/esm/__tests__/security.test.js.map +1 -0
  32. package/dist/esm/__tests__/version-manager.test.d.ts +1 -0
  33. package/dist/esm/__tests__/version-manager.test.js +45 -0
  34. package/dist/esm/__tests__/version-manager.test.js.map +1 -0
  35. package/dist/esm/app-review/app-review-manager.d.ts +24 -0
  36. package/dist/esm/app-review/app-review-manager.js +195 -0
  37. package/dist/esm/app-review/app-review-manager.js.map +1 -0
  38. package/dist/esm/app-review/index.d.ts +5 -0
  39. package/dist/esm/app-review/index.js +6 -0
  40. package/dist/esm/app-review/index.js.map +1 -0
  41. package/dist/esm/app-review/platform-review-handler.d.ts +20 -0
  42. package/dist/esm/app-review/platform-review-handler.js +138 -0
  43. package/dist/esm/app-review/platform-review-handler.js.map +1 -0
  44. package/dist/esm/app-review/review-conditions-checker.d.ts +22 -0
  45. package/dist/esm/app-review/review-conditions-checker.js +155 -0
  46. package/dist/esm/app-review/review-conditions-checker.js.map +1 -0
  47. package/dist/esm/app-review/review-rate-limiter.d.ts +23 -0
  48. package/dist/esm/app-review/review-rate-limiter.js +164 -0
  49. package/dist/esm/app-review/review-rate-limiter.js.map +1 -0
  50. package/dist/esm/app-review/types.d.ts +41 -0
  51. package/dist/esm/app-review/types.js +2 -0
  52. package/dist/esm/app-review/types.js.map +1 -0
  53. package/dist/esm/app-update/app-update-checker.d.ts +13 -0
  54. package/dist/esm/app-update/app-update-checker.js +104 -0
  55. package/dist/esm/app-update/app-update-checker.js.map +1 -0
  56. package/dist/esm/app-update/app-update-installer.d.ts +19 -0
  57. package/dist/esm/app-update/app-update-installer.js +123 -0
  58. package/dist/esm/app-update/app-update-installer.js.map +1 -0
  59. package/dist/esm/app-update/app-update-manager.d.ts +28 -0
  60. package/dist/esm/app-update/app-update-manager.js +199 -0
  61. package/dist/esm/app-update/app-update-manager.js.map +1 -0
  62. package/dist/esm/app-update/app-update-notifier.d.ts +14 -0
  63. package/dist/esm/app-update/app-update-notifier.js +100 -0
  64. package/dist/esm/app-update/app-update-notifier.js.map +1 -0
  65. package/dist/esm/app-update/index.d.ts +6 -0
  66. package/dist/esm/app-update/index.js +7 -0
  67. package/dist/esm/app-update/index.js.map +1 -0
  68. package/dist/esm/app-update/platform-app-update.d.ts +19 -0
  69. package/dist/esm/app-update/platform-app-update.js +129 -0
  70. package/dist/esm/app-update/platform-app-update.js.map +1 -0
  71. package/dist/esm/app-update/types.d.ts +58 -0
  72. package/dist/esm/app-update/types.js +12 -0
  73. package/dist/esm/app-update/types.js.map +1 -0
  74. package/dist/esm/background-update/background-scheduler.d.ts +17 -0
  75. package/dist/esm/background-update/background-scheduler.js +195 -0
  76. package/dist/esm/background-update/background-scheduler.js.map +1 -0
  77. package/dist/esm/background-update/index.d.ts +3 -0
  78. package/dist/esm/background-update/index.js +3 -0
  79. package/dist/esm/background-update/index.js.map +1 -0
  80. package/dist/esm/background-update/notification-manager.d.ts +29 -0
  81. package/dist/esm/background-update/notification-manager.js +89 -0
  82. package/dist/esm/background-update/notification-manager.js.map +1 -0
  83. package/dist/esm/core/analytics.d.ts +70 -0
  84. package/dist/esm/core/analytics.js +137 -0
  85. package/dist/esm/core/analytics.js.map +1 -0
  86. package/dist/esm/core/cache-manager.d.ts +72 -0
  87. package/dist/esm/core/cache-manager.js +275 -0
  88. package/dist/esm/core/cache-manager.js.map +1 -0
  89. package/dist/esm/core/config.d.ts +48 -0
  90. package/dist/esm/core/config.js +83 -0
  91. package/dist/esm/core/config.js.map +1 -0
  92. package/dist/esm/core/errors.d.ts +51 -0
  93. package/dist/esm/core/errors.js +80 -0
  94. package/dist/esm/core/errors.js.map +1 -0
  95. package/dist/esm/core/logger.d.ts +21 -0
  96. package/dist/esm/core/logger.js +109 -0
  97. package/dist/esm/core/logger.js.map +1 -0
  98. package/dist/esm/core/performance.d.ts +53 -0
  99. package/dist/esm/core/performance.js +140 -0
  100. package/dist/esm/core/performance.js.map +1 -0
  101. package/dist/esm/core/plugin-manager.d.ts +66 -0
  102. package/dist/esm/core/plugin-manager.js +148 -0
  103. package/dist/esm/core/plugin-manager.js.map +1 -0
  104. package/dist/esm/core/security.d.ts +93 -0
  105. package/dist/esm/core/security.js +315 -0
  106. package/dist/esm/core/security.js.map +1 -0
  107. package/dist/esm/definitions.d.ts +639 -0
  108. package/dist/esm/definitions.js +103 -0
  109. package/dist/esm/definitions.js.map +1 -0
  110. package/dist/esm/index.d.ts +12 -0
  111. package/dist/esm/index.js +16 -0
  112. package/dist/esm/index.js.map +1 -0
  113. package/dist/esm/live-update/bundle-manager.d.ts +94 -0
  114. package/dist/esm/live-update/bundle-manager.js +310 -0
  115. package/dist/esm/live-update/bundle-manager.js.map +1 -0
  116. package/dist/esm/live-update/certificate-pinning.d.ts +38 -0
  117. package/dist/esm/live-update/certificate-pinning.js +78 -0
  118. package/dist/esm/live-update/certificate-pinning.js.map +1 -0
  119. package/dist/esm/live-update/download-manager.d.ts +67 -0
  120. package/dist/esm/live-update/download-manager.js +319 -0
  121. package/dist/esm/live-update/download-manager.js.map +1 -0
  122. package/dist/esm/live-update/update-manager.d.ts +52 -0
  123. package/dist/esm/live-update/update-manager.js +294 -0
  124. package/dist/esm/live-update/update-manager.js.map +1 -0
  125. package/dist/esm/live-update/version-manager.d.ts +84 -0
  126. package/dist/esm/live-update/version-manager.js +335 -0
  127. package/dist/esm/live-update/version-manager.js.map +1 -0
  128. package/dist/esm/plugin.d.ts +6 -0
  129. package/dist/esm/plugin.js +283 -0
  130. package/dist/esm/plugin.js.map +1 -0
  131. package/dist/esm/security/crypto.d.ts +25 -0
  132. package/dist/esm/security/crypto.js +70 -0
  133. package/dist/esm/security/crypto.js.map +1 -0
  134. package/dist/esm/security/validator.d.ts +60 -0
  135. package/dist/esm/security/validator.js +143 -0
  136. package/dist/esm/security/validator.js.map +1 -0
  137. package/dist/esm/web.d.ts +74 -0
  138. package/dist/esm/web.js +595 -0
  139. package/dist/esm/web.js.map +1 -0
  140. package/dist/plugin.cjs.js +2 -0
  141. package/dist/plugin.cjs.js.map +1 -0
  142. package/dist/plugin.esm.js +2 -0
  143. package/dist/plugin.esm.js.map +1 -0
  144. package/dist/plugin.js +3 -0
  145. package/dist/plugin.js.map +1 -0
  146. package/docs/APP_REVIEW_GUIDE.md +768 -0
  147. package/docs/BUNDLE_SIGNING.md +264 -0
  148. package/docs/LIVE_UPDATES_GUIDE.md +650 -0
  149. package/docs/MIGRATION.md +192 -0
  150. package/docs/NATIVE_UPDATES_GUIDE.md +694 -0
  151. package/docs/QUICK_START.md +606 -0
  152. package/docs/README.md +111 -0
  153. package/docs/REMAINING_FEATURES.md +139 -0
  154. package/docs/api/app-review-api.md +259 -0
  155. package/docs/api/app-update-api.md +238 -0
  156. package/docs/api/events-api.md +451 -0
  157. package/docs/api/live-update-api.md +265 -0
  158. package/docs/background-updates.md +392 -0
  159. package/docs/examples/advanced-scenarios.md +410 -0
  160. package/docs/examples/basic-usage.md +185 -0
  161. package/docs/features/app-reviews.md +975 -0
  162. package/docs/features/app-updates.md +785 -0
  163. package/docs/features/live-updates.md +633 -0
  164. package/docs/getting-started/configuration.md +468 -0
  165. package/docs/getting-started/installation.md +209 -0
  166. package/docs/getting-started/quick-start.md +379 -0
  167. package/docs/guides/deployment-guide.md +333 -0
  168. package/docs/guides/migration-from-codepush.md +142 -0
  169. package/docs/guides/security-best-practices.md +1057 -0
  170. package/docs/guides/testing-guide.md +373 -0
  171. package/docs/production-readiness.md +478 -0
  172. package/docs/security/certificate-pinning.md +122 -0
  173. package/docs/server-requirements.md +147 -0
  174. package/ios/Plugin/AppReview/AppReviewPlugin.swift +158 -0
  175. package/ios/Plugin/AppUpdate/AppUpdatePlugin.swift +234 -0
  176. package/ios/Plugin/BackgroundUpdate/BackgroundNotificationManager.swift +329 -0
  177. package/ios/Plugin/BackgroundUpdate/BackgroundUpdatePlugin.swift +396 -0
  178. package/ios/Plugin/CapacitorNativeUpdatePlugin.m +45 -0
  179. package/ios/Plugin/CapacitorNativeUpdatePlugin.swift +190 -0
  180. package/ios/Plugin/Info.plist +43 -0
  181. package/ios/Plugin/LiveUpdate/LiveUpdatePlugin.swift +689 -0
  182. package/ios/Plugin/LiveUpdate/WebViewConfiguration.swift +45 -0
  183. package/ios/Plugin/Security/SecurityManager.swift +289 -0
  184. package/package.json +90 -0
@@ -0,0 +1,390 @@
1
+ package com.aoneahsan.nativeupdate
2
+
3
+ import android.app.NotificationChannel
4
+ import android.app.NotificationManager
5
+ import android.app.PendingIntent
6
+ import android.content.Context
7
+ import android.content.Intent
8
+ import android.content.pm.PackageManager
9
+ import android.os.Build
10
+ import androidx.core.app.NotificationCompat
11
+ import androidx.core.app.NotificationManagerCompat
12
+ import androidx.core.content.ContextCompat
13
+ import com.getcapacitor.JSObject
14
+ import com.getcapacitor.Plugin
15
+
16
+ class BackgroundNotificationManager(
17
+ private val context: Context,
18
+ private val plugin: BackgroundUpdatePlugin
19
+ ) {
20
+
21
+ companion object {
22
+ private const val CHANNEL_ID = "capacitor_native_update"
23
+ private const val CHANNEL_NAME = "App Updates"
24
+ private const val NOTIFICATION_ID = 1001
25
+ private const val ACTION_UPDATE_NOW = "com.aoneahsan.nativeupdate.UPDATE_NOW"
26
+ private const val ACTION_UPDATE_LATER = "com.aoneahsan.nativeupdate.UPDATE_LATER"
27
+ private const val ACTION_DISMISS = "com.aoneahsan.nativeupdate.DISMISS"
28
+ }
29
+
30
+ private var preferences: NotificationPreferences = NotificationPreferences.default()
31
+
32
+ fun createNotificationChannel() {
33
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
34
+ val channel = NotificationChannel(
35
+ CHANNEL_ID,
36
+ CHANNEL_NAME,
37
+ NotificationManager.IMPORTANCE_DEFAULT
38
+ ).apply {
39
+ description = "Notifications for app updates"
40
+ enableLights(true)
41
+ enableVibration(true)
42
+ }
43
+
44
+ val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
45
+ notificationManager.createNotificationChannel(channel)
46
+ }
47
+ }
48
+
49
+ fun setPreferences(preferencesData: JSObject) {
50
+ preferences = NotificationPreferences.fromJSObject(preferencesData)
51
+
52
+ // Update channel if preferences changed
53
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
54
+ val channelId = preferences.channelId ?: CHANNEL_ID
55
+ val channelName = preferences.channelName ?: CHANNEL_NAME
56
+
57
+ val channel = NotificationChannel(
58
+ channelId,
59
+ channelName,
60
+ getNotificationImportance(preferences.priority)
61
+ ).apply {
62
+ description = "Notifications for app updates"
63
+ enableLights(true)
64
+ enableVibration(preferences.vibrationEnabled)
65
+
66
+ if (!preferences.soundEnabled) {
67
+ setSound(null, null)
68
+ }
69
+ }
70
+
71
+ val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
72
+ notificationManager.createNotificationChannel(channel)
73
+ }
74
+ }
75
+
76
+ fun getPermissionStatus(): NotificationPermissionStatus {
77
+ val notificationManager = NotificationManagerCompat.from(context)
78
+ val areNotificationsEnabled = notificationManager.areNotificationsEnabled()
79
+
80
+ return NotificationPermissionStatus(
81
+ granted = areNotificationsEnabled,
82
+ canRequest = !areNotificationsEnabled,
83
+ shouldShowRationale = false // Android doesn't have this concept
84
+ )
85
+ }
86
+
87
+ fun requestPermissions(): Boolean {
88
+ // On Android 13+, notification permissions need to be requested
89
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
90
+ return ContextCompat.checkSelfPermission(
91
+ context,
92
+ android.Manifest.permission.POST_NOTIFICATIONS
93
+ ) == PackageManager.PERMISSION_GRANTED
94
+ }
95
+
96
+ // For older versions, notifications are enabled by default
97
+ return true
98
+ }
99
+
100
+ fun sendUpdateNotification(
101
+ appUpdate: AppUpdateInfo?,
102
+ liveUpdate: LatestVersion?
103
+ ): Boolean {
104
+ val permissionStatus = getPermissionStatus()
105
+ if (!permissionStatus.granted) {
106
+ return false
107
+ }
108
+
109
+ val notificationBuilder = createNotificationBuilder(appUpdate, liveUpdate)
110
+
111
+ try {
112
+ val notificationManager = NotificationManagerCompat.from(context)
113
+ notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build())
114
+
115
+ // Notify listeners
116
+ val eventData = JSObject()
117
+ eventData.put("type", if (appUpdate?.updateAvailable == true) "app_update" else "live_update")
118
+ eventData.put("updateAvailable", true)
119
+ eventData.put("version", appUpdate?.availableVersion ?: liveUpdate?.version ?: "unknown")
120
+ eventData.put("action", "shown")
121
+
122
+ plugin.notifyListeners("backgroundUpdateNotification", eventData)
123
+
124
+ return true
125
+ } catch (e: Exception) {
126
+ android.util.Log.e("BackgroundNotificationManager", "Failed to send notification", e)
127
+ return false
128
+ }
129
+ }
130
+
131
+ fun cancelNotification() {
132
+ val notificationManager = NotificationManagerCompat.from(context)
133
+ notificationManager.cancel(NOTIFICATION_ID)
134
+ }
135
+
136
+ private fun createNotificationBuilder(
137
+ appUpdate: AppUpdateInfo?,
138
+ liveUpdate: LatestVersion?
139
+ ): NotificationCompat.Builder {
140
+ val title = determineTitle(appUpdate, liveUpdate)
141
+ val content = determineContent(appUpdate, liveUpdate)
142
+
143
+ val builder = NotificationCompat.Builder(context, preferences.channelId ?: CHANNEL_ID)
144
+ .setSmallIcon(getNotificationIcon())
145
+ .setContentTitle(title)
146
+ .setContentText(content)
147
+ .setStyle(NotificationCompat.BigTextStyle().bigText(content))
148
+ .setPriority(getNotificationPriority(preferences.priority))
149
+ .setAutoCancel(true)
150
+ .setOnlyAlertOnce(true)
151
+
152
+ // Set sound
153
+ if (preferences.soundEnabled) {
154
+ builder.setDefaults(NotificationCompat.DEFAULT_SOUND)
155
+ }
156
+
157
+ // Set vibration
158
+ if (preferences.vibrationEnabled) {
159
+ builder.setVibrate(longArrayOf(0, 300, 300, 300))
160
+ }
161
+
162
+ // Set content intent (tap action)
163
+ val contentIntent = createContentIntent(appUpdate, liveUpdate)
164
+ builder.setContentIntent(contentIntent)
165
+
166
+ // Add action buttons if enabled
167
+ if (preferences.showActions) {
168
+ addActionButtons(builder, appUpdate, liveUpdate)
169
+ }
170
+
171
+ return builder
172
+ }
173
+
174
+ private fun determineTitle(appUpdate: AppUpdateInfo?, liveUpdate: LatestVersion?): String {
175
+ return when {
176
+ appUpdate?.updateAvailable == true && liveUpdate?.available == true -> "App Updates Available"
177
+ appUpdate?.updateAvailable == true -> "App Update Available"
178
+ liveUpdate?.available == true -> "Content Update Available"
179
+ else -> preferences.title ?: "App Update Available"
180
+ }
181
+ }
182
+
183
+ private fun determineContent(appUpdate: AppUpdateInfo?, liveUpdate: LatestVersion?): String {
184
+ return when {
185
+ appUpdate?.updateAvailable == true && liveUpdate?.available == true ->
186
+ "App version ${appUpdate.availableVersion} and content updates are available"
187
+ appUpdate?.updateAvailable == true ->
188
+ "Version ${appUpdate.availableVersion} is available"
189
+ liveUpdate?.available == true ->
190
+ "New content version ${liveUpdate.version} is available"
191
+ else -> preferences.description ?: "A new version of the app is available"
192
+ }
193
+ }
194
+
195
+ private fun getNotificationIcon(): Int {
196
+ // Try to get custom icon, fallback to default
197
+ return try {
198
+ context.packageManager.getApplicationInfo(context.packageName, 0).icon
199
+ } catch (e: Exception) {
200
+ android.R.drawable.ic_dialog_info
201
+ }
202
+ }
203
+
204
+ private fun getNotificationPriority(priority: NotificationPriority): Int {
205
+ return when (priority) {
206
+ NotificationPriority.MIN -> NotificationCompat.PRIORITY_MIN
207
+ NotificationPriority.LOW -> NotificationCompat.PRIORITY_LOW
208
+ NotificationPriority.DEFAULT -> NotificationCompat.PRIORITY_DEFAULT
209
+ NotificationPriority.HIGH -> NotificationCompat.PRIORITY_HIGH
210
+ NotificationPriority.MAX -> NotificationCompat.PRIORITY_MAX
211
+ }
212
+ }
213
+
214
+ private fun getNotificationImportance(priority: NotificationPriority): Int {
215
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
216
+ when (priority) {
217
+ NotificationPriority.MIN -> NotificationManager.IMPORTANCE_MIN
218
+ NotificationPriority.LOW -> NotificationManager.IMPORTANCE_LOW
219
+ NotificationPriority.DEFAULT -> NotificationManager.IMPORTANCE_DEFAULT
220
+ NotificationPriority.HIGH -> NotificationManager.IMPORTANCE_HIGH
221
+ NotificationPriority.MAX -> NotificationManager.IMPORTANCE_MAX
222
+ }
223
+ } else {
224
+ NotificationManager.IMPORTANCE_DEFAULT
225
+ }
226
+ }
227
+
228
+ private fun createContentIntent(appUpdate: AppUpdateInfo?, liveUpdate: LatestVersion?): PendingIntent {
229
+ val intent = context.packageManager.getLaunchIntentForPackage(context.packageName)
230
+ ?: Intent()
231
+
232
+ intent.putExtra("notification_action", "tapped")
233
+ intent.putExtra("update_type", if (appUpdate?.updateAvailable == true) "app_update" else "live_update")
234
+
235
+ return PendingIntent.getActivity(
236
+ context,
237
+ 0,
238
+ intent,
239
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
240
+ )
241
+ }
242
+
243
+ private fun addActionButtons(
244
+ builder: NotificationCompat.Builder,
245
+ appUpdate: AppUpdateInfo?,
246
+ liveUpdate: LatestVersion?
247
+ ) {
248
+ val actionLabels = preferences.actionLabels ?: ActionLabels.default()
249
+
250
+ // Update Now action
251
+ val updateNowIntent = Intent(ACTION_UPDATE_NOW).apply {
252
+ putExtra("update_type", if (appUpdate?.updateAvailable == true) "app_update" else "live_update")
253
+ }
254
+ val updateNowPendingIntent = PendingIntent.getBroadcast(
255
+ context,
256
+ 1,
257
+ updateNowIntent,
258
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
259
+ )
260
+
261
+ builder.addAction(
262
+ android.R.drawable.ic_menu_upload,
263
+ actionLabels.updateNow ?: "Update Now",
264
+ updateNowPendingIntent
265
+ )
266
+
267
+ // Update Later action
268
+ val updateLaterIntent = Intent(ACTION_UPDATE_LATER)
269
+ val updateLaterPendingIntent = PendingIntent.getBroadcast(
270
+ context,
271
+ 2,
272
+ updateLaterIntent,
273
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
274
+ )
275
+
276
+ builder.addAction(
277
+ android.R.drawable.ic_menu_recent_history,
278
+ actionLabels.updateLater ?: "Later",
279
+ updateLaterPendingIntent
280
+ )
281
+
282
+ // Dismiss action
283
+ val dismissIntent = Intent(ACTION_DISMISS)
284
+ val dismissPendingIntent = PendingIntent.getBroadcast(
285
+ context,
286
+ 3,
287
+ dismissIntent,
288
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
289
+ )
290
+
291
+ builder.addAction(
292
+ android.R.drawable.ic_menu_close_clear_cancel,
293
+ actionLabels.dismiss ?: "Dismiss",
294
+ dismissPendingIntent
295
+ )
296
+ }
297
+ }
298
+
299
+ data class NotificationPreferences(
300
+ val title: String? = null,
301
+ val description: String? = null,
302
+ val iconName: String? = null,
303
+ val soundEnabled: Boolean = true,
304
+ val vibrationEnabled: Boolean = true,
305
+ val showActions: Boolean = true,
306
+ val actionLabels: ActionLabels? = null,
307
+ val channelId: String? = null,
308
+ val channelName: String? = null,
309
+ val priority: NotificationPriority = NotificationPriority.DEFAULT
310
+ ) {
311
+ companion object {
312
+ fun default(): NotificationPreferences {
313
+ return NotificationPreferences(
314
+ title = "App Update Available",
315
+ description = "A new version of the app is available",
316
+ actionLabels = ActionLabels.default()
317
+ )
318
+ }
319
+
320
+ fun fromJSObject(obj: JSObject): NotificationPreferences {
321
+ val actionLabelsObj = obj.getJSObject("actionLabels")
322
+ val priorityString = obj.getString("priority", "default")
323
+
324
+ return NotificationPreferences(
325
+ title = obj.getString("title"),
326
+ description = obj.getString("description"),
327
+ iconName = obj.getString("iconName"),
328
+ soundEnabled = obj.getBoolean("soundEnabled", true),
329
+ vibrationEnabled = obj.getBoolean("vibrationEnabled", true),
330
+ showActions = obj.getBoolean("showActions", true),
331
+ actionLabels = actionLabelsObj?.let { ActionLabels.fromJSObject(it) } ?: ActionLabels.default(),
332
+ channelId = obj.getString("channelId"),
333
+ channelName = obj.getString("channelName"),
334
+ priority = NotificationPriority.fromString(priorityString)
335
+ )
336
+ }
337
+ }
338
+ }
339
+
340
+ data class ActionLabels(
341
+ val updateNow: String? = null,
342
+ val updateLater: String? = null,
343
+ val dismiss: String? = null
344
+ ) {
345
+ companion object {
346
+ fun default(): ActionLabels {
347
+ return ActionLabels(
348
+ updateNow = "Update Now",
349
+ updateLater = "Later",
350
+ dismiss = "Dismiss"
351
+ )
352
+ }
353
+
354
+ fun fromJSObject(obj: JSObject): ActionLabels {
355
+ return ActionLabels(
356
+ updateNow = obj.getString("updateNow"),
357
+ updateLater = obj.getString("updateLater"),
358
+ dismiss = obj.getString("dismiss")
359
+ )
360
+ }
361
+ }
362
+ }
363
+
364
+ enum class NotificationPriority(val value: String) {
365
+ MIN("min"),
366
+ LOW("low"),
367
+ DEFAULT("default"),
368
+ HIGH("high"),
369
+ MAX("max");
370
+
371
+ companion object {
372
+ fun fromString(value: String): NotificationPriority {
373
+ return values().find { it.value == value } ?: DEFAULT
374
+ }
375
+ }
376
+ }
377
+
378
+ data class NotificationPermissionStatus(
379
+ val granted: Boolean,
380
+ val canRequest: Boolean,
381
+ val shouldShowRationale: Boolean? = null
382
+ ) {
383
+ fun toJSObject(): JSObject {
384
+ val obj = JSObject()
385
+ obj.put("granted", granted)
386
+ obj.put("canRequest", canRequest)
387
+ shouldShowRationale?.let { obj.put("shouldShowRationale", it) }
388
+ return obj
389
+ }
390
+ }
@@ -0,0 +1,46 @@
1
+ package com.aoneahsan.nativeupdate
2
+
3
+ /**
4
+ * Singleton manager to maintain references to plugin instances
5
+ * This allows background workers to access plugin functionality
6
+ */
7
+ object BackgroundUpdateManager {
8
+ @Volatile
9
+ private var backgroundUpdatePlugin: BackgroundUpdatePlugin? = null
10
+
11
+ @Volatile
12
+ private var liveUpdatePlugin: LiveUpdatePlugin? = null
13
+
14
+ @Volatile
15
+ private var appUpdatePlugin: AppUpdatePlugin? = null
16
+
17
+ fun registerBackgroundUpdatePlugin(plugin: BackgroundUpdatePlugin) {
18
+ backgroundUpdatePlugin = plugin
19
+ }
20
+
21
+ fun registerLiveUpdatePlugin(plugin: LiveUpdatePlugin) {
22
+ liveUpdatePlugin = plugin
23
+ }
24
+
25
+ fun registerAppUpdatePlugin(plugin: AppUpdatePlugin) {
26
+ appUpdatePlugin = plugin
27
+ }
28
+
29
+ fun getBackgroundUpdatePlugin(): BackgroundUpdatePlugin? {
30
+ return backgroundUpdatePlugin
31
+ }
32
+
33
+ fun getLiveUpdatePlugin(): LiveUpdatePlugin? {
34
+ return liveUpdatePlugin
35
+ }
36
+
37
+ fun getAppUpdatePlugin(): AppUpdatePlugin? {
38
+ return appUpdatePlugin
39
+ }
40
+
41
+ fun clear() {
42
+ backgroundUpdatePlugin = null
43
+ liveUpdatePlugin = null
44
+ appUpdatePlugin = null
45
+ }
46
+ }