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