@capgo/capacitor-updater 8.46.3 → 8.47.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/README.md +178 -19
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +466 -30
- package/android/src/main/java/ee/forgr/capacitor_updater/CapgoUpdater.java +239 -22
- package/android/src/main/java/ee/forgr/capacitor_updater/ShakeMenu.java +95 -3
- package/dist/docs.json +473 -0
- package/dist/esm/definitions.d.ts +241 -0
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +4 -1
- package/dist/esm/web.js +30 -0
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +30 -0
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +30 -0
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/CapacitorUpdaterPlugin/CapacitorUpdaterPlugin.swift +354 -15
- package/ios/Sources/CapacitorUpdaterPlugin/CapgoUpdater.swift +194 -8
- package/ios/Sources/CapacitorUpdaterPlugin/InternalUtils.swift +2 -0
- package/ios/Sources/CapacitorUpdaterPlugin/ShakeMenu.swift +47 -3
- package/package.json +1 -1
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
package ee.forgr.capacitor_updater;
|
|
8
8
|
|
|
9
9
|
import android.app.Activity;
|
|
10
|
+
import android.app.AlertDialog;
|
|
10
11
|
import android.content.Context;
|
|
11
12
|
import android.content.Intent;
|
|
12
13
|
import android.content.SharedPreferences;
|
|
@@ -85,6 +86,12 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
85
86
|
private static final String STATS_URL_PREF_KEY = "CapacitorUpdater.statsUrl";
|
|
86
87
|
private static final String CHANNEL_URL_PREF_KEY = "CapacitorUpdater.channelUrl";
|
|
87
88
|
private static final String DEFAULT_CHANNEL_PREF_KEY = "CapacitorUpdater.defaultChannel";
|
|
89
|
+
private static final String PREVIEW_SESSION_PREF_KEY = "CapacitorUpdater.previewSession";
|
|
90
|
+
private static final String PREVIEW_PREVIOUS_SHAKE_MENU_PREF_KEY = "CapacitorUpdater.previewPreviousShakeMenu";
|
|
91
|
+
private static final String PREVIEW_PREVIOUS_SHAKE_CHANNEL_SELECTOR_PREF_KEY = "CapacitorUpdater.previewPreviousShakeChannelSelector";
|
|
92
|
+
private static final String PREVIEW_PREVIOUS_NEXT_BUNDLE_PREF_KEY = "CapacitorUpdater.previewPreviousNextBundle";
|
|
93
|
+
private static final String PREVIEW_PREVIOUS_APP_ID_PREF_KEY = "CapacitorUpdater.previewPreviousAppId";
|
|
94
|
+
private static final String PREVIEW_APP_ID_PREF_KEY = "CapacitorUpdater.previewAppId";
|
|
88
95
|
private static final String[] BREAKING_EVENT_NAMES = { "breakingAvailable", "majorAvailable" };
|
|
89
96
|
private static final String LAST_FAILED_BUNDLE_PREF_KEY = "CapacitorUpdater.lastFailedBundle";
|
|
90
97
|
private static final String LAST_REPORTED_APP_EXIT_TIMESTAMP_PREF_KEY = "CapacitorUpdater.lastReportedAppExitTimestamp";
|
|
@@ -106,7 +113,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
106
113
|
static final int APPLICATION_EXIT_REASON_USER_REQUESTED = 10;
|
|
107
114
|
static final int APPLICATION_EXIT_REASON_DEPENDENCY_DIED = 12;
|
|
108
115
|
|
|
109
|
-
private final String pluginVersion = "8.
|
|
116
|
+
private final String pluginVersion = "8.47.0";
|
|
110
117
|
private static final String DELAY_CONDITION_PREFERENCES = "";
|
|
111
118
|
|
|
112
119
|
private SharedPreferences.Editor editor;
|
|
@@ -136,7 +143,10 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
136
143
|
private volatile boolean onLaunchDirectUpdateUsed = false;
|
|
137
144
|
Boolean shakeMenuEnabled = false;
|
|
138
145
|
Boolean shakeChannelSelectorEnabled = false;
|
|
146
|
+
Boolean previewSessionEnabled = false;
|
|
147
|
+
private Boolean previewSessionAlertPending = false;
|
|
139
148
|
private Boolean allowManualBundleError = false;
|
|
149
|
+
private Boolean allowPreview = false;
|
|
140
150
|
Boolean allowSetDefaultChannel = true;
|
|
141
151
|
|
|
142
152
|
String getUpdateUrl() {
|
|
@@ -421,6 +431,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
421
431
|
"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"
|
|
422
432
|
);
|
|
423
433
|
}
|
|
434
|
+
this.allowPreview = this.getConfig().getBoolean("allowPreview", false);
|
|
424
435
|
logger.info("appId: " + implementation.appId);
|
|
425
436
|
|
|
426
437
|
this.persistCustomId = this.getConfig().getBoolean("persistCustomId", false);
|
|
@@ -509,12 +520,30 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
509
520
|
this.implementation.timeout = this.getConfig().getInt("responseTimeout", 20) * 1000;
|
|
510
521
|
this.shakeMenuEnabled = this.getConfig().getBoolean("shakeMenu", false);
|
|
511
522
|
this.shakeChannelSelectorEnabled = this.getConfig().getBoolean("allowShakeChannelSelector", false);
|
|
523
|
+
this.previewSessionEnabled = Boolean.TRUE.equals(this.allowPreview) && this.prefs.getBoolean(PREVIEW_SESSION_PREF_KEY, false);
|
|
524
|
+
if (!Boolean.TRUE.equals(this.allowPreview) && this.prefs.getBoolean(PREVIEW_SESSION_PREF_KEY, false)) {
|
|
525
|
+
this.clearPreviewSessionBecauseDisabled();
|
|
526
|
+
}
|
|
527
|
+
this.implementation.previewSession = Boolean.TRUE.equals(this.previewSessionEnabled);
|
|
528
|
+
if (Boolean.TRUE.equals(this.previewSessionEnabled)) {
|
|
529
|
+
final String previewAppId = this.prefs.getString(PREVIEW_APP_ID_PREF_KEY, "");
|
|
530
|
+
if (previewAppId != null && !previewAppId.isEmpty()) {
|
|
531
|
+
this.setActiveAppId(previewAppId);
|
|
532
|
+
logger.info("Using preview appId " + previewAppId);
|
|
533
|
+
}
|
|
534
|
+
this.shakeMenuEnabled = true;
|
|
535
|
+
this.shakeChannelSelectorEnabled = false;
|
|
536
|
+
}
|
|
512
537
|
boolean resetWhenUpdate = this.getConfig().getBoolean("resetWhenUpdate", true);
|
|
513
538
|
|
|
514
539
|
// Check if app was recently installed/updated BEFORE cleanupObsoleteVersions updates LatestVersionNative
|
|
515
540
|
this.wasRecentlyInstalledOrUpdated = this.checkIfRecentlyInstalledOrUpdated();
|
|
541
|
+
final boolean nativeBuildVersionChanged = this.hasNativeBuildVersionChanged();
|
|
516
542
|
|
|
517
543
|
this.implementation.autoReset(this.currentBuildVersion, resetWhenUpdate);
|
|
544
|
+
if (nativeBuildVersionChanged) {
|
|
545
|
+
this.clearPreviewSessionForNativeBuildChange();
|
|
546
|
+
}
|
|
518
547
|
this.reportPreviousAppExitReasons();
|
|
519
548
|
this.reportPreviousWebViewRenderProcessGone();
|
|
520
549
|
this.installWebViewStatsReporter();
|
|
@@ -860,6 +889,11 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
860
889
|
return false;
|
|
861
890
|
}
|
|
862
891
|
|
|
892
|
+
private boolean hasNativeBuildVersionChanged() {
|
|
893
|
+
final String lastKnownVersion = this.getStoredNativeBuildVersion();
|
|
894
|
+
return !lastKnownVersion.isEmpty() && !lastKnownVersion.equals(this.currentBuildVersion);
|
|
895
|
+
}
|
|
896
|
+
|
|
863
897
|
private void reportPreviousAppExitReasons() {
|
|
864
898
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R || this.implementation == null || this.implementation.statsUrl.isEmpty()) {
|
|
865
899
|
return;
|
|
@@ -1057,6 +1091,19 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1057
1091
|
return map;
|
|
1058
1092
|
}
|
|
1059
1093
|
|
|
1094
|
+
private static JSObject jsonObjectToJSObject(final JSONObject json) throws JSONException {
|
|
1095
|
+
final JSObject ret = new JSObject();
|
|
1096
|
+
final JSONArray names = json.names();
|
|
1097
|
+
if (names == null) {
|
|
1098
|
+
return ret;
|
|
1099
|
+
}
|
|
1100
|
+
for (int i = 0; i < names.length(); i++) {
|
|
1101
|
+
final String key = names.getString(i);
|
|
1102
|
+
ret.put(key, json.get(key));
|
|
1103
|
+
}
|
|
1104
|
+
return ret;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1060
1107
|
@PluginMethod
|
|
1061
1108
|
public void reportWebViewError(final PluginCall call) {
|
|
1062
1109
|
final JSObject data = call.getData();
|
|
@@ -2004,6 +2051,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
2004
2051
|
}
|
|
2005
2052
|
this.notifyBundleSet(next);
|
|
2006
2053
|
this.implementation.setNextBundle(null);
|
|
2054
|
+
this.showPreviewSessionNoticeIfNeeded();
|
|
2007
2055
|
call.resolve();
|
|
2008
2056
|
return;
|
|
2009
2057
|
}
|
|
@@ -2015,6 +2063,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
2015
2063
|
}
|
|
2016
2064
|
|
|
2017
2065
|
if (this._reload()) {
|
|
2066
|
+
this.showPreviewSessionNoticeIfNeeded();
|
|
2018
2067
|
call.resolve();
|
|
2019
2068
|
} else {
|
|
2020
2069
|
logger.error("Reload failed");
|
|
@@ -2069,6 +2118,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
2069
2118
|
} else {
|
|
2070
2119
|
logger.info("Bundle successfully set to " + id);
|
|
2071
2120
|
this.notifyBundleSet(this.implementation.getBundleInfo(id));
|
|
2121
|
+
this.showPreviewSessionNoticeIfNeeded();
|
|
2072
2122
|
call.resolve();
|
|
2073
2123
|
}
|
|
2074
2124
|
} catch (final Exception e) {
|
|
@@ -2078,6 +2128,308 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
2078
2128
|
});
|
|
2079
2129
|
}
|
|
2080
2130
|
|
|
2131
|
+
@PluginMethod
|
|
2132
|
+
public void startPreviewSession(final PluginCall call) {
|
|
2133
|
+
if (!Boolean.TRUE.equals(this.allowPreview)) {
|
|
2134
|
+
logger.error("startPreviewSession not allowed set allowPreview in your config to true to enable it");
|
|
2135
|
+
call.reject("startPreviewSession not allowed");
|
|
2136
|
+
return;
|
|
2137
|
+
}
|
|
2138
|
+
final String previewAppId = this.normalizePreviewAppId(call.getString("appId"));
|
|
2139
|
+
startNewThread(() -> {
|
|
2140
|
+
try {
|
|
2141
|
+
if (!Boolean.TRUE.equals(this.previewSessionEnabled)) {
|
|
2142
|
+
final BundleInfo current = this.implementation.getCurrentBundle();
|
|
2143
|
+
if (!this.implementation.setPreviewFallbackBundle(current.getId())) {
|
|
2144
|
+
logger.error("Could not save current bundle as preview fallback");
|
|
2145
|
+
call.reject("Could not save current bundle as preview fallback");
|
|
2146
|
+
return;
|
|
2147
|
+
}
|
|
2148
|
+
|
|
2149
|
+
final BundleInfo previousNext = this.implementation.getNextBundle();
|
|
2150
|
+
if (previousNext == null || previousNext.isDeleted() || previousNext.isErrorStatus()) {
|
|
2151
|
+
this.editor.remove(PREVIEW_PREVIOUS_NEXT_BUNDLE_PREF_KEY);
|
|
2152
|
+
} else {
|
|
2153
|
+
this.editor.putString(PREVIEW_PREVIOUS_NEXT_BUNDLE_PREF_KEY, previousNext.getId());
|
|
2154
|
+
}
|
|
2155
|
+
|
|
2156
|
+
this.editor.putString(PREVIEW_PREVIOUS_APP_ID_PREF_KEY, this.implementation.appId);
|
|
2157
|
+
this.editor.putBoolean(PREVIEW_PREVIOUS_SHAKE_MENU_PREF_KEY, Boolean.TRUE.equals(this.shakeMenuEnabled));
|
|
2158
|
+
this.editor.putBoolean(
|
|
2159
|
+
PREVIEW_PREVIOUS_SHAKE_CHANNEL_SELECTOR_PREF_KEY,
|
|
2160
|
+
Boolean.TRUE.equals(this.shakeChannelSelectorEnabled)
|
|
2161
|
+
);
|
|
2162
|
+
logger.info("Preview session started with fallback bundle: " + current);
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2165
|
+
if (previewAppId != null) {
|
|
2166
|
+
this.setActiveAppId(previewAppId);
|
|
2167
|
+
this.editor.putString(PREVIEW_APP_ID_PREF_KEY, previewAppId);
|
|
2168
|
+
logger.info("Preview session using appId: " + previewAppId);
|
|
2169
|
+
}
|
|
2170
|
+
|
|
2171
|
+
this.previewSessionEnabled = true;
|
|
2172
|
+
this.previewSessionAlertPending = true;
|
|
2173
|
+
this.implementation.previewSession = true;
|
|
2174
|
+
this.shakeMenuEnabled = true;
|
|
2175
|
+
this.shakeChannelSelectorEnabled = false;
|
|
2176
|
+
this.editor.putBoolean(PREVIEW_SESSION_PREF_KEY, true);
|
|
2177
|
+
this.editor.apply();
|
|
2178
|
+
this.ensureShakeMenuStarted();
|
|
2179
|
+
call.resolve();
|
|
2180
|
+
} catch (final Exception e) {
|
|
2181
|
+
logger.error("Could not start preview session " + e.getMessage());
|
|
2182
|
+
call.reject("Could not start preview session", e);
|
|
2183
|
+
}
|
|
2184
|
+
});
|
|
2185
|
+
}
|
|
2186
|
+
|
|
2187
|
+
public boolean leavePreviewSessionFromShakeMenu() {
|
|
2188
|
+
final BundleInfo previewBundle = this.implementation.getCurrentBundle();
|
|
2189
|
+
|
|
2190
|
+
final boolean didReset = this.resetToPreviewFallbackBundle();
|
|
2191
|
+
if (!didReset) {
|
|
2192
|
+
return false;
|
|
2193
|
+
}
|
|
2194
|
+
|
|
2195
|
+
if (!this.clearPreviewChannelOverride()) {
|
|
2196
|
+
return false;
|
|
2197
|
+
}
|
|
2198
|
+
final BundleInfo previewFallbackBundle = this.implementation.getPreviewFallbackBundle();
|
|
2199
|
+
this.endPreviewSession();
|
|
2200
|
+
final BundleInfo restoredNextBundle = this.implementation.getNextBundle();
|
|
2201
|
+
if (
|
|
2202
|
+
!previewBundle.isBuiltin() &&
|
|
2203
|
+
(previewFallbackBundle == null || !previewBundle.getId().equals(previewFallbackBundle.getId())) &&
|
|
2204
|
+
(restoredNextBundle == null || !previewBundle.getId().equals(restoredNextBundle.getId()))
|
|
2205
|
+
) {
|
|
2206
|
+
try {
|
|
2207
|
+
this.implementation.delete(previewBundle.getId(), false);
|
|
2208
|
+
} catch (final Exception err) {
|
|
2209
|
+
logger.warn("Cannot delete preview bundle " + previewBundle.getId() + ": " + err.getMessage());
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
return true;
|
|
2213
|
+
}
|
|
2214
|
+
|
|
2215
|
+
public boolean reloadPreviewSessionFromShakeMenu() {
|
|
2216
|
+
return this._reload();
|
|
2217
|
+
}
|
|
2218
|
+
|
|
2219
|
+
public boolean hasActivePreviewSession() {
|
|
2220
|
+
return Boolean.TRUE.equals(this.previewSessionEnabled);
|
|
2221
|
+
}
|
|
2222
|
+
|
|
2223
|
+
private boolean resetToPreviewFallbackBundle() {
|
|
2224
|
+
final BundleInfo fallback = this.implementation.getPreviewFallbackBundle();
|
|
2225
|
+
if (fallback == null || fallback.isErrorStatus()) {
|
|
2226
|
+
logger.error("No preview fallback bundle available");
|
|
2227
|
+
return false;
|
|
2228
|
+
}
|
|
2229
|
+
if (!this.implementation.canSet(fallback)) {
|
|
2230
|
+
logger.error("Preview fallback bundle is not installable");
|
|
2231
|
+
return false;
|
|
2232
|
+
}
|
|
2233
|
+
|
|
2234
|
+
final CapgoUpdater.ResetState previousState = this.implementation.captureResetState();
|
|
2235
|
+
final String previousBundleName = this.implementation.getCurrentBundle().getVersionName();
|
|
2236
|
+
logger.info("Resetting to preview fallback bundle: " + fallback.getVersionName());
|
|
2237
|
+
if (this.implementation.stagePreviewFallbackReload(fallback) && this._reload()) {
|
|
2238
|
+
this.implementation.finalizeResetTransition(previousBundleName, false);
|
|
2239
|
+
this.notifyBundleSet(fallback);
|
|
2240
|
+
return true;
|
|
2241
|
+
}
|
|
2242
|
+
this.implementation.restoreResetState(previousState);
|
|
2243
|
+
this.restoreLiveBundleStateAfterFailedReload();
|
|
2244
|
+
return false;
|
|
2245
|
+
}
|
|
2246
|
+
|
|
2247
|
+
private void endPreviewSession() {
|
|
2248
|
+
final boolean previousShakeMenuEnabled = this.prefs.getBoolean(
|
|
2249
|
+
PREVIEW_PREVIOUS_SHAKE_MENU_PREF_KEY,
|
|
2250
|
+
this.getConfig().getBoolean("shakeMenu", false)
|
|
2251
|
+
);
|
|
2252
|
+
final boolean previousShakeChannelSelectorEnabled = this.prefs.getBoolean(
|
|
2253
|
+
PREVIEW_PREVIOUS_SHAKE_CHANNEL_SELECTOR_PREF_KEY,
|
|
2254
|
+
this.getConfig().getBoolean("allowShakeChannelSelector", false)
|
|
2255
|
+
);
|
|
2256
|
+
this.restorePreviewPreviousNextBundle();
|
|
2257
|
+
this.restorePreviewPreviousAppId();
|
|
2258
|
+
|
|
2259
|
+
this.previewSessionEnabled = false;
|
|
2260
|
+
this.previewSessionAlertPending = false;
|
|
2261
|
+
this.implementation.previewSession = false;
|
|
2262
|
+
this.shakeMenuEnabled = previousShakeMenuEnabled;
|
|
2263
|
+
this.shakeChannelSelectorEnabled = previousShakeChannelSelectorEnabled;
|
|
2264
|
+
this.implementation.setPreviewFallbackBundle(null);
|
|
2265
|
+
this.clearPreviewSessionPreferences();
|
|
2266
|
+
logger.info("Preview session ended");
|
|
2267
|
+
}
|
|
2268
|
+
|
|
2269
|
+
private void clearPreviewSessionBecauseDisabled() {
|
|
2270
|
+
logger.info("Preview session disabled by config; restoring preview fallback");
|
|
2271
|
+
final BundleInfo fallback = this.implementation.getPreviewFallbackBundle();
|
|
2272
|
+
final BundleInfo bundleToRestore = fallback == null || fallback.isErrorStatus()
|
|
2273
|
+
? this.implementation.getBundleInfo(BundleInfo.ID_BUILTIN)
|
|
2274
|
+
: fallback;
|
|
2275
|
+
|
|
2276
|
+
if (this.implementation.canSet(bundleToRestore)) {
|
|
2277
|
+
this.implementation.stagePreviewFallbackReload(bundleToRestore);
|
|
2278
|
+
} else {
|
|
2279
|
+
logger.warn("Could not restore preview fallback while disabling preview");
|
|
2280
|
+
}
|
|
2281
|
+
|
|
2282
|
+
this.restorePreviewPreviousNextBundle();
|
|
2283
|
+
this.restorePreviewPreviousAppId();
|
|
2284
|
+
this.previewSessionEnabled = false;
|
|
2285
|
+
this.previewSessionAlertPending = false;
|
|
2286
|
+
this.implementation.previewSession = false;
|
|
2287
|
+
this.shakeMenuEnabled = this.getConfig().getBoolean("shakeMenu", false);
|
|
2288
|
+
this.shakeChannelSelectorEnabled = this.getConfig().getBoolean("allowShakeChannelSelector", false);
|
|
2289
|
+
this.clearPreviewSessionPreferences();
|
|
2290
|
+
}
|
|
2291
|
+
|
|
2292
|
+
private void clearPreviewSessionPreferences() {
|
|
2293
|
+
if (this.implementation != null) {
|
|
2294
|
+
this.implementation.setPreviewFallbackBundle(null);
|
|
2295
|
+
}
|
|
2296
|
+
this.editor.remove(PREVIEW_SESSION_PREF_KEY);
|
|
2297
|
+
this.editor.remove(PREVIEW_PREVIOUS_SHAKE_MENU_PREF_KEY);
|
|
2298
|
+
this.editor.remove(PREVIEW_PREVIOUS_SHAKE_CHANNEL_SELECTOR_PREF_KEY);
|
|
2299
|
+
this.editor.remove(PREVIEW_PREVIOUS_NEXT_BUNDLE_PREF_KEY);
|
|
2300
|
+
this.editor.remove(PREVIEW_PREVIOUS_APP_ID_PREF_KEY);
|
|
2301
|
+
this.editor.remove(PREVIEW_APP_ID_PREF_KEY);
|
|
2302
|
+
this.editor.apply();
|
|
2303
|
+
}
|
|
2304
|
+
|
|
2305
|
+
private void setActiveAppId(final String appId) {
|
|
2306
|
+
this.implementation.appId = appId;
|
|
2307
|
+
if (this.implementation.versionOs != null) {
|
|
2308
|
+
DownloadService.updateUserAgent(this.implementation.appId, this.pluginVersion, this.implementation.versionOs);
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
|
|
2312
|
+
private void restorePreviewPreviousAppId() {
|
|
2313
|
+
final String previousAppId = this.prefs.getString(PREVIEW_PREVIOUS_APP_ID_PREF_KEY, "");
|
|
2314
|
+
if (previousAppId == null || previousAppId.isEmpty()) {
|
|
2315
|
+
return;
|
|
2316
|
+
}
|
|
2317
|
+
this.setActiveAppId(previousAppId);
|
|
2318
|
+
logger.info("Restored appId after preview: " + previousAppId);
|
|
2319
|
+
}
|
|
2320
|
+
|
|
2321
|
+
private String normalizePreviewAppId(final String rawAppId) {
|
|
2322
|
+
if (rawAppId == null) {
|
|
2323
|
+
return null;
|
|
2324
|
+
}
|
|
2325
|
+
|
|
2326
|
+
final String appId = rawAppId.trim();
|
|
2327
|
+
if (appId.isEmpty()) {
|
|
2328
|
+
return null;
|
|
2329
|
+
}
|
|
2330
|
+
|
|
2331
|
+
final String lowercasedAppId = appId.toLowerCase(java.util.Locale.ROOT);
|
|
2332
|
+
if ("undefined".equals(lowercasedAppId) || "null".equals(lowercasedAppId)) {
|
|
2333
|
+
return null;
|
|
2334
|
+
}
|
|
2335
|
+
|
|
2336
|
+
return appId;
|
|
2337
|
+
}
|
|
2338
|
+
|
|
2339
|
+
private void clearPreviewSessionForNativeBuildChange() {
|
|
2340
|
+
if (!Boolean.TRUE.equals(this.previewSessionEnabled) && this.implementation.getPreviewFallbackBundle() == null) {
|
|
2341
|
+
return;
|
|
2342
|
+
}
|
|
2343
|
+
logger.info("Native build changed; clearing preview session state");
|
|
2344
|
+
this.previewSessionEnabled = false;
|
|
2345
|
+
this.previewSessionAlertPending = false;
|
|
2346
|
+
this.implementation.previewSession = false;
|
|
2347
|
+
this.shakeMenuEnabled = this.getConfig().getBoolean("shakeMenu", false);
|
|
2348
|
+
this.shakeChannelSelectorEnabled = this.getConfig().getBoolean("allowShakeChannelSelector", false);
|
|
2349
|
+
this.restorePreviewPreviousAppId();
|
|
2350
|
+
this.implementation.setPreviewFallbackBundle(null);
|
|
2351
|
+
this.implementation.setNextBundle(null);
|
|
2352
|
+
this.clearPreviewChannelOverride();
|
|
2353
|
+
this.clearPreviewSessionPreferences();
|
|
2354
|
+
}
|
|
2355
|
+
|
|
2356
|
+
private boolean clearPreviewChannelOverride() {
|
|
2357
|
+
final String configDefaultChannel = this.getConfig().getString("defaultChannel", "");
|
|
2358
|
+
final AtomicReference<Map<String, Object>> unsetChannelResult = new AtomicReference<>();
|
|
2359
|
+
try {
|
|
2360
|
+
this.implementation.unsetChannel(this.editor, DEFAULT_CHANNEL_PREF_KEY, configDefaultChannel, unsetChannelResult::set);
|
|
2361
|
+
} catch (final Exception err) {
|
|
2362
|
+
logger.error("Could not clear preview channel override: " + err.getMessage());
|
|
2363
|
+
return false;
|
|
2364
|
+
}
|
|
2365
|
+
|
|
2366
|
+
final Map<String, Object> result = unsetChannelResult.get();
|
|
2367
|
+
if (result == null) {
|
|
2368
|
+
logger.error("Could not clear preview channel override: no result");
|
|
2369
|
+
return false;
|
|
2370
|
+
}
|
|
2371
|
+
if (result.containsKey("error")) {
|
|
2372
|
+
final Object message = result.getOrDefault("message", result.get("error"));
|
|
2373
|
+
logger.error("Could not clear preview channel override: " + message);
|
|
2374
|
+
return false;
|
|
2375
|
+
}
|
|
2376
|
+
return true;
|
|
2377
|
+
}
|
|
2378
|
+
|
|
2379
|
+
private void restorePreviewPreviousNextBundle() {
|
|
2380
|
+
final String previousNextBundleId = this.prefs.getString(PREVIEW_PREVIOUS_NEXT_BUNDLE_PREF_KEY, null);
|
|
2381
|
+
if (previousNextBundleId == null || previousNextBundleId.isEmpty()) {
|
|
2382
|
+
this.implementation.setNextBundle(null);
|
|
2383
|
+
return;
|
|
2384
|
+
}
|
|
2385
|
+
if (!this.implementation.setNextBundle(previousNextBundleId)) {
|
|
2386
|
+
logger.warn("Could not restore pre-preview next bundle: " + previousNextBundleId);
|
|
2387
|
+
this.implementation.setNextBundle(null);
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
|
|
2391
|
+
private void ensureShakeMenuStarted() {
|
|
2392
|
+
if (getActivity() instanceof com.getcapacitor.BridgeActivity && shakeMenu == null) {
|
|
2393
|
+
try {
|
|
2394
|
+
shakeMenu = new ShakeMenu(this, (com.getcapacitor.BridgeActivity) getActivity(), logger);
|
|
2395
|
+
logger.info("Shake menu initialized");
|
|
2396
|
+
} catch (Exception e) {
|
|
2397
|
+
logger.error("Failed to initialize shake menu: " + e.getMessage());
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
|
|
2402
|
+
private void showPreviewSessionNoticeIfNeeded() {
|
|
2403
|
+
if (!Boolean.TRUE.equals(this.previewSessionEnabled) || !Boolean.TRUE.equals(this.previewSessionAlertPending)) {
|
|
2404
|
+
return;
|
|
2405
|
+
}
|
|
2406
|
+
this.previewSessionAlertPending = false;
|
|
2407
|
+
|
|
2408
|
+
new Handler(Looper.getMainLooper()).postDelayed(
|
|
2409
|
+
() -> {
|
|
2410
|
+
try {
|
|
2411
|
+
if (!Boolean.TRUE.equals(this.previewSessionEnabled)) {
|
|
2412
|
+
return;
|
|
2413
|
+
}
|
|
2414
|
+
if (getActivity() == null || getActivity().isFinishing()) {
|
|
2415
|
+
this.previewSessionAlertPending = true;
|
|
2416
|
+
return;
|
|
2417
|
+
}
|
|
2418
|
+
|
|
2419
|
+
new AlertDialog.Builder(getActivity())
|
|
2420
|
+
.setTitle("Preview started")
|
|
2421
|
+
.setMessage("Shake your device anytime to reload or leave the test app.")
|
|
2422
|
+
.setPositiveButton("Got it", (dialog, which) -> dialog.dismiss())
|
|
2423
|
+
.show();
|
|
2424
|
+
} catch (final Exception e) {
|
|
2425
|
+
this.previewSessionAlertPending = true;
|
|
2426
|
+
logger.warn("Could not show preview session notice: " + e.getMessage());
|
|
2427
|
+
}
|
|
2428
|
+
},
|
|
2429
|
+
600
|
|
2430
|
+
);
|
|
2431
|
+
}
|
|
2432
|
+
|
|
2081
2433
|
@PluginMethod
|
|
2082
2434
|
public void delete(final PluginCall call) {
|
|
2083
2435
|
final String id = call.getString("id");
|
|
@@ -2159,37 +2511,121 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
2159
2511
|
@PluginMethod
|
|
2160
2512
|
public void getLatest(final PluginCall call) {
|
|
2161
2513
|
final String channel = call.getString("channel");
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
} else if (jsRes.has("message")) {
|
|
2184
|
-
String latestVersion = jsRes.has("version") ? jsRes.getString("version") : "";
|
|
2185
|
-
CapacitorUpdaterPlugin.this.notifyBreakingEventsIfNeeded(jsRes, latestVersion);
|
|
2186
|
-
call.reject(jsRes.getString("message"));
|
|
2187
|
-
return;
|
|
2514
|
+
final boolean includeBundleSize = call.getBoolean("includeBundleSize", false);
|
|
2515
|
+
final String previewAppId = this.normalizePreviewAppId(call.getString("appId"));
|
|
2516
|
+
final boolean hasPreviewAppId = previewAppId != null;
|
|
2517
|
+
if (hasPreviewAppId && !Boolean.TRUE.equals(this.allowPreview)) {
|
|
2518
|
+
logger.error("getLatest preview override not allowed set allowPreview in your config to true to enable it");
|
|
2519
|
+
call.reject("getLatest preview override not allowed");
|
|
2520
|
+
return;
|
|
2521
|
+
}
|
|
2522
|
+
|
|
2523
|
+
final Callback latestCallback = (res) -> {
|
|
2524
|
+
JSObject jsRes = InternalUtils.mapToJSObject(res);
|
|
2525
|
+
if (jsRes.has("error") || jsRes.has("kind")) {
|
|
2526
|
+
String error = jsRes.has("error") ? jsRes.getString("error") : "";
|
|
2527
|
+
String errorMessage = jsRes.has("message") ? jsRes.getString("message") : "server did not provide a message";
|
|
2528
|
+
String kind = CapacitorUpdaterPlugin.this.getUpdateResponseKind(jsRes.has("kind") ? jsRes.getString("kind") : null);
|
|
2529
|
+
String latestVersion = jsRes.has("version") ? jsRes.getString("version") : "";
|
|
2530
|
+
jsRes.put("kind", kind);
|
|
2531
|
+
CapacitorUpdaterPlugin.this.notifyBreakingEventsIfNeeded(jsRes, latestVersion);
|
|
2532
|
+
if ("failed".equals(kind)) {
|
|
2533
|
+
logger.error("getLatest failed with error: " + error + ", message: " + errorMessage);
|
|
2534
|
+
call.reject(error.isEmpty() ? errorMessage : error);
|
|
2188
2535
|
} else {
|
|
2536
|
+
if (!jsRes.has("version") || jsRes.getString("version").isEmpty()) {
|
|
2537
|
+
jsRes.put("version", CapacitorUpdaterPlugin.this.implementation.getCurrentBundle().getVersionName());
|
|
2538
|
+
}
|
|
2539
|
+
logger.info("getLatest returned " + kind + ": " + errorMessage);
|
|
2189
2540
|
call.resolve(jsRes);
|
|
2190
2541
|
}
|
|
2191
|
-
|
|
2192
|
-
|
|
2542
|
+
return;
|
|
2543
|
+
} else if (jsRes.has("message")) {
|
|
2544
|
+
String latestVersion = jsRes.has("version") ? jsRes.getString("version") : "";
|
|
2545
|
+
CapacitorUpdaterPlugin.this.notifyBreakingEventsIfNeeded(jsRes, latestVersion);
|
|
2546
|
+
call.reject(jsRes.getString("message"));
|
|
2547
|
+
return;
|
|
2548
|
+
} else {
|
|
2549
|
+
if (includeBundleSize) {
|
|
2550
|
+
CapacitorUpdaterPlugin.this.attachBundleSize(jsRes);
|
|
2551
|
+
}
|
|
2552
|
+
call.resolve(jsRes);
|
|
2553
|
+
}
|
|
2554
|
+
};
|
|
2555
|
+
|
|
2556
|
+
startNewThread(() -> {
|
|
2557
|
+
if (hasPreviewAppId) {
|
|
2558
|
+
CapacitorUpdaterPlugin.this.implementation.getLatest(
|
|
2559
|
+
CapacitorUpdaterPlugin.this.updateUrl,
|
|
2560
|
+
channel,
|
|
2561
|
+
previewAppId,
|
|
2562
|
+
latestCallback
|
|
2563
|
+
);
|
|
2564
|
+
return;
|
|
2565
|
+
}
|
|
2566
|
+
CapacitorUpdaterPlugin.this.implementation.getLatest(CapacitorUpdaterPlugin.this.updateUrl, channel, latestCallback);
|
|
2567
|
+
});
|
|
2568
|
+
}
|
|
2569
|
+
|
|
2570
|
+
private void attachBundleSize(final JSObject latest) {
|
|
2571
|
+
try {
|
|
2572
|
+
final JSONArray manifest = latest.optJSONArray("manifest");
|
|
2573
|
+
if (manifest == null || manifest.length() == 0) {
|
|
2574
|
+
return;
|
|
2575
|
+
}
|
|
2576
|
+
final String sessionKey = latest.optString("sessionKey", "");
|
|
2577
|
+
final JSONObject missing = this.implementation.missingBundleFilesResult(manifest, sessionKey);
|
|
2578
|
+
final JSONArray missingManifest = missing.getJSONArray("missing");
|
|
2579
|
+
final JSONObject size = this.implementation.getBundleDownloadSize(
|
|
2580
|
+
this.updateUrl,
|
|
2581
|
+
latest.optString("version", ""),
|
|
2582
|
+
missingManifest
|
|
2583
|
+
);
|
|
2584
|
+
latest.put("missing", missing);
|
|
2585
|
+
latest.put("downloadSize", size);
|
|
2586
|
+
} catch (Exception e) {
|
|
2587
|
+
logger.error("Failed to attach bundle size to getLatest result");
|
|
2588
|
+
logger.debug("Error: " + e.getMessage());
|
|
2589
|
+
}
|
|
2590
|
+
}
|
|
2591
|
+
|
|
2592
|
+
@PluginMethod
|
|
2593
|
+
public void getMissingBundleFiles(final PluginCall call) {
|
|
2594
|
+
final JSONArray manifest = call.getData().optJSONArray("manifest");
|
|
2595
|
+
if (manifest == null) {
|
|
2596
|
+
call.reject("getMissingBundleFiles called without manifest");
|
|
2597
|
+
return;
|
|
2598
|
+
}
|
|
2599
|
+
String sessionKey = call.getString("sessionKey");
|
|
2600
|
+
if (sessionKey == null) {
|
|
2601
|
+
sessionKey = "";
|
|
2602
|
+
}
|
|
2603
|
+
final String finalSessionKey = sessionKey;
|
|
2604
|
+
startNewThread(() -> {
|
|
2605
|
+
try {
|
|
2606
|
+
call.resolve(jsonObjectToJSObject(this.implementation.missingBundleFilesResult(manifest, finalSessionKey)));
|
|
2607
|
+
} catch (Exception e) {
|
|
2608
|
+
call.reject("Could not get missing bundle files", e);
|
|
2609
|
+
}
|
|
2610
|
+
});
|
|
2611
|
+
}
|
|
2612
|
+
|
|
2613
|
+
@PluginMethod
|
|
2614
|
+
public void getBundleDownloadSize(final PluginCall call) {
|
|
2615
|
+
final JSONArray manifest = call.getData().optJSONArray("manifest");
|
|
2616
|
+
if (manifest == null) {
|
|
2617
|
+
call.reject("getBundleDownloadSize called without manifest");
|
|
2618
|
+
return;
|
|
2619
|
+
}
|
|
2620
|
+
final String version = call.getData().optString("version", "");
|
|
2621
|
+
startNewThread(() -> {
|
|
2622
|
+
try {
|
|
2623
|
+
final JSONObject size = this.implementation.getBundleDownloadSize(this.updateUrl, version, manifest);
|
|
2624
|
+
call.resolve(jsonObjectToJSObject(size));
|
|
2625
|
+
} catch (Exception e) {
|
|
2626
|
+
call.reject("Could not get bundle download size", e);
|
|
2627
|
+
}
|
|
2628
|
+
});
|
|
2193
2629
|
}
|
|
2194
2630
|
|
|
2195
2631
|
public String triggerBackgroundUpdateCheck() {
|
|
@@ -3330,7 +3766,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
3330
3766
|
call.reject("setAppId called without appId");
|
|
3331
3767
|
return;
|
|
3332
3768
|
}
|
|
3333
|
-
this.
|
|
3769
|
+
this.setActiveAppId(appId);
|
|
3334
3770
|
call.resolve();
|
|
3335
3771
|
}
|
|
3336
3772
|
|