@capgo/capacitor-updater 6.3.18 → 6.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -26
- package/android/build.gradle +2 -1
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdater.java +227 -172
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +25 -12
- package/android/src/main/java/ee/forgr/capacitor_updater/DataManager.java +28 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadService.java +206 -133
- package/dist/docs.json +5 -5
- package/dist/esm/definitions.d.ts +7 -4
- package/dist/esm/definitions.js.map +1 -1
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js.map +1 -1
- package/ios/Plugin/CapacitorUpdater.swift +31 -15
- package/ios/Plugin/CapacitorUpdaterPlugin.swift +13 -11
- package/package.json +13 -13
|
@@ -14,7 +14,6 @@ import android.content.pm.PackageInfo;
|
|
|
14
14
|
import android.content.pm.PackageManager;
|
|
15
15
|
import android.os.Build;
|
|
16
16
|
import android.util.Log;
|
|
17
|
-
import com.android.volley.toolbox.Volley;
|
|
18
17
|
import com.getcapacitor.CapConfig;
|
|
19
18
|
import com.getcapacitor.JSArray;
|
|
20
19
|
import com.getcapacitor.JSObject;
|
|
@@ -32,6 +31,7 @@ import java.net.MalformedURLException;
|
|
|
32
31
|
import java.net.URL;
|
|
33
32
|
import java.text.SimpleDateFormat;
|
|
34
33
|
import java.util.ArrayList;
|
|
34
|
+
import java.util.Arrays;
|
|
35
35
|
import java.util.Date;
|
|
36
36
|
import java.util.Iterator;
|
|
37
37
|
import java.util.List;
|
|
@@ -42,6 +42,8 @@ import java.util.UUID;
|
|
|
42
42
|
import java.util.concurrent.Phaser;
|
|
43
43
|
import java.util.concurrent.TimeUnit;
|
|
44
44
|
import java.util.concurrent.TimeoutException;
|
|
45
|
+
import okhttp3.OkHttpClient;
|
|
46
|
+
import okhttp3.Protocol;
|
|
45
47
|
import org.json.JSONArray;
|
|
46
48
|
import org.json.JSONException;
|
|
47
49
|
|
|
@@ -49,12 +51,13 @@ import org.json.JSONException;
|
|
|
49
51
|
public class CapacitorUpdaterPlugin extends Plugin {
|
|
50
52
|
|
|
51
53
|
private static final String updateUrlDefault =
|
|
52
|
-
"https://
|
|
53
|
-
private static final String statsUrlDefault =
|
|
54
|
+
"https://plugin.capgo.app/updates";
|
|
55
|
+
private static final String statsUrlDefault =
|
|
56
|
+
"https://plugin.capgo.app/stats";
|
|
54
57
|
private static final String channelUrlDefault =
|
|
55
|
-
"https://
|
|
58
|
+
"https://plugin.capgo.app/channel_self";
|
|
56
59
|
|
|
57
|
-
private final String PLUGIN_VERSION = "6.3
|
|
60
|
+
private final String PLUGIN_VERSION = "6.6.3";
|
|
58
61
|
private static final String DELAY_CONDITION_PREFERENCES = "";
|
|
59
62
|
|
|
60
63
|
private SharedPreferences.Editor editor;
|
|
@@ -135,9 +138,12 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
135
138
|
.getString("version", pInfo.versionName);
|
|
136
139
|
this.implementation.PLUGIN_VERSION = this.PLUGIN_VERSION;
|
|
137
140
|
this.implementation.versionCode = Integer.toString(pInfo.versionCode);
|
|
138
|
-
this.implementation.
|
|
139
|
-
|
|
140
|
-
|
|
141
|
+
this.implementation.client = new OkHttpClient.Builder()
|
|
142
|
+
.protocols(Arrays.asList(Protocol.HTTP_2, Protocol.HTTP_1_1))
|
|
143
|
+
.connectTimeout(this.implementation.timeout, TimeUnit.MILLISECONDS)
|
|
144
|
+
.readTimeout(this.implementation.timeout, TimeUnit.MILLISECONDS)
|
|
145
|
+
.writeTimeout(this.implementation.timeout, TimeUnit.MILLISECONDS)
|
|
146
|
+
.build();
|
|
141
147
|
|
|
142
148
|
this.implementation.directUpdate = this.getConfig()
|
|
143
149
|
.getBoolean("directUpdate", false);
|
|
@@ -223,9 +229,8 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
223
229
|
this.updateUrl = this.getConfig().getString("updateUrl", updateUrlDefault);
|
|
224
230
|
this.autoUpdate = this.getConfig().getBoolean("autoUpdate", true);
|
|
225
231
|
this.appReadyTimeout = this.getConfig().getInt("appReadyTimeout", 10000);
|
|
226
|
-
this.implementation.timeout =
|
|
227
|
-
.getInt("responseTimeout", 20) *
|
|
228
|
-
1000;
|
|
232
|
+
this.implementation.timeout =
|
|
233
|
+
this.getConfig().getInt("responseTimeout", 20) * 1000;
|
|
229
234
|
boolean resetWhenUpdate =
|
|
230
235
|
this.getConfig().getBoolean("resetWhenUpdate", true);
|
|
231
236
|
|
|
@@ -1106,7 +1111,15 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
1106
1111
|
Boolean error
|
|
1107
1112
|
) {
|
|
1108
1113
|
if (error) {
|
|
1109
|
-
Log.i(
|
|
1114
|
+
Log.i(
|
|
1115
|
+
CapacitorUpdater.TAG,
|
|
1116
|
+
"endBackGroundTaskWithNotif error: " +
|
|
1117
|
+
error +
|
|
1118
|
+
" current: " +
|
|
1119
|
+
current.getVersionName() +
|
|
1120
|
+
"latestVersionName: " +
|
|
1121
|
+
latestVersionName
|
|
1122
|
+
);
|
|
1110
1123
|
this.implementation.sendStats("download_fail", current.getVersionName());
|
|
1111
1124
|
final JSObject ret = new JSObject();
|
|
1112
1125
|
ret.put("version", latestVersionName);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
package ee.forgr.capacitor_updater;
|
|
2
|
+
|
|
3
|
+
import org.json.JSONArray;
|
|
4
|
+
|
|
5
|
+
public class DataManager {
|
|
6
|
+
|
|
7
|
+
private static DataManager instance;
|
|
8
|
+
private JSONArray currentManifest;
|
|
9
|
+
|
|
10
|
+
private DataManager() {}
|
|
11
|
+
|
|
12
|
+
public static synchronized DataManager getInstance() {
|
|
13
|
+
if (instance == null) {
|
|
14
|
+
instance = new DataManager();
|
|
15
|
+
}
|
|
16
|
+
return instance;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public void setManifest(JSONArray manifest) {
|
|
20
|
+
this.currentManifest = manifest;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public JSONArray getAndClearManifest() {
|
|
24
|
+
JSONArray manifest = this.currentManifest;
|
|
25
|
+
this.currentManifest = null;
|
|
26
|
+
return manifest;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
package ee.forgr.capacitor_updater;
|
|
7
7
|
|
|
8
8
|
import android.app.IntentService;
|
|
9
|
+
import android.app.Notification;
|
|
10
|
+
import android.app.NotificationChannel;
|
|
11
|
+
import android.app.NotificationManager;
|
|
9
12
|
import android.content.Intent;
|
|
13
|
+
import android.os.Build;
|
|
14
|
+
import android.os.Handler;
|
|
15
|
+
import android.os.Looper;
|
|
10
16
|
import android.util.Log;
|
|
11
|
-
import com.android.volley.DefaultRetryPolicy;
|
|
12
|
-
import com.android.volley.NetworkResponse;
|
|
13
|
-
import com.android.volley.Request;
|
|
14
|
-
import com.android.volley.Response;
|
|
15
|
-
import com.android.volley.toolbox.HttpHeaderParser;
|
|
16
|
-
import com.android.volley.toolbox.Volley;
|
|
17
17
|
import java.io.*;
|
|
18
18
|
import java.io.FileInputStream;
|
|
19
19
|
import java.net.HttpURLConnection;
|
|
@@ -21,19 +21,27 @@ import java.net.URL;
|
|
|
21
21
|
import java.nio.channels.FileChannel;
|
|
22
22
|
import java.security.MessageDigest;
|
|
23
23
|
import java.util.ArrayList;
|
|
24
|
+
import java.util.Arrays;
|
|
24
25
|
import java.util.List;
|
|
25
26
|
import java.util.concurrent.CompletableFuture;
|
|
26
27
|
import java.util.concurrent.ExecutorService;
|
|
27
28
|
import java.util.concurrent.Executors;
|
|
28
29
|
import java.util.concurrent.Future;
|
|
30
|
+
import java.util.concurrent.TimeUnit;
|
|
29
31
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
30
32
|
import java.util.concurrent.atomic.AtomicLong;
|
|
33
|
+
import okhttp3.OkHttpClient;
|
|
34
|
+
import okhttp3.Protocol;
|
|
35
|
+
import okhttp3.Request;
|
|
36
|
+
import okhttp3.Response;
|
|
37
|
+
import okhttp3.ResponseBody;
|
|
31
38
|
import org.brotli.dec.BrotliInputStream;
|
|
32
39
|
import org.json.JSONArray;
|
|
33
40
|
import org.json.JSONObject;
|
|
34
41
|
|
|
35
42
|
public class DownloadService extends IntentService {
|
|
36
43
|
|
|
44
|
+
public static final String TAG = "Capacitor-updater";
|
|
37
45
|
public static final String URL = "URL";
|
|
38
46
|
public static final String ID = "id";
|
|
39
47
|
public static final String PERCENT = "percent";
|
|
@@ -48,11 +56,59 @@ public class DownloadService extends IntentService {
|
|
|
48
56
|
public static final String IS_MANIFEST = "is_manifest";
|
|
49
57
|
public static final String MANIFEST = "manifest";
|
|
50
58
|
private static final String UPDATE_FILE = "update.dat";
|
|
59
|
+
private static final int NOTIFICATION_ID = 1;
|
|
60
|
+
private static final long NOTIFICATION_DELAY_MS = 4000; // 4 seconds
|
|
61
|
+
|
|
62
|
+
private final OkHttpClient client = new OkHttpClient.Builder()
|
|
63
|
+
.protocols(Arrays.asList(Protocol.HTTP_2, Protocol.HTTP_1_1))
|
|
64
|
+
.build();
|
|
65
|
+
private Handler handler = new Handler(Looper.getMainLooper());
|
|
66
|
+
private Runnable notificationRunnable;
|
|
67
|
+
private boolean isNotificationShown = false;
|
|
51
68
|
|
|
52
69
|
public DownloadService() {
|
|
53
70
|
super("Background DownloadService");
|
|
54
71
|
}
|
|
55
72
|
|
|
73
|
+
@Override
|
|
74
|
+
public void onCreate() {
|
|
75
|
+
super.onCreate();
|
|
76
|
+
notificationRunnable = () -> startForeground();
|
|
77
|
+
handler.postDelayed(notificationRunnable, NOTIFICATION_DELAY_MS);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@Override
|
|
81
|
+
public void onDestroy() {
|
|
82
|
+
super.onDestroy();
|
|
83
|
+
handler.removeCallbacks(notificationRunnable);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private void startForeground() {
|
|
87
|
+
isNotificationShown = true;
|
|
88
|
+
String channelId = createNotificationChannel();
|
|
89
|
+
Notification.Builder builder = new Notification.Builder(this, channelId)
|
|
90
|
+
.setContentTitle("Downloading Update")
|
|
91
|
+
.setContentText("Download in progress")
|
|
92
|
+
.setSmallIcon(android.R.drawable.stat_sys_download)
|
|
93
|
+
.setOngoing(true);
|
|
94
|
+
|
|
95
|
+
startForeground(NOTIFICATION_ID, builder.build());
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private String createNotificationChannel() {
|
|
99
|
+
String channelId = "capacitor_updater_channel";
|
|
100
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
101
|
+
NotificationChannel channel = new NotificationChannel(
|
|
102
|
+
channelId,
|
|
103
|
+
"Capacitor Updater Downloads",
|
|
104
|
+
NotificationManager.IMPORTANCE_LOW
|
|
105
|
+
);
|
|
106
|
+
NotificationManager manager = getSystemService(NotificationManager.class);
|
|
107
|
+
manager.createNotificationChannel(channel);
|
|
108
|
+
}
|
|
109
|
+
return channelId;
|
|
110
|
+
}
|
|
111
|
+
|
|
56
112
|
private int calcTotalPercent(long downloadedBytes, long contentLength) {
|
|
57
113
|
if (contentLength <= 0) {
|
|
58
114
|
return 0;
|
|
@@ -73,18 +129,32 @@ public class DownloadService extends IntentService {
|
|
|
73
129
|
String version = intent.getStringExtra(VERSION);
|
|
74
130
|
String sessionKey = intent.getStringExtra(SESSIONKEY);
|
|
75
131
|
String checksum = intent.getStringExtra(CHECKSUM);
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
Log.d("DownloadService", "onHandleIntent" +
|
|
79
|
-
if (
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
132
|
+
boolean isManifest = intent.getBooleanExtra(IS_MANIFEST, false);
|
|
133
|
+
|
|
134
|
+
Log.d(TAG + " DownloadService", "onHandleIntent isManifest: " + isManifest);
|
|
135
|
+
if (isManifest) {
|
|
136
|
+
JSONArray manifest = DataManager.getInstance().getAndClearManifest();
|
|
137
|
+
if (manifest != null) {
|
|
138
|
+
handleManifestDownload(
|
|
139
|
+
id,
|
|
140
|
+
documentsDir,
|
|
141
|
+
dest,
|
|
142
|
+
version,
|
|
143
|
+
sessionKey,
|
|
144
|
+
manifest.toString()
|
|
145
|
+
);
|
|
146
|
+
} else {
|
|
147
|
+
Log.e(TAG + " DownloadService", "Manifest is null");
|
|
148
|
+
publishResults(
|
|
149
|
+
"",
|
|
150
|
+
id,
|
|
151
|
+
version,
|
|
152
|
+
checksum,
|
|
153
|
+
sessionKey,
|
|
154
|
+
"Manifest is null",
|
|
155
|
+
false
|
|
156
|
+
);
|
|
157
|
+
}
|
|
88
158
|
} else {
|
|
89
159
|
handleSingleFileDownload(
|
|
90
160
|
url,
|
|
@@ -107,13 +177,18 @@ public class DownloadService extends IntentService {
|
|
|
107
177
|
String manifestString
|
|
108
178
|
) {
|
|
109
179
|
try {
|
|
110
|
-
Log.d("DownloadService", "handleManifestDownload");
|
|
180
|
+
Log.d(TAG + " DownloadService", "handleManifestDownload");
|
|
111
181
|
JSONArray manifest = new JSONArray(manifestString);
|
|
112
182
|
File destFolder = new File(documentsDir, dest);
|
|
113
183
|
File cacheFolder = new File(
|
|
114
184
|
getApplicationContext().getCacheDir(),
|
|
115
185
|
"capgo_downloads"
|
|
116
186
|
);
|
|
187
|
+
File builtinFolder = new File(
|
|
188
|
+
getApplicationContext().getFilesDir(),
|
|
189
|
+
"public"
|
|
190
|
+
);
|
|
191
|
+
|
|
117
192
|
// Ensure directories are created
|
|
118
193
|
if (!destFolder.exists() && !destFolder.mkdirs()) {
|
|
119
194
|
throw new IOException(
|
|
@@ -132,12 +207,9 @@ public class DownloadService extends IntentService {
|
|
|
132
207
|
final AtomicBoolean hasError = new AtomicBoolean(false);
|
|
133
208
|
|
|
134
209
|
// Use more threads for I/O-bound operations
|
|
135
|
-
int threadCount = Math.min(
|
|
136
|
-
Runtime.getRuntime().availableProcessors() * 2,
|
|
137
|
-
32
|
|
138
|
-
);
|
|
210
|
+
int threadCount = Math.min(64, Math.max(32, totalFiles));
|
|
139
211
|
ExecutorService executor = Executors.newFixedThreadPool(threadCount);
|
|
140
|
-
|
|
212
|
+
List<Future<?>> futures = new ArrayList<>();
|
|
141
213
|
|
|
142
214
|
for (int i = 0; i < totalFiles; i++) {
|
|
143
215
|
JSONObject entry = manifest.getJSONObject(i);
|
|
@@ -150,6 +222,7 @@ public class DownloadService extends IntentService {
|
|
|
150
222
|
cacheFolder,
|
|
151
223
|
fileHash + "_" + new File(fileName).getName()
|
|
152
224
|
);
|
|
225
|
+
File builtinFile = new File(builtinFolder, fileName);
|
|
153
226
|
|
|
154
227
|
// Ensure parent directories of the target file exist
|
|
155
228
|
if (
|
|
@@ -162,49 +235,60 @@ public class DownloadService extends IntentService {
|
|
|
162
235
|
);
|
|
163
236
|
}
|
|
164
237
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
downloadAndVerify(
|
|
184
|
-
downloadUrl,
|
|
185
|
-
targetFile,
|
|
186
|
-
cacheFile,
|
|
187
|
-
fileHash,
|
|
188
|
-
id
|
|
189
|
-
);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
long completed = completedFiles.incrementAndGet();
|
|
193
|
-
int percent = calcTotalPercent(completed, totalFiles);
|
|
194
|
-
notifyDownload(id, percent);
|
|
195
|
-
} catch (Exception e) {
|
|
196
|
-
Log.e("DownloadService", "Error processing file: " + fileName, e);
|
|
197
|
-
hasError.set(true);
|
|
238
|
+
Future<?> future = executor.submit(() -> {
|
|
239
|
+
try {
|
|
240
|
+
if (builtinFile.exists() && verifyChecksum(builtinFile, fileHash)) {
|
|
241
|
+
copyFile(builtinFile, targetFile);
|
|
242
|
+
Log.d(TAG + " DownloadService", "using builtin file " + fileName);
|
|
243
|
+
} else if (
|
|
244
|
+
cacheFile.exists() && verifyChecksum(cacheFile, fileHash)
|
|
245
|
+
) {
|
|
246
|
+
copyFile(cacheFile, targetFile);
|
|
247
|
+
Log.d(TAG + " DownloadService", "already cached " + fileName);
|
|
248
|
+
} else {
|
|
249
|
+
downloadAndVerify(
|
|
250
|
+
downloadUrl,
|
|
251
|
+
targetFile,
|
|
252
|
+
cacheFile,
|
|
253
|
+
fileHash,
|
|
254
|
+
id
|
|
255
|
+
);
|
|
198
256
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
257
|
+
|
|
258
|
+
long completed = completedFiles.incrementAndGet();
|
|
259
|
+
int percent = calcTotalPercent(completed, totalFiles);
|
|
260
|
+
notifyDownload(id, percent);
|
|
261
|
+
} catch (Exception e) {
|
|
262
|
+
Log.e(
|
|
263
|
+
TAG + " DownloadService",
|
|
264
|
+
"Error processing file: " + fileName,
|
|
265
|
+
e
|
|
266
|
+
);
|
|
267
|
+
hasError.set(true);
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
futures.add(future);
|
|
202
271
|
}
|
|
203
272
|
|
|
204
273
|
// Wait for all downloads to complete
|
|
205
|
-
|
|
274
|
+
for (Future<?> future : futures) {
|
|
275
|
+
try {
|
|
276
|
+
future.get();
|
|
277
|
+
} catch (Exception e) {
|
|
278
|
+
Log.e(TAG + " DownloadService", "Error waiting for download", e);
|
|
279
|
+
hasError.set(true);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
206
282
|
|
|
207
283
|
executor.shutdown();
|
|
284
|
+
try {
|
|
285
|
+
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
|
|
286
|
+
executor.shutdownNow();
|
|
287
|
+
}
|
|
288
|
+
} catch (InterruptedException e) {
|
|
289
|
+
executor.shutdownNow();
|
|
290
|
+
Thread.currentThread().interrupt();
|
|
291
|
+
}
|
|
208
292
|
|
|
209
293
|
if (hasError.get()) {
|
|
210
294
|
throw new IOException("One or more files failed to download");
|
|
@@ -212,9 +296,10 @@ public class DownloadService extends IntentService {
|
|
|
212
296
|
|
|
213
297
|
publishResults(dest, id, version, "", sessionKey, "", true);
|
|
214
298
|
} catch (Exception e) {
|
|
215
|
-
Log.e("DownloadService", "Error in handleManifestDownload", e);
|
|
299
|
+
Log.e(TAG + " DownloadService", "Error in handleManifestDownload", e);
|
|
216
300
|
publishResults("", id, version, "", sessionKey, e.getMessage(), true);
|
|
217
301
|
}
|
|
302
|
+
stopForegroundIfNeeded();
|
|
218
303
|
}
|
|
219
304
|
|
|
220
305
|
private void handleSingleFileDownload(
|
|
@@ -338,6 +423,7 @@ public class DownloadService extends IntentService {
|
|
|
338
423
|
false
|
|
339
424
|
);
|
|
340
425
|
}
|
|
426
|
+
stopForegroundIfNeeded();
|
|
341
427
|
}
|
|
342
428
|
|
|
343
429
|
private void clearDownloadData(String docDir) {
|
|
@@ -404,10 +490,9 @@ public class DownloadService extends IntentService {
|
|
|
404
490
|
String expectedHash,
|
|
405
491
|
String id
|
|
406
492
|
) throws Exception {
|
|
407
|
-
Log.d("DownloadService", "downloadAndVerify " + downloadUrl);
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
connection.setRequestMethod("GET");
|
|
493
|
+
Log.d(TAG + " DownloadService", "downloadAndVerify " + downloadUrl);
|
|
494
|
+
|
|
495
|
+
Request request = new Request.Builder().url(downloadUrl).build();
|
|
411
496
|
|
|
412
497
|
// Create a temporary file for the compressed data
|
|
413
498
|
File compressedFile = new File(
|
|
@@ -415,79 +500,56 @@ public class DownloadService extends IntentService {
|
|
|
415
500
|
"temp_" + targetFile.getName() + ".br"
|
|
416
501
|
);
|
|
417
502
|
|
|
418
|
-
try (
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
) {
|
|
422
|
-
byte[] buffer = new byte[8192];
|
|
423
|
-
int bytesRead;
|
|
424
|
-
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
|
425
|
-
compressedFos.write(buffer, 0, bytesRead);
|
|
503
|
+
try (Response response = client.newCall(request).execute()) {
|
|
504
|
+
if (!response.isSuccessful()) {
|
|
505
|
+
throw new IOException("Unexpected response code: " + response.code());
|
|
426
506
|
}
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
// Decompress the file
|
|
430
|
-
try (
|
|
431
|
-
FileInputStream fis = new FileInputStream(compressedFile);
|
|
432
|
-
BrotliInputStream brotliInputStream = new BrotliInputStream(fis);
|
|
433
|
-
FileOutputStream fos = new FileOutputStream(targetFile)
|
|
434
|
-
) {
|
|
435
|
-
byte[] buffer = new byte[8192];
|
|
436
|
-
int len;
|
|
437
|
-
while ((len = brotliInputStream.read(buffer)) != -1) {
|
|
438
|
-
fos.write(buffer, 0, len);
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
// Delete the compressed file
|
|
443
|
-
compressedFile.delete();
|
|
444
507
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
throw new IOException(
|
|
454
|
-
"Checksum verification failed for " +
|
|
455
|
-
targetFile.getName() +
|
|
456
|
-
" " +
|
|
457
|
-
expectedHash +
|
|
458
|
-
" " +
|
|
459
|
-
actualHash
|
|
460
|
-
);
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
// Custom request for handling input stream
|
|
465
|
-
private class InputStreamVolleyRequest extends Request<byte[]> {
|
|
508
|
+
// Download compressed file
|
|
509
|
+
try (
|
|
510
|
+
ResponseBody responseBody = response.body();
|
|
511
|
+
FileOutputStream compressedFos = new FileOutputStream(compressedFile)
|
|
512
|
+
) {
|
|
513
|
+
if (responseBody == null) {
|
|
514
|
+
throw new IOException("Response body is null");
|
|
515
|
+
}
|
|
466
516
|
|
|
467
|
-
|
|
517
|
+
byte[] buffer = new byte[8192];
|
|
518
|
+
int bytesRead;
|
|
519
|
+
try (InputStream inputStream = responseBody.byteStream()) {
|
|
520
|
+
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
|
521
|
+
compressedFos.write(buffer, 0, bytesRead);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
468
525
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
526
|
+
// Decompress the file
|
|
527
|
+
try (
|
|
528
|
+
FileInputStream fis = new FileInputStream(compressedFile);
|
|
529
|
+
BrotliInputStream brotliInputStream = new BrotliInputStream(fis);
|
|
530
|
+
FileOutputStream fos = new FileOutputStream(targetFile)
|
|
531
|
+
) {
|
|
532
|
+
byte[] buffer = new byte[8192];
|
|
533
|
+
int len;
|
|
534
|
+
while ((len = brotliInputStream.read(buffer)) != -1) {
|
|
535
|
+
fos.write(buffer, 0, len);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
478
538
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
mListener.onResponse(response);
|
|
482
|
-
}
|
|
539
|
+
// Delete the compressed file
|
|
540
|
+
compressedFile.delete();
|
|
483
541
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
542
|
+
// Verify checksum
|
|
543
|
+
String actualHash = calculateFileHash(targetFile);
|
|
544
|
+
if (actualHash.equals(expectedHash)) {
|
|
545
|
+
// Only cache if checksum is correct
|
|
546
|
+
copyFile(targetFile, cacheFile);
|
|
547
|
+
} else {
|
|
548
|
+
targetFile.delete();
|
|
549
|
+
throw new IOException(
|
|
550
|
+
"Checksum verification failed for " + targetFile.getName()
|
|
551
|
+
);
|
|
552
|
+
}
|
|
491
553
|
}
|
|
492
554
|
}
|
|
493
555
|
|
|
@@ -519,4 +581,15 @@ public class DownloadService extends IntentService {
|
|
|
519
581
|
}
|
|
520
582
|
return sb.toString();
|
|
521
583
|
}
|
|
584
|
+
|
|
585
|
+
private void stopForegroundIfNeeded() {
|
|
586
|
+
handler.removeCallbacks(notificationRunnable);
|
|
587
|
+
if (isNotificationShown) {
|
|
588
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
589
|
+
stopForeground(STOP_FOREGROUND_REMOVE);
|
|
590
|
+
} else {
|
|
591
|
+
stopForeground(true);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
522
595
|
}
|
package/dist/docs.json
CHANGED
|
@@ -466,7 +466,7 @@
|
|
|
466
466
|
"text": "4.7.0"
|
|
467
467
|
}
|
|
468
468
|
],
|
|
469
|
-
"docs": "Sets the channel for this device. The channel has to allow for self assignment for this to work.\nDo not use this method to set the channel at boot when `autoUpdate` is enabled in the {@link PluginsConfig}.\nThis method is to set the channel after the app is ready.",
|
|
469
|
+
"docs": "Sets the channel for this device. The channel has to allow for self assignment for this to work.\nDo not use this method to set the channel at boot when `autoUpdate` is enabled in the {@link PluginsConfig}.\nThis method is to set the channel after the app is ready.\nThis methods send to Capgo backend a request to link the device ID to the channel. Capgo can accept or refuse depending of the setting of your channel.",
|
|
470
470
|
"complexTypes": [
|
|
471
471
|
"ChannelRes",
|
|
472
472
|
"SetChannelOptions"
|
|
@@ -1908,7 +1908,7 @@
|
|
|
1908
1908
|
"name": "updateUrl",
|
|
1909
1909
|
"tags": [
|
|
1910
1910
|
{
|
|
1911
|
-
"text": "https://
|
|
1911
|
+
"text": "https://plugin.capgo.app/updates",
|
|
1912
1912
|
"name": "default"
|
|
1913
1913
|
},
|
|
1914
1914
|
{
|
|
@@ -1924,7 +1924,7 @@
|
|
|
1924
1924
|
"name": "channelUrl",
|
|
1925
1925
|
"tags": [
|
|
1926
1926
|
{
|
|
1927
|
-
"text": "https://
|
|
1927
|
+
"text": "https://plugin.capgo.app/channel_self",
|
|
1928
1928
|
"name": "default"
|
|
1929
1929
|
},
|
|
1930
1930
|
{
|
|
@@ -1940,7 +1940,7 @@
|
|
|
1940
1940
|
"name": "statsUrl",
|
|
1941
1941
|
"tags": [
|
|
1942
1942
|
{
|
|
1943
|
-
"text": "https://
|
|
1943
|
+
"text": "https://plugin.capgo.app/stats",
|
|
1944
1944
|
"name": "default"
|
|
1945
1945
|
},
|
|
1946
1946
|
{
|
|
@@ -1960,7 +1960,7 @@
|
|
|
1960
1960
|
"name": "default"
|
|
1961
1961
|
}
|
|
1962
1962
|
],
|
|
1963
|
-
"docs": "Configure the private key for end to end live update encryption.\n\nOnly available for Android and iOS.",
|
|
1963
|
+
"docs": "Configure the private key for end to end live update encryption.\n\nOnly available for Android and iOS. Deprecated in version 6.2.0. will be removed in version 7.0.0.",
|
|
1964
1964
|
"complexTypes": [],
|
|
1965
1965
|
"type": "string | undefined"
|
|
1966
1966
|
},
|
|
@@ -64,7 +64,7 @@ declare module "@capacitor/cli" {
|
|
|
64
64
|
*
|
|
65
65
|
* Only available for Android and iOS.
|
|
66
66
|
*
|
|
67
|
-
* @default https://
|
|
67
|
+
* @default https://plugin.capgo.app/updates
|
|
68
68
|
* @example https://example.com/api/auto_update
|
|
69
69
|
*/
|
|
70
70
|
updateUrl?: string;
|
|
@@ -73,7 +73,7 @@ declare module "@capacitor/cli" {
|
|
|
73
73
|
*
|
|
74
74
|
* Only available for Android and iOS.
|
|
75
75
|
*
|
|
76
|
-
* @default https://
|
|
76
|
+
* @default https://plugin.capgo.app/channel_self
|
|
77
77
|
* @example https://example.com/api/channel
|
|
78
78
|
*/
|
|
79
79
|
channelUrl?: string;
|
|
@@ -82,14 +82,14 @@ declare module "@capacitor/cli" {
|
|
|
82
82
|
*
|
|
83
83
|
* Only available for Android and iOS. Set to "" to disable stats reporting.
|
|
84
84
|
*
|
|
85
|
-
* @default https://
|
|
85
|
+
* @default https://plugin.capgo.app/stats
|
|
86
86
|
* @example https://example.com/api/stats
|
|
87
87
|
*/
|
|
88
88
|
statsUrl?: string;
|
|
89
89
|
/**
|
|
90
90
|
* Configure the private key for end to end live update encryption.
|
|
91
91
|
*
|
|
92
|
-
* Only available for Android and iOS.
|
|
92
|
+
* Only available for Android and iOS. Deprecated in version 6.2.0. will be removed in version 7.0.0.
|
|
93
93
|
*
|
|
94
94
|
* @default undefined
|
|
95
95
|
*/
|
|
@@ -356,6 +356,9 @@ export interface CapacitorUpdaterPlugin {
|
|
|
356
356
|
* Sets the channel for this device. The channel has to allow for self assignment for this to work.
|
|
357
357
|
* Do not use this method to set the channel at boot when `autoUpdate` is enabled in the {@link PluginsConfig}.
|
|
358
358
|
* This method is to set the channel after the app is ready.
|
|
359
|
+
* This methods send to Capgo backend a request to link the device ID to the channel. Capgo can accept or refuse depending of the setting of your channel.
|
|
360
|
+
*
|
|
361
|
+
*
|
|
359
362
|
*
|
|
360
363
|
* @param options Is the {@link SetChannelOptions} channel to set
|
|
361
364
|
* @returns {Promise<ChannelRes>} A Promise which is resolved when the new channel is set
|