@capgo/capacitor-updater 6.14.0 → 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 +11 -11
  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 +960 -1165
  6. package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +1259 -1629
  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 +17 -17
  16. package/dist/esm/definitions.d.ts +13 -13
  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 +8 -8
  29. package/ios/Plugin/CapacitorUpdaterPlugin.swift +1 -1
  30. package/package.json +5 -7
@@ -53,1732 +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.14.0";
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(false);
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 =
848
- this.implementation.list(call.getBoolean("raw", false));
849
- final JSObject ret = new JSObject();
850
- final JSArray values = new JSArray();
851
- for (final BundleInfo bundle : res) {
852
- values.put(bundle.toJSON());
853
- }
854
- ret.put("bundles", values);
855
- call.resolve(ret);
856
- } catch (final Exception e) {
857
- Log.e(CapacitorUpdater.TAG, "Could not list bundles", e);
858
- call.reject("Could not list bundles", e);
859
815
  }
860
- }
861
-
862
- @PluginMethod
863
- public void getLatest(final PluginCall call) {
864
- final String channel = call.getString("channel");
865
- startNewThread(() ->
866
- CapacitorUpdaterPlugin.this.implementation.getLatest(
867
- CapacitorUpdaterPlugin.this.updateUrl,
868
- channel,
869
- res -> {
870
- if (res.has("error")) {
871
- call.reject(res.getString("error"));
872
- return;
873
- } else if (res.has("message")) {
874
- call.reject(res.getString("message"));
875
- return;
876
- } else {
877
- call.resolve(res);
878
- }
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");
879
826
  final JSObject ret = new JSObject();
880
- Iterator<String> keys = res.keys();
881
- while (keys.hasNext()) {
882
- String key = keys.next();
883
- if (res.has(key)) {
884
- try {
885
- ret.put(key, res.get(key));
886
- } catch (JSONException e) {
887
- e.printStackTrace();
888
- }
889
- }
890
- }
827
+ ret.put("bundle", bundle.toJSON());
891
828
  call.resolve(ret);
892
- }
893
- )
894
- );
895
- }
896
-
897
- private boolean _reset(final Boolean toLastSuccessful) {
898
- final BundleInfo fallback = this.implementation.getFallbackBundle();
899
- this.implementation.reset();
900
-
901
- if (toLastSuccessful && !fallback.isBuiltin()) {
902
- Log.i(CapacitorUpdater.TAG, "Resetting to: " + fallback);
903
- 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
+ }
904
833
  }
905
834
 
906
- Log.i(CapacitorUpdater.TAG, "Resetting to native.");
907
- return this._reload();
908
- }
909
-
910
- @PluginMethod
911
- public void reset(final PluginCall call) {
912
- try {
913
- final Boolean toLastSuccessful = call.getBoolean(
914
- "toLastSuccessful",
915
- false
916
- );
917
- if (this._reset(toLastSuccessful)) {
918
- call.resolve();
919
- return;
920
- }
921
- Log.e(CapacitorUpdater.TAG, "Reset failed");
922
- call.reject("Reset failed");
923
- } catch (final Exception e) {
924
- Log.e(CapacitorUpdater.TAG, "Reset failed", e);
925
- 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
+ }
926
853
  }
927
- }
928
-
929
- @PluginMethod
930
- public void current(final PluginCall call) {
931
- try {
932
- final JSObject ret = new JSObject();
933
- final BundleInfo bundle = this.implementation.getCurrentBundle();
934
- ret.put("bundle", bundle.toJSON());
935
- ret.put("native", this.currentVersionNative);
936
- call.resolve(ret);
937
- } catch (final Exception e) {
938
- Log.e(CapacitorUpdater.TAG, "Could not get current bundle", e);
939
- 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
+ }
940
865
  }
941
- }
942
-
943
- @PluginMethod
944
- public void getNextBundle(final PluginCall call) {
945
- try {
946
- final BundleInfo bundle = this.implementation.getNextBundle();
947
- if (bundle == null) {
948
- call.resolve(null);
949
- return;
950
- }
951
-
952
- call.resolve(bundle.toJSON());
953
- } catch (final Exception e) {
954
- Log.e(CapacitorUpdater.TAG, "Could not get next bundle", e);
955
- 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
+ }
956
877
  }
957
- }
958
878
 
959
- public void checkForUpdateAfterDelay() {
960
- if (this.periodCheckDelay == 0 || !this._isAutoUpdateEnabled()) {
961
- 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
+ }
962
886
  }
963
- final Timer timer = new Timer();
964
- timer.schedule(
965
- new TimerTask() {
966
- @Override
967
- public void run() {
968
- try {
969
- CapacitorUpdaterPlugin.this.implementation.getLatest(
970
- CapacitorUpdaterPlugin.this.updateUrl,
971
- null,
972
- res -> {
973
- if (res.has("error")) {
974
- Log.e(
975
- CapacitorUpdater.TAG,
976
- Objects.requireNonNull(res.getString("error"))
977
- );
978
- } else if (res.has("version")) {
979
- String newVersion = res.getString("version");
980
- String currentVersion = String.valueOf(
981
- CapacitorUpdaterPlugin.this.implementation.getCurrentBundle()
982
- );
983
- if (!Objects.equals(newVersion, currentVersion)) {
984
- Log.i(
985
- CapacitorUpdater.TAG,
986
- "New version found: " + newVersion
987
- );
988
- CapacitorUpdaterPlugin.this.backgroundDownload();
989
- }
990
- }
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;
991
939
  }
992
- );
993
- } catch (final Exception e) {
994
- Log.e(CapacitorUpdater.TAG, "Failed to check for update", e);
995
- }
996
- }
997
- },
998
- this.periodCheckDelay,
999
- this.periodCheckDelay
1000
- );
1001
- }
1002
-
1003
- @PluginMethod
1004
- public void notifyAppReady(final PluginCall call) {
1005
- try {
1006
- final BundleInfo bundle = this.implementation.getCurrentBundle();
1007
- this.implementation.setSuccess(bundle, this.autoDeletePrevious);
1008
- Log.i(
1009
- CapacitorUpdater.TAG,
1010
- "Current bundle loaded successfully. ['notifyAppReady()' was called] " +
1011
- bundle
1012
- );
1013
- Log.i(CapacitorUpdater.TAG, "semaphoreReady countDown");
1014
- this.semaphoreDown();
1015
- Log.i(CapacitorUpdater.TAG, "semaphoreReady countDown done");
1016
- final JSObject ret = new JSObject();
1017
- ret.put("bundle", bundle.toJSON());
1018
- call.resolve(ret);
1019
- } catch (final Exception e) {
1020
- Log.e(
1021
- CapacitorUpdater.TAG,
1022
- "Failed to notify app ready state. [Error calling 'notifyAppReady()']",
1023
- e
1024
- );
1025
- call.reject("Failed to commit app ready state.", e);
940
+ }
941
+ }
1026
942
  }
1027
- }
1028
-
1029
- @PluginMethod
1030
- public void setMultiDelay(final PluginCall call) {
1031
- try {
1032
- final Object delayConditions = call.getData().opt("delayConditions");
1033
- if (delayConditions == null) {
1034
- Log.e(
1035
- CapacitorUpdater.TAG,
1036
- "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())
1037
955
  );
1038
- call.reject("setMultiDelay called without delayCondition");
1039
- return;
1040
- }
1041
- if (_setMultiDelay(delayConditions.toString())) {
1042
- call.resolve();
1043
- } else {
1044
- call.reject("Failed to delay update");
1045
- }
1046
- } catch (final Exception e) {
1047
- Log.e(
1048
- CapacitorUpdater.TAG,
1049
- "Failed to delay update, [Error calling 'setMultiDelay()']",
1050
- e
1051
- );
1052
- call.reject("Failed to delay update", e);
1053
- }
1054
- }
1055
-
1056
- private Boolean _setMultiDelay(String delayConditions) {
1057
- try {
1058
- this.editor.putString(DELAY_CONDITION_PREFERENCES, delayConditions);
1059
- this.editor.commit();
1060
- Log.i(CapacitorUpdater.TAG, "Delay update saved");
1061
- return true;
1062
- } catch (final Exception e) {
1063
- Log.e(
1064
- CapacitorUpdater.TAG,
1065
- "Failed to delay update, [Error calling '_setMultiDelay()']",
1066
- e
1067
- );
1068
- return false;
1069
956
  }
1070
- }
1071
-
1072
- private boolean _cancelDelay(String source) {
1073
- try {
1074
- this.editor.remove(DELAY_CONDITION_PREFERENCES);
1075
- this.editor.commit();
1076
- Log.i(CapacitorUpdater.TAG, "All delays canceled from " + source);
1077
- return true;
1078
- } catch (final Exception e) {
1079
- Log.e(CapacitorUpdater.TAG, "Failed to cancel update delay", e);
1080
- 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
+ }
1081
968
  }
1082
- }
1083
-
1084
- @PluginMethod
1085
- public void cancelDelay(final PluginCall call) {
1086
- if (this._cancelDelay("JS")) {
1087
- call.resolve();
1088
- } else {
1089
- 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
+ }
1090
982
  }
1091
- }
1092
-
1093
- private void _checkCancelDelay(Boolean killed) {
1094
- Gson gson = new Gson();
1095
- String delayUpdatePreferences = prefs.getString(
1096
- DELAY_CONDITION_PREFERENCES,
1097
- "[]"
1098
- );
1099
- Type type = new TypeToken<ArrayList<DelayCondition>>() {}.getType();
1100
- ArrayList<DelayCondition> delayConditionList = gson.fromJson(
1101
- delayUpdatePreferences,
1102
- type
1103
- );
1104
- for (DelayCondition condition : delayConditionList) {
1105
- String kind = condition.getKind().toString();
1106
- String value = condition.getValue();
1107
- if (!kind.isEmpty()) {
1108
- switch (kind) {
1109
- case "background":
1110
- if (!killed) {
1111
- this._cancelDelay("background check");
983
+
984
+ private void checkAppReady() {
985
+ try {
986
+ if (this.appReadyCheck != null) {
987
+ this.appReadyCheck.interrupt();
1112
988
  }
1113
- break;
1114
- case "kill":
1115
- if (killed) {
1116
- this._cancelDelay("kill check");
1117
- 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;
1118
1220
  }
1119
- break;
1120
- case "date":
1121
- if (!"".equals(value)) {
1122
- try {
1123
- final SimpleDateFormat sdf = new SimpleDateFormat(
1124
- "yyyy-MM-dd'T'HH:mm:ss.SSS"
1125
- );
1126
- Date date = sdf.parse(value);
1127
- assert date != null;
1128
- if (new Date().compareTo(date) > 0) {
1129
- 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!");
1130
1232
  }
1131
- } catch (final Exception e) {
1132
- this._cancelDelay("date parsing issue");
1133
- }
1134
- } else {
1135
- this._cancelDelay("delayVal absent");
1136
1233
  }
1137
- break;
1138
- case "nativeVersion":
1139
- if (!"".equals(value)) {
1140
- try {
1141
- final Version versionLimit = new Version(value);
1142
- if (this.currentVersionNative.isAtLeast(versionLimit)) {
1143
- 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);
1144
1267
  }
1145
- } catch (final Exception e) {
1146
- this._cancelDelay("nativeVersion parsing issue");
1147
- }
1148
- } else {
1149
- this._cancelDelay("delayVal absent");
1150
1268
  }
1151
- break;
1269
+ } else {
1270
+ Log.i(CapacitorUpdater.TAG, "notifyAppReady was called. This is fine: " + current.getId());
1152
1271
  }
1153
- }
1154
- }
1155
- }
1156
-
1157
- private Boolean _isAutoUpdateEnabled() {
1158
- final CapConfig config = CapConfig.loadDefault(this.getActivity());
1159
- String serverUrl = config.getServerUrl();
1160
- if (serverUrl != null && !serverUrl.isEmpty()) {
1161
- // log warning autoupdate disabled when serverUrl is set
1162
- Log.w(
1163
- CapacitorUpdater.TAG,
1164
- "AutoUpdate is automatic disabled when serverUrl is set."
1165
- );
1166
- }
1167
- return (
1168
- CapacitorUpdaterPlugin.this.autoUpdate &&
1169
- !"".equals(CapacitorUpdaterPlugin.this.updateUrl) &&
1170
- (serverUrl == null || serverUrl.isEmpty())
1171
- );
1172
- }
1173
-
1174
- @PluginMethod
1175
- public void isAutoUpdateEnabled(final PluginCall call) {
1176
- try {
1177
- final JSObject ret = new JSObject();
1178
- ret.put("enabled", this._isAutoUpdateEnabled());
1179
- call.resolve(ret);
1180
- } catch (final Exception e) {
1181
- Log.e(CapacitorUpdater.TAG, "Could not get autoUpdate status", e);
1182
- call.reject("Could not get autoUpdate status", e);
1183
1272
  }
1184
- }
1185
-
1186
- @PluginMethod
1187
- public void isAutoUpdateAvailable(final PluginCall call) {
1188
- try {
1189
- final CapConfig config = CapConfig.loadDefault(this.getActivity());
1190
- String serverUrl = config.getServerUrl();
1191
- final JSObject ret = new JSObject();
1192
- ret.put("available", serverUrl == null || serverUrl.isEmpty());
1193
- call.resolve(ret);
1194
- } catch (final Exception e) {
1195
- Log.e(CapacitorUpdater.TAG, "Could not get autoUpdate availability", e);
1196
- call.reject("Could not get autoUpdate availability", e);
1197
- }
1198
- }
1199
-
1200
- private void checkAppReady() {
1201
- try {
1202
- if (this.appReadyCheck != null) {
1203
- this.appReadyCheck.interrupt();
1204
- }
1205
- this.appReadyCheck = startNewThread(new DeferredNotifyAppReadyCheck());
1206
- } catch (final Exception e) {
1207
- Log.e(
1208
- CapacitorUpdater.TAG,
1209
- "Failed to start " + DeferredNotifyAppReadyCheck.class.getName(),
1210
- e
1211
- );
1212
- }
1213
- }
1214
-
1215
- private boolean isValidURL(String urlStr) {
1216
- try {
1217
- new URL(urlStr);
1218
- return true;
1219
- } catch (MalformedURLException e) {
1220
- return false;
1221
- }
1222
- }
1223
-
1224
- private void endBackGroundTaskWithNotif(
1225
- String msg,
1226
- String latestVersionName,
1227
- BundleInfo current,
1228
- Boolean error
1229
- ) {
1230
- if (error) {
1231
- Log.i(
1232
- CapacitorUpdater.TAG,
1233
- "endBackGroundTaskWithNotif error: " +
1234
- error +
1235
- " current: " +
1236
- current.getVersionName() +
1237
- "latestVersionName: " +
1238
- latestVersionName
1239
- );
1240
- this.implementation.sendStats("download_fail", current.getVersionName());
1241
- final JSObject ret = new JSObject();
1242
- ret.put("version", latestVersionName);
1243
- this.notifyListeners("downloadFailed", ret);
1244
- }
1245
- final JSObject ret = new JSObject();
1246
- ret.put("bundle", current.toJSON());
1247
- this.notifyListeners("noNeedUpdate", ret);
1248
- this.sendReadyToJs(current, msg);
1249
- this.backgroundDownloadTask = null;
1250
- Log.i(CapacitorUpdater.TAG, "endBackGroundTaskWithNotif " + msg);
1251
- }
1252
-
1253
- private Thread backgroundDownload() {
1254
- String messageUpdate = this.implementation.directUpdate
1255
- ? "Update will occur now."
1256
- : "Update will occur next time app moves to background.";
1257
- return startNewThread(() -> {
1258
- Log.i(
1259
- CapacitorUpdater.TAG,
1260
- "Check for update via: " + CapacitorUpdaterPlugin.this.updateUrl
1261
- );
1262
- CapacitorUpdaterPlugin.this.implementation.getLatest(
1263
- CapacitorUpdaterPlugin.this.updateUrl,
1264
- null,
1265
- res -> {
1266
- final BundleInfo current =
1267
- CapacitorUpdaterPlugin.this.implementation.getCurrentBundle();
1273
+
1274
+ private class DeferredNotifyAppReadyCheck implements Runnable {
1275
+
1276
+ @Override
1277
+ public void run() {
1268
1278
  try {
1269
- if (res.has("message")) {
1270
1279
  Log.i(
1271
- CapacitorUpdater.TAG,
1272
- "API message: " + res.get("message")
1280
+ CapacitorUpdater.TAG,
1281
+ "Wait for " + CapacitorUpdaterPlugin.this.appReadyTimeout + "ms, then check for notifyAppReady"
1273
1282
  );
1274
- if (
1275
- res.has("major") &&
1276
- res.getBoolean("major") &&
1277
- res.has("version")
1278
- ) {
1279
- final JSObject majorAvailable = new JSObject();
1280
- majorAvailable.put("version", res.getString("version"));
1281
- CapacitorUpdaterPlugin.this.notifyListeners(
1282
- "majorAvailable",
1283
- majorAvailable
1284
- );
1285
- }
1286
- CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1287
- res.getString("message"),
1288
- current.getVersionName(),
1289
- current,
1290
- true
1291
- );
1292
- return;
1293
- }
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
+ }
1294
1291
 
1295
- 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
+ }
1296
1307
 
1297
- if ("builtin".equals(latestVersionName)) {
1298
- Log.i(CapacitorUpdater.TAG, "Latest version is builtin");
1299
- if (CapacitorUpdaterPlugin.this.implementation.directUpdate) {
1300
- Log.i(
1301
- CapacitorUpdater.TAG,
1302
- "Direct update to builtin version"
1303
- );
1304
- this._reset(false);
1305
- CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1306
- "Updated to builtin version",
1307
- latestVersionName,
1308
- CapacitorUpdaterPlugin.this.implementation.getCurrentBundle(),
1309
- false
1310
- );
1311
- } else {
1312
- Log.i(CapacitorUpdater.TAG, "Setting next bundle to builtin");
1313
- CapacitorUpdaterPlugin.this.implementation.setNextBundle(
1314
- BundleInfo.ID_BUILTIN
1315
- );
1316
- CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1317
- "Next update will be to builtin version",
1318
- latestVersionName,
1319
- current,
1320
- false
1321
- );
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";
1322
1322
  }
1323
- return;
1324
- }
1325
-
1326
- if (
1327
- !res.has("url") ||
1328
- !CapacitorUpdaterPlugin.this.isValidURL(res.getString("url"))
1329
- ) {
1330
- Log.e(CapacitorUpdater.TAG, "Error no url or wrong format");
1331
- CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1332
- "Error no url or wrong format",
1333
- current.getVersionName(),
1334
- current,
1335
- true
1336
- );
1337
- return;
1338
- }
1339
-
1340
- if (
1341
- latestVersionName != null &&
1342
- !latestVersionName.isEmpty() &&
1343
- !current.getVersionName().equals(latestVersionName)
1344
- ) {
1345
- final BundleInfo latest =
1346
- CapacitorUpdaterPlugin.this.implementation.getBundleInfoByName(
1347
- latestVersionName
1348
- );
1349
- if (latest != null) {
1350
- final JSObject ret = new JSObject();
1351
- ret.put("bundle", latest.toJSON());
1352
- if (latest.isErrorStatus()) {
1353
- Log.e(
1354
- CapacitorUpdater.TAG,
1355
- "Latest bundle already exists, and is in error state. Aborting update."
1356
- );
1357
- CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1358
- "Latest bundle already exists, and is in error state. Aborting update.",
1359
- latestVersionName,
1360
- current,
1361
- true
1362
- );
1363
- return;
1364
- }
1365
- if (latest.isDownloaded()) {
1366
- Log.i(
1367
- CapacitorUpdater.TAG,
1368
- "Latest bundle already exists and download is NOT required. " +
1369
- messageUpdate
1370
- );
1371
- if (
1372
- CapacitorUpdaterPlugin.this.implementation.directUpdate
1373
- ) {
1374
- CapacitorUpdaterPlugin.this.implementation.set(latest);
1375
- CapacitorUpdaterPlugin.this._reload();
1376
- CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1377
- "Update installed",
1378
- latestVersionName,
1379
- latest,
1380
- false
1381
- );
1382
- } else {
1383
- CapacitorUpdaterPlugin.this.notifyListeners(
1384
- "updateAvailable",
1385
- ret
1386
- );
1387
- CapacitorUpdaterPlugin.this.implementation.setNextBundle(
1388
- latest.getId()
1389
- );
1390
- CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1391
- "update downloaded, will install next background",
1392
- latestVersionName,
1393
- latest,
1394
- false
1395
- );
1396
- }
1397
- return;
1398
- }
1399
- if (latest.isDeleted()) {
1400
- Log.i(
1401
- CapacitorUpdater.TAG,
1402
- "Latest bundle already exists and will be deleted, download will overwrite it."
1403
- );
1404
- try {
1405
- final Boolean deleted =
1406
- CapacitorUpdaterPlugin.this.implementation.delete(
1407
- latest.getId(),
1408
- true
1409
- );
1410
- if (deleted) {
1411
- Log.i(
1412
- CapacitorUpdater.TAG,
1413
- "Failed bundle deleted: " + latest.getVersionName()
1414
- );
1415
- }
1416
- } catch (final IOException e) {
1417
- Log.e(
1418
- CapacitorUpdater.TAG,
1419
- "Failed to delete failed bundle: " +
1420
- latest.getVersionName(),
1421
- e
1422
- );
1423
- }
1424
- }
1323
+ }
1324
+ if (backgroundValue != null) {
1325
+ taskRunning = true;
1326
+ final Long timeout = Long.parseLong(backgroundValue);
1327
+ if (backgroundTask != null) {
1328
+ backgroundTask.interrupt();
1425
1329
  }
1426
- startNewThread(() -> {
1427
- try {
1428
- Log.i(
1429
- CapacitorUpdater.TAG,
1430
- "New bundle: " +
1431
- latestVersionName +
1432
- " found. Current is: " +
1433
- current.getVersionName() +
1434
- ". " +
1435
- messageUpdate
1436
- );
1437
-
1438
- final String url = res.getString("url");
1439
- final String sessionKey = res.has("sessionKey")
1440
- ? res.getString("sessionKey")
1441
- : "";
1442
- final String checksum = res.has("checksum")
1443
- ? res.getString("checksum")
1444
- : "";
1445
-
1446
- if (res.has("manifest")) {
1447
- // Handle manifest-based download
1448
- JSONArray manifest = res.getJSONArray("manifest");
1449
- CapacitorUpdaterPlugin.this.implementation.downloadBackground(
1450
- url,
1451
- latestVersionName,
1452
- sessionKey,
1453
- checksum,
1454
- manifest
1455
- );
1456
- } else {
1457
- // Handle single file download (existing code)
1458
- CapacitorUpdaterPlugin.this.implementation.downloadBackground(
1459
- url,
1460
- latestVersionName,
1461
- sessionKey,
1462
- checksum,
1463
- null
1464
- );
1465
- }
1466
- } catch (final Exception e) {
1467
- Log.e(CapacitorUpdater.TAG, "error downloading file", e);
1468
- CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1469
- "Error downloading file",
1470
- latestVersionName,
1471
- CapacitorUpdaterPlugin.this.implementation.getCurrentBundle(),
1472
- true
1473
- );
1474
- }
1475
- });
1476
- } else {
1477
- Log.i(
1478
- CapacitorUpdater.TAG,
1479
- "No need to update, " +
1480
- current.getId() +
1481
- " is the latest bundle."
1482
- );
1483
- CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1484
- "No need to update",
1485
- latestVersionName,
1486
- current,
1487
- false
1488
- );
1489
- }
1490
- } catch (final JSONException e) {
1491
- Log.e(CapacitorUpdater.TAG, "error parsing JSON", e);
1492
- CapacitorUpdaterPlugin.this.endBackGroundTaskWithNotif(
1493
- "Error parsing JSON",
1494
- current.getVersionName(),
1495
- current,
1496
- true
1330
+ backgroundTask = startNewThread(
1331
+ () -> {
1332
+ taskRunning = false;
1333
+ _checkCancelDelay(false);
1334
+ installNext();
1335
+ },
1336
+ timeout
1497
1337
  );
1338
+ } else {
1339
+ this._checkCancelDelay(false);
1340
+ this.installNext();
1498
1341
  }
1499
- }
1500
- );
1501
- });
1502
- }
1503
-
1504
- private void installNext() {
1505
- try {
1506
- Gson gson = new Gson();
1507
- String delayUpdatePreferences = prefs.getString(
1508
- DELAY_CONDITION_PREFERENCES,
1509
- "[]"
1510
- );
1511
- Type type = new TypeToken<ArrayList<DelayCondition>>() {}.getType();
1512
- ArrayList<DelayCondition> delayConditionList = gson.fromJson(
1513
- delayUpdatePreferences,
1514
- type
1515
- );
1516
- if (delayConditionList != null && !delayConditionList.isEmpty()) {
1517
- Log.i(
1518
- CapacitorUpdater.TAG,
1519
- "Update delayed until delay conditions met"
1520
- );
1521
- return;
1522
- }
1523
- final BundleInfo current = this.implementation.getCurrentBundle();
1524
- final BundleInfo next = this.implementation.getNextBundle();
1525
-
1526
- if (
1527
- next != null &&
1528
- !next.isErrorStatus() &&
1529
- !next.getId().equals(current.getId())
1530
- ) {
1531
- // There is a next bundle waiting for activation
1532
- Log.d(CapacitorUpdater.TAG, "Next bundle is: " + next.getVersionName());
1533
- if (this.implementation.set(next) && this._reload()) {
1534
- Log.i(
1535
- CapacitorUpdater.TAG,
1536
- "Updated to bundle: " + next.getVersionName()
1537
- );
1538
- this.implementation.setNextBundle(null);
1539
- } else {
1540
- Log.e(
1541
- CapacitorUpdater.TAG,
1542
- "Update to bundle: " + next.getVersionName() + " Failed!"
1543
- );
1544
- }
1545
- }
1546
- } catch (final Exception e) {
1547
- Log.e(CapacitorUpdater.TAG, "Error during onActivityStopped", e);
1548
- }
1549
- }
1550
-
1551
- private void checkRevert() {
1552
- // Automatically roll back to fallback version if notifyAppReady has not been called yet
1553
- final BundleInfo current = this.implementation.getCurrentBundle();
1554
-
1555
- if (current.isBuiltin()) {
1556
- Log.i(
1557
- CapacitorUpdater.TAG,
1558
- "Built-in bundle is active. We skip the check for notifyAppReady."
1559
- );
1560
- return;
1342
+ } catch (final Exception e) {
1343
+ Log.e(CapacitorUpdater.TAG, "Error during onActivityStopped", e);
1344
+ }
1561
1345
  }
1562
- Log.d(CapacitorUpdater.TAG, "Current bundle is: " + current);
1563
-
1564
- if (BundleStatus.SUCCESS != current.getStatus()) {
1565
- Log.e(
1566
- CapacitorUpdater.TAG,
1567
- "notifyAppReady was not called, roll back current bundle: " +
1568
- current.getId()
1569
- );
1570
- Log.i(
1571
- CapacitorUpdater.TAG,
1572
- "Did you forget to call 'notifyAppReady()' in your Capacitor App code?"
1573
- );
1574
- final JSObject ret = new JSObject();
1575
- ret.put("bundle", current.toJSON());
1576
- this.notifyListeners("updateFailed", ret);
1577
- this.implementation.sendStats("update_fail", current.getVersionName());
1578
- this.implementation.setError(current);
1579
- this._reset(true);
1580
- if (
1581
- CapacitorUpdaterPlugin.this.autoDeleteFailed && !current.isBuiltin()
1582
- ) {
1583
- Log.i(
1584
- CapacitorUpdater.TAG,
1585
- "Deleting failing bundle: " + current.getVersionName()
1586
- );
1346
+
1347
+ private boolean isMainActivity() {
1348
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
1349
+ return false;
1350
+ }
1587
1351
  try {
1588
- final Boolean res =
1589
- this.implementation.delete(current.getId(), false);
1590
- if (res) {
1591
- Log.i(
1592
- CapacitorUpdater.TAG,
1593
- "Failed bundle deleted: " + current.getVersionName()
1594
- );
1595
- }
1596
- } catch (final IOException e) {
1597
- Log.e(
1598
- CapacitorUpdater.TAG,
1599
- "Failed to delete failed bundle: " + current.getVersionName(),
1600
- e
1601
- );
1602
- }
1603
- }
1604
- } else {
1605
- Log.i(
1606
- CapacitorUpdater.TAG,
1607
- "notifyAppReady was called. This is fine: " + current.getId()
1608
- );
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
+ }
1609
1368
  }
1610
- }
1611
1369
 
1612
- private class DeferredNotifyAppReadyCheck implements Runnable {
1370
+ private void appKilled() {
1371
+ Log.d(CapacitorUpdater.TAG, "onActivityDestroyed: all activity destroyed");
1372
+ this._checkCancelDelay(true);
1373
+ }
1613
1374
 
1614
1375
  @Override
1615
- public void run() {
1616
- try {
1617
- Log.i(
1618
- CapacitorUpdater.TAG,
1619
- "Wait for " +
1620
- CapacitorUpdaterPlugin.this.appReadyTimeout +
1621
- "ms, then check for notifyAppReady"
1622
- );
1623
- Thread.sleep(CapacitorUpdaterPlugin.this.appReadyTimeout);
1624
- CapacitorUpdaterPlugin.this.checkRevert();
1625
- CapacitorUpdaterPlugin.this.appReadyCheck = null;
1626
- } catch (final InterruptedException e) {
1627
- Log.i(
1628
- CapacitorUpdater.TAG,
1629
- DeferredNotifyAppReadyCheck.class.getName() + " was interrupted."
1630
- );
1631
- }
1632
- }
1633
- }
1634
-
1635
- public void appMovedToForeground() {
1636
- final BundleInfo current =
1637
- CapacitorUpdaterPlugin.this.implementation.getCurrentBundle();
1638
- CapacitorUpdaterPlugin.this.implementation.sendStats(
1639
- "app_moved_to_foreground",
1640
- current.getVersionName()
1641
- );
1642
- this._checkCancelDelay(false);
1643
- if (
1644
- CapacitorUpdaterPlugin.this._isAutoUpdateEnabled() &&
1645
- (this.backgroundDownloadTask == null ||
1646
- !this.backgroundDownloadTask.isAlive())
1647
- ) {
1648
- this.backgroundDownloadTask = this.backgroundDownload();
1649
- } else {
1650
- Log.i(CapacitorUpdater.TAG, "Auto update is disabled");
1651
- this.sendReadyToJs(current, "disabled");
1652
- }
1653
- this.checkAppReady();
1654
- }
1655
-
1656
- public void appMovedToBackground() {
1657
- final BundleInfo current =
1658
- CapacitorUpdaterPlugin.this.implementation.getCurrentBundle();
1659
- CapacitorUpdaterPlugin.this.implementation.sendStats(
1660
- "app_moved_to_background",
1661
- current.getVersionName()
1662
- );
1663
- Log.i(CapacitorUpdater.TAG, "Checking for pending update");
1664
- try {
1665
- Gson gson = new Gson();
1666
- String delayUpdatePreferences = prefs.getString(
1667
- DELAY_CONDITION_PREFERENCES,
1668
- "[]"
1669
- );
1670
- Type type = new TypeToken<ArrayList<DelayCondition>>() {}.getType();
1671
- ArrayList<DelayCondition> delayConditionList = gson.fromJson(
1672
- delayUpdatePreferences,
1673
- type
1674
- );
1675
- String backgroundValue = null;
1676
- for (DelayCondition delayCondition : delayConditionList) {
1677
- if (delayCondition.getKind().toString().equals("background")) {
1678
- String value = delayCondition.getValue();
1679
- backgroundValue = (value != null && !value.isEmpty()) ? value : "0";
1680
- }
1681
- }
1682
- if (backgroundValue != null) {
1683
- taskRunning = true;
1684
- final Long timeout = Long.parseLong(backgroundValue);
1685
- if (backgroundTask != null) {
1686
- backgroundTask.interrupt();
1687
- }
1688
- backgroundTask = startNewThread(
1689
- () -> {
1690
- taskRunning = false;
1691
- _checkCancelDelay(false);
1692
- installNext();
1693
- },
1694
- timeout
1695
- );
1696
- } else {
1697
- this._checkCancelDelay(false);
1698
- this.installNext();
1699
- }
1700
- } catch (final Exception e) {
1701
- 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;
1702
1382
  }
1703
- }
1704
1383
 
1705
- private boolean isMainActivity() {
1706
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
1707
- return false;
1708
- }
1709
- try {
1710
- Context mContext = this.getContext();
1711
- ActivityManager activityManager =
1712
- (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
1713
- List<ActivityManager.AppTask> runningTasks =
1714
- activityManager.getAppTasks();
1715
- if (runningTasks.isEmpty()) {
1716
- return false;
1717
- }
1718
- ActivityManager.RecentTaskInfo runningTask = runningTasks
1719
- .get(0)
1720
- .getTaskInfo();
1721
- String className = Objects.requireNonNull(
1722
- runningTask.baseIntent.getComponent()
1723
- ).getClassName();
1724
- if (runningTask.topActivity == null) {
1725
- return false;
1726
- }
1727
- String runningActivity = runningTask.topActivity.getClassName();
1728
- return className.equals(runningActivity);
1729
- } catch (NullPointerException e) {
1730
- return false;
1384
+ @Override
1385
+ public void handleOnStop() {
1386
+ isPreviousMainActivity = isMainActivity();
1387
+ if (isPreviousMainActivity) {
1388
+ this.appMovedToBackground();
1389
+ }
1731
1390
  }
1732
- }
1733
1391
 
1734
- private void appKilled() {
1735
- Log.d(CapacitorUpdater.TAG, "onActivityDestroyed: all activity destroyed");
1736
- this._checkCancelDelay(true);
1737
- }
1738
-
1739
- @Override
1740
- public void handleOnStart() {
1741
- if (isPreviousMainActivity) {
1742
- this.appMovedToForeground();
1743
- }
1744
- Log.i(
1745
- CapacitorUpdater.TAG,
1746
- "onActivityStarted " + getActivity().getClass().getName()
1747
- );
1748
- isPreviousMainActivity = true;
1749
- }
1750
-
1751
- @Override
1752
- public void handleOnStop() {
1753
- isPreviousMainActivity = isMainActivity();
1754
- if (isPreviousMainActivity) {
1755
- this.appMovedToBackground();
1392
+ @Override
1393
+ public void handleOnResume() {
1394
+ if (backgroundTask != null && taskRunning) {
1395
+ backgroundTask.interrupt();
1396
+ }
1397
+ this.implementation.activity = getActivity();
1756
1398
  }
1757
- }
1758
1399
 
1759
- @Override
1760
- public void handleOnResume() {
1761
- if (backgroundTask != null && taskRunning) {
1762
- backgroundTask.interrupt();
1400
+ @Override
1401
+ public void handleOnPause() {
1402
+ this.implementation.activity = getActivity();
1763
1403
  }
1764
- this.implementation.activity = getActivity();
1765
- }
1766
-
1767
- @Override
1768
- public void handleOnPause() {
1769
- this.implementation.activity = getActivity();
1770
- }
1771
-
1772
- @Override
1773
- public void handleOnDestroy() {
1774
- Log.i(
1775
- CapacitorUpdater.TAG,
1776
- "onActivityDestroyed " + getActivity().getClass().getName()
1777
- );
1778
- this.implementation.activity = getActivity();
1779
- counterActivityCreate--;
1780
- if (counterActivityCreate == 0) {
1781
- 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
+ }
1782
1413
  }
1783
- }
1784
1414
  }