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