@capgo/capacitor-updater 6.14.26 → 6.14.29
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/CapgoCapacitorUpdater.podspec +3 -2
- package/Package.swift +2 -2
- package/README.md +341 -74
- package/android/build.gradle +20 -8
- package/android/proguard-rules.pro +22 -5
- package/android/src/main/java/ee/forgr/capacitor_updater/BundleInfo.java +52 -16
- package/android/src/main/java/ee/forgr/capacitor_updater/Callback.java +2 -2
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +1196 -508
- package/android/src/main/java/ee/forgr/capacitor_updater/{CapacitorUpdater.java → CapgoUpdater.java} +522 -154
- package/android/src/main/java/ee/forgr/capacitor_updater/{CryptoCipher.java → CryptoCipherV1.java} +17 -9
- package/android/src/main/java/ee/forgr/capacitor_updater/CryptoCipherV2.java +15 -26
- package/android/src/main/java/ee/forgr/capacitor_updater/DelayCondition.java +0 -3
- package/android/src/main/java/ee/forgr/capacitor_updater/DelayUpdateUtils.java +260 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadService.java +300 -119
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadWorkerManager.java +63 -25
- package/android/src/main/java/ee/forgr/capacitor_updater/Logger.java +338 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/ShakeDetector.java +72 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/ShakeMenu.java +169 -0
- package/dist/docs.json +652 -63
- package/dist/esm/definitions.d.ts +265 -15
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/history.d.ts +1 -0
- package/dist/esm/history.js +283 -0
- package/dist/esm/history.js.map +1 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/web.d.ts +12 -1
- package/dist/esm/web.js +29 -2
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +311 -2
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +311 -2
- package/dist/plugin.js.map +1 -1
- package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/AES.swift +6 -3
- package/ios/Sources/CapacitorUpdaterPlugin/CapacitorUpdaterPlugin.swift +1575 -0
- package/ios/{Plugin/CapacitorUpdater.swift → Sources/CapacitorUpdaterPlugin/CapgoUpdater.swift} +365 -139
- package/ios/{Plugin/CryptoCipher.swift → Sources/CapacitorUpdaterPlugin/CryptoCipherV1.swift} +13 -6
- package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/CryptoCipherV2.swift +33 -27
- package/ios/Sources/CapacitorUpdaterPlugin/DelayUpdateUtils.swift +220 -0
- package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/InternalUtils.swift +47 -0
- package/ios/Sources/CapacitorUpdaterPlugin/Logger.swift +310 -0
- package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/RSA.swift +1 -0
- package/ios/Sources/CapacitorUpdaterPlugin/ShakeMenu.swift +112 -0
- package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/UserDefaultsExtension.swift +0 -2
- package/package.json +20 -16
- package/ios/Plugin/CapacitorUpdaterPlugin.swift +0 -1030
- /package/{LICENCE → LICENSE} +0 -0
- /package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/BigInt.swift +0 -0
- /package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/BundleInfo.swift +0 -0
- /package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/BundleStatus.swift +0 -0
- /package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/DelayCondition.swift +0 -0
- /package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/DelayUntilNext.swift +0 -0
- /package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/Info.plist +0 -0
|
@@ -12,31 +12,36 @@ import android.content.Context;
|
|
|
12
12
|
import android.content.SharedPreferences;
|
|
13
13
|
import android.content.pm.PackageInfo;
|
|
14
14
|
import android.content.pm.PackageManager;
|
|
15
|
+
import android.graphics.Color;
|
|
15
16
|
import android.os.Build;
|
|
17
|
+
import android.os.Handler;
|
|
16
18
|
import android.os.Looper;
|
|
17
|
-
import android.
|
|
19
|
+
import android.view.Gravity;
|
|
20
|
+
import android.view.View;
|
|
21
|
+
import android.view.ViewGroup;
|
|
22
|
+
import android.widget.FrameLayout;
|
|
23
|
+
import android.widget.ProgressBar;
|
|
18
24
|
import com.getcapacitor.CapConfig;
|
|
19
25
|
import com.getcapacitor.JSArray;
|
|
20
26
|
import com.getcapacitor.JSObject;
|
|
21
27
|
import com.getcapacitor.Plugin;
|
|
22
28
|
import com.getcapacitor.PluginCall;
|
|
29
|
+
import com.getcapacitor.PluginHandle;
|
|
23
30
|
import com.getcapacitor.PluginMethod;
|
|
24
31
|
import com.getcapacitor.annotation.CapacitorPlugin;
|
|
25
32
|
import com.getcapacitor.plugin.WebView;
|
|
26
|
-
import com.google.gson.Gson;
|
|
27
|
-
import com.google.gson.reflect.TypeToken;
|
|
28
33
|
import io.github.g00fy2.versioncompare.Version;
|
|
29
34
|
import java.io.IOException;
|
|
30
|
-
import java.lang.reflect.Type;
|
|
31
35
|
import java.net.MalformedURLException;
|
|
32
36
|
import java.net.URL;
|
|
33
|
-
import java.text.SimpleDateFormat;
|
|
34
37
|
import java.util.ArrayList;
|
|
35
38
|
import java.util.Arrays;
|
|
36
39
|
import java.util.Date;
|
|
37
|
-
import java.util.
|
|
40
|
+
import java.util.HashSet;
|
|
38
41
|
import java.util.List;
|
|
42
|
+
import java.util.Map;
|
|
39
43
|
import java.util.Objects;
|
|
44
|
+
import java.util.Set;
|
|
40
45
|
import java.util.Timer;
|
|
41
46
|
import java.util.TimerTask;
|
|
42
47
|
import java.util.UUID;
|
|
@@ -45,24 +50,35 @@ import java.util.concurrent.Semaphore;
|
|
|
45
50
|
import java.util.concurrent.TimeUnit;
|
|
46
51
|
import java.util.concurrent.TimeoutException;
|
|
47
52
|
import java.util.concurrent.atomic.AtomicReference;
|
|
48
|
-
|
|
49
|
-
import okhttp3.Protocol;
|
|
53
|
+
// Removed OkHttpClient and Protocol imports - using shared client in DownloadService instead
|
|
50
54
|
import org.json.JSONArray;
|
|
51
55
|
import org.json.JSONException;
|
|
56
|
+
import org.json.JSONObject;
|
|
52
57
|
|
|
53
58
|
@CapacitorPlugin(name = "CapacitorUpdater")
|
|
54
59
|
public class CapacitorUpdaterPlugin extends Plugin {
|
|
55
60
|
|
|
61
|
+
private final Logger logger = new Logger("CapgoUpdater");
|
|
62
|
+
|
|
56
63
|
private static final String updateUrlDefault = "https://plugin.capgo.app/updates";
|
|
57
64
|
private static final String statsUrlDefault = "https://plugin.capgo.app/stats";
|
|
58
65
|
private static final String channelUrlDefault = "https://plugin.capgo.app/channel_self";
|
|
59
|
-
|
|
60
|
-
private final String
|
|
66
|
+
private static final String KEEP_URL_FLAG_KEY = "__capgo_keep_url_path_after_reload";
|
|
67
|
+
private static final String CUSTOM_ID_PREF_KEY = "CapacitorUpdater.customId";
|
|
68
|
+
private static final String UPDATE_URL_PREF_KEY = "CapacitorUpdater.updateUrl";
|
|
69
|
+
private static final String STATS_URL_PREF_KEY = "CapacitorUpdater.statsUrl";
|
|
70
|
+
private static final String CHANNEL_URL_PREF_KEY = "CapacitorUpdater.channelUrl";
|
|
71
|
+
private static final String[] BREAKING_EVENT_NAMES = { "breakingAvailable", "majorAvailable" };
|
|
72
|
+
private static final String LAST_FAILED_BUNDLE_PREF_KEY = "CapacitorUpdater.lastFailedBundle";
|
|
73
|
+
|
|
74
|
+
private final String PLUGIN_VERSION = "6.14.29";
|
|
61
75
|
private static final String DELAY_CONDITION_PREFERENCES = "";
|
|
62
76
|
|
|
63
77
|
private SharedPreferences.Editor editor;
|
|
64
78
|
private SharedPreferences prefs;
|
|
65
|
-
protected
|
|
79
|
+
protected CapgoUpdater implementation;
|
|
80
|
+
private Boolean persistCustomId = false;
|
|
81
|
+
private Boolean persistModifyUrl = false;
|
|
66
82
|
|
|
67
83
|
private Integer appReadyTimeout = 10000;
|
|
68
84
|
private Integer periodCheckDelay = 0;
|
|
@@ -71,9 +87,18 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
71
87
|
private Boolean autoUpdate = false;
|
|
72
88
|
private String updateUrl = "";
|
|
73
89
|
private Version currentVersionNative;
|
|
90
|
+
private String currentBuildVersion;
|
|
74
91
|
private Thread backgroundTask;
|
|
75
92
|
private Boolean taskRunning = false;
|
|
76
93
|
private Boolean keepUrlPathAfterReload = false;
|
|
94
|
+
private Boolean autoSplashscreen = false;
|
|
95
|
+
private Boolean autoSplashscreenLoader = false;
|
|
96
|
+
private Integer autoSplashscreenTimeout = 10000;
|
|
97
|
+
private Boolean autoSplashscreenTimedOut = false;
|
|
98
|
+
private String directUpdateMode = "false";
|
|
99
|
+
private Boolean wasRecentlyInstalledOrUpdated = false;
|
|
100
|
+
Boolean shakeMenuEnabled = false;
|
|
101
|
+
private Boolean allowManualBundleError = false;
|
|
77
102
|
|
|
78
103
|
private Boolean isPreviousMainActivity = true;
|
|
79
104
|
|
|
@@ -85,6 +110,63 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
85
110
|
|
|
86
111
|
private int lastNotifiedStatPercent = 0;
|
|
87
112
|
|
|
113
|
+
private DelayUpdateUtils delayUpdateUtils;
|
|
114
|
+
|
|
115
|
+
private ShakeMenu shakeMenu;
|
|
116
|
+
private final Handler mainHandler = new Handler(Looper.getMainLooper());
|
|
117
|
+
private FrameLayout splashscreenLoaderOverlay;
|
|
118
|
+
private Runnable splashscreenTimeoutRunnable;
|
|
119
|
+
|
|
120
|
+
private void notifyBreakingEvents(final String version) {
|
|
121
|
+
if (version == null || version.isEmpty()) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
for (final String eventName : BREAKING_EVENT_NAMES) {
|
|
125
|
+
final JSObject payload = new JSObject();
|
|
126
|
+
payload.put("version", version);
|
|
127
|
+
CapacitorUpdaterPlugin.this.notifyListeners(eventName, payload);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
private JSObject mapToJSObject(Map<String, Object> map) {
|
|
132
|
+
JSObject jsObject = new JSObject();
|
|
133
|
+
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
|
134
|
+
jsObject.put(entry.getKey(), entry.getValue());
|
|
135
|
+
}
|
|
136
|
+
return jsObject;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private void persistLastFailedBundle(BundleInfo bundle) {
|
|
140
|
+
if (this.prefs == null) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
final SharedPreferences.Editor localEditor = this.prefs.edit();
|
|
144
|
+
if (bundle == null) {
|
|
145
|
+
localEditor.remove(LAST_FAILED_BUNDLE_PREF_KEY);
|
|
146
|
+
} else {
|
|
147
|
+
final JSONObject json = new JSONObject(bundle.toJSONMap());
|
|
148
|
+
localEditor.putString(LAST_FAILED_BUNDLE_PREF_KEY, json.toString());
|
|
149
|
+
}
|
|
150
|
+
localEditor.apply();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private BundleInfo readLastFailedBundle() {
|
|
154
|
+
if (this.prefs == null) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
final String raw = this.prefs.getString(LAST_FAILED_BUNDLE_PREF_KEY, null);
|
|
158
|
+
if (raw == null || raw.trim().isEmpty()) {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
return BundleInfo.fromJSON(raw);
|
|
163
|
+
} catch (final JSONException e) {
|
|
164
|
+
logger.error("Failed to parse failed bundle info: " + e.getMessage());
|
|
165
|
+
this.persistLastFailedBundle(null);
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
88
170
|
public Thread startNewThread(final Runnable function, Number waitTime) {
|
|
89
171
|
Thread bgTask = new Thread(() -> {
|
|
90
172
|
try {
|
|
@@ -111,54 +193,90 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
111
193
|
this.editor = this.prefs.edit();
|
|
112
194
|
|
|
113
195
|
try {
|
|
114
|
-
this.implementation = new
|
|
196
|
+
this.implementation = new CapgoUpdater(logger) {
|
|
115
197
|
@Override
|
|
116
198
|
public void notifyDownload(final String id, final int percent) {
|
|
117
|
-
|
|
199
|
+
activity.runOnUiThread(() -> {
|
|
200
|
+
CapacitorUpdaterPlugin.this.notifyDownload(id, percent);
|
|
201
|
+
});
|
|
118
202
|
}
|
|
119
203
|
|
|
120
204
|
@Override
|
|
121
205
|
public void directUpdateFinish(final BundleInfo latest) {
|
|
122
|
-
|
|
206
|
+
activity.runOnUiThread(() -> {
|
|
207
|
+
CapacitorUpdaterPlugin.this.directUpdateFinish(latest);
|
|
208
|
+
});
|
|
123
209
|
}
|
|
124
210
|
|
|
125
211
|
@Override
|
|
126
|
-
public void notifyListeners(final String id, final
|
|
127
|
-
|
|
212
|
+
public void notifyListeners(final String id, final Map<String, Object> res) {
|
|
213
|
+
activity.runOnUiThread(() -> {
|
|
214
|
+
CapacitorUpdaterPlugin.this.notifyListeners(id, CapacitorUpdaterPlugin.this.mapToJSObject(res));
|
|
215
|
+
});
|
|
128
216
|
}
|
|
129
217
|
};
|
|
130
218
|
final PackageInfo pInfo = this.getContext().getPackageManager().getPackageInfo(this.getContext().getPackageName(), 0);
|
|
131
219
|
this.implementation.activity = this.getActivity();
|
|
132
220
|
this.implementation.versionBuild = this.getConfig().getString("version", pInfo.versionName);
|
|
221
|
+
this.implementation.CAP_SERVER_PATH = WebView.CAP_SERVER_PATH;
|
|
133
222
|
this.implementation.PLUGIN_VERSION = this.PLUGIN_VERSION;
|
|
134
223
|
this.implementation.versionCode = Integer.toString(pInfo.versionCode);
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
.
|
|
141
|
-
|
|
142
|
-
|
|
224
|
+
// Removed unused OkHttpClient creation - using shared client in DownloadService instead
|
|
225
|
+
// Handle directUpdate configuration - support string values and backward compatibility
|
|
226
|
+
String directUpdateConfig = this.getConfig().getString("directUpdate", null);
|
|
227
|
+
if (directUpdateConfig != null) {
|
|
228
|
+
this.directUpdateMode = directUpdateConfig;
|
|
229
|
+
this.implementation.directUpdate = directUpdateConfig.equals("always") || directUpdateConfig.equals("atInstall");
|
|
230
|
+
} else {
|
|
231
|
+
Boolean directUpdateBool = this.getConfig().getBoolean("directUpdate", false);
|
|
232
|
+
if (directUpdateBool) {
|
|
233
|
+
this.directUpdateMode = "always"; // backward compatibility: true = always
|
|
234
|
+
this.implementation.directUpdate = true;
|
|
235
|
+
} else {
|
|
236
|
+
this.directUpdateMode = "false";
|
|
237
|
+
this.implementation.directUpdate = false;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
143
240
|
this.currentVersionNative = new Version(this.getConfig().getString("version", pInfo.versionName));
|
|
241
|
+
this.currentBuildVersion = Integer.toString(pInfo.versionCode);
|
|
242
|
+
this.delayUpdateUtils = new DelayUpdateUtils(this.prefs, this.editor, this.currentVersionNative, logger);
|
|
144
243
|
} catch (final PackageManager.NameNotFoundException e) {
|
|
145
|
-
|
|
244
|
+
logger.error("Error instantiating implementation " + e.getMessage());
|
|
146
245
|
return;
|
|
147
246
|
} catch (final Exception e) {
|
|
148
|
-
|
|
247
|
+
logger.error("Error getting current native app version " + e.getMessage());
|
|
149
248
|
return;
|
|
150
249
|
}
|
|
250
|
+
|
|
251
|
+
boolean disableJSLogging = this.getConfig().getBoolean("disableJSLogging", false);
|
|
252
|
+
// Set the bridge in the Logger when webView is available
|
|
253
|
+
if (this.bridge != null && this.bridge.getWebView() != null && !disableJSLogging) {
|
|
254
|
+
logger.setBridge(this.bridge);
|
|
255
|
+
logger.info("WebView set successfully for logging");
|
|
256
|
+
} else {
|
|
257
|
+
logger.info("WebView not ready yet, will be set later");
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Set logger for shared classes
|
|
261
|
+
CryptoCipherV1.setLogger(logger);
|
|
262
|
+
CryptoCipherV2.setLogger(logger);
|
|
263
|
+
DownloadService.setLogger(logger);
|
|
264
|
+
DownloadWorkerManager.setLogger(logger);
|
|
265
|
+
|
|
151
266
|
final CapConfig config = CapConfig.loadDefault(this.getActivity());
|
|
152
267
|
this.implementation.appId = InternalUtils.getPackageName(getContext().getPackageManager(), getContext().getPackageName());
|
|
153
268
|
this.implementation.appId = config.getString("appId", this.implementation.appId);
|
|
154
269
|
this.implementation.appId = this.getConfig().getString("appId", this.implementation.appId);
|
|
155
270
|
if (this.implementation.appId == null || this.implementation.appId.isEmpty()) {
|
|
156
|
-
// crash the app
|
|
271
|
+
// crash the app on purpose it should not happen
|
|
157
272
|
throw new RuntimeException(
|
|
158
273
|
"appId is missing in capacitor.config.json or plugin config, and cannot be retrieved from the native app, please add it globally or in the plugin config"
|
|
159
274
|
);
|
|
160
275
|
}
|
|
161
|
-
|
|
276
|
+
logger.info("appId: " + implementation.appId);
|
|
277
|
+
|
|
278
|
+
this.persistCustomId = this.getConfig().getBoolean("persistCustomId", false);
|
|
279
|
+
this.persistModifyUrl = this.getConfig().getBoolean("persistModifyUrl", false);
|
|
162
280
|
this.implementation.publicKey = this.getConfig().getString("publicKey", "");
|
|
163
281
|
this.implementation.privateKey = this.getConfig().getString("privateKey", "");
|
|
164
282
|
if (this.implementation.privateKey != null && !this.implementation.privateKey.isEmpty()) {
|
|
@@ -166,6 +284,22 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
166
284
|
}
|
|
167
285
|
this.implementation.statsUrl = this.getConfig().getString("statsUrl", statsUrlDefault);
|
|
168
286
|
this.implementation.channelUrl = this.getConfig().getString("channelUrl", channelUrlDefault);
|
|
287
|
+
if (Boolean.TRUE.equals(this.persistModifyUrl)) {
|
|
288
|
+
if (this.prefs.contains(STATS_URL_PREF_KEY)) {
|
|
289
|
+
final String storedStatsUrl = this.prefs.getString(STATS_URL_PREF_KEY, this.implementation.statsUrl);
|
|
290
|
+
if (storedStatsUrl != null) {
|
|
291
|
+
this.implementation.statsUrl = storedStatsUrl;
|
|
292
|
+
logger.info("Loaded persisted statsUrl");
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
if (this.prefs.contains(CHANNEL_URL_PREF_KEY)) {
|
|
296
|
+
final String storedChannelUrl = this.prefs.getString(CHANNEL_URL_PREF_KEY, this.implementation.channelUrl);
|
|
297
|
+
if (storedChannelUrl != null) {
|
|
298
|
+
this.implementation.channelUrl = storedChannelUrl;
|
|
299
|
+
logger.info("Loaded persisted channelUrl");
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
169
303
|
int userValue = this.getConfig().getInt("periodCheckDelay", 0);
|
|
170
304
|
this.implementation.defaultChannel = this.getConfig().getString("defaultChannel", "");
|
|
171
305
|
|
|
@@ -181,103 +315,364 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
181
315
|
this.implementation.versionOs = Build.VERSION.RELEASE;
|
|
182
316
|
this.implementation.deviceID = this.prefs.getString("appUUID", UUID.randomUUID().toString()).toLowerCase();
|
|
183
317
|
this.editor.putString("appUUID", this.implementation.deviceID);
|
|
184
|
-
this.editor.
|
|
185
|
-
|
|
186
|
-
|
|
318
|
+
this.editor.apply();
|
|
319
|
+
|
|
320
|
+
// Update User-Agent for shared OkHttpClient with OS version
|
|
321
|
+
DownloadService.updateUserAgent(this.implementation.appId, this.PLUGIN_VERSION, this.implementation.versionOs);
|
|
322
|
+
|
|
323
|
+
if (Boolean.TRUE.equals(this.persistCustomId)) {
|
|
324
|
+
final String storedCustomId = this.prefs.getString(CUSTOM_ID_PREF_KEY, "");
|
|
325
|
+
if (storedCustomId != null && !storedCustomId.isEmpty()) {
|
|
326
|
+
this.implementation.customId = storedCustomId;
|
|
327
|
+
logger.info("Loaded persisted customId");
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
logger.info("init for device " + this.implementation.deviceID);
|
|
331
|
+
logger.info("version native " + this.currentVersionNative.getOriginalString());
|
|
187
332
|
this.autoDeleteFailed = this.getConfig().getBoolean("autoDeleteFailed", true);
|
|
188
333
|
this.autoDeletePrevious = this.getConfig().getBoolean("autoDeletePrevious", true);
|
|
189
334
|
this.updateUrl = this.getConfig().getString("updateUrl", updateUrlDefault);
|
|
335
|
+
if (Boolean.TRUE.equals(this.persistModifyUrl)) {
|
|
336
|
+
if (this.prefs.contains(UPDATE_URL_PREF_KEY)) {
|
|
337
|
+
final String storedUpdateUrl = this.prefs.getString(UPDATE_URL_PREF_KEY, this.updateUrl);
|
|
338
|
+
if (storedUpdateUrl != null) {
|
|
339
|
+
this.updateUrl = storedUpdateUrl;
|
|
340
|
+
logger.info("Loaded persisted updateUrl");
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
190
344
|
this.autoUpdate = this.getConfig().getBoolean("autoUpdate", true);
|
|
191
345
|
this.appReadyTimeout = this.getConfig().getInt("appReadyTimeout", 10000);
|
|
192
346
|
this.keepUrlPathAfterReload = this.getConfig().getBoolean("keepUrlPathAfterReload", false);
|
|
347
|
+
this.syncKeepUrlPathFlag(this.keepUrlPathAfterReload);
|
|
348
|
+
this.allowManualBundleError = this.getConfig().getBoolean("allowManualBundleError", false);
|
|
349
|
+
this.autoSplashscreen = this.getConfig().getBoolean("autoSplashscreen", false);
|
|
350
|
+
this.autoSplashscreenLoader = this.getConfig().getBoolean("autoSplashscreenLoader", false);
|
|
351
|
+
int splashscreenTimeoutValue = this.getConfig().getInt("autoSplashscreenTimeout", 10000);
|
|
352
|
+
this.autoSplashscreenTimeout = Math.max(0, splashscreenTimeoutValue);
|
|
193
353
|
this.implementation.timeout = this.getConfig().getInt("responseTimeout", 20) * 1000;
|
|
354
|
+
this.shakeMenuEnabled = this.getConfig().getBoolean("shakeMenu", false);
|
|
194
355
|
boolean resetWhenUpdate = this.getConfig().getBoolean("resetWhenUpdate", true);
|
|
195
356
|
|
|
357
|
+
// Check if app was recently installed/updated BEFORE cleanupObsoleteVersions updates LatestVersionNative
|
|
358
|
+
this.wasRecentlyInstalledOrUpdated = this.checkIfRecentlyInstalledOrUpdated();
|
|
359
|
+
|
|
196
360
|
this.implementation.autoReset();
|
|
197
361
|
if (resetWhenUpdate) {
|
|
198
362
|
this.cleanupObsoleteVersions();
|
|
199
363
|
}
|
|
200
364
|
|
|
201
|
-
// Check for 'kill' delay condition on app launch
|
|
202
|
-
// This handles cases where the app was killed by the system (onDestroy is not reliable)
|
|
203
|
-
this._checkCancelDelay(true);
|
|
204
|
-
|
|
205
365
|
this.checkForUpdateAfterDelay();
|
|
206
366
|
}
|
|
207
367
|
|
|
208
368
|
private void semaphoreWait(Number waitTime) {
|
|
209
|
-
Log.i(CapacitorUpdater.TAG, "semaphoreWait " + waitTime);
|
|
210
369
|
try {
|
|
211
|
-
// Log.i(CapacitorUpdater.TAG, "semaphoreReady count " + CapacitorUpdaterPlugin.this.semaphoreReady.getCount());
|
|
212
370
|
semaphoreReady.awaitAdvanceInterruptibly(semaphoreReady.getPhase(), waitTime.longValue(), TimeUnit.SECONDS);
|
|
213
|
-
|
|
214
|
-
Log.i(CapacitorUpdater.TAG, "semaphoreReady count " + semaphoreReady.getPhase());
|
|
371
|
+
logger.info("semaphoreReady count " + semaphoreReady.getPhase());
|
|
215
372
|
} catch (InterruptedException e) {
|
|
216
|
-
|
|
217
|
-
|
|
373
|
+
logger.info("semaphoreWait InterruptedException");
|
|
374
|
+
Thread.currentThread().interrupt(); // Restore interrupted status
|
|
218
375
|
} catch (TimeoutException e) {
|
|
219
|
-
|
|
376
|
+
logger.error("Semaphore timeout: " + e.getMessage());
|
|
377
|
+
// Don't throw runtime exception, just log and continue
|
|
220
378
|
}
|
|
221
379
|
}
|
|
222
380
|
|
|
223
381
|
private void semaphoreUp() {
|
|
224
|
-
|
|
382
|
+
logger.info("semaphoreUp");
|
|
225
383
|
semaphoreReady.register();
|
|
226
384
|
}
|
|
227
385
|
|
|
228
386
|
private void semaphoreDown() {
|
|
229
|
-
|
|
230
|
-
|
|
387
|
+
logger.info("semaphoreDown");
|
|
388
|
+
logger.info("semaphoreDown count " + semaphoreReady.getPhase());
|
|
231
389
|
semaphoreReady.arriveAndDeregister();
|
|
232
390
|
}
|
|
233
391
|
|
|
234
392
|
private void sendReadyToJs(final BundleInfo current, final String msg) {
|
|
235
|
-
|
|
393
|
+
sendReadyToJs(current, msg, false);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
private void sendReadyToJs(final BundleInfo current, final String msg, final boolean isDirectUpdate) {
|
|
397
|
+
logger.info("sendReadyToJs: " + msg);
|
|
236
398
|
final JSObject ret = new JSObject();
|
|
237
|
-
ret.put("bundle", current.
|
|
399
|
+
ret.put("bundle", mapToJSObject(current.toJSONMap()));
|
|
238
400
|
ret.put("status", msg);
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
401
|
+
|
|
402
|
+
// No need to wait for semaphore anymore since _reload() has already waited
|
|
403
|
+
this.notifyListeners("appReady", ret, true);
|
|
404
|
+
|
|
405
|
+
// Auto hide splashscreen if enabled
|
|
406
|
+
// We show it on background when conditions are met, so we should hide it on foreground regardless of update outcome
|
|
407
|
+
if (this.autoSplashscreen) {
|
|
408
|
+
this.hideSplashscreen();
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
private void hideSplashscreen() {
|
|
413
|
+
if (Looper.myLooper() == Looper.getMainLooper()) {
|
|
414
|
+
hideSplashscreenInternal();
|
|
415
|
+
} else {
|
|
416
|
+
this.mainHandler.post(this::hideSplashscreenInternal);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
private void hideSplashscreenInternal() {
|
|
421
|
+
cancelSplashscreenTimeout();
|
|
422
|
+
removeSplashscreenLoader();
|
|
423
|
+
|
|
424
|
+
try {
|
|
425
|
+
if (getBridge() == null) {
|
|
426
|
+
logger.warn("Bridge not ready for hiding splashscreen with autoSplashscreen");
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Try to call the SplashScreen plugin directly through the bridge
|
|
431
|
+
PluginHandle splashScreenPlugin = getBridge().getPlugin("SplashScreen");
|
|
432
|
+
if (splashScreenPlugin != null) {
|
|
433
|
+
try {
|
|
434
|
+
// Create a plugin call for the hide method using reflection to access private msgHandler
|
|
435
|
+
JSObject options = new JSObject();
|
|
436
|
+
java.lang.reflect.Field msgHandlerField = getBridge().getClass().getDeclaredField("msgHandler");
|
|
437
|
+
msgHandlerField.setAccessible(true);
|
|
438
|
+
Object msgHandler = msgHandlerField.get(getBridge());
|
|
439
|
+
|
|
440
|
+
PluginCall call = new PluginCall(
|
|
441
|
+
(com.getcapacitor.MessageHandler) msgHandler,
|
|
442
|
+
"SplashScreen",
|
|
443
|
+
"FAKE_CALLBACK_ID_HIDE",
|
|
444
|
+
"hide",
|
|
445
|
+
options
|
|
446
|
+
);
|
|
447
|
+
|
|
448
|
+
// Call the hide method directly
|
|
449
|
+
splashScreenPlugin.invoke("hide", call);
|
|
450
|
+
logger.info("Splashscreen hidden automatically via direct plugin call");
|
|
451
|
+
} catch (Exception e) {
|
|
452
|
+
logger.error("Failed to call SplashScreen hide method: " + e.getMessage());
|
|
453
|
+
}
|
|
454
|
+
} else {
|
|
455
|
+
logger.warn("autoSplashscreen: SplashScreen plugin not found. Install @capacitor/splash-screen plugin.");
|
|
456
|
+
}
|
|
457
|
+
} catch (Exception e) {
|
|
458
|
+
logger.error(
|
|
459
|
+
"Error hiding splashscreen with autoSplashscreen: " +
|
|
460
|
+
e.getMessage() +
|
|
461
|
+
". Make sure @capacitor/splash-screen plugin is installed and configured."
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
private void showSplashscreen() {
|
|
467
|
+
if (Looper.myLooper() == Looper.getMainLooper()) {
|
|
468
|
+
showSplashscreenNow();
|
|
469
|
+
} else {
|
|
470
|
+
this.mainHandler.post(this::showSplashscreenNow);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
private void showSplashscreenNow() {
|
|
475
|
+
cancelSplashscreenTimeout();
|
|
476
|
+
this.autoSplashscreenTimedOut = false;
|
|
477
|
+
|
|
478
|
+
try {
|
|
479
|
+
if (getBridge() == null) {
|
|
480
|
+
logger.warn("Bridge not ready for showing splashscreen with autoSplashscreen");
|
|
481
|
+
} else {
|
|
482
|
+
PluginHandle splashScreenPlugin = getBridge().getPlugin("SplashScreen");
|
|
483
|
+
if (splashScreenPlugin != null) {
|
|
484
|
+
JSObject options = new JSObject();
|
|
485
|
+
java.lang.reflect.Field msgHandlerField = getBridge().getClass().getDeclaredField("msgHandler");
|
|
486
|
+
msgHandlerField.setAccessible(true);
|
|
487
|
+
Object msgHandler = msgHandlerField.get(getBridge());
|
|
488
|
+
|
|
489
|
+
PluginCall call = new PluginCall(
|
|
490
|
+
(com.getcapacitor.MessageHandler) msgHandler,
|
|
491
|
+
"SplashScreen",
|
|
492
|
+
"FAKE_CALLBACK_ID_SHOW",
|
|
493
|
+
"show",
|
|
494
|
+
options
|
|
495
|
+
);
|
|
496
|
+
|
|
497
|
+
splashScreenPlugin.invoke("show", call);
|
|
498
|
+
logger.info("Splashscreen shown synchronously to prevent flash");
|
|
499
|
+
} else {
|
|
500
|
+
logger.warn("autoSplashscreen: SplashScreen plugin not found");
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
} catch (Exception e) {
|
|
504
|
+
logger.error("Failed to show splashscreen synchronously: " + e.getMessage());
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
addSplashscreenLoaderIfNeeded();
|
|
508
|
+
scheduleSplashscreenTimeout();
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
private void addSplashscreenLoaderIfNeeded() {
|
|
512
|
+
if (!Boolean.TRUE.equals(this.autoSplashscreenLoader)) {
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
Runnable addLoader = () -> {
|
|
517
|
+
if (this.splashscreenLoaderOverlay != null) {
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
Activity activity = getActivity();
|
|
522
|
+
if (activity == null) {
|
|
523
|
+
logger.warn("autoSplashscreen: Activity not available for loader overlay");
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
ProgressBar progressBar = new ProgressBar(activity);
|
|
528
|
+
progressBar.setIndeterminate(true);
|
|
529
|
+
|
|
530
|
+
FrameLayout overlay = new FrameLayout(activity);
|
|
531
|
+
overlay.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
|
532
|
+
overlay.setClickable(false);
|
|
533
|
+
overlay.setFocusable(false);
|
|
534
|
+
overlay.setBackgroundColor(Color.TRANSPARENT);
|
|
535
|
+
overlay.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
|
536
|
+
|
|
537
|
+
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
|
|
538
|
+
ViewGroup.LayoutParams.WRAP_CONTENT,
|
|
539
|
+
ViewGroup.LayoutParams.WRAP_CONTENT
|
|
540
|
+
);
|
|
541
|
+
params.gravity = Gravity.CENTER;
|
|
542
|
+
overlay.addView(progressBar, params);
|
|
543
|
+
|
|
544
|
+
ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
|
|
545
|
+
decorView.addView(overlay);
|
|
546
|
+
|
|
547
|
+
this.splashscreenLoaderOverlay = overlay;
|
|
548
|
+
};
|
|
549
|
+
|
|
550
|
+
if (Looper.myLooper() == Looper.getMainLooper()) {
|
|
551
|
+
addLoader.run();
|
|
552
|
+
} else {
|
|
553
|
+
this.mainHandler.post(addLoader);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
private void removeSplashscreenLoader() {
|
|
558
|
+
Runnable removeLoader = () -> {
|
|
559
|
+
if (this.splashscreenLoaderOverlay != null) {
|
|
560
|
+
ViewGroup parent = (ViewGroup) this.splashscreenLoaderOverlay.getParent();
|
|
561
|
+
if (parent != null) {
|
|
562
|
+
parent.removeView(this.splashscreenLoaderOverlay);
|
|
563
|
+
}
|
|
564
|
+
this.splashscreenLoaderOverlay = null;
|
|
565
|
+
}
|
|
566
|
+
};
|
|
567
|
+
|
|
568
|
+
if (Looper.myLooper() == Looper.getMainLooper()) {
|
|
569
|
+
removeLoader.run();
|
|
570
|
+
} else {
|
|
571
|
+
this.mainHandler.post(removeLoader);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
private void scheduleSplashscreenTimeout() {
|
|
576
|
+
if (this.autoSplashscreenTimeout == null || this.autoSplashscreenTimeout <= 0) {
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
cancelSplashscreenTimeout();
|
|
581
|
+
|
|
582
|
+
this.splashscreenTimeoutRunnable = () -> {
|
|
583
|
+
logger.info("autoSplashscreen timeout reached, hiding splashscreen");
|
|
584
|
+
this.autoSplashscreenTimedOut = true;
|
|
585
|
+
this.implementation.directUpdate = false;
|
|
586
|
+
hideSplashscreen();
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
this.mainHandler.postDelayed(this.splashscreenTimeoutRunnable, this.autoSplashscreenTimeout);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
private void cancelSplashscreenTimeout() {
|
|
593
|
+
if (this.splashscreenTimeoutRunnable != null) {
|
|
594
|
+
this.mainHandler.removeCallbacks(this.splashscreenTimeoutRunnable);
|
|
595
|
+
this.splashscreenTimeoutRunnable = null;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
private boolean checkIfRecentlyInstalledOrUpdated() {
|
|
600
|
+
String currentVersion = this.currentBuildVersion;
|
|
601
|
+
String lastKnownVersion = this.prefs.getString("LatestNativeBuildVersion", "");
|
|
602
|
+
|
|
603
|
+
if (lastKnownVersion.isEmpty()) {
|
|
604
|
+
// First time running, consider it as recently installed
|
|
605
|
+
return true;
|
|
606
|
+
} else if (!lastKnownVersion.equals(currentVersion)) {
|
|
607
|
+
// Version changed, consider it as recently updated
|
|
608
|
+
return true;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
return false;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
private boolean shouldUseDirectUpdate() {
|
|
615
|
+
if (Boolean.TRUE.equals(this.autoSplashscreenTimedOut)) {
|
|
616
|
+
return false;
|
|
617
|
+
}
|
|
618
|
+
switch (this.directUpdateMode) {
|
|
619
|
+
case "false":
|
|
620
|
+
return false;
|
|
621
|
+
case "always":
|
|
622
|
+
return true;
|
|
623
|
+
case "atInstall":
|
|
624
|
+
if (this.wasRecentlyInstalledOrUpdated) {
|
|
625
|
+
// Reset the flag after first use to prevent subsequent foreground events from using direct update
|
|
626
|
+
this.wasRecentlyInstalledOrUpdated = false;
|
|
627
|
+
return true;
|
|
628
|
+
}
|
|
629
|
+
return false;
|
|
630
|
+
default:
|
|
631
|
+
return false;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
private boolean isDirectUpdateCurrentlyAllowed(final boolean plannedDirectUpdate) {
|
|
636
|
+
return plannedDirectUpdate && !Boolean.TRUE.equals(this.autoSplashscreenTimedOut);
|
|
245
637
|
}
|
|
246
638
|
|
|
247
639
|
private void directUpdateFinish(final BundleInfo latest) {
|
|
248
640
|
CapacitorUpdaterPlugin.this.implementation.set(latest);
|
|
249
641
|
CapacitorUpdaterPlugin.this._reload();
|
|
250
|
-
sendReadyToJs(latest, "update installed");
|
|
642
|
+
sendReadyToJs(latest, "update installed", true);
|
|
251
643
|
}
|
|
252
644
|
|
|
253
645
|
private void cleanupObsoleteVersions() {
|
|
254
|
-
|
|
255
|
-
final Version previous = new Version(this.prefs.getString("LatestVersionNative", ""));
|
|
646
|
+
startNewThread(() -> {
|
|
256
647
|
try {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
) {
|
|
261
|
-
Log.i(CapacitorUpdater.TAG, "New native version detected: " + this.currentVersionNative);
|
|
648
|
+
final String previous = this.prefs.getString("LatestNativeBuildVersion", "");
|
|
649
|
+
if (!"".equals(previous) && !Objects.equals(this.currentBuildVersion, previous)) {
|
|
650
|
+
logger.info("New native build version detected: " + this.currentBuildVersion);
|
|
262
651
|
this.implementation.reset(true);
|
|
263
652
|
final List<BundleInfo> installed = this.implementation.list(false);
|
|
264
653
|
for (final BundleInfo bundle : installed) {
|
|
265
654
|
try {
|
|
266
|
-
|
|
655
|
+
logger.info("Deleting obsolete bundle: " + bundle.getId());
|
|
267
656
|
this.implementation.delete(bundle.getId());
|
|
268
657
|
} catch (final Exception e) {
|
|
269
|
-
|
|
658
|
+
logger.error("Failed to delete: " + bundle.getId() + " " + e.getMessage());
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
final List<BundleInfo> storedBundles = this.implementation.list(true);
|
|
662
|
+
final Set<String> allowedIds = new HashSet<>();
|
|
663
|
+
for (final BundleInfo info : storedBundles) {
|
|
664
|
+
if (info != null && info.getId() != null && !info.getId().isEmpty()) {
|
|
665
|
+
allowedIds.add(info.getId());
|
|
270
666
|
}
|
|
271
667
|
}
|
|
668
|
+
this.implementation.cleanupDownloadDirectories(allowedIds);
|
|
272
669
|
}
|
|
273
|
-
|
|
274
|
-
|
|
670
|
+
this.editor.putString("LatestNativeBuildVersion", this.currentBuildVersion);
|
|
671
|
+
this.editor.apply();
|
|
672
|
+
} catch (Exception e) {
|
|
673
|
+
logger.error("Error during cleanupObsoleteVersions: " + e.getMessage());
|
|
275
674
|
}
|
|
276
|
-
}
|
|
277
|
-
Log.e(CapacitorUpdater.TAG, "Error calculating previous native version", e);
|
|
278
|
-
}
|
|
279
|
-
this.editor.putString("LatestVersionNative", this.currentVersionNative.toString());
|
|
280
|
-
this.editor.commit();
|
|
675
|
+
});
|
|
281
676
|
}
|
|
282
677
|
|
|
283
678
|
public void notifyDownload(final String id, final int percent) {
|
|
@@ -285,7 +680,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
285
680
|
final JSObject ret = new JSObject();
|
|
286
681
|
ret.put("percent", percent);
|
|
287
682
|
final BundleInfo bundleInfo = this.implementation.getBundleInfo(id);
|
|
288
|
-
ret.put("bundle", bundleInfo.
|
|
683
|
+
ret.put("bundle", mapToJSObject(bundleInfo.toJSONMap()));
|
|
289
684
|
this.notifyListeners("download", ret);
|
|
290
685
|
|
|
291
686
|
if (percent == 100) {
|
|
@@ -301,58 +696,70 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
301
696
|
}
|
|
302
697
|
}
|
|
303
698
|
} catch (final Exception e) {
|
|
304
|
-
|
|
699
|
+
logger.error("Could not notify listeners " + e.getMessage());
|
|
305
700
|
}
|
|
306
701
|
}
|
|
307
702
|
|
|
308
703
|
@PluginMethod
|
|
309
704
|
public void setUpdateUrl(final PluginCall call) {
|
|
310
705
|
if (!this.getConfig().getBoolean("allowModifyUrl", false)) {
|
|
311
|
-
|
|
706
|
+
logger.error("setUpdateUrl not allowed set allowModifyUrl in your config to true to allow it");
|
|
312
707
|
call.reject("setUpdateUrl not allowed");
|
|
313
708
|
return;
|
|
314
709
|
}
|
|
315
710
|
final String url = call.getString("url");
|
|
316
711
|
if (url == null) {
|
|
317
|
-
|
|
712
|
+
logger.error("setUpdateUrl called without url");
|
|
318
713
|
call.reject("setUpdateUrl called without url");
|
|
319
714
|
return;
|
|
320
715
|
}
|
|
321
716
|
this.updateUrl = url;
|
|
717
|
+
if (Boolean.TRUE.equals(this.persistModifyUrl)) {
|
|
718
|
+
this.editor.putString(UPDATE_URL_PREF_KEY, url);
|
|
719
|
+
this.editor.apply();
|
|
720
|
+
}
|
|
322
721
|
call.resolve();
|
|
323
722
|
}
|
|
324
723
|
|
|
325
724
|
@PluginMethod
|
|
326
725
|
public void setStatsUrl(final PluginCall call) {
|
|
327
726
|
if (!this.getConfig().getBoolean("allowModifyUrl", false)) {
|
|
328
|
-
|
|
727
|
+
logger.error("setStatsUrl not allowed set allowModifyUrl in your config to true to allow it");
|
|
329
728
|
call.reject("setStatsUrl not allowed");
|
|
330
729
|
return;
|
|
331
730
|
}
|
|
332
731
|
final String url = call.getString("url");
|
|
333
732
|
if (url == null) {
|
|
334
|
-
|
|
733
|
+
logger.error("setStatsUrl called without url");
|
|
335
734
|
call.reject("setStatsUrl called without url");
|
|
336
735
|
return;
|
|
337
736
|
}
|
|
338
737
|
this.implementation.statsUrl = url;
|
|
738
|
+
if (Boolean.TRUE.equals(this.persistModifyUrl)) {
|
|
739
|
+
this.editor.putString(STATS_URL_PREF_KEY, url);
|
|
740
|
+
this.editor.apply();
|
|
741
|
+
}
|
|
339
742
|
call.resolve();
|
|
340
743
|
}
|
|
341
744
|
|
|
342
745
|
@PluginMethod
|
|
343
746
|
public void setChannelUrl(final PluginCall call) {
|
|
344
747
|
if (!this.getConfig().getBoolean("allowModifyUrl", false)) {
|
|
345
|
-
|
|
748
|
+
logger.error("setChannelUrl not allowed set allowModifyUrl in your config to true to allow it");
|
|
346
749
|
call.reject("setChannelUrl not allowed");
|
|
347
750
|
return;
|
|
348
751
|
}
|
|
349
752
|
final String url = call.getString("url");
|
|
350
753
|
if (url == null) {
|
|
351
|
-
|
|
754
|
+
logger.error("setChannelUrl called without url");
|
|
352
755
|
call.reject("setChannelUrl called without url");
|
|
353
756
|
return;
|
|
354
757
|
}
|
|
355
758
|
this.implementation.channelUrl = url;
|
|
759
|
+
if (Boolean.TRUE.equals(this.persistModifyUrl)) {
|
|
760
|
+
this.editor.putString(CHANNEL_URL_PREF_KEY, url);
|
|
761
|
+
this.editor.apply();
|
|
762
|
+
}
|
|
356
763
|
call.resolve();
|
|
357
764
|
}
|
|
358
765
|
|
|
@@ -363,7 +770,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
363
770
|
ret.put("version", this.implementation.versionBuild);
|
|
364
771
|
call.resolve(ret);
|
|
365
772
|
} catch (final Exception e) {
|
|
366
|
-
|
|
773
|
+
logger.error("Could not get version " + e.getMessage());
|
|
367
774
|
call.reject("Could not get version", e);
|
|
368
775
|
}
|
|
369
776
|
}
|
|
@@ -375,7 +782,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
375
782
|
ret.put("deviceId", this.implementation.deviceID);
|
|
376
783
|
call.resolve(ret);
|
|
377
784
|
} catch (final Exception e) {
|
|
378
|
-
|
|
785
|
+
logger.error("Could not get device id " + e.getMessage());
|
|
379
786
|
call.reject("Could not get device id", e);
|
|
380
787
|
}
|
|
381
788
|
}
|
|
@@ -384,11 +791,20 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
384
791
|
public void setCustomId(final PluginCall call) {
|
|
385
792
|
final String customId = call.getString("customId");
|
|
386
793
|
if (customId == null) {
|
|
387
|
-
|
|
794
|
+
logger.error("setCustomId called without customId");
|
|
388
795
|
call.reject("setCustomId called without customId");
|
|
389
796
|
return;
|
|
390
797
|
}
|
|
391
798
|
this.implementation.customId = customId;
|
|
799
|
+
if (Boolean.TRUE.equals(this.persistCustomId)) {
|
|
800
|
+
if (customId.isEmpty()) {
|
|
801
|
+
this.editor.remove(CUSTOM_ID_PREF_KEY);
|
|
802
|
+
} else {
|
|
803
|
+
this.editor.putString(CUSTOM_ID_PREF_KEY, customId);
|
|
804
|
+
}
|
|
805
|
+
this.editor.apply();
|
|
806
|
+
}
|
|
807
|
+
call.resolve();
|
|
392
808
|
}
|
|
393
809
|
|
|
394
810
|
@PluginMethod
|
|
@@ -398,7 +814,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
398
814
|
ret.put("version", this.PLUGIN_VERSION);
|
|
399
815
|
call.resolve(ret);
|
|
400
816
|
} catch (final Exception e) {
|
|
401
|
-
|
|
817
|
+
logger.error("Could not get plugin version " + e.getMessage());
|
|
402
818
|
call.reject("Could not get plugin version", e);
|
|
403
819
|
}
|
|
404
820
|
}
|
|
@@ -408,22 +824,30 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
408
824
|
final Boolean triggerAutoUpdate = call.getBoolean("triggerAutoUpdate", false);
|
|
409
825
|
|
|
410
826
|
try {
|
|
411
|
-
|
|
827
|
+
logger.info("unsetChannel triggerAutoUpdate: " + triggerAutoUpdate);
|
|
412
828
|
startNewThread(() ->
|
|
413
|
-
CapacitorUpdaterPlugin.this.implementation.unsetChannel(res -> {
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
829
|
+
CapacitorUpdaterPlugin.this.implementation.unsetChannel((res) -> {
|
|
830
|
+
JSObject jsRes = mapToJSObject(res);
|
|
831
|
+
if (jsRes.has("error")) {
|
|
832
|
+
String errorMessage = jsRes.has("message") ? jsRes.getString("message") : jsRes.getString("error");
|
|
833
|
+
String errorCode = jsRes.getString("error");
|
|
834
|
+
|
|
835
|
+
JSObject errorObj = new JSObject();
|
|
836
|
+
errorObj.put("message", errorMessage);
|
|
837
|
+
errorObj.put("error", errorCode);
|
|
838
|
+
|
|
839
|
+
call.reject(errorMessage, "UNSETCHANNEL_FAILED", null, errorObj);
|
|
840
|
+
} else {
|
|
841
|
+
if (CapacitorUpdaterPlugin.this._isAutoUpdateEnabled() && Boolean.TRUE.equals(triggerAutoUpdate)) {
|
|
842
|
+
logger.info("Calling autoupdater after channel change!");
|
|
843
|
+
backgroundDownload();
|
|
422
844
|
}
|
|
423
|
-
|
|
845
|
+
call.resolve(jsRes);
|
|
846
|
+
}
|
|
847
|
+
})
|
|
424
848
|
);
|
|
425
849
|
} catch (final Exception e) {
|
|
426
|
-
|
|
850
|
+
logger.error("Failed to unsetChannel: " + e.getMessage());
|
|
427
851
|
call.reject("Failed to unsetChannel: ", e);
|
|
428
852
|
}
|
|
429
853
|
}
|
|
@@ -434,27 +858,38 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
434
858
|
final Boolean triggerAutoUpdate = call.getBoolean("triggerAutoUpdate", false);
|
|
435
859
|
|
|
436
860
|
if (channel == null) {
|
|
437
|
-
|
|
438
|
-
|
|
861
|
+
logger.error("setChannel called without channel");
|
|
862
|
+
JSObject errorObj = new JSObject();
|
|
863
|
+
errorObj.put("message", "setChannel called without channel");
|
|
864
|
+
errorObj.put("error", "missing_parameter");
|
|
865
|
+
call.reject("setChannel called without channel", "SETCHANNEL_INVALID_PARAMS", null, errorObj);
|
|
439
866
|
return;
|
|
440
867
|
}
|
|
441
868
|
try {
|
|
442
|
-
|
|
869
|
+
logger.info("setChannel " + channel + " triggerAutoUpdate: " + triggerAutoUpdate);
|
|
443
870
|
startNewThread(() ->
|
|
444
|
-
CapacitorUpdaterPlugin.this.implementation.setChannel(channel, res -> {
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
871
|
+
CapacitorUpdaterPlugin.this.implementation.setChannel(channel, (res) -> {
|
|
872
|
+
JSObject jsRes = mapToJSObject(res);
|
|
873
|
+
if (jsRes.has("error")) {
|
|
874
|
+
String errorMessage = jsRes.has("message") ? jsRes.getString("message") : jsRes.getString("error");
|
|
875
|
+
String errorCode = jsRes.getString("error");
|
|
876
|
+
|
|
877
|
+
JSObject errorObj = new JSObject();
|
|
878
|
+
errorObj.put("message", errorMessage);
|
|
879
|
+
errorObj.put("error", errorCode);
|
|
880
|
+
|
|
881
|
+
call.reject(errorMessage, "SETCHANNEL_FAILED", null, errorObj);
|
|
882
|
+
} else {
|
|
883
|
+
if (CapacitorUpdaterPlugin.this._isAutoUpdateEnabled() && Boolean.TRUE.equals(triggerAutoUpdate)) {
|
|
884
|
+
logger.info("Calling autoupdater after channel change!");
|
|
885
|
+
backgroundDownload();
|
|
453
886
|
}
|
|
454
|
-
|
|
887
|
+
call.resolve(jsRes);
|
|
888
|
+
}
|
|
889
|
+
})
|
|
455
890
|
);
|
|
456
891
|
} catch (final Exception e) {
|
|
457
|
-
|
|
892
|
+
logger.error("Failed to setChannel: " + channel + " " + e.getMessage());
|
|
458
893
|
call.reject("Failed to setChannel: " + channel, e);
|
|
459
894
|
}
|
|
460
895
|
}
|
|
@@ -462,50 +897,98 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
462
897
|
@PluginMethod
|
|
463
898
|
public void getChannel(final PluginCall call) {
|
|
464
899
|
try {
|
|
465
|
-
|
|
900
|
+
logger.info("getChannel");
|
|
466
901
|
startNewThread(() ->
|
|
467
|
-
CapacitorUpdaterPlugin.this.implementation.getChannel(res -> {
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
902
|
+
CapacitorUpdaterPlugin.this.implementation.getChannel((res) -> {
|
|
903
|
+
JSObject jsRes = mapToJSObject(res);
|
|
904
|
+
if (jsRes.has("error")) {
|
|
905
|
+
String errorMessage = jsRes.has("message") ? jsRes.getString("message") : jsRes.getString("error");
|
|
906
|
+
String errorCode = jsRes.getString("error");
|
|
907
|
+
|
|
908
|
+
JSObject errorObj = new JSObject();
|
|
909
|
+
errorObj.put("message", errorMessage);
|
|
910
|
+
errorObj.put("error", errorCode);
|
|
911
|
+
|
|
912
|
+
call.reject(errorMessage, "GETCHANNEL_FAILED", null, errorObj);
|
|
913
|
+
} else {
|
|
914
|
+
call.resolve(jsRes);
|
|
915
|
+
}
|
|
916
|
+
})
|
|
474
917
|
);
|
|
475
918
|
} catch (final Exception e) {
|
|
476
|
-
|
|
919
|
+
logger.error("Failed to getChannel " + e.getMessage());
|
|
477
920
|
call.reject("Failed to getChannel", e);
|
|
478
921
|
}
|
|
479
922
|
}
|
|
480
923
|
|
|
924
|
+
@PluginMethod
|
|
925
|
+
public void listChannels(final PluginCall call) {
|
|
926
|
+
try {
|
|
927
|
+
logger.info("listChannels");
|
|
928
|
+
startNewThread(() ->
|
|
929
|
+
CapacitorUpdaterPlugin.this.implementation.listChannels((res) -> {
|
|
930
|
+
JSObject jsRes = mapToJSObject(res);
|
|
931
|
+
if (jsRes.has("error")) {
|
|
932
|
+
String errorMessage = jsRes.has("message") ? jsRes.getString("message") : jsRes.getString("error");
|
|
933
|
+
String errorCode = jsRes.getString("error");
|
|
934
|
+
|
|
935
|
+
JSObject errorObj = new JSObject();
|
|
936
|
+
errorObj.put("message", errorMessage);
|
|
937
|
+
errorObj.put("error", errorCode);
|
|
938
|
+
|
|
939
|
+
call.reject(errorMessage, "LISTCHANNELS_FAILED", null, errorObj);
|
|
940
|
+
} else {
|
|
941
|
+
call.resolve(jsRes);
|
|
942
|
+
}
|
|
943
|
+
})
|
|
944
|
+
);
|
|
945
|
+
} catch (final Exception e) {
|
|
946
|
+
logger.error("Failed to listChannels: " + e.getMessage());
|
|
947
|
+
call.reject("Failed to listChannels", e);
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
|
|
481
951
|
@PluginMethod
|
|
482
952
|
public void download(final PluginCall call) {
|
|
483
953
|
final String url = call.getString("url");
|
|
484
954
|
final String version = call.getString("version");
|
|
485
955
|
final String sessionKey = call.getString("sessionKey", "");
|
|
486
956
|
final String checksum = call.getString("checksum", "");
|
|
957
|
+
final JSONArray manifest = call.getData().optJSONArray("manifest");
|
|
487
958
|
if (url == null) {
|
|
488
|
-
|
|
959
|
+
logger.error("Download called without url");
|
|
489
960
|
call.reject("Download called without url");
|
|
490
961
|
return;
|
|
491
962
|
}
|
|
492
963
|
if (version == null) {
|
|
493
|
-
|
|
964
|
+
logger.error("Download called without version");
|
|
494
965
|
call.reject("Download called without version");
|
|
495
966
|
return;
|
|
496
967
|
}
|
|
497
968
|
try {
|
|
498
|
-
|
|
969
|
+
logger.info("Downloading " + url);
|
|
499
970
|
startNewThread(() -> {
|
|
500
971
|
try {
|
|
501
|
-
final BundleInfo downloaded
|
|
972
|
+
final BundleInfo downloaded;
|
|
973
|
+
if (manifest != null) {
|
|
974
|
+
// For manifest downloads, we need to handle this asynchronously
|
|
975
|
+
// since there's no synchronous downloadManifest method in Java
|
|
976
|
+
CapacitorUpdaterPlugin.this.implementation.downloadBackground(url, version, sessionKey, checksum, manifest);
|
|
977
|
+
// Return immediately with a pending status - the actual result will come via listeners
|
|
978
|
+
final String id = CapacitorUpdaterPlugin.this.implementation.randomString();
|
|
979
|
+
downloaded = new BundleInfo(id, version, BundleStatus.DOWNLOADING, new Date(System.currentTimeMillis()), "");
|
|
980
|
+
call.resolve(mapToJSObject(downloaded.toJSONMap()));
|
|
981
|
+
return;
|
|
982
|
+
} else {
|
|
983
|
+
downloaded = CapacitorUpdaterPlugin.this.implementation.download(url, version, sessionKey, checksum);
|
|
984
|
+
}
|
|
502
985
|
if (downloaded.isErrorStatus()) {
|
|
503
986
|
throw new RuntimeException("Download failed: " + downloaded.getStatus());
|
|
504
987
|
} else {
|
|
505
|
-
call.resolve(downloaded.
|
|
988
|
+
call.resolve(mapToJSObject(downloaded.toJSONMap()));
|
|
506
989
|
}
|
|
507
990
|
} catch (final Exception e) {
|
|
508
|
-
|
|
991
|
+
logger.error("Failed to download from: " + url + " " + e.getMessage());
|
|
509
992
|
call.reject("Failed to download from: " + url, e);
|
|
510
993
|
final JSObject ret = new JSObject();
|
|
511
994
|
ret.put("version", version);
|
|
@@ -515,7 +998,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
515
998
|
}
|
|
516
999
|
});
|
|
517
1000
|
} catch (final Exception e) {
|
|
518
|
-
|
|
1001
|
+
logger.error("Failed to download from: " + url + " " + e.getMessage());
|
|
519
1002
|
call.reject("Failed to download from: " + url, e);
|
|
520
1003
|
final JSObject ret = new JSObject();
|
|
521
1004
|
ret.put("version", version);
|
|
@@ -525,10 +1008,27 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
525
1008
|
}
|
|
526
1009
|
}
|
|
527
1010
|
|
|
1011
|
+
private void syncKeepUrlPathFlag(final boolean enabled) {
|
|
1012
|
+
if (this.bridge == null || this.bridge.getWebView() == null) {
|
|
1013
|
+
return;
|
|
1014
|
+
}
|
|
1015
|
+
final String script = enabled
|
|
1016
|
+
? "(function(){try{localStorage.setItem('" +
|
|
1017
|
+
KEEP_URL_FLAG_KEY +
|
|
1018
|
+
"','1');}catch(e){}window.__capgoKeepUrlPathAfterReload=true;var evt;try{evt=new CustomEvent('CapacitorUpdaterKeepUrlPathAfterReload',{detail:{enabled:true}});}catch(err){evt=document.createEvent('CustomEvent');evt.initCustomEvent('CapacitorUpdaterKeepUrlPathAfterReload',false,false,{enabled:true});}window.dispatchEvent(evt);})();"
|
|
1019
|
+
: "(function(){try{localStorage.removeItem('" +
|
|
1020
|
+
KEEP_URL_FLAG_KEY +
|
|
1021
|
+
"');}catch(e){}delete window.__capgoKeepUrlPathAfterReload;var evt;try{evt=new CustomEvent('CapacitorUpdaterKeepUrlPathAfterReload',{detail:{enabled:false}});}catch(err){evt=document.createEvent('CustomEvent');evt.initCustomEvent('CapacitorUpdaterKeepUrlPathAfterReload',false,false,{enabled:false});}window.dispatchEvent(evt);})();";
|
|
1022
|
+
this.bridge.getWebView().post(() -> this.bridge.getWebView().evaluateJavascript(script, null));
|
|
1023
|
+
}
|
|
1024
|
+
|
|
528
1025
|
protected boolean _reload() {
|
|
529
1026
|
final String path = this.implementation.getCurrentBundlePath();
|
|
1027
|
+
if (this.keepUrlPathAfterReload) {
|
|
1028
|
+
this.syncKeepUrlPathFlag(true);
|
|
1029
|
+
}
|
|
530
1030
|
this.semaphoreUp();
|
|
531
|
-
|
|
1031
|
+
logger.info("Reloading: " + path);
|
|
532
1032
|
|
|
533
1033
|
AtomicReference<URL> url = new AtomicReference<>();
|
|
534
1034
|
if (this.keepUrlPathAfterReload) {
|
|
@@ -536,23 +1036,38 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
536
1036
|
if (Looper.myLooper() != Looper.getMainLooper()) {
|
|
537
1037
|
Semaphore mainThreadSemaphore = new Semaphore(0);
|
|
538
1038
|
this.bridge.executeOnMainThread(() -> {
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
1039
|
+
try {
|
|
1040
|
+
if (this.bridge != null && this.bridge.getWebView() != null) {
|
|
1041
|
+
String currentUrl = this.bridge.getWebView().getUrl();
|
|
1042
|
+
if (currentUrl != null) {
|
|
1043
|
+
url.set(new URL(currentUrl));
|
|
1044
|
+
}
|
|
543
1045
|
}
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
1046
|
+
} catch (Exception e) {
|
|
1047
|
+
logger.error("Error executing on main thread " + e.getMessage());
|
|
1048
|
+
}
|
|
1049
|
+
mainThreadSemaphore.release();
|
|
1050
|
+
});
|
|
1051
|
+
|
|
1052
|
+
// Add timeout to prevent indefinite blocking
|
|
1053
|
+
if (!mainThreadSemaphore.tryAcquire(10, TimeUnit.SECONDS)) {
|
|
1054
|
+
logger.error("Timeout waiting for main thread operation");
|
|
1055
|
+
}
|
|
547
1056
|
} else {
|
|
548
1057
|
try {
|
|
549
|
-
|
|
1058
|
+
if (this.bridge != null && this.bridge.getWebView() != null) {
|
|
1059
|
+
String currentUrl = this.bridge.getWebView().getUrl();
|
|
1060
|
+
if (currentUrl != null) {
|
|
1061
|
+
url.set(new URL(currentUrl));
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
550
1064
|
} catch (Exception e) {
|
|
551
|
-
|
|
1065
|
+
logger.error("Error executing on main thread " + e.getMessage());
|
|
552
1066
|
}
|
|
553
1067
|
}
|
|
554
1068
|
} catch (InterruptedException e) {
|
|
555
|
-
|
|
1069
|
+
logger.error("Error waiting for main thread or getting the current URL from webview " + e.getMessage());
|
|
1070
|
+
Thread.currentThread().interrupt(); // Restore interrupted status
|
|
556
1071
|
}
|
|
557
1072
|
}
|
|
558
1073
|
|
|
@@ -568,13 +1083,14 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
568
1083
|
finalUrl = new URL(this.bridge.getAppUrl());
|
|
569
1084
|
finalUrl = new URL(finalUrl.getProtocol(), finalUrl.getHost(), finalUrl.getPort(), url.get().getPath());
|
|
570
1085
|
URL finalUrl1 = finalUrl;
|
|
571
|
-
this.bridge.getWebView()
|
|
572
|
-
.
|
|
573
|
-
|
|
1086
|
+
this.bridge.getWebView().post(() -> {
|
|
1087
|
+
this.bridge.getWebView().loadUrl(finalUrl1.toString());
|
|
1088
|
+
if (!this.keepUrlPathAfterReload) {
|
|
574
1089
|
this.bridge.getWebView().clearHistory();
|
|
575
|
-
}
|
|
1090
|
+
}
|
|
1091
|
+
});
|
|
576
1092
|
} catch (MalformedURLException e) {
|
|
577
|
-
|
|
1093
|
+
logger.error("Cannot get finalUrl from capacitor bridge " + e.getMessage());
|
|
578
1094
|
|
|
579
1095
|
if (this.implementation.isUsingBuiltin()) {
|
|
580
1096
|
this.bridge.setServerAssetPath(path);
|
|
@@ -588,10 +1104,29 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
588
1104
|
} else {
|
|
589
1105
|
this.bridge.setServerBasePath(path);
|
|
590
1106
|
}
|
|
1107
|
+
if (this.bridge != null && this.bridge.getWebView() != null) {
|
|
1108
|
+
this.bridge.getWebView().post(() -> {
|
|
1109
|
+
if (this.bridge.getWebView() != null) {
|
|
1110
|
+
this.bridge.getWebView().loadUrl(this.bridge.getAppUrl());
|
|
1111
|
+
if (!this.keepUrlPathAfterReload) {
|
|
1112
|
+
this.bridge.getWebView().clearHistory();
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
});
|
|
1116
|
+
}
|
|
591
1117
|
}
|
|
592
1118
|
|
|
593
1119
|
this.checkAppReady();
|
|
594
1120
|
this.notifyListeners("appReloaded", new JSObject());
|
|
1121
|
+
|
|
1122
|
+
// Wait for the reload to complete (until notifyAppReady is called)
|
|
1123
|
+
try {
|
|
1124
|
+
this.semaphoreWait(this.appReadyTimeout);
|
|
1125
|
+
} catch (Exception e) {
|
|
1126
|
+
logger.error("Error waiting for app ready: " + e.getMessage());
|
|
1127
|
+
return false;
|
|
1128
|
+
}
|
|
1129
|
+
|
|
595
1130
|
return true;
|
|
596
1131
|
}
|
|
597
1132
|
|
|
@@ -601,11 +1136,11 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
601
1136
|
if (this._reload()) {
|
|
602
1137
|
call.resolve();
|
|
603
1138
|
} else {
|
|
604
|
-
|
|
1139
|
+
logger.error("Reload failed");
|
|
605
1140
|
call.reject("Reload failed");
|
|
606
1141
|
}
|
|
607
1142
|
} catch (final Exception e) {
|
|
608
|
-
|
|
1143
|
+
logger.error("Could not reload " + e.getMessage());
|
|
609
1144
|
call.reject("Could not reload", e);
|
|
610
1145
|
}
|
|
611
1146
|
}
|
|
@@ -614,20 +1149,20 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
614
1149
|
public void next(final PluginCall call) {
|
|
615
1150
|
final String id = call.getString("id");
|
|
616
1151
|
if (id == null) {
|
|
617
|
-
|
|
1152
|
+
logger.error("Next called without id");
|
|
618
1153
|
call.reject("Next called without id");
|
|
619
1154
|
return;
|
|
620
1155
|
}
|
|
621
1156
|
try {
|
|
622
|
-
|
|
1157
|
+
logger.info("Setting next active id " + id);
|
|
623
1158
|
if (!this.implementation.setNextBundle(id)) {
|
|
624
|
-
|
|
1159
|
+
logger.error("Set next id failed. Bundle " + id + " does not exist.");
|
|
625
1160
|
call.reject("Set next id failed. Bundle " + id + " does not exist.");
|
|
626
1161
|
} else {
|
|
627
|
-
call.resolve(this.implementation.getBundleInfo(id).
|
|
1162
|
+
call.resolve(mapToJSObject(this.implementation.getBundleInfo(id).toJSONMap()));
|
|
628
1163
|
}
|
|
629
1164
|
} catch (final Exception e) {
|
|
630
|
-
|
|
1165
|
+
logger.error("Could not set next id " + id + " " + e.getMessage());
|
|
631
1166
|
call.reject("Could not set next id: " + id, e);
|
|
632
1167
|
}
|
|
633
1168
|
}
|
|
@@ -636,21 +1171,21 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
636
1171
|
public void set(final PluginCall call) {
|
|
637
1172
|
final String id = call.getString("id");
|
|
638
1173
|
if (id == null) {
|
|
639
|
-
|
|
1174
|
+
logger.error("Set called without id");
|
|
640
1175
|
call.reject("Set called without id");
|
|
641
1176
|
return;
|
|
642
1177
|
}
|
|
643
1178
|
try {
|
|
644
|
-
|
|
1179
|
+
logger.info("Setting active bundle " + id);
|
|
645
1180
|
if (!this.implementation.set(id)) {
|
|
646
|
-
|
|
1181
|
+
logger.info("No such bundle " + id);
|
|
647
1182
|
call.reject("Update failed, id " + id + " does not exist.");
|
|
648
1183
|
} else {
|
|
649
|
-
|
|
1184
|
+
logger.info("Bundle successfully set to " + id);
|
|
650
1185
|
this.reload(call);
|
|
651
1186
|
}
|
|
652
1187
|
} catch (final Exception e) {
|
|
653
|
-
|
|
1188
|
+
logger.error("Could not set id " + id + " " + e.getMessage());
|
|
654
1189
|
call.reject("Could not set id " + id, e);
|
|
655
1190
|
}
|
|
656
1191
|
}
|
|
@@ -659,25 +1194,63 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
659
1194
|
public void delete(final PluginCall call) {
|
|
660
1195
|
final String id = call.getString("id");
|
|
661
1196
|
if (id == null) {
|
|
662
|
-
|
|
1197
|
+
logger.error("missing id");
|
|
663
1198
|
call.reject("missing id");
|
|
664
1199
|
return;
|
|
665
1200
|
}
|
|
666
|
-
|
|
1201
|
+
logger.info("Deleting id " + id);
|
|
667
1202
|
try {
|
|
668
1203
|
final Boolean res = this.implementation.delete(id);
|
|
669
1204
|
if (res) {
|
|
670
1205
|
call.resolve();
|
|
671
1206
|
} else {
|
|
672
|
-
|
|
1207
|
+
logger.error("Delete failed, id " + id + " does not exist");
|
|
673
1208
|
call.reject("Delete failed, id " + id + " does not exist or it cannot be deleted (perhaps it is the 'next' bundle)");
|
|
674
1209
|
}
|
|
675
1210
|
} catch (final Exception e) {
|
|
676
|
-
|
|
1211
|
+
logger.error("Could not delete id " + id + " " + e.getMessage());
|
|
677
1212
|
call.reject("Could not delete id " + id, e);
|
|
678
1213
|
}
|
|
679
1214
|
}
|
|
680
1215
|
|
|
1216
|
+
@PluginMethod
|
|
1217
|
+
public void setBundleError(final PluginCall call) {
|
|
1218
|
+
if (!Boolean.TRUE.equals(this.allowManualBundleError)) {
|
|
1219
|
+
logger.error("setBundleError called without allowManualBundleError");
|
|
1220
|
+
call.reject("setBundleError not allowed. Set allowManualBundleError to true in your config to enable it.");
|
|
1221
|
+
return;
|
|
1222
|
+
}
|
|
1223
|
+
final String id = call.getString("id");
|
|
1224
|
+
if (id == null) {
|
|
1225
|
+
logger.error("setBundleError called without id");
|
|
1226
|
+
call.reject("setBundleError called without id");
|
|
1227
|
+
return;
|
|
1228
|
+
}
|
|
1229
|
+
try {
|
|
1230
|
+
final BundleInfo bundle = this.implementation.getBundleInfo(id);
|
|
1231
|
+
if (bundle == null || bundle.isUnknown()) {
|
|
1232
|
+
logger.error("setBundleError called with unknown bundle " + id);
|
|
1233
|
+
call.reject("Bundle " + id + " does not exist");
|
|
1234
|
+
return;
|
|
1235
|
+
}
|
|
1236
|
+
if (bundle.isBuiltin()) {
|
|
1237
|
+
logger.error("setBundleError called on builtin bundle");
|
|
1238
|
+
call.reject("Cannot set builtin bundle to error state");
|
|
1239
|
+
return;
|
|
1240
|
+
}
|
|
1241
|
+
if (Boolean.TRUE.equals(this.autoUpdate)) {
|
|
1242
|
+
logger.warn("setBundleError used while autoUpdate is enabled; this method is intended for manual mode");
|
|
1243
|
+
}
|
|
1244
|
+
this.implementation.setError(bundle);
|
|
1245
|
+
final JSObject ret = new JSObject();
|
|
1246
|
+
ret.put("bundle", mapToJSObject(this.implementation.getBundleInfo(id).toJSONMap()));
|
|
1247
|
+
call.resolve(ret);
|
|
1248
|
+
} catch (final Exception e) {
|
|
1249
|
+
logger.error("Could not set bundle error for id " + id + " " + e.getMessage());
|
|
1250
|
+
call.reject("Could not set bundle error for id " + id, e);
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
|
|
681
1254
|
@PluginMethod
|
|
682
1255
|
public void list(final PluginCall call) {
|
|
683
1256
|
try {
|
|
@@ -685,12 +1258,12 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
685
1258
|
final JSObject ret = new JSObject();
|
|
686
1259
|
final JSArray values = new JSArray();
|
|
687
1260
|
for (final BundleInfo bundle : res) {
|
|
688
|
-
values.put(bundle.
|
|
1261
|
+
values.put(mapToJSObject(bundle.toJSONMap()));
|
|
689
1262
|
}
|
|
690
1263
|
ret.put("bundles", values);
|
|
691
1264
|
call.resolve(ret);
|
|
692
1265
|
} catch (final Exception e) {
|
|
693
|
-
|
|
1266
|
+
logger.error("Could not list bundles " + e.getMessage());
|
|
694
1267
|
call.reject("Could not list bundles", e);
|
|
695
1268
|
}
|
|
696
1269
|
}
|
|
@@ -699,30 +1272,18 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
699
1272
|
public void getLatest(final PluginCall call) {
|
|
700
1273
|
final String channel = call.getString("channel");
|
|
701
1274
|
startNewThread(() ->
|
|
702
|
-
CapacitorUpdaterPlugin.this.implementation.getLatest(CapacitorUpdaterPlugin.this.updateUrl, channel, res -> {
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
while (keys.hasNext()) {
|
|
715
|
-
String key = keys.next();
|
|
716
|
-
if (res.has(key)) {
|
|
717
|
-
try {
|
|
718
|
-
ret.put(key, res.get(key));
|
|
719
|
-
} catch (JSONException e) {
|
|
720
|
-
e.printStackTrace();
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
call.resolve(ret);
|
|
725
|
-
})
|
|
1275
|
+
CapacitorUpdaterPlugin.this.implementation.getLatest(CapacitorUpdaterPlugin.this.updateUrl, channel, (res) -> {
|
|
1276
|
+
JSObject jsRes = mapToJSObject(res);
|
|
1277
|
+
if (jsRes.has("error")) {
|
|
1278
|
+
call.reject(jsRes.getString("error"));
|
|
1279
|
+
return;
|
|
1280
|
+
} else if (jsRes.has("message")) {
|
|
1281
|
+
call.reject(jsRes.getString("message"));
|
|
1282
|
+
return;
|
|
1283
|
+
} else {
|
|
1284
|
+
call.resolve(jsRes);
|
|
1285
|
+
}
|
|
1286
|
+
})
|
|
726
1287
|
);
|
|
727
1288
|
}
|
|
728
1289
|
|
|
@@ -731,11 +1292,11 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
731
1292
|
this.implementation.reset();
|
|
732
1293
|
|
|
733
1294
|
if (toLastSuccessful && !fallback.isBuiltin()) {
|
|
734
|
-
|
|
1295
|
+
logger.info("Resetting to: " + fallback);
|
|
735
1296
|
return this.implementation.set(fallback) && this._reload();
|
|
736
1297
|
}
|
|
737
1298
|
|
|
738
|
-
|
|
1299
|
+
logger.info("Resetting to native.");
|
|
739
1300
|
return this._reload();
|
|
740
1301
|
}
|
|
741
1302
|
|
|
@@ -747,24 +1308,25 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
747
1308
|
call.resolve();
|
|
748
1309
|
return;
|
|
749
1310
|
}
|
|
750
|
-
|
|
1311
|
+
logger.error("Reset failed");
|
|
751
1312
|
call.reject("Reset failed");
|
|
752
1313
|
} catch (final Exception e) {
|
|
753
|
-
|
|
1314
|
+
logger.error("Reset failed " + e.getMessage());
|
|
754
1315
|
call.reject("Reset failed", e);
|
|
755
1316
|
}
|
|
756
1317
|
}
|
|
757
1318
|
|
|
758
1319
|
@PluginMethod
|
|
759
1320
|
public void current(final PluginCall call) {
|
|
1321
|
+
ensureBridgeSet();
|
|
760
1322
|
try {
|
|
761
1323
|
final JSObject ret = new JSObject();
|
|
762
1324
|
final BundleInfo bundle = this.implementation.getCurrentBundle();
|
|
763
|
-
ret.put("bundle", bundle.
|
|
1325
|
+
ret.put("bundle", mapToJSObject(bundle.toJSONMap()));
|
|
764
1326
|
ret.put("native", this.currentVersionNative);
|
|
765
1327
|
call.resolve(ret);
|
|
766
1328
|
} catch (final Exception e) {
|
|
767
|
-
|
|
1329
|
+
logger.error("Could not get current bundle " + e.getMessage());
|
|
768
1330
|
call.reject("Could not get current bundle", e);
|
|
769
1331
|
}
|
|
770
1332
|
}
|
|
@@ -778,13 +1340,33 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
778
1340
|
return;
|
|
779
1341
|
}
|
|
780
1342
|
|
|
781
|
-
call.resolve(bundle.
|
|
1343
|
+
call.resolve(mapToJSObject(bundle.toJSONMap()));
|
|
782
1344
|
} catch (final Exception e) {
|
|
783
|
-
|
|
1345
|
+
logger.error("Could not get next bundle " + e.getMessage());
|
|
784
1346
|
call.reject("Could not get next bundle", e);
|
|
785
1347
|
}
|
|
786
1348
|
}
|
|
787
1349
|
|
|
1350
|
+
@PluginMethod
|
|
1351
|
+
public void getFailedUpdate(final PluginCall call) {
|
|
1352
|
+
try {
|
|
1353
|
+
final BundleInfo bundle = this.readLastFailedBundle();
|
|
1354
|
+
if (bundle == null || bundle.isUnknown()) {
|
|
1355
|
+
call.resolve(null);
|
|
1356
|
+
return;
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
this.persistLastFailedBundle(null);
|
|
1360
|
+
|
|
1361
|
+
final JSObject ret = new JSObject();
|
|
1362
|
+
ret.put("bundle", mapToJSObject(bundle.toJSONMap()));
|
|
1363
|
+
call.resolve(ret);
|
|
1364
|
+
} catch (final Exception e) {
|
|
1365
|
+
logger.error("Could not get failed update " + e.getMessage());
|
|
1366
|
+
call.reject("Could not get failed update", e);
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
|
|
788
1370
|
public void checkForUpdateAfterDelay() {
|
|
789
1371
|
if (this.periodCheckDelay == 0 || !this._isAutoUpdateEnabled()) {
|
|
790
1372
|
return;
|
|
@@ -795,20 +1377,21 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
795
1377
|
@Override
|
|
796
1378
|
public void run() {
|
|
797
1379
|
try {
|
|
798
|
-
CapacitorUpdaterPlugin.this.implementation.getLatest(CapacitorUpdaterPlugin.this.updateUrl, null, res -> {
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
1380
|
+
CapacitorUpdaterPlugin.this.implementation.getLatest(CapacitorUpdaterPlugin.this.updateUrl, null, (res) -> {
|
|
1381
|
+
JSObject jsRes = mapToJSObject(res);
|
|
1382
|
+
if (jsRes.has("error")) {
|
|
1383
|
+
logger.error(Objects.requireNonNull(jsRes.getString("error")));
|
|
1384
|
+
} else if (jsRes.has("version")) {
|
|
1385
|
+
String newVersion = jsRes.getString("version");
|
|
1386
|
+
String currentVersion = String.valueOf(CapacitorUpdaterPlugin.this.implementation.getCurrentBundle());
|
|
1387
|
+
if (!Objects.equals(newVersion, currentVersion)) {
|
|
1388
|
+
logger.info("New version found: " + newVersion);
|
|
1389
|
+
CapacitorUpdaterPlugin.this.backgroundDownload();
|
|
808
1390
|
}
|
|
809
|
-
}
|
|
1391
|
+
}
|
|
1392
|
+
});
|
|
810
1393
|
} catch (final Exception e) {
|
|
811
|
-
|
|
1394
|
+
logger.error("Failed to check for update " + e.getMessage());
|
|
812
1395
|
}
|
|
813
1396
|
}
|
|
814
1397
|
},
|
|
@@ -819,18 +1402,19 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
819
1402
|
|
|
820
1403
|
@PluginMethod
|
|
821
1404
|
public void notifyAppReady(final PluginCall call) {
|
|
1405
|
+
ensureBridgeSet();
|
|
822
1406
|
try {
|
|
823
1407
|
final BundleInfo bundle = this.implementation.getCurrentBundle();
|
|
824
1408
|
this.implementation.setSuccess(bundle, this.autoDeletePrevious);
|
|
825
|
-
|
|
826
|
-
|
|
1409
|
+
logger.info("Current bundle loaded successfully. ['notifyAppReady()' was called] " + bundle);
|
|
1410
|
+
logger.info("semaphoreReady countDown");
|
|
827
1411
|
this.semaphoreDown();
|
|
828
|
-
|
|
1412
|
+
logger.info("semaphoreReady countDown done");
|
|
829
1413
|
final JSObject ret = new JSObject();
|
|
830
|
-
ret.put("bundle", bundle.
|
|
1414
|
+
ret.put("bundle", mapToJSObject(bundle.toJSONMap()));
|
|
831
1415
|
call.resolve(ret);
|
|
832
1416
|
} catch (final Exception e) {
|
|
833
|
-
|
|
1417
|
+
logger.error("Failed to notify app ready state. [Error calling 'notifyAppReady()'] " + e.getMessage());
|
|
834
1418
|
call.reject("Failed to commit app ready state.", e);
|
|
835
1419
|
}
|
|
836
1420
|
}
|
|
@@ -838,118 +1422,46 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
838
1422
|
@PluginMethod
|
|
839
1423
|
public void setMultiDelay(final PluginCall call) {
|
|
840
1424
|
try {
|
|
841
|
-
final
|
|
1425
|
+
final JSONArray delayConditions = call.getData().optJSONArray("delayConditions");
|
|
842
1426
|
if (delayConditions == null) {
|
|
843
|
-
|
|
1427
|
+
logger.error("setMultiDelay called without delayCondition");
|
|
844
1428
|
call.reject("setMultiDelay called without delayCondition");
|
|
845
1429
|
return;
|
|
846
1430
|
}
|
|
847
|
-
|
|
1431
|
+
for (int i = 0; i < delayConditions.length(); i++) {
|
|
1432
|
+
final JSONObject object = delayConditions.optJSONObject(i);
|
|
1433
|
+
if (object != null && object.optString("kind").equals("background") && object.optString("value").isEmpty()) {
|
|
1434
|
+
object.put("value", "0");
|
|
1435
|
+
delayConditions.put(i, object);
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
if (this.delayUpdateUtils.setMultiDelay(delayConditions.toString())) {
|
|
848
1440
|
call.resolve();
|
|
849
1441
|
} else {
|
|
850
1442
|
call.reject("Failed to delay update");
|
|
851
1443
|
}
|
|
852
1444
|
} catch (final Exception e) {
|
|
853
|
-
|
|
1445
|
+
logger.error("Failed to delay update, [Error calling 'setMultiDelay()'] " + e.getMessage());
|
|
854
1446
|
call.reject("Failed to delay update", e);
|
|
855
1447
|
}
|
|
856
1448
|
}
|
|
857
1449
|
|
|
858
|
-
private Boolean _setMultiDelay(String delayConditions) {
|
|
859
|
-
try {
|
|
860
|
-
this.editor.putString(DELAY_CONDITION_PREFERENCES, delayConditions);
|
|
861
|
-
this.editor.commit();
|
|
862
|
-
Log.i(CapacitorUpdater.TAG, "Delay update saved");
|
|
863
|
-
return true;
|
|
864
|
-
} catch (final Exception e) {
|
|
865
|
-
Log.e(CapacitorUpdater.TAG, "Failed to delay update, [Error calling '_setMultiDelay()']", e);
|
|
866
|
-
return false;
|
|
867
|
-
}
|
|
868
|
-
}
|
|
869
|
-
|
|
870
|
-
private boolean _cancelDelay(String source) {
|
|
871
|
-
try {
|
|
872
|
-
this.editor.remove(DELAY_CONDITION_PREFERENCES);
|
|
873
|
-
this.editor.commit();
|
|
874
|
-
Log.i(CapacitorUpdater.TAG, "All delays canceled from " + source);
|
|
875
|
-
return true;
|
|
876
|
-
} catch (final Exception e) {
|
|
877
|
-
Log.e(CapacitorUpdater.TAG, "Failed to cancel update delay", e);
|
|
878
|
-
return false;
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
|
|
882
1450
|
@PluginMethod
|
|
883
1451
|
public void cancelDelay(final PluginCall call) {
|
|
884
|
-
if (this.
|
|
1452
|
+
if (this.delayUpdateUtils.cancelDelay("JS")) {
|
|
885
1453
|
call.resolve();
|
|
886
1454
|
} else {
|
|
887
1455
|
call.reject("Failed to cancel delay");
|
|
888
1456
|
}
|
|
889
1457
|
}
|
|
890
1458
|
|
|
891
|
-
private void _checkCancelDelay(Boolean killed) {
|
|
892
|
-
Gson gson = new Gson();
|
|
893
|
-
String delayUpdatePreferences = prefs.getString(DELAY_CONDITION_PREFERENCES, "[]");
|
|
894
|
-
Type type = new TypeToken<ArrayList<DelayCondition>>() {}.getType();
|
|
895
|
-
ArrayList<DelayCondition> delayConditionList = gson.fromJson(delayUpdatePreferences, type);
|
|
896
|
-
for (DelayCondition condition : delayConditionList) {
|
|
897
|
-
String kind = condition.getKind().toString();
|
|
898
|
-
String value = condition.getValue();
|
|
899
|
-
if (!kind.isEmpty()) {
|
|
900
|
-
switch (kind) {
|
|
901
|
-
case "background":
|
|
902
|
-
if (!killed) {
|
|
903
|
-
this._cancelDelay("background check");
|
|
904
|
-
}
|
|
905
|
-
break;
|
|
906
|
-
case "kill":
|
|
907
|
-
if (killed) {
|
|
908
|
-
this._cancelDelay("kill check");
|
|
909
|
-
this.installNext();
|
|
910
|
-
}
|
|
911
|
-
break;
|
|
912
|
-
case "date":
|
|
913
|
-
if (!"".equals(value)) {
|
|
914
|
-
try {
|
|
915
|
-
final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
|
|
916
|
-
Date date = sdf.parse(value);
|
|
917
|
-
assert date != null;
|
|
918
|
-
if (new Date().compareTo(date) > 0) {
|
|
919
|
-
this._cancelDelay("date expired");
|
|
920
|
-
}
|
|
921
|
-
} catch (final Exception e) {
|
|
922
|
-
this._cancelDelay("date parsing issue");
|
|
923
|
-
}
|
|
924
|
-
} else {
|
|
925
|
-
this._cancelDelay("delayVal absent");
|
|
926
|
-
}
|
|
927
|
-
break;
|
|
928
|
-
case "nativeVersion":
|
|
929
|
-
if (!"".equals(value)) {
|
|
930
|
-
try {
|
|
931
|
-
final Version versionLimit = new Version(value);
|
|
932
|
-
if (this.currentVersionNative.isAtLeast(versionLimit)) {
|
|
933
|
-
this._cancelDelay("nativeVersion above limit");
|
|
934
|
-
}
|
|
935
|
-
} catch (final Exception e) {
|
|
936
|
-
this._cancelDelay("nativeVersion parsing issue");
|
|
937
|
-
}
|
|
938
|
-
} else {
|
|
939
|
-
this._cancelDelay("delayVal absent");
|
|
940
|
-
}
|
|
941
|
-
break;
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
|
|
947
1459
|
private Boolean _isAutoUpdateEnabled() {
|
|
948
1460
|
final CapConfig config = CapConfig.loadDefault(this.getActivity());
|
|
949
1461
|
String serverUrl = config.getServerUrl();
|
|
950
1462
|
if (serverUrl != null && !serverUrl.isEmpty()) {
|
|
951
1463
|
// log warning autoupdate disabled when serverUrl is set
|
|
952
|
-
|
|
1464
|
+
logger.warn("AutoUpdate is automatic disabled when serverUrl is set.");
|
|
953
1465
|
}
|
|
954
1466
|
return (
|
|
955
1467
|
CapacitorUpdaterPlugin.this.autoUpdate &&
|
|
@@ -965,7 +1477,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
965
1477
|
ret.put("enabled", this._isAutoUpdateEnabled());
|
|
966
1478
|
call.resolve(ret);
|
|
967
1479
|
} catch (final Exception e) {
|
|
968
|
-
|
|
1480
|
+
logger.error("Could not get autoUpdate status " + e.getMessage());
|
|
969
1481
|
call.reject("Could not get autoUpdate status", e);
|
|
970
1482
|
}
|
|
971
1483
|
}
|
|
@@ -979,7 +1491,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
979
1491
|
ret.put("available", serverUrl == null || serverUrl.isEmpty());
|
|
980
1492
|
call.resolve(ret);
|
|
981
1493
|
} catch (final Exception e) {
|
|
982
|
-
|
|
1494
|
+
logger.error("Could not get autoUpdate availability " + e.getMessage());
|
|
983
1495
|
call.reject("Could not get autoUpdate availability", e);
|
|
984
1496
|
}
|
|
985
1497
|
}
|
|
@@ -991,7 +1503,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
991
1503
|
}
|
|
992
1504
|
this.appReadyCheck = startNewThread(new DeferredNotifyAppReadyCheck());
|
|
993
1505
|
} catch (final Exception e) {
|
|
994
|
-
|
|
1506
|
+
logger.error("Failed to start " + DeferredNotifyAppReadyCheck.class.getName() + " " + e.getMessage());
|
|
995
1507
|
}
|
|
996
1508
|
}
|
|
997
1509
|
|
|
@@ -1004,8 +1516,14 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1004
1516
|
}
|
|
1005
1517
|
}
|
|
1006
1518
|
|
|
1519
|
+
private void ensureBridgeSet() {
|
|
1520
|
+
if (this.bridge != null && this.bridge.getWebView() != null) {
|
|
1521
|
+
logger.setBridge(this.bridge);
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1007
1525
|
private void endBackGroundTaskWithNotif(String msg, String latestVersionName, BundleInfo current, Boolean error) {
|
|
1008
|
-
endBackGroundTaskWithNotif(msg, latestVersionName, current, error, "download_fail", "downloadFailed");
|
|
1526
|
+
endBackGroundTaskWithNotif(msg, latestVersionName, current, error, false, "download_fail", "downloadFailed");
|
|
1009
1527
|
}
|
|
1010
1528
|
|
|
1011
1529
|
private void endBackGroundTaskWithNotif(
|
|
@@ -1013,18 +1531,28 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1013
1531
|
String latestVersionName,
|
|
1014
1532
|
BundleInfo current,
|
|
1015
1533
|
Boolean error,
|
|
1534
|
+
Boolean isDirectUpdate
|
|
1535
|
+
) {
|
|
1536
|
+
endBackGroundTaskWithNotif(msg, latestVersionName, current, error, isDirectUpdate, "download_fail", "downloadFailed");
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
private void endBackGroundTaskWithNotif(
|
|
1540
|
+
String msg,
|
|
1541
|
+
String latestVersionName,
|
|
1542
|
+
BundleInfo current,
|
|
1543
|
+
Boolean error,
|
|
1544
|
+
Boolean isDirectUpdate,
|
|
1016
1545
|
String failureAction,
|
|
1017
1546
|
String failureEvent
|
|
1018
1547
|
) {
|
|
1019
1548
|
if (error) {
|
|
1020
|
-
|
|
1021
|
-
CapacitorUpdater.TAG,
|
|
1549
|
+
logger.info(
|
|
1022
1550
|
"endBackGroundTaskWithNotif error: " +
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1551
|
+
error +
|
|
1552
|
+
" current: " +
|
|
1553
|
+
current.getVersionName() +
|
|
1554
|
+
"latestVersionName: " +
|
|
1555
|
+
latestVersionName
|
|
1028
1556
|
);
|
|
1029
1557
|
this.implementation.sendStats(failureAction, current.getVersionName());
|
|
1030
1558
|
final JSObject ret = new JSObject();
|
|
@@ -1032,105 +1560,117 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1032
1560
|
this.notifyListeners(failureEvent, ret);
|
|
1033
1561
|
}
|
|
1034
1562
|
final JSObject ret = new JSObject();
|
|
1035
|
-
ret.put("bundle", current.
|
|
1563
|
+
ret.put("bundle", mapToJSObject(current.toJSONMap()));
|
|
1036
1564
|
this.notifyListeners("noNeedUpdate", ret);
|
|
1037
|
-
this.sendReadyToJs(current, msg);
|
|
1565
|
+
this.sendReadyToJs(current, msg, isDirectUpdate);
|
|
1038
1566
|
this.backgroundDownloadTask = null;
|
|
1039
|
-
|
|
1567
|
+
logger.info("endBackGroundTaskWithNotif " + msg);
|
|
1040
1568
|
}
|
|
1041
1569
|
|
|
1042
1570
|
private Thread backgroundDownload() {
|
|
1043
|
-
|
|
1571
|
+
final boolean plannedDirectUpdate = this.shouldUseDirectUpdate();
|
|
1572
|
+
final boolean initialDirectUpdateAllowed = this.isDirectUpdateCurrentlyAllowed(plannedDirectUpdate);
|
|
1573
|
+
this.implementation.directUpdate = initialDirectUpdateAllowed;
|
|
1574
|
+
final String messageUpdate = initialDirectUpdateAllowed
|
|
1044
1575
|
? "Update will occur now."
|
|
1045
1576
|
: "Update will occur next time app moves to background.";
|
|
1046
1577
|
return startNewThread(() -> {
|
|
1047
|
-
|
|
1048
|
-
|
|
1578
|
+
logger.info("Check for update via: " + CapacitorUpdaterPlugin.this.updateUrl);
|
|
1579
|
+
try {
|
|
1580
|
+
CapacitorUpdaterPlugin.this.implementation.getLatest(CapacitorUpdaterPlugin.this.updateUrl, null, (res) -> {
|
|
1581
|
+
JSObject jsRes = mapToJSObject(res);
|
|
1049
1582
|
final BundleInfo current = CapacitorUpdaterPlugin.this.implementation.getCurrentBundle();
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
}
|
|
1075
|
-
return;
|
|
1076
|
-
}
|
|
1583
|
+
|
|
1584
|
+
// Handle network errors and other failures first
|
|
1585
|
+
if (jsRes.has("error")) {
|
|
1586
|
+
String error = jsRes.getString("error");
|
|
1587
|
+
logger.error("getLatest failed with error: " + error);
|
|
1588
|
+
String latestVersion = jsRes.has("version") ? jsRes.getString("version") : current.getVersionName();
|
|
1589
|
+
if ("response_error".equals(error)) {
|
|
1590
|
+
CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
|
|
1591
|
+
"Network error: " + error,
|
|
1592
|
+
latestVersion,
|
|
1593
|
+
current,
|
|
1594
|
+
true,
|
|
1595
|
+
plannedDirectUpdate
|
|
1596
|
+
);
|
|
1597
|
+
} else {
|
|
1598
|
+
CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
|
|
1599
|
+
error,
|
|
1600
|
+
latestVersion,
|
|
1601
|
+
current,
|
|
1602
|
+
true,
|
|
1603
|
+
plannedDirectUpdate,
|
|
1604
|
+
"backend_refusal",
|
|
1605
|
+
"backendRefused"
|
|
1606
|
+
);
|
|
1077
1607
|
}
|
|
1608
|
+
return;
|
|
1609
|
+
}
|
|
1078
1610
|
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
CapacitorUpdaterPlugin.this.notifyListeners("majorAvailable", majorAvailable);
|
|
1611
|
+
try {
|
|
1612
|
+
if (jsRes.has("message")) {
|
|
1613
|
+
logger.info("API message: " + jsRes.get("message"));
|
|
1614
|
+
if (jsRes.has("version") && (jsRes.has("breaking") || jsRes.has("major"))) {
|
|
1615
|
+
CapacitorUpdaterPlugin.this.notifyBreakingEvents(jsRes.getString("version"));
|
|
1085
1616
|
}
|
|
1086
|
-
|
|
1087
|
-
? res.optString("version", current.getVersionName())
|
|
1088
|
-
: current.getVersionName();
|
|
1617
|
+
String latestVersion = jsRes.has("version") ? jsRes.getString("version") : current.getVersionName();
|
|
1089
1618
|
CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1619
|
+
jsRes.getString("message"),
|
|
1620
|
+
latestVersion,
|
|
1621
|
+
current,
|
|
1622
|
+
true,
|
|
1623
|
+
plannedDirectUpdate,
|
|
1624
|
+
"backend_refusal",
|
|
1625
|
+
"backendRefused"
|
|
1626
|
+
);
|
|
1097
1627
|
return;
|
|
1098
1628
|
}
|
|
1099
1629
|
|
|
1100
|
-
final String latestVersionName =
|
|
1630
|
+
final String latestVersionName = jsRes.getString("version");
|
|
1101
1631
|
|
|
1102
1632
|
if ("builtin".equals(latestVersionName)) {
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1633
|
+
logger.info("Latest version is builtin");
|
|
1634
|
+
final boolean directUpdateAllowedNow = CapacitorUpdaterPlugin.this.isDirectUpdateCurrentlyAllowed(
|
|
1635
|
+
plannedDirectUpdate
|
|
1636
|
+
);
|
|
1637
|
+
if (directUpdateAllowedNow) {
|
|
1638
|
+
logger.info("Direct update to builtin version");
|
|
1106
1639
|
this._reset(false);
|
|
1107
1640
|
CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1641
|
+
"Updated to builtin version",
|
|
1642
|
+
latestVersionName,
|
|
1643
|
+
CapacitorUpdaterPlugin.this.implementation.getCurrentBundle(),
|
|
1644
|
+
false,
|
|
1645
|
+
true
|
|
1646
|
+
);
|
|
1113
1647
|
} else {
|
|
1114
|
-
|
|
1648
|
+
if (plannedDirectUpdate && !directUpdateAllowedNow) {
|
|
1649
|
+
logger.info(
|
|
1650
|
+
"Direct update skipped because splashscreen timeout occurred. Update will be applied later."
|
|
1651
|
+
);
|
|
1652
|
+
}
|
|
1653
|
+
logger.info("Setting next bundle to builtin");
|
|
1115
1654
|
CapacitorUpdaterPlugin.this.implementation.setNextBundle(BundleInfo.ID_BUILTIN);
|
|
1116
1655
|
CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1656
|
+
"Next update will be to builtin version",
|
|
1657
|
+
latestVersionName,
|
|
1658
|
+
current,
|
|
1659
|
+
false
|
|
1660
|
+
);
|
|
1122
1661
|
}
|
|
1123
1662
|
return;
|
|
1124
1663
|
}
|
|
1125
1664
|
|
|
1126
|
-
if (!
|
|
1127
|
-
|
|
1665
|
+
if (!jsRes.has("url") || !CapacitorUpdaterPlugin.this.isValidURL(jsRes.getString("url"))) {
|
|
1666
|
+
logger.error("Error no url or wrong format");
|
|
1128
1667
|
CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1668
|
+
"Error no url or wrong format",
|
|
1669
|
+
current.getVersionName(),
|
|
1670
|
+
current,
|
|
1671
|
+
true,
|
|
1672
|
+
plannedDirectUpdate
|
|
1673
|
+
);
|
|
1134
1674
|
return;
|
|
1135
1675
|
}
|
|
1136
1676
|
|
|
@@ -1140,129 +1680,158 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1140
1680
|
final BundleInfo latest = CapacitorUpdaterPlugin.this.implementation.getBundleInfoByName(latestVersionName);
|
|
1141
1681
|
if (latest != null) {
|
|
1142
1682
|
final JSObject ret = new JSObject();
|
|
1143
|
-
ret.put("bundle", latest.
|
|
1683
|
+
ret.put("bundle", mapToJSObject(latest.toJSONMap()));
|
|
1144
1684
|
if (latest.isErrorStatus()) {
|
|
1145
|
-
|
|
1685
|
+
logger.error("Latest bundle already exists, and is in error state. Aborting update.");
|
|
1146
1686
|
CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1687
|
+
"Latest bundle already exists, and is in error state. Aborting update.",
|
|
1688
|
+
latestVersionName,
|
|
1689
|
+
current,
|
|
1690
|
+
true,
|
|
1691
|
+
plannedDirectUpdate
|
|
1692
|
+
);
|
|
1152
1693
|
return;
|
|
1153
1694
|
}
|
|
1154
1695
|
if (latest.isDownloaded()) {
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1696
|
+
logger.info("Latest bundle already exists and download is NOT required. " + messageUpdate);
|
|
1697
|
+
final boolean directUpdateAllowedNow = CapacitorUpdaterPlugin.this.isDirectUpdateCurrentlyAllowed(
|
|
1698
|
+
plannedDirectUpdate
|
|
1158
1699
|
);
|
|
1159
|
-
if (
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1700
|
+
if (directUpdateAllowedNow) {
|
|
1701
|
+
String delayUpdatePreferences = prefs.getString(DelayUpdateUtils.DELAY_CONDITION_PREFERENCES, "[]");
|
|
1702
|
+
ArrayList<DelayCondition> delayConditionList = delayUpdateUtils.parseDelayConditions(
|
|
1703
|
+
delayUpdatePreferences
|
|
1704
|
+
);
|
|
1705
|
+
if (!delayConditionList.isEmpty()) {
|
|
1706
|
+
logger.info("Update delayed until delay conditions met");
|
|
1707
|
+
CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
|
|
1708
|
+
"Update delayed until delay conditions met",
|
|
1164
1709
|
latestVersionName,
|
|
1165
1710
|
latest,
|
|
1166
|
-
false
|
|
1711
|
+
false,
|
|
1712
|
+
plannedDirectUpdate
|
|
1167
1713
|
);
|
|
1714
|
+
return;
|
|
1715
|
+
}
|
|
1716
|
+
CapacitorUpdaterPlugin.this.implementation.set(latest);
|
|
1717
|
+
CapacitorUpdaterPlugin.this._reload();
|
|
1718
|
+
CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
|
|
1719
|
+
"Update installed",
|
|
1720
|
+
latestVersionName,
|
|
1721
|
+
latest,
|
|
1722
|
+
false,
|
|
1723
|
+
true
|
|
1724
|
+
);
|
|
1168
1725
|
} else {
|
|
1726
|
+
if (plannedDirectUpdate && !directUpdateAllowedNow) {
|
|
1727
|
+
logger.info(
|
|
1728
|
+
"Direct update skipped because splashscreen timeout occurred. Update will install on next background."
|
|
1729
|
+
);
|
|
1730
|
+
}
|
|
1169
1731
|
CapacitorUpdaterPlugin.this.notifyListeners("updateAvailable", ret);
|
|
1170
1732
|
CapacitorUpdaterPlugin.this.implementation.setNextBundle(latest.getId());
|
|
1171
1733
|
CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1734
|
+
"update downloaded, will install next background",
|
|
1735
|
+
latestVersionName,
|
|
1736
|
+
latest,
|
|
1737
|
+
false
|
|
1738
|
+
);
|
|
1177
1739
|
}
|
|
1178
1740
|
return;
|
|
1179
1741
|
}
|
|
1180
1742
|
if (latest.isDeleted()) {
|
|
1181
|
-
|
|
1182
|
-
CapacitorUpdater.TAG,
|
|
1183
|
-
"Latest bundle already exists and will be deleted, download will overwrite it."
|
|
1184
|
-
);
|
|
1743
|
+
logger.info("Latest bundle already exists and will be deleted, download will overwrite it.");
|
|
1185
1744
|
try {
|
|
1186
1745
|
final Boolean deleted = CapacitorUpdaterPlugin.this.implementation.delete(latest.getId(), true);
|
|
1187
1746
|
if (deleted) {
|
|
1188
|
-
|
|
1747
|
+
logger.info("Failed bundle deleted: " + latest.getVersionName());
|
|
1189
1748
|
}
|
|
1190
1749
|
} catch (final IOException e) {
|
|
1191
|
-
|
|
1750
|
+
logger.error("Failed to delete failed bundle: " + latest.getVersionName() + " " + e.getMessage());
|
|
1192
1751
|
}
|
|
1193
1752
|
}
|
|
1194
1753
|
}
|
|
1195
1754
|
startNewThread(() -> {
|
|
1196
1755
|
try {
|
|
1197
|
-
|
|
1198
|
-
CapacitorUpdater.TAG,
|
|
1756
|
+
logger.info(
|
|
1199
1757
|
"New bundle: " +
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1758
|
+
latestVersionName +
|
|
1759
|
+
" found. Current is: " +
|
|
1760
|
+
current.getVersionName() +
|
|
1761
|
+
". " +
|
|
1762
|
+
messageUpdate
|
|
1205
1763
|
);
|
|
1206
1764
|
|
|
1207
|
-
final String url =
|
|
1208
|
-
final String sessionKey =
|
|
1209
|
-
final String checksum =
|
|
1765
|
+
final String url = jsRes.getString("url");
|
|
1766
|
+
final String sessionKey = jsRes.has("sessionKey") ? jsRes.getString("sessionKey") : "";
|
|
1767
|
+
final String checksum = jsRes.has("checksum") ? jsRes.getString("checksum") : "";
|
|
1210
1768
|
|
|
1211
|
-
if (
|
|
1769
|
+
if (jsRes.has("manifest")) {
|
|
1212
1770
|
// Handle manifest-based download
|
|
1213
|
-
JSONArray manifest =
|
|
1771
|
+
JSONArray manifest = jsRes.getJSONArray("manifest");
|
|
1214
1772
|
CapacitorUpdaterPlugin.this.implementation.downloadBackground(
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1773
|
+
url,
|
|
1774
|
+
latestVersionName,
|
|
1775
|
+
sessionKey,
|
|
1776
|
+
checksum,
|
|
1777
|
+
manifest
|
|
1778
|
+
);
|
|
1221
1779
|
} else {
|
|
1222
1780
|
// Handle single file download (existing code)
|
|
1223
1781
|
CapacitorUpdaterPlugin.this.implementation.downloadBackground(
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1782
|
+
url,
|
|
1783
|
+
latestVersionName,
|
|
1784
|
+
sessionKey,
|
|
1785
|
+
checksum,
|
|
1786
|
+
null
|
|
1787
|
+
);
|
|
1230
1788
|
}
|
|
1231
1789
|
} catch (final Exception e) {
|
|
1232
|
-
|
|
1790
|
+
logger.error("error downloading file " + e.getMessage());
|
|
1233
1791
|
CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1792
|
+
"Error downloading file",
|
|
1793
|
+
latestVersionName,
|
|
1794
|
+
CapacitorUpdaterPlugin.this.implementation.getCurrentBundle(),
|
|
1795
|
+
true,
|
|
1796
|
+
plannedDirectUpdate
|
|
1797
|
+
);
|
|
1239
1798
|
}
|
|
1240
1799
|
});
|
|
1241
1800
|
} else {
|
|
1242
|
-
|
|
1801
|
+
logger.info("No need to update, " + current.getId() + " is the latest bundle.");
|
|
1243
1802
|
CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif("No need to update", latestVersionName, current, false);
|
|
1244
1803
|
}
|
|
1245
1804
|
} catch (final JSONException e) {
|
|
1246
|
-
|
|
1805
|
+
logger.error("error parsing JSON " + e.getMessage());
|
|
1247
1806
|
CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1807
|
+
"Error parsing JSON",
|
|
1808
|
+
current.getVersionName(),
|
|
1809
|
+
current,
|
|
1810
|
+
true,
|
|
1811
|
+
plannedDirectUpdate
|
|
1812
|
+
);
|
|
1253
1813
|
}
|
|
1254
1814
|
});
|
|
1815
|
+
} catch (final Exception e) {
|
|
1816
|
+
logger.error("getLatest call failed: " + e.getMessage());
|
|
1817
|
+
final BundleInfo current = CapacitorUpdaterPlugin.this.implementation.getCurrentBundle();
|
|
1818
|
+
CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
|
|
1819
|
+
"Network connection failed",
|
|
1820
|
+
current.getVersionName(),
|
|
1821
|
+
current,
|
|
1822
|
+
true,
|
|
1823
|
+
plannedDirectUpdate
|
|
1824
|
+
);
|
|
1825
|
+
}
|
|
1255
1826
|
});
|
|
1256
1827
|
}
|
|
1257
1828
|
|
|
1258
1829
|
private void installNext() {
|
|
1259
1830
|
try {
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
if (delayConditionList != null && !delayConditionList.isEmpty()) {
|
|
1265
|
-
Log.i(CapacitorUpdater.TAG, "Update delayed until delay conditions met");
|
|
1831
|
+
String delayUpdatePreferences = prefs.getString(DelayUpdateUtils.DELAY_CONDITION_PREFERENCES, "[]");
|
|
1832
|
+
ArrayList<DelayCondition> delayConditionList = delayUpdateUtils.parseDelayConditions(delayUpdatePreferences);
|
|
1833
|
+
if (!delayConditionList.isEmpty()) {
|
|
1834
|
+
logger.info("Update delayed until delay conditions met");
|
|
1266
1835
|
return;
|
|
1267
1836
|
}
|
|
1268
1837
|
final BundleInfo current = this.implementation.getCurrentBundle();
|
|
@@ -1270,16 +1839,16 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1270
1839
|
|
|
1271
1840
|
if (next != null && !next.isErrorStatus() && !next.getId().equals(current.getId())) {
|
|
1272
1841
|
// There is a next bundle waiting for activation
|
|
1273
|
-
|
|
1842
|
+
logger.debug("Next bundle is: " + next.getVersionName());
|
|
1274
1843
|
if (this.implementation.set(next) && this._reload()) {
|
|
1275
|
-
|
|
1844
|
+
logger.info("Updated to bundle: " + next.getVersionName());
|
|
1276
1845
|
this.implementation.setNextBundle(null);
|
|
1277
1846
|
} else {
|
|
1278
|
-
|
|
1847
|
+
logger.error("Update to bundle: " + next.getVersionName() + " Failed!");
|
|
1279
1848
|
}
|
|
1280
1849
|
}
|
|
1281
1850
|
} catch (final Exception e) {
|
|
1282
|
-
|
|
1851
|
+
logger.error("Error during onActivityStopped " + e.getMessage());
|
|
1283
1852
|
}
|
|
1284
1853
|
}
|
|
1285
1854
|
|
|
@@ -1288,33 +1857,34 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1288
1857
|
final BundleInfo current = this.implementation.getCurrentBundle();
|
|
1289
1858
|
|
|
1290
1859
|
if (current.isBuiltin()) {
|
|
1291
|
-
|
|
1860
|
+
logger.info("Built-in bundle is active. We skip the check for notifyAppReady.");
|
|
1292
1861
|
return;
|
|
1293
1862
|
}
|
|
1294
|
-
|
|
1863
|
+
logger.debug("Current bundle is: " + current);
|
|
1295
1864
|
|
|
1296
1865
|
if (BundleStatus.SUCCESS != current.getStatus()) {
|
|
1297
|
-
|
|
1298
|
-
|
|
1866
|
+
logger.error("notifyAppReady was not called, roll back current bundle: " + current.getId());
|
|
1867
|
+
logger.info("Did you forget to call 'notifyAppReady()' in your Capacitor App code?");
|
|
1299
1868
|
final JSObject ret = new JSObject();
|
|
1300
|
-
ret.put("bundle", current.
|
|
1869
|
+
ret.put("bundle", mapToJSObject(current.toJSONMap()));
|
|
1870
|
+
this.persistLastFailedBundle(current);
|
|
1301
1871
|
this.notifyListeners("updateFailed", ret);
|
|
1302
1872
|
this.implementation.sendStats("update_fail", current.getVersionName());
|
|
1303
1873
|
this.implementation.setError(current);
|
|
1304
1874
|
this._reset(true);
|
|
1305
1875
|
if (CapacitorUpdaterPlugin.this.autoDeleteFailed && !current.isBuiltin()) {
|
|
1306
|
-
|
|
1876
|
+
logger.info("Deleting failing bundle: " + current.getVersionName());
|
|
1307
1877
|
try {
|
|
1308
1878
|
final Boolean res = this.implementation.delete(current.getId(), false);
|
|
1309
1879
|
if (res) {
|
|
1310
|
-
|
|
1880
|
+
logger.info("Failed bundle deleted: " + current.getVersionName());
|
|
1311
1881
|
}
|
|
1312
1882
|
} catch (final IOException e) {
|
|
1313
|
-
|
|
1883
|
+
logger.error("Failed to delete failed bundle: " + current.getVersionName() + " " + e.getMessage());
|
|
1314
1884
|
}
|
|
1315
1885
|
}
|
|
1316
1886
|
} else {
|
|
1317
|
-
|
|
1887
|
+
logger.info("notifyAppReady was called. This is fine: " + current.getId());
|
|
1318
1888
|
}
|
|
1319
1889
|
}
|
|
1320
1890
|
|
|
@@ -1323,15 +1893,12 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1323
1893
|
@Override
|
|
1324
1894
|
public void run() {
|
|
1325
1895
|
try {
|
|
1326
|
-
|
|
1327
|
-
CapacitorUpdater.TAG,
|
|
1328
|
-
"Wait for " + CapacitorUpdaterPlugin.this.appReadyTimeout + "ms, then check for notifyAppReady"
|
|
1329
|
-
);
|
|
1896
|
+
logger.info("Wait for " + CapacitorUpdaterPlugin.this.appReadyTimeout + "ms, then check for notifyAppReady");
|
|
1330
1897
|
Thread.sleep(CapacitorUpdaterPlugin.this.appReadyTimeout);
|
|
1331
1898
|
CapacitorUpdaterPlugin.this.checkRevert();
|
|
1332
1899
|
CapacitorUpdaterPlugin.this.appReadyCheck = null;
|
|
1333
1900
|
} catch (final InterruptedException e) {
|
|
1334
|
-
|
|
1901
|
+
logger.info(DeferredNotifyAppReadyCheck.class.getName() + " was interrupted.");
|
|
1335
1902
|
}
|
|
1336
1903
|
}
|
|
1337
1904
|
}
|
|
@@ -1339,14 +1906,16 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1339
1906
|
public void appMovedToForeground() {
|
|
1340
1907
|
final BundleInfo current = CapacitorUpdaterPlugin.this.implementation.getCurrentBundle();
|
|
1341
1908
|
CapacitorUpdaterPlugin.this.implementation.sendStats("app_moved_to_foreground", current.getVersionName());
|
|
1342
|
-
this.
|
|
1909
|
+
this.delayUpdateUtils.checkCancelDelay(DelayUpdateUtils.CancelDelaySource.FOREGROUND);
|
|
1910
|
+
this.delayUpdateUtils.unsetBackgroundTimestamp();
|
|
1911
|
+
|
|
1343
1912
|
if (
|
|
1344
1913
|
CapacitorUpdaterPlugin.this._isAutoUpdateEnabled() &&
|
|
1345
1914
|
(this.backgroundDownloadTask == null || !this.backgroundDownloadTask.isAlive())
|
|
1346
1915
|
) {
|
|
1347
1916
|
this.backgroundDownloadTask = this.backgroundDownload();
|
|
1348
1917
|
} else {
|
|
1349
|
-
|
|
1918
|
+
logger.info("Auto update is disabled");
|
|
1350
1919
|
this.sendReadyToJs(current, "disabled");
|
|
1351
1920
|
}
|
|
1352
1921
|
this.checkAppReady();
|
|
@@ -1354,40 +1923,42 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1354
1923
|
|
|
1355
1924
|
public void appMovedToBackground() {
|
|
1356
1925
|
final BundleInfo current = CapacitorUpdaterPlugin.this.implementation.getCurrentBundle();
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
String value = delayCondition.getValue();
|
|
1368
|
-
backgroundValue = (value != null && !value.isEmpty()) ? value : "0";
|
|
1369
|
-
}
|
|
1926
|
+
|
|
1927
|
+
// Show splashscreen FIRST, before any other background work to ensure launcher shows it
|
|
1928
|
+
if (this.autoSplashscreen) {
|
|
1929
|
+
boolean canShowSplashscreen = true;
|
|
1930
|
+
|
|
1931
|
+
if (!this._isAutoUpdateEnabled()) {
|
|
1932
|
+
logger.warn(
|
|
1933
|
+
"autoSplashscreen is enabled but autoUpdate is disabled. Splashscreen will not be shown. Enable autoUpdate or disable autoSplashscreen."
|
|
1934
|
+
);
|
|
1935
|
+
canShowSplashscreen = false;
|
|
1370
1936
|
}
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
backgroundTask.interrupt();
|
|
1376
|
-
}
|
|
1377
|
-
backgroundTask = startNewThread(
|
|
1378
|
-
() -> {
|
|
1379
|
-
taskRunning = false;
|
|
1380
|
-
_checkCancelDelay(false);
|
|
1381
|
-
installNext();
|
|
1382
|
-
},
|
|
1383
|
-
timeout
|
|
1937
|
+
|
|
1938
|
+
if (!this.shouldUseDirectUpdate()) {
|
|
1939
|
+
logger.warn(
|
|
1940
|
+
"autoSplashscreen is enabled but directUpdate is not configured for immediate updates. Set directUpdate to 'always' or 'atInstall', or disable autoSplashscreen."
|
|
1384
1941
|
);
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1942
|
+
canShowSplashscreen = false;
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1945
|
+
if (canShowSplashscreen) {
|
|
1946
|
+
logger.info("Showing splashscreen for launcher/task switcher");
|
|
1947
|
+
this.showSplashscreen();
|
|
1388
1948
|
}
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1951
|
+
// Do other background work after splashscreen is shown
|
|
1952
|
+
CapacitorUpdaterPlugin.this.implementation.sendStats("app_moved_to_background", current.getVersionName());
|
|
1953
|
+
logger.info("Checking for pending update");
|
|
1954
|
+
|
|
1955
|
+
try {
|
|
1956
|
+
// We need to set "backgrounded time"
|
|
1957
|
+
this.delayUpdateUtils.setBackgroundTimestamp(System.currentTimeMillis());
|
|
1958
|
+
this.delayUpdateUtils.checkCancelDelay(DelayUpdateUtils.CancelDelaySource.BACKGROUND);
|
|
1959
|
+
this.installNext();
|
|
1389
1960
|
} catch (final Exception e) {
|
|
1390
|
-
|
|
1961
|
+
logger.error("Error during onActivityStopped " + e.getMessage());
|
|
1391
1962
|
}
|
|
1392
1963
|
}
|
|
1393
1964
|
|
|
@@ -1416,37 +1987,154 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1416
1987
|
|
|
1417
1988
|
@Override
|
|
1418
1989
|
public void handleOnStart() {
|
|
1419
|
-
|
|
1420
|
-
|
|
1990
|
+
try {
|
|
1991
|
+
if (isPreviousMainActivity) {
|
|
1992
|
+
logger.info("handleOnStart: appMovedToForeground");
|
|
1993
|
+
this.appMovedToForeground();
|
|
1994
|
+
}
|
|
1995
|
+
logger.info("handleOnStart: onActivityStarted " + getActivity().getClass().getName());
|
|
1996
|
+
isPreviousMainActivity = true;
|
|
1997
|
+
|
|
1998
|
+
// Initialize shake menu if enabled and activity is BridgeActivity
|
|
1999
|
+
if (shakeMenuEnabled && getActivity() instanceof com.getcapacitor.BridgeActivity && shakeMenu == null) {
|
|
2000
|
+
try {
|
|
2001
|
+
shakeMenu = new ShakeMenu(this, (com.getcapacitor.BridgeActivity) getActivity(), logger);
|
|
2002
|
+
logger.info("Shake menu initialized");
|
|
2003
|
+
} catch (Exception e) {
|
|
2004
|
+
logger.error("Failed to initialize shake menu: " + e.getMessage());
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
} catch (Exception e) {
|
|
2008
|
+
logger.error("Failed to run handleOnStart: " + e.getMessage());
|
|
1421
2009
|
}
|
|
1422
|
-
Log.i(CapacitorUpdater.TAG, "onActivityStarted " + getActivity().getClass().getName());
|
|
1423
|
-
isPreviousMainActivity = true;
|
|
1424
2010
|
}
|
|
1425
2011
|
|
|
1426
2012
|
@Override
|
|
1427
2013
|
public void handleOnStop() {
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
2014
|
+
try {
|
|
2015
|
+
isPreviousMainActivity = isMainActivity();
|
|
2016
|
+
if (isPreviousMainActivity) {
|
|
2017
|
+
logger.info("handleOnStop: appMovedToBackground");
|
|
2018
|
+
this.appMovedToBackground();
|
|
2019
|
+
}
|
|
2020
|
+
} catch (Exception e) {
|
|
2021
|
+
logger.error("Failed to run handleOnStop: " + e.getMessage());
|
|
1431
2022
|
}
|
|
1432
2023
|
}
|
|
1433
2024
|
|
|
1434
2025
|
@Override
|
|
1435
2026
|
public void handleOnResume() {
|
|
1436
|
-
|
|
1437
|
-
backgroundTask
|
|
2027
|
+
try {
|
|
2028
|
+
if (backgroundTask != null && taskRunning) {
|
|
2029
|
+
backgroundTask.interrupt();
|
|
2030
|
+
}
|
|
2031
|
+
this.implementation.activity = getActivity();
|
|
2032
|
+
} catch (Exception e) {
|
|
2033
|
+
logger.error("Failed to run handleOnResume: " + e.getMessage());
|
|
1438
2034
|
}
|
|
1439
|
-
this.implementation.activity = getActivity();
|
|
1440
2035
|
}
|
|
1441
2036
|
|
|
1442
2037
|
@Override
|
|
1443
2038
|
public void handleOnPause() {
|
|
1444
|
-
|
|
2039
|
+
try {
|
|
2040
|
+
this.implementation.activity = getActivity();
|
|
2041
|
+
} catch (Exception e) {
|
|
2042
|
+
logger.error("Failed to run handleOnPause: " + e.getMessage());
|
|
2043
|
+
}
|
|
1445
2044
|
}
|
|
1446
2045
|
|
|
1447
2046
|
@Override
|
|
1448
2047
|
public void handleOnDestroy() {
|
|
1449
|
-
|
|
1450
|
-
|
|
2048
|
+
try {
|
|
2049
|
+
logger.info("onActivityDestroyed " + getActivity().getClass().getName());
|
|
2050
|
+
this.implementation.activity = getActivity();
|
|
2051
|
+
|
|
2052
|
+
// Clean up shake menu
|
|
2053
|
+
if (shakeMenu != null) {
|
|
2054
|
+
try {
|
|
2055
|
+
shakeMenu.stop();
|
|
2056
|
+
shakeMenu = null;
|
|
2057
|
+
logger.info("Shake menu cleaned up");
|
|
2058
|
+
} catch (Exception e) {
|
|
2059
|
+
logger.error("Failed to clean up shake menu: " + e.getMessage());
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
} catch (Exception e) {
|
|
2063
|
+
logger.error("Failed to run handleOnDestroy: " + e.getMessage());
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2067
|
+
@PluginMethod
|
|
2068
|
+
public void setShakeMenu(final PluginCall call) {
|
|
2069
|
+
final Boolean enabled = call.getBoolean("enabled");
|
|
2070
|
+
if (enabled == null) {
|
|
2071
|
+
logger.error("setShakeMenu called without enabled parameter");
|
|
2072
|
+
call.reject("setShakeMenu called without enabled parameter");
|
|
2073
|
+
return;
|
|
2074
|
+
}
|
|
2075
|
+
|
|
2076
|
+
this.shakeMenuEnabled = enabled;
|
|
2077
|
+
logger.info("Shake menu " + (enabled ? "enabled" : "disabled"));
|
|
2078
|
+
|
|
2079
|
+
// Manage shake menu instance based on enabled state
|
|
2080
|
+
if (enabled && getActivity() instanceof com.getcapacitor.BridgeActivity && shakeMenu == null) {
|
|
2081
|
+
try {
|
|
2082
|
+
shakeMenu = new ShakeMenu(this, (com.getcapacitor.BridgeActivity) getActivity(), logger);
|
|
2083
|
+
logger.info("Shake menu initialized");
|
|
2084
|
+
} catch (Exception e) {
|
|
2085
|
+
logger.error("Failed to initialize shake menu: " + e.getMessage());
|
|
2086
|
+
}
|
|
2087
|
+
} else if (!enabled && shakeMenu != null) {
|
|
2088
|
+
try {
|
|
2089
|
+
shakeMenu.stop();
|
|
2090
|
+
shakeMenu = null;
|
|
2091
|
+
logger.info("Shake menu stopped");
|
|
2092
|
+
} catch (Exception e) {
|
|
2093
|
+
logger.error("Failed to stop shake menu: " + e.getMessage());
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
call.resolve();
|
|
2098
|
+
}
|
|
2099
|
+
|
|
2100
|
+
@PluginMethod
|
|
2101
|
+
public void isShakeMenuEnabled(final PluginCall call) {
|
|
2102
|
+
try {
|
|
2103
|
+
final JSObject ret = new JSObject();
|
|
2104
|
+
ret.put("enabled", this.shakeMenuEnabled);
|
|
2105
|
+
call.resolve(ret);
|
|
2106
|
+
} catch (final Exception e) {
|
|
2107
|
+
logger.error("Could not get shake menu status " + e.getMessage());
|
|
2108
|
+
call.reject("Could not get shake menu status", e);
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
|
|
2112
|
+
@PluginMethod
|
|
2113
|
+
public void getAppId(final PluginCall call) {
|
|
2114
|
+
try {
|
|
2115
|
+
final JSObject ret = new JSObject();
|
|
2116
|
+
ret.put("appId", this.implementation.appId);
|
|
2117
|
+
call.resolve(ret);
|
|
2118
|
+
} catch (final Exception e) {
|
|
2119
|
+
logger.error("Could not get appId " + e.getMessage());
|
|
2120
|
+
call.reject("Could not get appId", e);
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
|
|
2124
|
+
@PluginMethod
|
|
2125
|
+
public void setAppId(final PluginCall call) {
|
|
2126
|
+
if (!this.getConfig().getBoolean("allowModifyAppId", false)) {
|
|
2127
|
+
logger.error("setAppId not allowed set allowModifyAppId in your config to true to allow it");
|
|
2128
|
+
call.reject("setAppId not allowed");
|
|
2129
|
+
return;
|
|
2130
|
+
}
|
|
2131
|
+
final String appId = call.getString("appId");
|
|
2132
|
+
if (appId == null) {
|
|
2133
|
+
logger.error("setAppId called without appId");
|
|
2134
|
+
call.reject("setAppId called without appId");
|
|
2135
|
+
return;
|
|
2136
|
+
}
|
|
2137
|
+
this.implementation.appId = appId;
|
|
2138
|
+
call.resolve();
|
|
1451
2139
|
}
|
|
1452
2140
|
}
|