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