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.
- package/CapacitorNativeUpdate.podspec +18 -0
- package/LICENSE +21 -0
- package/Readme.md +451 -0
- package/android/build.gradle +92 -0
- package/android/gradle/wrapper/gradle-wrapper.properties +8 -0
- package/android/gradle.properties +17 -0
- package/android/proguard-rules.pro +29 -0
- package/android/settings.gradle +2 -0
- package/android/src/main/AndroidManifest.xml +34 -0
- package/android/src/main/java/com/aoneahsan/nativeupdate/AppReviewPlugin.kt +153 -0
- package/android/src/main/java/com/aoneahsan/nativeupdate/AppUpdatePlugin.kt +275 -0
- package/android/src/main/java/com/aoneahsan/nativeupdate/BackgroundNotificationManager.kt +390 -0
- package/android/src/main/java/com/aoneahsan/nativeupdate/BackgroundUpdateManager.kt +46 -0
- package/android/src/main/java/com/aoneahsan/nativeupdate/BackgroundUpdatePlugin.kt +333 -0
- package/android/src/main/java/com/aoneahsan/nativeupdate/BackgroundUpdateWorker.kt +251 -0
- package/android/src/main/java/com/aoneahsan/nativeupdate/CapacitorNativeUpdatePlugin.kt +265 -0
- package/android/src/main/java/com/aoneahsan/nativeupdate/LiveUpdatePlugin.kt +526 -0
- package/android/src/main/java/com/aoneahsan/nativeupdate/NotificationActionReceiver.kt +99 -0
- package/android/src/main/java/com/aoneahsan/nativeupdate/SecurityManager.kt +249 -0
- package/dist/esm/__tests__/bundle-manager.test.d.ts +1 -0
- package/dist/esm/__tests__/bundle-manager.test.js +123 -0
- package/dist/esm/__tests__/bundle-manager.test.js.map +1 -0
- package/dist/esm/__tests__/config.test.d.ts +1 -0
- package/dist/esm/__tests__/config.test.js +69 -0
- package/dist/esm/__tests__/config.test.js.map +1 -0
- package/dist/esm/__tests__/integration.test.d.ts +1 -0
- package/dist/esm/__tests__/integration.test.js +78 -0
- package/dist/esm/__tests__/integration.test.js.map +1 -0
- package/dist/esm/__tests__/security.test.d.ts +1 -0
- package/dist/esm/__tests__/security.test.js +54 -0
- package/dist/esm/__tests__/security.test.js.map +1 -0
- package/dist/esm/__tests__/version-manager.test.d.ts +1 -0
- package/dist/esm/__tests__/version-manager.test.js +45 -0
- package/dist/esm/__tests__/version-manager.test.js.map +1 -0
- package/dist/esm/app-review/app-review-manager.d.ts +24 -0
- package/dist/esm/app-review/app-review-manager.js +195 -0
- package/dist/esm/app-review/app-review-manager.js.map +1 -0
- package/dist/esm/app-review/index.d.ts +5 -0
- package/dist/esm/app-review/index.js +6 -0
- package/dist/esm/app-review/index.js.map +1 -0
- package/dist/esm/app-review/platform-review-handler.d.ts +20 -0
- package/dist/esm/app-review/platform-review-handler.js +138 -0
- package/dist/esm/app-review/platform-review-handler.js.map +1 -0
- package/dist/esm/app-review/review-conditions-checker.d.ts +22 -0
- package/dist/esm/app-review/review-conditions-checker.js +155 -0
- package/dist/esm/app-review/review-conditions-checker.js.map +1 -0
- package/dist/esm/app-review/review-rate-limiter.d.ts +23 -0
- package/dist/esm/app-review/review-rate-limiter.js +164 -0
- package/dist/esm/app-review/review-rate-limiter.js.map +1 -0
- package/dist/esm/app-review/types.d.ts +41 -0
- package/dist/esm/app-review/types.js +2 -0
- package/dist/esm/app-review/types.js.map +1 -0
- package/dist/esm/app-update/app-update-checker.d.ts +13 -0
- package/dist/esm/app-update/app-update-checker.js +104 -0
- package/dist/esm/app-update/app-update-checker.js.map +1 -0
- package/dist/esm/app-update/app-update-installer.d.ts +19 -0
- package/dist/esm/app-update/app-update-installer.js +123 -0
- package/dist/esm/app-update/app-update-installer.js.map +1 -0
- package/dist/esm/app-update/app-update-manager.d.ts +28 -0
- package/dist/esm/app-update/app-update-manager.js +199 -0
- package/dist/esm/app-update/app-update-manager.js.map +1 -0
- package/dist/esm/app-update/app-update-notifier.d.ts +14 -0
- package/dist/esm/app-update/app-update-notifier.js +100 -0
- package/dist/esm/app-update/app-update-notifier.js.map +1 -0
- package/dist/esm/app-update/index.d.ts +6 -0
- package/dist/esm/app-update/index.js +7 -0
- package/dist/esm/app-update/index.js.map +1 -0
- package/dist/esm/app-update/platform-app-update.d.ts +19 -0
- package/dist/esm/app-update/platform-app-update.js +129 -0
- package/dist/esm/app-update/platform-app-update.js.map +1 -0
- package/dist/esm/app-update/types.d.ts +58 -0
- package/dist/esm/app-update/types.js +12 -0
- package/dist/esm/app-update/types.js.map +1 -0
- package/dist/esm/background-update/background-scheduler.d.ts +17 -0
- package/dist/esm/background-update/background-scheduler.js +195 -0
- package/dist/esm/background-update/background-scheduler.js.map +1 -0
- package/dist/esm/background-update/index.d.ts +3 -0
- package/dist/esm/background-update/index.js +3 -0
- package/dist/esm/background-update/index.js.map +1 -0
- package/dist/esm/background-update/notification-manager.d.ts +29 -0
- package/dist/esm/background-update/notification-manager.js +89 -0
- package/dist/esm/background-update/notification-manager.js.map +1 -0
- package/dist/esm/core/analytics.d.ts +70 -0
- package/dist/esm/core/analytics.js +137 -0
- package/dist/esm/core/analytics.js.map +1 -0
- package/dist/esm/core/cache-manager.d.ts +72 -0
- package/dist/esm/core/cache-manager.js +275 -0
- package/dist/esm/core/cache-manager.js.map +1 -0
- package/dist/esm/core/config.d.ts +48 -0
- package/dist/esm/core/config.js +83 -0
- package/dist/esm/core/config.js.map +1 -0
- package/dist/esm/core/errors.d.ts +51 -0
- package/dist/esm/core/errors.js +80 -0
- package/dist/esm/core/errors.js.map +1 -0
- package/dist/esm/core/logger.d.ts +21 -0
- package/dist/esm/core/logger.js +109 -0
- package/dist/esm/core/logger.js.map +1 -0
- package/dist/esm/core/performance.d.ts +53 -0
- package/dist/esm/core/performance.js +140 -0
- package/dist/esm/core/performance.js.map +1 -0
- package/dist/esm/core/plugin-manager.d.ts +66 -0
- package/dist/esm/core/plugin-manager.js +148 -0
- package/dist/esm/core/plugin-manager.js.map +1 -0
- package/dist/esm/core/security.d.ts +93 -0
- package/dist/esm/core/security.js +315 -0
- package/dist/esm/core/security.js.map +1 -0
- package/dist/esm/definitions.d.ts +639 -0
- package/dist/esm/definitions.js +103 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +12 -0
- package/dist/esm/index.js +16 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/live-update/bundle-manager.d.ts +94 -0
- package/dist/esm/live-update/bundle-manager.js +310 -0
- package/dist/esm/live-update/bundle-manager.js.map +1 -0
- package/dist/esm/live-update/certificate-pinning.d.ts +38 -0
- package/dist/esm/live-update/certificate-pinning.js +78 -0
- package/dist/esm/live-update/certificate-pinning.js.map +1 -0
- package/dist/esm/live-update/download-manager.d.ts +67 -0
- package/dist/esm/live-update/download-manager.js +319 -0
- package/dist/esm/live-update/download-manager.js.map +1 -0
- package/dist/esm/live-update/update-manager.d.ts +52 -0
- package/dist/esm/live-update/update-manager.js +294 -0
- package/dist/esm/live-update/update-manager.js.map +1 -0
- package/dist/esm/live-update/version-manager.d.ts +84 -0
- package/dist/esm/live-update/version-manager.js +335 -0
- package/dist/esm/live-update/version-manager.js.map +1 -0
- package/dist/esm/plugin.d.ts +6 -0
- package/dist/esm/plugin.js +283 -0
- package/dist/esm/plugin.js.map +1 -0
- package/dist/esm/security/crypto.d.ts +25 -0
- package/dist/esm/security/crypto.js +70 -0
- package/dist/esm/security/crypto.js.map +1 -0
- package/dist/esm/security/validator.d.ts +60 -0
- package/dist/esm/security/validator.js +143 -0
- package/dist/esm/security/validator.js.map +1 -0
- package/dist/esm/web.d.ts +74 -0
- package/dist/esm/web.js +595 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +2 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.esm.js +2 -0
- package/dist/plugin.esm.js.map +1 -0
- package/dist/plugin.js +3 -0
- package/dist/plugin.js.map +1 -0
- package/docs/APP_REVIEW_GUIDE.md +768 -0
- package/docs/BUNDLE_SIGNING.md +264 -0
- package/docs/LIVE_UPDATES_GUIDE.md +650 -0
- package/docs/MIGRATION.md +192 -0
- package/docs/NATIVE_UPDATES_GUIDE.md +694 -0
- package/docs/QUICK_START.md +606 -0
- package/docs/README.md +111 -0
- package/docs/REMAINING_FEATURES.md +139 -0
- package/docs/api/app-review-api.md +259 -0
- package/docs/api/app-update-api.md +238 -0
- package/docs/api/events-api.md +451 -0
- package/docs/api/live-update-api.md +265 -0
- package/docs/background-updates.md +392 -0
- package/docs/examples/advanced-scenarios.md +410 -0
- package/docs/examples/basic-usage.md +185 -0
- package/docs/features/app-reviews.md +975 -0
- package/docs/features/app-updates.md +785 -0
- package/docs/features/live-updates.md +633 -0
- package/docs/getting-started/configuration.md +468 -0
- package/docs/getting-started/installation.md +209 -0
- package/docs/getting-started/quick-start.md +379 -0
- package/docs/guides/deployment-guide.md +333 -0
- package/docs/guides/migration-from-codepush.md +142 -0
- package/docs/guides/security-best-practices.md +1057 -0
- package/docs/guides/testing-guide.md +373 -0
- package/docs/production-readiness.md +478 -0
- package/docs/security/certificate-pinning.md +122 -0
- package/docs/server-requirements.md +147 -0
- package/ios/Plugin/AppReview/AppReviewPlugin.swift +158 -0
- package/ios/Plugin/AppUpdate/AppUpdatePlugin.swift +234 -0
- package/ios/Plugin/BackgroundUpdate/BackgroundNotificationManager.swift +329 -0
- package/ios/Plugin/BackgroundUpdate/BackgroundUpdatePlugin.swift +396 -0
- package/ios/Plugin/CapacitorNativeUpdatePlugin.m +45 -0
- package/ios/Plugin/CapacitorNativeUpdatePlugin.swift +190 -0
- package/ios/Plugin/Info.plist +43 -0
- package/ios/Plugin/LiveUpdate/LiveUpdatePlugin.swift +689 -0
- package/ios/Plugin/LiveUpdate/WebViewConfiguration.swift +45 -0
- package/ios/Plugin/Security/SecurityManager.swift +289 -0
- 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
|
+
}
|