@capgo/capacitor-updater 5.35.0 → 5.39.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/CapgoCapacitorUpdater.podspec +1 -1
- package/Package.swift +1 -1
- package/README.md +309 -5
- package/android/build.gradle +3 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +430 -49
- package/android/src/main/java/ee/forgr/capacitor_updater/CapgoUpdater.java +88 -4
- package/android/src/main/java/ee/forgr/capacitor_updater/CryptoCipher.java +19 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadService.java +27 -2
- package/dist/docs.json +609 -3
- package/dist/esm/definitions.d.ts +428 -2
- package/dist/esm/definitions.js +103 -1
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +6 -1
- package/dist/esm/web.js +24 -0
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +132 -0
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +132 -0
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/CapacitorUpdaterPlugin/CapacitorUpdaterPlugin.swift +290 -22
- package/ios/Sources/CapacitorUpdaterPlugin/CapgoUpdater.swift +106 -12
- package/ios/Sources/CapacitorUpdaterPlugin/CryptoCipher.swift +19 -0
- package/ios/Sources/CapacitorUpdaterPlugin/InternalUtils.swift +1 -0
- package/package.json +1 -1
|
@@ -55,6 +55,20 @@ import org.json.JSONArray;
|
|
|
55
55
|
import org.json.JSONException;
|
|
56
56
|
import org.json.JSONObject;
|
|
57
57
|
|
|
58
|
+
// Play Store In-App Updates
|
|
59
|
+
import com.google.android.play.core.appupdate.AppUpdateInfo;
|
|
60
|
+
import com.google.android.play.core.appupdate.AppUpdateManager;
|
|
61
|
+
import com.google.android.play.core.appupdate.AppUpdateManagerFactory;
|
|
62
|
+
import com.google.android.play.core.appupdate.AppUpdateOptions;
|
|
63
|
+
import com.google.android.play.core.install.InstallState;
|
|
64
|
+
import com.google.android.play.core.install.InstallStateUpdatedListener;
|
|
65
|
+
import com.google.android.play.core.install.model.AppUpdateType;
|
|
66
|
+
import com.google.android.play.core.install.model.InstallStatus;
|
|
67
|
+
import com.google.android.play.core.install.model.UpdateAvailability;
|
|
68
|
+
import com.google.android.gms.tasks.Task;
|
|
69
|
+
import android.content.Intent;
|
|
70
|
+
import android.net.Uri;
|
|
71
|
+
|
|
58
72
|
@CapacitorPlugin(name = "CapacitorUpdater")
|
|
59
73
|
public class CapacitorUpdaterPlugin extends Plugin {
|
|
60
74
|
|
|
@@ -72,7 +86,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
72
86
|
private static final String[] BREAKING_EVENT_NAMES = { "breakingAvailable", "majorAvailable" };
|
|
73
87
|
private static final String LAST_FAILED_BUNDLE_PREF_KEY = "CapacitorUpdater.lastFailedBundle";
|
|
74
88
|
|
|
75
|
-
private final String pluginVersion = "5.
|
|
89
|
+
private final String pluginVersion = "5.39.0";
|
|
76
90
|
private static final String DELAY_CONDITION_PREFERENCES = "";
|
|
77
91
|
|
|
78
92
|
private SharedPreferences.Editor editor;
|
|
@@ -111,6 +125,11 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
111
125
|
// private static final CountDownLatch semaphoreReady = new CountDownLatch(1);
|
|
112
126
|
private static final Phaser semaphoreReady = new Phaser(1);
|
|
113
127
|
|
|
128
|
+
// Lock to ensure cleanup completes before downloads start
|
|
129
|
+
private final Object cleanupLock = new Object();
|
|
130
|
+
private volatile boolean cleanupComplete = false;
|
|
131
|
+
private volatile Thread cleanupThread = null;
|
|
132
|
+
|
|
114
133
|
private int lastNotifiedStatPercent = 0;
|
|
115
134
|
|
|
116
135
|
private DelayUpdateUtils delayUpdateUtils;
|
|
@@ -120,6 +139,12 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
120
139
|
private FrameLayout splashscreenLoaderOverlay;
|
|
121
140
|
private Runnable splashscreenTimeoutRunnable;
|
|
122
141
|
|
|
142
|
+
// Play Store In-App Updates
|
|
143
|
+
private AppUpdateManager appUpdateManager;
|
|
144
|
+
private AppUpdateInfo cachedAppUpdateInfo;
|
|
145
|
+
private static final int APP_UPDATE_REQUEST_CODE = 9001;
|
|
146
|
+
private InstallStateUpdatedListener installStateUpdatedListener;
|
|
147
|
+
|
|
123
148
|
private void notifyBreakingEvents(final String version) {
|
|
124
149
|
if (version == null || version.isEmpty()) {
|
|
125
150
|
return;
|
|
@@ -304,7 +329,12 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
304
329
|
this.persistCustomId = this.getConfig().getBoolean("persistCustomId", false);
|
|
305
330
|
this.persistModifyUrl = this.getConfig().getBoolean("persistModifyUrl", false);
|
|
306
331
|
this.allowSetDefaultChannel = this.getConfig().getBoolean("allowSetDefaultChannel", true);
|
|
307
|
-
this.implementation.
|
|
332
|
+
this.implementation.setPublicKey(this.getConfig().getString("publicKey", ""));
|
|
333
|
+
// Log public key prefix if encryption is enabled
|
|
334
|
+
String keyId = this.implementation.getKeyId();
|
|
335
|
+
if (keyId != null && !keyId.isEmpty()) {
|
|
336
|
+
logger.info("Public key prefix: " + keyId);
|
|
337
|
+
}
|
|
308
338
|
this.implementation.statsUrl = this.getConfig().getString("statsUrl", statsUrlDefault);
|
|
309
339
|
this.implementation.channelUrl = this.getConfig().getString("channelUrl", channelUrlDefault);
|
|
310
340
|
if (Boolean.TRUE.equals(this.persistModifyUrl)) {
|
|
@@ -377,7 +407,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
377
407
|
}
|
|
378
408
|
}
|
|
379
409
|
this.autoUpdate = this.getConfig().getBoolean("autoUpdate", true);
|
|
380
|
-
this.appReadyTimeout = this.getConfig().getInt("appReadyTimeout", 10000);
|
|
410
|
+
this.appReadyTimeout = Math.max(1000, this.getConfig().getInt("appReadyTimeout", 10000)); // Minimum 1 second
|
|
381
411
|
this.keepUrlPathAfterReload = this.getConfig().getBoolean("keepUrlPathAfterReload", false);
|
|
382
412
|
this.syncKeepUrlPathFlag(this.keepUrlPathAfterReload);
|
|
383
413
|
this.allowManualBundleError = this.getConfig().getBoolean("allowManualBundleError", false);
|
|
@@ -696,39 +726,83 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
696
726
|
}
|
|
697
727
|
|
|
698
728
|
private void cleanupObsoleteVersions() {
|
|
699
|
-
startNewThread(() -> {
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
729
|
+
cleanupThread = startNewThread(() -> {
|
|
730
|
+
synchronized (cleanupLock) {
|
|
731
|
+
try {
|
|
732
|
+
final String previous = this.prefs.getString("LatestNativeBuildVersion", "");
|
|
733
|
+
if (!"".equals(previous) && !Objects.equals(this.currentBuildVersion, previous)) {
|
|
734
|
+
logger.info("New native build version detected: " + this.currentBuildVersion);
|
|
735
|
+
this.implementation.reset(true);
|
|
736
|
+
final List<BundleInfo> installed = this.implementation.list(false);
|
|
737
|
+
for (final BundleInfo bundle : installed) {
|
|
738
|
+
// Check if thread was interrupted (cancelled)
|
|
739
|
+
if (Thread.currentThread().isInterrupted()) {
|
|
740
|
+
logger.warn("Cleanup was cancelled, stopping");
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
try {
|
|
744
|
+
logger.info("Deleting obsolete bundle: " + bundle.getId());
|
|
745
|
+
this.implementation.delete(bundle.getId());
|
|
746
|
+
} catch (final Exception e) {
|
|
747
|
+
logger.error("Failed to delete: " + bundle.getId() + " " + e.getMessage());
|
|
748
|
+
}
|
|
712
749
|
}
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
750
|
+
final List<BundleInfo> storedBundles = this.implementation.list(true);
|
|
751
|
+
final Set<String> allowedIds = new HashSet<>();
|
|
752
|
+
for (final BundleInfo info : storedBundles) {
|
|
753
|
+
if (info != null && info.getId() != null && !info.getId().isEmpty()) {
|
|
754
|
+
allowedIds.add(info.getId());
|
|
755
|
+
}
|
|
719
756
|
}
|
|
757
|
+
this.implementation.cleanupDownloadDirectories(allowedIds, Thread.currentThread());
|
|
758
|
+
this.implementation.cleanupOrphanedTempFolders(Thread.currentThread());
|
|
759
|
+
|
|
760
|
+
// Check again before the expensive delta cache cleanup
|
|
761
|
+
if (Thread.currentThread().isInterrupted()) {
|
|
762
|
+
logger.warn("Cleanup was cancelled before delta cache cleanup");
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
765
|
+
this.implementation.cleanupDeltaCache(Thread.currentThread());
|
|
720
766
|
}
|
|
721
|
-
this.
|
|
722
|
-
this.
|
|
767
|
+
this.editor.putString("LatestNativeBuildVersion", this.currentBuildVersion);
|
|
768
|
+
this.editor.apply();
|
|
769
|
+
} catch (Exception e) {
|
|
770
|
+
logger.error("Error during cleanupObsoleteVersions: " + e.getMessage());
|
|
771
|
+
} finally {
|
|
772
|
+
cleanupComplete = true;
|
|
773
|
+
logger.info("Cleanup complete");
|
|
723
774
|
}
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
775
|
+
}
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
// Start a timeout watchdog thread to cancel cleanup if it takes too long
|
|
779
|
+
final long timeout = this.appReadyTimeout / 2;
|
|
780
|
+
startNewThread(() -> {
|
|
781
|
+
try {
|
|
782
|
+
Thread.sleep(timeout);
|
|
783
|
+
if (cleanupThread != null && cleanupThread.isAlive() && !cleanupComplete) {
|
|
784
|
+
logger.warn("Cleanup timeout exceeded (" + timeout + "ms), interrupting cleanup thread");
|
|
785
|
+
cleanupThread.interrupt();
|
|
786
|
+
}
|
|
787
|
+
} catch (InterruptedException e) {
|
|
788
|
+
// Watchdog thread was interrupted, that's fine
|
|
728
789
|
}
|
|
729
790
|
});
|
|
730
791
|
}
|
|
731
792
|
|
|
793
|
+
private void waitForCleanupIfNeeded() {
|
|
794
|
+
if (cleanupComplete) {
|
|
795
|
+
return; // Already done, no need to wait
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
logger.info("Waiting for cleanup to complete before starting download...");
|
|
799
|
+
|
|
800
|
+
// Wait for cleanup to complete - blocks until lock is released
|
|
801
|
+
synchronized (cleanupLock) {
|
|
802
|
+
logger.info("Cleanup finished, proceeding with download");
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
|
|
732
806
|
public void notifyDownload(final String id, final int percent) {
|
|
733
807
|
try {
|
|
734
808
|
final JSObject ret = new JSObject();
|
|
@@ -1674,6 +1748,8 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1674
1748
|
? "Update will occur now."
|
|
1675
1749
|
: "Update will occur next time app moves to background.";
|
|
1676
1750
|
return startNewThread(() -> {
|
|
1751
|
+
// Wait for cleanup to complete before starting download
|
|
1752
|
+
waitForCleanupIfNeeded();
|
|
1677
1753
|
logger.info("Check for update via: " + CapacitorUpdaterPlugin.this.updateUrl);
|
|
1678
1754
|
try {
|
|
1679
1755
|
CapacitorUpdaterPlugin.this.implementation.getLatest(CapacitorUpdaterPlugin.this.updateUrl, null, (res) -> {
|
|
@@ -2135,27 +2211,6 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
2135
2211
|
}
|
|
2136
2212
|
}
|
|
2137
2213
|
|
|
2138
|
-
@Override
|
|
2139
|
-
public void handleOnDestroy() {
|
|
2140
|
-
try {
|
|
2141
|
-
logger.info("onActivityDestroyed " + getActivity().getClass().getName());
|
|
2142
|
-
this.implementation.activity = getActivity();
|
|
2143
|
-
|
|
2144
|
-
// Clean up shake menu
|
|
2145
|
-
if (shakeMenu != null) {
|
|
2146
|
-
try {
|
|
2147
|
-
shakeMenu.stop();
|
|
2148
|
-
shakeMenu = null;
|
|
2149
|
-
logger.info("Shake menu cleaned up");
|
|
2150
|
-
} catch (Exception e) {
|
|
2151
|
-
logger.error("Failed to clean up shake menu: " + e.getMessage());
|
|
2152
|
-
}
|
|
2153
|
-
}
|
|
2154
|
-
} catch (Exception e) {
|
|
2155
|
-
logger.error("Failed to run handleOnDestroy: " + e.getMessage());
|
|
2156
|
-
}
|
|
2157
|
-
}
|
|
2158
|
-
|
|
2159
2214
|
@PluginMethod
|
|
2160
2215
|
public void setShakeMenu(final PluginCall call) {
|
|
2161
2216
|
final Boolean enabled = call.getBoolean("enabled");
|
|
@@ -2229,4 +2284,330 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
2229
2284
|
this.implementation.appId = appId;
|
|
2230
2285
|
call.resolve();
|
|
2231
2286
|
}
|
|
2287
|
+
|
|
2288
|
+
// ============================================================================
|
|
2289
|
+
// Play Store In-App Update Methods
|
|
2290
|
+
// ============================================================================
|
|
2291
|
+
|
|
2292
|
+
// AppUpdateAvailability enum values matching TypeScript definitions
|
|
2293
|
+
private static final int UPDATE_AVAILABILITY_UNKNOWN = 0;
|
|
2294
|
+
private static final int UPDATE_AVAILABILITY_NOT_AVAILABLE = 1;
|
|
2295
|
+
private static final int UPDATE_AVAILABILITY_AVAILABLE = 2;
|
|
2296
|
+
private static final int UPDATE_AVAILABILITY_IN_PROGRESS = 3;
|
|
2297
|
+
|
|
2298
|
+
// AppUpdateResultCode enum values matching TypeScript definitions
|
|
2299
|
+
private static final int RESULT_OK = 0;
|
|
2300
|
+
private static final int RESULT_CANCELED = 1;
|
|
2301
|
+
private static final int RESULT_FAILED = 2;
|
|
2302
|
+
private static final int RESULT_NOT_AVAILABLE = 3;
|
|
2303
|
+
private static final int RESULT_NOT_ALLOWED = 4;
|
|
2304
|
+
private static final int RESULT_INFO_MISSING = 5;
|
|
2305
|
+
|
|
2306
|
+
private AppUpdateManager getAppUpdateManager() {
|
|
2307
|
+
if (appUpdateManager == null) {
|
|
2308
|
+
appUpdateManager = AppUpdateManagerFactory.create(getContext());
|
|
2309
|
+
}
|
|
2310
|
+
return appUpdateManager;
|
|
2311
|
+
}
|
|
2312
|
+
|
|
2313
|
+
private int mapUpdateAvailability(int playStoreAvailability) {
|
|
2314
|
+
switch (playStoreAvailability) {
|
|
2315
|
+
case UpdateAvailability.UPDATE_AVAILABLE:
|
|
2316
|
+
return UPDATE_AVAILABILITY_AVAILABLE;
|
|
2317
|
+
case UpdateAvailability.UPDATE_NOT_AVAILABLE:
|
|
2318
|
+
return UPDATE_AVAILABILITY_NOT_AVAILABLE;
|
|
2319
|
+
case UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS:
|
|
2320
|
+
return UPDATE_AVAILABILITY_IN_PROGRESS;
|
|
2321
|
+
default:
|
|
2322
|
+
return UPDATE_AVAILABILITY_UNKNOWN;
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2325
|
+
|
|
2326
|
+
@PluginMethod
|
|
2327
|
+
public void getAppUpdateInfo(final PluginCall call) {
|
|
2328
|
+
logger.info("Getting Play Store update info");
|
|
2329
|
+
|
|
2330
|
+
try {
|
|
2331
|
+
AppUpdateManager manager = getAppUpdateManager();
|
|
2332
|
+
Task<AppUpdateInfo> appUpdateInfoTask = manager.getAppUpdateInfo();
|
|
2333
|
+
|
|
2334
|
+
appUpdateInfoTask.addOnSuccessListener(appUpdateInfo -> {
|
|
2335
|
+
cachedAppUpdateInfo = appUpdateInfo;
|
|
2336
|
+
|
|
2337
|
+
JSObject result = new JSObject();
|
|
2338
|
+
try {
|
|
2339
|
+
PackageInfo pInfo = getContext().getPackageManager().getPackageInfo(getContext().getPackageName(), 0);
|
|
2340
|
+
result.put("currentVersionName", pInfo.versionName);
|
|
2341
|
+
result.put("currentVersionCode", String.valueOf(pInfo.versionCode));
|
|
2342
|
+
} catch (PackageManager.NameNotFoundException e) {
|
|
2343
|
+
result.put("currentVersionName", "0.0.0");
|
|
2344
|
+
result.put("currentVersionCode", "0");
|
|
2345
|
+
}
|
|
2346
|
+
|
|
2347
|
+
result.put("updateAvailability", mapUpdateAvailability(appUpdateInfo.updateAvailability()));
|
|
2348
|
+
|
|
2349
|
+
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE) {
|
|
2350
|
+
result.put("availableVersionCode", String.valueOf(appUpdateInfo.availableVersionCode()));
|
|
2351
|
+
// Play Store doesn't provide version name, only version code
|
|
2352
|
+
result.put("availableVersionName", String.valueOf(appUpdateInfo.availableVersionCode()));
|
|
2353
|
+
result.put("updatePriority", appUpdateInfo.updatePriority());
|
|
2354
|
+
result.put("immediateUpdateAllowed", appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE));
|
|
2355
|
+
result.put("flexibleUpdateAllowed", appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE));
|
|
2356
|
+
|
|
2357
|
+
Integer stalenessDays = appUpdateInfo.clientVersionStalenessDays();
|
|
2358
|
+
if (stalenessDays != null) {
|
|
2359
|
+
result.put("clientVersionStalenessDays", stalenessDays);
|
|
2360
|
+
}
|
|
2361
|
+
} else {
|
|
2362
|
+
result.put("immediateUpdateAllowed", false);
|
|
2363
|
+
result.put("flexibleUpdateAllowed", false);
|
|
2364
|
+
}
|
|
2365
|
+
|
|
2366
|
+
result.put("installStatus", appUpdateInfo.installStatus());
|
|
2367
|
+
|
|
2368
|
+
call.resolve(result);
|
|
2369
|
+
}).addOnFailureListener(e -> {
|
|
2370
|
+
logger.error("Failed to get app update info: " + e.getMessage());
|
|
2371
|
+
call.reject("Failed to get app update info: " + e.getMessage());
|
|
2372
|
+
});
|
|
2373
|
+
} catch (Exception e) {
|
|
2374
|
+
logger.error("Error getting app update info: " + e.getMessage());
|
|
2375
|
+
call.reject("Error getting app update info: " + e.getMessage());
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
|
|
2379
|
+
@PluginMethod
|
|
2380
|
+
public void openAppStore(final PluginCall call) {
|
|
2381
|
+
String packageName = call.getString("packageName");
|
|
2382
|
+
if (packageName == null || packageName.isEmpty()) {
|
|
2383
|
+
packageName = getContext().getPackageName();
|
|
2384
|
+
}
|
|
2385
|
+
|
|
2386
|
+
try {
|
|
2387
|
+
// Try to open Play Store app first
|
|
2388
|
+
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + packageName));
|
|
2389
|
+
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
2390
|
+
getContext().startActivity(intent);
|
|
2391
|
+
call.resolve();
|
|
2392
|
+
} catch (android.content.ActivityNotFoundException e) {
|
|
2393
|
+
// Fall back to browser
|
|
2394
|
+
try {
|
|
2395
|
+
Intent intent = new Intent(Intent.ACTION_VIEW,
|
|
2396
|
+
Uri.parse("https://play.google.com/store/apps/details?id=" + packageName));
|
|
2397
|
+
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
2398
|
+
getContext().startActivity(intent);
|
|
2399
|
+
call.resolve();
|
|
2400
|
+
} catch (Exception ex) {
|
|
2401
|
+
logger.error("Failed to open Play Store: " + ex.getMessage());
|
|
2402
|
+
call.reject("Failed to open Play Store: " + ex.getMessage());
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
|
|
2407
|
+
@PluginMethod
|
|
2408
|
+
public void performImmediateUpdate(final PluginCall call) {
|
|
2409
|
+
if (cachedAppUpdateInfo == null) {
|
|
2410
|
+
logger.error("No update info available. Call getAppUpdateInfo first.");
|
|
2411
|
+
JSObject result = new JSObject();
|
|
2412
|
+
result.put("code", RESULT_INFO_MISSING);
|
|
2413
|
+
call.resolve(result);
|
|
2414
|
+
return;
|
|
2415
|
+
}
|
|
2416
|
+
|
|
2417
|
+
if (cachedAppUpdateInfo.updateAvailability() != UpdateAvailability.UPDATE_AVAILABLE) {
|
|
2418
|
+
logger.info("No update available");
|
|
2419
|
+
JSObject result = new JSObject();
|
|
2420
|
+
result.put("code", RESULT_NOT_AVAILABLE);
|
|
2421
|
+
call.resolve(result);
|
|
2422
|
+
return;
|
|
2423
|
+
}
|
|
2424
|
+
|
|
2425
|
+
if (!cachedAppUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
|
|
2426
|
+
logger.info("Immediate update not allowed");
|
|
2427
|
+
JSObject result = new JSObject();
|
|
2428
|
+
result.put("code", RESULT_NOT_ALLOWED);
|
|
2429
|
+
call.resolve(result);
|
|
2430
|
+
return;
|
|
2431
|
+
}
|
|
2432
|
+
|
|
2433
|
+
try {
|
|
2434
|
+
Activity activity = getActivity();
|
|
2435
|
+
if (activity == null) {
|
|
2436
|
+
call.reject("Activity not available");
|
|
2437
|
+
return;
|
|
2438
|
+
}
|
|
2439
|
+
|
|
2440
|
+
// Save the call for later resolution
|
|
2441
|
+
bridge.saveCall(call);
|
|
2442
|
+
|
|
2443
|
+
AppUpdateManager manager = getAppUpdateManager();
|
|
2444
|
+
manager.startUpdateFlowForResult(
|
|
2445
|
+
cachedAppUpdateInfo,
|
|
2446
|
+
activity,
|
|
2447
|
+
AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build(),
|
|
2448
|
+
APP_UPDATE_REQUEST_CODE
|
|
2449
|
+
);
|
|
2450
|
+
} catch (Exception e) {
|
|
2451
|
+
logger.error("Failed to start immediate update: " + e.getMessage());
|
|
2452
|
+
JSObject result = new JSObject();
|
|
2453
|
+
result.put("code", RESULT_FAILED);
|
|
2454
|
+
call.resolve(result);
|
|
2455
|
+
}
|
|
2456
|
+
}
|
|
2457
|
+
|
|
2458
|
+
@PluginMethod
|
|
2459
|
+
public void startFlexibleUpdate(final PluginCall call) {
|
|
2460
|
+
if (cachedAppUpdateInfo == null) {
|
|
2461
|
+
logger.error("No update info available. Call getAppUpdateInfo first.");
|
|
2462
|
+
JSObject result = new JSObject();
|
|
2463
|
+
result.put("code", RESULT_INFO_MISSING);
|
|
2464
|
+
call.resolve(result);
|
|
2465
|
+
return;
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2468
|
+
if (cachedAppUpdateInfo.updateAvailability() != UpdateAvailability.UPDATE_AVAILABLE) {
|
|
2469
|
+
logger.info("No update available");
|
|
2470
|
+
JSObject result = new JSObject();
|
|
2471
|
+
result.put("code", RESULT_NOT_AVAILABLE);
|
|
2472
|
+
call.resolve(result);
|
|
2473
|
+
return;
|
|
2474
|
+
}
|
|
2475
|
+
|
|
2476
|
+
if (!cachedAppUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) {
|
|
2477
|
+
logger.info("Flexible update not allowed");
|
|
2478
|
+
JSObject result = new JSObject();
|
|
2479
|
+
result.put("code", RESULT_NOT_ALLOWED);
|
|
2480
|
+
call.resolve(result);
|
|
2481
|
+
return;
|
|
2482
|
+
}
|
|
2483
|
+
|
|
2484
|
+
try {
|
|
2485
|
+
Activity activity = getActivity();
|
|
2486
|
+
if (activity == null) {
|
|
2487
|
+
call.reject("Activity not available");
|
|
2488
|
+
return;
|
|
2489
|
+
}
|
|
2490
|
+
|
|
2491
|
+
// Register listener for flexible update state changes
|
|
2492
|
+
AppUpdateManager manager = getAppUpdateManager();
|
|
2493
|
+
|
|
2494
|
+
// Remove any existing listener
|
|
2495
|
+
if (installStateUpdatedListener != null) {
|
|
2496
|
+
manager.unregisterListener(installStateUpdatedListener);
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2499
|
+
installStateUpdatedListener = state -> {
|
|
2500
|
+
JSObject eventData = new JSObject();
|
|
2501
|
+
eventData.put("installStatus", state.installStatus());
|
|
2502
|
+
|
|
2503
|
+
if (state.installStatus() == InstallStatus.DOWNLOADING) {
|
|
2504
|
+
eventData.put("bytesDownloaded", state.bytesDownloaded());
|
|
2505
|
+
eventData.put("totalBytesToDownload", state.totalBytesToDownload());
|
|
2506
|
+
}
|
|
2507
|
+
|
|
2508
|
+
notifyListeners("onFlexibleUpdateStateChange", eventData);
|
|
2509
|
+
};
|
|
2510
|
+
|
|
2511
|
+
manager.registerListener(installStateUpdatedListener);
|
|
2512
|
+
|
|
2513
|
+
// Save the call for later resolution
|
|
2514
|
+
bridge.saveCall(call);
|
|
2515
|
+
|
|
2516
|
+
manager.startUpdateFlowForResult(
|
|
2517
|
+
cachedAppUpdateInfo,
|
|
2518
|
+
activity,
|
|
2519
|
+
AppUpdateOptions.newBuilder(AppUpdateType.FLEXIBLE).build(),
|
|
2520
|
+
APP_UPDATE_REQUEST_CODE
|
|
2521
|
+
);
|
|
2522
|
+
} catch (Exception e) {
|
|
2523
|
+
logger.error("Failed to start flexible update: " + e.getMessage());
|
|
2524
|
+
JSObject result = new JSObject();
|
|
2525
|
+
result.put("code", RESULT_FAILED);
|
|
2526
|
+
call.resolve(result);
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
|
|
2530
|
+
@PluginMethod
|
|
2531
|
+
public void completeFlexibleUpdate(final PluginCall call) {
|
|
2532
|
+
try {
|
|
2533
|
+
AppUpdateManager manager = getAppUpdateManager();
|
|
2534
|
+
manager.completeUpdate()
|
|
2535
|
+
.addOnSuccessListener(aVoid -> {
|
|
2536
|
+
// The app will restart, so this may not be called
|
|
2537
|
+
call.resolve();
|
|
2538
|
+
})
|
|
2539
|
+
.addOnFailureListener(e -> {
|
|
2540
|
+
logger.error("Failed to complete flexible update: " + e.getMessage());
|
|
2541
|
+
call.reject("Failed to complete flexible update: " + e.getMessage());
|
|
2542
|
+
});
|
|
2543
|
+
} catch (Exception e) {
|
|
2544
|
+
logger.error("Error completing flexible update: " + e.getMessage());
|
|
2545
|
+
call.reject("Error completing flexible update: " + e.getMessage());
|
|
2546
|
+
}
|
|
2547
|
+
}
|
|
2548
|
+
|
|
2549
|
+
@Override
|
|
2550
|
+
protected void handleOnActivityResult(int requestCode, int resultCode, Intent data) {
|
|
2551
|
+
super.handleOnActivityResult(requestCode, resultCode, data);
|
|
2552
|
+
|
|
2553
|
+
if (requestCode == APP_UPDATE_REQUEST_CODE) {
|
|
2554
|
+
PluginCall savedCall = bridge.getSavedCall("com.getcapacitor.PluginCall");
|
|
2555
|
+
if (savedCall == null) {
|
|
2556
|
+
// Try to get any saved call (for backward compatibility)
|
|
2557
|
+
return;
|
|
2558
|
+
}
|
|
2559
|
+
|
|
2560
|
+
JSObject result = new JSObject();
|
|
2561
|
+
if (resultCode == Activity.RESULT_OK) {
|
|
2562
|
+
result.put("code", RESULT_OK);
|
|
2563
|
+
} else if (resultCode == Activity.RESULT_CANCELED) {
|
|
2564
|
+
result.put("code", RESULT_CANCELED);
|
|
2565
|
+
} else {
|
|
2566
|
+
result.put("code", RESULT_FAILED);
|
|
2567
|
+
}
|
|
2568
|
+
savedCall.resolve(result);
|
|
2569
|
+
bridge.releaseCall(savedCall);
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
|
|
2573
|
+
@Override
|
|
2574
|
+
protected void handleOnDestroy() {
|
|
2575
|
+
// Clean up the install state listener
|
|
2576
|
+
if (installStateUpdatedListener != null && appUpdateManager != null) {
|
|
2577
|
+
try {
|
|
2578
|
+
appUpdateManager.unregisterListener(installStateUpdatedListener);
|
|
2579
|
+
installStateUpdatedListener = null;
|
|
2580
|
+
} catch (Exception e) {
|
|
2581
|
+
logger.error("Failed to unregister install state listener: " + e.getMessage());
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2584
|
+
|
|
2585
|
+
handleOnDestroyInternal();
|
|
2586
|
+
}
|
|
2587
|
+
|
|
2588
|
+
private void handleOnDestroyInternal() {
|
|
2589
|
+
// Original handleOnDestroy code
|
|
2590
|
+
try {
|
|
2591
|
+
logger.info("onActivityDestroyed " + getActivity().getClass().getName());
|
|
2592
|
+
this.implementation.activity = getActivity();
|
|
2593
|
+
|
|
2594
|
+
// Check for 'kill' delay condition on activity destroy
|
|
2595
|
+
// Note: onDestroy is not reliably called - also check on next app launch
|
|
2596
|
+
this.delayUpdateUtils.checkCancelDelay(DelayUpdateUtils.CancelDelaySource.KILLED);
|
|
2597
|
+
this.delayUpdateUtils.setBackgroundTimestamp(0);
|
|
2598
|
+
|
|
2599
|
+
// Clean up shake menu
|
|
2600
|
+
if (shakeMenu != null) {
|
|
2601
|
+
try {
|
|
2602
|
+
shakeMenu.stop();
|
|
2603
|
+
shakeMenu = null;
|
|
2604
|
+
logger.info("Shake menu cleaned up");
|
|
2605
|
+
} catch (Exception e) {
|
|
2606
|
+
logger.error("Failed to clean up shake menu: " + e.getMessage());
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
} catch (Exception e) {
|
|
2610
|
+
logger.error("Failed to run handleOnDestroy: " + e.getMessage());
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2232
2613
|
}
|