@capgo/capacitor-updater 6.43.5 → 6.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 +1 -1
- package/README.md +149 -39
- package/android/build.gradle +3 -3
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +534 -170
- package/android/src/main/java/ee/forgr/capacitor_updater/CapgoUpdater.java +151 -28
- 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/ios/Sources/CapacitorUpdaterPlugin/CapacitorUpdaterPlugin.swift +557 -134
- package/ios/Sources/CapacitorUpdaterPlugin/CapgoUpdater.swift +213 -50
- package/ios/Sources/CapacitorUpdaterPlugin/InternalUtils.swift +2 -0
- package/ios/Sources/CapacitorUpdaterPlugin/ShakeMenu.swift +20 -3
- package/package.json +5 -2
|
@@ -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 = "
|
|
92
|
+
private final String pluginVersion = "6.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() {
|
|
@@ -1230,12 +1387,12 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1230
1387
|
this.bridge.getWebView().post(() -> this.bridge.getWebView().evaluateJavascript(script, null));
|
|
1231
1388
|
}
|
|
1232
1389
|
|
|
1233
|
-
|
|
1390
|
+
private void applyCurrentBundleToBridge() {
|
|
1234
1391
|
final String path = this.implementation.getCurrentBundlePath();
|
|
1392
|
+
final boolean usingBuiltin = this.implementation.isUsingBuiltin();
|
|
1235
1393
|
if (this.keepUrlPathAfterReload) {
|
|
1236
1394
|
this.syncKeepUrlPathFlag(true);
|
|
1237
1395
|
}
|
|
1238
|
-
this.semaphoreUp();
|
|
1239
1396
|
logger.info("Reloading: " + path);
|
|
1240
1397
|
|
|
1241
1398
|
AtomicReference<URL> url = new AtomicReference<>();
|
|
@@ -1280,7 +1437,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1280
1437
|
}
|
|
1281
1438
|
|
|
1282
1439
|
if (url.get() != null) {
|
|
1283
|
-
if (
|
|
1440
|
+
if (usingBuiltin) {
|
|
1284
1441
|
this.bridge.getLocalServer().hostAssets(path);
|
|
1285
1442
|
} else {
|
|
1286
1443
|
this.bridge.getLocalServer().hostFiles(path);
|
|
@@ -1300,14 +1457,14 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1300
1457
|
} catch (MalformedURLException e) {
|
|
1301
1458
|
logger.error("Cannot get finalUrl from capacitor bridge " + e.getMessage());
|
|
1302
1459
|
|
|
1303
|
-
if (
|
|
1460
|
+
if (usingBuiltin) {
|
|
1304
1461
|
this.bridge.setServerAssetPath(path);
|
|
1305
1462
|
} else {
|
|
1306
1463
|
this.bridge.setServerBasePath(path);
|
|
1307
1464
|
}
|
|
1308
1465
|
}
|
|
1309
1466
|
} else {
|
|
1310
|
-
if (
|
|
1467
|
+
if (usingBuiltin) {
|
|
1311
1468
|
this.bridge.setServerAssetPath(path);
|
|
1312
1469
|
} else {
|
|
1313
1470
|
this.bridge.setServerBasePath(path);
|
|
@@ -1323,34 +1480,75 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1323
1480
|
});
|
|
1324
1481
|
}
|
|
1325
1482
|
}
|
|
1483
|
+
}
|
|
1326
1484
|
|
|
1327
|
-
|
|
1328
|
-
this.notifyListeners("appReloaded", new JSObject());
|
|
1329
|
-
|
|
1330
|
-
// Wait for the reload to complete (until notifyAppReady is called)
|
|
1485
|
+
protected void restoreLiveBundleStateAfterFailedReload() {
|
|
1331
1486
|
try {
|
|
1332
|
-
this.
|
|
1333
|
-
} catch (Exception e) {
|
|
1334
|
-
logger.
|
|
1335
|
-
return false;
|
|
1487
|
+
this.applyCurrentBundleToBridge();
|
|
1488
|
+
} catch (final Exception e) {
|
|
1489
|
+
logger.warn("Failed to restore live bundle after rejected reload: " + e.getMessage());
|
|
1336
1490
|
}
|
|
1491
|
+
}
|
|
1337
1492
|
|
|
1338
|
-
|
|
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);
|
|
1339
1503
|
}
|
|
1340
1504
|
|
|
1341
1505
|
@PluginMethod
|
|
1342
1506
|
public void reload(final PluginCall call) {
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
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);
|
|
1349
1550
|
}
|
|
1350
|
-
}
|
|
1351
|
-
logger.error("Could not reload " + e.getMessage());
|
|
1352
|
-
call.reject("Could not reload", e);
|
|
1353
|
-
}
|
|
1551
|
+
});
|
|
1354
1552
|
}
|
|
1355
1553
|
|
|
1356
1554
|
@PluginMethod
|
|
@@ -1388,9 +1586,13 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1388
1586
|
if (!this.implementation.set(id)) {
|
|
1389
1587
|
logger.info("No such bundle " + id);
|
|
1390
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);
|
|
1391
1592
|
} else {
|
|
1392
1593
|
logger.info("Bundle successfully set to " + id);
|
|
1393
|
-
this.
|
|
1594
|
+
this.notifyBundleSet(this.implementation.getBundleInfo(id));
|
|
1595
|
+
call.resolve();
|
|
1394
1596
|
}
|
|
1395
1597
|
} catch (final Exception e) {
|
|
1396
1598
|
logger.error("Could not set id " + id + " " + e.getMessage());
|
|
@@ -1482,11 +1684,21 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1482
1684
|
startNewThread(() ->
|
|
1483
1685
|
CapacitorUpdaterPlugin.this.implementation.getLatest(CapacitorUpdaterPlugin.this.updateUrl, channel, (res) -> {
|
|
1484
1686
|
JSObject jsRes = InternalUtils.mapToJSObject(res);
|
|
1485
|
-
if (jsRes.has("error")) {
|
|
1486
|
-
String error = jsRes.getString("error");
|
|
1687
|
+
if (jsRes.has("error") || jsRes.has("kind")) {
|
|
1688
|
+
String error = jsRes.has("error") ? jsRes.getString("error") : "";
|
|
1487
1689
|
String errorMessage = jsRes.has("message") ? jsRes.getString("message") : "server did not provide a message";
|
|
1488
|
-
|
|
1489
|
-
|
|
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
|
+
}
|
|
1490
1702
|
return;
|
|
1491
1703
|
} else if (jsRes.has("message")) {
|
|
1492
1704
|
call.reject(jsRes.getString("message"));
|
|
@@ -1498,24 +1710,83 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1498
1710
|
);
|
|
1499
1711
|
}
|
|
1500
1712
|
|
|
1501
|
-
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) {
|
|
1502
1718
|
final BundleInfo fallback = this.implementation.getFallbackBundle();
|
|
1503
|
-
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();
|
|
1504
1722
|
|
|
1505
|
-
if (
|
|
1506
|
-
|
|
1507
|
-
|
|
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;
|
|
1508
1749
|
}
|
|
1509
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();
|
|
1510
1772
|
logger.info("Resetting to native.");
|
|
1511
|
-
|
|
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;
|
|
1512
1782
|
}
|
|
1513
1783
|
|
|
1514
1784
|
@PluginMethod
|
|
1515
1785
|
public void reset(final PluginCall call) {
|
|
1516
1786
|
try {
|
|
1517
1787
|
final Boolean toLastSuccessful = call.getBoolean("toLastSuccessful", false);
|
|
1518
|
-
|
|
1788
|
+
final Boolean usePendingBundle = call.getBoolean("usePendingBundle", false);
|
|
1789
|
+
if (this._reset(toLastSuccessful, usePendingBundle)) {
|
|
1519
1790
|
call.resolve();
|
|
1520
1791
|
return;
|
|
1521
1792
|
}
|
|
@@ -1590,12 +1861,33 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1590
1861
|
try {
|
|
1591
1862
|
CapacitorUpdaterPlugin.this.implementation.getLatest(CapacitorUpdaterPlugin.this.updateUrl, null, (res) -> {
|
|
1592
1863
|
JSObject jsRes = InternalUtils.mapToJSObject(res);
|
|
1593
|
-
if (jsRes.has("error")) {
|
|
1594
|
-
|
|
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") : "";
|
|
1595
1867
|
String errorMessage = jsRes.has("message")
|
|
1596
1868
|
? jsRes.getString("message")
|
|
1597
1869
|
: "server did not provide a message";
|
|
1598
|
-
|
|
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
|
+
}
|
|
1599
1891
|
} else if (jsRes.has("version")) {
|
|
1600
1892
|
String newVersion = jsRes.getString("version");
|
|
1601
1893
|
String currentVersion = String.valueOf(CapacitorUpdaterPlugin.this.implementation.getCurrentBundle());
|
|
@@ -1717,11 +2009,15 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1717
2009
|
}
|
|
1718
2010
|
|
|
1719
2011
|
private void checkAppReady() {
|
|
2012
|
+
this.checkAppReady(this.resolveAppReadyCheckTimeoutMs());
|
|
2013
|
+
}
|
|
2014
|
+
|
|
2015
|
+
private void checkAppReady(final long waitTimeMs) {
|
|
1720
2016
|
try {
|
|
1721
2017
|
if (this.appReadyCheck != null) {
|
|
1722
2018
|
this.appReadyCheck.interrupt();
|
|
1723
2019
|
}
|
|
1724
|
-
this.appReadyCheck = startNewThread(new DeferredNotifyAppReadyCheck());
|
|
2020
|
+
this.appReadyCheck = startNewThread(new DeferredNotifyAppReadyCheck(waitTimeMs));
|
|
1725
2021
|
} catch (final Exception e) {
|
|
1726
2022
|
logger.error("Failed to start " + DeferredNotifyAppReadyCheck.class.getName() + " " + e.getMessage());
|
|
1727
2023
|
}
|
|
@@ -1736,6 +2032,31 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1736
2032
|
}
|
|
1737
2033
|
}
|
|
1738
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
|
+
|
|
1739
2060
|
private void ensureBridgeSet() {
|
|
1740
2061
|
if (this.bridge != null && this.bridge.getWebView() != null) {
|
|
1741
2062
|
logger.setBridge(this.bridge);
|
|
@@ -1773,11 +2094,12 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1773
2094
|
String latestVersionName,
|
|
1774
2095
|
BundleInfo current,
|
|
1775
2096
|
Boolean error,
|
|
1776
|
-
Boolean
|
|
2097
|
+
Boolean plannedDirectUpdate,
|
|
1777
2098
|
String failureAction,
|
|
1778
2099
|
String failureEvent,
|
|
1779
2100
|
boolean shouldSendStats
|
|
1780
2101
|
) {
|
|
2102
|
+
this.consumeOnLaunchDirectUpdateAttempt(Boolean.TRUE.equals(plannedDirectUpdate));
|
|
1781
2103
|
if (error) {
|
|
1782
2104
|
logger.info(
|
|
1783
2105
|
"endBackGroundTaskWithNotif error: " +
|
|
@@ -1797,7 +2119,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1797
2119
|
final JSObject ret = new JSObject();
|
|
1798
2120
|
ret.put("bundle", InternalUtils.mapToJSObject(current.toJSONMap()));
|
|
1799
2121
|
this.notifyListeners("noNeedUpdate", ret);
|
|
1800
|
-
this.sendReadyToJs(current, msg,
|
|
2122
|
+
this.sendReadyToJs(current, msg, plannedDirectUpdate);
|
|
1801
2123
|
this.backgroundDownloadTask = null;
|
|
1802
2124
|
this.downloadStartTimeMs = 0;
|
|
1803
2125
|
logger.info("endBackGroundTaskWithNotif " + msg);
|
|
@@ -1831,7 +2153,6 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1831
2153
|
private Thread backgroundDownload() {
|
|
1832
2154
|
final boolean plannedDirectUpdate = this.shouldUseDirectUpdate();
|
|
1833
2155
|
final boolean initialDirectUpdateAllowed = this.isDirectUpdateCurrentlyAllowed(plannedDirectUpdate);
|
|
1834
|
-
this.implementation.directUpdate = initialDirectUpdateAllowed;
|
|
1835
2156
|
final String messageUpdate = initialDirectUpdateAllowed
|
|
1836
2157
|
? "Update will occur now."
|
|
1837
2158
|
: "Update will occur next time app moves to background.";
|
|
@@ -1845,30 +2166,37 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1845
2166
|
final BundleInfo current = CapacitorUpdaterPlugin.this.implementation.getCurrentBundle();
|
|
1846
2167
|
|
|
1847
2168
|
// Handle network errors and other failures first
|
|
1848
|
-
if (jsRes.has("error")) {
|
|
1849
|
-
String error = jsRes.getString("error");
|
|
2169
|
+
if (jsRes.has("error") || jsRes.has("kind")) {
|
|
2170
|
+
String error = jsRes.has("error") ? jsRes.getString("error") : "";
|
|
1850
2171
|
String errorMessage = jsRes.has("message") ? jsRes.getString("message") : "server did not provide a message";
|
|
1851
2172
|
int statusCode = jsRes.has("statusCode") ? jsRes.optInt("statusCode", 0) : 0;
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
logger.error(
|
|
1855
|
-
"getLatest failed with error: " + error + ", message: " + errorMessage + ", statusCode: " + statusCode
|
|
1856
|
-
);
|
|
2173
|
+
String kind = CapacitorUpdaterPlugin.this.getUpdateResponseKind(jsRes.has("kind") ? jsRes.getString("kind") : null);
|
|
1857
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
|
+
}
|
|
1858
2186
|
|
|
2187
|
+
boolean isFailure = "failed".equals(kind);
|
|
1859
2188
|
CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
|
|
1860
2189
|
errorMessage,
|
|
1861
2190
|
latestVersion,
|
|
1862
2191
|
current,
|
|
1863
|
-
|
|
2192
|
+
isFailure,
|
|
1864
2193
|
plannedDirectUpdate,
|
|
1865
2194
|
"download_fail",
|
|
1866
2195
|
"downloadFailed",
|
|
1867
|
-
|
|
2196
|
+
isFailure
|
|
1868
2197
|
);
|
|
1869
2198
|
return;
|
|
1870
2199
|
}
|
|
1871
|
-
|
|
1872
2200
|
try {
|
|
1873
2201
|
final String latestVersionName = jsRes.getString("version");
|
|
1874
2202
|
|
|
@@ -1879,7 +2207,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1879
2207
|
);
|
|
1880
2208
|
if (directUpdateAllowedNow) {
|
|
1881
2209
|
logger.info("Direct update to builtin version");
|
|
1882
|
-
this._reset(false);
|
|
2210
|
+
this._reset(false, false);
|
|
1883
2211
|
CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
|
|
1884
2212
|
"Updated to builtin version",
|
|
1885
2213
|
latestVersionName,
|
|
@@ -1899,7 +2227,8 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1899
2227
|
"Next update will be to builtin version",
|
|
1900
2228
|
latestVersionName,
|
|
1901
2229
|
current,
|
|
1902
|
-
false
|
|
2230
|
+
false,
|
|
2231
|
+
plannedDirectUpdate
|
|
1903
2232
|
);
|
|
1904
2233
|
}
|
|
1905
2234
|
return;
|
|
@@ -1935,7 +2264,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1935
2264
|
);
|
|
1936
2265
|
return;
|
|
1937
2266
|
}
|
|
1938
|
-
if (latest.isDownloaded()) {
|
|
2267
|
+
if (latest.isDownloaded() && BundleStatus.DOWNLOADING != latest.getStatus()) {
|
|
1939
2268
|
logger.info("Latest bundle already exists and download is NOT required. " + messageUpdate);
|
|
1940
2269
|
final boolean directUpdateAllowedNow = CapacitorUpdaterPlugin.this.isDirectUpdateCurrentlyAllowed(
|
|
1941
2270
|
plannedDirectUpdate
|
|
@@ -1956,15 +2285,26 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1956
2285
|
);
|
|
1957
2286
|
return;
|
|
1958
2287
|
}
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
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
|
+
}
|
|
1968
2308
|
} else {
|
|
1969
2309
|
if (plannedDirectUpdate && !directUpdateAllowedNow) {
|
|
1970
2310
|
logger.info(
|
|
@@ -1977,7 +2317,8 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1977
2317
|
"update downloaded, will install next background",
|
|
1978
2318
|
latestVersionName,
|
|
1979
2319
|
latest,
|
|
1980
|
-
false
|
|
2320
|
+
false,
|
|
2321
|
+
plannedDirectUpdate
|
|
1981
2322
|
);
|
|
1982
2323
|
}
|
|
1983
2324
|
return;
|
|
@@ -1994,6 +2335,14 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1994
2335
|
}
|
|
1995
2336
|
}
|
|
1996
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;
|
|
1997
2346
|
startNewThread(() -> {
|
|
1998
2347
|
try {
|
|
1999
2348
|
logger.info(
|
|
@@ -2042,7 +2391,13 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
2042
2391
|
});
|
|
2043
2392
|
} else {
|
|
2044
2393
|
logger.info("No need to update, " + current.getId() + " is the latest bundle.");
|
|
2045
|
-
CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
|
|
2394
|
+
CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
|
|
2395
|
+
"No need to update",
|
|
2396
|
+
latestVersionName,
|
|
2397
|
+
current,
|
|
2398
|
+
false,
|
|
2399
|
+
plannedDirectUpdate
|
|
2400
|
+
);
|
|
2046
2401
|
}
|
|
2047
2402
|
} catch (final Exception e) {
|
|
2048
2403
|
logger.error("error in update check " + e.getMessage());
|
|
@@ -2086,15 +2441,18 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
2086
2441
|
if (next != null && !next.isErrorStatus() && !next.getId().equals(current.getId())) {
|
|
2087
2442
|
// There is a next bundle waiting for activation
|
|
2088
2443
|
logger.debug("Next bundle is: " + next.getVersionName());
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
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
|
+
});
|
|
2095
2453
|
}
|
|
2096
2454
|
} catch (final Exception e) {
|
|
2097
|
-
logger.error("Error during
|
|
2455
|
+
logger.error("Error during installNext " + e);
|
|
2098
2456
|
}
|
|
2099
2457
|
}
|
|
2100
2458
|
|
|
@@ -2117,7 +2475,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
2117
2475
|
this.notifyListeners("updateFailed", ret);
|
|
2118
2476
|
this.implementation.sendStats("update_fail", current.getVersionName());
|
|
2119
2477
|
this.implementation.setError(current);
|
|
2120
|
-
this.
|
|
2478
|
+
this.performReset(true, false, true);
|
|
2121
2479
|
if (CapacitorUpdaterPlugin.this.autoDeleteFailed && !current.isBuiltin()) {
|
|
2122
2480
|
logger.info("Deleting failing bundle: " + current.getVersionName());
|
|
2123
2481
|
try {
|
|
@@ -2136,11 +2494,17 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
2136
2494
|
|
|
2137
2495
|
private class DeferredNotifyAppReadyCheck implements Runnable {
|
|
2138
2496
|
|
|
2497
|
+
private final long waitTimeMs;
|
|
2498
|
+
|
|
2499
|
+
DeferredNotifyAppReadyCheck(final long waitTimeMs) {
|
|
2500
|
+
this.waitTimeMs = waitTimeMs;
|
|
2501
|
+
}
|
|
2502
|
+
|
|
2139
2503
|
@Override
|
|
2140
2504
|
public void run() {
|
|
2141
2505
|
try {
|
|
2142
|
-
logger.info("Wait for " +
|
|
2143
|
-
Thread.sleep(
|
|
2506
|
+
logger.info("Wait for " + this.waitTimeMs + "ms, then check for notifyAppReady");
|
|
2507
|
+
Thread.sleep(this.waitTimeMs);
|
|
2144
2508
|
CapacitorUpdaterPlugin.this.checkRevert();
|
|
2145
2509
|
CapacitorUpdaterPlugin.this.appReadyCheck = null;
|
|
2146
2510
|
} catch (final InterruptedException e) {
|
|
@@ -2493,9 +2857,9 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
2493
2857
|
|
|
2494
2858
|
JSObject result = new JSObject();
|
|
2495
2859
|
try {
|
|
2496
|
-
PackageInfo pInfo =
|
|
2860
|
+
PackageInfo pInfo = getCurrentPackageInfo();
|
|
2497
2861
|
result.put("currentVersionName", pInfo.versionName);
|
|
2498
|
-
result.put("currentVersionCode",
|
|
2862
|
+
result.put("currentVersionCode", getVersionCode(pInfo));
|
|
2499
2863
|
} catch (PackageManager.NameNotFoundException e) {
|
|
2500
2864
|
result.put("currentVersionName", "0.0.0");
|
|
2501
2865
|
result.put("currentVersionCode", "0");
|