@capawesome/capacitor-live-update 8.7.0 → 8.7.1
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/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/LiveUpdate.java +58 -3
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/LiveUpdatePlugin.java +17 -1
- package/ios/Plugin/LiveUpdate.swift +74 -6
- package/ios/Plugin/LiveUpdatePlugin.swift +15 -2
- package/package.json +1 -1
|
@@ -9,6 +9,7 @@ import android.content.res.AssetManager;
|
|
|
9
9
|
import android.os.Build;
|
|
10
10
|
import android.os.Handler;
|
|
11
11
|
import android.os.Looper;
|
|
12
|
+
import android.os.SystemClock;
|
|
12
13
|
import android.util.Base64;
|
|
13
14
|
import androidx.annotation.NonNull;
|
|
14
15
|
import androidx.annotation.Nullable;
|
|
@@ -115,8 +116,18 @@ public class LiveUpdate {
|
|
|
115
116
|
private final Handler rollbackHandler = new Handler(Looper.getMainLooper());
|
|
116
117
|
private final String manifestFileName = "capawesome-live-update-manifest.json"; // DO NOT CHANGE!
|
|
117
118
|
|
|
119
|
+
private boolean activityStopped = false;
|
|
118
120
|
private boolean initialPageLoaded = false;
|
|
119
121
|
private long lastAutoUpdateCheckTimestamp = 0;
|
|
122
|
+
|
|
123
|
+
/** Milliseconds left on the rollback timer while it is paused in the background. */
|
|
124
|
+
@Nullable
|
|
125
|
+
private Long remainingRollbackTimeout = null;
|
|
126
|
+
|
|
127
|
+
/** Uptime at which the scheduled rollback fires, or {@code null} if none is scheduled. */
|
|
128
|
+
@Nullable
|
|
129
|
+
private Long rollbackDeadline = null;
|
|
130
|
+
|
|
120
131
|
private boolean rollbackPerformed = false;
|
|
121
132
|
private boolean syncInProgress = false;
|
|
122
133
|
|
|
@@ -421,6 +432,16 @@ public class LiveUpdate {
|
|
|
421
432
|
}
|
|
422
433
|
}
|
|
423
434
|
|
|
435
|
+
public synchronized void handleOnStart() {
|
|
436
|
+
activityStopped = false;
|
|
437
|
+
resumeRollbackTimer();
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
public synchronized void handleOnStop() {
|
|
441
|
+
activityStopped = true;
|
|
442
|
+
pauseRollbackTimer();
|
|
443
|
+
}
|
|
444
|
+
|
|
424
445
|
public void ready(@NonNull NonEmptyCallback callback) {
|
|
425
446
|
Logger.debug(LiveUpdatePlugin.TAG, "App is ready.");
|
|
426
447
|
if (config.getReadyTimeout() <= 0) {
|
|
@@ -1356,6 +1377,23 @@ public class LiveUpdate {
|
|
|
1356
1377
|
sync(options, callback);
|
|
1357
1378
|
}
|
|
1358
1379
|
|
|
1380
|
+
private void pauseRollbackTimer() {
|
|
1381
|
+
if (rollbackDeadline == null) {
|
|
1382
|
+
return;
|
|
1383
|
+
}
|
|
1384
|
+
rollbackHandler.removeCallbacksAndMessages(null);
|
|
1385
|
+
remainingRollbackTimeout = Math.max(0, rollbackDeadline - SystemClock.uptimeMillis());
|
|
1386
|
+
rollbackDeadline = null;
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
private void resumeRollbackTimer() {
|
|
1390
|
+
if (remainingRollbackTimeout == null) {
|
|
1391
|
+
return;
|
|
1392
|
+
}
|
|
1393
|
+
scheduleRollback(remainingRollbackTimeout);
|
|
1394
|
+
remainingRollbackTimeout = null;
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1359
1397
|
@NonNull
|
|
1360
1398
|
private String requireAppId() throws Exception {
|
|
1361
1399
|
return requireAppId(null);
|
|
@@ -1532,16 +1570,33 @@ public class LiveUpdate {
|
|
|
1532
1570
|
preferences.setPreviousBundleId(bundleId);
|
|
1533
1571
|
}
|
|
1534
1572
|
|
|
1535
|
-
private void
|
|
1573
|
+
private void scheduleRollback(long delayMillis) {
|
|
1574
|
+
rollbackDeadline = SystemClock.uptimeMillis() + delayMillis;
|
|
1575
|
+
rollbackHandler.postDelayed(() -> {
|
|
1576
|
+
synchronized (this) {
|
|
1577
|
+
rollbackDeadline = null;
|
|
1578
|
+
}
|
|
1579
|
+
rollback();
|
|
1580
|
+
}, delayMillis);
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
/** The timer only counts time in the foreground, because a stopped activity may not run JS. */
|
|
1584
|
+
private synchronized void startRollbackTimer() {
|
|
1536
1585
|
if (config.getReadyTimeout() <= 0) {
|
|
1537
1586
|
return;
|
|
1538
1587
|
}
|
|
1539
1588
|
stopRollbackTimer();
|
|
1540
|
-
|
|
1589
|
+
if (activityStopped) {
|
|
1590
|
+
remainingRollbackTimeout = (long) config.getReadyTimeout();
|
|
1591
|
+
} else {
|
|
1592
|
+
scheduleRollback(config.getReadyTimeout());
|
|
1593
|
+
}
|
|
1541
1594
|
}
|
|
1542
1595
|
|
|
1543
|
-
private void stopRollbackTimer() {
|
|
1596
|
+
private synchronized void stopRollbackTimer() {
|
|
1544
1597
|
rollbackHandler.removeCallbacksAndMessages(null);
|
|
1598
|
+
rollbackDeadline = null;
|
|
1599
|
+
remainingRollbackTimeout = null;
|
|
1545
1600
|
}
|
|
1546
1601
|
|
|
1547
1602
|
private boolean tryCopyCurrentBundleFile(@NonNull ManifestItem fileToCopy, @NonNull File destinationDirectory) {
|
package/android/src/main/java/io/capawesome/capacitorjs/plugins/liveupdate/LiveUpdatePlugin.java
CHANGED
|
@@ -41,7 +41,7 @@ import java.util.Map;
|
|
|
41
41
|
public class LiveUpdatePlugin extends Plugin {
|
|
42
42
|
|
|
43
43
|
public static final String TAG = "LiveUpdate";
|
|
44
|
-
public static final String VERSION = "8.7.
|
|
44
|
+
public static final String VERSION = "8.7.1";
|
|
45
45
|
public static final String SHARED_PREFERENCES_NAME = "CapawesomeLiveUpdate"; // DO NOT CHANGE
|
|
46
46
|
public static final String ERROR_APP_ID_MISSING = "appId must be configured.";
|
|
47
47
|
public static final String ERROR_BUNDLE_DIRECTORY_NOT_FOUND = "Bundle directory could not be resolved.";
|
|
@@ -132,6 +132,22 @@ public class LiveUpdatePlugin extends Plugin {
|
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
+
@Override
|
|
136
|
+
protected void handleOnStart() {
|
|
137
|
+
super.handleOnStart();
|
|
138
|
+
if (implementation != null) {
|
|
139
|
+
implementation.handleOnStart();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
@Override
|
|
144
|
+
protected void handleOnStop() {
|
|
145
|
+
super.handleOnStop();
|
|
146
|
+
if (implementation != null) {
|
|
147
|
+
implementation.handleOnStop();
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
135
151
|
@PluginMethod
|
|
136
152
|
public void clearBlockedBundles(PluginCall call) {
|
|
137
153
|
try {
|
|
@@ -27,9 +27,15 @@ import CommonCrypto
|
|
|
27
27
|
private let plugin: LiveUpdatePlugin?
|
|
28
28
|
private let preferences: LiveUpdatePreferences
|
|
29
29
|
|
|
30
|
+
private var isAppInBackground = false
|
|
31
|
+
private var lastAutoUpdateCheckTimestamp: Int64 = 0
|
|
32
|
+
/// Milliseconds left on the rollback timer while it is paused in the background.
|
|
33
|
+
private var remainingRollbackTimeout: Int?
|
|
34
|
+
private var rollbackDeadline: DispatchTime?
|
|
30
35
|
private var rollbackDispatchWorkItem: DispatchWorkItem?
|
|
31
36
|
private var rollbackPerformed = false
|
|
32
|
-
|
|
37
|
+
/// Guards the rollback timer state: plugin methods run on the bridge queue, lifecycle callbacks on main.
|
|
38
|
+
private let rollbackTimerLock = NSLock()
|
|
33
39
|
private var syncInProgress = false
|
|
34
40
|
|
|
35
41
|
/// - Parameter plugin: The Capacitor plugin, or `nil` for a headless instance
|
|
@@ -225,7 +231,18 @@ import CommonCrypto
|
|
|
225
231
|
}
|
|
226
232
|
}
|
|
227
233
|
|
|
234
|
+
@objc public func handleAppDidEnterBackground() {
|
|
235
|
+
rollbackTimerLock.lock()
|
|
236
|
+
defer { rollbackTimerLock.unlock() }
|
|
237
|
+
isAppInBackground = true
|
|
238
|
+
pauseRollbackTimer()
|
|
239
|
+
}
|
|
240
|
+
|
|
228
241
|
@objc public func handleAppWillEnterForeground() {
|
|
242
|
+
rollbackTimerLock.lock()
|
|
243
|
+
defer { rollbackTimerLock.unlock() }
|
|
244
|
+
isAppInBackground = false
|
|
245
|
+
resumeRollbackTimer()
|
|
229
246
|
if config.autoUpdateStrategy == "background" {
|
|
230
247
|
performAutoUpdate()
|
|
231
248
|
}
|
|
@@ -410,6 +427,14 @@ import CommonCrypto
|
|
|
410
427
|
return Self.downloadsDirectoryUrl
|
|
411
428
|
}
|
|
412
429
|
|
|
430
|
+
/// Must be called with `rollbackTimerLock` held.
|
|
431
|
+
private func clearRollbackTimer() {
|
|
432
|
+
rollbackDispatchWorkItem?.cancel()
|
|
433
|
+
rollbackDispatchWorkItem = nil
|
|
434
|
+
rollbackDeadline = nil
|
|
435
|
+
remainingRollbackTimeout = nil
|
|
436
|
+
}
|
|
437
|
+
|
|
413
438
|
private func copyCurrentBundleFile(fileToCopy: ManifestItem, toDirectory: URL) throws {
|
|
414
439
|
let currentBundleId = getCurrentBundleId()
|
|
415
440
|
let destination = toDirectory.appendingPathComponent(fileToCopy.href)
|
|
@@ -816,6 +841,27 @@ import CommonCrypto
|
|
|
816
841
|
}
|
|
817
842
|
}
|
|
818
843
|
|
|
844
|
+
/// Must be called with `rollbackTimerLock` held.
|
|
845
|
+
private func pauseRollbackTimer() {
|
|
846
|
+
guard let deadline = rollbackDeadline else {
|
|
847
|
+
return
|
|
848
|
+
}
|
|
849
|
+
rollbackDispatchWorkItem?.cancel()
|
|
850
|
+
rollbackDispatchWorkItem = nil
|
|
851
|
+
rollbackDeadline = nil
|
|
852
|
+
let now = DispatchTime.now()
|
|
853
|
+
remainingRollbackTimeout = deadline > now ? Int((deadline.uptimeNanoseconds - now.uptimeNanoseconds) / 1_000_000) : 0
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
/// Must be called with `rollbackTimerLock` held.
|
|
857
|
+
private func resumeRollbackTimer() {
|
|
858
|
+
guard let remainingRollbackTimeout = remainingRollbackTimeout else {
|
|
859
|
+
return
|
|
860
|
+
}
|
|
861
|
+
self.remainingRollbackTimeout = nil
|
|
862
|
+
scheduleRollback(afterMilliseconds: remainingRollbackTimeout)
|
|
863
|
+
}
|
|
864
|
+
|
|
819
865
|
private func requireAppId(_ appIdOverride: String? = nil) throws -> String {
|
|
820
866
|
guard let appId = appIdOverride ?? getAppId(), !appId.isEmpty else {
|
|
821
867
|
throw CustomError.appIdMissing
|
|
@@ -968,19 +1014,41 @@ import CommonCrypto
|
|
|
968
1014
|
preferences.setPreviousBundleId(bundleId)
|
|
969
1015
|
}
|
|
970
1016
|
|
|
1017
|
+
private func scheduleRollback(afterMilliseconds milliseconds: Int) {
|
|
1018
|
+
let deadline = DispatchTime.now() + DispatchTimeInterval.milliseconds(milliseconds)
|
|
1019
|
+
let workItem = DispatchWorkItem { [weak self] in
|
|
1020
|
+
guard let self = self else {
|
|
1021
|
+
return
|
|
1022
|
+
}
|
|
1023
|
+
self.rollbackTimerLock.lock()
|
|
1024
|
+
self.rollbackDeadline = nil
|
|
1025
|
+
self.rollbackTimerLock.unlock()
|
|
1026
|
+
self.rollback()
|
|
1027
|
+
}
|
|
1028
|
+
rollbackDeadline = deadline
|
|
1029
|
+
rollbackDispatchWorkItem = workItem
|
|
1030
|
+
DispatchQueue.main.asyncAfter(deadline: deadline, execute: workItem)
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
/// The timer only counts time in the foreground, because a suspended app cannot call `ready()`.
|
|
971
1034
|
private func startRollbackTimer() {
|
|
972
1035
|
guard config.readyTimeout > 0 else {
|
|
973
1036
|
return
|
|
974
1037
|
}
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
1038
|
+
rollbackTimerLock.lock()
|
|
1039
|
+
defer { rollbackTimerLock.unlock() }
|
|
1040
|
+
clearRollbackTimer()
|
|
1041
|
+
if isAppInBackground {
|
|
1042
|
+
remainingRollbackTimeout = config.readyTimeout
|
|
1043
|
+
} else {
|
|
1044
|
+
scheduleRollback(afterMilliseconds: config.readyTimeout)
|
|
978
1045
|
}
|
|
979
|
-
DispatchQueue.main.asyncAfter(deadline: .now() + DispatchTimeInterval.milliseconds(config.readyTimeout), execute: rollbackDispatchWorkItem!)
|
|
980
1046
|
}
|
|
981
1047
|
|
|
982
1048
|
private func stopRollbackTimer() {
|
|
983
|
-
|
|
1049
|
+
rollbackTimerLock.lock()
|
|
1050
|
+
defer { rollbackTimerLock.unlock() }
|
|
1051
|
+
clearRollbackTimer()
|
|
984
1052
|
}
|
|
985
1053
|
|
|
986
1054
|
private func tryCopyCurrentBundleFile(
|
|
@@ -8,7 +8,7 @@ import Capacitor
|
|
|
8
8
|
@objc(LiveUpdatePlugin)
|
|
9
9
|
public class LiveUpdatePlugin: CAPPlugin, CAPBridgedPlugin {
|
|
10
10
|
public static let tag = "LiveUpdate"
|
|
11
|
-
public static let version = "8.7.
|
|
11
|
+
public static let version = "8.7.1"
|
|
12
12
|
public static let userDefaultsPrefix = "CapawesomeLiveUpdate" // DO NOT CHANGE
|
|
13
13
|
|
|
14
14
|
public let identifier = "LiveUpdatePlugin"
|
|
@@ -54,13 +54,22 @@ public class LiveUpdatePlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
54
54
|
// Notify implementation about load
|
|
55
55
|
implementation?.handleLoad()
|
|
56
56
|
|
|
57
|
-
// Register for app
|
|
57
|
+
// Register for app lifecycle notifications
|
|
58
|
+
NotificationCenter.default.addObserver(
|
|
59
|
+
self,
|
|
60
|
+
selector: #selector(handleAppDidEnterBackground),
|
|
61
|
+
name: UIApplication.didEnterBackgroundNotification,
|
|
62
|
+
object: nil
|
|
63
|
+
)
|
|
58
64
|
NotificationCenter.default.addObserver(
|
|
59
65
|
self,
|
|
60
66
|
selector: #selector(handleAppWillEnterForeground),
|
|
61
67
|
name: UIApplication.willEnterForegroundNotification,
|
|
62
68
|
object: nil
|
|
63
69
|
)
|
|
70
|
+
if UIApplication.shared.applicationState == .background {
|
|
71
|
+
implementation?.handleAppDidEnterBackground()
|
|
72
|
+
}
|
|
64
73
|
}
|
|
65
74
|
|
|
66
75
|
@objc func clearBlockedBundles(_ call: CAPPluginCall) {
|
|
@@ -387,6 +396,10 @@ public class LiveUpdatePlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
387
396
|
notifyListeners(eventReloaded, data: event, retainUntilConsumed: true)
|
|
388
397
|
}
|
|
389
398
|
|
|
399
|
+
@objc private func handleAppDidEnterBackground() {
|
|
400
|
+
implementation?.handleAppDidEnterBackground()
|
|
401
|
+
}
|
|
402
|
+
|
|
390
403
|
@objc private func handleAppWillEnterForeground() {
|
|
391
404
|
implementation?.handleAppWillEnterForeground()
|
|
392
405
|
}
|
package/package.json
CHANGED