@capgo/capacitor-updater 7.2.13 → 7.2.17
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/README.md +1 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdater.java +8 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +30 -118
- package/android/src/main/java/ee/forgr/capacitor_updater/DelayUpdateUtils.java +222 -0
- package/dist/docs.json +1 -1
- package/dist/esm/definitions.d.ts +1 -0
- package/dist/esm/definitions.js.map +1 -1
- package/ios/Plugin/CapacitorUpdaterPlugin.swift +75 -118
- package/ios/Plugin/DelayUpdateUtils.swift +220 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -764,6 +764,7 @@ addListener(eventName: 'download', listenerFunc: (state: DownloadEvent) => void)
|
|
|
764
764
|
```
|
|
765
765
|
|
|
766
766
|
Listen for bundle download event in the App. Fires once a download has started, during downloading and when finished.
|
|
767
|
+
This will return you all download percent during the download
|
|
767
768
|
|
|
768
769
|
| Param | Type |
|
|
769
770
|
| ------------------ | --------------------------------------------------------------------------- |
|
|
@@ -322,6 +322,14 @@ public class CapacitorUpdater {
|
|
|
322
322
|
|
|
323
323
|
if (!isManifest) {
|
|
324
324
|
String checksumDecrypted = Objects.requireNonNullElse(checksumRes, "");
|
|
325
|
+
|
|
326
|
+
// If public key is present but no checksum provided, refuse installation
|
|
327
|
+
if (!this.publicKey.isEmpty() && checksumDecrypted.isEmpty()) {
|
|
328
|
+
Log.e(CapacitorUpdater.TAG, "Public key present but no checksum provided");
|
|
329
|
+
this.sendStats("checksum_required");
|
|
330
|
+
throw new IOException("Checksum required when public key is present: " + id);
|
|
331
|
+
}
|
|
332
|
+
|
|
325
333
|
if (!sessionKey.isEmpty()) {
|
|
326
334
|
CryptoCipherV2.decryptFile(downloaded, publicKey, sessionKey);
|
|
327
335
|
checksumDecrypted = CryptoCipherV2.decryptChecksum(checksumRes, publicKey);
|
|
@@ -49,6 +49,7 @@ import okhttp3.OkHttpClient;
|
|
|
49
49
|
import okhttp3.Protocol;
|
|
50
50
|
import org.json.JSONArray;
|
|
51
51
|
import org.json.JSONException;
|
|
52
|
+
import org.json.JSONObject;
|
|
52
53
|
|
|
53
54
|
@CapacitorPlugin(name = "CapacitorUpdater")
|
|
54
55
|
public class CapacitorUpdaterPlugin extends Plugin {
|
|
@@ -57,7 +58,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
57
58
|
private static final String statsUrlDefault = "https://plugin.capgo.app/stats";
|
|
58
59
|
private static final String channelUrlDefault = "https://plugin.capgo.app/channel_self";
|
|
59
60
|
|
|
60
|
-
private final String PLUGIN_VERSION = "7.2.
|
|
61
|
+
private final String PLUGIN_VERSION = "7.2.17";
|
|
61
62
|
private static final String DELAY_CONDITION_PREFERENCES = "";
|
|
62
63
|
|
|
63
64
|
private SharedPreferences.Editor editor;
|
|
@@ -86,6 +87,8 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
86
87
|
|
|
87
88
|
private int lastNotifiedStatPercent = 0;
|
|
88
89
|
|
|
90
|
+
private DelayUpdateUtils delayUpdateUtils;
|
|
91
|
+
|
|
89
92
|
public Thread startNewThread(final Runnable function, Number waitTime) {
|
|
90
93
|
Thread bgTask = new Thread(() -> {
|
|
91
94
|
try {
|
|
@@ -140,9 +143,14 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
140
143
|
.readTimeout(this.implementation.timeout, TimeUnit.MILLISECONDS)
|
|
141
144
|
.writeTimeout(this.implementation.timeout, TimeUnit.MILLISECONDS)
|
|
142
145
|
.build();
|
|
143
|
-
|
|
144
146
|
this.implementation.directUpdate = this.getConfig().getBoolean("directUpdate", false);
|
|
145
147
|
this.currentVersionNative = new Version(this.getConfig().getString("version", pInfo.versionName));
|
|
148
|
+
this.delayUpdateUtils = new DelayUpdateUtils(
|
|
149
|
+
this.prefs,
|
|
150
|
+
this.editor,
|
|
151
|
+
this.currentVersionNative,
|
|
152
|
+
CapacitorUpdaterPlugin.this::installNext
|
|
153
|
+
);
|
|
146
154
|
} catch (final PackageManager.NameNotFoundException e) {
|
|
147
155
|
Log.e(CapacitorUpdater.TAG, "Error instantiating implementation", e);
|
|
148
156
|
return;
|
|
@@ -831,13 +839,21 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
831
839
|
@PluginMethod
|
|
832
840
|
public void setMultiDelay(final PluginCall call) {
|
|
833
841
|
try {
|
|
834
|
-
final
|
|
842
|
+
final JSONArray delayConditions = call.getData().optJSONArray("delayConditions");
|
|
835
843
|
if (delayConditions == null) {
|
|
836
844
|
Log.e(CapacitorUpdater.TAG, "setMultiDelay called without delayCondition");
|
|
837
845
|
call.reject("setMultiDelay called without delayCondition");
|
|
838
846
|
return;
|
|
839
847
|
}
|
|
840
|
-
|
|
848
|
+
for (int i = 0; i < delayConditions.length(); i++) {
|
|
849
|
+
final JSONObject object = delayConditions.optJSONObject(i);
|
|
850
|
+
if (object != null && object.optString("kind").equals("background") && object.optString("value").isEmpty()) {
|
|
851
|
+
object.put("value", "0");
|
|
852
|
+
delayConditions.put(i, object);
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
if (this.delayUpdateUtils.setMultiDelay(delayConditions.toString())) {
|
|
841
857
|
call.resolve();
|
|
842
858
|
} else {
|
|
843
859
|
call.reject("Failed to delay update");
|
|
@@ -848,95 +864,15 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
848
864
|
}
|
|
849
865
|
}
|
|
850
866
|
|
|
851
|
-
private Boolean _setMultiDelay(String delayConditions) {
|
|
852
|
-
try {
|
|
853
|
-
this.editor.putString(DELAY_CONDITION_PREFERENCES, delayConditions);
|
|
854
|
-
this.editor.commit();
|
|
855
|
-
Log.i(CapacitorUpdater.TAG, "Delay update saved");
|
|
856
|
-
return true;
|
|
857
|
-
} catch (final Exception e) {
|
|
858
|
-
Log.e(CapacitorUpdater.TAG, "Failed to delay update, [Error calling '_setMultiDelay()']", e);
|
|
859
|
-
return false;
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
private boolean _cancelDelay(String source) {
|
|
864
|
-
try {
|
|
865
|
-
this.editor.remove(DELAY_CONDITION_PREFERENCES);
|
|
866
|
-
this.editor.commit();
|
|
867
|
-
Log.i(CapacitorUpdater.TAG, "All delays canceled from " + source);
|
|
868
|
-
return true;
|
|
869
|
-
} catch (final Exception e) {
|
|
870
|
-
Log.e(CapacitorUpdater.TAG, "Failed to cancel update delay", e);
|
|
871
|
-
return false;
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
|
|
875
867
|
@PluginMethod
|
|
876
868
|
public void cancelDelay(final PluginCall call) {
|
|
877
|
-
if (this.
|
|
869
|
+
if (this.delayUpdateUtils.cancelDelay("JS")) {
|
|
878
870
|
call.resolve();
|
|
879
871
|
} else {
|
|
880
872
|
call.reject("Failed to cancel delay");
|
|
881
873
|
}
|
|
882
874
|
}
|
|
883
875
|
|
|
884
|
-
private void _checkCancelDelay(Boolean killed) {
|
|
885
|
-
Gson gson = new Gson();
|
|
886
|
-
String delayUpdatePreferences = prefs.getString(DELAY_CONDITION_PREFERENCES, "[]");
|
|
887
|
-
Type type = new TypeToken<ArrayList<DelayCondition>>() {}.getType();
|
|
888
|
-
ArrayList<DelayCondition> delayConditionList = gson.fromJson(delayUpdatePreferences, type);
|
|
889
|
-
for (DelayCondition condition : delayConditionList) {
|
|
890
|
-
String kind = condition.getKind().toString();
|
|
891
|
-
String value = condition.getValue();
|
|
892
|
-
if (!kind.isEmpty()) {
|
|
893
|
-
switch (kind) {
|
|
894
|
-
case "background":
|
|
895
|
-
if (!killed) {
|
|
896
|
-
this._cancelDelay("background check");
|
|
897
|
-
}
|
|
898
|
-
break;
|
|
899
|
-
case "kill":
|
|
900
|
-
if (killed) {
|
|
901
|
-
this._cancelDelay("kill check");
|
|
902
|
-
this.installNext();
|
|
903
|
-
}
|
|
904
|
-
break;
|
|
905
|
-
case "date":
|
|
906
|
-
if (!"".equals(value)) {
|
|
907
|
-
try {
|
|
908
|
-
final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
|
|
909
|
-
Date date = sdf.parse(value);
|
|
910
|
-
assert date != null;
|
|
911
|
-
if (new Date().compareTo(date) > 0) {
|
|
912
|
-
this._cancelDelay("date expired");
|
|
913
|
-
}
|
|
914
|
-
} catch (final Exception e) {
|
|
915
|
-
this._cancelDelay("date parsing issue");
|
|
916
|
-
}
|
|
917
|
-
} else {
|
|
918
|
-
this._cancelDelay("delayVal absent");
|
|
919
|
-
}
|
|
920
|
-
break;
|
|
921
|
-
case "nativeVersion":
|
|
922
|
-
if (!"".equals(value)) {
|
|
923
|
-
try {
|
|
924
|
-
final Version versionLimit = new Version(value);
|
|
925
|
-
if (this.currentVersionNative.isAtLeast(versionLimit)) {
|
|
926
|
-
this._cancelDelay("nativeVersion above limit");
|
|
927
|
-
}
|
|
928
|
-
} catch (final Exception e) {
|
|
929
|
-
this._cancelDelay("nativeVersion parsing issue");
|
|
930
|
-
}
|
|
931
|
-
} else {
|
|
932
|
-
this._cancelDelay("delayVal absent");
|
|
933
|
-
}
|
|
934
|
-
break;
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
|
|
940
876
|
private Boolean _isAutoUpdateEnabled() {
|
|
941
877
|
final CapConfig config = CapConfig.loadDefault(this.getActivity());
|
|
942
878
|
String serverUrl = config.getServerUrl();
|
|
@@ -1107,7 +1043,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1107
1043
|
);
|
|
1108
1044
|
if (CapacitorUpdaterPlugin.this.implementation.directUpdate) {
|
|
1109
1045
|
Gson gson = new Gson();
|
|
1110
|
-
String delayUpdatePreferences = prefs.getString(DELAY_CONDITION_PREFERENCES, "[]");
|
|
1046
|
+
String delayUpdatePreferences = prefs.getString(DelayUpdateUtils.DELAY_CONDITION_PREFERENCES, "[]");
|
|
1111
1047
|
Type type = new TypeToken<ArrayList<DelayCondition>>() {}.getType();
|
|
1112
1048
|
ArrayList<DelayCondition> delayConditionList = gson.fromJson(delayUpdatePreferences, type);
|
|
1113
1049
|
if (delayConditionList != null && !delayConditionList.isEmpty()) {
|
|
@@ -1221,7 +1157,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1221
1157
|
private void installNext() {
|
|
1222
1158
|
try {
|
|
1223
1159
|
Gson gson = new Gson();
|
|
1224
|
-
String delayUpdatePreferences = prefs.getString(DELAY_CONDITION_PREFERENCES, "[]");
|
|
1160
|
+
String delayUpdatePreferences = prefs.getString(DelayUpdateUtils.DELAY_CONDITION_PREFERENCES, "[]");
|
|
1225
1161
|
Type type = new TypeToken<ArrayList<DelayCondition>>() {}.getType();
|
|
1226
1162
|
ArrayList<DelayCondition> delayConditionList = gson.fromJson(delayUpdatePreferences, type);
|
|
1227
1163
|
if (delayConditionList != null && !delayConditionList.isEmpty()) {
|
|
@@ -1302,7 +1238,8 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1302
1238
|
public void appMovedToForeground() {
|
|
1303
1239
|
final BundleInfo current = CapacitorUpdaterPlugin.this.implementation.getCurrentBundle();
|
|
1304
1240
|
CapacitorUpdaterPlugin.this.implementation.sendStats("app_moved_to_foreground", current.getVersionName());
|
|
1305
|
-
this.
|
|
1241
|
+
this.delayUpdateUtils.checkCancelDelay(DelayUpdateUtils.CancelDelaySource.FOREGROUND);
|
|
1242
|
+
this.delayUpdateUtils.unsetBackgroundTimestamp();
|
|
1306
1243
|
if (
|
|
1307
1244
|
CapacitorUpdaterPlugin.this._isAutoUpdateEnabled() &&
|
|
1308
1245
|
(this.backgroundDownloadTask == null || !this.backgroundDownloadTask.isAlive())
|
|
@@ -1320,35 +1257,10 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1320
1257
|
CapacitorUpdaterPlugin.this.implementation.sendStats("app_moved_to_background", current.getVersionName());
|
|
1321
1258
|
Log.i(CapacitorUpdater.TAG, "Checking for pending update");
|
|
1322
1259
|
try {
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
String backgroundValue = null;
|
|
1328
|
-
for (DelayCondition delayCondition : delayConditionList) {
|
|
1329
|
-
if (delayCondition.getKind().toString().equals("background")) {
|
|
1330
|
-
String value = delayCondition.getValue();
|
|
1331
|
-
backgroundValue = (value != null && !value.isEmpty()) ? value : "0";
|
|
1332
|
-
}
|
|
1333
|
-
}
|
|
1334
|
-
if (backgroundValue != null) {
|
|
1335
|
-
taskRunning = true;
|
|
1336
|
-
final Long timeout = Long.parseLong(backgroundValue);
|
|
1337
|
-
if (backgroundTask != null) {
|
|
1338
|
-
backgroundTask.interrupt();
|
|
1339
|
-
}
|
|
1340
|
-
backgroundTask = startNewThread(
|
|
1341
|
-
() -> {
|
|
1342
|
-
taskRunning = false;
|
|
1343
|
-
_checkCancelDelay(false);
|
|
1344
|
-
installNext();
|
|
1345
|
-
},
|
|
1346
|
-
timeout
|
|
1347
|
-
);
|
|
1348
|
-
} else {
|
|
1349
|
-
this._checkCancelDelay(false);
|
|
1350
|
-
this.installNext();
|
|
1351
|
-
}
|
|
1260
|
+
// We need to set "backgrounded time"
|
|
1261
|
+
this.delayUpdateUtils.setBackgroundTimestamp(System.currentTimeMillis());
|
|
1262
|
+
this.delayUpdateUtils.checkCancelDelay(DelayUpdateUtils.CancelDelaySource.BACKGROUND);
|
|
1263
|
+
this.installNext();
|
|
1352
1264
|
} catch (final Exception e) {
|
|
1353
1265
|
Log.e(CapacitorUpdater.TAG, "Error during onActivityStopped", e);
|
|
1354
1266
|
}
|
|
@@ -1379,7 +1291,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1379
1291
|
|
|
1380
1292
|
private void appKilled() {
|
|
1381
1293
|
Log.d(CapacitorUpdater.TAG, "onActivityDestroyed: all activity destroyed");
|
|
1382
|
-
this.
|
|
1294
|
+
this.delayUpdateUtils.checkCancelDelay(DelayUpdateUtils.CancelDelaySource.KILLED);
|
|
1383
1295
|
}
|
|
1384
1296
|
|
|
1385
1297
|
@Override
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
package ee.forgr.capacitor_updater;
|
|
2
|
+
|
|
3
|
+
import android.content.SharedPreferences;
|
|
4
|
+
import android.util.Log;
|
|
5
|
+
import com.google.common.reflect.TypeToken;
|
|
6
|
+
import com.google.gson.Gson;
|
|
7
|
+
import io.github.g00fy2.versioncompare.Version;
|
|
8
|
+
import java.lang.reflect.Type;
|
|
9
|
+
import java.text.SimpleDateFormat;
|
|
10
|
+
import java.util.ArrayList;
|
|
11
|
+
import java.util.Date;
|
|
12
|
+
|
|
13
|
+
public class DelayUpdateUtils {
|
|
14
|
+
|
|
15
|
+
public static final String DELAY_CONDITION_PREFERENCES = "DELAY_CONDITION_PREFERENCES_CAPGO";
|
|
16
|
+
public static final String BACKGROUND_TIMESTAMP_KEY = "BACKGROUND_TIMESTAMP_KEY_CAPGO";
|
|
17
|
+
|
|
18
|
+
private final SharedPreferences prefs;
|
|
19
|
+
private final SharedPreferences.Editor editor;
|
|
20
|
+
private final Version currentVersionNative;
|
|
21
|
+
private final Runnable installNext;
|
|
22
|
+
|
|
23
|
+
public DelayUpdateUtils(SharedPreferences prefs, SharedPreferences.Editor editor, Version currentVersionNative, Runnable installNext) {
|
|
24
|
+
this.prefs = prefs;
|
|
25
|
+
this.editor = editor;
|
|
26
|
+
this.currentVersionNative = currentVersionNative;
|
|
27
|
+
this.installNext = installNext;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public enum CancelDelaySource {
|
|
31
|
+
KILLED,
|
|
32
|
+
BACKGROUND,
|
|
33
|
+
FOREGROUND
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public void checkCancelDelay(CancelDelaySource source) {
|
|
37
|
+
Gson gson = new Gson();
|
|
38
|
+
String delayUpdatePreferences = prefs.getString(DELAY_CONDITION_PREFERENCES, "[]");
|
|
39
|
+
Type type = new TypeToken<ArrayList<DelayCondition>>() {}.getType();
|
|
40
|
+
ArrayList<DelayCondition> delayConditionList = gson.fromJson(delayUpdatePreferences, type);
|
|
41
|
+
ArrayList<DelayCondition> delayConditionListToKeep = new ArrayList<>(delayConditionList.size());
|
|
42
|
+
int index = 0;
|
|
43
|
+
|
|
44
|
+
for (DelayCondition condition : delayConditionList) {
|
|
45
|
+
DelayUntilNext kind = condition.getKind();
|
|
46
|
+
String value = condition.getValue();
|
|
47
|
+
switch (kind) {
|
|
48
|
+
case DelayUntilNext.background:
|
|
49
|
+
if (source == CancelDelaySource.FOREGROUND) {
|
|
50
|
+
long backgroundedAt = getBackgroundTimestamp();
|
|
51
|
+
long now = System.currentTimeMillis();
|
|
52
|
+
long delta = Math.max(0, now - backgroundedAt);
|
|
53
|
+
long longValue = 0L;
|
|
54
|
+
try {
|
|
55
|
+
longValue = Long.parseLong(value);
|
|
56
|
+
} catch (NumberFormatException e) {
|
|
57
|
+
Log.e(
|
|
58
|
+
CapacitorUpdater.TAG,
|
|
59
|
+
"Background condition (value: " +
|
|
60
|
+
value +
|
|
61
|
+
") had an invalid value at index " +
|
|
62
|
+
index +
|
|
63
|
+
". We will likely remove it."
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (delta > longValue) {
|
|
68
|
+
Log.i(
|
|
69
|
+
CapacitorUpdater.TAG,
|
|
70
|
+
"Background condition (value: " +
|
|
71
|
+
value +
|
|
72
|
+
") deleted at index " +
|
|
73
|
+
index +
|
|
74
|
+
". Delta: " +
|
|
75
|
+
delta +
|
|
76
|
+
", longValue: " +
|
|
77
|
+
longValue
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
} else {
|
|
81
|
+
delayConditionListToKeep.add(condition);
|
|
82
|
+
Log.i(
|
|
83
|
+
CapacitorUpdater.TAG,
|
|
84
|
+
"Background delay (value: " +
|
|
85
|
+
value +
|
|
86
|
+
") condition kept at index " +
|
|
87
|
+
index +
|
|
88
|
+
" (source: " +
|
|
89
|
+
source.toString() +
|
|
90
|
+
")"
|
|
91
|
+
);
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
case DelayUntilNext.kill:
|
|
95
|
+
if (source == CancelDelaySource.KILLED) {
|
|
96
|
+
this.installNext.run();
|
|
97
|
+
} else {
|
|
98
|
+
delayConditionListToKeep.add(condition);
|
|
99
|
+
Log.i(
|
|
100
|
+
CapacitorUpdater.TAG,
|
|
101
|
+
"Kill delay (value: " + value + ") condition kept at index " + index + " (source: " + source.toString() + ")"
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
break;
|
|
105
|
+
case DelayUntilNext.date:
|
|
106
|
+
if (!"".equals(value)) {
|
|
107
|
+
try {
|
|
108
|
+
final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
|
|
109
|
+
Date date = sdf.parse(value);
|
|
110
|
+
assert date != null;
|
|
111
|
+
if (new Date().compareTo(date) > 0) {
|
|
112
|
+
Log.i(
|
|
113
|
+
CapacitorUpdater.TAG,
|
|
114
|
+
"Date delay (value: " + value + ") condition removed due to expired date at index " + index
|
|
115
|
+
);
|
|
116
|
+
} else {
|
|
117
|
+
delayConditionListToKeep.add(condition);
|
|
118
|
+
Log.i(CapacitorUpdater.TAG, "Date delay (value: " + value + ") condition kept at index " + index);
|
|
119
|
+
}
|
|
120
|
+
} catch (final Exception e) {
|
|
121
|
+
Log.e(
|
|
122
|
+
CapacitorUpdater.TAG,
|
|
123
|
+
"Date delay (value: " + value + ") condition removed due to parsing issue at index " + index,
|
|
124
|
+
e
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
Log.d(
|
|
129
|
+
CapacitorUpdater.TAG,
|
|
130
|
+
"Date delay (value: " + value + ") condition removed due to empty value at index " + index
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
break;
|
|
134
|
+
case DelayUntilNext.nativeVersion:
|
|
135
|
+
if (!"".equals(value)) {
|
|
136
|
+
try {
|
|
137
|
+
final Version versionLimit = new Version(value);
|
|
138
|
+
if (this.currentVersionNative.isAtLeast(versionLimit)) {
|
|
139
|
+
Log.i(
|
|
140
|
+
CapacitorUpdater.TAG,
|
|
141
|
+
"Native version delay (value: " + value + ") condition removed due to above limit at index " + index
|
|
142
|
+
);
|
|
143
|
+
} else {
|
|
144
|
+
delayConditionListToKeep.add(condition);
|
|
145
|
+
Log.i(CapacitorUpdater.TAG, "Native version delay (value: " + value + ") condition kept at index " + index);
|
|
146
|
+
}
|
|
147
|
+
} catch (final Exception e) {
|
|
148
|
+
Log.e(
|
|
149
|
+
CapacitorUpdater.TAG,
|
|
150
|
+
"Native version delay (value: " + value + ") condition removed due to parsing issue at index " + index,
|
|
151
|
+
e
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
} else {
|
|
155
|
+
Log.d(
|
|
156
|
+
CapacitorUpdater.TAG,
|
|
157
|
+
"Native version delay (value: " + value + ") condition removed due to empty value at index " + index
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
index++;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (!delayConditionListToKeep.isEmpty()) {
|
|
166
|
+
this.setMultiDelay(gson.toJson(delayConditionListToKeep));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
public Boolean setMultiDelay(String delayConditions) {
|
|
171
|
+
try {
|
|
172
|
+
this.editor.putString(DELAY_CONDITION_PREFERENCES, delayConditions);
|
|
173
|
+
this.editor.commit();
|
|
174
|
+
Log.i(CapacitorUpdater.TAG, "Delay update saved");
|
|
175
|
+
return true;
|
|
176
|
+
} catch (final Exception e) {
|
|
177
|
+
Log.e(CapacitorUpdater.TAG, "Failed to delay update, [Error calling '_setMultiDelay()']", e);
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
public void setBackgroundTimestamp(long backgroundTimestamp) {
|
|
183
|
+
try {
|
|
184
|
+
this.editor.putLong(BACKGROUND_TIMESTAMP_KEY, backgroundTimestamp);
|
|
185
|
+
this.editor.commit();
|
|
186
|
+
Log.i(CapacitorUpdater.TAG, "Delay update saved");
|
|
187
|
+
} catch (final Exception e) {
|
|
188
|
+
Log.e(CapacitorUpdater.TAG, "Failed to delay update, [Error calling '_setBackgroundTimestamp()']", e);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
public void unsetBackgroundTimestamp() {
|
|
193
|
+
try {
|
|
194
|
+
this.editor.remove(BACKGROUND_TIMESTAMP_KEY);
|
|
195
|
+
this.editor.commit();
|
|
196
|
+
Log.i(CapacitorUpdater.TAG, "Delay update saved");
|
|
197
|
+
} catch (final Exception e) {
|
|
198
|
+
Log.e(CapacitorUpdater.TAG, "Failed to delay update, [Error calling '_unsetBackgroundTimestamp()']", e);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private long getBackgroundTimestamp() {
|
|
203
|
+
try {
|
|
204
|
+
return this.prefs.getLong(BACKGROUND_TIMESTAMP_KEY, 0);
|
|
205
|
+
} catch (final Exception e) {
|
|
206
|
+
Log.e(CapacitorUpdater.TAG, "Failed to delay update, [Error calling '_getBackgroundTimestamp()']", e);
|
|
207
|
+
return 0;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
public boolean cancelDelay(String source) {
|
|
212
|
+
try {
|
|
213
|
+
this.editor.remove(DELAY_CONDITION_PREFERENCES);
|
|
214
|
+
this.editor.commit();
|
|
215
|
+
Log.i(CapacitorUpdater.TAG, "All delays canceled from " + source);
|
|
216
|
+
return true;
|
|
217
|
+
} catch (final Exception e) {
|
|
218
|
+
Log.e(CapacitorUpdater.TAG, "Failed to cancel update delay", e);
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
package/dist/docs.json
CHANGED
|
@@ -703,7 +703,7 @@
|
|
|
703
703
|
"text": "2.0.11"
|
|
704
704
|
}
|
|
705
705
|
],
|
|
706
|
-
"docs": "Listen for bundle download event in the App. Fires once a download has started, during downloading and when finished
|
|
706
|
+
"docs": "Listen for bundle download event in the App. Fires once a download has started, during downloading and when finished.\nThis will return you all download percent during the download",
|
|
707
707
|
"complexTypes": [
|
|
708
708
|
"PluginListenerHandle",
|
|
709
709
|
"DownloadEvent"
|
|
@@ -429,6 +429,7 @@ export interface CapacitorUpdaterPlugin {
|
|
|
429
429
|
removeAllListeners(): Promise<void>;
|
|
430
430
|
/**
|
|
431
431
|
* Listen for bundle download event in the App. Fires once a download has started, during downloading and when finished.
|
|
432
|
+
* This will return you all download percent during the download
|
|
432
433
|
*
|
|
433
434
|
* @since 2.0.11
|
|
434
435
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAAA;;;;GAIG","sourcesContent":["/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n/// <reference types=\"@capacitor/cli\" />\n\nimport type { PluginListenerHandle } from '@capacitor/core';\n\ndeclare module '@capacitor/cli' {\n export interface PluginsConfig {\n /**\n * CapacitorUpdater can be configured with these options:\n */\n CapacitorUpdater?: {\n /**\n * Configure the number of milliseconds the native plugin should wait before considering an update 'failed'.\n *\n * Only available for Android and iOS.\n *\n * @default 10000 // (10 seconds)\n * @example 1000 // (1 second)\n */\n appReadyTimeout?: number;\n /**\n * Configure the number of milliseconds the native plugin should wait before considering API timeout.\n *\n * Only available for Android and iOS.\n *\n * @default 20 // (20 second)\n * @example 10 // (10 second)\n */\n responseTimeout?: number;\n /**\n * Configure whether the plugin should use automatically delete failed bundles.\n *\n * Only available for Android and iOS.\n *\n * @default true\n * @example false\n */\n autoDeleteFailed?: boolean;\n\n /**\n * Configure whether the plugin should use automatically delete previous bundles after a successful update.\n *\n * Only available for Android and iOS.\n *\n * @default true\n * @example false\n */\n autoDeletePrevious?: boolean;\n\n /**\n * Configure whether the plugin should use Auto Update via an update server.\n *\n * Only available for Android and iOS.\n *\n * @default true\n * @example false\n */\n autoUpdate?: boolean;\n\n /**\n * Automatically delete previous downloaded bundles when a newer native app bundle is installed to the device.\n *\n * Only available for Android and iOS.\n *\n * @default true\n * @example false\n */\n resetWhenUpdate?: boolean;\n\n /**\n * Configure the URL / endpoint to which update checks are sent.\n *\n * Only available for Android and iOS.\n *\n * @default https://plugin.capgo.app/updates\n * @example https://example.com/api/auto_update\n */\n updateUrl?: string;\n\n /**\n * Configure the URL / endpoint for channel operations.\n *\n * Only available for Android and iOS.\n *\n * @default https://plugin.capgo.app/channel_self\n * @example https://example.com/api/channel\n */\n channelUrl?: string;\n\n /**\n * Configure the URL / endpoint to which update statistics are sent.\n *\n * Only available for Android and iOS. Set to \"\" to disable stats reporting.\n *\n * @default https://plugin.capgo.app/stats\n * @example https://example.com/api/stats\n */\n statsUrl?: string;\n /**\n * Configure the public key for end to end live update encryption Version 2\n *\n * Only available for Android and iOS.\n *\n * @default undefined\n * @since 6.2.0\n */\n publicKey?: string;\n\n /**\n * Configure the current version of the app. This will be used for the first update request.\n * If not set, the plugin will get the version from the native code.\n *\n * Only available for Android and iOS.\n *\n * @default undefined\n * @since 4.17.48\n */\n version?: string;\n /**\n * Make the plugin direct install the update when the app what just updated/installed. Only for autoUpdate mode.\n *\n * Only available for Android and iOS.\n *\n * @default undefined\n * @since 5.1.0\n */\n directUpdate?: boolean;\n\n /**\n * Configure the delay period for period update check. the unit is in seconds.\n *\n * Only available for Android and iOS.\n * Cannot be less than 600 seconds (10 minutes).\n *\n * @default 600 // (10 minutes)\n */\n periodCheckDelay?: number;\n\n /**\n * Configure the CLI to use a local server for testing or self-hosted update server.\n *\n *\n * @default undefined\n * @since 4.17.48\n */\n localS3?: boolean;\n /**\n * Configure the CLI to use a local server for testing or self-hosted update server.\n *\n *\n * @default undefined\n * @since 4.17.48\n */\n localHost?: string;\n /**\n * Configure the CLI to use a local server for testing or self-hosted update server.\n *\n *\n * @default undefined\n * @since 4.17.48\n */\n localWebHost?: string;\n /**\n * Configure the CLI to use a local server for testing or self-hosted update server.\n *\n *\n * @default undefined\n * @since 4.17.48\n */\n localSupa?: string;\n /**\n * Configure the CLI to use a local server for testing.\n *\n *\n * @default undefined\n * @since 4.17.48\n */\n localSupaAnon?: string;\n /**\n * Configure the CLI to use a local api for testing.\n *\n *\n * @default undefined\n * @since 6.3.3\n */\n localApi?: string;\n /**\n * Configure the CLI to use a local file api for testing.\n *\n *\n * @default undefined\n * @since 6.3.3\n */\n localApiFiles?: string;\n /**\n * Allow the plugin to modify the updateUrl, statsUrl and channelUrl dynamically from the JavaScript side.\n *\n *\n * @default false\n * @since 5.4.0\n */\n allowModifyUrl?: boolean;\n\n /**\n * Set the default channel for the app in the config. Case sensitive.\n * This will setting will override the default channel set in the cloud, but will still respect overrides made in the cloud.\n *\n *\n * @default undefined\n * @since 5.5.0\n */\n defaultChannel?: string;\n /**\n * Configure the app id for the app in the config.\n *\n * @default undefined\n * @since 6.0.0\n */\n appId?: string;\n\n /**\n * Configure the plugin to keep the URL path after a reload.\n * WARNING: When a reload is triggered, 'window.history' will be cleared.\n *\n * @default false\n * @since 6.8.0\n */\n keepUrlPathAfterReload?: boolean;\n };\n }\n}\n\nexport interface CapacitorUpdaterPlugin {\n /**\n * Notify Capacitor Updater that the current bundle is working (a rollback will occur if this method is not called on every app launch)\n * By default this method should be called in the first 10 sec after app launch, otherwise a rollback will occur.\n * Change this behaviour with {@link appReadyTimeout}\n *\n * @returns {Promise<AppReadyResult>} an Promise resolved directly\n * @throws {Error}\n */\n notifyAppReady(): Promise<AppReadyResult>;\n\n /**\n * Set the updateUrl for the app, this will be used to check for updates.\n *\n * @param options contains the URL to use for checking for updates.\n * @returns {Promise<void>}\n * @throws {Error}\n * @since 5.4.0\n */\n setUpdateUrl(options: UpdateUrl): Promise<void>;\n\n /**\n * Set the statsUrl for the app, this will be used to send statistics. Passing an empty string will disable statistics gathering.\n *\n * @param options contains the URL to use for sending statistics.\n * @returns {Promise<void>}\n * @throws {Error}\n * @since 5.4.0\n */\n setStatsUrl(options: StatsUrl): Promise<void>;\n\n /**\n * Set the channelUrl for the app, this will be used to set the channel.\n *\n * @param options contains the URL to use for setting the channel.\n * @returns {Promise<void>}\n * @throws {Error}\n * @since 5.4.0\n */\n setChannelUrl(options: ChannelUrl): Promise<void>;\n\n /**\n * Download a new bundle from the provided URL, it should be a zip file, with files inside or with a unique id inside with all your files\n *\n * @example const bundle = await CapacitorUpdater.download({ url: `https://example.com/versions/${version}/dist.zip`, version });\n * @returns {Promise<BundleInfo>} The {@link BundleInfo} for the specified bundle.\n * @param options The {@link DownloadOptions} for downloading a new bundle zip.\n */\n download(options: DownloadOptions): Promise<BundleInfo>;\n\n /**\n * Set the next bundle to be used when the app is reloaded.\n *\n * @param options Contains the ID of the next Bundle to set on next app launch. {@link BundleInfo.id}\n * @returns {Promise<BundleInfo>} The {@link BundleInfo} for the specified bundle id.\n * @throws {Error} When there is no index.html file inside the bundle folder.\n */\n next(options: BundleId): Promise<BundleInfo>;\n\n /**\n * Set the current bundle and immediately reloads the app.\n *\n * @param options A {@link BundleId} object containing the new bundle id to set as current.\n * @returns {Promise<void>}\n * @throws {Error} When there are is no index.html file inside the bundle folder.\n */\n set(options: BundleId): Promise<void>;\n\n /**\n * Deletes the specified bundle from the native app storage. Use with {@link list} to get the stored Bundle IDs.\n *\n * @param options A {@link BundleId} object containing the ID of a bundle to delete (note, this is the bundle id, NOT the version name)\n * @returns {Promise<void>} When the bundle is deleted\n * @throws {Error}\n */\n delete(options: BundleId): Promise<void>;\n\n /**\n * Get all locally downloaded bundles in your app\n *\n * @returns {Promise<BundleListResult>} A Promise containing the {@link BundleListResult.bundles}\n * @param options The {@link ListOptions} for listing bundles\n * @throws {Error}\n */\n list(options?: ListOptions): Promise<BundleListResult>;\n\n /**\n * Reset the app to the `builtin` bundle (the one sent to Apple App Store / Google Play Store ) or the last successfully loaded bundle.\n *\n * @param options Containing {@link ResetOptions.toLastSuccessful}, `true` resets to the builtin bundle and `false` will reset to the last successfully loaded bundle.\n * @returns {Promise<void>}\n * @throws {Error}\n */\n reset(options?: ResetOptions): Promise<void>;\n\n /**\n * Get the current bundle, if none are set it returns `builtin`. currentNative is the original bundle installed on the device\n *\n * @returns {Promise<CurrentBundleResult>} A Promise evaluating to the {@link CurrentBundleResult}\n * @throws {Error}\n */\n current(): Promise<CurrentBundleResult>;\n\n /**\n * Reload the view\n *\n * @returns {Promise<void>} A Promise which is resolved when the view is reloaded\n * @throws {Error}\n */\n reload(): Promise<void>;\n\n /**\n * Sets a {@link DelayCondition} array containing conditions that the Plugin will use to delay the update.\n * After all conditions are met, the update process will run start again as usual, so update will be installed after a backgrounding or killing the app.\n * For the `date` kind, the value should be an iso8601 date string.\n * For the `background` kind, the value should be a number in milliseconds.\n * For the `nativeVersion` kind, the value should be the version number.\n * For the `kill` kind, the value is not used.\n * The function has unconsistent behavior the option kill do trigger the update after the first kill and not after the next background like other options. This will be fixed in a future major release.\n *\n * @example\n * // Delay the update after the user kills the app or after a background of 300000 ms (5 minutes)\n * await CapacitorUpdater.setMultiDelay({ delayConditions: [{ kind: 'kill' }, { kind: 'background', value: '300000' }] })\n * @example\n * // Delay the update after the specific iso8601 date is expired\n * await CapacitorUpdater.setMultiDelay({ delayConditions: [{ kind: 'date', value: '2022-09-14T06:14:11.920Z' }] })\n * @example\n * // Delay the update after the first background (default behaviour without setting delay)\n * await CapacitorUpdater.setMultiDelay({ delayConditions: [{ kind: 'background' }] })\n * @param options Containing the {@link MultiDelayConditions} array of conditions to set\n * @returns {Promise<void>}\n * @throws {Error}\n * @since 4.3.0\n */\n setMultiDelay(options: MultiDelayConditions): Promise<void>;\n\n /**\n * Cancels a {@link DelayCondition} to process an update immediately.\n *\n * @returns {Promise<void>}\n * @throws {Error}\n * @since 4.0.0\n */\n cancelDelay(): Promise<void>;\n\n /**\n * Get Latest bundle available from update Url\n *\n * @returns {Promise<LatestVersion>} A Promise resolved when url is loaded\n * @throws {Error}\n * @since 4.0.0\n */\n getLatest(options?: GetLatestOptions): Promise<LatestVersion>;\n\n /**\n * Sets the channel for this device. The channel has to allow for self assignment for this to work.\n * Do not use this method to set the channel at boot.\n * This method is to set the channel after the app is ready, and user interacted.\n * If you want to set the channel at boot, use the {@link PluginsConfig} to set the default channel.\n * This methods send to Capgo backend a request to link the device ID to the channel. Capgo can accept or refuse depending of the setting of your channel.\n *\n *\n *\n * @param options Is the {@link SetChannelOptions} channel to set\n * @returns {Promise<ChannelRes>} A Promise which is resolved when the new channel is set\n * @throws {Error}\n * @since 4.7.0\n */\n setChannel(options: SetChannelOptions): Promise<ChannelRes>;\n\n /**\n * Unset the channel for this device. The device will then return to the default channel\n *\n * @returns {Promise<ChannelRes>} A Promise resolved when channel is set\n * @throws {Error}\n * @since 4.7.0\n */\n unsetChannel(options: UnsetChannelOptions): Promise<void>;\n\n /**\n * Get the channel for this device\n *\n * @returns {Promise<ChannelRes>} A Promise that resolves with the channel info\n * @throws {Error}\n * @since 4.8.0\n */\n getChannel(): Promise<GetChannelRes>;\n\n /**\n * Set a custom ID for this device\n *\n * @param options is the {@link SetCustomIdOptions} customId to set\n * @returns {Promise<void>} an Promise resolved instantly\n * @throws {Error}\n * @since 4.9.0\n */\n setCustomId(options: SetCustomIdOptions): Promise<void>;\n\n /**\n * Get the native app version or the builtin version if set in config\n *\n * @returns {Promise<BuiltinVersion>} A Promise with version for this device\n * @since 5.2.0\n */\n getBuiltinVersion(): Promise<BuiltinVersion>;\n\n /**\n * Get unique ID used to identify device (sent to auto update server)\n *\n * @returns {Promise<DeviceId>} A Promise with id for this device\n * @throws {Error}\n */\n getDeviceId(): Promise<DeviceId>;\n\n /**\n * Get the native Capacitor Updater plugin version (sent to auto update server)\n *\n * @returns {Promise<PluginVersion>} A Promise with Plugin version\n * @throws {Error}\n */\n getPluginVersion(): Promise<PluginVersion>;\n\n /**\n * Get the state of auto update config.\n *\n * @returns {Promise<AutoUpdateEnabled>} The status for auto update. Evaluates to `false` in manual mode.\n * @throws {Error}\n */\n isAutoUpdateEnabled(): Promise<AutoUpdateEnabled>;\n\n /**\n * Remove all listeners for this plugin.\n *\n * @since 1.0.0\n */\n removeAllListeners(): Promise<void>;\n\n /**\n * Listen for bundle download event in the App. Fires once a download has started, during downloading and when finished.\n *\n * @since 2.0.11\n */\n addListener(eventName: 'download', listenerFunc: (state: DownloadEvent) => void): Promise<PluginListenerHandle>;\n\n /**\n * Listen for no need to update event, useful when you want force check every time the app is launched\n *\n * @since 4.0.0\n */\n addListener(eventName: 'noNeedUpdate', listenerFunc: (state: NoNeedEvent) => void): Promise<PluginListenerHandle>;\n\n /**\n * Listen for available update event, useful when you want to force check every time the app is launched\n *\n * @since 4.0.0\n */\n addListener(\n eventName: 'updateAvailable',\n listenerFunc: (state: UpdateAvailableEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n /**\n * Listen for downloadComplete events.\n *\n * @since 4.0.0\n */\n addListener(\n eventName: 'downloadComplete',\n listenerFunc: (state: DownloadCompleteEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n /**\n * Listen for Major update event in the App, let you know when major update is blocked by setting disableAutoUpdateBreaking\n *\n * @since 2.3.0\n */\n addListener(\n eventName: 'majorAvailable',\n listenerFunc: (state: MajorAvailableEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n /**\n * Listen for update fail event in the App, let you know when update has fail to install at next app start\n *\n * @since 2.3.0\n */\n addListener(\n eventName: 'updateFailed',\n listenerFunc: (state: UpdateFailedEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n /**\n * Listen for download fail event in the App, let you know when a bundle download has failed\n *\n * @since 4.0.0\n */\n addListener(\n eventName: 'downloadFailed',\n listenerFunc: (state: DownloadFailedEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n /**\n * Listen for reload event in the App, let you know when reload has happened\n *\n * @since 4.3.0\n */\n addListener(eventName: 'appReloaded', listenerFunc: () => void): Promise<PluginListenerHandle>;\n\n /**\n * Listen for app ready event in the App, let you know when app is ready to use\n *\n * @since 5.1.0\n */\n addListener(eventName: 'appReady', listenerFunc: (state: AppReadyEvent) => void): Promise<PluginListenerHandle>;\n\n /**\n * Get if auto update is available (not disabled by serverUrl).\n *\n * @returns {Promise<AutoUpdateAvailable>} The availability status for auto update. Evaluates to `false` when serverUrl is set.\n * @throws {Error}\n */\n isAutoUpdateAvailable(): Promise<AutoUpdateAvailable>;\n\n /**\n * Get the next bundle that will be used when the app reloads.\n * Returns null if no next bundle is set.\n *\n * @returns {Promise<BundleInfo | null>} A Promise that resolves with the next bundle information or null\n * @throws {Error}\n * @since 6.8.0\n */\n getNextBundle(): Promise<BundleInfo | null>;\n}\n\n/**\n * pending: The bundle is pending to be **SET** as the next bundle.\n * downloading: The bundle is being downloaded.\n * success: The bundle has been downloaded and is ready to be **SET** as the next bundle.\n * error: The bundle has failed to download.\n */\nexport type BundleStatus = 'success' | 'error' | 'pending' | 'downloading';\n\nexport type DelayUntilNext = 'background' | 'kill' | 'nativeVersion' | 'date';\n\nexport interface NoNeedEvent {\n /**\n * Current status of download, between 0 and 100.\n *\n * @since 4.0.0\n */\n bundle: BundleInfo;\n}\n\nexport interface UpdateAvailableEvent {\n /**\n * Current status of download, between 0 and 100.\n *\n * @since 4.0.0\n */\n bundle: BundleInfo;\n}\n\nexport interface ChannelRes {\n /**\n * Current status of set channel\n *\n * @since 4.7.0\n */\n status: string;\n error?: string;\n message?: string;\n}\n\nexport interface GetChannelRes {\n /**\n * Current status of get channel\n *\n * @since 4.8.0\n */\n channel?: string;\n error?: string;\n message?: string;\n status?: string;\n allowSet?: boolean;\n}\n\nexport interface DownloadEvent {\n /**\n * Current status of download, between 0 and 100.\n *\n * @since 4.0.0\n */\n percent: number;\n bundle: BundleInfo;\n}\n\nexport interface MajorAvailableEvent {\n /**\n * Emit when a new major bundle is available.\n *\n * @since 4.0.0\n */\n version: string;\n}\n\nexport interface DownloadFailedEvent {\n /**\n * Emit when a download fail.\n *\n * @since 4.0.0\n */\n version: string;\n}\n\nexport interface DownloadCompleteEvent {\n /**\n * Emit when a new update is available.\n *\n * @since 4.0.0\n */\n bundle: BundleInfo;\n}\n\nexport interface UpdateFailedEvent {\n /**\n * Emit when a update failed to install.\n *\n * @since 4.0.0\n */\n bundle: BundleInfo;\n}\n\nexport interface AppReadyEvent {\n /**\n * Emitted when the app is ready to use.\n *\n * @since 5.2.0\n */\n bundle: BundleInfo;\n status: string;\n}\n\nexport interface ManifestEntry {\n file_name: string | null;\n file_hash: string | null;\n download_url: string | null;\n}\n\nexport interface LatestVersion {\n /**\n * Result of getLatest method\n *\n * @since 4.0.0\n */\n version: string;\n /**\n * @since 6\n */\n checksum?: string;\n major?: boolean;\n message?: string;\n sessionKey?: string;\n error?: string;\n old?: string;\n url?: string;\n /**\n * @since 6.1\n */\n manifest?: ManifestEntry[];\n}\n\nexport interface BundleInfo {\n id: string;\n version: string;\n downloaded: string;\n checksum: string;\n status: BundleStatus;\n}\n\nexport interface SetChannelOptions {\n channel: string;\n triggerAutoUpdate?: boolean;\n}\n\nexport interface UnsetChannelOptions {\n triggerAutoUpdate?: boolean;\n}\n\nexport interface SetCustomIdOptions {\n customId: string;\n}\n\nexport interface DelayCondition {\n /**\n * Set up delay conditions in setMultiDelay\n * @param value is useless for @param kind \"kill\", optional for \"background\" (default value: \"0\") and required for \"nativeVersion\" and \"date\"\n */\n kind: DelayUntilNext;\n value?: string;\n}\n\nexport interface GetLatestOptions {\n /**\n * The channel to get the latest version for\n * The channel must allow 'self_assign' for this to work\n * @since 6.8.0\n * @default undefined\n */\n channel?: string;\n}\n\nexport interface AppReadyResult {\n bundle: BundleInfo;\n}\n\nexport interface UpdateUrl {\n url: string;\n}\n\nexport interface StatsUrl {\n url: string;\n}\n\nexport interface ChannelUrl {\n url: string;\n}\n\n/**\n * This URL and versions are used to download the bundle from the server, If you use backend all information will be gived by the method getLatest.\n * If you don't use backend, you need to provide the URL and version of the bundle. Checksum and sessionKey are required if you encrypted the bundle with the CLI command encrypt, you should receive them as result of the command.\n */\nexport interface DownloadOptions {\n /**\n * The URL of the bundle zip file (e.g: dist.zip) to be downloaded. (This can be any URL. E.g: Amazon S3, a GitHub tag, any other place you've hosted your bundle.)\n */\n url: string;\n /**\n * The version code/name of this bundle/version\n */\n version: string;\n /**\n * The session key for the update, when the bundle is encrypted with a session key\n * @since 4.0.0\n * @default undefined\n */\n sessionKey?: string;\n /**\n * The checksum for the update, it should be in sha256 and encrypted with private key if the bundle is encrypted\n * @since 4.0.0\n * @default undefined\n */\n checksum?: string;\n}\n\nexport interface BundleId {\n id: string;\n}\n\nexport interface BundleListResult {\n bundles: BundleInfo[];\n}\n\nexport interface ResetOptions {\n toLastSuccessful: boolean;\n}\n\nexport interface ListOptions {\n /**\n * Whether to return the raw bundle list or the manifest. If true, the list will attempt to read the internal database instead of files on disk.\n * @since 6.14.0\n * @default false\n */\n raw?: boolean;\n}\n\nexport interface CurrentBundleResult {\n bundle: BundleInfo;\n native: string;\n}\n\nexport interface MultiDelayConditions {\n delayConditions: DelayCondition[];\n}\n\nexport interface BuiltinVersion {\n version: string;\n}\n\nexport interface DeviceId {\n deviceId: string;\n}\n\nexport interface PluginVersion {\n version: string;\n}\n\nexport interface AutoUpdateEnabled {\n enabled: boolean;\n}\n\nexport interface AutoUpdateAvailable {\n available: boolean;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAAA;;;;GAIG","sourcesContent":["/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n/// <reference types=\"@capacitor/cli\" />\n\nimport type { PluginListenerHandle } from '@capacitor/core';\n\ndeclare module '@capacitor/cli' {\n export interface PluginsConfig {\n /**\n * CapacitorUpdater can be configured with these options:\n */\n CapacitorUpdater?: {\n /**\n * Configure the number of milliseconds the native plugin should wait before considering an update 'failed'.\n *\n * Only available for Android and iOS.\n *\n * @default 10000 // (10 seconds)\n * @example 1000 // (1 second)\n */\n appReadyTimeout?: number;\n /**\n * Configure the number of milliseconds the native plugin should wait before considering API timeout.\n *\n * Only available for Android and iOS.\n *\n * @default 20 // (20 second)\n * @example 10 // (10 second)\n */\n responseTimeout?: number;\n /**\n * Configure whether the plugin should use automatically delete failed bundles.\n *\n * Only available for Android and iOS.\n *\n * @default true\n * @example false\n */\n autoDeleteFailed?: boolean;\n\n /**\n * Configure whether the plugin should use automatically delete previous bundles after a successful update.\n *\n * Only available for Android and iOS.\n *\n * @default true\n * @example false\n */\n autoDeletePrevious?: boolean;\n\n /**\n * Configure whether the plugin should use Auto Update via an update server.\n *\n * Only available for Android and iOS.\n *\n * @default true\n * @example false\n */\n autoUpdate?: boolean;\n\n /**\n * Automatically delete previous downloaded bundles when a newer native app bundle is installed to the device.\n *\n * Only available for Android and iOS.\n *\n * @default true\n * @example false\n */\n resetWhenUpdate?: boolean;\n\n /**\n * Configure the URL / endpoint to which update checks are sent.\n *\n * Only available for Android and iOS.\n *\n * @default https://plugin.capgo.app/updates\n * @example https://example.com/api/auto_update\n */\n updateUrl?: string;\n\n /**\n * Configure the URL / endpoint for channel operations.\n *\n * Only available for Android and iOS.\n *\n * @default https://plugin.capgo.app/channel_self\n * @example https://example.com/api/channel\n */\n channelUrl?: string;\n\n /**\n * Configure the URL / endpoint to which update statistics are sent.\n *\n * Only available for Android and iOS. Set to \"\" to disable stats reporting.\n *\n * @default https://plugin.capgo.app/stats\n * @example https://example.com/api/stats\n */\n statsUrl?: string;\n /**\n * Configure the public key for end to end live update encryption Version 2\n *\n * Only available for Android and iOS.\n *\n * @default undefined\n * @since 6.2.0\n */\n publicKey?: string;\n\n /**\n * Configure the current version of the app. This will be used for the first update request.\n * If not set, the plugin will get the version from the native code.\n *\n * Only available for Android and iOS.\n *\n * @default undefined\n * @since 4.17.48\n */\n version?: string;\n /**\n * Make the plugin direct install the update when the app what just updated/installed. Only for autoUpdate mode.\n *\n * Only available for Android and iOS.\n *\n * @default undefined\n * @since 5.1.0\n */\n directUpdate?: boolean;\n\n /**\n * Configure the delay period for period update check. the unit is in seconds.\n *\n * Only available for Android and iOS.\n * Cannot be less than 600 seconds (10 minutes).\n *\n * @default 600 // (10 minutes)\n */\n periodCheckDelay?: number;\n\n /**\n * Configure the CLI to use a local server for testing or self-hosted update server.\n *\n *\n * @default undefined\n * @since 4.17.48\n */\n localS3?: boolean;\n /**\n * Configure the CLI to use a local server for testing or self-hosted update server.\n *\n *\n * @default undefined\n * @since 4.17.48\n */\n localHost?: string;\n /**\n * Configure the CLI to use a local server for testing or self-hosted update server.\n *\n *\n * @default undefined\n * @since 4.17.48\n */\n localWebHost?: string;\n /**\n * Configure the CLI to use a local server for testing or self-hosted update server.\n *\n *\n * @default undefined\n * @since 4.17.48\n */\n localSupa?: string;\n /**\n * Configure the CLI to use a local server for testing.\n *\n *\n * @default undefined\n * @since 4.17.48\n */\n localSupaAnon?: string;\n /**\n * Configure the CLI to use a local api for testing.\n *\n *\n * @default undefined\n * @since 6.3.3\n */\n localApi?: string;\n /**\n * Configure the CLI to use a local file api for testing.\n *\n *\n * @default undefined\n * @since 6.3.3\n */\n localApiFiles?: string;\n /**\n * Allow the plugin to modify the updateUrl, statsUrl and channelUrl dynamically from the JavaScript side.\n *\n *\n * @default false\n * @since 5.4.0\n */\n allowModifyUrl?: boolean;\n\n /**\n * Set the default channel for the app in the config. Case sensitive.\n * This will setting will override the default channel set in the cloud, but will still respect overrides made in the cloud.\n *\n *\n * @default undefined\n * @since 5.5.0\n */\n defaultChannel?: string;\n /**\n * Configure the app id for the app in the config.\n *\n * @default undefined\n * @since 6.0.0\n */\n appId?: string;\n\n /**\n * Configure the plugin to keep the URL path after a reload.\n * WARNING: When a reload is triggered, 'window.history' will be cleared.\n *\n * @default false\n * @since 6.8.0\n */\n keepUrlPathAfterReload?: boolean;\n };\n }\n}\n\nexport interface CapacitorUpdaterPlugin {\n /**\n * Notify Capacitor Updater that the current bundle is working (a rollback will occur if this method is not called on every app launch)\n * By default this method should be called in the first 10 sec after app launch, otherwise a rollback will occur.\n * Change this behaviour with {@link appReadyTimeout}\n *\n * @returns {Promise<AppReadyResult>} an Promise resolved directly\n * @throws {Error}\n */\n notifyAppReady(): Promise<AppReadyResult>;\n\n /**\n * Set the updateUrl for the app, this will be used to check for updates.\n *\n * @param options contains the URL to use for checking for updates.\n * @returns {Promise<void>}\n * @throws {Error}\n * @since 5.4.0\n */\n setUpdateUrl(options: UpdateUrl): Promise<void>;\n\n /**\n * Set the statsUrl for the app, this will be used to send statistics. Passing an empty string will disable statistics gathering.\n *\n * @param options contains the URL to use for sending statistics.\n * @returns {Promise<void>}\n * @throws {Error}\n * @since 5.4.0\n */\n setStatsUrl(options: StatsUrl): Promise<void>;\n\n /**\n * Set the channelUrl for the app, this will be used to set the channel.\n *\n * @param options contains the URL to use for setting the channel.\n * @returns {Promise<void>}\n * @throws {Error}\n * @since 5.4.0\n */\n setChannelUrl(options: ChannelUrl): Promise<void>;\n\n /**\n * Download a new bundle from the provided URL, it should be a zip file, with files inside or with a unique id inside with all your files\n *\n * @example const bundle = await CapacitorUpdater.download({ url: `https://example.com/versions/${version}/dist.zip`, version });\n * @returns {Promise<BundleInfo>} The {@link BundleInfo} for the specified bundle.\n * @param options The {@link DownloadOptions} for downloading a new bundle zip.\n */\n download(options: DownloadOptions): Promise<BundleInfo>;\n\n /**\n * Set the next bundle to be used when the app is reloaded.\n *\n * @param options Contains the ID of the next Bundle to set on next app launch. {@link BundleInfo.id}\n * @returns {Promise<BundleInfo>} The {@link BundleInfo} for the specified bundle id.\n * @throws {Error} When there is no index.html file inside the bundle folder.\n */\n next(options: BundleId): Promise<BundleInfo>;\n\n /**\n * Set the current bundle and immediately reloads the app.\n *\n * @param options A {@link BundleId} object containing the new bundle id to set as current.\n * @returns {Promise<void>}\n * @throws {Error} When there are is no index.html file inside the bundle folder.\n */\n set(options: BundleId): Promise<void>;\n\n /**\n * Deletes the specified bundle from the native app storage. Use with {@link list} to get the stored Bundle IDs.\n *\n * @param options A {@link BundleId} object containing the ID of a bundle to delete (note, this is the bundle id, NOT the version name)\n * @returns {Promise<void>} When the bundle is deleted\n * @throws {Error}\n */\n delete(options: BundleId): Promise<void>;\n\n /**\n * Get all locally downloaded bundles in your app\n *\n * @returns {Promise<BundleListResult>} A Promise containing the {@link BundleListResult.bundles}\n * @param options The {@link ListOptions} for listing bundles\n * @throws {Error}\n */\n list(options?: ListOptions): Promise<BundleListResult>;\n\n /**\n * Reset the app to the `builtin` bundle (the one sent to Apple App Store / Google Play Store ) or the last successfully loaded bundle.\n *\n * @param options Containing {@link ResetOptions.toLastSuccessful}, `true` resets to the builtin bundle and `false` will reset to the last successfully loaded bundle.\n * @returns {Promise<void>}\n * @throws {Error}\n */\n reset(options?: ResetOptions): Promise<void>;\n\n /**\n * Get the current bundle, if none are set it returns `builtin`. currentNative is the original bundle installed on the device\n *\n * @returns {Promise<CurrentBundleResult>} A Promise evaluating to the {@link CurrentBundleResult}\n * @throws {Error}\n */\n current(): Promise<CurrentBundleResult>;\n\n /**\n * Reload the view\n *\n * @returns {Promise<void>} A Promise which is resolved when the view is reloaded\n * @throws {Error}\n */\n reload(): Promise<void>;\n\n /**\n * Sets a {@link DelayCondition} array containing conditions that the Plugin will use to delay the update.\n * After all conditions are met, the update process will run start again as usual, so update will be installed after a backgrounding or killing the app.\n * For the `date` kind, the value should be an iso8601 date string.\n * For the `background` kind, the value should be a number in milliseconds.\n * For the `nativeVersion` kind, the value should be the version number.\n * For the `kill` kind, the value is not used.\n * The function has unconsistent behavior the option kill do trigger the update after the first kill and not after the next background like other options. This will be fixed in a future major release.\n *\n * @example\n * // Delay the update after the user kills the app or after a background of 300000 ms (5 minutes)\n * await CapacitorUpdater.setMultiDelay({ delayConditions: [{ kind: 'kill' }, { kind: 'background', value: '300000' }] })\n * @example\n * // Delay the update after the specific iso8601 date is expired\n * await CapacitorUpdater.setMultiDelay({ delayConditions: [{ kind: 'date', value: '2022-09-14T06:14:11.920Z' }] })\n * @example\n * // Delay the update after the first background (default behaviour without setting delay)\n * await CapacitorUpdater.setMultiDelay({ delayConditions: [{ kind: 'background' }] })\n * @param options Containing the {@link MultiDelayConditions} array of conditions to set\n * @returns {Promise<void>}\n * @throws {Error}\n * @since 4.3.0\n */\n setMultiDelay(options: MultiDelayConditions): Promise<void>;\n\n /**\n * Cancels a {@link DelayCondition} to process an update immediately.\n *\n * @returns {Promise<void>}\n * @throws {Error}\n * @since 4.0.0\n */\n cancelDelay(): Promise<void>;\n\n /**\n * Get Latest bundle available from update Url\n *\n * @returns {Promise<LatestVersion>} A Promise resolved when url is loaded\n * @throws {Error}\n * @since 4.0.0\n */\n getLatest(options?: GetLatestOptions): Promise<LatestVersion>;\n\n /**\n * Sets the channel for this device. The channel has to allow for self assignment for this to work.\n * Do not use this method to set the channel at boot.\n * This method is to set the channel after the app is ready, and user interacted.\n * If you want to set the channel at boot, use the {@link PluginsConfig} to set the default channel.\n * This methods send to Capgo backend a request to link the device ID to the channel. Capgo can accept or refuse depending of the setting of your channel.\n *\n *\n *\n * @param options Is the {@link SetChannelOptions} channel to set\n * @returns {Promise<ChannelRes>} A Promise which is resolved when the new channel is set\n * @throws {Error}\n * @since 4.7.0\n */\n setChannel(options: SetChannelOptions): Promise<ChannelRes>;\n\n /**\n * Unset the channel for this device. The device will then return to the default channel\n *\n * @returns {Promise<ChannelRes>} A Promise resolved when channel is set\n * @throws {Error}\n * @since 4.7.0\n */\n unsetChannel(options: UnsetChannelOptions): Promise<void>;\n\n /**\n * Get the channel for this device\n *\n * @returns {Promise<ChannelRes>} A Promise that resolves with the channel info\n * @throws {Error}\n * @since 4.8.0\n */\n getChannel(): Promise<GetChannelRes>;\n\n /**\n * Set a custom ID for this device\n *\n * @param options is the {@link SetCustomIdOptions} customId to set\n * @returns {Promise<void>} an Promise resolved instantly\n * @throws {Error}\n * @since 4.9.0\n */\n setCustomId(options: SetCustomIdOptions): Promise<void>;\n\n /**\n * Get the native app version or the builtin version if set in config\n *\n * @returns {Promise<BuiltinVersion>} A Promise with version for this device\n * @since 5.2.0\n */\n getBuiltinVersion(): Promise<BuiltinVersion>;\n\n /**\n * Get unique ID used to identify device (sent to auto update server)\n *\n * @returns {Promise<DeviceId>} A Promise with id for this device\n * @throws {Error}\n */\n getDeviceId(): Promise<DeviceId>;\n\n /**\n * Get the native Capacitor Updater plugin version (sent to auto update server)\n *\n * @returns {Promise<PluginVersion>} A Promise with Plugin version\n * @throws {Error}\n */\n getPluginVersion(): Promise<PluginVersion>;\n\n /**\n * Get the state of auto update config.\n *\n * @returns {Promise<AutoUpdateEnabled>} The status for auto update. Evaluates to `false` in manual mode.\n * @throws {Error}\n */\n isAutoUpdateEnabled(): Promise<AutoUpdateEnabled>;\n\n /**\n * Remove all listeners for this plugin.\n *\n * @since 1.0.0\n */\n removeAllListeners(): Promise<void>;\n\n /**\n * Listen for bundle download event in the App. Fires once a download has started, during downloading and when finished.\n * This will return you all download percent during the download\n *\n * @since 2.0.11\n */\n addListener(eventName: 'download', listenerFunc: (state: DownloadEvent) => void): Promise<PluginListenerHandle>;\n\n /**\n * Listen for no need to update event, useful when you want force check every time the app is launched\n *\n * @since 4.0.0\n */\n addListener(eventName: 'noNeedUpdate', listenerFunc: (state: NoNeedEvent) => void): Promise<PluginListenerHandle>;\n\n /**\n * Listen for available update event, useful when you want to force check every time the app is launched\n *\n * @since 4.0.0\n */\n addListener(\n eventName: 'updateAvailable',\n listenerFunc: (state: UpdateAvailableEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n /**\n * Listen for downloadComplete events.\n *\n * @since 4.0.0\n */\n addListener(\n eventName: 'downloadComplete',\n listenerFunc: (state: DownloadCompleteEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n /**\n * Listen for Major update event in the App, let you know when major update is blocked by setting disableAutoUpdateBreaking\n *\n * @since 2.3.0\n */\n addListener(\n eventName: 'majorAvailable',\n listenerFunc: (state: MajorAvailableEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n /**\n * Listen for update fail event in the App, let you know when update has fail to install at next app start\n *\n * @since 2.3.0\n */\n addListener(\n eventName: 'updateFailed',\n listenerFunc: (state: UpdateFailedEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n /**\n * Listen for download fail event in the App, let you know when a bundle download has failed\n *\n * @since 4.0.0\n */\n addListener(\n eventName: 'downloadFailed',\n listenerFunc: (state: DownloadFailedEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n /**\n * Listen for reload event in the App, let you know when reload has happened\n *\n * @since 4.3.0\n */\n addListener(eventName: 'appReloaded', listenerFunc: () => void): Promise<PluginListenerHandle>;\n\n /**\n * Listen for app ready event in the App, let you know when app is ready to use\n *\n * @since 5.1.0\n */\n addListener(eventName: 'appReady', listenerFunc: (state: AppReadyEvent) => void): Promise<PluginListenerHandle>;\n\n /**\n * Get if auto update is available (not disabled by serverUrl).\n *\n * @returns {Promise<AutoUpdateAvailable>} The availability status for auto update. Evaluates to `false` when serverUrl is set.\n * @throws {Error}\n */\n isAutoUpdateAvailable(): Promise<AutoUpdateAvailable>;\n\n /**\n * Get the next bundle that will be used when the app reloads.\n * Returns null if no next bundle is set.\n *\n * @returns {Promise<BundleInfo | null>} A Promise that resolves with the next bundle information or null\n * @throws {Error}\n * @since 6.8.0\n */\n getNextBundle(): Promise<BundleInfo | null>;\n}\n\n/**\n * pending: The bundle is pending to be **SET** as the next bundle.\n * downloading: The bundle is being downloaded.\n * success: The bundle has been downloaded and is ready to be **SET** as the next bundle.\n * error: The bundle has failed to download.\n */\nexport type BundleStatus = 'success' | 'error' | 'pending' | 'downloading';\n\nexport type DelayUntilNext = 'background' | 'kill' | 'nativeVersion' | 'date';\n\nexport interface NoNeedEvent {\n /**\n * Current status of download, between 0 and 100.\n *\n * @since 4.0.0\n */\n bundle: BundleInfo;\n}\n\nexport interface UpdateAvailableEvent {\n /**\n * Current status of download, between 0 and 100.\n *\n * @since 4.0.0\n */\n bundle: BundleInfo;\n}\n\nexport interface ChannelRes {\n /**\n * Current status of set channel\n *\n * @since 4.7.0\n */\n status: string;\n error?: string;\n message?: string;\n}\n\nexport interface GetChannelRes {\n /**\n * Current status of get channel\n *\n * @since 4.8.0\n */\n channel?: string;\n error?: string;\n message?: string;\n status?: string;\n allowSet?: boolean;\n}\n\nexport interface DownloadEvent {\n /**\n * Current status of download, between 0 and 100.\n *\n * @since 4.0.0\n */\n percent: number;\n bundle: BundleInfo;\n}\n\nexport interface MajorAvailableEvent {\n /**\n * Emit when a new major bundle is available.\n *\n * @since 4.0.0\n */\n version: string;\n}\n\nexport interface DownloadFailedEvent {\n /**\n * Emit when a download fail.\n *\n * @since 4.0.0\n */\n version: string;\n}\n\nexport interface DownloadCompleteEvent {\n /**\n * Emit when a new update is available.\n *\n * @since 4.0.0\n */\n bundle: BundleInfo;\n}\n\nexport interface UpdateFailedEvent {\n /**\n * Emit when a update failed to install.\n *\n * @since 4.0.0\n */\n bundle: BundleInfo;\n}\n\nexport interface AppReadyEvent {\n /**\n * Emitted when the app is ready to use.\n *\n * @since 5.2.0\n */\n bundle: BundleInfo;\n status: string;\n}\n\nexport interface ManifestEntry {\n file_name: string | null;\n file_hash: string | null;\n download_url: string | null;\n}\n\nexport interface LatestVersion {\n /**\n * Result of getLatest method\n *\n * @since 4.0.0\n */\n version: string;\n /**\n * @since 6\n */\n checksum?: string;\n major?: boolean;\n message?: string;\n sessionKey?: string;\n error?: string;\n old?: string;\n url?: string;\n /**\n * @since 6.1\n */\n manifest?: ManifestEntry[];\n}\n\nexport interface BundleInfo {\n id: string;\n version: string;\n downloaded: string;\n checksum: string;\n status: BundleStatus;\n}\n\nexport interface SetChannelOptions {\n channel: string;\n triggerAutoUpdate?: boolean;\n}\n\nexport interface UnsetChannelOptions {\n triggerAutoUpdate?: boolean;\n}\n\nexport interface SetCustomIdOptions {\n customId: string;\n}\n\nexport interface DelayCondition {\n /**\n * Set up delay conditions in setMultiDelay\n * @param value is useless for @param kind \"kill\", optional for \"background\" (default value: \"0\") and required for \"nativeVersion\" and \"date\"\n */\n kind: DelayUntilNext;\n value?: string;\n}\n\nexport interface GetLatestOptions {\n /**\n * The channel to get the latest version for\n * The channel must allow 'self_assign' for this to work\n * @since 6.8.0\n * @default undefined\n */\n channel?: string;\n}\n\nexport interface AppReadyResult {\n bundle: BundleInfo;\n}\n\nexport interface UpdateUrl {\n url: string;\n}\n\nexport interface StatsUrl {\n url: string;\n}\n\nexport interface ChannelUrl {\n url: string;\n}\n\n/**\n * This URL and versions are used to download the bundle from the server, If you use backend all information will be gived by the method getLatest.\n * If you don't use backend, you need to provide the URL and version of the bundle. Checksum and sessionKey are required if you encrypted the bundle with the CLI command encrypt, you should receive them as result of the command.\n */\nexport interface DownloadOptions {\n /**\n * The URL of the bundle zip file (e.g: dist.zip) to be downloaded. (This can be any URL. E.g: Amazon S3, a GitHub tag, any other place you've hosted your bundle.)\n */\n url: string;\n /**\n * The version code/name of this bundle/version\n */\n version: string;\n /**\n * The session key for the update, when the bundle is encrypted with a session key\n * @since 4.0.0\n * @default undefined\n */\n sessionKey?: string;\n /**\n * The checksum for the update, it should be in sha256 and encrypted with private key if the bundle is encrypted\n * @since 4.0.0\n * @default undefined\n */\n checksum?: string;\n}\n\nexport interface BundleId {\n id: string;\n}\n\nexport interface BundleListResult {\n bundles: BundleInfo[];\n}\n\nexport interface ResetOptions {\n toLastSuccessful: boolean;\n}\n\nexport interface ListOptions {\n /**\n * Whether to return the raw bundle list or the manifest. If true, the list will attempt to read the internal database instead of files on disk.\n * @since 6.14.0\n * @default false\n */\n raw?: boolean;\n}\n\nexport interface CurrentBundleResult {\n bundle: BundleInfo;\n native: string;\n}\n\nexport interface MultiDelayConditions {\n delayConditions: DelayCondition[];\n}\n\nexport interface BuiltinVersion {\n version: string;\n}\n\nexport interface DeviceId {\n deviceId: string;\n}\n\nexport interface PluginVersion {\n version: string;\n}\n\nexport interface AutoUpdateEnabled {\n enabled: boolean;\n}\n\nexport interface AutoUpdateAvailable {\n available: boolean;\n}\n"]}
|
|
@@ -45,11 +45,11 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
45
45
|
CAPPluginMethod(name: "getNextBundle", returnType: CAPPluginReturnPromise)
|
|
46
46
|
]
|
|
47
47
|
public var implementation = CapacitorUpdater()
|
|
48
|
-
private let PLUGIN_VERSION: String = "7.2.
|
|
48
|
+
private let PLUGIN_VERSION: String = "7.2.17"
|
|
49
49
|
static let updateUrlDefault = "https://plugin.capgo.app/updates"
|
|
50
50
|
static let statsUrlDefault = "https://plugin.capgo.app/stats"
|
|
51
51
|
static let channelUrlDefault = "https://plugin.capgo.app/channel_self"
|
|
52
|
-
|
|
52
|
+
// Note: DELAY_CONDITION_PREFERENCES is now defined in DelayUpdateUtils.DELAY_CONDITION_PREFERENCES
|
|
53
53
|
private var updateUrl = ""
|
|
54
54
|
private var statsUrl = ""
|
|
55
55
|
private var backgroundTaskID: UIBackgroundTaskIdentifier = UIBackgroundTaskIdentifier.invalid
|
|
@@ -67,6 +67,8 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
67
67
|
private var periodCheckDelay = 0
|
|
68
68
|
let semaphoreReady = DispatchSemaphore(value: 0)
|
|
69
69
|
|
|
70
|
+
private var delayUpdateUtils: DelayUpdateUtils!
|
|
71
|
+
|
|
70
72
|
override public func load() {
|
|
71
73
|
#if targetEnvironment(simulator)
|
|
72
74
|
print("\(CapacitorUpdater.TAG) ::::: SIMULATOR :::::")
|
|
@@ -109,6 +111,11 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
109
111
|
implementation.publicKey = getConfig().getString("publicKey", "")!
|
|
110
112
|
implementation.notifyDownloadRaw = notifyDownload
|
|
111
113
|
implementation.PLUGIN_VERSION = self.PLUGIN_VERSION
|
|
114
|
+
|
|
115
|
+
// Initialize DelayUpdateUtils
|
|
116
|
+
self.delayUpdateUtils = DelayUpdateUtils(currentVersionNative: currentVersionNative, installNext: { [weak self] in
|
|
117
|
+
self?.installNext()
|
|
118
|
+
})
|
|
112
119
|
let config = (self.bridge?.viewController as? CAPBridgeViewController)?.instanceDescriptor().legacyConfig
|
|
113
120
|
implementation.appId = Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String ?? ""
|
|
114
121
|
implementation.appId = config?["appId"] as? String ?? implementation.appId
|
|
@@ -291,6 +298,18 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
291
298
|
DispatchQueue.global(qos: .background).async {
|
|
292
299
|
do {
|
|
293
300
|
let next = try self.implementation.download(url: url!, version: version, sessionKey: sessionKey)
|
|
301
|
+
// If public key is present but no checksum provided, refuse installation
|
|
302
|
+
if self.implementation.publicKey != "" && checksum == "" {
|
|
303
|
+
print("\(CapacitorUpdater.TAG) Public key present but no checksum provided")
|
|
304
|
+
self.implementation.sendStats(action: "checksum_required", versionName: next.getVersionName())
|
|
305
|
+
let id = next.getId()
|
|
306
|
+
let resDel = self.implementation.delete(id: id)
|
|
307
|
+
if !resDel {
|
|
308
|
+
print("\(CapacitorUpdater.TAG) Delete failed, id \(id) doesn't exist")
|
|
309
|
+
}
|
|
310
|
+
throw ObjectSavableError.checksum
|
|
311
|
+
}
|
|
312
|
+
|
|
294
313
|
checksum = try CryptoCipherV2.decryptChecksum(checksum: checksum, publicKey: self.implementation.publicKey)
|
|
295
314
|
if (checksum != "" || self.implementation.publicKey != "") && next.getChecksum() != checksum {
|
|
296
315
|
print("\(CapacitorUpdater.TAG) Error checksum", next.getChecksum(), checksum)
|
|
@@ -549,96 +568,45 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
549
568
|
call.reject("setMultiDelay called without delayCondition")
|
|
550
569
|
return
|
|
551
570
|
}
|
|
552
|
-
let delayConditions: String = toJson(object: delayConditionList)
|
|
553
|
-
if _setMultiDelay(delayConditions: delayConditions) {
|
|
554
|
-
call.resolve()
|
|
555
|
-
} else {
|
|
556
|
-
call.reject("Failed to delay update")
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
571
|
|
|
560
|
-
|
|
561
|
-
if
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
572
|
+
// Handle background conditions with empty value (set to "0")
|
|
573
|
+
if var modifiableList = delayConditionList as? [[String: Any]] {
|
|
574
|
+
for i in 0..<modifiableList.count {
|
|
575
|
+
if let kind = modifiableList[i]["kind"] as? String,
|
|
576
|
+
kind == "background",
|
|
577
|
+
let value = modifiableList[i]["value"] as? String,
|
|
578
|
+
value.isEmpty {
|
|
579
|
+
modifiableList[i]["value"] = "0"
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
let delayConditions: String = toJson(object: modifiableList)
|
|
583
|
+
if delayUpdateUtils.setMultiDelay(delayConditions: delayConditions) {
|
|
584
|
+
call.resolve()
|
|
585
|
+
} else {
|
|
586
|
+
call.reject("Failed to delay update")
|
|
587
|
+
}
|
|
566
588
|
} else {
|
|
567
|
-
|
|
568
|
-
|
|
589
|
+
let delayConditions: String = toJson(object: delayConditionList)
|
|
590
|
+
if delayUpdateUtils.setMultiDelay(delayConditions: delayConditions) {
|
|
591
|
+
call.resolve()
|
|
592
|
+
} else {
|
|
593
|
+
call.reject("Failed to delay update")
|
|
594
|
+
}
|
|
569
595
|
}
|
|
570
596
|
}
|
|
571
597
|
|
|
572
|
-
|
|
573
|
-
print("\(CapacitorUpdater.TAG) delay Canceled from \(source)")
|
|
574
|
-
UserDefaults.standard.removeObject(forKey: DELAY_CONDITION_PREFERENCES)
|
|
575
|
-
UserDefaults.standard.synchronize()
|
|
576
|
-
}
|
|
598
|
+
// Note: _setMultiDelay and _cancelDelay methods have been moved to DelayUpdateUtils class
|
|
577
599
|
|
|
578
600
|
@objc func cancelDelay(_ call: CAPPluginCall) {
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
private func _checkCancelDelay(killed: Bool) {
|
|
584
|
-
let delayUpdatePreferences = UserDefaults.standard.string(forKey: DELAY_CONDITION_PREFERENCES) ?? "[]"
|
|
585
|
-
let delayConditionList: [DelayCondition] = fromJsonArr(json: delayUpdatePreferences).map { obj -> DelayCondition in
|
|
586
|
-
let kind: String = obj.value(forKey: "kind") as! String
|
|
587
|
-
let value: String? = obj.value(forKey: "value") as? String
|
|
588
|
-
return DelayCondition(kind: kind, value: value)
|
|
589
|
-
}
|
|
590
|
-
for condition in delayConditionList {
|
|
591
|
-
let kind: String? = condition.getKind()
|
|
592
|
-
let value: String? = condition.getValue()
|
|
593
|
-
if kind != nil {
|
|
594
|
-
switch kind {
|
|
595
|
-
case "background":
|
|
596
|
-
if !killed {
|
|
597
|
-
self._cancelDelay(source: "background check")
|
|
598
|
-
}
|
|
599
|
-
case "kill":
|
|
600
|
-
if killed {
|
|
601
|
-
self._cancelDelay(source: "kill check")
|
|
602
|
-
// instant install for kill action
|
|
603
|
-
self.installNext()
|
|
604
|
-
}
|
|
605
|
-
case "date":
|
|
606
|
-
if value != nil && value != "" {
|
|
607
|
-
let dateFormatter = ISO8601DateFormatter()
|
|
608
|
-
dateFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
|
|
609
|
-
guard let ExpireDate = dateFormatter.date(from: value!) else {
|
|
610
|
-
self._cancelDelay(source: "date parsing issue")
|
|
611
|
-
return
|
|
612
|
-
}
|
|
613
|
-
if ExpireDate < Date() {
|
|
614
|
-
self._cancelDelay(source: "date expired")
|
|
615
|
-
}
|
|
616
|
-
} else {
|
|
617
|
-
self._cancelDelay(source: "delayVal absent")
|
|
618
|
-
}
|
|
619
|
-
case "nativeVersion":
|
|
620
|
-
if value != nil && value != "" {
|
|
621
|
-
do {
|
|
622
|
-
let versionLimit = try Version(value!)
|
|
623
|
-
if self.currentVersionNative >= versionLimit {
|
|
624
|
-
self._cancelDelay(source: "nativeVersion above limit")
|
|
625
|
-
}
|
|
626
|
-
} catch {
|
|
627
|
-
self._cancelDelay(source: "nativeVersion parsing issue")
|
|
628
|
-
}
|
|
629
|
-
} else {
|
|
630
|
-
self._cancelDelay(source: "delayVal absent")
|
|
631
|
-
}
|
|
632
|
-
case .none:
|
|
633
|
-
print("\(CapacitorUpdater.TAG) _checkCancelDelay switch case none error")
|
|
634
|
-
case .some:
|
|
635
|
-
print("\(CapacitorUpdater.TAG) _checkCancelDelay switch case some error")
|
|
636
|
-
}
|
|
637
|
-
}
|
|
601
|
+
if delayUpdateUtils.cancelDelay(source: "JS") {
|
|
602
|
+
call.resolve()
|
|
603
|
+
} else {
|
|
604
|
+
call.reject("Failed to cancel delay")
|
|
638
605
|
}
|
|
639
|
-
// self.checkAppReady() why this here?
|
|
640
606
|
}
|
|
641
607
|
|
|
608
|
+
// Note: _checkCancelDelay method has been moved to DelayUpdateUtils class
|
|
609
|
+
|
|
642
610
|
private func _isAutoUpdateEnabled() -> Bool {
|
|
643
611
|
let instanceDescriptor = (self.bridge?.viewController as? CAPBridgeViewController)?.instanceDescriptor()
|
|
644
612
|
if instanceDescriptor?.serverURL != nil {
|
|
@@ -818,10 +786,20 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
818
786
|
return
|
|
819
787
|
}
|
|
820
788
|
if self.directUpdate {
|
|
789
|
+
let delayUpdatePreferences = UserDefaults.standard.string(forKey: DelayUpdateUtils.DELAY_CONDITION_PREFERENCES) ?? "[]"
|
|
790
|
+
let delayConditionList: [DelayCondition] = self.fromJsonArr(json: delayUpdatePreferences).map { obj -> DelayCondition in
|
|
791
|
+
let kind: String = obj.value(forKey: "kind") as! String
|
|
792
|
+
let value: String? = obj.value(forKey: "value") as? String
|
|
793
|
+
return DelayCondition(kind: kind, value: value)
|
|
794
|
+
}
|
|
795
|
+
if !delayConditionList.isEmpty {
|
|
796
|
+
print("\(CapacitorUpdater.TAG) Update delayed until delay conditions met")
|
|
797
|
+
self.endBackGroundTaskWithNotif(msg: "Update delayed until delay conditions met", latestVersionName: latestVersionName, current: next, error: false)
|
|
798
|
+
return
|
|
799
|
+
}
|
|
821
800
|
_ = self.implementation.set(bundle: next)
|
|
822
801
|
_ = self._reload()
|
|
823
|
-
self.
|
|
824
|
-
self.endBackGroundTaskWithNotif(msg: "update installed", latestVersionName: latestVersionName, current: current, error: false)
|
|
802
|
+
self.endBackGroundTaskWithNotif(msg: "update installed", latestVersionName: latestVersionName, current: next, error: false)
|
|
825
803
|
} else {
|
|
826
804
|
self.notifyListeners("updateAvailable", data: ["bundle": next.toJSON()])
|
|
827
805
|
_ = self.implementation.setNextBundle(next: next.getId())
|
|
@@ -844,17 +822,17 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
844
822
|
|
|
845
823
|
@objc func appKilled() {
|
|
846
824
|
print("\(CapacitorUpdater.TAG) onActivityDestroyed: all activity destroyed")
|
|
847
|
-
self.
|
|
825
|
+
self.delayUpdateUtils.checkCancelDelay(source: .killed)
|
|
848
826
|
}
|
|
849
827
|
|
|
850
828
|
private func installNext() {
|
|
851
|
-
let delayUpdatePreferences = UserDefaults.standard.string(forKey: DELAY_CONDITION_PREFERENCES) ?? "[]"
|
|
852
|
-
let delayConditionList: [DelayCondition]
|
|
829
|
+
let delayUpdatePreferences = UserDefaults.standard.string(forKey: DelayUpdateUtils.DELAY_CONDITION_PREFERENCES) ?? "[]"
|
|
830
|
+
let delayConditionList: [DelayCondition] = fromJsonArr(json: delayUpdatePreferences).map { obj -> DelayCondition in
|
|
853
831
|
let kind: String = obj.value(forKey: "kind") as! String
|
|
854
832
|
let value: String? = obj.value(forKey: "value") as? String
|
|
855
833
|
return DelayCondition(kind: kind, value: value)
|
|
856
834
|
}
|
|
857
|
-
if delayConditionList
|
|
835
|
+
if !delayConditionList.isEmpty {
|
|
858
836
|
print("\(CapacitorUpdater.TAG) Update delayed until delay conditions met")
|
|
859
837
|
return
|
|
860
838
|
}
|
|
@@ -893,6 +871,8 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
893
871
|
@objc func appMovedToForeground() {
|
|
894
872
|
let current: BundleInfo = self.implementation.getCurrentBundle()
|
|
895
873
|
self.implementation.sendStats(action: "app_moved_to_foreground", versionName: current.getVersionName())
|
|
874
|
+
self.delayUpdateUtils.checkCancelDelay(source: .foreground)
|
|
875
|
+
self.delayUpdateUtils.unsetBackgroundTimestamp()
|
|
896
876
|
if backgroundWork != nil && taskRunning {
|
|
897
877
|
backgroundWork!.cancel()
|
|
898
878
|
print("\(CapacitorUpdater.TAG) Background Timer Task canceled, Activity resumed before timer completes")
|
|
@@ -929,38 +909,15 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
929
909
|
}
|
|
930
910
|
|
|
931
911
|
@objc func appMovedToBackground() {
|
|
932
|
-
self.implementation.
|
|
912
|
+
let current: BundleInfo = self.implementation.getCurrentBundle()
|
|
913
|
+
self.implementation.sendStats(action: "app_moved_to_background", versionName: current.getVersionName())
|
|
933
914
|
print("\(CapacitorUpdater.TAG) Check for pending update")
|
|
934
|
-
let delayUpdatePreferences = UserDefaults.standard.string(forKey: DELAY_CONDITION_PREFERENCES) ?? "[]"
|
|
935
|
-
|
|
936
|
-
let delayConditionList: [DelayCondition] = fromJsonArr(json: delayUpdatePreferences).map { obj -> DelayCondition in
|
|
937
|
-
let kind: String = obj.value(forKey: "kind") as! String
|
|
938
|
-
let value: String? = obj.value(forKey: "value") as? String
|
|
939
|
-
return DelayCondition(kind: kind, value: value)
|
|
940
|
-
}
|
|
941
|
-
var backgroundValue: String?
|
|
942
|
-
for delayCondition in delayConditionList {
|
|
943
|
-
if delayCondition.getKind() == "background" {
|
|
944
|
-
let value: String? = delayCondition.getValue()
|
|
945
|
-
backgroundValue = (value != nil && value != "") ? value! : "0"
|
|
946
|
-
}
|
|
947
|
-
}
|
|
948
|
-
if backgroundValue != nil {
|
|
949
|
-
self.taskRunning = true
|
|
950
|
-
let interval: Double = (Double(backgroundValue!) ?? 0.0) / 1000
|
|
951
|
-
self.backgroundWork?.cancel()
|
|
952
|
-
self.backgroundWork = DispatchWorkItem(block: {
|
|
953
|
-
// IOS never executes this task in background
|
|
954
|
-
self.taskRunning = false
|
|
955
|
-
self._checkCancelDelay(killed: false)
|
|
956
|
-
self.installNext()
|
|
957
|
-
})
|
|
958
|
-
DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + interval, execute: self.backgroundWork!)
|
|
959
|
-
} else {
|
|
960
|
-
self._checkCancelDelay(killed: false)
|
|
961
|
-
self.installNext()
|
|
962
|
-
}
|
|
963
915
|
|
|
916
|
+
// Set background timestamp
|
|
917
|
+
let backgroundTimestamp = Int64(Date().timeIntervalSince1970 * 1000) // Convert to milliseconds
|
|
918
|
+
self.delayUpdateUtils.setBackgroundTimestamp(backgroundTimestamp)
|
|
919
|
+
self.delayUpdateUtils.checkCancelDelay(source: .background)
|
|
920
|
+
self.installNext()
|
|
964
921
|
}
|
|
965
922
|
|
|
966
923
|
@objc func getNextBundle(_ call: CAPPluginCall) {
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
//
|
|
8
|
+
// DelayUpdateUtils.swift
|
|
9
|
+
// Plugin
|
|
10
|
+
//
|
|
11
|
+
// Created by Auto-generated based on Android implementation
|
|
12
|
+
// Copyright © 2024 Capgo. All rights reserved.
|
|
13
|
+
//
|
|
14
|
+
|
|
15
|
+
import Foundation
|
|
16
|
+
import Version
|
|
17
|
+
|
|
18
|
+
public class DelayUpdateUtils {
|
|
19
|
+
|
|
20
|
+
static let DELAY_CONDITION_PREFERENCES = "DELAY_CONDITION_PREFERENCES_CAPGO"
|
|
21
|
+
static let BACKGROUND_TIMESTAMP_KEY = "BACKGROUND_TIMESTAMP_KEY_CAPGO"
|
|
22
|
+
|
|
23
|
+
private let currentVersionNative: Version
|
|
24
|
+
private let installNext: () -> Void
|
|
25
|
+
|
|
26
|
+
public enum CancelDelaySource {
|
|
27
|
+
case killed
|
|
28
|
+
case background
|
|
29
|
+
case foreground
|
|
30
|
+
|
|
31
|
+
var description: String {
|
|
32
|
+
switch self {
|
|
33
|
+
case .killed: return "KILLED"
|
|
34
|
+
case .background: return "BACKGROUND"
|
|
35
|
+
case .foreground: return "FOREGROUND"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public init(currentVersionNative: Version, installNext: @escaping () -> Void) {
|
|
41
|
+
self.currentVersionNative = currentVersionNative
|
|
42
|
+
self.installNext = installNext
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public func checkCancelDelay(source: CancelDelaySource) {
|
|
46
|
+
let delayUpdatePreferences = UserDefaults.standard.string(forKey: DelayUpdateUtils.DELAY_CONDITION_PREFERENCES) ?? "[]"
|
|
47
|
+
let delayConditionList: [DelayCondition] = fromJsonArr(json: delayUpdatePreferences).map { obj -> DelayCondition in
|
|
48
|
+
let kind: String = obj.value(forKey: "kind") as! String
|
|
49
|
+
let value: String? = obj.value(forKey: "value") as? String
|
|
50
|
+
return DelayCondition(kind: kind, value: value)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
var delayConditionListToKeep: [DelayCondition] = []
|
|
54
|
+
var index = 0
|
|
55
|
+
|
|
56
|
+
for condition in delayConditionList {
|
|
57
|
+
let kind = condition.getKind()
|
|
58
|
+
let value = condition.getValue()
|
|
59
|
+
|
|
60
|
+
switch kind {
|
|
61
|
+
case "background":
|
|
62
|
+
if source == .foreground {
|
|
63
|
+
let backgroundedAt = getBackgroundTimestamp()
|
|
64
|
+
let now = Int64(Date().timeIntervalSince1970 * 1000) // Convert to milliseconds
|
|
65
|
+
let delta = max(0, now - backgroundedAt)
|
|
66
|
+
|
|
67
|
+
var longValue: Int64 = 0
|
|
68
|
+
if let value = value, !value.isEmpty {
|
|
69
|
+
longValue = Int64(value) ?? 0
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if delta > longValue {
|
|
73
|
+
print("\(CapacitorUpdater.TAG) Background condition (value: \(value ?? "")) deleted at index \(index). Delta: \(delta), longValue: \(longValue)")
|
|
74
|
+
} else {
|
|
75
|
+
delayConditionListToKeep.append(condition)
|
|
76
|
+
print("\(CapacitorUpdater.TAG) Background delay (value: \(value ?? "")) condition kept at index \(index) (source: \(source.description))")
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
delayConditionListToKeep.append(condition)
|
|
80
|
+
print("\(CapacitorUpdater.TAG) Background delay (value: \(value ?? "")) condition kept at index \(index) (source: \(source.description))")
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
case "kill":
|
|
84
|
+
if source == .killed {
|
|
85
|
+
self.installNext()
|
|
86
|
+
} else {
|
|
87
|
+
delayConditionListToKeep.append(condition)
|
|
88
|
+
print("\(CapacitorUpdater.TAG) Kill delay (value: \(value ?? "")) condition kept at index \(index) (source: \(source.description))")
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
case "date":
|
|
92
|
+
if let value = value, !value.isEmpty {
|
|
93
|
+
do {
|
|
94
|
+
let dateFormatter = ISO8601DateFormatter()
|
|
95
|
+
dateFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
|
|
96
|
+
|
|
97
|
+
if let date = dateFormatter.date(from: value) {
|
|
98
|
+
if Date() > date {
|
|
99
|
+
print("\(CapacitorUpdater.TAG) Date delay (value: \(value)) condition removed due to expired date at index \(index)")
|
|
100
|
+
} else {
|
|
101
|
+
delayConditionListToKeep.append(condition)
|
|
102
|
+
print("\(CapacitorUpdater.TAG) Date delay (value: \(value)) condition kept at index \(index)")
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
print("\(CapacitorUpdater.TAG) Date delay (value: \(value)) condition removed due to parsing issue at index \(index)")
|
|
106
|
+
}
|
|
107
|
+
} catch {
|
|
108
|
+
print("\(CapacitorUpdater.TAG) Date delay (value: \(value)) condition removed due to parsing issue at index \(index): \(error)")
|
|
109
|
+
}
|
|
110
|
+
} else {
|
|
111
|
+
print("\(CapacitorUpdater.TAG) Date delay (value: \(value ?? "")) condition removed due to empty value at index \(index)")
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
case "nativeVersion":
|
|
115
|
+
if let value = value, !value.isEmpty {
|
|
116
|
+
do {
|
|
117
|
+
let versionLimit = try Version(value)
|
|
118
|
+
if currentVersionNative >= versionLimit {
|
|
119
|
+
print("\(CapacitorUpdater.TAG) Native version delay (value: \(value)) condition removed due to above limit at index \(index)")
|
|
120
|
+
} else {
|
|
121
|
+
delayConditionListToKeep.append(condition)
|
|
122
|
+
print("\(CapacitorUpdater.TAG) Native version delay (value: \(value)) condition kept at index \(index)")
|
|
123
|
+
}
|
|
124
|
+
} catch {
|
|
125
|
+
print("\(CapacitorUpdater.TAG) Native version delay (value: \(value)) condition removed due to parsing issue at index \(index): \(error)")
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
print("\(CapacitorUpdater.TAG) Native version delay (value: \(value ?? "")) condition removed due to empty value at index \(index)")
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
default:
|
|
132
|
+
print("\(CapacitorUpdater.TAG) Unknown delay condition kind: \(kind) at index \(index)")
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
index += 1
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if !delayConditionListToKeep.isEmpty {
|
|
139
|
+
let json = toJson(object: delayConditionListToKeep.map { $0.toJSON() })
|
|
140
|
+
_ = setMultiDelay(delayConditions: json)
|
|
141
|
+
} else {
|
|
142
|
+
// Clear all delay conditions if none are left to keep
|
|
143
|
+
_ = cancelDelay(source: "checkCancelDelay")
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
public func setMultiDelay(delayConditions: String) -> Bool {
|
|
148
|
+
do {
|
|
149
|
+
UserDefaults.standard.set(delayConditions, forKey: DelayUpdateUtils.DELAY_CONDITION_PREFERENCES)
|
|
150
|
+
UserDefaults.standard.synchronize()
|
|
151
|
+
print("\(CapacitorUpdater.TAG) Delay update saved")
|
|
152
|
+
return true
|
|
153
|
+
} catch {
|
|
154
|
+
print("\(CapacitorUpdater.TAG) Failed to delay update, [Error calling 'setMultiDelay()']: \(error)")
|
|
155
|
+
return false
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
public func setBackgroundTimestamp(_ backgroundTimestamp: Int64) {
|
|
160
|
+
do {
|
|
161
|
+
UserDefaults.standard.set(backgroundTimestamp, forKey: DelayUpdateUtils.BACKGROUND_TIMESTAMP_KEY)
|
|
162
|
+
UserDefaults.standard.synchronize()
|
|
163
|
+
print("\(CapacitorUpdater.TAG) Background timestamp saved")
|
|
164
|
+
} catch {
|
|
165
|
+
print("\(CapacitorUpdater.TAG) Failed to save background timestamp, [Error calling 'setBackgroundTimestamp()']: \(error)")
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
public func unsetBackgroundTimestamp() {
|
|
170
|
+
do {
|
|
171
|
+
UserDefaults.standard.removeObject(forKey: DelayUpdateUtils.BACKGROUND_TIMESTAMP_KEY)
|
|
172
|
+
UserDefaults.standard.synchronize()
|
|
173
|
+
print("\(CapacitorUpdater.TAG) Background timestamp removed")
|
|
174
|
+
} catch {
|
|
175
|
+
print("\(CapacitorUpdater.TAG) Failed to remove background timestamp, [Error calling 'unsetBackgroundTimestamp()']: \(error)")
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private func getBackgroundTimestamp() -> Int64 {
|
|
180
|
+
do {
|
|
181
|
+
let timestamp = UserDefaults.standard.object(forKey: DelayUpdateUtils.BACKGROUND_TIMESTAMP_KEY) as? Int64 ?? 0
|
|
182
|
+
return timestamp
|
|
183
|
+
} catch {
|
|
184
|
+
print("\(CapacitorUpdater.TAG) Failed to get background timestamp, [Error calling 'getBackgroundTimestamp()']: \(error)")
|
|
185
|
+
return 0
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
public func cancelDelay(source: String) -> Bool {
|
|
190
|
+
do {
|
|
191
|
+
UserDefaults.standard.removeObject(forKey: DelayUpdateUtils.DELAY_CONDITION_PREFERENCES)
|
|
192
|
+
UserDefaults.standard.synchronize()
|
|
193
|
+
print("\(CapacitorUpdater.TAG) All delays canceled from \(source)")
|
|
194
|
+
return true
|
|
195
|
+
} catch {
|
|
196
|
+
print("\(CapacitorUpdater.TAG) Failed to cancel update delay: \(error)")
|
|
197
|
+
return false
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// MARK: - Helper methods
|
|
202
|
+
|
|
203
|
+
private func toJson(object: Any) -> String {
|
|
204
|
+
guard let data = try? JSONSerialization.data(withJSONObject: object, options: []) else {
|
|
205
|
+
return ""
|
|
206
|
+
}
|
|
207
|
+
return String(data: data, encoding: String.Encoding.utf8) ?? ""
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
private func fromJsonArr(json: String) -> [NSObject] {
|
|
211
|
+
guard let jsonData = json.data(using: .utf8) else {
|
|
212
|
+
return []
|
|
213
|
+
}
|
|
214
|
+
let object = try? JSONSerialization.jsonObject(
|
|
215
|
+
with: jsonData,
|
|
216
|
+
options: .mutableContainers
|
|
217
|
+
) as? [NSObject]
|
|
218
|
+
return object ?? []
|
|
219
|
+
}
|
|
220
|
+
}
|