@capgo/capacitor-updater 6.13.2 → 6.14.2

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.
Files changed (30) hide show
  1. package/README.md +25 -14
  2. package/android/src/main/java/ee/forgr/capacitor_updater/BundleInfo.java +134 -194
  3. package/android/src/main/java/ee/forgr/capacitor_updater/BundleStatus.java +23 -23
  4. package/android/src/main/java/ee/forgr/capacitor_updater/Callback.java +1 -1
  5. package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdater.java +964 -1153
  6. package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +1259 -1628
  7. package/android/src/main/java/ee/forgr/capacitor_updater/CryptoCipher.java +161 -197
  8. package/android/src/main/java/ee/forgr/capacitor_updater/CryptoCipherV2.java +202 -256
  9. package/android/src/main/java/ee/forgr/capacitor_updater/DataManager.java +16 -16
  10. package/android/src/main/java/ee/forgr/capacitor_updater/DelayCondition.java +44 -48
  11. package/android/src/main/java/ee/forgr/capacitor_updater/DelayUntilNext.java +4 -4
  12. package/android/src/main/java/ee/forgr/capacitor_updater/DownloadService.java +378 -467
  13. package/android/src/main/java/ee/forgr/capacitor_updater/DownloadWorkerManager.java +71 -83
  14. package/android/src/main/java/ee/forgr/capacitor_updater/InternalUtils.java +19 -28
  15. package/dist/docs.json +57 -21
  16. package/dist/esm/definitions.d.ts +24 -15
  17. package/dist/esm/definitions.js.map +1 -1
  18. package/dist/esm/index.d.ts +2 -2
  19. package/dist/esm/index.js +4 -4
  20. package/dist/esm/index.js.map +1 -1
  21. package/dist/esm/web.d.ts +2 -2
  22. package/dist/esm/web.js +43 -43
  23. package/dist/esm/web.js.map +1 -1
  24. package/dist/plugin.cjs.js +43 -43
  25. package/dist/plugin.cjs.js.map +1 -1
  26. package/dist/plugin.js +43 -43
  27. package/dist/plugin.js.map +1 -1
  28. package/ios/Plugin/CapacitorUpdater.swift +51 -29
  29. package/ios/Plugin/CapacitorUpdaterPlugin.swift +3 -2
  30. package/package.json +5 -7
@@ -53,1731 +53,1362 @@ import org.json.JSONException;
53
53
  @CapacitorPlugin(name = "CapacitorUpdater")
54
54
  public class CapacitorUpdaterPlugin extends Plugin {
55
55
 
56
- private static final String updateUrlDefault =
57
- "https://plugin.capgo.app/updates";
58
- private static final String statsUrlDefault =
59
- "https://plugin.capgo.app/stats";
60
- private static final String channelUrlDefault =
61
- "https://plugin.capgo.app/channel_self";
62
-
63
- private final String PLUGIN_VERSION = "6.13.2";
64
- private static final String DELAY_CONDITION_PREFERENCES = "";
65
-
66
- private SharedPreferences.Editor editor;
67
- private SharedPreferences prefs;
68
- protected CapacitorUpdater implementation;
69
-
70
- private Integer appReadyTimeout = 10000;
71
- private Integer counterActivityCreate = 0;
72
- private Integer periodCheckDelay = 0;
73
- private Boolean autoDeleteFailed = true;
74
- private Boolean autoDeletePrevious = true;
75
- private Boolean autoUpdate = false;
76
- private String updateUrl = "";
77
- private Version currentVersionNative;
78
- private Thread backgroundTask;
79
- private Boolean taskRunning = false;
80
- private Boolean keepUrlPathAfterReload = false;
81
-
82
- private Boolean isPreviousMainActivity = true;
83
-
84
- private volatile Thread backgroundDownloadTask;
85
- private volatile Thread appReadyCheck;
86
-
87
- // private static final CountDownLatch semaphoreReady = new CountDownLatch(1);
88
- private static final Phaser semaphoreReady = new Phaser(1);
89
-
90
- private int lastNotifiedStatPercent = 0;
91
-
92
- public Thread startNewThread(final Runnable function, Number waitTime) {
93
- Thread bgTask = new Thread(() -> {
94
- try {
95
- if (waitTime.longValue() > 0) {
96
- Thread.sleep(waitTime.longValue());
97
- }
98
- function.run();
99
- } catch (Exception e) {
100
- e.printStackTrace();
101
- }
102
- });
103
- bgTask.start();
104
- return bgTask;
105
- }
106
-
107
- public Thread startNewThread(final Runnable function) {
108
- return startNewThread(function, 0);
109
- }
110
-
111
- @Override
112
- public void load() {
113
- super.load();
114
- this.counterActivityCreate++;
115
- this.prefs = this.getContext()
116
- .getSharedPreferences(WebView.WEBVIEW_PREFS_NAME, Activity.MODE_PRIVATE);
117
- this.editor = this.prefs.edit();
118
-
119
- try {
120
- this.implementation = new CapacitorUpdater() {
121
- @Override
122
- public void notifyDownload(final String id, final int percent) {
123
- CapacitorUpdaterPlugin.this.notifyDownload(id, percent);
124
- }
56
+ private static final String updateUrlDefault = "https://plugin.capgo.app/updates";
57
+ private static final String statsUrlDefault = "https://plugin.capgo.app/stats";
58
+ private static final String channelUrlDefault = "https://plugin.capgo.app/channel_self";
125
59
 
126
- @Override
127
- public void directUpdateFinish(final BundleInfo latest) {
128
- CapacitorUpdaterPlugin.this.directUpdateFinish(latest);
60
+ private final String PLUGIN_VERSION = "6.14.2";
61
+ private static final String DELAY_CONDITION_PREFERENCES = "";
62
+
63
+ private SharedPreferences.Editor editor;
64
+ private SharedPreferences prefs;
65
+ protected CapacitorUpdater implementation;
66
+
67
+ private Integer appReadyTimeout = 10000;
68
+ private Integer counterActivityCreate = 0;
69
+ private Integer periodCheckDelay = 0;
70
+ private Boolean autoDeleteFailed = true;
71
+ private Boolean autoDeletePrevious = true;
72
+ private Boolean autoUpdate = false;
73
+ private String updateUrl = "";
74
+ private Version currentVersionNative;
75
+ private Thread backgroundTask;
76
+ private Boolean taskRunning = false;
77
+ private Boolean keepUrlPathAfterReload = false;
78
+
79
+ private Boolean isPreviousMainActivity = true;
80
+
81
+ private volatile Thread backgroundDownloadTask;
82
+ private volatile Thread appReadyCheck;
83
+
84
+ // private static final CountDownLatch semaphoreReady = new CountDownLatch(1);
85
+ private static final Phaser semaphoreReady = new Phaser(1);
86
+
87
+ private int lastNotifiedStatPercent = 0;
88
+
89
+ public Thread startNewThread(final Runnable function, Number waitTime) {
90
+ Thread bgTask = new Thread(() -> {
91
+ try {
92
+ if (waitTime.longValue() > 0) {
93
+ Thread.sleep(waitTime.longValue());
94
+ }
95
+ function.run();
96
+ } catch (Exception e) {
97
+ e.printStackTrace();
98
+ }
99
+ });
100
+ bgTask.start();
101
+ return bgTask;
102
+ }
103
+
104
+ public Thread startNewThread(final Runnable function) {
105
+ return startNewThread(function, 0);
106
+ }
107
+
108
+ @Override
109
+ public void load() {
110
+ super.load();
111
+ this.counterActivityCreate++;
112
+ this.prefs = this.getContext().getSharedPreferences(WebView.WEBVIEW_PREFS_NAME, Activity.MODE_PRIVATE);
113
+ this.editor = this.prefs.edit();
114
+
115
+ try {
116
+ this.implementation = new CapacitorUpdater() {
117
+ @Override
118
+ public void notifyDownload(final String id, final int percent) {
119
+ CapacitorUpdaterPlugin.this.notifyDownload(id, percent);
120
+ }
121
+
122
+ @Override
123
+ public void directUpdateFinish(final BundleInfo latest) {
124
+ CapacitorUpdaterPlugin.this.directUpdateFinish(latest);
125
+ }
126
+
127
+ @Override
128
+ public void notifyListeners(final String id, final JSObject res) {
129
+ CapacitorUpdaterPlugin.this.notifyListeners(id, res);
130
+ }
131
+ };
132
+ final PackageInfo pInfo = this.getContext().getPackageManager().getPackageInfo(this.getContext().getPackageName(), 0);
133
+ this.implementation.activity = this.getActivity();
134
+ this.implementation.versionBuild = this.getConfig().getString("version", pInfo.versionName);
135
+ this.implementation.PLUGIN_VERSION = this.PLUGIN_VERSION;
136
+ this.implementation.versionCode = Integer.toString(pInfo.versionCode);
137
+ this.implementation.client = new OkHttpClient.Builder()
138
+ .protocols(Arrays.asList(Protocol.HTTP_2, Protocol.HTTP_1_1))
139
+ .connectTimeout(this.implementation.timeout, TimeUnit.MILLISECONDS)
140
+ .readTimeout(this.implementation.timeout, TimeUnit.MILLISECONDS)
141
+ .writeTimeout(this.implementation.timeout, TimeUnit.MILLISECONDS)
142
+ .build();
143
+
144
+ this.implementation.directUpdate = this.getConfig().getBoolean("directUpdate", false);
145
+ this.currentVersionNative = new Version(this.getConfig().getString("version", pInfo.versionName));
146
+ } catch (final PackageManager.NameNotFoundException e) {
147
+ Log.e(CapacitorUpdater.TAG, "Error instantiating implementation", e);
148
+ return;
149
+ } catch (final Exception e) {
150
+ Log.e(CapacitorUpdater.TAG, "Error getting current native app version", e);
151
+ return;
152
+ }
153
+ final CapConfig config = CapConfig.loadDefault(this.getActivity());
154
+ this.implementation.appId = InternalUtils.getPackageName(getContext().getPackageManager(), getContext().getPackageName());
155
+ this.implementation.appId = config.getString("appId", this.implementation.appId);
156
+ this.implementation.appId = this.getConfig().getString("appId", this.implementation.appId);
157
+ if (this.implementation.appId == null || this.implementation.appId.isEmpty()) {
158
+ // crash the app
159
+ throw new RuntimeException(
160
+ "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
+ );
162
+ }
163
+ Log.i(CapacitorUpdater.TAG, "appId: " + implementation.appId);
164
+ this.implementation.publicKey = this.getConfig().getString("publicKey", "");
165
+ this.implementation.privateKey = this.getConfig().getString("privateKey", "");
166
+ if (this.implementation.privateKey != null && !this.implementation.privateKey.isEmpty()) {
167
+ this.implementation.hasOldPrivateKeyPropertyInConfig = true;
168
+ }
169
+ this.implementation.statsUrl = this.getConfig().getString("statsUrl", statsUrlDefault);
170
+ this.implementation.channelUrl = this.getConfig().getString("channelUrl", channelUrlDefault);
171
+ int userValue = this.getConfig().getInt("periodCheckDelay", 0);
172
+ this.implementation.defaultChannel = this.getConfig().getString("defaultChannel", "");
173
+
174
+ if (userValue >= 0 && userValue <= 600) {
175
+ this.periodCheckDelay = 600 * 1000;
176
+ } else if (userValue > 600) {
177
+ this.periodCheckDelay = userValue * 1000;
129
178
  }
130
179
 
131
- @Override
132
- public void notifyListeners(final String id, final JSObject res) {
133
- CapacitorUpdaterPlugin.this.notifyListeners(id, res);
134
- }
135
- };
136
- final PackageInfo pInfo =
137
- this.getContext()
138
- .getPackageManager()
139
- .getPackageInfo(this.getContext().getPackageName(), 0);
140
- this.implementation.activity = this.getActivity();
141
- this.implementation.versionBuild = this.getConfig()
142
- .getString("version", pInfo.versionName);
143
- this.implementation.PLUGIN_VERSION = this.PLUGIN_VERSION;
144
- this.implementation.versionCode = Integer.toString(pInfo.versionCode);
145
- this.implementation.client = new OkHttpClient.Builder()
146
- .protocols(Arrays.asList(Protocol.HTTP_2, Protocol.HTTP_1_1))
147
- .connectTimeout(this.implementation.timeout, TimeUnit.MILLISECONDS)
148
- .readTimeout(this.implementation.timeout, TimeUnit.MILLISECONDS)
149
- .writeTimeout(this.implementation.timeout, TimeUnit.MILLISECONDS)
150
- .build();
151
-
152
- this.implementation.directUpdate = this.getConfig()
153
- .getBoolean("directUpdate", false);
154
- this.currentVersionNative = new Version(
155
- this.getConfig().getString("version", pInfo.versionName)
156
- );
157
- } catch (final PackageManager.NameNotFoundException e) {
158
- Log.e(CapacitorUpdater.TAG, "Error instantiating implementation", e);
159
- return;
160
- } catch (final Exception e) {
161
- Log.e(
162
- CapacitorUpdater.TAG,
163
- "Error getting current native app version",
164
- e
165
- );
166
- return;
180
+ this.implementation.documentsDir = this.getContext().getFilesDir();
181
+ this.implementation.prefs = this.prefs;
182
+ this.implementation.editor = this.editor;
183
+ this.implementation.versionOs = Build.VERSION.RELEASE;
184
+ this.implementation.deviceID = this.prefs.getString("appUUID", UUID.randomUUID().toString()).toLowerCase();
185
+ this.editor.putString("appUUID", this.implementation.deviceID);
186
+ this.editor.commit();
187
+ Log.i(CapacitorUpdater.TAG, "init for device " + this.implementation.deviceID);
188
+ Log.i(CapacitorUpdater.TAG, "version native " + this.currentVersionNative.getOriginalString());
189
+ this.autoDeleteFailed = this.getConfig().getBoolean("autoDeleteFailed", true);
190
+ this.autoDeletePrevious = this.getConfig().getBoolean("autoDeletePrevious", true);
191
+ this.updateUrl = this.getConfig().getString("updateUrl", updateUrlDefault);
192
+ this.autoUpdate = this.getConfig().getBoolean("autoUpdate", true);
193
+ this.appReadyTimeout = this.getConfig().getInt("appReadyTimeout", 10000);
194
+ this.keepUrlPathAfterReload = this.getConfig().getBoolean("keepUrlPathAfterReload", false);
195
+ this.implementation.timeout = this.getConfig().getInt("responseTimeout", 20) * 1000;
196
+ boolean resetWhenUpdate = this.getConfig().getBoolean("resetWhenUpdate", true);
197
+
198
+ this.implementation.autoReset();
199
+ if (resetWhenUpdate) {
200
+ this.cleanupObsoleteVersions();
201
+ }
202
+ this.checkForUpdateAfterDelay();
167
203
  }
168
- final CapConfig config = CapConfig.loadDefault(this.getActivity());
169
- this.implementation.appId = InternalUtils.getPackageName(
170
- getContext().getPackageManager(),
171
- getContext().getPackageName()
172
- );
173
- this.implementation.appId = config.getString(
174
- "appId",
175
- this.implementation.appId
176
- );
177
- this.implementation.appId = this.getConfig()
178
- .getString("appId", this.implementation.appId);
179
- if (
180
- this.implementation.appId == null || this.implementation.appId.isEmpty()
181
- ) {
182
- // crash the app
183
- throw new RuntimeException(
184
- "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"
185
- );
204
+
205
+ private void semaphoreWait(Number waitTime) {
206
+ Log.i(CapacitorUpdater.TAG, "semaphoreWait " + waitTime);
207
+ try {
208
+ // Log.i(CapacitorUpdater.TAG, "semaphoreReady count " + CapacitorUpdaterPlugin.this.semaphoreReady.getCount());
209
+ semaphoreReady.awaitAdvanceInterruptibly(semaphoreReady.getPhase(), waitTime.longValue(), TimeUnit.SECONDS);
210
+ // Log.i(CapacitorUpdater.TAG, "semaphoreReady await " + res);
211
+ Log.i(CapacitorUpdater.TAG, "semaphoreReady count " + semaphoreReady.getPhase());
212
+ } catch (InterruptedException e) {
213
+ Log.i(CapacitorUpdater.TAG, "semaphoreWait InterruptedException");
214
+ e.printStackTrace();
215
+ } catch (TimeoutException e) {
216
+ throw new RuntimeException(e);
217
+ }
186
218
  }
187
- Log.i(CapacitorUpdater.TAG, "appId: " + implementation.appId);
188
- this.implementation.publicKey = this.getConfig().getString("publicKey", "");
189
- this.implementation.privateKey = this.getConfig()
190
- .getString("privateKey", "");
191
- if (
192
- this.implementation.privateKey != null &&
193
- !this.implementation.privateKey.isEmpty()
194
- ) {
195
- this.implementation.hasOldPrivateKeyPropertyInConfig = true;
219
+
220
+ private void semaphoreUp() {
221
+ Log.i(CapacitorUpdater.TAG, "semaphoreUp");
222
+ semaphoreReady.register();
196
223
  }
197
- this.implementation.statsUrl = this.getConfig()
198
- .getString("statsUrl", statsUrlDefault);
199
- this.implementation.channelUrl = this.getConfig()
200
- .getString("channelUrl", channelUrlDefault);
201
- int userValue = this.getConfig().getInt("periodCheckDelay", 0);
202
- this.implementation.defaultChannel = this.getConfig()
203
- .getString("defaultChannel", "");
204
-
205
- if (userValue >= 0 && userValue <= 600) {
206
- this.periodCheckDelay = 600 * 1000;
207
- } else if (userValue > 600) {
208
- this.periodCheckDelay = userValue * 1000;
224
+
225
+ private void semaphoreDown() {
226
+ Log.i(CapacitorUpdater.TAG, "semaphoreDown");
227
+ Log.i(CapacitorUpdater.TAG, "semaphoreDown count " + semaphoreReady.getPhase());
228
+ semaphoreReady.arriveAndDeregister();
209
229
  }
210
230
 
211
- this.implementation.documentsDir = this.getContext().getFilesDir();
212
- this.implementation.prefs = this.prefs;
213
- this.implementation.editor = this.editor;
214
- this.implementation.versionOs = Build.VERSION.RELEASE;
215
- this.implementation.deviceID = this.prefs.getString(
216
- "appUUID",
217
- UUID.randomUUID().toString()
218
- ).toLowerCase();
219
- this.editor.putString("appUUID", this.implementation.deviceID);
220
- this.editor.commit();
221
- Log.i(
222
- CapacitorUpdater.TAG,
223
- "init for device " + this.implementation.deviceID
224
- );
225
- Log.i(
226
- CapacitorUpdater.TAG,
227
- "version native " + this.currentVersionNative.getOriginalString()
228
- );
229
- this.autoDeleteFailed = this.getConfig()
230
- .getBoolean("autoDeleteFailed", true);
231
- this.autoDeletePrevious = this.getConfig()
232
- .getBoolean("autoDeletePrevious", true);
233
- this.updateUrl = this.getConfig().getString("updateUrl", updateUrlDefault);
234
- this.autoUpdate = this.getConfig().getBoolean("autoUpdate", true);
235
- this.appReadyTimeout = this.getConfig().getInt("appReadyTimeout", 10000);
236
- this.keepUrlPathAfterReload = this.getConfig()
237
- .getBoolean("keepUrlPathAfterReload", false);
238
- this.implementation.timeout =
239
- this.getConfig().getInt("responseTimeout", 20) * 1000;
240
- boolean resetWhenUpdate =
241
- this.getConfig().getBoolean("resetWhenUpdate", true);
242
-
243
- this.implementation.autoReset();
244
- if (resetWhenUpdate) {
245
- this.cleanupObsoleteVersions();
231
+ private void sendReadyToJs(final BundleInfo current, final String msg) {
232
+ Log.i(CapacitorUpdater.TAG, "sendReadyToJs");
233
+ final JSObject ret = new JSObject();
234
+ ret.put("bundle", current.toJSON());
235
+ ret.put("status", msg);
236
+ startNewThread(() -> {
237
+ Log.i(CapacitorUpdater.TAG, "semaphoreReady sendReadyToJs");
238
+ semaphoreWait(CapacitorUpdaterPlugin.this.appReadyTimeout);
239
+ Log.i(CapacitorUpdater.TAG, "semaphoreReady sendReadyToJs done");
240
+ CapacitorUpdaterPlugin.this.notifyListeners("appReady", ret);
241
+ });
246
242
  }
247
- this.checkForUpdateAfterDelay();
248
- }
249
-
250
- private void semaphoreWait(Number waitTime) {
251
- Log.i(CapacitorUpdater.TAG, "semaphoreWait " + waitTime);
252
- try {
253
- // Log.i(CapacitorUpdater.TAG, "semaphoreReady count " + CapacitorUpdaterPlugin.this.semaphoreReady.getCount());
254
- semaphoreReady.awaitAdvanceInterruptibly(
255
- semaphoreReady.getPhase(),
256
- waitTime.longValue(),
257
- TimeUnit.SECONDS
258
- );
259
- // Log.i(CapacitorUpdater.TAG, "semaphoreReady await " + res);
260
- Log.i(
261
- CapacitorUpdater.TAG,
262
- "semaphoreReady count " + semaphoreReady.getPhase()
263
- );
264
- } catch (InterruptedException e) {
265
- Log.i(CapacitorUpdater.TAG, "semaphoreWait InterruptedException");
266
- e.printStackTrace();
267
- } catch (TimeoutException e) {
268
- throw new RuntimeException(e);
243
+
244
+ private void directUpdateFinish(final BundleInfo latest) {
245
+ CapacitorUpdaterPlugin.this.implementation.set(latest);
246
+ CapacitorUpdaterPlugin.this._reload();
247
+ sendReadyToJs(latest, "update installed");
269
248
  }
270
- }
271
-
272
- private void semaphoreUp() {
273
- Log.i(CapacitorUpdater.TAG, "semaphoreUp");
274
- semaphoreReady.register();
275
- }
276
-
277
- private void semaphoreDown() {
278
- Log.i(CapacitorUpdater.TAG, "semaphoreDown");
279
- Log.i(
280
- CapacitorUpdater.TAG,
281
- "semaphoreDown count " + semaphoreReady.getPhase()
282
- );
283
- semaphoreReady.arriveAndDeregister();
284
- }
285
-
286
- private void sendReadyToJs(final BundleInfo current, final String msg) {
287
- Log.i(CapacitorUpdater.TAG, "sendReadyToJs");
288
- final JSObject ret = new JSObject();
289
- ret.put("bundle", current.toJSON());
290
- ret.put("status", msg);
291
- startNewThread(() -> {
292
- Log.i(CapacitorUpdater.TAG, "semaphoreReady sendReadyToJs");
293
- semaphoreWait(CapacitorUpdaterPlugin.this.appReadyTimeout);
294
- Log.i(CapacitorUpdater.TAG, "semaphoreReady sendReadyToJs done");
295
- CapacitorUpdaterPlugin.this.notifyListeners("appReady", ret);
296
- });
297
- }
298
-
299
- private void directUpdateFinish(final BundleInfo latest) {
300
- CapacitorUpdaterPlugin.this.implementation.set(latest);
301
- CapacitorUpdaterPlugin.this._reload();
302
- sendReadyToJs(latest, "update installed");
303
- }
304
-
305
- private void cleanupObsoleteVersions() {
306
- try {
307
- final Version previous = new Version(
308
- this.prefs.getString("LatestVersionNative", "")
309
- );
310
- try {
311
- if (
312
- !"".equals(previous.getOriginalString()) &&
313
- !Objects.equals(
314
- this.currentVersionNative.getOriginalString(),
315
- previous.getOriginalString()
316
- )
317
- ) {
318
- Log.i(
319
- CapacitorUpdater.TAG,
320
- "New native version detected: " + this.currentVersionNative
321
- );
322
- this.implementation.reset(true);
323
- final List<BundleInfo> installed = this.implementation.list();
324
- for (final BundleInfo bundle : installed) {
249
+
250
+ private void cleanupObsoleteVersions() {
251
+ try {
252
+ final Version previous = new Version(this.prefs.getString("LatestVersionNative", ""));
325
253
  try {
326
- Log.i(
327
- CapacitorUpdater.TAG,
328
- "Deleting obsolete bundle: " + bundle.getId()
329
- );
330
- this.implementation.delete(bundle.getId());
254
+ if (
255
+ !"".equals(previous.getOriginalString()) &&
256
+ !Objects.equals(this.currentVersionNative.getOriginalString(), previous.getOriginalString())
257
+ ) {
258
+ Log.i(CapacitorUpdater.TAG, "New native version detected: " + this.currentVersionNative);
259
+ this.implementation.reset(true);
260
+ final List<BundleInfo> installed = this.implementation.list(false);
261
+ for (final BundleInfo bundle : installed) {
262
+ try {
263
+ Log.i(CapacitorUpdater.TAG, "Deleting obsolete bundle: " + bundle.getId());
264
+ this.implementation.delete(bundle.getId());
265
+ } catch (final Exception e) {
266
+ Log.e(CapacitorUpdater.TAG, "Failed to delete: " + bundle.getId(), e);
267
+ }
268
+ }
269
+ }
331
270
  } catch (final Exception e) {
332
- Log.e(
333
- CapacitorUpdater.TAG,
334
- "Failed to delete: " + bundle.getId(),
335
- e
336
- );
271
+ Log.e(CapacitorUpdater.TAG, "Could not determine the current version", e);
337
272
  }
338
- }
273
+ } catch (final Exception e) {
274
+ Log.e(CapacitorUpdater.TAG, "Error calculating previous native version", e);
339
275
  }
340
- } catch (final Exception e) {
341
- Log.e(
342
- CapacitorUpdater.TAG,
343
- "Could not determine the current version",
344
- e
345
- );
346
- }
347
- } catch (final Exception e) {
348
- Log.e(
349
- CapacitorUpdater.TAG,
350
- "Error calculating previous native version",
351
- e
352
- );
276
+ this.editor.putString("LatestVersionNative", this.currentVersionNative.toString());
277
+ this.editor.commit();
353
278
  }
354
- this.editor.putString(
355
- "LatestVersionNative",
356
- this.currentVersionNative.toString()
357
- );
358
- this.editor.commit();
359
- }
360
-
361
- public void notifyDownload(final String id, final int percent) {
362
- try {
363
- final JSObject ret = new JSObject();
364
- ret.put("percent", percent);
365
- final BundleInfo bundleInfo = this.implementation.getBundleInfo(id);
366
- ret.put("bundle", bundleInfo.toJSON());
367
- this.notifyListeners("download", ret);
368
-
369
- if (percent == 100) {
370
- final JSObject retDownloadComplete = new JSObject(
371
- ret,
372
- new String[] { "bundle" }
373
- );
374
- this.notifyListeners("downloadComplete", retDownloadComplete);
375
- this.implementation.sendStats(
376
- "download_complete",
377
- bundleInfo.getVersionName()
378
- );
379
- lastNotifiedStatPercent = 100;
380
- } else {
381
- int currentStatPercent = (percent / 10) * 10; // Round down to nearest 10
382
- if (currentStatPercent > lastNotifiedStatPercent) {
383
- this.implementation.sendStats(
384
- "download_" + currentStatPercent,
385
- bundleInfo.getVersionName()
386
- );
387
- lastNotifiedStatPercent = currentStatPercent;
279
+
280
+ public void notifyDownload(final String id, final int percent) {
281
+ try {
282
+ final JSObject ret = new JSObject();
283
+ ret.put("percent", percent);
284
+ final BundleInfo bundleInfo = this.implementation.getBundleInfo(id);
285
+ ret.put("bundle", bundleInfo.toJSON());
286
+ this.notifyListeners("download", ret);
287
+
288
+ if (percent == 100) {
289
+ final JSObject retDownloadComplete = new JSObject(ret, new String[] { "bundle" });
290
+ this.notifyListeners("downloadComplete", retDownloadComplete);
291
+ this.implementation.sendStats("download_complete", bundleInfo.getVersionName());
292
+ lastNotifiedStatPercent = 100;
293
+ } else {
294
+ int currentStatPercent = (percent / 10) * 10; // Round down to nearest 10
295
+ if (currentStatPercent > lastNotifiedStatPercent) {
296
+ this.implementation.sendStats("download_" + currentStatPercent, bundleInfo.getVersionName());
297
+ lastNotifiedStatPercent = currentStatPercent;
298
+ }
299
+ }
300
+ } catch (final Exception e) {
301
+ Log.e(CapacitorUpdater.TAG, "Could not notify listeners", e);
388
302
  }
389
- }
390
- } catch (final Exception e) {
391
- Log.e(CapacitorUpdater.TAG, "Could not notify listeners", e);
392
303
  }
393
- }
394
-
395
- @PluginMethod
396
- public void setUpdateUrl(final PluginCall call) {
397
- if (!this.getConfig().getBoolean("allowModifyUrl", false)) {
398
- Log.e(
399
- CapacitorUpdater.TAG,
400
- "setUpdateUrl not allowed set allowModifyUrl in your config to true to allow it"
401
- );
402
- call.reject("setUpdateUrl not allowed");
403
- return;
304
+
305
+ @PluginMethod
306
+ public void setUpdateUrl(final PluginCall call) {
307
+ if (!this.getConfig().getBoolean("allowModifyUrl", false)) {
308
+ Log.e(CapacitorUpdater.TAG, "setUpdateUrl not allowed set allowModifyUrl in your config to true to allow it");
309
+ call.reject("setUpdateUrl not allowed");
310
+ return;
311
+ }
312
+ final String url = call.getString("url");
313
+ if (url == null) {
314
+ Log.e(CapacitorUpdater.TAG, "setUpdateUrl called without url");
315
+ call.reject("setUpdateUrl called without url");
316
+ return;
317
+ }
318
+ this.updateUrl = url;
319
+ call.resolve();
404
320
  }
405
- final String url = call.getString("url");
406
- if (url == null) {
407
- Log.e(CapacitorUpdater.TAG, "setUpdateUrl called without url");
408
- call.reject("setUpdateUrl called without url");
409
- return;
321
+
322
+ @PluginMethod
323
+ public void setStatsUrl(final PluginCall call) {
324
+ if (!this.getConfig().getBoolean("allowModifyUrl", false)) {
325
+ Log.e(CapacitorUpdater.TAG, "setStatsUrl not allowed set allowModifyUrl in your config to true to allow it");
326
+ call.reject("setStatsUrl not allowed");
327
+ return;
328
+ }
329
+ final String url = call.getString("url");
330
+ if (url == null) {
331
+ Log.e(CapacitorUpdater.TAG, "setStatsUrl called without url");
332
+ call.reject("setStatsUrl called without url");
333
+ return;
334
+ }
335
+ this.implementation.statsUrl = url;
336
+ call.resolve();
410
337
  }
411
- this.updateUrl = url;
412
- call.resolve();
413
- }
414
-
415
- @PluginMethod
416
- public void setStatsUrl(final PluginCall call) {
417
- if (!this.getConfig().getBoolean("allowModifyUrl", false)) {
418
- Log.e(
419
- CapacitorUpdater.TAG,
420
- "setStatsUrl not allowed set allowModifyUrl in your config to true to allow it"
421
- );
422
- call.reject("setStatsUrl not allowed");
423
- return;
338
+
339
+ @PluginMethod
340
+ public void setChannelUrl(final PluginCall call) {
341
+ if (!this.getConfig().getBoolean("allowModifyUrl", false)) {
342
+ Log.e(CapacitorUpdater.TAG, "setChannelUrl not allowed set allowModifyUrl in your config to true to allow it");
343
+ call.reject("setChannelUrl not allowed");
344
+ return;
345
+ }
346
+ final String url = call.getString("url");
347
+ if (url == null) {
348
+ Log.e(CapacitorUpdater.TAG, "setChannelUrl called without url");
349
+ call.reject("setChannelUrl called without url");
350
+ return;
351
+ }
352
+ this.implementation.channelUrl = url;
353
+ call.resolve();
424
354
  }
425
- final String url = call.getString("url");
426
- if (url == null) {
427
- Log.e(CapacitorUpdater.TAG, "setStatsUrl called without url");
428
- call.reject("setStatsUrl called without url");
429
- return;
355
+
356
+ @PluginMethod
357
+ public void getBuiltinVersion(final PluginCall call) {
358
+ try {
359
+ final JSObject ret = new JSObject();
360
+ ret.put("version", this.implementation.versionBuild);
361
+ call.resolve(ret);
362
+ } catch (final Exception e) {
363
+ Log.e(CapacitorUpdater.TAG, "Could not get version", e);
364
+ call.reject("Could not get version", e);
365
+ }
430
366
  }
431
- this.implementation.statsUrl = url;
432
- call.resolve();
433
- }
434
-
435
- @PluginMethod
436
- public void setChannelUrl(final PluginCall call) {
437
- if (!this.getConfig().getBoolean("allowModifyUrl", false)) {
438
- Log.e(
439
- CapacitorUpdater.TAG,
440
- "setChannelUrl not allowed set allowModifyUrl in your config to true to allow it"
441
- );
442
- call.reject("setChannelUrl not allowed");
443
- return;
367
+
368
+ @PluginMethod
369
+ public void getDeviceId(final PluginCall call) {
370
+ try {
371
+ final JSObject ret = new JSObject();
372
+ ret.put("deviceId", this.implementation.deviceID);
373
+ call.resolve(ret);
374
+ } catch (final Exception e) {
375
+ Log.e(CapacitorUpdater.TAG, "Could not get device id", e);
376
+ call.reject("Could not get device id", e);
377
+ }
444
378
  }
445
- final String url = call.getString("url");
446
- if (url == null) {
447
- Log.e(CapacitorUpdater.TAG, "setChannelUrl called without url");
448
- call.reject("setChannelUrl called without url");
449
- return;
379
+
380
+ @PluginMethod
381
+ public void setCustomId(final PluginCall call) {
382
+ final String customId = call.getString("customId");
383
+ if (customId == null) {
384
+ Log.e(CapacitorUpdater.TAG, "setCustomId called without customId");
385
+ call.reject("setCustomId called without customId");
386
+ return;
387
+ }
388
+ this.implementation.customId = customId;
450
389
  }
451
- this.implementation.channelUrl = url;
452
- call.resolve();
453
- }
454
-
455
- @PluginMethod
456
- public void getBuiltinVersion(final PluginCall call) {
457
- try {
458
- final JSObject ret = new JSObject();
459
- ret.put("version", this.implementation.versionBuild);
460
- call.resolve(ret);
461
- } catch (final Exception e) {
462
- Log.e(CapacitorUpdater.TAG, "Could not get version", e);
463
- call.reject("Could not get version", e);
390
+
391
+ @PluginMethod
392
+ public void getPluginVersion(final PluginCall call) {
393
+ try {
394
+ final JSObject ret = new JSObject();
395
+ ret.put("version", this.PLUGIN_VERSION);
396
+ call.resolve(ret);
397
+ } catch (final Exception e) {
398
+ Log.e(CapacitorUpdater.TAG, "Could not get plugin version", e);
399
+ call.reject("Could not get plugin version", e);
400
+ }
464
401
  }
465
- }
466
-
467
- @PluginMethod
468
- public void getDeviceId(final PluginCall call) {
469
- try {
470
- final JSObject ret = new JSObject();
471
- ret.put("deviceId", this.implementation.deviceID);
472
- call.resolve(ret);
473
- } catch (final Exception e) {
474
- Log.e(CapacitorUpdater.TAG, "Could not get device id", e);
475
- call.reject("Could not get device id", e);
402
+
403
+ @PluginMethod
404
+ public void unsetChannel(final PluginCall call) {
405
+ final Boolean triggerAutoUpdate = call.getBoolean("triggerAutoUpdate", false);
406
+
407
+ try {
408
+ Log.i(CapacitorUpdater.TAG, "unsetChannel triggerAutoUpdate: " + triggerAutoUpdate);
409
+ startNewThread(() ->
410
+ CapacitorUpdaterPlugin.this.implementation.unsetChannel(res -> {
411
+ if (res.has("error")) {
412
+ call.reject(res.getString("error"));
413
+ } else {
414
+ if (CapacitorUpdaterPlugin.this._isAutoUpdateEnabled() && Boolean.TRUE.equals(triggerAutoUpdate)) {
415
+ Log.i(CapacitorUpdater.TAG, "Calling autoupdater after channel change!");
416
+ backgroundDownload();
417
+ }
418
+ call.resolve(res);
419
+ }
420
+ })
421
+ );
422
+ } catch (final Exception e) {
423
+ Log.e(CapacitorUpdater.TAG, "Failed to unsetChannel: ", e);
424
+ call.reject("Failed to unsetChannel: ", e);
425
+ }
426
+ }
427
+
428
+ @PluginMethod
429
+ public void setChannel(final PluginCall call) {
430
+ final String channel = call.getString("channel");
431
+ final Boolean triggerAutoUpdate = call.getBoolean("triggerAutoUpdate", false);
432
+
433
+ if (channel == null) {
434
+ Log.e(CapacitorUpdater.TAG, "setChannel called without channel");
435
+ call.reject("setChannel called without channel");
436
+ return;
437
+ }
438
+ try {
439
+ Log.i(CapacitorUpdater.TAG, "setChannel " + channel + " triggerAutoUpdate: " + triggerAutoUpdate);
440
+ startNewThread(() ->
441
+ CapacitorUpdaterPlugin.this.implementation.setChannel(channel, res -> {
442
+ if (res.has("error")) {
443
+ call.reject(res.getString("error"));
444
+ } else {
445
+ if (CapacitorUpdaterPlugin.this._isAutoUpdateEnabled() && Boolean.TRUE.equals(triggerAutoUpdate)) {
446
+ Log.i(CapacitorUpdater.TAG, "Calling autoupdater after channel change!");
447
+ backgroundDownload();
448
+ }
449
+ call.resolve(res);
450
+ }
451
+ })
452
+ );
453
+ } catch (final Exception e) {
454
+ Log.e(CapacitorUpdater.TAG, "Failed to setChannel: " + channel, e);
455
+ call.reject("Failed to setChannel: " + channel, e);
456
+ }
476
457
  }
477
- }
478
-
479
- @PluginMethod
480
- public void setCustomId(final PluginCall call) {
481
- final String customId = call.getString("customId");
482
- if (customId == null) {
483
- Log.e(CapacitorUpdater.TAG, "setCustomId called without customId");
484
- call.reject("setCustomId called without customId");
485
- return;
458
+
459
+ @PluginMethod
460
+ public void getChannel(final PluginCall call) {
461
+ try {
462
+ Log.i(CapacitorUpdater.TAG, "getChannel");
463
+ startNewThread(() ->
464
+ CapacitorUpdaterPlugin.this.implementation.getChannel(res -> {
465
+ if (res.has("error")) {
466
+ call.reject(res.getString("error"));
467
+ } else {
468
+ call.resolve(res);
469
+ }
470
+ })
471
+ );
472
+ } catch (final Exception e) {
473
+ Log.e(CapacitorUpdater.TAG, "Failed to getChannel", e);
474
+ call.reject("Failed to getChannel", e);
475
+ }
486
476
  }
487
- this.implementation.customId = customId;
488
- }
489
-
490
- @PluginMethod
491
- public void getPluginVersion(final PluginCall call) {
492
- try {
493
- final JSObject ret = new JSObject();
494
- ret.put("version", this.PLUGIN_VERSION);
495
- call.resolve(ret);
496
- } catch (final Exception e) {
497
- Log.e(CapacitorUpdater.TAG, "Could not get plugin version", e);
498
- call.reject("Could not get plugin version", e);
477
+
478
+ @PluginMethod
479
+ public void download(final PluginCall call) {
480
+ final String url = call.getString("url");
481
+ final String version = call.getString("version");
482
+ final String sessionKey = call.getString("sessionKey", "");
483
+ final String checksum = call.getString("checksum", "");
484
+ if (url == null) {
485
+ Log.e(CapacitorUpdater.TAG, "Download called without url");
486
+ call.reject("Download called without url");
487
+ return;
488
+ }
489
+ if (version == null) {
490
+ Log.e(CapacitorUpdater.TAG, "Download called without version");
491
+ call.reject("Download called without version");
492
+ return;
493
+ }
494
+ try {
495
+ Log.i(CapacitorUpdater.TAG, "Downloading " + url);
496
+ startNewThread(() -> {
497
+ try {
498
+ final BundleInfo downloaded = CapacitorUpdaterPlugin.this.implementation.download(url, version, sessionKey, checksum);
499
+ if (downloaded.isErrorStatus()) {
500
+ throw new RuntimeException("Download failed: " + downloaded.getStatus());
501
+ } else {
502
+ call.resolve(downloaded.toJSON());
503
+ }
504
+ } catch (final Exception e) {
505
+ Log.e(CapacitorUpdater.TAG, "Failed to download from: " + url, e);
506
+ call.reject("Failed to download from: " + url, e);
507
+ final JSObject ret = new JSObject();
508
+ ret.put("version", version);
509
+ CapacitorUpdaterPlugin.this.notifyListeners("downloadFailed", ret);
510
+ final BundleInfo current = CapacitorUpdaterPlugin.this.implementation.getCurrentBundle();
511
+ CapacitorUpdaterPlugin.this.implementation.sendStats("download_fail", current.getVersionName());
512
+ }
513
+ });
514
+ } catch (final Exception e) {
515
+ Log.e(CapacitorUpdater.TAG, "Failed to download from: " + url, e);
516
+ call.reject("Failed to download from: " + url, e);
517
+ final JSObject ret = new JSObject();
518
+ ret.put("version", version);
519
+ CapacitorUpdaterPlugin.this.notifyListeners("downloadFailed", ret);
520
+ final BundleInfo current = CapacitorUpdaterPlugin.this.implementation.getCurrentBundle();
521
+ CapacitorUpdaterPlugin.this.implementation.sendStats("download_fail", current.getVersionName());
522
+ }
499
523
  }
500
- }
501
-
502
- @PluginMethod
503
- public void unsetChannel(final PluginCall call) {
504
- final Boolean triggerAutoUpdate = call.getBoolean(
505
- "triggerAutoUpdate",
506
- false
507
- );
508
-
509
- try {
510
- Log.i(
511
- CapacitorUpdater.TAG,
512
- "unsetChannel triggerAutoUpdate: " + triggerAutoUpdate
513
- );
514
- startNewThread(() ->
515
- CapacitorUpdaterPlugin.this.implementation.unsetChannel(res -> {
516
- if (res.has("error")) {
517
- call.reject(res.getString("error"));
524
+
525
+ protected boolean _reload() {
526
+ final String path = this.implementation.getCurrentBundlePath();
527
+ this.semaphoreUp();
528
+ Log.i(CapacitorUpdater.TAG, "Reloading: " + path);
529
+
530
+ AtomicReference<URL> url = new AtomicReference<>();
531
+ if (this.keepUrlPathAfterReload) {
532
+ try {
533
+ if (Looper.myLooper() != Looper.getMainLooper()) {
534
+ Semaphore mainThreadSemaphore = new Semaphore(0);
535
+ this.bridge.executeOnMainThread(() -> {
536
+ try {
537
+ url.set(new URL(this.bridge.getWebView().getUrl()));
538
+ } catch (Exception e) {
539
+ Log.e(CapacitorUpdater.TAG, "Error executing on main thread", e);
540
+ }
541
+ mainThreadSemaphore.release();
542
+ });
543
+ mainThreadSemaphore.acquire();
544
+ } else {
545
+ try {
546
+ url.set(new URL(this.bridge.getWebView().getUrl()));
547
+ } catch (Exception e) {
548
+ Log.e(CapacitorUpdater.TAG, "Error executing on main thread", e);
549
+ }
550
+ }
551
+ } catch (InterruptedException e) {
552
+ Log.e(CapacitorUpdater.TAG, "Error waiting for main thread or getting the current URL from webview", e);
553
+ }
554
+ }
555
+
556
+ if (url.get() != null) {
557
+ if (this.implementation.isUsingBuiltin()) {
558
+ this.bridge.getLocalServer().hostAssets(path);
518
559
  } else {
519
- if (
520
- CapacitorUpdaterPlugin.this._isAutoUpdateEnabled() &&
521
- Boolean.TRUE.equals(triggerAutoUpdate)
522
- ) {
523
- Log.i(
524
- CapacitorUpdater.TAG,
525
- "Calling autoupdater after channel change!"
526
- );
527
- backgroundDownload();
528
- }
529
- call.resolve(res);
560
+ this.bridge.getLocalServer().hostFiles(path);
530
561
  }
531
- })
532
- );
533
- } catch (final Exception e) {
534
- Log.e(CapacitorUpdater.TAG, "Failed to unsetChannel: ", e);
535
- call.reject("Failed to unsetChannel: ", e);
536
- }
537
- }
538
-
539
- @PluginMethod
540
- public void setChannel(final PluginCall call) {
541
- final String channel = call.getString("channel");
542
- final Boolean triggerAutoUpdate = call.getBoolean(
543
- "triggerAutoUpdate",
544
- false
545
- );
546
-
547
- if (channel == null) {
548
- Log.e(CapacitorUpdater.TAG, "setChannel called without channel");
549
- call.reject("setChannel called without channel");
550
- return;
551
- }
552
- try {
553
- Log.i(
554
- CapacitorUpdater.TAG,
555
- "setChannel " + channel + " triggerAutoUpdate: " + triggerAutoUpdate
556
- );
557
- startNewThread(() ->
558
- CapacitorUpdaterPlugin.this.implementation.setChannel(channel, res -> {
559
- if (res.has("error")) {
560
- call.reject(res.getString("error"));
562
+
563
+ try {
564
+ URL finalUrl = null;
565
+ finalUrl = new URL(this.bridge.getAppUrl());
566
+ finalUrl = new URL(finalUrl.getProtocol(), finalUrl.getHost(), finalUrl.getPort(), url.get().getPath());
567
+ URL finalUrl1 = finalUrl;
568
+ this.bridge.getWebView()
569
+ .post(() -> {
570
+ this.bridge.getWebView().loadUrl(finalUrl1.toString());
571
+ this.bridge.getWebView().clearHistory();
572
+ });
573
+ } catch (MalformedURLException e) {
574
+ Log.e(CapacitorUpdater.TAG, "Cannot get finalUrl from capacitor bridge", e);
575
+
576
+ if (this.implementation.isUsingBuiltin()) {
577
+ this.bridge.setServerAssetPath(path);
578
+ } else {
579
+ this.bridge.setServerBasePath(path);
580
+ }
581
+ }
582
+ } else {
583
+ if (this.implementation.isUsingBuiltin()) {
584
+ this.bridge.setServerAssetPath(path);
561
585
  } else {
562
- if (
563
- CapacitorUpdaterPlugin.this._isAutoUpdateEnabled() &&
564
- Boolean.TRUE.equals(triggerAutoUpdate)
565
- ) {
566
- Log.i(
567
- CapacitorUpdater.TAG,
568
- "Calling autoupdater after channel change!"
569
- );
570
- backgroundDownload();
571
- }
572
- call.resolve(res);
586
+ this.bridge.setServerBasePath(path);
573
587
  }
574
- })
575
- );
576
- } catch (final Exception e) {
577
- Log.e(CapacitorUpdater.TAG, "Failed to setChannel: " + channel, e);
578
- call.reject("Failed to setChannel: " + channel, e);
588
+ }
589
+
590
+ this.checkAppReady();
591
+ this.notifyListeners("appReloaded", new JSObject());
592
+ return true;
579
593
  }
580
- }
581
-
582
- @PluginMethod
583
- public void getChannel(final PluginCall call) {
584
- try {
585
- Log.i(CapacitorUpdater.TAG, "getChannel");
586
- startNewThread(() ->
587
- CapacitorUpdaterPlugin.this.implementation.getChannel(res -> {
588
- if (res.has("error")) {
589
- call.reject(res.getString("error"));
594
+
595
+ @PluginMethod
596
+ public void reload(final PluginCall call) {
597
+ try {
598
+ if (this._reload()) {
599
+ call.resolve();
590
600
  } else {
591
- call.resolve(res);
601
+ Log.e(CapacitorUpdater.TAG, "Reload failed");
602
+ call.reject("Reload failed");
592
603
  }
593
- })
594
- );
595
- } catch (final Exception e) {
596
- Log.e(CapacitorUpdater.TAG, "Failed to getChannel", e);
597
- call.reject("Failed to getChannel", e);
604
+ } catch (final Exception e) {
605
+ Log.e(CapacitorUpdater.TAG, "Could not reload", e);
606
+ call.reject("Could not reload", e);
607
+ }
598
608
  }
599
- }
600
-
601
- @PluginMethod
602
- public void download(final PluginCall call) {
603
- final String url = call.getString("url");
604
- final String version = call.getString("version");
605
- final String sessionKey = call.getString("sessionKey", "");
606
- final String checksum = call.getString("checksum", "");
607
- if (url == null) {
608
- Log.e(CapacitorUpdater.TAG, "Download called without url");
609
- call.reject("Download called without url");
610
- return;
609
+
610
+ @PluginMethod
611
+ public void next(final PluginCall call) {
612
+ final String id = call.getString("id");
613
+ if (id == null) {
614
+ Log.e(CapacitorUpdater.TAG, "Next called without id");
615
+ call.reject("Next called without id");
616
+ return;
617
+ }
618
+ try {
619
+ Log.i(CapacitorUpdater.TAG, "Setting next active id " + id);
620
+ if (!this.implementation.setNextBundle(id)) {
621
+ Log.e(CapacitorUpdater.TAG, "Set next id failed. Bundle " + id + " does not exist.");
622
+ call.reject("Set next id failed. Bundle " + id + " does not exist.");
623
+ } else {
624
+ call.resolve(this.implementation.getBundleInfo(id).toJSON());
625
+ }
626
+ } catch (final Exception e) {
627
+ Log.e(CapacitorUpdater.TAG, "Could not set next id " + id, e);
628
+ call.reject("Could not set next id: " + id, e);
629
+ }
611
630
  }
612
- if (version == null) {
613
- Log.e(CapacitorUpdater.TAG, "Download called without version");
614
- call.reject("Download called without version");
615
- return;
631
+
632
+ @PluginMethod
633
+ public void set(final PluginCall call) {
634
+ final String id = call.getString("id");
635
+ if (id == null) {
636
+ Log.e(CapacitorUpdater.TAG, "Set called without id");
637
+ call.reject("Set called without id");
638
+ return;
639
+ }
640
+ try {
641
+ Log.i(CapacitorUpdater.TAG, "Setting active bundle " + id);
642
+ if (!this.implementation.set(id)) {
643
+ Log.i(CapacitorUpdater.TAG, "No such bundle " + id);
644
+ call.reject("Update failed, id " + id + " does not exist.");
645
+ } else {
646
+ Log.i(CapacitorUpdater.TAG, "Bundle successfully set to " + id);
647
+ this.reload(call);
648
+ }
649
+ } catch (final Exception e) {
650
+ Log.e(CapacitorUpdater.TAG, "Could not set id " + id, e);
651
+ call.reject("Could not set id " + id, e);
652
+ }
616
653
  }
617
- try {
618
- Log.i(CapacitorUpdater.TAG, "Downloading " + url);
619
- startNewThread(() -> {
654
+
655
+ @PluginMethod
656
+ public void delete(final PluginCall call) {
657
+ final String id = call.getString("id");
658
+ if (id == null) {
659
+ Log.e(CapacitorUpdater.TAG, "missing id");
660
+ call.reject("missing id");
661
+ return;
662
+ }
663
+ Log.i(CapacitorUpdater.TAG, "Deleting id " + id);
620
664
  try {
621
- final BundleInfo downloaded =
622
- CapacitorUpdaterPlugin.this.implementation.download(
623
- url,
624
- version,
625
- sessionKey,
626
- checksum
627
- );
628
- if (downloaded.isErrorStatus()) {
629
- throw new RuntimeException(
630
- "Download failed: " + downloaded.getStatus()
631
- );
632
- } else {
633
- call.resolve(downloaded.toJSON());
634
- }
665
+ final Boolean res = this.implementation.delete(id);
666
+ if (res) {
667
+ call.resolve();
668
+ } else {
669
+ Log.e(CapacitorUpdater.TAG, "Delete failed, id " + id + " does not exist");
670
+ call.reject("Delete failed, id " + id + " does not exist or it cannot be deleted (perhaps it is the 'next' bundle)");
671
+ }
635
672
  } catch (final Exception e) {
636
- Log.e(CapacitorUpdater.TAG, "Failed to download from: " + url, e);
637
- call.reject("Failed to download from: " + url, e);
638
- final JSObject ret = new JSObject();
639
- ret.put("version", version);
640
- CapacitorUpdaterPlugin.this.notifyListeners("downloadFailed", ret);
641
- final BundleInfo current =
642
- CapacitorUpdaterPlugin.this.implementation.getCurrentBundle();
643
- CapacitorUpdaterPlugin.this.implementation.sendStats(
644
- "download_fail",
645
- current.getVersionName()
646
- );
673
+ Log.e(CapacitorUpdater.TAG, "Could not delete id " + id, e);
674
+ call.reject("Could not delete id " + id, e);
647
675
  }
648
- });
649
- } catch (final Exception e) {
650
- Log.e(CapacitorUpdater.TAG, "Failed to download from: " + url, e);
651
- call.reject("Failed to download from: " + url, e);
652
- final JSObject ret = new JSObject();
653
- ret.put("version", version);
654
- CapacitorUpdaterPlugin.this.notifyListeners("downloadFailed", ret);
655
- final BundleInfo current =
656
- CapacitorUpdaterPlugin.this.implementation.getCurrentBundle();
657
- CapacitorUpdaterPlugin.this.implementation.sendStats(
658
- "download_fail",
659
- current.getVersionName()
660
- );
661
676
  }
662
- }
663
-
664
- protected boolean _reload() {
665
- final String path = this.implementation.getCurrentBundlePath();
666
- this.semaphoreUp();
667
- Log.i(CapacitorUpdater.TAG, "Reloading: " + path);
668
-
669
- AtomicReference<URL> url = new AtomicReference<>();
670
- if (this.keepUrlPathAfterReload) {
671
- try {
672
- if (Looper.myLooper() != Looper.getMainLooper()) {
673
- Semaphore mainThreadSemaphore = new Semaphore(0);
674
- this.bridge.executeOnMainThread(() -> {
675
- try {
676
- url.set(new URL(this.bridge.getWebView().getUrl()));
677
- } catch (Exception e) {
678
- Log.e(
679
- CapacitorUpdater.TAG,
680
- "Error executing on main thread",
681
- e
682
- );
683
- }
684
- mainThreadSemaphore.release();
685
- });
686
- mainThreadSemaphore.acquire();
687
- } else {
688
- try {
689
- url.set(new URL(this.bridge.getWebView().getUrl()));
690
- } catch (Exception e) {
691
- Log.e(CapacitorUpdater.TAG, "Error executing on main thread", e);
692
- }
693
- }
694
- } catch (InterruptedException e) {
695
- Log.e(
696
- CapacitorUpdater.TAG,
697
- "Error waiting for main thread or getting the current URL from webview",
698
- e
699
- );
700
- }
677
+
678
+ @PluginMethod
679
+ public void list(final PluginCall call) {
680
+ try {
681
+ final List<BundleInfo> res = this.implementation.list(call.getBoolean("raw", false));
682
+ final JSObject ret = new JSObject();
683
+ final JSArray values = new JSArray();
684
+ for (final BundleInfo bundle : res) {
685
+ values.put(bundle.toJSON());
686
+ }
687
+ ret.put("bundles", values);
688
+ call.resolve(ret);
689
+ } catch (final Exception e) {
690
+ Log.e(CapacitorUpdater.TAG, "Could not list bundles", e);
691
+ call.reject("Could not list bundles", e);
692
+ }
701
693
  }
702
694
 
703
- if (url.get() != null) {
704
- if (this.implementation.isUsingBuiltin()) {
705
- this.bridge.getLocalServer().hostAssets(path);
706
- } else {
707
- this.bridge.getLocalServer().hostFiles(path);
708
- }
709
-
710
- try {
711
- URL finalUrl = null;
712
- finalUrl = new URL(this.bridge.getAppUrl());
713
- finalUrl = new URL(
714
- finalUrl.getProtocol(),
715
- finalUrl.getHost(),
716
- finalUrl.getPort(),
717
- url.get().getPath()
718
- );
719
- URL finalUrl1 = finalUrl;
720
- this.bridge.getWebView()
721
- .post(() -> {
722
- this.bridge.getWebView().loadUrl(finalUrl1.toString());
723
- this.bridge.getWebView().clearHistory();
724
- });
725
- } catch (MalformedURLException e) {
726
- Log.e(
727
- CapacitorUpdater.TAG,
728
- "Cannot get finalUrl from capacitor bridge",
729
- e
695
+ @PluginMethod
696
+ public void getLatest(final PluginCall call) {
697
+ final String channel = call.getString("channel");
698
+ startNewThread(() ->
699
+ CapacitorUpdaterPlugin.this.implementation.getLatest(CapacitorUpdaterPlugin.this.updateUrl, channel, res -> {
700
+ if (res.has("error")) {
701
+ call.reject(res.getString("error"));
702
+ return;
703
+ } else if (res.has("message")) {
704
+ call.reject(res.getString("message"));
705
+ return;
706
+ } else {
707
+ call.resolve(res);
708
+ }
709
+ final JSObject ret = new JSObject();
710
+ Iterator<String> keys = res.keys();
711
+ while (keys.hasNext()) {
712
+ String key = keys.next();
713
+ if (res.has(key)) {
714
+ try {
715
+ ret.put(key, res.get(key));
716
+ } catch (JSONException e) {
717
+ e.printStackTrace();
718
+ }
719
+ }
720
+ }
721
+ call.resolve(ret);
722
+ })
730
723
  );
731
-
732
- if (this.implementation.isUsingBuiltin()) {
733
- this.bridge.setServerAssetPath(path);
734
- } else {
735
- this.bridge.setServerBasePath(path);
736
- }
737
- }
738
- } else {
739
- if (this.implementation.isUsingBuiltin()) {
740
- this.bridge.setServerAssetPath(path);
741
- } else {
742
- this.bridge.setServerBasePath(path);
743
- }
744
724
  }
745
725
 
746
- this.checkAppReady();
747
- this.notifyListeners("appReloaded", new JSObject());
748
- return true;
749
- }
726
+ private boolean _reset(final Boolean toLastSuccessful) {
727
+ final BundleInfo fallback = this.implementation.getFallbackBundle();
728
+ this.implementation.reset();
750
729
 
751
- @PluginMethod
752
- public void reload(final PluginCall call) {
753
- try {
754
- if (this._reload()) {
755
- call.resolve();
756
- } else {
757
- Log.e(CapacitorUpdater.TAG, "Reload failed");
758
- call.reject("Reload failed");
759
- }
760
- } catch (final Exception e) {
761
- Log.e(CapacitorUpdater.TAG, "Could not reload", e);
762
- call.reject("Could not reload", e);
763
- }
764
- }
765
-
766
- @PluginMethod
767
- public void next(final PluginCall call) {
768
- final String id = call.getString("id");
769
- if (id == null) {
770
- Log.e(CapacitorUpdater.TAG, "Next called without id");
771
- call.reject("Next called without id");
772
- return;
773
- }
774
- try {
775
- Log.i(CapacitorUpdater.TAG, "Setting next active id " + id);
776
- if (!this.implementation.setNextBundle(id)) {
777
- Log.e(
778
- CapacitorUpdater.TAG,
779
- "Set next id failed. Bundle " + id + " does not exist."
780
- );
781
- call.reject("Set next id failed. Bundle " + id + " does not exist.");
782
- } else {
783
- call.resolve(this.implementation.getBundleInfo(id).toJSON());
784
- }
785
- } catch (final Exception e) {
786
- Log.e(CapacitorUpdater.TAG, "Could not set next id " + id, e);
787
- call.reject("Could not set next id: " + id, e);
730
+ if (toLastSuccessful && !fallback.isBuiltin()) {
731
+ Log.i(CapacitorUpdater.TAG, "Resetting to: " + fallback);
732
+ return this.implementation.set(fallback) && this._reload();
733
+ }
734
+
735
+ Log.i(CapacitorUpdater.TAG, "Resetting to native.");
736
+ return this._reload();
788
737
  }
789
- }
790
-
791
- @PluginMethod
792
- public void set(final PluginCall call) {
793
- final String id = call.getString("id");
794
- if (id == null) {
795
- Log.e(CapacitorUpdater.TAG, "Set called without id");
796
- call.reject("Set called without id");
797
- return;
738
+
739
+ @PluginMethod
740
+ public void reset(final PluginCall call) {
741
+ try {
742
+ final Boolean toLastSuccessful = call.getBoolean("toLastSuccessful", false);
743
+ if (this._reset(toLastSuccessful)) {
744
+ call.resolve();
745
+ return;
746
+ }
747
+ Log.e(CapacitorUpdater.TAG, "Reset failed");
748
+ call.reject("Reset failed");
749
+ } catch (final Exception e) {
750
+ Log.e(CapacitorUpdater.TAG, "Reset failed", e);
751
+ call.reject("Reset failed", e);
752
+ }
798
753
  }
799
- try {
800
- Log.i(CapacitorUpdater.TAG, "Setting active bundle " + id);
801
- if (!this.implementation.set(id)) {
802
- Log.i(CapacitorUpdater.TAG, "No such bundle " + id);
803
- call.reject("Update failed, id " + id + " does not exist.");
804
- } else {
805
- Log.i(CapacitorUpdater.TAG, "Bundle successfully set to " + id);
806
- this.reload(call);
807
- }
808
- } catch (final Exception e) {
809
- Log.e(CapacitorUpdater.TAG, "Could not set id " + id, e);
810
- call.reject("Could not set id " + id, e);
754
+
755
+ @PluginMethod
756
+ public void current(final PluginCall call) {
757
+ try {
758
+ final JSObject ret = new JSObject();
759
+ final BundleInfo bundle = this.implementation.getCurrentBundle();
760
+ ret.put("bundle", bundle.toJSON());
761
+ ret.put("native", this.currentVersionNative);
762
+ call.resolve(ret);
763
+ } catch (final Exception e) {
764
+ Log.e(CapacitorUpdater.TAG, "Could not get current bundle", e);
765
+ call.reject("Could not get current bundle", e);
766
+ }
811
767
  }
812
- }
813
-
814
- @PluginMethod
815
- public void delete(final PluginCall call) {
816
- final String id = call.getString("id");
817
- if (id == null) {
818
- Log.e(CapacitorUpdater.TAG, "missing id");
819
- call.reject("missing id");
820
- return;
768
+
769
+ @PluginMethod
770
+ public void getNextBundle(final PluginCall call) {
771
+ try {
772
+ final BundleInfo bundle = this.implementation.getNextBundle();
773
+ if (bundle == null) {
774
+ call.resolve(null);
775
+ return;
776
+ }
777
+
778
+ call.resolve(bundle.toJSON());
779
+ } catch (final Exception e) {
780
+ Log.e(CapacitorUpdater.TAG, "Could not get next bundle", e);
781
+ call.reject("Could not get next bundle", e);
782
+ }
821
783
  }
822
- Log.i(CapacitorUpdater.TAG, "Deleting id " + id);
823
- try {
824
- final Boolean res = this.implementation.delete(id);
825
- if (res) {
826
- call.resolve();
827
- } else {
828
- Log.e(
829
- CapacitorUpdater.TAG,
830
- "Delete failed, id " + id + " does not exist"
831
- );
832
- call.reject(
833
- "Delete failed, id " +
834
- id +
835
- " does not exist or it cannot be deleted (perhaps it is the 'next' bundle)"
784
+
785
+ public void checkForUpdateAfterDelay() {
786
+ if (this.periodCheckDelay == 0 || !this._isAutoUpdateEnabled()) {
787
+ return;
788
+ }
789
+ final Timer timer = new Timer();
790
+ timer.schedule(
791
+ new TimerTask() {
792
+ @Override
793
+ public void run() {
794
+ try {
795
+ CapacitorUpdaterPlugin.this.implementation.getLatest(CapacitorUpdaterPlugin.this.updateUrl, null, res -> {
796
+ if (res.has("error")) {
797
+ Log.e(CapacitorUpdater.TAG, Objects.requireNonNull(res.getString("error")));
798
+ } else if (res.has("version")) {
799
+ String newVersion = res.getString("version");
800
+ String currentVersion = String.valueOf(CapacitorUpdaterPlugin.this.implementation.getCurrentBundle());
801
+ if (!Objects.equals(newVersion, currentVersion)) {
802
+ Log.i(CapacitorUpdater.TAG, "New version found: " + newVersion);
803
+ CapacitorUpdaterPlugin.this.backgroundDownload();
804
+ }
805
+ }
806
+ });
807
+ } catch (final Exception e) {
808
+ Log.e(CapacitorUpdater.TAG, "Failed to check for update", e);
809
+ }
810
+ }
811
+ },
812
+ this.periodCheckDelay,
813
+ this.periodCheckDelay
836
814
  );
837
- }
838
- } catch (final Exception e) {
839
- Log.e(CapacitorUpdater.TAG, "Could not delete id " + id, e);
840
- call.reject("Could not delete id " + id, e);
841
- }
842
- }
843
-
844
- @PluginMethod
845
- public void list(final PluginCall call) {
846
- try {
847
- final List<BundleInfo> res = this.implementation.list();
848
- final JSObject ret = new JSObject();
849
- final JSArray values = new JSArray();
850
- for (final BundleInfo bundle : res) {
851
- values.put(bundle.toJSON());
852
- }
853
- ret.put("bundles", values);
854
- call.resolve(ret);
855
- } catch (final Exception e) {
856
- Log.e(CapacitorUpdater.TAG, "Could not list bundles", e);
857
- call.reject("Could not list bundles", e);
858
815
  }
859
- }
860
-
861
- @PluginMethod
862
- public void getLatest(final PluginCall call) {
863
- final String channel = call.getString("channel");
864
- startNewThread(() ->
865
- CapacitorUpdaterPlugin.this.implementation.getLatest(
866
- CapacitorUpdaterPlugin.this.updateUrl,
867
- channel,
868
- res -> {
869
- if (res.has("error")) {
870
- call.reject(res.getString("error"));
871
- return;
872
- } else if (res.has("message")) {
873
- call.reject(res.getString("message"));
874
- return;
875
- } else {
876
- call.resolve(res);
877
- }
816
+
817
+ @PluginMethod
818
+ public void notifyAppReady(final PluginCall call) {
819
+ try {
820
+ final BundleInfo bundle = this.implementation.getCurrentBundle();
821
+ this.implementation.setSuccess(bundle, this.autoDeletePrevious);
822
+ Log.i(CapacitorUpdater.TAG, "Current bundle loaded successfully. ['notifyAppReady()' was called] " + bundle);
823
+ Log.i(CapacitorUpdater.TAG, "semaphoreReady countDown");
824
+ this.semaphoreDown();
825
+ Log.i(CapacitorUpdater.TAG, "semaphoreReady countDown done");
878
826
  final JSObject ret = new JSObject();
879
- Iterator<String> keys = res.keys();
880
- while (keys.hasNext()) {
881
- String key = keys.next();
882
- if (res.has(key)) {
883
- try {
884
- ret.put(key, res.get(key));
885
- } catch (JSONException e) {
886
- e.printStackTrace();
887
- }
888
- }
889
- }
827
+ ret.put("bundle", bundle.toJSON());
890
828
  call.resolve(ret);
891
- }
892
- )
893
- );
894
- }
895
-
896
- private boolean _reset(final Boolean toLastSuccessful) {
897
- final BundleInfo fallback = this.implementation.getFallbackBundle();
898
- this.implementation.reset();
899
-
900
- if (toLastSuccessful && !fallback.isBuiltin()) {
901
- Log.i(CapacitorUpdater.TAG, "Resetting to: " + fallback);
902
- return this.implementation.set(fallback) && this._reload();
829
+ } catch (final Exception e) {
830
+ Log.e(CapacitorUpdater.TAG, "Failed to notify app ready state. [Error calling 'notifyAppReady()']", e);
831
+ call.reject("Failed to commit app ready state.", e);
832
+ }
903
833
  }
904
834
 
905
- Log.i(CapacitorUpdater.TAG, "Resetting to native.");
906
- return this._reload();
907
- }
908
-
909
- @PluginMethod
910
- public void reset(final PluginCall call) {
911
- try {
912
- final Boolean toLastSuccessful = call.getBoolean(
913
- "toLastSuccessful",
914
- false
915
- );
916
- if (this._reset(toLastSuccessful)) {
917
- call.resolve();
918
- return;
919
- }
920
- Log.e(CapacitorUpdater.TAG, "Reset failed");
921
- call.reject("Reset failed");
922
- } catch (final Exception e) {
923
- Log.e(CapacitorUpdater.TAG, "Reset failed", e);
924
- call.reject("Reset failed", e);
835
+ @PluginMethod
836
+ public void setMultiDelay(final PluginCall call) {
837
+ try {
838
+ final Object delayConditions = call.getData().opt("delayConditions");
839
+ if (delayConditions == null) {
840
+ Log.e(CapacitorUpdater.TAG, "setMultiDelay called without delayCondition");
841
+ call.reject("setMultiDelay called without delayCondition");
842
+ return;
843
+ }
844
+ if (_setMultiDelay(delayConditions.toString())) {
845
+ call.resolve();
846
+ } else {
847
+ call.reject("Failed to delay update");
848
+ }
849
+ } catch (final Exception e) {
850
+ Log.e(CapacitorUpdater.TAG, "Failed to delay update, [Error calling 'setMultiDelay()']", e);
851
+ call.reject("Failed to delay update", e);
852
+ }
925
853
  }
926
- }
927
-
928
- @PluginMethod
929
- public void current(final PluginCall call) {
930
- try {
931
- final JSObject ret = new JSObject();
932
- final BundleInfo bundle = this.implementation.getCurrentBundle();
933
- ret.put("bundle", bundle.toJSON());
934
- ret.put("native", this.currentVersionNative);
935
- call.resolve(ret);
936
- } catch (final Exception e) {
937
- Log.e(CapacitorUpdater.TAG, "Could not get current bundle", e);
938
- call.reject("Could not get current bundle", e);
854
+
855
+ private Boolean _setMultiDelay(String delayConditions) {
856
+ try {
857
+ this.editor.putString(DELAY_CONDITION_PREFERENCES, delayConditions);
858
+ this.editor.commit();
859
+ Log.i(CapacitorUpdater.TAG, "Delay update saved");
860
+ return true;
861
+ } catch (final Exception e) {
862
+ Log.e(CapacitorUpdater.TAG, "Failed to delay update, [Error calling '_setMultiDelay()']", e);
863
+ return false;
864
+ }
939
865
  }
940
- }
941
-
942
- @PluginMethod
943
- public void getNextBundle(final PluginCall call) {
944
- try {
945
- final BundleInfo bundle = this.implementation.getNextBundle();
946
- if (bundle == null) {
947
- call.resolve(null);
948
- return;
949
- }
950
-
951
- call.resolve(bundle.toJSON());
952
- } catch (final Exception e) {
953
- Log.e(CapacitorUpdater.TAG, "Could not get next bundle", e);
954
- call.reject("Could not get next bundle", e);
866
+
867
+ private boolean _cancelDelay(String source) {
868
+ try {
869
+ this.editor.remove(DELAY_CONDITION_PREFERENCES);
870
+ this.editor.commit();
871
+ Log.i(CapacitorUpdater.TAG, "All delays canceled from " + source);
872
+ return true;
873
+ } catch (final Exception e) {
874
+ Log.e(CapacitorUpdater.TAG, "Failed to cancel update delay", e);
875
+ return false;
876
+ }
955
877
  }
956
- }
957
878
 
958
- public void checkForUpdateAfterDelay() {
959
- if (this.periodCheckDelay == 0 || !this._isAutoUpdateEnabled()) {
960
- return;
879
+ @PluginMethod
880
+ public void cancelDelay(final PluginCall call) {
881
+ if (this._cancelDelay("JS")) {
882
+ call.resolve();
883
+ } else {
884
+ call.reject("Failed to cancel delay");
885
+ }
961
886
  }
962
- final Timer timer = new Timer();
963
- timer.schedule(
964
- new TimerTask() {
965
- @Override
966
- public void run() {
967
- try {
968
- CapacitorUpdaterPlugin.this.implementation.getLatest(
969
- CapacitorUpdaterPlugin.this.updateUrl,
970
- null,
971
- res -> {
972
- if (res.has("error")) {
973
- Log.e(
974
- CapacitorUpdater.TAG,
975
- Objects.requireNonNull(res.getString("error"))
976
- );
977
- } else if (res.has("version")) {
978
- String newVersion = res.getString("version");
979
- String currentVersion = String.valueOf(
980
- CapacitorUpdaterPlugin.this.implementation.getCurrentBundle()
981
- );
982
- if (!Objects.equals(newVersion, currentVersion)) {
983
- Log.i(
984
- CapacitorUpdater.TAG,
985
- "New version found: " + newVersion
986
- );
987
- CapacitorUpdaterPlugin.this.backgroundDownload();
988
- }
989
- }
887
+
888
+ private void _checkCancelDelay(Boolean killed) {
889
+ Gson gson = new Gson();
890
+ String delayUpdatePreferences = prefs.getString(DELAY_CONDITION_PREFERENCES, "[]");
891
+ Type type = new TypeToken<ArrayList<DelayCondition>>() {}.getType();
892
+ ArrayList<DelayCondition> delayConditionList = gson.fromJson(delayUpdatePreferences, type);
893
+ for (DelayCondition condition : delayConditionList) {
894
+ String kind = condition.getKind().toString();
895
+ String value = condition.getValue();
896
+ if (!kind.isEmpty()) {
897
+ switch (kind) {
898
+ case "background":
899
+ if (!killed) {
900
+ this._cancelDelay("background check");
901
+ }
902
+ break;
903
+ case "kill":
904
+ if (killed) {
905
+ this._cancelDelay("kill check");
906
+ this.installNext();
907
+ }
908
+ break;
909
+ case "date":
910
+ if (!"".equals(value)) {
911
+ try {
912
+ final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
913
+ Date date = sdf.parse(value);
914
+ assert date != null;
915
+ if (new Date().compareTo(date) > 0) {
916
+ this._cancelDelay("date expired");
917
+ }
918
+ } catch (final Exception e) {
919
+ this._cancelDelay("date parsing issue");
920
+ }
921
+ } else {
922
+ this._cancelDelay("delayVal absent");
923
+ }
924
+ break;
925
+ case "nativeVersion":
926
+ if (!"".equals(value)) {
927
+ try {
928
+ final Version versionLimit = new Version(value);
929
+ if (this.currentVersionNative.isAtLeast(versionLimit)) {
930
+ this._cancelDelay("nativeVersion above limit");
931
+ }
932
+ } catch (final Exception e) {
933
+ this._cancelDelay("nativeVersion parsing issue");
934
+ }
935
+ } else {
936
+ this._cancelDelay("delayVal absent");
937
+ }
938
+ break;
990
939
  }
991
- );
992
- } catch (final Exception e) {
993
- Log.e(CapacitorUpdater.TAG, "Failed to check for update", e);
994
- }
995
- }
996
- },
997
- this.periodCheckDelay,
998
- this.periodCheckDelay
999
- );
1000
- }
1001
-
1002
- @PluginMethod
1003
- public void notifyAppReady(final PluginCall call) {
1004
- try {
1005
- final BundleInfo bundle = this.implementation.getCurrentBundle();
1006
- this.implementation.setSuccess(bundle, this.autoDeletePrevious);
1007
- Log.i(
1008
- CapacitorUpdater.TAG,
1009
- "Current bundle loaded successfully. ['notifyAppReady()' was called] " +
1010
- bundle
1011
- );
1012
- Log.i(CapacitorUpdater.TAG, "semaphoreReady countDown");
1013
- this.semaphoreDown();
1014
- Log.i(CapacitorUpdater.TAG, "semaphoreReady countDown done");
1015
- final JSObject ret = new JSObject();
1016
- ret.put("bundle", bundle.toJSON());
1017
- call.resolve(ret);
1018
- } catch (final Exception e) {
1019
- Log.e(
1020
- CapacitorUpdater.TAG,
1021
- "Failed to notify app ready state. [Error calling 'notifyAppReady()']",
1022
- e
1023
- );
1024
- call.reject("Failed to commit app ready state.", e);
940
+ }
941
+ }
1025
942
  }
1026
- }
1027
-
1028
- @PluginMethod
1029
- public void setMultiDelay(final PluginCall call) {
1030
- try {
1031
- final Object delayConditions = call.getData().opt("delayConditions");
1032
- if (delayConditions == null) {
1033
- Log.e(
1034
- CapacitorUpdater.TAG,
1035
- "setMultiDelay called without delayCondition"
943
+
944
+ private Boolean _isAutoUpdateEnabled() {
945
+ final CapConfig config = CapConfig.loadDefault(this.getActivity());
946
+ String serverUrl = config.getServerUrl();
947
+ if (serverUrl != null && !serverUrl.isEmpty()) {
948
+ // log warning autoupdate disabled when serverUrl is set
949
+ Log.w(CapacitorUpdater.TAG, "AutoUpdate is automatic disabled when serverUrl is set.");
950
+ }
951
+ return (
952
+ CapacitorUpdaterPlugin.this.autoUpdate &&
953
+ !"".equals(CapacitorUpdaterPlugin.this.updateUrl) &&
954
+ (serverUrl == null || serverUrl.isEmpty())
1036
955
  );
1037
- call.reject("setMultiDelay called without delayCondition");
1038
- return;
1039
- }
1040
- if (_setMultiDelay(delayConditions.toString())) {
1041
- call.resolve();
1042
- } else {
1043
- call.reject("Failed to delay update");
1044
- }
1045
- } catch (final Exception e) {
1046
- Log.e(
1047
- CapacitorUpdater.TAG,
1048
- "Failed to delay update, [Error calling 'setMultiDelay()']",
1049
- e
1050
- );
1051
- call.reject("Failed to delay update", e);
1052
- }
1053
- }
1054
-
1055
- private Boolean _setMultiDelay(String delayConditions) {
1056
- try {
1057
- this.editor.putString(DELAY_CONDITION_PREFERENCES, delayConditions);
1058
- this.editor.commit();
1059
- Log.i(CapacitorUpdater.TAG, "Delay update saved");
1060
- return true;
1061
- } catch (final Exception e) {
1062
- Log.e(
1063
- CapacitorUpdater.TAG,
1064
- "Failed to delay update, [Error calling '_setMultiDelay()']",
1065
- e
1066
- );
1067
- return false;
1068
956
  }
1069
- }
1070
-
1071
- private boolean _cancelDelay(String source) {
1072
- try {
1073
- this.editor.remove(DELAY_CONDITION_PREFERENCES);
1074
- this.editor.commit();
1075
- Log.i(CapacitorUpdater.TAG, "All delays canceled from " + source);
1076
- return true;
1077
- } catch (final Exception e) {
1078
- Log.e(CapacitorUpdater.TAG, "Failed to cancel update delay", e);
1079
- return false;
957
+
958
+ @PluginMethod
959
+ public void isAutoUpdateEnabled(final PluginCall call) {
960
+ try {
961
+ final JSObject ret = new JSObject();
962
+ ret.put("enabled", this._isAutoUpdateEnabled());
963
+ call.resolve(ret);
964
+ } catch (final Exception e) {
965
+ Log.e(CapacitorUpdater.TAG, "Could not get autoUpdate status", e);
966
+ call.reject("Could not get autoUpdate status", e);
967
+ }
1080
968
  }
1081
- }
1082
-
1083
- @PluginMethod
1084
- public void cancelDelay(final PluginCall call) {
1085
- if (this._cancelDelay("JS")) {
1086
- call.resolve();
1087
- } else {
1088
- call.reject("Failed to cancel delay");
969
+
970
+ @PluginMethod
971
+ public void isAutoUpdateAvailable(final PluginCall call) {
972
+ try {
973
+ final CapConfig config = CapConfig.loadDefault(this.getActivity());
974
+ String serverUrl = config.getServerUrl();
975
+ final JSObject ret = new JSObject();
976
+ ret.put("available", serverUrl == null || serverUrl.isEmpty());
977
+ call.resolve(ret);
978
+ } catch (final Exception e) {
979
+ Log.e(CapacitorUpdater.TAG, "Could not get autoUpdate availability", e);
980
+ call.reject("Could not get autoUpdate availability", e);
981
+ }
1089
982
  }
1090
- }
1091
-
1092
- private void _checkCancelDelay(Boolean killed) {
1093
- Gson gson = new Gson();
1094
- String delayUpdatePreferences = prefs.getString(
1095
- DELAY_CONDITION_PREFERENCES,
1096
- "[]"
1097
- );
1098
- Type type = new TypeToken<ArrayList<DelayCondition>>() {}.getType();
1099
- ArrayList<DelayCondition> delayConditionList = gson.fromJson(
1100
- delayUpdatePreferences,
1101
- type
1102
- );
1103
- for (DelayCondition condition : delayConditionList) {
1104
- String kind = condition.getKind().toString();
1105
- String value = condition.getValue();
1106
- if (!kind.isEmpty()) {
1107
- switch (kind) {
1108
- case "background":
1109
- if (!killed) {
1110
- this._cancelDelay("background check");
983
+
984
+ private void checkAppReady() {
985
+ try {
986
+ if (this.appReadyCheck != null) {
987
+ this.appReadyCheck.interrupt();
1111
988
  }
1112
- break;
1113
- case "kill":
1114
- if (killed) {
1115
- this._cancelDelay("kill check");
1116
- this.installNext();
989
+ this.appReadyCheck = startNewThread(new DeferredNotifyAppReadyCheck());
990
+ } catch (final Exception e) {
991
+ Log.e(CapacitorUpdater.TAG, "Failed to start " + DeferredNotifyAppReadyCheck.class.getName(), e);
992
+ }
993
+ }
994
+
995
+ private boolean isValidURL(String urlStr) {
996
+ try {
997
+ new URL(urlStr);
998
+ return true;
999
+ } catch (MalformedURLException e) {
1000
+ return false;
1001
+ }
1002
+ }
1003
+
1004
+ private void endBackGroundTaskWithNotif(String msg, String latestVersionName, BundleInfo current, Boolean error) {
1005
+ if (error) {
1006
+ Log.i(
1007
+ CapacitorUpdater.TAG,
1008
+ "endBackGroundTaskWithNotif error: " +
1009
+ error +
1010
+ " current: " +
1011
+ current.getVersionName() +
1012
+ "latestVersionName: " +
1013
+ latestVersionName
1014
+ );
1015
+ this.implementation.sendStats("download_fail", current.getVersionName());
1016
+ final JSObject ret = new JSObject();
1017
+ ret.put("version", latestVersionName);
1018
+ this.notifyListeners("downloadFailed", ret);
1019
+ }
1020
+ final JSObject ret = new JSObject();
1021
+ ret.put("bundle", current.toJSON());
1022
+ this.notifyListeners("noNeedUpdate", ret);
1023
+ this.sendReadyToJs(current, msg);
1024
+ this.backgroundDownloadTask = null;
1025
+ Log.i(CapacitorUpdater.TAG, "endBackGroundTaskWithNotif " + msg);
1026
+ }
1027
+
1028
+ private Thread backgroundDownload() {
1029
+ String messageUpdate = this.implementation.directUpdate
1030
+ ? "Update will occur now."
1031
+ : "Update will occur next time app moves to background.";
1032
+ return startNewThread(() -> {
1033
+ Log.i(CapacitorUpdater.TAG, "Check for update via: " + CapacitorUpdaterPlugin.this.updateUrl);
1034
+ CapacitorUpdaterPlugin.this.implementation.getLatest(CapacitorUpdaterPlugin.this.updateUrl, null, res -> {
1035
+ final BundleInfo current = CapacitorUpdaterPlugin.this.implementation.getCurrentBundle();
1036
+ try {
1037
+ if (res.has("message")) {
1038
+ Log.i(CapacitorUpdater.TAG, "API message: " + res.get("message"));
1039
+ if (res.has("major") && res.getBoolean("major") && res.has("version")) {
1040
+ final JSObject majorAvailable = new JSObject();
1041
+ majorAvailable.put("version", res.getString("version"));
1042
+ CapacitorUpdaterPlugin.this.notifyListeners("majorAvailable", majorAvailable);
1043
+ }
1044
+ CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1045
+ res.getString("message"),
1046
+ current.getVersionName(),
1047
+ current,
1048
+ true
1049
+ );
1050
+ return;
1051
+ }
1052
+
1053
+ final String latestVersionName = res.getString("version");
1054
+
1055
+ if ("builtin".equals(latestVersionName)) {
1056
+ Log.i(CapacitorUpdater.TAG, "Latest version is builtin");
1057
+ if (CapacitorUpdaterPlugin.this.implementation.directUpdate) {
1058
+ Log.i(CapacitorUpdater.TAG, "Direct update to builtin version");
1059
+ this._reset(false);
1060
+ CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1061
+ "Updated to builtin version",
1062
+ latestVersionName,
1063
+ CapacitorUpdaterPlugin.this.implementation.getCurrentBundle(),
1064
+ false
1065
+ );
1066
+ } else {
1067
+ Log.i(CapacitorUpdater.TAG, "Setting next bundle to builtin");
1068
+ CapacitorUpdaterPlugin.this.implementation.setNextBundle(BundleInfo.ID_BUILTIN);
1069
+ CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1070
+ "Next update will be to builtin version",
1071
+ latestVersionName,
1072
+ current,
1073
+ false
1074
+ );
1075
+ }
1076
+ return;
1077
+ }
1078
+
1079
+ if (!res.has("url") || !CapacitorUpdaterPlugin.this.isValidURL(res.getString("url"))) {
1080
+ Log.e(CapacitorUpdater.TAG, "Error no url or wrong format");
1081
+ CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1082
+ "Error no url or wrong format",
1083
+ current.getVersionName(),
1084
+ current,
1085
+ true
1086
+ );
1087
+ return;
1088
+ }
1089
+
1090
+ if (
1091
+ latestVersionName != null && !latestVersionName.isEmpty() && !current.getVersionName().equals(latestVersionName)
1092
+ ) {
1093
+ final BundleInfo latest = CapacitorUpdaterPlugin.this.implementation.getBundleInfoByName(latestVersionName);
1094
+ if (latest != null) {
1095
+ final JSObject ret = new JSObject();
1096
+ ret.put("bundle", latest.toJSON());
1097
+ if (latest.isErrorStatus()) {
1098
+ Log.e(CapacitorUpdater.TAG, "Latest bundle already exists, and is in error state. Aborting update.");
1099
+ CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1100
+ "Latest bundle already exists, and is in error state. Aborting update.",
1101
+ latestVersionName,
1102
+ current,
1103
+ true
1104
+ );
1105
+ return;
1106
+ }
1107
+ if (latest.isDownloaded()) {
1108
+ Log.i(
1109
+ CapacitorUpdater.TAG,
1110
+ "Latest bundle already exists and download is NOT required. " + messageUpdate
1111
+ );
1112
+ if (CapacitorUpdaterPlugin.this.implementation.directUpdate) {
1113
+ CapacitorUpdaterPlugin.this.implementation.set(latest);
1114
+ CapacitorUpdaterPlugin.this._reload();
1115
+ CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1116
+ "Update installed",
1117
+ latestVersionName,
1118
+ latest,
1119
+ false
1120
+ );
1121
+ } else {
1122
+ CapacitorUpdaterPlugin.this.notifyListeners("updateAvailable", ret);
1123
+ CapacitorUpdaterPlugin.this.implementation.setNextBundle(latest.getId());
1124
+ CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1125
+ "update downloaded, will install next background",
1126
+ latestVersionName,
1127
+ latest,
1128
+ false
1129
+ );
1130
+ }
1131
+ return;
1132
+ }
1133
+ if (latest.isDeleted()) {
1134
+ Log.i(
1135
+ CapacitorUpdater.TAG,
1136
+ "Latest bundle already exists and will be deleted, download will overwrite it."
1137
+ );
1138
+ try {
1139
+ final Boolean deleted = CapacitorUpdaterPlugin.this.implementation.delete(latest.getId(), true);
1140
+ if (deleted) {
1141
+ Log.i(CapacitorUpdater.TAG, "Failed bundle deleted: " + latest.getVersionName());
1142
+ }
1143
+ } catch (final IOException e) {
1144
+ Log.e(CapacitorUpdater.TAG, "Failed to delete failed bundle: " + latest.getVersionName(), e);
1145
+ }
1146
+ }
1147
+ }
1148
+ startNewThread(() -> {
1149
+ try {
1150
+ Log.i(
1151
+ CapacitorUpdater.TAG,
1152
+ "New bundle: " +
1153
+ latestVersionName +
1154
+ " found. Current is: " +
1155
+ current.getVersionName() +
1156
+ ". " +
1157
+ messageUpdate
1158
+ );
1159
+
1160
+ final String url = res.getString("url");
1161
+ final String sessionKey = res.has("sessionKey") ? res.getString("sessionKey") : "";
1162
+ final String checksum = res.has("checksum") ? res.getString("checksum") : "";
1163
+
1164
+ if (res.has("manifest")) {
1165
+ // Handle manifest-based download
1166
+ JSONArray manifest = res.getJSONArray("manifest");
1167
+ CapacitorUpdaterPlugin.this.implementation.downloadBackground(
1168
+ url,
1169
+ latestVersionName,
1170
+ sessionKey,
1171
+ checksum,
1172
+ manifest
1173
+ );
1174
+ } else {
1175
+ // Handle single file download (existing code)
1176
+ CapacitorUpdaterPlugin.this.implementation.downloadBackground(
1177
+ url,
1178
+ latestVersionName,
1179
+ sessionKey,
1180
+ checksum,
1181
+ null
1182
+ );
1183
+ }
1184
+ } catch (final Exception e) {
1185
+ Log.e(CapacitorUpdater.TAG, "error downloading file", e);
1186
+ CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1187
+ "Error downloading file",
1188
+ latestVersionName,
1189
+ CapacitorUpdaterPlugin.this.implementation.getCurrentBundle(),
1190
+ true
1191
+ );
1192
+ }
1193
+ });
1194
+ } else {
1195
+ Log.i(CapacitorUpdater.TAG, "No need to update, " + current.getId() + " is the latest bundle.");
1196
+ CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif("No need to update", latestVersionName, current, false);
1197
+ }
1198
+ } catch (final JSONException e) {
1199
+ Log.e(CapacitorUpdater.TAG, "error parsing JSON", e);
1200
+ CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1201
+ "Error parsing JSON",
1202
+ current.getVersionName(),
1203
+ current,
1204
+ true
1205
+ );
1206
+ }
1207
+ });
1208
+ });
1209
+ }
1210
+
1211
+ private void installNext() {
1212
+ try {
1213
+ Gson gson = new Gson();
1214
+ String delayUpdatePreferences = prefs.getString(DELAY_CONDITION_PREFERENCES, "[]");
1215
+ Type type = new TypeToken<ArrayList<DelayCondition>>() {}.getType();
1216
+ ArrayList<DelayCondition> delayConditionList = gson.fromJson(delayUpdatePreferences, type);
1217
+ if (delayConditionList != null && !delayConditionList.isEmpty()) {
1218
+ Log.i(CapacitorUpdater.TAG, "Update delayed until delay conditions met");
1219
+ return;
1117
1220
  }
1118
- break;
1119
- case "date":
1120
- if (!"".equals(value)) {
1121
- try {
1122
- final SimpleDateFormat sdf = new SimpleDateFormat(
1123
- "yyyy-MM-dd'T'HH:mm:ss.SSS"
1124
- );
1125
- Date date = sdf.parse(value);
1126
- assert date != null;
1127
- if (new Date().compareTo(date) > 0) {
1128
- this._cancelDelay("date expired");
1221
+ final BundleInfo current = this.implementation.getCurrentBundle();
1222
+ final BundleInfo next = this.implementation.getNextBundle();
1223
+
1224
+ if (next != null && !next.isErrorStatus() && !next.getId().equals(current.getId())) {
1225
+ // There is a next bundle waiting for activation
1226
+ Log.d(CapacitorUpdater.TAG, "Next bundle is: " + next.getVersionName());
1227
+ if (this.implementation.set(next) && this._reload()) {
1228
+ Log.i(CapacitorUpdater.TAG, "Updated to bundle: " + next.getVersionName());
1229
+ this.implementation.setNextBundle(null);
1230
+ } else {
1231
+ Log.e(CapacitorUpdater.TAG, "Update to bundle: " + next.getVersionName() + " Failed!");
1129
1232
  }
1130
- } catch (final Exception e) {
1131
- this._cancelDelay("date parsing issue");
1132
- }
1133
- } else {
1134
- this._cancelDelay("delayVal absent");
1135
1233
  }
1136
- break;
1137
- case "nativeVersion":
1138
- if (!"".equals(value)) {
1139
- try {
1140
- final Version versionLimit = new Version(value);
1141
- if (this.currentVersionNative.isAtLeast(versionLimit)) {
1142
- this._cancelDelay("nativeVersion above limit");
1234
+ } catch (final Exception e) {
1235
+ Log.e(CapacitorUpdater.TAG, "Error during onActivityStopped", e);
1236
+ }
1237
+ }
1238
+
1239
+ private void checkRevert() {
1240
+ // Automatically roll back to fallback version if notifyAppReady has not been called yet
1241
+ final BundleInfo current = this.implementation.getCurrentBundle();
1242
+
1243
+ if (current.isBuiltin()) {
1244
+ Log.i(CapacitorUpdater.TAG, "Built-in bundle is active. We skip the check for notifyAppReady.");
1245
+ return;
1246
+ }
1247
+ Log.d(CapacitorUpdater.TAG, "Current bundle is: " + current);
1248
+
1249
+ if (BundleStatus.SUCCESS != current.getStatus()) {
1250
+ Log.e(CapacitorUpdater.TAG, "notifyAppReady was not called, roll back current bundle: " + current.getId());
1251
+ Log.i(CapacitorUpdater.TAG, "Did you forget to call 'notifyAppReady()' in your Capacitor App code?");
1252
+ final JSObject ret = new JSObject();
1253
+ ret.put("bundle", current.toJSON());
1254
+ this.notifyListeners("updateFailed", ret);
1255
+ this.implementation.sendStats("update_fail", current.getVersionName());
1256
+ this.implementation.setError(current);
1257
+ this._reset(true);
1258
+ if (CapacitorUpdaterPlugin.this.autoDeleteFailed && !current.isBuiltin()) {
1259
+ Log.i(CapacitorUpdater.TAG, "Deleting failing bundle: " + current.getVersionName());
1260
+ try {
1261
+ final Boolean res = this.implementation.delete(current.getId(), false);
1262
+ if (res) {
1263
+ Log.i(CapacitorUpdater.TAG, "Failed bundle deleted: " + current.getVersionName());
1264
+ }
1265
+ } catch (final IOException e) {
1266
+ Log.e(CapacitorUpdater.TAG, "Failed to delete failed bundle: " + current.getVersionName(), e);
1143
1267
  }
1144
- } catch (final Exception e) {
1145
- this._cancelDelay("nativeVersion parsing issue");
1146
- }
1147
- } else {
1148
- this._cancelDelay("delayVal absent");
1149
1268
  }
1150
- break;
1269
+ } else {
1270
+ Log.i(CapacitorUpdater.TAG, "notifyAppReady was called. This is fine: " + current.getId());
1151
1271
  }
1152
- }
1153
- }
1154
- }
1155
-
1156
- private Boolean _isAutoUpdateEnabled() {
1157
- final CapConfig config = CapConfig.loadDefault(this.getActivity());
1158
- String serverUrl = config.getServerUrl();
1159
- if (serverUrl != null && !serverUrl.isEmpty()) {
1160
- // log warning autoupdate disabled when serverUrl is set
1161
- Log.w(
1162
- CapacitorUpdater.TAG,
1163
- "AutoUpdate is automatic disabled when serverUrl is set."
1164
- );
1165
- }
1166
- return (
1167
- CapacitorUpdaterPlugin.this.autoUpdate &&
1168
- !"".equals(CapacitorUpdaterPlugin.this.updateUrl) &&
1169
- (serverUrl == null || serverUrl.isEmpty())
1170
- );
1171
- }
1172
-
1173
- @PluginMethod
1174
- public void isAutoUpdateEnabled(final PluginCall call) {
1175
- try {
1176
- final JSObject ret = new JSObject();
1177
- ret.put("enabled", this._isAutoUpdateEnabled());
1178
- call.resolve(ret);
1179
- } catch (final Exception e) {
1180
- Log.e(CapacitorUpdater.TAG, "Could not get autoUpdate status", e);
1181
- call.reject("Could not get autoUpdate status", e);
1182
1272
  }
1183
- }
1184
-
1185
- @PluginMethod
1186
- public void isAutoUpdateAvailable(final PluginCall call) {
1187
- try {
1188
- final CapConfig config = CapConfig.loadDefault(this.getActivity());
1189
- String serverUrl = config.getServerUrl();
1190
- final JSObject ret = new JSObject();
1191
- ret.put("available", serverUrl == null || serverUrl.isEmpty());
1192
- call.resolve(ret);
1193
- } catch (final Exception e) {
1194
- Log.e(CapacitorUpdater.TAG, "Could not get autoUpdate availability", e);
1195
- call.reject("Could not get autoUpdate availability", e);
1196
- }
1197
- }
1198
-
1199
- private void checkAppReady() {
1200
- try {
1201
- if (this.appReadyCheck != null) {
1202
- this.appReadyCheck.interrupt();
1203
- }
1204
- this.appReadyCheck = startNewThread(new DeferredNotifyAppReadyCheck());
1205
- } catch (final Exception e) {
1206
- Log.e(
1207
- CapacitorUpdater.TAG,
1208
- "Failed to start " + DeferredNotifyAppReadyCheck.class.getName(),
1209
- e
1210
- );
1211
- }
1212
- }
1213
-
1214
- private boolean isValidURL(String urlStr) {
1215
- try {
1216
- new URL(urlStr);
1217
- return true;
1218
- } catch (MalformedURLException e) {
1219
- return false;
1220
- }
1221
- }
1222
-
1223
- private void endBackGroundTaskWithNotif(
1224
- String msg,
1225
- String latestVersionName,
1226
- BundleInfo current,
1227
- Boolean error
1228
- ) {
1229
- if (error) {
1230
- Log.i(
1231
- CapacitorUpdater.TAG,
1232
- "endBackGroundTaskWithNotif error: " +
1233
- error +
1234
- " current: " +
1235
- current.getVersionName() +
1236
- "latestVersionName: " +
1237
- latestVersionName
1238
- );
1239
- this.implementation.sendStats("download_fail", current.getVersionName());
1240
- final JSObject ret = new JSObject();
1241
- ret.put("version", latestVersionName);
1242
- this.notifyListeners("downloadFailed", ret);
1243
- }
1244
- final JSObject ret = new JSObject();
1245
- ret.put("bundle", current.toJSON());
1246
- this.notifyListeners("noNeedUpdate", ret);
1247
- this.sendReadyToJs(current, msg);
1248
- this.backgroundDownloadTask = null;
1249
- Log.i(CapacitorUpdater.TAG, "endBackGroundTaskWithNotif " + msg);
1250
- }
1251
-
1252
- private Thread backgroundDownload() {
1253
- String messageUpdate = this.implementation.directUpdate
1254
- ? "Update will occur now."
1255
- : "Update will occur next time app moves to background.";
1256
- return startNewThread(() -> {
1257
- Log.i(
1258
- CapacitorUpdater.TAG,
1259
- "Check for update via: " + CapacitorUpdaterPlugin.this.updateUrl
1260
- );
1261
- CapacitorUpdaterPlugin.this.implementation.getLatest(
1262
- CapacitorUpdaterPlugin.this.updateUrl,
1263
- null,
1264
- res -> {
1265
- final BundleInfo current =
1266
- CapacitorUpdaterPlugin.this.implementation.getCurrentBundle();
1273
+
1274
+ private class DeferredNotifyAppReadyCheck implements Runnable {
1275
+
1276
+ @Override
1277
+ public void run() {
1267
1278
  try {
1268
- if (res.has("message")) {
1269
1279
  Log.i(
1270
- CapacitorUpdater.TAG,
1271
- "API message: " + res.get("message")
1280
+ CapacitorUpdater.TAG,
1281
+ "Wait for " + CapacitorUpdaterPlugin.this.appReadyTimeout + "ms, then check for notifyAppReady"
1272
1282
  );
1273
- if (
1274
- res.has("major") &&
1275
- res.getBoolean("major") &&
1276
- res.has("version")
1277
- ) {
1278
- final JSObject majorAvailable = new JSObject();
1279
- majorAvailable.put("version", res.getString("version"));
1280
- CapacitorUpdaterPlugin.this.notifyListeners(
1281
- "majorAvailable",
1282
- majorAvailable
1283
- );
1284
- }
1285
- CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1286
- res.getString("message"),
1287
- current.getVersionName(),
1288
- current,
1289
- true
1290
- );
1291
- return;
1292
- }
1283
+ Thread.sleep(CapacitorUpdaterPlugin.this.appReadyTimeout);
1284
+ CapacitorUpdaterPlugin.this.checkRevert();
1285
+ CapacitorUpdaterPlugin.this.appReadyCheck = null;
1286
+ } catch (final InterruptedException e) {
1287
+ Log.i(CapacitorUpdater.TAG, DeferredNotifyAppReadyCheck.class.getName() + " was interrupted.");
1288
+ }
1289
+ }
1290
+ }
1293
1291
 
1294
- final String latestVersionName = res.getString("version");
1292
+ public void appMovedToForeground() {
1293
+ final BundleInfo current = CapacitorUpdaterPlugin.this.implementation.getCurrentBundle();
1294
+ CapacitorUpdaterPlugin.this.implementation.sendStats("app_moved_to_foreground", current.getVersionName());
1295
+ this._checkCancelDelay(false);
1296
+ if (
1297
+ CapacitorUpdaterPlugin.this._isAutoUpdateEnabled() &&
1298
+ (this.backgroundDownloadTask == null || !this.backgroundDownloadTask.isAlive())
1299
+ ) {
1300
+ this.backgroundDownloadTask = this.backgroundDownload();
1301
+ } else {
1302
+ Log.i(CapacitorUpdater.TAG, "Auto update is disabled");
1303
+ this.sendReadyToJs(current, "disabled");
1304
+ }
1305
+ this.checkAppReady();
1306
+ }
1295
1307
 
1296
- if ("builtin".equals(latestVersionName)) {
1297
- Log.i(CapacitorUpdater.TAG, "Latest version is builtin");
1298
- if (CapacitorUpdaterPlugin.this.implementation.directUpdate) {
1299
- Log.i(
1300
- CapacitorUpdater.TAG,
1301
- "Direct update to builtin version"
1302
- );
1303
- this._reset(false);
1304
- CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1305
- "Updated to builtin version",
1306
- latestVersionName,
1307
- CapacitorUpdaterPlugin.this.implementation.getCurrentBundle(),
1308
- false
1309
- );
1310
- } else {
1311
- Log.i(CapacitorUpdater.TAG, "Setting next bundle to builtin");
1312
- CapacitorUpdaterPlugin.this.implementation.setNextBundle(
1313
- BundleInfo.ID_BUILTIN
1314
- );
1315
- CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1316
- "Next update will be to builtin version",
1317
- latestVersionName,
1318
- current,
1319
- false
1320
- );
1308
+ public void appMovedToBackground() {
1309
+ final BundleInfo current = CapacitorUpdaterPlugin.this.implementation.getCurrentBundle();
1310
+ CapacitorUpdaterPlugin.this.implementation.sendStats("app_moved_to_background", current.getVersionName());
1311
+ Log.i(CapacitorUpdater.TAG, "Checking for pending update");
1312
+ try {
1313
+ Gson gson = new Gson();
1314
+ String delayUpdatePreferences = prefs.getString(DELAY_CONDITION_PREFERENCES, "[]");
1315
+ Type type = new TypeToken<ArrayList<DelayCondition>>() {}.getType();
1316
+ ArrayList<DelayCondition> delayConditionList = gson.fromJson(delayUpdatePreferences, type);
1317
+ String backgroundValue = null;
1318
+ for (DelayCondition delayCondition : delayConditionList) {
1319
+ if (delayCondition.getKind().toString().equals("background")) {
1320
+ String value = delayCondition.getValue();
1321
+ backgroundValue = (value != null && !value.isEmpty()) ? value : "0";
1321
1322
  }
1322
- return;
1323
- }
1324
-
1325
- if (
1326
- !res.has("url") ||
1327
- !CapacitorUpdaterPlugin.this.isValidURL(res.getString("url"))
1328
- ) {
1329
- Log.e(CapacitorUpdater.TAG, "Error no url or wrong format");
1330
- CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1331
- "Error no url or wrong format",
1332
- current.getVersionName(),
1333
- current,
1334
- true
1335
- );
1336
- return;
1337
- }
1338
-
1339
- if (
1340
- latestVersionName != null &&
1341
- !latestVersionName.isEmpty() &&
1342
- !current.getVersionName().equals(latestVersionName)
1343
- ) {
1344
- final BundleInfo latest =
1345
- CapacitorUpdaterPlugin.this.implementation.getBundleInfoByName(
1346
- latestVersionName
1347
- );
1348
- if (latest != null) {
1349
- final JSObject ret = new JSObject();
1350
- ret.put("bundle", latest.toJSON());
1351
- if (latest.isErrorStatus()) {
1352
- Log.e(
1353
- CapacitorUpdater.TAG,
1354
- "Latest bundle already exists, and is in error state. Aborting update."
1355
- );
1356
- CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1357
- "Latest bundle already exists, and is in error state. Aborting update.",
1358
- latestVersionName,
1359
- current,
1360
- true
1361
- );
1362
- return;
1363
- }
1364
- if (latest.isDownloaded()) {
1365
- Log.i(
1366
- CapacitorUpdater.TAG,
1367
- "Latest bundle already exists and download is NOT required. " +
1368
- messageUpdate
1369
- );
1370
- if (
1371
- CapacitorUpdaterPlugin.this.implementation.directUpdate
1372
- ) {
1373
- CapacitorUpdaterPlugin.this.implementation.set(latest);
1374
- CapacitorUpdaterPlugin.this._reload();
1375
- CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1376
- "Update installed",
1377
- latestVersionName,
1378
- latest,
1379
- false
1380
- );
1381
- } else {
1382
- CapacitorUpdaterPlugin.this.notifyListeners(
1383
- "updateAvailable",
1384
- ret
1385
- );
1386
- CapacitorUpdaterPlugin.this.implementation.setNextBundle(
1387
- latest.getId()
1388
- );
1389
- CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1390
- "update downloaded, will install next background",
1391
- latestVersionName,
1392
- latest,
1393
- false
1394
- );
1395
- }
1396
- return;
1397
- }
1398
- if (latest.isDeleted()) {
1399
- Log.i(
1400
- CapacitorUpdater.TAG,
1401
- "Latest bundle already exists and will be deleted, download will overwrite it."
1402
- );
1403
- try {
1404
- final Boolean deleted =
1405
- CapacitorUpdaterPlugin.this.implementation.delete(
1406
- latest.getId(),
1407
- true
1408
- );
1409
- if (deleted) {
1410
- Log.i(
1411
- CapacitorUpdater.TAG,
1412
- "Failed bundle deleted: " + latest.getVersionName()
1413
- );
1414
- }
1415
- } catch (final IOException e) {
1416
- Log.e(
1417
- CapacitorUpdater.TAG,
1418
- "Failed to delete failed bundle: " +
1419
- latest.getVersionName(),
1420
- e
1421
- );
1422
- }
1423
- }
1323
+ }
1324
+ if (backgroundValue != null) {
1325
+ taskRunning = true;
1326
+ final Long timeout = Long.parseLong(backgroundValue);
1327
+ if (backgroundTask != null) {
1328
+ backgroundTask.interrupt();
1424
1329
  }
1425
- startNewThread(() -> {
1426
- try {
1427
- Log.i(
1428
- CapacitorUpdater.TAG,
1429
- "New bundle: " +
1430
- latestVersionName +
1431
- " found. Current is: " +
1432
- current.getVersionName() +
1433
- ". " +
1434
- messageUpdate
1435
- );
1436
-
1437
- final String url = res.getString("url");
1438
- final String sessionKey = res.has("sessionKey")
1439
- ? res.getString("sessionKey")
1440
- : "";
1441
- final String checksum = res.has("checksum")
1442
- ? res.getString("checksum")
1443
- : "";
1444
-
1445
- if (res.has("manifest")) {
1446
- // Handle manifest-based download
1447
- JSONArray manifest = res.getJSONArray("manifest");
1448
- CapacitorUpdaterPlugin.this.implementation.downloadBackground(
1449
- url,
1450
- latestVersionName,
1451
- sessionKey,
1452
- checksum,
1453
- manifest
1454
- );
1455
- } else {
1456
- // Handle single file download (existing code)
1457
- CapacitorUpdaterPlugin.this.implementation.downloadBackground(
1458
- url,
1459
- latestVersionName,
1460
- sessionKey,
1461
- checksum,
1462
- null
1463
- );
1464
- }
1465
- } catch (final Exception e) {
1466
- Log.e(CapacitorUpdater.TAG, "error downloading file", e);
1467
- CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1468
- "Error downloading file",
1469
- latestVersionName,
1470
- CapacitorUpdaterPlugin.this.implementation.getCurrentBundle(),
1471
- true
1472
- );
1473
- }
1474
- });
1475
- } else {
1476
- Log.i(
1477
- CapacitorUpdater.TAG,
1478
- "No need to update, " +
1479
- current.getId() +
1480
- " is the latest bundle."
1481
- );
1482
- CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1483
- "No need to update",
1484
- latestVersionName,
1485
- current,
1486
- false
1487
- );
1488
- }
1489
- } catch (final JSONException e) {
1490
- Log.e(CapacitorUpdater.TAG, "error parsing JSON", e);
1491
- CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1492
- "Error parsing JSON",
1493
- current.getVersionName(),
1494
- current,
1495
- true
1330
+ backgroundTask = startNewThread(
1331
+ () -> {
1332
+ taskRunning = false;
1333
+ _checkCancelDelay(false);
1334
+ installNext();
1335
+ },
1336
+ timeout
1496
1337
  );
1338
+ } else {
1339
+ this._checkCancelDelay(false);
1340
+ this.installNext();
1497
1341
  }
1498
- }
1499
- );
1500
- });
1501
- }
1502
-
1503
- private void installNext() {
1504
- try {
1505
- Gson gson = new Gson();
1506
- String delayUpdatePreferences = prefs.getString(
1507
- DELAY_CONDITION_PREFERENCES,
1508
- "[]"
1509
- );
1510
- Type type = new TypeToken<ArrayList<DelayCondition>>() {}.getType();
1511
- ArrayList<DelayCondition> delayConditionList = gson.fromJson(
1512
- delayUpdatePreferences,
1513
- type
1514
- );
1515
- if (delayConditionList != null && !delayConditionList.isEmpty()) {
1516
- Log.i(
1517
- CapacitorUpdater.TAG,
1518
- "Update delayed until delay conditions met"
1519
- );
1520
- return;
1521
- }
1522
- final BundleInfo current = this.implementation.getCurrentBundle();
1523
- final BundleInfo next = this.implementation.getNextBundle();
1524
-
1525
- if (
1526
- next != null &&
1527
- !next.isErrorStatus() &&
1528
- !next.getId().equals(current.getId())
1529
- ) {
1530
- // There is a next bundle waiting for activation
1531
- Log.d(CapacitorUpdater.TAG, "Next bundle is: " + next.getVersionName());
1532
- if (this.implementation.set(next) && this._reload()) {
1533
- Log.i(
1534
- CapacitorUpdater.TAG,
1535
- "Updated to bundle: " + next.getVersionName()
1536
- );
1537
- this.implementation.setNextBundle(null);
1538
- } else {
1539
- Log.e(
1540
- CapacitorUpdater.TAG,
1541
- "Update to bundle: " + next.getVersionName() + " Failed!"
1542
- );
1543
- }
1544
- }
1545
- } catch (final Exception e) {
1546
- Log.e(CapacitorUpdater.TAG, "Error during onActivityStopped", e);
1547
- }
1548
- }
1549
-
1550
- private void checkRevert() {
1551
- // Automatically roll back to fallback version if notifyAppReady has not been called yet
1552
- final BundleInfo current = this.implementation.getCurrentBundle();
1553
-
1554
- if (current.isBuiltin()) {
1555
- Log.i(
1556
- CapacitorUpdater.TAG,
1557
- "Built-in bundle is active. We skip the check for notifyAppReady."
1558
- );
1559
- return;
1342
+ } catch (final Exception e) {
1343
+ Log.e(CapacitorUpdater.TAG, "Error during onActivityStopped", e);
1344
+ }
1560
1345
  }
1561
- Log.d(CapacitorUpdater.TAG, "Current bundle is: " + current);
1562
-
1563
- if (BundleStatus.SUCCESS != current.getStatus()) {
1564
- Log.e(
1565
- CapacitorUpdater.TAG,
1566
- "notifyAppReady was not called, roll back current bundle: " +
1567
- current.getId()
1568
- );
1569
- Log.i(
1570
- CapacitorUpdater.TAG,
1571
- "Did you forget to call 'notifyAppReady()' in your Capacitor App code?"
1572
- );
1573
- final JSObject ret = new JSObject();
1574
- ret.put("bundle", current.toJSON());
1575
- this.notifyListeners("updateFailed", ret);
1576
- this.implementation.sendStats("update_fail", current.getVersionName());
1577
- this.implementation.setError(current);
1578
- this._reset(true);
1579
- if (
1580
- CapacitorUpdaterPlugin.this.autoDeleteFailed && !current.isBuiltin()
1581
- ) {
1582
- Log.i(
1583
- CapacitorUpdater.TAG,
1584
- "Deleting failing bundle: " + current.getVersionName()
1585
- );
1346
+
1347
+ private boolean isMainActivity() {
1348
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
1349
+ return false;
1350
+ }
1586
1351
  try {
1587
- final Boolean res =
1588
- this.implementation.delete(current.getId(), false);
1589
- if (res) {
1590
- Log.i(
1591
- CapacitorUpdater.TAG,
1592
- "Failed bundle deleted: " + current.getVersionName()
1593
- );
1594
- }
1595
- } catch (final IOException e) {
1596
- Log.e(
1597
- CapacitorUpdater.TAG,
1598
- "Failed to delete failed bundle: " + current.getVersionName(),
1599
- e
1600
- );
1601
- }
1602
- }
1603
- } else {
1604
- Log.i(
1605
- CapacitorUpdater.TAG,
1606
- "notifyAppReady was called. This is fine: " + current.getId()
1607
- );
1352
+ Context mContext = this.getContext();
1353
+ ActivityManager activityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
1354
+ List<ActivityManager.AppTask> runningTasks = activityManager.getAppTasks();
1355
+ if (runningTasks.isEmpty()) {
1356
+ return false;
1357
+ }
1358
+ ActivityManager.RecentTaskInfo runningTask = runningTasks.get(0).getTaskInfo();
1359
+ String className = Objects.requireNonNull(runningTask.baseIntent.getComponent()).getClassName();
1360
+ if (runningTask.topActivity == null) {
1361
+ return false;
1362
+ }
1363
+ String runningActivity = runningTask.topActivity.getClassName();
1364
+ return className.equals(runningActivity);
1365
+ } catch (NullPointerException e) {
1366
+ return false;
1367
+ }
1608
1368
  }
1609
- }
1610
1369
 
1611
- private class DeferredNotifyAppReadyCheck implements Runnable {
1370
+ private void appKilled() {
1371
+ Log.d(CapacitorUpdater.TAG, "onActivityDestroyed: all activity destroyed");
1372
+ this._checkCancelDelay(true);
1373
+ }
1612
1374
 
1613
1375
  @Override
1614
- public void run() {
1615
- try {
1616
- Log.i(
1617
- CapacitorUpdater.TAG,
1618
- "Wait for " +
1619
- CapacitorUpdaterPlugin.this.appReadyTimeout +
1620
- "ms, then check for notifyAppReady"
1621
- );
1622
- Thread.sleep(CapacitorUpdaterPlugin.this.appReadyTimeout);
1623
- CapacitorUpdaterPlugin.this.checkRevert();
1624
- CapacitorUpdaterPlugin.this.appReadyCheck = null;
1625
- } catch (final InterruptedException e) {
1626
- Log.i(
1627
- CapacitorUpdater.TAG,
1628
- DeferredNotifyAppReadyCheck.class.getName() + " was interrupted."
1629
- );
1630
- }
1631
- }
1632
- }
1633
-
1634
- public void appMovedToForeground() {
1635
- final BundleInfo current =
1636
- CapacitorUpdaterPlugin.this.implementation.getCurrentBundle();
1637
- CapacitorUpdaterPlugin.this.implementation.sendStats(
1638
- "app_moved_to_foreground",
1639
- current.getVersionName()
1640
- );
1641
- this._checkCancelDelay(false);
1642
- if (
1643
- CapacitorUpdaterPlugin.this._isAutoUpdateEnabled() &&
1644
- (this.backgroundDownloadTask == null ||
1645
- !this.backgroundDownloadTask.isAlive())
1646
- ) {
1647
- this.backgroundDownloadTask = this.backgroundDownload();
1648
- } else {
1649
- Log.i(CapacitorUpdater.TAG, "Auto update is disabled");
1650
- this.sendReadyToJs(current, "disabled");
1651
- }
1652
- this.checkAppReady();
1653
- }
1654
-
1655
- public void appMovedToBackground() {
1656
- final BundleInfo current =
1657
- CapacitorUpdaterPlugin.this.implementation.getCurrentBundle();
1658
- CapacitorUpdaterPlugin.this.implementation.sendStats(
1659
- "app_moved_to_background",
1660
- current.getVersionName()
1661
- );
1662
- Log.i(CapacitorUpdater.TAG, "Checking for pending update");
1663
- try {
1664
- Gson gson = new Gson();
1665
- String delayUpdatePreferences = prefs.getString(
1666
- DELAY_CONDITION_PREFERENCES,
1667
- "[]"
1668
- );
1669
- Type type = new TypeToken<ArrayList<DelayCondition>>() {}.getType();
1670
- ArrayList<DelayCondition> delayConditionList = gson.fromJson(
1671
- delayUpdatePreferences,
1672
- type
1673
- );
1674
- String backgroundValue = null;
1675
- for (DelayCondition delayCondition : delayConditionList) {
1676
- if (delayCondition.getKind().toString().equals("background")) {
1677
- String value = delayCondition.getValue();
1678
- backgroundValue = (value != null && !value.isEmpty()) ? value : "0";
1679
- }
1680
- }
1681
- if (backgroundValue != null) {
1682
- taskRunning = true;
1683
- final Long timeout = Long.parseLong(backgroundValue);
1684
- if (backgroundTask != null) {
1685
- backgroundTask.interrupt();
1686
- }
1687
- backgroundTask = startNewThread(
1688
- () -> {
1689
- taskRunning = false;
1690
- _checkCancelDelay(false);
1691
- installNext();
1692
- },
1693
- timeout
1694
- );
1695
- } else {
1696
- this._checkCancelDelay(false);
1697
- this.installNext();
1698
- }
1699
- } catch (final Exception e) {
1700
- Log.e(CapacitorUpdater.TAG, "Error during onActivityStopped", e);
1376
+ public void handleOnStart() {
1377
+ if (isPreviousMainActivity) {
1378
+ this.appMovedToForeground();
1379
+ }
1380
+ Log.i(CapacitorUpdater.TAG, "onActivityStarted " + getActivity().getClass().getName());
1381
+ isPreviousMainActivity = true;
1701
1382
  }
1702
- }
1703
1383
 
1704
- private boolean isMainActivity() {
1705
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
1706
- return false;
1707
- }
1708
- try {
1709
- Context mContext = this.getContext();
1710
- ActivityManager activityManager =
1711
- (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
1712
- List<ActivityManager.AppTask> runningTasks =
1713
- activityManager.getAppTasks();
1714
- if (runningTasks.isEmpty()) {
1715
- return false;
1716
- }
1717
- ActivityManager.RecentTaskInfo runningTask = runningTasks
1718
- .get(0)
1719
- .getTaskInfo();
1720
- String className = Objects.requireNonNull(
1721
- runningTask.baseIntent.getComponent()
1722
- ).getClassName();
1723
- if (runningTask.topActivity == null) {
1724
- return false;
1725
- }
1726
- String runningActivity = runningTask.topActivity.getClassName();
1727
- return className.equals(runningActivity);
1728
- } catch (NullPointerException e) {
1729
- return false;
1384
+ @Override
1385
+ public void handleOnStop() {
1386
+ isPreviousMainActivity = isMainActivity();
1387
+ if (isPreviousMainActivity) {
1388
+ this.appMovedToBackground();
1389
+ }
1730
1390
  }
1731
- }
1732
1391
 
1733
- private void appKilled() {
1734
- Log.d(CapacitorUpdater.TAG, "onActivityDestroyed: all activity destroyed");
1735
- this._checkCancelDelay(true);
1736
- }
1737
-
1738
- @Override
1739
- public void handleOnStart() {
1740
- if (isPreviousMainActivity) {
1741
- this.appMovedToForeground();
1742
- }
1743
- Log.i(
1744
- CapacitorUpdater.TAG,
1745
- "onActivityStarted " + getActivity().getClass().getName()
1746
- );
1747
- isPreviousMainActivity = true;
1748
- }
1749
-
1750
- @Override
1751
- public void handleOnStop() {
1752
- isPreviousMainActivity = isMainActivity();
1753
- if (isPreviousMainActivity) {
1754
- this.appMovedToBackground();
1392
+ @Override
1393
+ public void handleOnResume() {
1394
+ if (backgroundTask != null && taskRunning) {
1395
+ backgroundTask.interrupt();
1396
+ }
1397
+ this.implementation.activity = getActivity();
1755
1398
  }
1756
- }
1757
1399
 
1758
- @Override
1759
- public void handleOnResume() {
1760
- if (backgroundTask != null && taskRunning) {
1761
- backgroundTask.interrupt();
1400
+ @Override
1401
+ public void handleOnPause() {
1402
+ this.implementation.activity = getActivity();
1762
1403
  }
1763
- this.implementation.activity = getActivity();
1764
- }
1765
-
1766
- @Override
1767
- public void handleOnPause() {
1768
- this.implementation.activity = getActivity();
1769
- }
1770
-
1771
- @Override
1772
- public void handleOnDestroy() {
1773
- Log.i(
1774
- CapacitorUpdater.TAG,
1775
- "onActivityDestroyed " + getActivity().getClass().getName()
1776
- );
1777
- this.implementation.activity = getActivity();
1778
- counterActivityCreate--;
1779
- if (counterActivityCreate == 0) {
1780
- this.appKilled();
1404
+
1405
+ @Override
1406
+ public void handleOnDestroy() {
1407
+ Log.i(CapacitorUpdater.TAG, "onActivityDestroyed " + getActivity().getClass().getName());
1408
+ this.implementation.activity = getActivity();
1409
+ counterActivityCreate--;
1410
+ if (counterActivityCreate == 0) {
1411
+ this.appKilled();
1412
+ }
1781
1413
  }
1782
- }
1783
1414
  }