@capgo/capacitor-updater 8.51.14 → 8.51.15

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.
@@ -1,6 +1,7 @@
1
1
  package ee.forgr.capacitor_updater;
2
2
 
3
3
  import android.content.Context;
4
+ import android.os.Build;
4
5
  import androidx.work.BackoffPolicy;
5
6
  import androidx.work.Configuration;
6
7
  import androidx.work.Constraints;
@@ -8,9 +9,18 @@ import androidx.work.Data;
8
9
  import androidx.work.ExistingWorkPolicy;
9
10
  import androidx.work.NetworkType;
10
11
  import androidx.work.OneTimeWorkRequest;
12
+ import androidx.work.OutOfQuotaPolicy;
13
+ import androidx.work.WorkInfo;
11
14
  import androidx.work.WorkManager;
12
15
  import androidx.work.WorkRequest;
16
+ import java.util.HashSet;
17
+ import java.util.List;
18
+ import java.util.Set;
19
+ import java.util.concurrent.ExecutorService;
20
+ import java.util.concurrent.Executors;
21
+ import java.util.concurrent.Future;
13
22
  import java.util.concurrent.TimeUnit;
23
+ import java.util.concurrent.TimeoutException;
14
24
 
15
25
  public class DownloadWorkerManager {
16
26
 
@@ -21,6 +31,7 @@ public class DownloadWorkerManager {
21
31
  }
22
32
 
23
33
  private static volatile boolean isInitialized = false;
34
+ private static final ExecutorService cancelExecutor = Executors.newSingleThreadExecutor();
24
35
 
25
36
  private static synchronized void initializeIfNeeded(Context context) {
26
37
  if (!isInitialized) {
@@ -120,6 +131,11 @@ public class DownloadWorkerManager {
120
131
  .addTag(id)
121
132
  .addTag(version)
122
133
  .addTag("capacitor_updater_download");
134
+ // Android 12+ expedited jobs skip the WorkManager delay without a
135
+ // foreground service. Older APIs require getForegroundInfo().
136
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
137
+ workRequestBuilder.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST);
138
+ }
123
139
 
124
140
  // More aggressive retry policy for emulators
125
141
  if (isEmulator) {
@@ -142,17 +158,101 @@ public class DownloadWorkerManager {
142
158
 
143
159
  public static void cancelVersionDownload(Context context, String version) {
144
160
  initializeIfNeeded(context.getApplicationContext());
145
- WorkManager.getInstance(context).cancelAllWorkByTag(version);
161
+ cancelExecutor.execute(() -> cancelVersionDownloadInternal(context, version, false));
162
+ }
163
+
164
+ public static boolean cancelVersionDownloadAndAwait(Context context, String version) {
165
+ initializeIfNeeded(context.getApplicationContext());
166
+ Future<?> future = cancelExecutor.submit(() -> cancelVersionDownloadInternal(context, version, true));
167
+ try {
168
+ future.get(10, TimeUnit.SECONDS);
169
+ return true;
170
+ } catch (TimeoutException e) {
171
+ future.cancel(true);
172
+ logger.error("Timed out awaiting version download cancel");
173
+ return false;
174
+ } catch (Exception e) {
175
+ logger.error("Error awaiting version download cancel: " + e.getMessage());
176
+ return false;
177
+ }
178
+ }
179
+
180
+ private static Set<String> collectManifestIdsForVersion(WorkManager workManager, String version) {
181
+ Set<String> downloadIds = new HashSet<>();
182
+ try {
183
+ List<WorkInfo> workInfos = workManager.getWorkInfosByTag(version).get();
184
+ for (WorkInfo workInfo : workInfos) {
185
+ for (String tag : workInfo.getTags()) {
186
+ if (!"capacitor_updater_download".equals(tag) && !version.equals(tag)) {
187
+ downloadIds.add(tag);
188
+ }
189
+ }
190
+ }
191
+ } catch (InterruptedException e) {
192
+ Thread.currentThread().interrupt();
193
+ throw new IllegalStateException("Interrupted while collecting manifest ids before version cancel", e);
194
+ } catch (Exception e) {
195
+ logger.error("Error collecting manifest ids before version cancel: " + e.getMessage());
196
+ }
197
+ return downloadIds;
198
+ }
199
+
200
+ private static void clearManifestIds(Set<String> downloadIds) {
201
+ for (String downloadId : downloadIds) {
202
+ DataManager.getInstance().clearManifest(downloadId);
203
+ }
204
+ }
205
+
206
+ private static void cancelVersionDownloadInternal(Context context, String version, boolean awaitFinished) {
207
+ if (Thread.currentThread().isInterrupted()) {
208
+ return;
209
+ }
210
+ WorkManager workManager = WorkManager.getInstance(context);
211
+ Set<String> downloadIds = collectManifestIdsForVersion(workManager, version);
212
+ workManager.cancelAllWorkByTag(version);
213
+ clearManifestIds(downloadIds);
214
+ if (awaitFinished) {
215
+ awaitVersionWorkFinished(workManager, version);
216
+ }
217
+ }
218
+
219
+ private static void awaitVersionWorkFinished(WorkManager workManager, String version) {
220
+ for (int i = 0; i < 100; i++) {
221
+ if (Thread.currentThread().isInterrupted()) {
222
+ throw new IllegalStateException("Interrupted while waiting for version download cancel");
223
+ }
224
+ try {
225
+ boolean anyActive = workManager
226
+ .getWorkInfosByTag(version)
227
+ .get()
228
+ .stream()
229
+ .anyMatch((workInfo) -> !workInfo.getState().isFinished());
230
+ if (!anyActive) {
231
+ return;
232
+ }
233
+ Thread.sleep(100);
234
+ } catch (InterruptedException e) {
235
+ Thread.currentThread().interrupt();
236
+ throw new IllegalStateException("Interrupted while waiting for download cancel", e);
237
+ } catch (Exception e) {
238
+ throw new IllegalStateException("Error waiting for download cancel: " + e.getMessage(), e);
239
+ }
240
+ }
241
+ throw new IllegalStateException("Timed out waiting for version download cancel: " + version);
146
242
  }
147
243
 
148
244
  public static void cancelBundleDownload(Context context, String id, String version) {
149
245
  String uniqueWorkName = "bundle_" + id + "_" + version;
150
246
  initializeIfNeeded(context.getApplicationContext());
151
- WorkManager.getInstance(context).cancelUniqueWork(uniqueWorkName);
247
+ WorkManager workManager = WorkManager.getInstance(context);
248
+ workManager.cancelUniqueWork(uniqueWorkName);
249
+ DataManager.getInstance().clearManifest(id);
152
250
  }
153
251
 
154
252
  public static void cancelAllDownloads(Context context) {
155
253
  initializeIfNeeded(context.getApplicationContext());
156
- WorkManager.getInstance(context).cancelAllWorkByTag("capacitor_updater_download");
254
+ WorkManager workManager = WorkManager.getInstance(context);
255
+ workManager.cancelAllWorkByTag("capacitor_updater_download");
256
+ DataManager.getInstance().clearAllManifests();
157
257
  }
158
258
  }
package/dist/docs.json CHANGED
@@ -4583,7 +4583,7 @@
4583
4583
  "name": "example"
4584
4584
  }
4585
4585
  ],
4586
- "docs": "Configure the number of seconds the native plugin should wait before considering API timeout.\n\nOnly available for Android and iOS.",
4586
+ "docs": "Configure the number of seconds the native plugin should wait before considering an HTTP timeout.\nApplies to update checks and file downloads. On Android these are idle connect/read/write\ntimeouts and do not cap total download time; on iOS the request timeout also bounds the\ntotal download duration.\n\nOnly available for Android and iOS.",
4587
4587
  "complexTypes": [],
4588
4588
  "type": "number | undefined"
4589
4589
  },
@@ -15,7 +15,10 @@ declare module '@capacitor/cli' {
15
15
  */
16
16
  appReadyTimeout?: number;
17
17
  /**
18
- * Configure the number of seconds the native plugin should wait before considering API timeout.
18
+ * Configure the number of seconds the native plugin should wait before considering an HTTP timeout.
19
+ * Applies to update checks and file downloads. On Android these are idle connect/read/write
20
+ * timeouts and do not cap total download time; on iOS the request timeout also bounds the
21
+ * total download duration.
19
22
  *
20
23
  * Only available for Android and iOS.
21
24
  *