@capgo/capacitor-updater 8.45.8 → 8.45.9

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.
@@ -88,8 +88,9 @@ public class CapacitorUpdaterPlugin extends Plugin {
88
88
  private static final String SPLASH_SCREEN_PLUGIN_ID = "SplashScreen";
89
89
  private static final int SPLASH_SCREEN_RETRY_DELAY_MS = 100;
90
90
  private static final int SPLASH_SCREEN_MAX_RETRIES = 20;
91
+ private static final long PENDING_BUNDLE_APP_READY_MIN_TIMEOUT_MS = 30000L;
91
92
 
92
- private final String pluginVersion = "8.45.8";
93
+ private final String pluginVersion = "8.45.9";
93
94
  private static final String DELAY_CONDITION_PREFERENCES = "";
94
95
 
95
96
  private SharedPreferences.Editor editor;
@@ -134,8 +135,12 @@ public class CapacitorUpdaterPlugin extends Plugin {
134
135
  private volatile long downloadStartTimeMs = 0;
135
136
  private static final long DOWNLOAD_TIMEOUT_MS = 3600000; // 1 hour timeout
136
137
 
137
- // private static final CountDownLatch semaphoreReady = new CountDownLatch(1);
138
- private static final Phaser semaphoreReady = new Phaser(1);
138
+ private final Phaser semaphoreReady = new Phaser(0) {
139
+ @Override
140
+ protected boolean onAdvance(final int phase, final int registeredParties) {
141
+ return false;
142
+ }
143
+ };
139
144
 
140
145
  // Lock to ensure cleanup completes before downloads start
141
146
  private final Object cleanupLock = new Object();
@@ -284,13 +289,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
284
289
 
285
290
  @Override
286
291
  public void directUpdateFinish(final BundleInfo latest) {
287
- if (activity != null) {
288
- activity.runOnUiThread(() -> {
289
- CapacitorUpdaterPlugin.this.directUpdateFinish(latest);
290
- });
291
- } else {
292
- logger.warn("directUpdateFinish: Activity is null, skipping notification");
293
- }
292
+ CapacitorUpdaterPlugin.this.scheduleDirectUpdateFinish(latest);
294
293
  }
295
294
 
296
295
  @Override
@@ -521,30 +520,69 @@ public class CapacitorUpdaterPlugin extends Plugin {
521
520
  }
522
521
  }
523
522
 
524
- private void semaphoreWait(Number waitTime) {
523
+ private boolean semaphoreWait(final int phase, Number waitTime) {
525
524
  try {
526
- semaphoreReady.awaitAdvanceInterruptibly(semaphoreReady.getPhase(), waitTime.longValue(), TimeUnit.SECONDS);
525
+ semaphoreReady.awaitAdvanceInterruptibly(phase, waitTime.longValue(), TimeUnit.MILLISECONDS);
527
526
  logger.info("semaphoreReady count " + semaphoreReady.getPhase());
527
+ return true;
528
528
  } catch (InterruptedException e) {
529
529
  logger.info("semaphoreWait InterruptedException");
530
+ cleanupTimedOutSemaphoreWait(phase);
530
531
  Thread.currentThread().interrupt(); // Restore interrupted status
532
+ return false;
531
533
  } catch (TimeoutException e) {
532
534
  logger.error("Semaphore timeout: " + e.getMessage());
533
- // Don't throw runtime exception, just log and continue
535
+ cleanupTimedOutSemaphoreWait(phase);
536
+ return false;
534
537
  }
535
538
  }
536
539
 
537
- private void semaphoreUp() {
540
+ private int semaphoreUp() {
538
541
  logger.info("semaphoreUp");
539
- semaphoreReady.register();
542
+ return semaphoreReady.register();
540
543
  }
541
544
 
542
545
  private void semaphoreDown() {
546
+ if (semaphoreReady.getRegisteredParties() == 0) {
547
+ logger.info("semaphoreDown skipped, no pending app ready wait");
548
+ return;
549
+ }
543
550
  logger.info("semaphoreDown");
544
551
  logger.info("semaphoreDown count " + semaphoreReady.getPhase());
545
552
  semaphoreReady.arriveAndDeregister();
546
553
  }
547
554
 
555
+ private void cleanupTimedOutSemaphoreWait(final int phase) {
556
+ if (semaphoreReady.getPhase() != phase || semaphoreReady.getRegisteredParties() == 0) {
557
+ return;
558
+ }
559
+ logger.info("Cleaning up stale app ready wait for phase " + phase);
560
+ semaphoreReady.arriveAndDeregister();
561
+ }
562
+
563
+ protected long getMinimumPendingBundleAppReadyTimeoutMs() {
564
+ return PENDING_BUNDLE_APP_READY_MIN_TIMEOUT_MS;
565
+ }
566
+
567
+ private long resolveAppReadyCheckTimeoutMs() {
568
+ long configuredTimeoutMs = this.appReadyTimeout.longValue();
569
+ try {
570
+ if (this.implementation == null) {
571
+ return configuredTimeoutMs;
572
+ }
573
+
574
+ final BundleInfo current = this.implementation.getCurrentBundle();
575
+ if (current == null || BundleStatus.SUCCESS == current.getStatus()) {
576
+ return configuredTimeoutMs;
577
+ }
578
+
579
+ return Math.max(configuredTimeoutMs, this.getMinimumPendingBundleAppReadyTimeoutMs());
580
+ } catch (final Exception e) {
581
+ logger.warn("Falling back to configured appReadyTimeout: " + e.getMessage());
582
+ return configuredTimeoutMs;
583
+ }
584
+ }
585
+
548
586
  private void sendReadyToJs(final BundleInfo current, final String msg) {
549
587
  sendReadyToJs(current, msg, false);
550
588
  }
@@ -864,6 +902,22 @@ public class CapacitorUpdaterPlugin extends Plugin {
864
902
  this.endBackGroundTaskWithNotif("test", current.getVersionName(), current, false, plannedDirectUpdate);
865
903
  }
866
904
 
905
+ void scheduleDirectUpdateFinish(final BundleInfo latest) {
906
+ startNewThread(() -> {
907
+ try {
908
+ Activity currentActivity = this.getActivity();
909
+ if (currentActivity != null) {
910
+ this.implementation.activity = currentActivity;
911
+ } else {
912
+ logger.warn("directUpdateFinish: Activity is null, proceeding without refreshing the activity reference");
913
+ }
914
+ this.directUpdateFinish(latest);
915
+ } catch (final Exception e) {
916
+ logger.error("directUpdateFinish failed: " + e.getMessage());
917
+ }
918
+ });
919
+ }
920
+
867
921
  private void directUpdateFinish(final BundleInfo latest) {
868
922
  if ("onLaunch".equals(this.directUpdateMode)) {
869
923
  this.onLaunchDirectUpdateUsed = true;
@@ -1438,21 +1492,15 @@ public class CapacitorUpdaterPlugin extends Plugin {
1438
1492
  }
1439
1493
 
1440
1494
  protected boolean _reload() {
1441
- this.semaphoreUp();
1495
+ final int phase = this.semaphoreUp();
1442
1496
  this.applyCurrentBundleToBridge();
1443
1497
 
1444
- this.checkAppReady();
1498
+ final long waitTimeMs = this.resolveAppReadyCheckTimeoutMs();
1499
+ this.checkAppReady(waitTimeMs);
1445
1500
  this.notifyListeners("appReloaded", new JSObject());
1446
1501
 
1447
1502
  // Wait for the reload to complete (until notifyAppReady is called)
1448
- try {
1449
- this.semaphoreWait(this.appReadyTimeout);
1450
- } catch (Exception e) {
1451
- logger.error("Error waiting for app ready: " + e.getMessage());
1452
- return false;
1453
- }
1454
-
1455
- return true;
1503
+ return this.semaphoreWait(phase, waitTimeMs);
1456
1504
  }
1457
1505
 
1458
1506
  @PluginMethod
@@ -1929,11 +1977,15 @@ public class CapacitorUpdaterPlugin extends Plugin {
1929
1977
  }
1930
1978
 
1931
1979
  private void checkAppReady() {
1980
+ this.checkAppReady(this.resolveAppReadyCheckTimeoutMs());
1981
+ }
1982
+
1983
+ private void checkAppReady(final long waitTimeMs) {
1932
1984
  try {
1933
1985
  if (this.appReadyCheck != null) {
1934
1986
  this.appReadyCheck.interrupt();
1935
1987
  }
1936
- this.appReadyCheck = startNewThread(new DeferredNotifyAppReadyCheck());
1988
+ this.appReadyCheck = startNewThread(new DeferredNotifyAppReadyCheck(waitTimeMs));
1937
1989
  } catch (final Exception e) {
1938
1990
  logger.error("Failed to start " + DeferredNotifyAppReadyCheck.class.getName() + " " + e.getMessage());
1939
1991
  }
@@ -2325,16 +2377,18 @@ public class CapacitorUpdaterPlugin extends Plugin {
2325
2377
  if (next != null && !next.isErrorStatus() && !next.getId().equals(current.getId())) {
2326
2378
  // There is a next bundle waiting for activation
2327
2379
  logger.debug("Next bundle is: " + next.getVersionName());
2328
- if (this.implementation.set(next) && this._reload()) {
2329
- logger.info("Updated to bundle: " + next.getVersionName());
2330
- this.notifyBundleSet(next);
2331
- this.implementation.setNextBundle(null);
2332
- } else {
2333
- logger.error("Update to bundle: " + next.getVersionName() + " Failed!");
2334
- }
2380
+ startNewThread(() -> {
2381
+ if (this.implementation.set(next) && this._reload()) {
2382
+ logger.info("Updated to bundle: " + next.getVersionName());
2383
+ this.notifyBundleSet(next);
2384
+ this.implementation.setNextBundle(null);
2385
+ } else {
2386
+ logger.error("Update to bundle: " + next.getVersionName() + " Failed!");
2387
+ }
2388
+ });
2335
2389
  }
2336
2390
  } catch (final Exception e) {
2337
- logger.error("Error during onActivityStopped " + e.getMessage());
2391
+ logger.error("Error during installNext " + e);
2338
2392
  }
2339
2393
  }
2340
2394
 
@@ -2376,11 +2430,17 @@ public class CapacitorUpdaterPlugin extends Plugin {
2376
2430
 
2377
2431
  private class DeferredNotifyAppReadyCheck implements Runnable {
2378
2432
 
2433
+ private final long waitTimeMs;
2434
+
2435
+ DeferredNotifyAppReadyCheck(final long waitTimeMs) {
2436
+ this.waitTimeMs = waitTimeMs;
2437
+ }
2438
+
2379
2439
  @Override
2380
2440
  public void run() {
2381
2441
  try {
2382
- logger.info("Wait for " + CapacitorUpdaterPlugin.this.appReadyTimeout + "ms, then check for notifyAppReady");
2383
- Thread.sleep(CapacitorUpdaterPlugin.this.appReadyTimeout);
2442
+ logger.info("Wait for " + this.waitTimeMs + "ms, then check for notifyAppReady");
2443
+ Thread.sleep(this.waitTimeMs);
2384
2444
  CapacitorUpdaterPlugin.this.checkRevert();
2385
2445
  CapacitorUpdaterPlugin.this.appReadyCheck = null;
2386
2446
  } catch (final InterruptedException e) {
@@ -72,7 +72,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
72
72
  CAPPluginMethod(name: "completeFlexibleUpdate", returnType: CAPPluginReturnPromise)
73
73
  ]
74
74
  public var implementation = CapgoUpdater()
75
- private let pluginVersion: String = "8.45.8"
75
+ private let pluginVersion: String = "8.45.9"
76
76
  static let updateUrlDefault = "https://plugin.capgo.app/updates"
77
77
  static let statsUrlDefault = "https://plugin.capgo.app/stats"
78
78
  static let channelUrlDefault = "https://plugin.capgo.app/channel_self"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-updater",
3
- "version": "8.45.8",
3
+ "version": "8.45.9",
4
4
  "license": "MPL-2.0",
5
5
  "description": "Live update for capacitor apps",
6
6
  "main": "dist/plugin.cjs.js",
@@ -48,6 +48,7 @@
48
48
  "test": "bun run test:ios && bun run test:android",
49
49
  "test:ios": "./scripts/test-ios.sh",
50
50
  "test:android": "cd android && ./gradlew test && cd ..",
51
+ "test:maestro": "./scripts/maestro/run-android-live-update.sh",
51
52
  "test:maestro:android": "./scripts/test-maestro-android.sh",
52
53
  "test:maestro:ios": "./scripts/test-maestro-ios.sh",
53
54
  "lint": "bun run eslint && bun run prettier -- --check && bun run swiftlint -- lint",