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,333 @@
1
+ package com.aoneahsan.nativeupdate
2
+
3
+ import android.content.Context
4
+ import android.content.pm.PackageManager
5
+ import androidx.work.*
6
+ import com.getcapacitor.JSObject
7
+ import com.getcapacitor.Plugin
8
+ import com.getcapacitor.PluginCall
9
+ import com.getcapacitor.PluginMethod
10
+ import com.getcapacitor.annotation.CapacitorPlugin
11
+ import java.util.concurrent.TimeUnit
12
+ import org.json.JSONObject
13
+ import org.json.JSONArray
14
+
15
+ @CapacitorPlugin(name = "BackgroundUpdatePlugin")
16
+ class BackgroundUpdatePlugin : Plugin() {
17
+
18
+ private lateinit var notificationManager: BackgroundNotificationManager
19
+ private var backgroundUpdateConfig: BackgroundUpdateConfig? = null
20
+ private var backgroundUpdateStatus: BackgroundUpdateStatus = BackgroundUpdateStatus()
21
+
22
+ companion object {
23
+ private const val WORK_NAME = "background_update_work"
24
+ private const val WORK_TAG = "capacitor_native_update"
25
+ }
26
+
27
+ override fun load() {
28
+ super.load()
29
+ notificationManager = BackgroundNotificationManager(context, this)
30
+ notificationManager.createNotificationChannel()
31
+
32
+ // Register this plugin instance with the manager
33
+ BackgroundUpdateManager.registerBackgroundUpdatePlugin(this)
34
+ }
35
+
36
+ @PluginMethod
37
+ fun enableBackgroundUpdates(call: PluginCall) {
38
+ try {
39
+ val configData = call.data
40
+ val config = BackgroundUpdateConfig.fromJSObject(configData)
41
+
42
+ backgroundUpdateConfig = config
43
+ backgroundUpdateStatus.enabled = config.enabled
44
+
45
+ if (config.enabled) {
46
+ scheduleBackgroundWork(config.checkInterval.toLong())
47
+ } else {
48
+ disableBackgroundUpdates()
49
+ }
50
+
51
+ call.resolve()
52
+ } catch (e: Exception) {
53
+ call.reject("Invalid configuration: ${e.message}")
54
+ }
55
+ }
56
+
57
+ @PluginMethod
58
+ fun disableBackgroundUpdates(call: PluginCall) {
59
+ disableBackgroundUpdates()
60
+ call.resolve()
61
+ }
62
+
63
+ @PluginMethod
64
+ fun getBackgroundUpdateStatus(call: PluginCall) {
65
+ call.resolve(backgroundUpdateStatus.toJSObject())
66
+ }
67
+
68
+ @PluginMethod
69
+ fun scheduleBackgroundCheck(call: PluginCall) {
70
+ val interval = call.getLong("interval", 24 * 60 * 60 * 1000L) // Default 24 hours
71
+ scheduleBackgroundWork(interval)
72
+ call.resolve()
73
+ }
74
+
75
+ @PluginMethod
76
+ fun triggerBackgroundCheck(call: PluginCall) {
77
+ val workRequest = OneTimeWorkRequestBuilder<BackgroundUpdateWorker>()
78
+ .setInputData(createWorkData())
79
+ .addTag(WORK_TAG)
80
+ .build()
81
+
82
+ WorkManager.getInstance(context)
83
+ .enqueueUniqueWork(
84
+ "${WORK_NAME}_manual",
85
+ ExistingWorkPolicy.REPLACE,
86
+ workRequest
87
+ )
88
+
89
+ // Return immediately - actual result will be sent via listeners
90
+ call.resolve(JSObject().put("triggered", true))
91
+ }
92
+
93
+ @PluginMethod
94
+ fun setNotificationPreferences(call: PluginCall) {
95
+ val preferences = call.data
96
+ notificationManager.setPreferences(preferences)
97
+ call.resolve()
98
+ }
99
+
100
+ @PluginMethod
101
+ fun getNotificationPermissions(call: PluginCall) {
102
+ val status = notificationManager.getPermissionStatus()
103
+ call.resolve(status.toJSObject())
104
+ }
105
+
106
+ @PluginMethod
107
+ fun requestNotificationPermissions(call: PluginCall) {
108
+ val granted = notificationManager.requestPermissions()
109
+ call.resolve(JSObject().put("granted", granted))
110
+ }
111
+
112
+ private fun disableBackgroundUpdates() {
113
+ backgroundUpdateStatus.enabled = false
114
+ backgroundUpdateStatus.isRunning = false
115
+ backgroundUpdateStatus.currentTaskId = null
116
+
117
+ WorkManager.getInstance(context).cancelAllWorkByTag(WORK_TAG)
118
+ }
119
+
120
+ private fun scheduleBackgroundWork(intervalMs: Long) {
121
+ val constraints = Constraints.Builder()
122
+ .setRequiredNetworkType(
123
+ if (backgroundUpdateConfig?.requireWifi == true)
124
+ NetworkType.UNMETERED
125
+ else
126
+ NetworkType.CONNECTED
127
+ )
128
+ .setRequiresBatteryNotLow(backgroundUpdateConfig?.respectBatteryOptimization != false)
129
+ .build()
130
+
131
+ val workRequest = PeriodicWorkRequestBuilder<BackgroundUpdateWorker>(
132
+ intervalMs,
133
+ TimeUnit.MILLISECONDS,
134
+ PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS,
135
+ TimeUnit.MILLISECONDS
136
+ )
137
+ .setConstraints(constraints)
138
+ .setInputData(createWorkData())
139
+ .addTag(WORK_TAG)
140
+ .build()
141
+
142
+ WorkManager.getInstance(context)
143
+ .enqueueUniquePeriodicWork(
144
+ WORK_NAME,
145
+ ExistingPeriodicWorkPolicy.REPLACE,
146
+ workRequest
147
+ )
148
+
149
+ backgroundUpdateStatus.nextCheckTime = System.currentTimeMillis() + intervalMs
150
+ }
151
+
152
+ private fun createWorkData(): Data {
153
+ val configJson = backgroundUpdateConfig?.toJSONObject()?.toString() ?: "{}"
154
+ return Data.Builder()
155
+ .putString("config", configJson)
156
+ .build()
157
+ }
158
+
159
+ fun updateBackgroundStatus(status: BackgroundUpdateStatus) {
160
+ backgroundUpdateStatus = status
161
+ }
162
+
163
+ fun getBackgroundConfig(): BackgroundUpdateConfig? {
164
+ return backgroundUpdateConfig
165
+ }
166
+
167
+ fun getNotificationManager(): BackgroundNotificationManager {
168
+ return notificationManager
169
+ }
170
+ }
171
+
172
+ data class BackgroundUpdateConfig(
173
+ val enabled: Boolean,
174
+ val checkInterval: Int,
175
+ val updateTypes: List<BackgroundUpdateType>,
176
+ val autoInstall: Boolean = false,
177
+ val notificationPreferences: NotificationPreferences? = null,
178
+ val respectBatteryOptimization: Boolean = true,
179
+ val allowMeteredConnection: Boolean = false,
180
+ val minimumBatteryLevel: Int = 20,
181
+ val requireWifi: Boolean = false,
182
+ val maxRetries: Int = 3,
183
+ val retryDelay: Int = 5000,
184
+ val taskIdentifier: String? = null
185
+ ) {
186
+ companion object {
187
+ fun fromJSObject(obj: JSObject): BackgroundUpdateConfig {
188
+ val enabled = obj.getBoolean("enabled", false)
189
+ val checkInterval = obj.getInt("checkInterval", 24 * 60 * 60 * 1000)
190
+ val updateTypesArray = obj.getJSONArray("updateTypes")
191
+ val updateTypes = mutableListOf<BackgroundUpdateType>()
192
+
193
+ for (i in 0 until updateTypesArray.length()) {
194
+ val typeString = updateTypesArray.getString(i)
195
+ BackgroundUpdateType.fromString(typeString)?.let { updateTypes.add(it) }
196
+ }
197
+
198
+ val notificationPrefs = obj.getJSObject("notificationPreferences")
199
+
200
+ return BackgroundUpdateConfig(
201
+ enabled = enabled,
202
+ checkInterval = checkInterval,
203
+ updateTypes = updateTypes,
204
+ autoInstall = obj.getBoolean("autoInstall", false),
205
+ notificationPreferences = notificationPrefs?.let { NotificationPreferences.fromJSObject(it) },
206
+ respectBatteryOptimization = obj.getBoolean("respectBatteryOptimization", true),
207
+ allowMeteredConnection = obj.getBoolean("allowMeteredConnection", false),
208
+ minimumBatteryLevel = obj.getInt("minimumBatteryLevel", 20),
209
+ requireWifi = obj.getBoolean("requireWifi", false),
210
+ maxRetries = obj.getInt("maxRetries", 3),
211
+ retryDelay = obj.getInt("retryDelay", 5000),
212
+ taskIdentifier = obj.getString("taskIdentifier")
213
+ )
214
+ }
215
+ }
216
+
217
+ fun toJSONObject(): JSONObject {
218
+ val obj = JSONObject()
219
+ obj.put("enabled", enabled)
220
+ obj.put("checkInterval", checkInterval)
221
+ obj.put("updateTypes", JSONArray(updateTypes.map { it.value }))
222
+ obj.put("autoInstall", autoInstall)
223
+ obj.put("respectBatteryOptimization", respectBatteryOptimization)
224
+ obj.put("allowMeteredConnection", allowMeteredConnection)
225
+ obj.put("minimumBatteryLevel", minimumBatteryLevel)
226
+ obj.put("requireWifi", requireWifi)
227
+ obj.put("maxRetries", maxRetries)
228
+ obj.put("retryDelay", retryDelay)
229
+ taskIdentifier?.let { obj.put("taskIdentifier", it) }
230
+ return obj
231
+ }
232
+ }
233
+
234
+ enum class BackgroundUpdateType(val value: String) {
235
+ APP_UPDATE("app_update"),
236
+ LIVE_UPDATE("live_update"),
237
+ BOTH("both");
238
+
239
+ companion object {
240
+ fun fromString(value: String): BackgroundUpdateType? {
241
+ return values().find { it.value == value }
242
+ }
243
+ }
244
+ }
245
+
246
+ data class BackgroundUpdateStatus(
247
+ var enabled: Boolean = false,
248
+ var lastCheckTime: Long? = null,
249
+ var nextCheckTime: Long? = null,
250
+ var lastUpdateTime: Long? = null,
251
+ var currentTaskId: String? = null,
252
+ var isRunning: Boolean = false,
253
+ var checkCount: Int = 0,
254
+ var failureCount: Int = 0,
255
+ var lastError: UpdateError? = null
256
+ ) {
257
+ fun toJSObject(): JSObject {
258
+ val obj = JSObject()
259
+ obj.put("enabled", enabled)
260
+ obj.put("isRunning", isRunning)
261
+ obj.put("checkCount", checkCount)
262
+ obj.put("failureCount", failureCount)
263
+
264
+ lastCheckTime?.let { obj.put("lastCheckTime", it) }
265
+ nextCheckTime?.let { obj.put("nextCheckTime", it) }
266
+ lastUpdateTime?.let { obj.put("lastUpdateTime", it) }
267
+ currentTaskId?.let { obj.put("currentTaskId", it) }
268
+ lastError?.let { obj.put("lastError", it.toJSObject()) }
269
+
270
+ return obj
271
+ }
272
+ }
273
+
274
+ data class BackgroundCheckResult(
275
+ val success: Boolean,
276
+ val updatesFound: Boolean,
277
+ val appUpdate: AppUpdateInfo? = null,
278
+ val liveUpdate: LatestVersion? = null,
279
+ val notificationSent: Boolean = false,
280
+ val error: UpdateError? = null
281
+ ) {
282
+ fun toJSObject(): JSObject {
283
+ val obj = JSObject()
284
+ obj.put("success", success)
285
+ obj.put("updatesFound", updatesFound)
286
+ obj.put("notificationSent", notificationSent)
287
+
288
+ appUpdate?.let { obj.put("appUpdate", it.toJSObject()) }
289
+ liveUpdate?.let { obj.put("liveUpdate", it.toJSObject()) }
290
+ error?.let { obj.put("error", it.toJSObject()) }
291
+
292
+ return obj
293
+ }
294
+ }
295
+
296
+ data class UpdateError(
297
+ val code: String,
298
+ val message: String
299
+ ) {
300
+ fun toJSObject(): JSObject {
301
+ val obj = JSObject()
302
+ obj.put("code", code)
303
+ obj.put("message", message)
304
+ return obj
305
+ }
306
+ }
307
+
308
+ // Placeholder data classes - would be defined in other plugins
309
+ data class AppUpdateInfo(
310
+ val updateAvailable: Boolean,
311
+ val currentVersion: String,
312
+ val availableVersion: String? = null
313
+ ) {
314
+ fun toJSObject(): JSObject {
315
+ val obj = JSObject()
316
+ obj.put("updateAvailable", updateAvailable)
317
+ obj.put("currentVersion", currentVersion)
318
+ availableVersion?.let { obj.put("availableVersion", it) }
319
+ return obj
320
+ }
321
+ }
322
+
323
+ data class LatestVersion(
324
+ val available: Boolean,
325
+ val version: String? = null
326
+ ) {
327
+ fun toJSObject(): JSObject {
328
+ val obj = JSObject()
329
+ obj.put("available", available)
330
+ version?.let { obj.put("version", it) }
331
+ return obj
332
+ }
333
+ }
@@ -0,0 +1,251 @@
1
+ package com.aoneahsan.nativeupdate
2
+
3
+ import android.content.Context
4
+ import androidx.work.*
5
+ import com.getcapacitor.Bridge
6
+ import com.getcapacitor.JSObject
7
+ import com.google.gson.Gson
8
+ import kotlinx.coroutines.Dispatchers
9
+ import kotlinx.coroutines.withContext
10
+ import org.json.JSONObject
11
+
12
+ class BackgroundUpdateWorker(
13
+ private val context: Context,
14
+ workerParams: WorkerParameters
15
+ ) : CoroutineWorker(context, workerParams) {
16
+
17
+ companion object {
18
+ private const val TAG = "BackgroundUpdateWorker"
19
+ private const val NOTIFICATION_ID = 1001
20
+ }
21
+
22
+ override suspend fun doWork(): Result {
23
+ return withContext(Dispatchers.IO) {
24
+ try {
25
+ val configJson = inputData.getString("config")
26
+ val config = parseConfig(configJson)
27
+
28
+ if (config == null || !config.enabled) {
29
+ return@withContext Result.failure()
30
+ }
31
+
32
+ val result = performBackgroundCheck(config)
33
+
34
+ // Update plugin status
35
+ updatePluginStatus(result)
36
+
37
+ // Send notification if updates found
38
+ if (result.updatesFound) {
39
+ sendNotification(result)
40
+ }
41
+
42
+ // Notify listeners
43
+ notifyListeners(result)
44
+
45
+ if (result.success) Result.success() else Result.retry()
46
+ } catch (e: Exception) {
47
+ android.util.Log.e(TAG, "Background update failed", e)
48
+ Result.failure()
49
+ }
50
+ }
51
+ }
52
+
53
+ private fun parseConfig(configJson: String?): BackgroundUpdateConfig? {
54
+ return try {
55
+ configJson?.let {
56
+ val jsonObject = JSONObject(it)
57
+ val jsObject = JSObject.fromJSONObject(jsonObject)
58
+ BackgroundUpdateConfig.fromJSObject(jsObject)
59
+ }
60
+ } catch (e: Exception) {
61
+ android.util.Log.e(TAG, "Failed to parse config", e)
62
+ null
63
+ }
64
+ }
65
+
66
+ private suspend fun performBackgroundCheck(config: BackgroundUpdateConfig): BackgroundCheckResult {
67
+ try {
68
+ var appUpdate: AppUpdateInfo? = null
69
+ var liveUpdate: LatestVersion? = null
70
+
71
+ // Check for app updates
72
+ if (config.updateTypes.contains(BackgroundUpdateType.APP_UPDATE) ||
73
+ config.updateTypes.contains(BackgroundUpdateType.BOTH)) {
74
+ appUpdate = checkForAppUpdate()
75
+ }
76
+
77
+ // Check for live updates
78
+ if (config.updateTypes.contains(BackgroundUpdateType.LIVE_UPDATE) ||
79
+ config.updateTypes.contains(BackgroundUpdateType.BOTH)) {
80
+ liveUpdate = checkForLiveUpdate()
81
+ }
82
+
83
+ val updatesFound = (appUpdate?.updateAvailable == true) || (liveUpdate?.available == true)
84
+
85
+ return BackgroundCheckResult(
86
+ success = true,
87
+ updatesFound = updatesFound,
88
+ appUpdate = appUpdate,
89
+ liveUpdate = liveUpdate,
90
+ notificationSent = false // Will be updated after notification is sent
91
+ )
92
+ } catch (e: Exception) {
93
+ android.util.Log.e(TAG, "Background check failed", e)
94
+ return BackgroundCheckResult(
95
+ success = false,
96
+ updatesFound = false,
97
+ error = UpdateError("UNKNOWN_ERROR", e.message ?: "Unknown error")
98
+ )
99
+ }
100
+ }
101
+
102
+ private suspend fun checkForAppUpdate(): AppUpdateInfo? {
103
+ return withContext(Dispatchers.IO) {
104
+ try {
105
+ val appUpdatePlugin = BackgroundUpdateManager.getAppUpdatePlugin()
106
+ if (appUpdatePlugin != null) {
107
+ // Use actual plugin to check for updates
108
+ appUpdatePlugin.getAppUpdateInfoAsync()
109
+ } else {
110
+ // Fallback to basic implementation
111
+ val currentVersion = getCurrentAppVersion()
112
+ val availableVersion = getAvailableAppVersion()
113
+
114
+ AppUpdateInfo(
115
+ updateAvailable = availableVersion != currentVersion,
116
+ currentVersion = currentVersion,
117
+ availableVersion = if (availableVersion != currentVersion) availableVersion else null
118
+ )
119
+ }
120
+ } catch (e: Exception) {
121
+ android.util.Log.e(TAG, "App update check failed", e)
122
+ null
123
+ }
124
+ }
125
+ }
126
+
127
+ private suspend fun checkForLiveUpdate(): LatestVersion? {
128
+ return withContext(Dispatchers.IO) {
129
+ try {
130
+ val liveUpdatePlugin = BackgroundUpdateManager.getLiveUpdatePlugin()
131
+ if (liveUpdatePlugin != null) {
132
+ // Use actual plugin to check for updates
133
+ liveUpdatePlugin.getLatestVersionAsync()
134
+ } else {
135
+ // Fallback to basic implementation
136
+ val hasUpdate = checkForLiveUpdateAvailable()
137
+
138
+ LatestVersion(
139
+ available = hasUpdate,
140
+ version = if (hasUpdate) "2.0.0" else null
141
+ )
142
+ }
143
+ } catch (e: Exception) {
144
+ android.util.Log.e(TAG, "Live update check failed", e)
145
+ null
146
+ }
147
+ }
148
+ }
149
+
150
+ private fun getCurrentAppVersion(): String {
151
+ return try {
152
+ val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
153
+ packageInfo.versionName ?: "1.0.0"
154
+ } catch (e: Exception) {
155
+ "1.0.0"
156
+ }
157
+ }
158
+
159
+ private fun getAvailableAppVersion(): String {
160
+ // This would typically check Google Play Store API
161
+ // For demo purposes, we'll return a mock version
162
+ return "1.0.1"
163
+ }
164
+
165
+ private fun checkForLiveUpdateAvailable(): Boolean {
166
+ // This would typically check the live update server
167
+ // For demo purposes, we'll return false
168
+ return false
169
+ }
170
+
171
+ private fun sendNotification(result: BackgroundCheckResult) {
172
+ try {
173
+ val plugin = getBackgroundUpdatePlugin()
174
+ val notificationManager = plugin?.getNotificationManager()
175
+
176
+ if (notificationManager != null) {
177
+ val sent = notificationManager.sendUpdateNotification(
178
+ result.appUpdate,
179
+ result.liveUpdate
180
+ )
181
+
182
+ // Update result
183
+ result.copy(notificationSent = sent)
184
+ }
185
+ } catch (e: Exception) {
186
+ android.util.Log.e(TAG, "Failed to send notification", e)
187
+ }
188
+ }
189
+
190
+ private fun updatePluginStatus(result: BackgroundCheckResult) {
191
+ try {
192
+ val plugin = getBackgroundUpdatePlugin()
193
+ val currentStatus = plugin?.getBackgroundConfig()?.let { BackgroundUpdateStatus() } ?: return
194
+
195
+ currentStatus.apply {
196
+ isRunning = false
197
+ checkCount++
198
+ lastCheckTime = System.currentTimeMillis()
199
+
200
+ if (result.success) {
201
+ lastError = null
202
+ if (result.updatesFound) {
203
+ lastUpdateTime = System.currentTimeMillis()
204
+ }
205
+ } else {
206
+ failureCount++
207
+ lastError = result.error
208
+ }
209
+ }
210
+
211
+ plugin.updateBackgroundStatus(currentStatus)
212
+ } catch (e: Exception) {
213
+ android.util.Log.e(TAG, "Failed to update plugin status", e)
214
+ }
215
+ }
216
+
217
+ private fun notifyListeners(result: BackgroundCheckResult) {
218
+ try {
219
+ val plugin = getBackgroundUpdatePlugin()
220
+
221
+ // Notify about progress
222
+ val progressData = JSObject()
223
+ progressData.put("type", if (result.appUpdate != null) "app_update" else "live_update")
224
+ progressData.put("status", if (result.success) "completed" else "failed")
225
+ progressData.put("percent", 100)
226
+
227
+ plugin?.notifyListeners("backgroundUpdateProgress", progressData)
228
+
229
+ // Notify about notification if sent
230
+ if (result.notificationSent) {
231
+ val notificationData = JSObject()
232
+ notificationData.put("type", if (result.appUpdate != null) "app_update" else "live_update")
233
+ notificationData.put("updateAvailable", result.updatesFound)
234
+ notificationData.put("action", "shown")
235
+
236
+ plugin?.notifyListeners("backgroundUpdateNotification", notificationData)
237
+ }
238
+ } catch (e: Exception) {
239
+ android.util.Log.e(TAG, "Failed to notify listeners", e)
240
+ }
241
+ }
242
+
243
+ private fun getBackgroundUpdatePlugin(): BackgroundUpdatePlugin? {
244
+ return try {
245
+ BackgroundUpdateManager.getBackgroundUpdatePlugin()
246
+ } catch (e: Exception) {
247
+ android.util.Log.e(TAG, "Failed to get plugin", e)
248
+ null
249
+ }
250
+ }
251
+ }