@capgo/capacitor-updater 7.43.3 → 7.45.10
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/Package.swift +5 -2
- package/README.md +149 -39
- package/android/build.gradle +3 -3
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +537 -172
- package/android/src/main/java/ee/forgr/capacitor_updater/CapgoUpdater.java +170 -35
- package/android/src/main/java/ee/forgr/capacitor_updater/DelayUpdateUtils.java +49 -13
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadService.java +38 -13
- package/android/src/main/java/ee/forgr/capacitor_updater/ShakeMenu.java +49 -9
- package/dist/docs.json +290 -10
- package/dist/esm/definitions.d.ts +134 -22
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +1 -2
- package/dist/esm/web.js +0 -4
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +0 -4
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +0 -4
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/CapacitorUpdaterPlugin/CapacitorUpdaterPlugin.swift +557 -135
- package/ios/Sources/CapacitorUpdaterPlugin/CapgoUpdater.swift +213 -50
- package/ios/Sources/CapacitorUpdaterPlugin/DelayUpdateUtils.swift +37 -16
- package/ios/Sources/CapacitorUpdaterPlugin/InternalUtils.swift +2 -0
- package/ios/Sources/CapacitorUpdaterPlugin/ShakeMenu.swift +20 -3
- package/package.json +11 -8
|
@@ -22,6 +22,8 @@ import android.view.View;
|
|
|
22
22
|
import android.view.ViewGroup;
|
|
23
23
|
import android.widget.FrameLayout;
|
|
24
24
|
import android.widget.ProgressBar;
|
|
25
|
+
import androidx.core.content.pm.PackageInfoCompat;
|
|
26
|
+
import com.getcapacitor.Bridge;
|
|
25
27
|
import com.getcapacitor.CapConfig;
|
|
26
28
|
import com.getcapacitor.JSArray;
|
|
27
29
|
import com.getcapacitor.JSObject;
|
|
@@ -29,6 +31,7 @@ import com.getcapacitor.Plugin;
|
|
|
29
31
|
import com.getcapacitor.PluginCall;
|
|
30
32
|
import com.getcapacitor.PluginHandle;
|
|
31
33
|
import com.getcapacitor.PluginMethod;
|
|
34
|
+
import com.getcapacitor.PluginResult;
|
|
32
35
|
import com.getcapacitor.annotation.CapacitorPlugin;
|
|
33
36
|
import com.getcapacitor.plugin.WebView;
|
|
34
37
|
import com.google.android.gms.tasks.Task;
|
|
@@ -47,7 +50,6 @@ import java.io.IOException;
|
|
|
47
50
|
import java.net.MalformedURLException;
|
|
48
51
|
import java.net.URL;
|
|
49
52
|
import java.util.ArrayList;
|
|
50
|
-
import java.util.Arrays;
|
|
51
53
|
import java.util.Date;
|
|
52
54
|
import java.util.HashSet;
|
|
53
55
|
import java.util.List;
|
|
@@ -56,7 +58,6 @@ import java.util.Objects;
|
|
|
56
58
|
import java.util.Set;
|
|
57
59
|
import java.util.Timer;
|
|
58
60
|
import java.util.TimerTask;
|
|
59
|
-
import java.util.UUID;
|
|
60
61
|
import java.util.concurrent.Phaser;
|
|
61
62
|
import java.util.concurrent.Semaphore;
|
|
62
63
|
import java.util.concurrent.TimeUnit;
|
|
@@ -83,8 +84,12 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
83
84
|
private static final String DEFAULT_CHANNEL_PREF_KEY = "CapacitorUpdater.defaultChannel";
|
|
84
85
|
private static final String[] BREAKING_EVENT_NAMES = { "breakingAvailable", "majorAvailable" };
|
|
85
86
|
private static final String LAST_FAILED_BUNDLE_PREF_KEY = "CapacitorUpdater.lastFailedBundle";
|
|
87
|
+
private static final String SPLASH_SCREEN_PLUGIN_ID = "SplashScreen";
|
|
88
|
+
private static final int SPLASH_SCREEN_RETRY_DELAY_MS = 100;
|
|
89
|
+
private static final int SPLASH_SCREEN_MAX_RETRIES = 20;
|
|
90
|
+
private static final long PENDING_BUNDLE_APP_READY_MIN_TIMEOUT_MS = 30000L;
|
|
86
91
|
|
|
87
|
-
private final String pluginVersion = "7.
|
|
92
|
+
private final String pluginVersion = "7.45.10";
|
|
88
93
|
private static final String DELAY_CONDITION_PREFERENCES = "";
|
|
89
94
|
|
|
90
95
|
private SharedPreferences.Editor editor;
|
|
@@ -108,9 +113,10 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
108
113
|
private Boolean autoSplashscreenLoader = false;
|
|
109
114
|
private Integer autoSplashscreenTimeout = 10000;
|
|
110
115
|
private Boolean autoSplashscreenTimedOut = false;
|
|
116
|
+
private int splashscreenInvocationToken = 0;
|
|
111
117
|
private String directUpdateMode = "false";
|
|
112
118
|
private Boolean wasRecentlyInstalledOrUpdated = false;
|
|
113
|
-
private
|
|
119
|
+
private volatile boolean onLaunchDirectUpdateUsed = false;
|
|
114
120
|
Boolean shakeMenuEnabled = false;
|
|
115
121
|
Boolean shakeChannelSelectorEnabled = false;
|
|
116
122
|
private Boolean allowManualBundleError = false;
|
|
@@ -128,8 +134,12 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
128
134
|
private volatile long downloadStartTimeMs = 0;
|
|
129
135
|
private static final long DOWNLOAD_TIMEOUT_MS = 3600000; // 1 hour timeout
|
|
130
136
|
|
|
131
|
-
|
|
132
|
-
|
|
137
|
+
private final Phaser semaphoreReady = new Phaser(0) {
|
|
138
|
+
@Override
|
|
139
|
+
protected boolean onAdvance(final int phase, final int registeredParties) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
};
|
|
133
143
|
|
|
134
144
|
// Lock to ensure cleanup completes before downloads start
|
|
135
145
|
private final Object cleanupLock = new Object();
|
|
@@ -145,6 +155,28 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
145
155
|
private FrameLayout splashscreenLoaderOverlay;
|
|
146
156
|
private Runnable splashscreenTimeoutRunnable;
|
|
147
157
|
|
|
158
|
+
private static final class FireAndForgetPluginCall extends PluginCall {
|
|
159
|
+
|
|
160
|
+
FireAndForgetPluginCall(final String methodName, final JSObject data) {
|
|
161
|
+
super(null, SPLASH_SCREEN_PLUGIN_ID, PluginCall.CALLBACK_ID_DANGLING, methodName, data);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
@Override
|
|
165
|
+
public void successCallback(final PluginResult successResult) {}
|
|
166
|
+
|
|
167
|
+
@Override
|
|
168
|
+
public void resolve(final JSObject data) {}
|
|
169
|
+
|
|
170
|
+
@Override
|
|
171
|
+
public void resolve() {}
|
|
172
|
+
|
|
173
|
+
@Override
|
|
174
|
+
public void errorCallback(final String msg) {}
|
|
175
|
+
|
|
176
|
+
@Override
|
|
177
|
+
public void reject(final String msg, final String code, final Exception ex, final JSObject data) {}
|
|
178
|
+
}
|
|
179
|
+
|
|
148
180
|
// App lifecycle observer using ProcessLifecycleOwner for reliable foreground/background detection
|
|
149
181
|
private AppLifecycleObserver appLifecycleObserver;
|
|
150
182
|
|
|
@@ -154,6 +186,19 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
154
186
|
private static final int APP_UPDATE_REQUEST_CODE = 9001;
|
|
155
187
|
private InstallStateUpdatedListener installStateUpdatedListener;
|
|
156
188
|
|
|
189
|
+
private PackageInfo getCurrentPackageInfo() throws PackageManager.NameNotFoundException {
|
|
190
|
+
final PackageManager packageManager = this.getContext().getPackageManager();
|
|
191
|
+
final String packageName = this.getContext().getPackageName();
|
|
192
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
193
|
+
return packageManager.getPackageInfo(packageName, PackageManager.PackageInfoFlags.of(0));
|
|
194
|
+
}
|
|
195
|
+
return packageManager.getPackageInfo(packageName, 0);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private String getVersionCode(final PackageInfo packageInfo) {
|
|
199
|
+
return Long.toString(PackageInfoCompat.getLongVersionCode(packageInfo));
|
|
200
|
+
}
|
|
201
|
+
|
|
157
202
|
private void notifyBreakingEvents(final String version) {
|
|
158
203
|
if (version == null || version.isEmpty()) {
|
|
159
204
|
return;
|
|
@@ -243,13 +288,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
243
288
|
|
|
244
289
|
@Override
|
|
245
290
|
public void directUpdateFinish(final BundleInfo latest) {
|
|
246
|
-
|
|
247
|
-
activity.runOnUiThread(() -> {
|
|
248
|
-
CapacitorUpdaterPlugin.this.directUpdateFinish(latest);
|
|
249
|
-
});
|
|
250
|
-
} else {
|
|
251
|
-
logger.warn("directUpdateFinish: Activity is null, skipping notification");
|
|
252
|
-
}
|
|
291
|
+
CapacitorUpdaterPlugin.this.scheduleDirectUpdateFinish(latest);
|
|
253
292
|
}
|
|
254
293
|
|
|
255
294
|
@Override
|
|
@@ -263,12 +302,12 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
263
302
|
}
|
|
264
303
|
}
|
|
265
304
|
};
|
|
266
|
-
final PackageInfo pInfo = this.
|
|
305
|
+
final PackageInfo pInfo = this.getCurrentPackageInfo();
|
|
267
306
|
this.implementation.activity = this.getActivity();
|
|
268
307
|
this.implementation.versionBuild = this.getConfig().getString("version", pInfo.versionName);
|
|
269
308
|
this.implementation.CAP_SERVER_PATH = WebView.CAP_SERVER_PATH;
|
|
270
309
|
this.implementation.pluginVersion = this.pluginVersion;
|
|
271
|
-
this.implementation.versionCode =
|
|
310
|
+
this.implementation.versionCode = this.getVersionCode(pInfo);
|
|
272
311
|
// Removed unused OkHttpClient creation - using shared client in DownloadService instead
|
|
273
312
|
// Handle directUpdate configuration - support string values and backward compatibility
|
|
274
313
|
String directUpdateConfig = this.getConfig().getString("directUpdate", null);
|
|
@@ -310,7 +349,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
310
349
|
}
|
|
311
350
|
}
|
|
312
351
|
this.currentVersionNative = new Version(this.getConfig().getString("version", pInfo.versionName));
|
|
313
|
-
this.currentBuildVersion =
|
|
352
|
+
this.currentBuildVersion = this.getVersionCode(pInfo);
|
|
314
353
|
this.delayUpdateUtils = new DelayUpdateUtils(this.prefs, this.editor, this.currentVersionNative, logger);
|
|
315
354
|
} catch (final PackageManager.NameNotFoundException e) {
|
|
316
355
|
logger.error("Error instantiating implementation " + e.getMessage());
|
|
@@ -480,34 +519,82 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
480
519
|
}
|
|
481
520
|
}
|
|
482
521
|
|
|
483
|
-
private
|
|
522
|
+
private boolean semaphoreWait(final int phase, Number waitTime) {
|
|
484
523
|
try {
|
|
485
|
-
semaphoreReady.awaitAdvanceInterruptibly(
|
|
524
|
+
semaphoreReady.awaitAdvanceInterruptibly(phase, waitTime.longValue(), TimeUnit.MILLISECONDS);
|
|
486
525
|
logger.info("semaphoreReady count " + semaphoreReady.getPhase());
|
|
526
|
+
return true;
|
|
487
527
|
} catch (InterruptedException e) {
|
|
488
528
|
logger.info("semaphoreWait InterruptedException");
|
|
529
|
+
cleanupTimedOutSemaphoreWait(phase);
|
|
489
530
|
Thread.currentThread().interrupt(); // Restore interrupted status
|
|
531
|
+
return false;
|
|
490
532
|
} catch (TimeoutException e) {
|
|
491
533
|
logger.error("Semaphore timeout: " + e.getMessage());
|
|
492
|
-
|
|
534
|
+
cleanupTimedOutSemaphoreWait(phase);
|
|
535
|
+
return false;
|
|
493
536
|
}
|
|
494
537
|
}
|
|
495
538
|
|
|
496
|
-
private
|
|
539
|
+
private int semaphoreUp() {
|
|
497
540
|
logger.info("semaphoreUp");
|
|
498
|
-
semaphoreReady.register();
|
|
541
|
+
return semaphoreReady.register();
|
|
499
542
|
}
|
|
500
543
|
|
|
501
544
|
private void semaphoreDown() {
|
|
545
|
+
if (semaphoreReady.getRegisteredParties() == 0) {
|
|
546
|
+
logger.info("semaphoreDown skipped, no pending app ready wait");
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
502
549
|
logger.info("semaphoreDown");
|
|
503
550
|
logger.info("semaphoreDown count " + semaphoreReady.getPhase());
|
|
504
551
|
semaphoreReady.arriveAndDeregister();
|
|
505
552
|
}
|
|
506
553
|
|
|
554
|
+
private void cleanupTimedOutSemaphoreWait(final int phase) {
|
|
555
|
+
if (semaphoreReady.getPhase() != phase || semaphoreReady.getRegisteredParties() == 0) {
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
logger.info("Cleaning up stale app ready wait for phase " + phase);
|
|
559
|
+
semaphoreReady.arriveAndDeregister();
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
protected long getMinimumPendingBundleAppReadyTimeoutMs() {
|
|
563
|
+
return PENDING_BUNDLE_APP_READY_MIN_TIMEOUT_MS;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
private long resolveAppReadyCheckTimeoutMs() {
|
|
567
|
+
long configuredTimeoutMs = this.appReadyTimeout.longValue();
|
|
568
|
+
try {
|
|
569
|
+
if (this.implementation == null) {
|
|
570
|
+
return configuredTimeoutMs;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
final BundleInfo current = this.implementation.getCurrentBundle();
|
|
574
|
+
if (current == null || BundleStatus.SUCCESS == current.getStatus()) {
|
|
575
|
+
return configuredTimeoutMs;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
return Math.max(configuredTimeoutMs, this.getMinimumPendingBundleAppReadyTimeoutMs());
|
|
579
|
+
} catch (final Exception e) {
|
|
580
|
+
logger.warn("Falling back to configured appReadyTimeout: " + e.getMessage());
|
|
581
|
+
return configuredTimeoutMs;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
507
585
|
private void sendReadyToJs(final BundleInfo current, final String msg) {
|
|
508
586
|
sendReadyToJs(current, msg, false);
|
|
509
587
|
}
|
|
510
588
|
|
|
589
|
+
private void notifyBundleSet(final BundleInfo bundle) {
|
|
590
|
+
if (bundle == null) {
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
final JSObject ret = new JSObject();
|
|
594
|
+
ret.put("bundle", InternalUtils.mapToJSObject(bundle.toJSONMap()));
|
|
595
|
+
this.notifyListeners("set", ret, true);
|
|
596
|
+
}
|
|
597
|
+
|
|
511
598
|
private void sendReadyToJs(final BundleInfo current, final String msg, final boolean isDirectUpdate) {
|
|
512
599
|
logger.info("sendReadyToJs: " + msg);
|
|
513
600
|
final JSObject ret = new JSObject();
|
|
@@ -535,47 +622,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
535
622
|
private void hideSplashscreenInternal() {
|
|
536
623
|
cancelSplashscreenTimeout();
|
|
537
624
|
removeSplashscreenLoader();
|
|
538
|
-
|
|
539
|
-
try {
|
|
540
|
-
if (getBridge() == null) {
|
|
541
|
-
logger.warn("Bridge not ready for hiding splashscreen with autoSplashscreen");
|
|
542
|
-
return;
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
// Try to call the SplashScreen plugin directly through the bridge
|
|
546
|
-
PluginHandle splashScreenPlugin = getBridge().getPlugin("SplashScreen");
|
|
547
|
-
if (splashScreenPlugin != null) {
|
|
548
|
-
try {
|
|
549
|
-
// Create a plugin call for the hide method using reflection to access private msgHandler
|
|
550
|
-
JSObject options = new JSObject();
|
|
551
|
-
java.lang.reflect.Field msgHandlerField = getBridge().getClass().getDeclaredField("msgHandler");
|
|
552
|
-
msgHandlerField.setAccessible(true);
|
|
553
|
-
Object msgHandler = msgHandlerField.get(getBridge());
|
|
554
|
-
|
|
555
|
-
PluginCall call = new PluginCall(
|
|
556
|
-
(com.getcapacitor.MessageHandler) msgHandler,
|
|
557
|
-
"SplashScreen",
|
|
558
|
-
"FAKE_CALLBACK_ID_HIDE",
|
|
559
|
-
"hide",
|
|
560
|
-
options
|
|
561
|
-
);
|
|
562
|
-
|
|
563
|
-
// Call the hide method directly
|
|
564
|
-
splashScreenPlugin.invoke("hide", call);
|
|
565
|
-
logger.info("Splashscreen hidden automatically via direct plugin call");
|
|
566
|
-
} catch (Exception e) {
|
|
567
|
-
logger.error("Failed to call SplashScreen hide method: " + e.getMessage());
|
|
568
|
-
}
|
|
569
|
-
} else {
|
|
570
|
-
logger.warn("autoSplashscreen: SplashScreen plugin not found. Install @capacitor/splash-screen plugin.");
|
|
571
|
-
}
|
|
572
|
-
} catch (Exception e) {
|
|
573
|
-
logger.error(
|
|
574
|
-
"Error hiding splashscreen with autoSplashscreen: " +
|
|
575
|
-
e.getMessage() +
|
|
576
|
-
". Make sure @capacitor/splash-screen plugin is installed and configured."
|
|
577
|
-
);
|
|
578
|
-
}
|
|
625
|
+
invokeSplashScreenPluginMethod("hide", new JSObject(), SPLASH_SCREEN_MAX_RETRIES, ++this.splashscreenInvocationToken);
|
|
579
626
|
}
|
|
580
627
|
|
|
581
628
|
private void showSplashscreen() {
|
|
@@ -590,37 +637,87 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
590
637
|
cancelSplashscreenTimeout();
|
|
591
638
|
this.autoSplashscreenTimedOut = false;
|
|
592
639
|
|
|
640
|
+
final JSObject options = new JSObject();
|
|
641
|
+
options.put("autoHide", false);
|
|
642
|
+
invokeSplashScreenPluginMethod("show", options, SPLASH_SCREEN_MAX_RETRIES, ++this.splashscreenInvocationToken);
|
|
643
|
+
|
|
644
|
+
addSplashscreenLoaderIfNeeded();
|
|
645
|
+
scheduleSplashscreenTimeout();
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
private void invokeSplashScreenPluginMethod(
|
|
649
|
+
final String methodName,
|
|
650
|
+
final JSObject options,
|
|
651
|
+
final int retriesRemaining,
|
|
652
|
+
final int requestToken
|
|
653
|
+
) {
|
|
654
|
+
if (requestToken != this.splashscreenInvocationToken) {
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
|
|
593
658
|
try {
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
(com.getcapacitor.MessageHandler) msgHandler,
|
|
606
|
-
"SplashScreen",
|
|
607
|
-
"FAKE_CALLBACK_ID_SHOW",
|
|
608
|
-
"show",
|
|
609
|
-
options
|
|
610
|
-
);
|
|
659
|
+
final Bridge bridge = getBridge();
|
|
660
|
+
if (bridge == null) {
|
|
661
|
+
retrySplashScreenInvocation(
|
|
662
|
+
methodName,
|
|
663
|
+
options,
|
|
664
|
+
retriesRemaining,
|
|
665
|
+
requestToken,
|
|
666
|
+
"Bridge not ready for " + ("show".equals(methodName) ? "showing" : "hiding") + " splashscreen"
|
|
667
|
+
);
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
611
670
|
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
671
|
+
final PluginHandle splashScreenPlugin = bridge.getPlugin(SPLASH_SCREEN_PLUGIN_ID);
|
|
672
|
+
if (splashScreenPlugin == null) {
|
|
673
|
+
retrySplashScreenInvocation(
|
|
674
|
+
methodName,
|
|
675
|
+
options,
|
|
676
|
+
retriesRemaining,
|
|
677
|
+
requestToken,
|
|
678
|
+
"autoSplashscreen: SplashScreen plugin not found. Install @capacitor/splash-screen plugin."
|
|
679
|
+
);
|
|
680
|
+
return;
|
|
617
681
|
}
|
|
618
|
-
|
|
619
|
-
|
|
682
|
+
|
|
683
|
+
splashScreenPlugin.invoke(methodName, new FireAndForgetPluginCall(methodName, options));
|
|
684
|
+
logger.info("Splashscreen " + methodName + " invoked automatically");
|
|
685
|
+
} catch (final Exception e) {
|
|
686
|
+
retrySplashScreenInvocation(
|
|
687
|
+
methodName,
|
|
688
|
+
options,
|
|
689
|
+
retriesRemaining,
|
|
690
|
+
requestToken,
|
|
691
|
+
"Failed to call SplashScreen " + methodName + " method: " + e.getMessage()
|
|
692
|
+
);
|
|
620
693
|
}
|
|
694
|
+
}
|
|
621
695
|
|
|
622
|
-
|
|
623
|
-
|
|
696
|
+
private void retrySplashScreenInvocation(
|
|
697
|
+
final String methodName,
|
|
698
|
+
final JSObject options,
|
|
699
|
+
final int retriesRemaining,
|
|
700
|
+
final int requestToken,
|
|
701
|
+
final String message
|
|
702
|
+
) {
|
|
703
|
+
if (retriesRemaining > 0) {
|
|
704
|
+
logger.info(message + ". Retrying.");
|
|
705
|
+
this.mainHandler.postDelayed(
|
|
706
|
+
() -> invokeSplashScreenPluginMethod(methodName, options, retriesRemaining - 1, requestToken),
|
|
707
|
+
SPLASH_SCREEN_RETRY_DELAY_MS
|
|
708
|
+
);
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
if ("show".equals(methodName)) {
|
|
713
|
+
logger.warn(message);
|
|
714
|
+
} else {
|
|
715
|
+
logger.error(message);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
boolean isCurrentSplashscreenInvocationTokenForTesting(final int requestToken) {
|
|
720
|
+
return requestToken == this.splashscreenInvocationToken;
|
|
624
721
|
}
|
|
625
722
|
|
|
626
723
|
private void addSplashscreenLoaderIfNeeded() {
|
|
@@ -761,14 +858,74 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
761
858
|
return plannedDirectUpdate && !Boolean.TRUE.equals(this.autoSplashscreenTimedOut);
|
|
762
859
|
}
|
|
763
860
|
|
|
861
|
+
static boolean shouldConsumeOnLaunchDirectUpdate(final String directUpdateMode, final boolean plannedDirectUpdate) {
|
|
862
|
+
return plannedDirectUpdate && "onLaunch".equals(directUpdateMode);
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
private void consumeOnLaunchDirectUpdateAttempt(final boolean plannedDirectUpdate) {
|
|
866
|
+
if (!shouldConsumeOnLaunchDirectUpdate(this.directUpdateMode, plannedDirectUpdate)) {
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
this.onLaunchDirectUpdateUsed = true;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
void configureDirectUpdateModeForTesting(final String directUpdateMode, final boolean onLaunchDirectUpdateUsed) {
|
|
874
|
+
this.directUpdateMode = directUpdateMode;
|
|
875
|
+
this.onLaunchDirectUpdateUsed = onLaunchDirectUpdateUsed;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
boolean shouldUseDirectUpdateForTesting() {
|
|
879
|
+
return this.shouldUseDirectUpdate();
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
boolean hasConsumedOnLaunchDirectUpdateForTesting() {
|
|
883
|
+
return this.onLaunchDirectUpdateUsed;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
boolean isVersionDownloadInProgress(final String version) {
|
|
887
|
+
return (
|
|
888
|
+
version != null &&
|
|
889
|
+
!version.isEmpty() &&
|
|
890
|
+
this.implementation != null &&
|
|
891
|
+
this.implementation.activity != null &&
|
|
892
|
+
DownloadWorkerManager.isVersionDownloading(this.implementation.activity, version)
|
|
893
|
+
);
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
void setLoggerForTesting(final Logger logger) {
|
|
897
|
+
this.logger = logger;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
void completeBackgroundTaskForTesting(final BundleInfo current, final boolean plannedDirectUpdate) {
|
|
901
|
+
this.endBackGroundTaskWithNotif("test", current.getVersionName(), current, false, plannedDirectUpdate);
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
void scheduleDirectUpdateFinish(final BundleInfo latest) {
|
|
905
|
+
startNewThread(() -> {
|
|
906
|
+
try {
|
|
907
|
+
Activity currentActivity = this.getActivity();
|
|
908
|
+
if (currentActivity != null) {
|
|
909
|
+
this.implementation.activity = currentActivity;
|
|
910
|
+
} else {
|
|
911
|
+
logger.warn("directUpdateFinish: Activity is null, proceeding without refreshing the activity reference");
|
|
912
|
+
}
|
|
913
|
+
this.directUpdateFinish(latest);
|
|
914
|
+
} catch (final Exception e) {
|
|
915
|
+
logger.error("directUpdateFinish failed: " + e.getMessage());
|
|
916
|
+
}
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
|
|
764
920
|
private void directUpdateFinish(final BundleInfo latest) {
|
|
765
921
|
if ("onLaunch".equals(this.directUpdateMode)) {
|
|
766
922
|
this.onLaunchDirectUpdateUsed = true;
|
|
767
923
|
this.implementation.directUpdate = false;
|
|
768
924
|
}
|
|
769
|
-
CapacitorUpdaterPlugin.this.implementation.set(latest)
|
|
770
|
-
|
|
771
|
-
|
|
925
|
+
if (CapacitorUpdaterPlugin.this.implementation.set(latest) && CapacitorUpdaterPlugin.this._reload()) {
|
|
926
|
+
this.notifyBundleSet(latest);
|
|
927
|
+
sendReadyToJs(latest, "update installed", true);
|
|
928
|
+
}
|
|
772
929
|
}
|
|
773
930
|
|
|
774
931
|
private void cleanupObsoleteVersions() {
|
|
@@ -1179,8 +1336,9 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1179
1336
|
final BundleInfo downloaded;
|
|
1180
1337
|
if (manifest != null) {
|
|
1181
1338
|
// For manifest downloads, we need to handle this asynchronously
|
|
1182
|
-
//
|
|
1183
|
-
|
|
1339
|
+
// to avoid automatically scheduling/applying the downloaded bundle.
|
|
1340
|
+
// Manual download must not schedule/apply the bundle automatically.
|
|
1341
|
+
CapacitorUpdaterPlugin.this.implementation.downloadBackground(url, version, sessionKey, checksum, manifest, false);
|
|
1184
1342
|
// Return immediately with a pending status - the actual result will come via listeners
|
|
1185
1343
|
final String id = CapacitorUpdaterPlugin.this.implementation.randomString();
|
|
1186
1344
|
downloaded = new BundleInfo(id, version, BundleStatus.DOWNLOADING, new Date(System.currentTimeMillis()), "");
|
|
@@ -1229,12 +1387,12 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1229
1387
|
this.bridge.getWebView().post(() -> this.bridge.getWebView().evaluateJavascript(script, null));
|
|
1230
1388
|
}
|
|
1231
1389
|
|
|
1232
|
-
|
|
1390
|
+
private void applyCurrentBundleToBridge() {
|
|
1233
1391
|
final String path = this.implementation.getCurrentBundlePath();
|
|
1392
|
+
final boolean usingBuiltin = this.implementation.isUsingBuiltin();
|
|
1234
1393
|
if (this.keepUrlPathAfterReload) {
|
|
1235
1394
|
this.syncKeepUrlPathFlag(true);
|
|
1236
1395
|
}
|
|
1237
|
-
this.semaphoreUp();
|
|
1238
1396
|
logger.info("Reloading: " + path);
|
|
1239
1397
|
|
|
1240
1398
|
AtomicReference<URL> url = new AtomicReference<>();
|
|
@@ -1279,7 +1437,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1279
1437
|
}
|
|
1280
1438
|
|
|
1281
1439
|
if (url.get() != null) {
|
|
1282
|
-
if (
|
|
1440
|
+
if (usingBuiltin) {
|
|
1283
1441
|
this.bridge.getLocalServer().hostAssets(path);
|
|
1284
1442
|
} else {
|
|
1285
1443
|
this.bridge.getLocalServer().hostFiles(path);
|
|
@@ -1299,14 +1457,14 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1299
1457
|
} catch (MalformedURLException e) {
|
|
1300
1458
|
logger.error("Cannot get finalUrl from capacitor bridge " + e.getMessage());
|
|
1301
1459
|
|
|
1302
|
-
if (
|
|
1460
|
+
if (usingBuiltin) {
|
|
1303
1461
|
this.bridge.setServerAssetPath(path);
|
|
1304
1462
|
} else {
|
|
1305
1463
|
this.bridge.setServerBasePath(path);
|
|
1306
1464
|
}
|
|
1307
1465
|
}
|
|
1308
1466
|
} else {
|
|
1309
|
-
if (
|
|
1467
|
+
if (usingBuiltin) {
|
|
1310
1468
|
this.bridge.setServerAssetPath(path);
|
|
1311
1469
|
} else {
|
|
1312
1470
|
this.bridge.setServerBasePath(path);
|
|
@@ -1322,34 +1480,75 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1322
1480
|
});
|
|
1323
1481
|
}
|
|
1324
1482
|
}
|
|
1483
|
+
}
|
|
1325
1484
|
|
|
1326
|
-
|
|
1327
|
-
this.notifyListeners("appReloaded", new JSObject());
|
|
1328
|
-
|
|
1329
|
-
// Wait for the reload to complete (until notifyAppReady is called)
|
|
1485
|
+
protected void restoreLiveBundleStateAfterFailedReload() {
|
|
1330
1486
|
try {
|
|
1331
|
-
this.
|
|
1332
|
-
} catch (Exception e) {
|
|
1333
|
-
logger.
|
|
1334
|
-
return false;
|
|
1487
|
+
this.applyCurrentBundleToBridge();
|
|
1488
|
+
} catch (final Exception e) {
|
|
1489
|
+
logger.warn("Failed to restore live bundle after rejected reload: " + e.getMessage());
|
|
1335
1490
|
}
|
|
1491
|
+
}
|
|
1336
1492
|
|
|
1337
|
-
|
|
1493
|
+
protected boolean _reload() {
|
|
1494
|
+
final int phase = this.semaphoreUp();
|
|
1495
|
+
this.applyCurrentBundleToBridge();
|
|
1496
|
+
|
|
1497
|
+
final long waitTimeMs = this.resolveAppReadyCheckTimeoutMs();
|
|
1498
|
+
this.checkAppReady(waitTimeMs);
|
|
1499
|
+
this.notifyListeners("appReloaded", new JSObject());
|
|
1500
|
+
|
|
1501
|
+
// Wait for the reload to complete (until notifyAppReady is called)
|
|
1502
|
+
return this.semaphoreWait(phase, waitTimeMs);
|
|
1338
1503
|
}
|
|
1339
1504
|
|
|
1340
1505
|
@PluginMethod
|
|
1341
1506
|
public void reload(final PluginCall call) {
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1507
|
+
startNewThread(() -> {
|
|
1508
|
+
try {
|
|
1509
|
+
final BundleInfo current = this.implementation.getCurrentBundle();
|
|
1510
|
+
final BundleInfo next = this.implementation.getNextBundle();
|
|
1511
|
+
|
|
1512
|
+
if (next != null && !next.isErrorStatus() && !next.getId().equals(current.getId())) {
|
|
1513
|
+
final CapgoUpdater.ResetState previousState = this.implementation.captureResetState();
|
|
1514
|
+
final String previousBundleName = this.implementation.getCurrentBundle().getVersionName();
|
|
1515
|
+
logger.info("Applying pending bundle before reload: " + next.getVersionName());
|
|
1516
|
+
final boolean didApplyPendingBundle;
|
|
1517
|
+
if (next.isBuiltin()) {
|
|
1518
|
+
this.implementation.prepareResetStateForTransition();
|
|
1519
|
+
didApplyPendingBundle = true;
|
|
1520
|
+
} else {
|
|
1521
|
+
didApplyPendingBundle = this.implementation.stagePendingReload(next);
|
|
1522
|
+
}
|
|
1523
|
+
if (didApplyPendingBundle && this._reload()) {
|
|
1524
|
+
if (next.isBuiltin()) {
|
|
1525
|
+
this.implementation.finalizeResetTransition(previousBundleName, false);
|
|
1526
|
+
} else {
|
|
1527
|
+
this.implementation.finalizePendingReload(next, previousBundleName);
|
|
1528
|
+
}
|
|
1529
|
+
this.notifyBundleSet(next);
|
|
1530
|
+
this.implementation.setNextBundle(null);
|
|
1531
|
+
call.resolve();
|
|
1532
|
+
return;
|
|
1533
|
+
}
|
|
1534
|
+
this.implementation.restoreResetState(previousState);
|
|
1535
|
+
this.restoreLiveBundleStateAfterFailedReload();
|
|
1536
|
+
logger.error("Reload failed after applying pending bundle: " + next.getVersionName());
|
|
1537
|
+
call.reject("Reload failed after applying pending bundle: " + next.getVersionName());
|
|
1538
|
+
return;
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
if (this._reload()) {
|
|
1542
|
+
call.resolve();
|
|
1543
|
+
} else {
|
|
1544
|
+
logger.error("Reload failed");
|
|
1545
|
+
call.reject("Reload failed");
|
|
1546
|
+
}
|
|
1547
|
+
} catch (final Exception e) {
|
|
1548
|
+
logger.error("Could not reload " + e.getMessage());
|
|
1549
|
+
call.reject("Could not reload", e);
|
|
1348
1550
|
}
|
|
1349
|
-
}
|
|
1350
|
-
logger.error("Could not reload " + e.getMessage());
|
|
1351
|
-
call.reject("Could not reload", e);
|
|
1352
|
-
}
|
|
1551
|
+
});
|
|
1353
1552
|
}
|
|
1354
1553
|
|
|
1355
1554
|
@PluginMethod
|
|
@@ -1387,9 +1586,13 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1387
1586
|
if (!this.implementation.set(id)) {
|
|
1388
1587
|
logger.info("No such bundle " + id);
|
|
1389
1588
|
call.reject("Update failed, id " + id + " does not exist.");
|
|
1589
|
+
} else if (!this._reload()) {
|
|
1590
|
+
logger.error("Reload failed after setting bundle " + id);
|
|
1591
|
+
call.reject("Reload failed after setting bundle " + id);
|
|
1390
1592
|
} else {
|
|
1391
1593
|
logger.info("Bundle successfully set to " + id);
|
|
1392
|
-
this.
|
|
1594
|
+
this.notifyBundleSet(this.implementation.getBundleInfo(id));
|
|
1595
|
+
call.resolve();
|
|
1393
1596
|
}
|
|
1394
1597
|
} catch (final Exception e) {
|
|
1395
1598
|
logger.error("Could not set id " + id + " " + e.getMessage());
|
|
@@ -1481,11 +1684,21 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1481
1684
|
startNewThread(() ->
|
|
1482
1685
|
CapacitorUpdaterPlugin.this.implementation.getLatest(CapacitorUpdaterPlugin.this.updateUrl, channel, (res) -> {
|
|
1483
1686
|
JSObject jsRes = InternalUtils.mapToJSObject(res);
|
|
1484
|
-
if (jsRes.has("error")) {
|
|
1485
|
-
String error = jsRes.getString("error");
|
|
1687
|
+
if (jsRes.has("error") || jsRes.has("kind")) {
|
|
1688
|
+
String error = jsRes.has("error") ? jsRes.getString("error") : "";
|
|
1486
1689
|
String errorMessage = jsRes.has("message") ? jsRes.getString("message") : "server did not provide a message";
|
|
1487
|
-
|
|
1488
|
-
|
|
1690
|
+
String kind = CapacitorUpdaterPlugin.this.getUpdateResponseKind(jsRes.has("kind") ? jsRes.getString("kind") : null);
|
|
1691
|
+
jsRes.put("kind", kind);
|
|
1692
|
+
if ("failed".equals(kind)) {
|
|
1693
|
+
logger.error("getLatest failed with error: " + error + ", message: " + errorMessage);
|
|
1694
|
+
call.reject(error.isEmpty() ? errorMessage : error);
|
|
1695
|
+
} else {
|
|
1696
|
+
if (!jsRes.has("version") || jsRes.getString("version").isEmpty()) {
|
|
1697
|
+
jsRes.put("version", CapacitorUpdaterPlugin.this.implementation.getCurrentBundle().getVersionName());
|
|
1698
|
+
}
|
|
1699
|
+
logger.info("getLatest returned " + kind + ": " + errorMessage);
|
|
1700
|
+
call.resolve(jsRes);
|
|
1701
|
+
}
|
|
1489
1702
|
return;
|
|
1490
1703
|
} else if (jsRes.has("message")) {
|
|
1491
1704
|
call.reject(jsRes.getString("message"));
|
|
@@ -1497,24 +1710,83 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1497
1710
|
);
|
|
1498
1711
|
}
|
|
1499
1712
|
|
|
1500
|
-
private boolean _reset(final Boolean toLastSuccessful) {
|
|
1713
|
+
private boolean _reset(final Boolean toLastSuccessful, final Boolean usePendingBundle) {
|
|
1714
|
+
return this.performReset(toLastSuccessful, usePendingBundle, false);
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
private boolean performReset(final Boolean toLastSuccessful, final Boolean usePendingBundle, final boolean internal) {
|
|
1501
1718
|
final BundleInfo fallback = this.implementation.getFallbackBundle();
|
|
1502
|
-
this.implementation.
|
|
1719
|
+
final BundleInfo pending = this.implementation.getNextBundle();
|
|
1720
|
+
final CapgoUpdater.ResetState previousState = this.implementation.captureResetState();
|
|
1721
|
+
final String previousBundleName = this.implementation.getCurrentBundle().getVersionName();
|
|
1503
1722
|
|
|
1504
|
-
if (
|
|
1505
|
-
|
|
1506
|
-
|
|
1723
|
+
if (Boolean.TRUE.equals(usePendingBundle)) {
|
|
1724
|
+
if (pending == null || pending.isErrorStatus()) {
|
|
1725
|
+
logger.error("No pending bundle available to reset to");
|
|
1726
|
+
return false;
|
|
1727
|
+
}
|
|
1728
|
+
if (!this.implementation.canSet(pending)) {
|
|
1729
|
+
logger.error("Pending bundle is not installable");
|
|
1730
|
+
return false;
|
|
1731
|
+
}
|
|
1732
|
+
this.implementation.prepareResetStateForTransition();
|
|
1733
|
+
logger.info("Resetting to pending bundle: " + pending.getVersionName());
|
|
1734
|
+
final boolean didApplyPendingBundle;
|
|
1735
|
+
if (pending.isBuiltin()) {
|
|
1736
|
+
didApplyPendingBundle = true;
|
|
1737
|
+
} else {
|
|
1738
|
+
didApplyPendingBundle = this.implementation.set(pending);
|
|
1739
|
+
}
|
|
1740
|
+
if (didApplyPendingBundle && this._reload()) {
|
|
1741
|
+
this.implementation.finalizeResetTransition(previousBundleName, internal);
|
|
1742
|
+
this.notifyBundleSet(pending);
|
|
1743
|
+
this.implementation.setNextBundle(null);
|
|
1744
|
+
return true;
|
|
1745
|
+
}
|
|
1746
|
+
this.implementation.restoreResetState(previousState);
|
|
1747
|
+
this.restoreLiveBundleStateAfterFailedReload();
|
|
1748
|
+
return false;
|
|
1507
1749
|
}
|
|
1508
1750
|
|
|
1751
|
+
if (Boolean.TRUE.equals(toLastSuccessful) && !fallback.isBuiltin()) {
|
|
1752
|
+
if (this.implementation.canSet(fallback)) {
|
|
1753
|
+
this.implementation.prepareResetStateForTransition();
|
|
1754
|
+
logger.info("Resetting to: " + fallback);
|
|
1755
|
+
if (this.implementation.set(fallback) && this._reload()) {
|
|
1756
|
+
this.implementation.finalizeResetTransition(previousBundleName, internal);
|
|
1757
|
+
this.notifyBundleSet(fallback);
|
|
1758
|
+
return true;
|
|
1759
|
+
}
|
|
1760
|
+
if (!internal) {
|
|
1761
|
+
this.implementation.restoreResetState(previousState);
|
|
1762
|
+
this.restoreLiveBundleStateAfterFailedReload();
|
|
1763
|
+
return false;
|
|
1764
|
+
}
|
|
1765
|
+
logger.warn("Fallback reload failed during internal reset, resetting to native instead");
|
|
1766
|
+
} else {
|
|
1767
|
+
logger.warn("Fallback bundle is not installable, resetting to native instead");
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
this.implementation.prepareResetStateForTransition();
|
|
1509
1772
|
logger.info("Resetting to native.");
|
|
1510
|
-
|
|
1773
|
+
if (this._reload()) {
|
|
1774
|
+
this.implementation.finalizeResetTransition(previousBundleName, internal);
|
|
1775
|
+
return true;
|
|
1776
|
+
}
|
|
1777
|
+
if (!internal) {
|
|
1778
|
+
this.implementation.restoreResetState(previousState);
|
|
1779
|
+
this.restoreLiveBundleStateAfterFailedReload();
|
|
1780
|
+
}
|
|
1781
|
+
return false;
|
|
1511
1782
|
}
|
|
1512
1783
|
|
|
1513
1784
|
@PluginMethod
|
|
1514
1785
|
public void reset(final PluginCall call) {
|
|
1515
1786
|
try {
|
|
1516
1787
|
final Boolean toLastSuccessful = call.getBoolean("toLastSuccessful", false);
|
|
1517
|
-
|
|
1788
|
+
final Boolean usePendingBundle = call.getBoolean("usePendingBundle", false);
|
|
1789
|
+
if (this._reset(toLastSuccessful, usePendingBundle)) {
|
|
1518
1790
|
call.resolve();
|
|
1519
1791
|
return;
|
|
1520
1792
|
}
|
|
@@ -1589,12 +1861,33 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1589
1861
|
try {
|
|
1590
1862
|
CapacitorUpdaterPlugin.this.implementation.getLatest(CapacitorUpdaterPlugin.this.updateUrl, null, (res) -> {
|
|
1591
1863
|
JSObject jsRes = InternalUtils.mapToJSObject(res);
|
|
1592
|
-
if (jsRes.has("error")) {
|
|
1593
|
-
|
|
1864
|
+
if (jsRes.has("error") || jsRes.has("kind")) {
|
|
1865
|
+
final BundleInfo current = CapacitorUpdaterPlugin.this.implementation.getCurrentBundle();
|
|
1866
|
+
String error = jsRes.has("error") ? jsRes.getString("error") : "";
|
|
1594
1867
|
String errorMessage = jsRes.has("message")
|
|
1595
1868
|
? jsRes.getString("message")
|
|
1596
1869
|
: "server did not provide a message";
|
|
1597
|
-
|
|
1870
|
+
int statusCode = jsRes.has("statusCode") ? jsRes.optInt("statusCode", 0) : 0;
|
|
1871
|
+
String kind = CapacitorUpdaterPlugin.this.getUpdateResponseKind(
|
|
1872
|
+
jsRes.has("kind") ? jsRes.getString("kind") : null
|
|
1873
|
+
);
|
|
1874
|
+
String latestVersion = jsRes.has("version") ? jsRes.getString("version") : current.getVersionName();
|
|
1875
|
+
CapacitorUpdaterPlugin.this.notifyUpdateCheckResult(
|
|
1876
|
+
kind,
|
|
1877
|
+
error,
|
|
1878
|
+
errorMessage,
|
|
1879
|
+
statusCode,
|
|
1880
|
+
latestVersion,
|
|
1881
|
+
current
|
|
1882
|
+
);
|
|
1883
|
+
|
|
1884
|
+
if ("failed".equals(kind)) {
|
|
1885
|
+
logger.error("getLatest failed with error: " + error + ", message: " + errorMessage);
|
|
1886
|
+
} else if ("blocked".equals(kind)) {
|
|
1887
|
+
logger.info("Update check blocked with error: " + error);
|
|
1888
|
+
} else {
|
|
1889
|
+
logger.info("No new version available");
|
|
1890
|
+
}
|
|
1598
1891
|
} else if (jsRes.has("version")) {
|
|
1599
1892
|
String newVersion = jsRes.getString("version");
|
|
1600
1893
|
String currentVersion = String.valueOf(CapacitorUpdaterPlugin.this.implementation.getCurrentBundle());
|
|
@@ -1716,11 +2009,15 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1716
2009
|
}
|
|
1717
2010
|
|
|
1718
2011
|
private void checkAppReady() {
|
|
2012
|
+
this.checkAppReady(this.resolveAppReadyCheckTimeoutMs());
|
|
2013
|
+
}
|
|
2014
|
+
|
|
2015
|
+
private void checkAppReady(final long waitTimeMs) {
|
|
1719
2016
|
try {
|
|
1720
2017
|
if (this.appReadyCheck != null) {
|
|
1721
2018
|
this.appReadyCheck.interrupt();
|
|
1722
2019
|
}
|
|
1723
|
-
this.appReadyCheck = startNewThread(new DeferredNotifyAppReadyCheck());
|
|
2020
|
+
this.appReadyCheck = startNewThread(new DeferredNotifyAppReadyCheck(waitTimeMs));
|
|
1724
2021
|
} catch (final Exception e) {
|
|
1725
2022
|
logger.error("Failed to start " + DeferredNotifyAppReadyCheck.class.getName() + " " + e.getMessage());
|
|
1726
2023
|
}
|
|
@@ -1735,6 +2032,31 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1735
2032
|
}
|
|
1736
2033
|
}
|
|
1737
2034
|
|
|
2035
|
+
private String getUpdateResponseKind(final String kind) {
|
|
2036
|
+
if ("up_to_date".equals(kind) || "blocked".equals(kind) || "failed".equals(kind)) {
|
|
2037
|
+
return kind;
|
|
2038
|
+
}
|
|
2039
|
+
return "failed";
|
|
2040
|
+
}
|
|
2041
|
+
|
|
2042
|
+
private void notifyUpdateCheckResult(
|
|
2043
|
+
final String kind,
|
|
2044
|
+
final String error,
|
|
2045
|
+
final String message,
|
|
2046
|
+
final int statusCode,
|
|
2047
|
+
final String version,
|
|
2048
|
+
final BundleInfo current
|
|
2049
|
+
) {
|
|
2050
|
+
JSObject ret = new JSObject();
|
|
2051
|
+
ret.put("kind", kind);
|
|
2052
|
+
ret.put("error", error);
|
|
2053
|
+
ret.put("message", message);
|
|
2054
|
+
ret.put("statusCode", statusCode);
|
|
2055
|
+
ret.put("version", version);
|
|
2056
|
+
ret.put("bundle", InternalUtils.mapToJSObject(current.toJSONMap()));
|
|
2057
|
+
this.notifyListeners("updateCheckResult", ret);
|
|
2058
|
+
}
|
|
2059
|
+
|
|
1738
2060
|
private void ensureBridgeSet() {
|
|
1739
2061
|
if (this.bridge != null && this.bridge.getWebView() != null) {
|
|
1740
2062
|
logger.setBridge(this.bridge);
|
|
@@ -1772,11 +2094,12 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1772
2094
|
String latestVersionName,
|
|
1773
2095
|
BundleInfo current,
|
|
1774
2096
|
Boolean error,
|
|
1775
|
-
Boolean
|
|
2097
|
+
Boolean plannedDirectUpdate,
|
|
1776
2098
|
String failureAction,
|
|
1777
2099
|
String failureEvent,
|
|
1778
2100
|
boolean shouldSendStats
|
|
1779
2101
|
) {
|
|
2102
|
+
this.consumeOnLaunchDirectUpdateAttempt(Boolean.TRUE.equals(plannedDirectUpdate));
|
|
1780
2103
|
if (error) {
|
|
1781
2104
|
logger.info(
|
|
1782
2105
|
"endBackGroundTaskWithNotif error: " +
|
|
@@ -1796,7 +2119,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1796
2119
|
final JSObject ret = new JSObject();
|
|
1797
2120
|
ret.put("bundle", InternalUtils.mapToJSObject(current.toJSONMap()));
|
|
1798
2121
|
this.notifyListeners("noNeedUpdate", ret);
|
|
1799
|
-
this.sendReadyToJs(current, msg,
|
|
2122
|
+
this.sendReadyToJs(current, msg, plannedDirectUpdate);
|
|
1800
2123
|
this.backgroundDownloadTask = null;
|
|
1801
2124
|
this.downloadStartTimeMs = 0;
|
|
1802
2125
|
logger.info("endBackGroundTaskWithNotif " + msg);
|
|
@@ -1830,7 +2153,6 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1830
2153
|
private Thread backgroundDownload() {
|
|
1831
2154
|
final boolean plannedDirectUpdate = this.shouldUseDirectUpdate();
|
|
1832
2155
|
final boolean initialDirectUpdateAllowed = this.isDirectUpdateCurrentlyAllowed(plannedDirectUpdate);
|
|
1833
|
-
this.implementation.directUpdate = initialDirectUpdateAllowed;
|
|
1834
2156
|
final String messageUpdate = initialDirectUpdateAllowed
|
|
1835
2157
|
? "Update will occur now."
|
|
1836
2158
|
: "Update will occur next time app moves to background.";
|
|
@@ -1844,30 +2166,37 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1844
2166
|
final BundleInfo current = CapacitorUpdaterPlugin.this.implementation.getCurrentBundle();
|
|
1845
2167
|
|
|
1846
2168
|
// Handle network errors and other failures first
|
|
1847
|
-
if (jsRes.has("error")) {
|
|
1848
|
-
String error = jsRes.getString("error");
|
|
2169
|
+
if (jsRes.has("error") || jsRes.has("kind")) {
|
|
2170
|
+
String error = jsRes.has("error") ? jsRes.getString("error") : "";
|
|
1849
2171
|
String errorMessage = jsRes.has("message") ? jsRes.getString("message") : "server did not provide a message";
|
|
1850
2172
|
int statusCode = jsRes.has("statusCode") ? jsRes.optInt("statusCode", 0) : 0;
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
logger.error(
|
|
1854
|
-
"getLatest failed with error: " + error + ", message: " + errorMessage + ", statusCode: " + statusCode
|
|
1855
|
-
);
|
|
2173
|
+
String kind = CapacitorUpdaterPlugin.this.getUpdateResponseKind(jsRes.has("kind") ? jsRes.getString("kind") : null);
|
|
1856
2174
|
String latestVersion = jsRes.has("version") ? jsRes.getString("version") : current.getVersionName();
|
|
2175
|
+
CapacitorUpdaterPlugin.this.notifyUpdateCheckResult(kind, error, errorMessage, statusCode, latestVersion, current);
|
|
2176
|
+
|
|
2177
|
+
if ("up_to_date".equals(kind)) {
|
|
2178
|
+
logger.info("No new version available");
|
|
2179
|
+
} else if ("blocked".equals(kind)) {
|
|
2180
|
+
logger.info("Update check blocked with error: " + error);
|
|
2181
|
+
} else {
|
|
2182
|
+
logger.error(
|
|
2183
|
+
"getLatest failed with error: " + error + ", message: " + errorMessage + ", statusCode: " + statusCode
|
|
2184
|
+
);
|
|
2185
|
+
}
|
|
1857
2186
|
|
|
2187
|
+
boolean isFailure = "failed".equals(kind);
|
|
1858
2188
|
CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
|
|
1859
2189
|
errorMessage,
|
|
1860
2190
|
latestVersion,
|
|
1861
2191
|
current,
|
|
1862
|
-
|
|
2192
|
+
isFailure,
|
|
1863
2193
|
plannedDirectUpdate,
|
|
1864
2194
|
"download_fail",
|
|
1865
2195
|
"downloadFailed",
|
|
1866
|
-
|
|
2196
|
+
isFailure
|
|
1867
2197
|
);
|
|
1868
2198
|
return;
|
|
1869
2199
|
}
|
|
1870
|
-
|
|
1871
2200
|
try {
|
|
1872
2201
|
final String latestVersionName = jsRes.getString("version");
|
|
1873
2202
|
|
|
@@ -1878,7 +2207,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1878
2207
|
);
|
|
1879
2208
|
if (directUpdateAllowedNow) {
|
|
1880
2209
|
logger.info("Direct update to builtin version");
|
|
1881
|
-
this._reset(false);
|
|
2210
|
+
this._reset(false, false);
|
|
1882
2211
|
CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
|
|
1883
2212
|
"Updated to builtin version",
|
|
1884
2213
|
latestVersionName,
|
|
@@ -1898,7 +2227,8 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1898
2227
|
"Next update will be to builtin version",
|
|
1899
2228
|
latestVersionName,
|
|
1900
2229
|
current,
|
|
1901
|
-
false
|
|
2230
|
+
false,
|
|
2231
|
+
plannedDirectUpdate
|
|
1902
2232
|
);
|
|
1903
2233
|
}
|
|
1904
2234
|
return;
|
|
@@ -1934,7 +2264,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1934
2264
|
);
|
|
1935
2265
|
return;
|
|
1936
2266
|
}
|
|
1937
|
-
if (latest.isDownloaded()) {
|
|
2267
|
+
if (latest.isDownloaded() && BundleStatus.DOWNLOADING != latest.getStatus()) {
|
|
1938
2268
|
logger.info("Latest bundle already exists and download is NOT required. " + messageUpdate);
|
|
1939
2269
|
final boolean directUpdateAllowedNow = CapacitorUpdaterPlugin.this.isDirectUpdateCurrentlyAllowed(
|
|
1940
2270
|
plannedDirectUpdate
|
|
@@ -1955,15 +2285,26 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1955
2285
|
);
|
|
1956
2286
|
return;
|
|
1957
2287
|
}
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
2288
|
+
if (
|
|
2289
|
+
CapacitorUpdaterPlugin.this.implementation.set(latest) && CapacitorUpdaterPlugin.this._reload()
|
|
2290
|
+
) {
|
|
2291
|
+
CapacitorUpdaterPlugin.this.notifyBundleSet(latest);
|
|
2292
|
+
CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
|
|
2293
|
+
"Update installed",
|
|
2294
|
+
latestVersionName,
|
|
2295
|
+
latest,
|
|
2296
|
+
false,
|
|
2297
|
+
true
|
|
2298
|
+
);
|
|
2299
|
+
} else {
|
|
2300
|
+
CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
|
|
2301
|
+
"Update install failed",
|
|
2302
|
+
latestVersionName,
|
|
2303
|
+
latest,
|
|
2304
|
+
true,
|
|
2305
|
+
true
|
|
2306
|
+
);
|
|
2307
|
+
}
|
|
1967
2308
|
} else {
|
|
1968
2309
|
if (plannedDirectUpdate && !directUpdateAllowedNow) {
|
|
1969
2310
|
logger.info(
|
|
@@ -1976,7 +2317,8 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1976
2317
|
"update downloaded, will install next background",
|
|
1977
2318
|
latestVersionName,
|
|
1978
2319
|
latest,
|
|
1979
|
-
false
|
|
2320
|
+
false,
|
|
2321
|
+
plannedDirectUpdate
|
|
1980
2322
|
);
|
|
1981
2323
|
}
|
|
1982
2324
|
return;
|
|
@@ -1993,6 +2335,14 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1993
2335
|
}
|
|
1994
2336
|
}
|
|
1995
2337
|
}
|
|
2338
|
+
final boolean retryingInFlightDownload =
|
|
2339
|
+
latest != null &&
|
|
2340
|
+
BundleStatus.DOWNLOADING == latest.getStatus() &&
|
|
2341
|
+
CapacitorUpdaterPlugin.this.isVersionDownloadInProgress(latest.getVersionName());
|
|
2342
|
+
CapacitorUpdaterPlugin.this.consumeOnLaunchDirectUpdateAttempt(plannedDirectUpdate);
|
|
2343
|
+
CapacitorUpdaterPlugin.this.implementation.directUpdate = retryingInFlightDownload
|
|
2344
|
+
? Boolean.TRUE.equals(CapacitorUpdaterPlugin.this.implementation.directUpdate) || initialDirectUpdateAllowed
|
|
2345
|
+
: initialDirectUpdateAllowed;
|
|
1996
2346
|
startNewThread(() -> {
|
|
1997
2347
|
try {
|
|
1998
2348
|
logger.info(
|
|
@@ -2041,7 +2391,13 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
2041
2391
|
});
|
|
2042
2392
|
} else {
|
|
2043
2393
|
logger.info("No need to update, " + current.getId() + " is the latest bundle.");
|
|
2044
|
-
CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
|
|
2394
|
+
CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
|
|
2395
|
+
"No need to update",
|
|
2396
|
+
latestVersionName,
|
|
2397
|
+
current,
|
|
2398
|
+
false,
|
|
2399
|
+
plannedDirectUpdate
|
|
2400
|
+
);
|
|
2045
2401
|
}
|
|
2046
2402
|
} catch (final Exception e) {
|
|
2047
2403
|
logger.error("error in update check " + e.getMessage());
|
|
@@ -2085,15 +2441,18 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
2085
2441
|
if (next != null && !next.isErrorStatus() && !next.getId().equals(current.getId())) {
|
|
2086
2442
|
// There is a next bundle waiting for activation
|
|
2087
2443
|
logger.debug("Next bundle is: " + next.getVersionName());
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2444
|
+
startNewThread(() -> {
|
|
2445
|
+
if (this.implementation.set(next) && this._reload()) {
|
|
2446
|
+
logger.info("Updated to bundle: " + next.getVersionName());
|
|
2447
|
+
this.notifyBundleSet(next);
|
|
2448
|
+
this.implementation.setNextBundle(null);
|
|
2449
|
+
} else {
|
|
2450
|
+
logger.error("Update to bundle: " + next.getVersionName() + " Failed!");
|
|
2451
|
+
}
|
|
2452
|
+
});
|
|
2094
2453
|
}
|
|
2095
2454
|
} catch (final Exception e) {
|
|
2096
|
-
logger.error("Error during
|
|
2455
|
+
logger.error("Error during installNext " + e);
|
|
2097
2456
|
}
|
|
2098
2457
|
}
|
|
2099
2458
|
|
|
@@ -2116,7 +2475,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
2116
2475
|
this.notifyListeners("updateFailed", ret);
|
|
2117
2476
|
this.implementation.sendStats("update_fail", current.getVersionName());
|
|
2118
2477
|
this.implementation.setError(current);
|
|
2119
|
-
this.
|
|
2478
|
+
this.performReset(true, false, true);
|
|
2120
2479
|
if (CapacitorUpdaterPlugin.this.autoDeleteFailed && !current.isBuiltin()) {
|
|
2121
2480
|
logger.info("Deleting failing bundle: " + current.getVersionName());
|
|
2122
2481
|
try {
|
|
@@ -2135,11 +2494,17 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
2135
2494
|
|
|
2136
2495
|
private class DeferredNotifyAppReadyCheck implements Runnable {
|
|
2137
2496
|
|
|
2497
|
+
private final long waitTimeMs;
|
|
2498
|
+
|
|
2499
|
+
DeferredNotifyAppReadyCheck(final long waitTimeMs) {
|
|
2500
|
+
this.waitTimeMs = waitTimeMs;
|
|
2501
|
+
}
|
|
2502
|
+
|
|
2138
2503
|
@Override
|
|
2139
2504
|
public void run() {
|
|
2140
2505
|
try {
|
|
2141
|
-
logger.info("Wait for " +
|
|
2142
|
-
Thread.sleep(
|
|
2506
|
+
logger.info("Wait for " + this.waitTimeMs + "ms, then check for notifyAppReady");
|
|
2507
|
+
Thread.sleep(this.waitTimeMs);
|
|
2143
2508
|
CapacitorUpdaterPlugin.this.checkRevert();
|
|
2144
2509
|
CapacitorUpdaterPlugin.this.appReadyCheck = null;
|
|
2145
2510
|
} catch (final InterruptedException e) {
|
|
@@ -2492,9 +2857,9 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
2492
2857
|
|
|
2493
2858
|
JSObject result = new JSObject();
|
|
2494
2859
|
try {
|
|
2495
|
-
PackageInfo pInfo =
|
|
2860
|
+
PackageInfo pInfo = getCurrentPackageInfo();
|
|
2496
2861
|
result.put("currentVersionName", pInfo.versionName);
|
|
2497
|
-
result.put("currentVersionCode",
|
|
2862
|
+
result.put("currentVersionCode", getVersionCode(pInfo));
|
|
2498
2863
|
} catch (PackageManager.NameNotFoundException e) {
|
|
2499
2864
|
result.put("currentVersionName", "0.0.0");
|
|
2500
2865
|
result.put("currentVersionCode", "0");
|