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,153 @@
1
+ package com.aoneahsan.nativeupdate
2
+
3
+ import android.app.Activity
4
+ import android.content.Context
5
+ import com.getcapacitor.JSObject
6
+ import com.getcapacitor.PluginCall
7
+ import com.google.android.play.core.review.ReviewInfo
8
+ import com.google.android.play.core.review.ReviewManager
9
+ import com.google.android.play.core.review.ReviewManagerFactory
10
+ import java.util.concurrent.TimeUnit
11
+
12
+ class AppReviewPlugin(
13
+ private val activity: Activity,
14
+ private val context: Context
15
+ ) {
16
+ private val reviewManager: ReviewManager = ReviewManagerFactory.create(context)
17
+ private var config: JSObject? = null
18
+ private val prefs = context.getSharedPreferences("native_update_review", Context.MODE_PRIVATE)
19
+
20
+ companion object {
21
+ private const val PREF_INSTALL_DATE = "install_date"
22
+ private const val PREF_LAST_REVIEW_REQUEST = "last_review_request"
23
+ private const val PREF_LAUNCH_COUNT = "launch_count"
24
+ private const val PREF_REVIEW_SHOWN_COUNT = "review_shown_count"
25
+ }
26
+
27
+ init {
28
+ // Initialize install date if not set
29
+ if (!prefs.contains(PREF_INSTALL_DATE)) {
30
+ prefs.edit().putLong(PREF_INSTALL_DATE, System.currentTimeMillis()).apply()
31
+ }
32
+
33
+ // Increment launch count
34
+ incrementLaunchCount()
35
+ }
36
+
37
+ fun configure(config: JSObject) {
38
+ this.config = config
39
+ }
40
+
41
+ fun requestReview(call: PluginCall) {
42
+ // First check if we can request a review
43
+ val canRequestResult = checkCanRequestReview()
44
+
45
+ if (!canRequestResult.first) {
46
+ val result = JSObject()
47
+ result.put("shown", false)
48
+ result.put("error", canRequestResult.second)
49
+ call.resolve(result)
50
+ return
51
+ }
52
+
53
+ // Request review info
54
+ val request = reviewManager.requestReviewFlow()
55
+
56
+ request.addOnCompleteListener { task ->
57
+ if (task.isSuccessful) {
58
+ val reviewInfo = task.result
59
+ launchReviewFlow(reviewInfo, call)
60
+ } else {
61
+ val result = JSObject()
62
+ result.put("shown", false)
63
+ result.put("error", "Failed to request review flow")
64
+ call.resolve(result)
65
+ }
66
+ }
67
+ }
68
+
69
+ fun canRequestReview(call: PluginCall) {
70
+ val (allowed, reason) = checkCanRequestReview()
71
+
72
+ val result = JSObject()
73
+ result.put("allowed", allowed)
74
+ if (!allowed) {
75
+ result.put("reason", reason)
76
+ }
77
+
78
+ call.resolve(result)
79
+ }
80
+
81
+ private fun launchReviewFlow(reviewInfo: ReviewInfo, call: PluginCall) {
82
+ val flow = reviewManager.launchReviewFlow(activity, reviewInfo)
83
+
84
+ flow.addOnCompleteListener { _ ->
85
+ // The flow has finished. The API does not indicate whether the user
86
+ // reviewed or not, or even whether the review dialog was shown.
87
+ // Update last review request time
88
+ prefs.edit().putLong(PREF_LAST_REVIEW_REQUEST, System.currentTimeMillis()).apply()
89
+
90
+ // Increment shown count
91
+ val shownCount = prefs.getInt(PREF_REVIEW_SHOWN_COUNT, 0)
92
+ prefs.edit().putInt(PREF_REVIEW_SHOWN_COUNT, shownCount + 1).apply()
93
+
94
+ val result = JSObject()
95
+ result.put("shown", true)
96
+ call.resolve(result)
97
+ }.addOnFailureListener { e ->
98
+ val result = JSObject()
99
+ result.put("shown", false)
100
+ result.put("error", e.message)
101
+ call.resolve(result)
102
+ }
103
+ }
104
+
105
+ private fun checkCanRequestReview(): Pair<Boolean, String?> {
106
+ val now = System.currentTimeMillis()
107
+ val installDate = prefs.getLong(PREF_INSTALL_DATE, now)
108
+ val lastReviewRequest = prefs.getLong(PREF_LAST_REVIEW_REQUEST, 0)
109
+ val launchCount = prefs.getInt(PREF_LAUNCH_COUNT, 0)
110
+ val shownCount = prefs.getInt(PREF_REVIEW_SHOWN_COUNT, 0)
111
+
112
+ // Check if debug mode is enabled
113
+ val debugMode = config?.getBool("debugMode") ?: false
114
+ if (debugMode) {
115
+ return Pair(true, null)
116
+ }
117
+
118
+ // Check minimum days since install
119
+ val minDaysSinceInstall = config?.getInteger("minimumDaysSinceInstall") ?: 7
120
+ val daysSinceInstall = TimeUnit.MILLISECONDS.toDays(now - installDate)
121
+ if (daysSinceInstall < minDaysSinceInstall) {
122
+ return Pair(false, "Not enough days since install")
123
+ }
124
+
125
+ // Check minimum days since last prompt
126
+ if (lastReviewRequest > 0) {
127
+ val minDaysSinceLastPrompt = config?.getInteger("minimumDaysSinceLastPrompt") ?: 90
128
+ val daysSinceLastPrompt = TimeUnit.MILLISECONDS.toDays(now - lastReviewRequest)
129
+ if (daysSinceLastPrompt < minDaysSinceLastPrompt) {
130
+ return Pair(false, "Too soon since last review request")
131
+ }
132
+ }
133
+
134
+ // Check minimum launch count
135
+ val minLaunchCount = config?.getInteger("minimumLaunchCount") ?: 3
136
+ if (launchCount < minLaunchCount) {
137
+ return Pair(false, "Not enough app launches")
138
+ }
139
+
140
+ // Google Play has internal quotas that we can't check directly
141
+ // But we can limit our own requests to be safe
142
+ if (shownCount >= 3) {
143
+ return Pair(false, "Review quota exceeded")
144
+ }
145
+
146
+ return Pair(true, null)
147
+ }
148
+
149
+ private fun incrementLaunchCount() {
150
+ val currentCount = prefs.getInt(PREF_LAUNCH_COUNT, 0)
151
+ prefs.edit().putInt(PREF_LAUNCH_COUNT, currentCount + 1).apply()
152
+ }
153
+ }
@@ -0,0 +1,275 @@
1
+ package com.aoneahsan.nativeupdate
2
+
3
+ import android.app.Activity
4
+ import android.content.Context
5
+ import android.content.Intent
6
+ import android.net.Uri
7
+ import com.getcapacitor.JSObject
8
+ import com.getcapacitor.PluginCall
9
+ import com.google.android.play.core.appupdate.AppUpdateInfo
10
+ import com.google.android.play.core.appupdate.AppUpdateManager
11
+ import com.google.android.play.core.appupdate.AppUpdateManagerFactory
12
+ import com.google.android.play.core.appupdate.AppUpdateOptions
13
+ import com.google.android.play.core.install.InstallState
14
+ import com.google.android.play.core.install.InstallStateUpdatedListener
15
+ import com.google.android.play.core.install.model.AppUpdateType
16
+ import com.google.android.play.core.install.model.InstallStatus
17
+ import com.google.android.play.core.install.model.UpdateAvailability
18
+ import com.google.android.play.core.ktx.isFlexibleUpdateAllowed
19
+ import com.google.android.play.core.ktx.isImmediateUpdateAllowed
20
+ import kotlinx.coroutines.tasks.await
21
+
22
+ class AppUpdatePlugin(
23
+ private val activity: Activity,
24
+ private val context: Context
25
+ ) {
26
+ private val appUpdateManager: AppUpdateManager = AppUpdateManagerFactory.create(context)
27
+ private var config: JSObject? = null
28
+ private var updateInfo: AppUpdateInfo? = null
29
+
30
+ companion object {
31
+ const val REQUEST_CODE_UPDATE = 12345
32
+ }
33
+
34
+ private val installStateUpdatedListener = InstallStateUpdatedListener { state ->
35
+ handleInstallState(state)
36
+ }
37
+
38
+ init {
39
+ appUpdateManager.registerListener(installStateUpdatedListener)
40
+ }
41
+
42
+ fun configure(config: JSObject) {
43
+ this.config = config
44
+ }
45
+
46
+ fun getAppUpdateInfo(call: PluginCall) {
47
+ val appUpdateInfoTask = appUpdateManager.appUpdateInfo
48
+
49
+ appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->
50
+ this.updateInfo = appUpdateInfo
51
+
52
+ val result = JSObject()
53
+ result.put("updateAvailable", appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE)
54
+ result.put("currentVersion", getCurrentAppVersion())
55
+ result.put("availableVersion", appUpdateInfo.availableVersionCode().toString())
56
+ result.put("updatePriority", appUpdateInfo.updatePriority())
57
+ result.put("immediateUpdateAllowed", appUpdateInfo.isImmediateUpdateAllowed)
58
+ result.put("flexibleUpdateAllowed", appUpdateInfo.isFlexibleUpdateAllowed)
59
+ result.put("clientVersionStalenessDays", appUpdateInfo.clientVersionStalenessDays() ?: -1)
60
+
61
+ // Add install status if update is in progress
62
+ if (appUpdateInfo.installStatus() != InstallStatus.UNKNOWN) {
63
+ result.put("installStatus", getInstallStatusString(appUpdateInfo.installStatus()))
64
+ result.put("bytesDownloaded", appUpdateInfo.bytesDownloaded())
65
+ result.put("totalBytesToDownload", appUpdateInfo.totalBytesToDownload())
66
+ }
67
+
68
+ call.resolve(result)
69
+ }.addOnFailureListener { e ->
70
+ call.reject("UPDATE_CHECK_FAILED", e.message)
71
+ }
72
+ }
73
+
74
+ fun performImmediateUpdate(call: PluginCall) {
75
+ val appUpdateInfo = updateInfo
76
+
77
+ if (appUpdateInfo == null) {
78
+ // Need to check for updates first
79
+ appUpdateManager.appUpdateInfo.addOnSuccessListener { info ->
80
+ this.updateInfo = info
81
+ startImmediateUpdate(info, call)
82
+ }.addOnFailureListener { e ->
83
+ call.reject("UPDATE_CHECK_FAILED", e.message)
84
+ }
85
+ } else {
86
+ startImmediateUpdate(appUpdateInfo, call)
87
+ }
88
+ }
89
+
90
+ fun startFlexibleUpdate(call: PluginCall) {
91
+ val appUpdateInfo = updateInfo
92
+
93
+ if (appUpdateInfo == null) {
94
+ // Need to check for updates first
95
+ appUpdateManager.appUpdateInfo.addOnSuccessListener { info ->
96
+ this.updateInfo = info
97
+ startFlexibleUpdateFlow(info, call)
98
+ }.addOnFailureListener { e ->
99
+ call.reject("UPDATE_CHECK_FAILED", e.message)
100
+ }
101
+ } else {
102
+ startFlexibleUpdateFlow(appUpdateInfo, call)
103
+ }
104
+ }
105
+
106
+ fun completeFlexibleUpdate(call: PluginCall) {
107
+ appUpdateManager.completeUpdate()
108
+ call.resolve()
109
+ }
110
+
111
+ fun openAppStore(call: PluginCall) {
112
+ try {
113
+ val packageName = call.getString("appId") ?: context.packageName
114
+
115
+ // Try to open in Play Store app first
116
+ try {
117
+ val intent = Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=$packageName"))
118
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
119
+ context.startActivity(intent)
120
+ } catch (e: Exception) {
121
+ // Fallback to web browser
122
+ val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=$packageName"))
123
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
124
+ context.startActivity(intent)
125
+ }
126
+
127
+ call.resolve()
128
+ } catch (e: Exception) {
129
+ call.reject("OPEN_STORE_FAILED", e.message)
130
+ }
131
+ }
132
+
133
+ private fun startImmediateUpdate(appUpdateInfo: AppUpdateInfo, call: PluginCall) {
134
+ if (!appUpdateInfo.isImmediateUpdateAllowed) {
135
+ call.reject("UPDATE_NOT_ALLOWED", "Immediate update is not allowed")
136
+ return
137
+ }
138
+
139
+ try {
140
+ val updateOptions = AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build()
141
+
142
+ appUpdateManager.startUpdateFlow(
143
+ appUpdateInfo,
144
+ activity,
145
+ updateOptions
146
+ )
147
+
148
+ call.resolve()
149
+ } catch (e: Exception) {
150
+ call.reject("UPDATE_FAILED", e.message)
151
+ }
152
+ }
153
+
154
+ private fun startFlexibleUpdateFlow(appUpdateInfo: AppUpdateInfo, call: PluginCall) {
155
+ if (!appUpdateInfo.isFlexibleUpdateAllowed) {
156
+ call.reject("UPDATE_NOT_ALLOWED", "Flexible update is not allowed")
157
+ return
158
+ }
159
+
160
+ try {
161
+ val updateOptions = AppUpdateOptions.newBuilder(AppUpdateType.FLEXIBLE).build()
162
+
163
+ appUpdateManager.startUpdateFlow(
164
+ appUpdateInfo,
165
+ activity,
166
+ updateOptions
167
+ )
168
+
169
+ call.resolve()
170
+ } catch (e: Exception) {
171
+ call.reject("UPDATE_FAILED", e.message)
172
+ }
173
+ }
174
+
175
+ private fun handleInstallState(state: InstallState) {
176
+ when (state.installStatus()) {
177
+ InstallStatus.DOWNLOADED -> {
178
+ // Update has been downloaded, prompt user to restart
179
+ popupSnackbarForCompleteUpdate()
180
+ }
181
+ InstallStatus.INSTALLED -> {
182
+ // Update installed, cleanup
183
+ appUpdateManager.unregisterListener(installStateUpdatedListener)
184
+ }
185
+ InstallStatus.FAILED -> {
186
+ // Update failed
187
+ }
188
+ else -> {
189
+ // Handle other states
190
+ }
191
+ }
192
+ }
193
+
194
+ private fun popupSnackbarForCompleteUpdate() {
195
+ // In a real implementation, show a snackbar or notification
196
+ // For now, we'll just complete the update
197
+ appUpdateManager.completeUpdate()
198
+ }
199
+
200
+ private fun getCurrentAppVersion(): String {
201
+ return try {
202
+ val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
203
+ packageInfo.versionName ?: "Unknown"
204
+ } catch (e: Exception) {
205
+ "Unknown"
206
+ }
207
+ }
208
+
209
+ private fun getInstallStatusString(status: Int): String {
210
+ return when (status) {
211
+ InstallStatus.PENDING -> "PENDING"
212
+ InstallStatus.DOWNLOADING -> "DOWNLOADING"
213
+ InstallStatus.DOWNLOADED -> "DOWNLOADED"
214
+ InstallStatus.INSTALLING -> "INSTALLING"
215
+ InstallStatus.INSTALLED -> "INSTALLED"
216
+ InstallStatus.FAILED -> "FAILED"
217
+ InstallStatus.CANCELED -> "CANCELED"
218
+ else -> "UNKNOWN"
219
+ }
220
+ }
221
+
222
+ fun onActivityResult(requestCode: Int, resultCode: Int) {
223
+ if (requestCode == REQUEST_CODE_UPDATE) {
224
+ when (resultCode) {
225
+ Activity.RESULT_OK -> {
226
+ // Update flow completed successfully
227
+ }
228
+ Activity.RESULT_CANCELED -> {
229
+ // User cancelled the update
230
+ }
231
+ else -> {
232
+ // Update flow failed
233
+ }
234
+ }
235
+ }
236
+ }
237
+
238
+ fun onResume() {
239
+ // Check if update is waiting to be installed
240
+ appUpdateManager.appUpdateInfo.addOnSuccessListener { appUpdateInfo ->
241
+ if (appUpdateInfo.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
242
+ // Resume the update
243
+ val updateOptions = AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build()
244
+ appUpdateManager.startUpdateFlow(
245
+ appUpdateInfo,
246
+ activity,
247
+ updateOptions
248
+ )
249
+ }
250
+ }
251
+ }
252
+
253
+ // Async method for background update checks
254
+ suspend fun getAppUpdateInfoAsync(): AppUpdateInfo? {
255
+ return try {
256
+ val appUpdateInfo = appUpdateManager.appUpdateInfo.await()
257
+
258
+ if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE) {
259
+ AppUpdateInfo(
260
+ updateAvailable = true,
261
+ currentVersion = getCurrentAppVersion(),
262
+ availableVersion = appUpdateInfo.availableVersionCode().toString()
263
+ )
264
+ } else {
265
+ AppUpdateInfo(
266
+ updateAvailable = false,
267
+ currentVersion = getCurrentAppVersion(),
268
+ availableVersion = null
269
+ )
270
+ }
271
+ } catch (e: Exception) {
272
+ null
273
+ }
274
+ }
275
+ }