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