@capgo/capacitor-updater 8.0.0 → 8.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CapgoCapacitorUpdater.podspec +7 -5
- package/Package.swift +37 -0
- package/README.md +1461 -231
- package/android/build.gradle +29 -12
- package/android/proguard-rules.pro +45 -0
- package/android/src/main/AndroidManifest.xml +0 -1
- package/android/src/main/java/ee/forgr/capacitor_updater/BundleInfo.java +223 -195
- package/android/src/main/java/ee/forgr/capacitor_updater/BundleStatus.java +23 -23
- package/android/src/main/java/ee/forgr/capacitor_updater/Callback.java +13 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +2159 -1234
- package/android/src/main/java/ee/forgr/capacitor_updater/CapgoUpdater.java +1507 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/CryptoCipher.java +330 -121
- package/android/src/main/java/ee/forgr/capacitor_updater/DataManager.java +28 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/DelayCondition.java +43 -49
- package/android/src/main/java/ee/forgr/capacitor_updater/DelayUntilNext.java +4 -4
- package/android/src/main/java/ee/forgr/capacitor_updater/DelayUpdateUtils.java +260 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/DeviceIdHelper.java +221 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadService.java +808 -117
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadWorkerManager.java +156 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/InternalUtils.java +32 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/Logger.java +338 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/ShakeDetector.java +72 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/ShakeMenu.java +169 -0
- package/dist/docs.json +2187 -625
- package/dist/esm/definitions.d.ts +1286 -249
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/history.d.ts +1 -0
- package/dist/esm/history.js +283 -0
- package/dist/esm/history.js.map +1 -0
- package/dist/esm/index.d.ts +3 -2
- package/dist/esm/index.js +5 -4
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/web.d.ts +36 -41
- package/dist/esm/web.js +94 -35
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +376 -35
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +376 -35
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/CapacitorUpdaterPlugin/AES.swift +69 -0
- package/ios/Sources/CapacitorUpdaterPlugin/BigInt.swift +55 -0
- package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/BundleInfo.swift +37 -10
- package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/BundleStatus.swift +1 -1
- package/ios/Sources/CapacitorUpdaterPlugin/CapacitorUpdaterPlugin.swift +1605 -0
- package/ios/Sources/CapacitorUpdaterPlugin/CapgoUpdater.swift +1526 -0
- package/ios/Sources/CapacitorUpdaterPlugin/CryptoCipher.swift +267 -0
- package/ios/Sources/CapacitorUpdaterPlugin/DelayUpdateUtils.swift +220 -0
- package/ios/Sources/CapacitorUpdaterPlugin/DeviceIdHelper.swift +120 -0
- package/ios/Sources/CapacitorUpdaterPlugin/InternalUtils.swift +311 -0
- package/ios/Sources/CapacitorUpdaterPlugin/Logger.swift +310 -0
- package/ios/Sources/CapacitorUpdaterPlugin/RSA.swift +274 -0
- package/ios/Sources/CapacitorUpdaterPlugin/ShakeMenu.swift +112 -0
- package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/UserDefaultsExtension.swift +0 -2
- package/package.json +41 -35
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdater.java +0 -1130
- package/ios/Plugin/CapacitorUpdater.swift +0 -858
- package/ios/Plugin/CapacitorUpdaterPlugin.h +0 -10
- package/ios/Plugin/CapacitorUpdaterPlugin.m +0 -27
- package/ios/Plugin/CapacitorUpdaterPlugin.swift +0 -675
- package/ios/Plugin/CryptoCipher.swift +0 -240
- /package/{LICENCE → LICENSE} +0 -0
- /package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/DelayCondition.swift +0 -0
- /package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/DelayUntilNext.swift +0 -0
- /package/ios/{Plugin → Sources/CapacitorUpdaterPlugin}/Info.plist +0 -0
|
@@ -1,1130 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
3
|
-
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
-
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
package ee.forgr.capacitor_updater;
|
|
8
|
-
|
|
9
|
-
import android.app.Activity;
|
|
10
|
-
import android.content.BroadcastReceiver;
|
|
11
|
-
import android.content.Context;
|
|
12
|
-
import android.content.Intent;
|
|
13
|
-
import android.content.IntentFilter;
|
|
14
|
-
import android.content.SharedPreferences;
|
|
15
|
-
import android.os.Build;
|
|
16
|
-
import android.os.Bundle;
|
|
17
|
-
import android.util.Base64;
|
|
18
|
-
import android.util.Log;
|
|
19
|
-
import com.android.volley.BuildConfig;
|
|
20
|
-
import com.android.volley.DefaultRetryPolicy;
|
|
21
|
-
import com.android.volley.NetworkResponse;
|
|
22
|
-
import com.android.volley.Request;
|
|
23
|
-
import com.android.volley.RequestQueue;
|
|
24
|
-
import com.android.volley.Response;
|
|
25
|
-
import com.android.volley.VolleyError;
|
|
26
|
-
import com.android.volley.toolbox.HttpHeaderParser;
|
|
27
|
-
import com.android.volley.toolbox.JsonObjectRequest;
|
|
28
|
-
import com.getcapacitor.JSObject;
|
|
29
|
-
import com.getcapacitor.plugin.WebView;
|
|
30
|
-
import java.io.BufferedInputStream;
|
|
31
|
-
import java.io.DataInputStream;
|
|
32
|
-
import java.io.File;
|
|
33
|
-
import java.io.FileInputStream;
|
|
34
|
-
import java.io.FileNotFoundException;
|
|
35
|
-
import java.io.FileOutputStream;
|
|
36
|
-
import java.io.FilenameFilter;
|
|
37
|
-
import java.io.IOException;
|
|
38
|
-
import java.io.InputStream;
|
|
39
|
-
import java.io.UnsupportedEncodingException;
|
|
40
|
-
import java.net.URL;
|
|
41
|
-
import java.net.URLConnection;
|
|
42
|
-
import java.security.GeneralSecurityException;
|
|
43
|
-
import java.security.PrivateKey;
|
|
44
|
-
import java.security.SecureRandom;
|
|
45
|
-
import java.util.ArrayList;
|
|
46
|
-
import java.util.Date;
|
|
47
|
-
import java.util.Iterator;
|
|
48
|
-
import java.util.List;
|
|
49
|
-
import java.util.zip.CRC32;
|
|
50
|
-
import java.util.zip.ZipEntry;
|
|
51
|
-
import java.util.zip.ZipInputStream;
|
|
52
|
-
import javax.crypto.SecretKey;
|
|
53
|
-
import org.json.JSONException;
|
|
54
|
-
import org.json.JSONObject;
|
|
55
|
-
|
|
56
|
-
interface Callback {
|
|
57
|
-
void callback(JSObject jsoObject);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
public class CapacitorUpdater {
|
|
61
|
-
|
|
62
|
-
private static final String AB =
|
|
63
|
-
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
64
|
-
private static final SecureRandom rnd = new SecureRandom();
|
|
65
|
-
|
|
66
|
-
private static final String INFO_SUFFIX = "_info";
|
|
67
|
-
|
|
68
|
-
private static final String FALLBACK_VERSION = "pastVersion";
|
|
69
|
-
private static final String NEXT_VERSION = "nextVersion";
|
|
70
|
-
private static final String bundleDirectory = "versions";
|
|
71
|
-
|
|
72
|
-
public static final String TAG = "Capacitor-updater";
|
|
73
|
-
public static final int timeout = 20000;
|
|
74
|
-
|
|
75
|
-
public SharedPreferences.Editor editor;
|
|
76
|
-
public SharedPreferences prefs;
|
|
77
|
-
|
|
78
|
-
public RequestQueue requestQueue;
|
|
79
|
-
|
|
80
|
-
public File documentsDir;
|
|
81
|
-
public Activity activity;
|
|
82
|
-
public String PLUGIN_VERSION = "";
|
|
83
|
-
public String versionBuild = "";
|
|
84
|
-
public String versionCode = "";
|
|
85
|
-
public String versionOs = "";
|
|
86
|
-
|
|
87
|
-
public String customId = "";
|
|
88
|
-
public String statsUrl = "";
|
|
89
|
-
public String channelUrl = "";
|
|
90
|
-
public String appId = "";
|
|
91
|
-
public String privateKey = "";
|
|
92
|
-
public String deviceID = "";
|
|
93
|
-
|
|
94
|
-
private final FilenameFilter filter = new FilenameFilter() {
|
|
95
|
-
@Override
|
|
96
|
-
public boolean accept(final File f, final String name) {
|
|
97
|
-
// ignore directories generated by mac os x
|
|
98
|
-
return (
|
|
99
|
-
!name.startsWith("__MACOSX") &&
|
|
100
|
-
!name.startsWith(".") &&
|
|
101
|
-
!name.startsWith(".DS_Store")
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
private boolean isProd() {
|
|
107
|
-
return !BuildConfig.DEBUG;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
private boolean isEmulator() {
|
|
111
|
-
return (
|
|
112
|
-
(
|
|
113
|
-
Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic")
|
|
114
|
-
) ||
|
|
115
|
-
Build.FINGERPRINT.startsWith("generic") ||
|
|
116
|
-
Build.FINGERPRINT.startsWith("unknown") ||
|
|
117
|
-
Build.HARDWARE.contains("goldfish") ||
|
|
118
|
-
Build.HARDWARE.contains("ranchu") ||
|
|
119
|
-
Build.MODEL.contains("google_sdk") ||
|
|
120
|
-
Build.MODEL.contains("Emulator") ||
|
|
121
|
-
Build.MODEL.contains("Android SDK built for x86") ||
|
|
122
|
-
Build.MANUFACTURER.contains("Genymotion") ||
|
|
123
|
-
Build.PRODUCT.contains("sdk_google") ||
|
|
124
|
-
Build.PRODUCT.contains("google_sdk") ||
|
|
125
|
-
Build.PRODUCT.contains("sdk") ||
|
|
126
|
-
Build.PRODUCT.contains("sdk_x86") ||
|
|
127
|
-
Build.PRODUCT.contains("sdk_gphone64_arm64") ||
|
|
128
|
-
Build.PRODUCT.contains("vbox86p") ||
|
|
129
|
-
Build.PRODUCT.contains("emulator") ||
|
|
130
|
-
Build.PRODUCT.contains("simulator")
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
private int calcTotalPercent(
|
|
135
|
-
final int percent,
|
|
136
|
-
final int min,
|
|
137
|
-
final int max
|
|
138
|
-
) {
|
|
139
|
-
return (percent * (max - min)) / 100 + min;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
void notifyDownload(final String id, final int percent) {
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
void notifyListeners(final String id, final JSObject res) {
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
private String randomString(final int len) {
|
|
151
|
-
final StringBuilder sb = new StringBuilder(len);
|
|
152
|
-
for (int i = 0; i < len; i++) sb.append(
|
|
153
|
-
AB.charAt(rnd.nextInt(AB.length()))
|
|
154
|
-
);
|
|
155
|
-
return sb.toString();
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
private File unzip(final String id, final File zipFile, final String dest)
|
|
159
|
-
throws IOException {
|
|
160
|
-
final File targetDirectory = new File(this.documentsDir, dest);
|
|
161
|
-
final ZipInputStream zis = new ZipInputStream(
|
|
162
|
-
new BufferedInputStream(new FileInputStream(zipFile))
|
|
163
|
-
);
|
|
164
|
-
try {
|
|
165
|
-
int count;
|
|
166
|
-
final int bufferSize = 8192;
|
|
167
|
-
final byte[] buffer = new byte[bufferSize];
|
|
168
|
-
final long lengthTotal = zipFile.length();
|
|
169
|
-
long lengthRead = bufferSize;
|
|
170
|
-
int percent = 0;
|
|
171
|
-
this.notifyDownload(id, 75);
|
|
172
|
-
|
|
173
|
-
ZipEntry entry;
|
|
174
|
-
while ((entry = zis.getNextEntry()) != null) {
|
|
175
|
-
if (entry.getName().contains("\\")) {
|
|
176
|
-
Log.e(
|
|
177
|
-
TAG,
|
|
178
|
-
"unzip: Windows path is not supported, please use unix path as require by zip RFC: " +
|
|
179
|
-
entry.getName()
|
|
180
|
-
);
|
|
181
|
-
}
|
|
182
|
-
final File file = new File(targetDirectory, entry.getName());
|
|
183
|
-
final String canonicalPath = file.getCanonicalPath();
|
|
184
|
-
final String canonicalDir = targetDirectory.getCanonicalPath();
|
|
185
|
-
final File dir = entry.isDirectory() ? file : file.getParentFile();
|
|
186
|
-
|
|
187
|
-
if (!canonicalPath.startsWith(canonicalDir)) {
|
|
188
|
-
throw new FileNotFoundException(
|
|
189
|
-
"SecurityException, Failed to ensure directory is the start path : " +
|
|
190
|
-
canonicalDir +
|
|
191
|
-
" of " +
|
|
192
|
-
canonicalPath
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
if (!dir.isDirectory() && !dir.mkdirs()) {
|
|
197
|
-
throw new FileNotFoundException(
|
|
198
|
-
"Failed to ensure directory: " + dir.getAbsolutePath()
|
|
199
|
-
);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
if (entry.isDirectory()) {
|
|
203
|
-
continue;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
try (final FileOutputStream outputStream = new FileOutputStream(file)) {
|
|
207
|
-
while ((count = zis.read(buffer)) != -1) outputStream.write(
|
|
208
|
-
buffer,
|
|
209
|
-
0,
|
|
210
|
-
count
|
|
211
|
-
);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
final int newPercent = (int) ((lengthRead / (float) lengthTotal) * 100);
|
|
215
|
-
if (lengthTotal > 1 && newPercent != percent) {
|
|
216
|
-
percent = newPercent;
|
|
217
|
-
this.notifyDownload(id, this.calcTotalPercent(percent, 75, 90));
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
lengthRead += entry.getCompressedSize();
|
|
221
|
-
}
|
|
222
|
-
return targetDirectory;
|
|
223
|
-
} finally {
|
|
224
|
-
try {
|
|
225
|
-
zis.close();
|
|
226
|
-
} catch (final IOException e) {
|
|
227
|
-
Log.e(TAG, "Failed to close zip input stream", e);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
private void flattenAssets(final File sourceFile, final String dest)
|
|
233
|
-
throws IOException {
|
|
234
|
-
if (!sourceFile.exists()) {
|
|
235
|
-
throw new FileNotFoundException(
|
|
236
|
-
"Source file not found: " + sourceFile.getPath()
|
|
237
|
-
);
|
|
238
|
-
}
|
|
239
|
-
final File destinationFile = new File(this.documentsDir, dest);
|
|
240
|
-
destinationFile.getParentFile().mkdirs();
|
|
241
|
-
final String[] entries = sourceFile.list(this.filter);
|
|
242
|
-
if (entries == null || entries.length == 0) {
|
|
243
|
-
throw new IOException(
|
|
244
|
-
"Source file was not a directory or was empty: " + sourceFile.getPath()
|
|
245
|
-
);
|
|
246
|
-
}
|
|
247
|
-
if (entries.length == 1 && !"index.html".equals(entries[0])) {
|
|
248
|
-
final File child = new File(sourceFile, entries[0]);
|
|
249
|
-
child.renameTo(destinationFile);
|
|
250
|
-
} else {
|
|
251
|
-
sourceFile.renameTo(destinationFile);
|
|
252
|
-
}
|
|
253
|
-
sourceFile.delete();
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
public void onResume() {
|
|
257
|
-
this.activity.registerReceiver(
|
|
258
|
-
receiver,
|
|
259
|
-
new IntentFilter(DownloadService.NOTIFICATION)
|
|
260
|
-
);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
public void onPause() {
|
|
264
|
-
this.activity.unregisterReceiver(receiver);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
private BroadcastReceiver receiver = new BroadcastReceiver() {
|
|
268
|
-
@Override
|
|
269
|
-
public void onReceive(Context context, Intent intent) {
|
|
270
|
-
String action = intent.getAction();
|
|
271
|
-
Bundle bundle = intent.getExtras();
|
|
272
|
-
if (bundle != null) {
|
|
273
|
-
if (action == DownloadService.PERCENTDOWNLOAD) {
|
|
274
|
-
String id = bundle.getString(DownloadService.ID);
|
|
275
|
-
int percent = bundle.getInt(DownloadService.PERCENT);
|
|
276
|
-
CapacitorUpdater.this.notifyDownload(id, percent);
|
|
277
|
-
} else if (action == DownloadService.NOTIFICATION) {
|
|
278
|
-
String id = bundle.getString(DownloadService.ID);
|
|
279
|
-
String dest = bundle.getString(DownloadService.FILEDEST);
|
|
280
|
-
String version = bundle.getString(DownloadService.VERSION);
|
|
281
|
-
String sessionKey = bundle.getString(DownloadService.SESSIONKEY);
|
|
282
|
-
String checksum = bundle.getString(DownloadService.CHECKSUM);
|
|
283
|
-
Log.i(
|
|
284
|
-
CapacitorUpdater.TAG,
|
|
285
|
-
"res " +
|
|
286
|
-
id +
|
|
287
|
-
" " +
|
|
288
|
-
dest +
|
|
289
|
-
" " +
|
|
290
|
-
version +
|
|
291
|
-
" " +
|
|
292
|
-
sessionKey +
|
|
293
|
-
" " +
|
|
294
|
-
checksum
|
|
295
|
-
);
|
|
296
|
-
if (dest == null) {
|
|
297
|
-
final JSObject ret = new JSObject();
|
|
298
|
-
ret.put(
|
|
299
|
-
"version",
|
|
300
|
-
CapacitorUpdater.this.getCurrentBundle().getVersionName()
|
|
301
|
-
);
|
|
302
|
-
CapacitorUpdater.this.notifyListeners("downloadFailed", ret);
|
|
303
|
-
CapacitorUpdater.this.sendStats(
|
|
304
|
-
"download_fail",
|
|
305
|
-
CapacitorUpdater.this.getCurrentBundle().getVersionName()
|
|
306
|
-
);
|
|
307
|
-
return;
|
|
308
|
-
}
|
|
309
|
-
CapacitorUpdater.this.finishDownload(
|
|
310
|
-
id,
|
|
311
|
-
dest,
|
|
312
|
-
version,
|
|
313
|
-
sessionKey,
|
|
314
|
-
checksum,
|
|
315
|
-
true
|
|
316
|
-
);
|
|
317
|
-
} else {
|
|
318
|
-
Log.i(TAG, "Unknown action " + action);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
};
|
|
323
|
-
|
|
324
|
-
public void finishDownload(
|
|
325
|
-
String id,
|
|
326
|
-
String dest,
|
|
327
|
-
String version,
|
|
328
|
-
String sessionKey,
|
|
329
|
-
String checksumRes,
|
|
330
|
-
Boolean setNext
|
|
331
|
-
) {
|
|
332
|
-
try {
|
|
333
|
-
final File downloaded = new File(this.documentsDir, dest);
|
|
334
|
-
this.decryptFile(downloaded, sessionKey, version);
|
|
335
|
-
final String checksum;
|
|
336
|
-
checksum = this.getChecksum(downloaded);
|
|
337
|
-
this.notifyDownload(id, 71);
|
|
338
|
-
final File unzipped = this.unzip(id, downloaded, this.randomString(10));
|
|
339
|
-
downloaded.delete();
|
|
340
|
-
this.notifyDownload(id, 91);
|
|
341
|
-
final String idName = bundleDirectory + "/" + id;
|
|
342
|
-
this.flattenAssets(unzipped, idName);
|
|
343
|
-
this.notifyDownload(id, 100);
|
|
344
|
-
this.saveBundleInfo(id, null);
|
|
345
|
-
BundleInfo next = new BundleInfo(
|
|
346
|
-
id,
|
|
347
|
-
version,
|
|
348
|
-
BundleStatus.PENDING,
|
|
349
|
-
new Date(System.currentTimeMillis()),
|
|
350
|
-
checksum
|
|
351
|
-
);
|
|
352
|
-
this.saveBundleInfo(id, next);
|
|
353
|
-
if (
|
|
354
|
-
checksumRes != null &&
|
|
355
|
-
!checksumRes.isEmpty() &&
|
|
356
|
-
!checksumRes.equals(checksum)
|
|
357
|
-
) {
|
|
358
|
-
Log.e(
|
|
359
|
-
CapacitorUpdater.TAG,
|
|
360
|
-
"Error checksum " + next.getChecksum() + " " + checksum
|
|
361
|
-
);
|
|
362
|
-
this.sendStats("checksum_fail", getCurrentBundle().getVersionName());
|
|
363
|
-
final Boolean res = this.delete(id);
|
|
364
|
-
if (res) {
|
|
365
|
-
Log.i(
|
|
366
|
-
CapacitorUpdater.TAG,
|
|
367
|
-
"Failed bundle deleted: " + next.getVersionName()
|
|
368
|
-
);
|
|
369
|
-
}
|
|
370
|
-
throw new IOException("Checksum failed: " + id);
|
|
371
|
-
}
|
|
372
|
-
final JSObject ret = new JSObject();
|
|
373
|
-
ret.put("bundle", next.toJSON());
|
|
374
|
-
CapacitorUpdater.this.notifyListeners("updateAvailable", ret);
|
|
375
|
-
if (setNext) {
|
|
376
|
-
this.setNextBundle(next.getId());
|
|
377
|
-
}
|
|
378
|
-
} catch (IOException e) {
|
|
379
|
-
e.printStackTrace();
|
|
380
|
-
final JSObject ret = new JSObject();
|
|
381
|
-
ret.put(
|
|
382
|
-
"version",
|
|
383
|
-
CapacitorUpdater.this.getCurrentBundle().getVersionName()
|
|
384
|
-
);
|
|
385
|
-
CapacitorUpdater.this.notifyListeners("downloadFailed", ret);
|
|
386
|
-
CapacitorUpdater.this.sendStats(
|
|
387
|
-
"download_fail",
|
|
388
|
-
CapacitorUpdater.this.getCurrentBundle().getVersionName()
|
|
389
|
-
);
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
private void downloadFileBackground(
|
|
394
|
-
final String id,
|
|
395
|
-
final String url,
|
|
396
|
-
final String version,
|
|
397
|
-
final String sessionKey,
|
|
398
|
-
final String checksum,
|
|
399
|
-
final String dest
|
|
400
|
-
) {
|
|
401
|
-
Intent intent = new Intent(this.activity, DownloadService.class);
|
|
402
|
-
intent.putExtra(DownloadService.URL, url);
|
|
403
|
-
intent.putExtra(DownloadService.FILEDEST, dest);
|
|
404
|
-
intent.putExtra(
|
|
405
|
-
DownloadService.DOCDIR,
|
|
406
|
-
this.documentsDir.getAbsolutePath()
|
|
407
|
-
);
|
|
408
|
-
intent.putExtra(DownloadService.ID, id);
|
|
409
|
-
intent.putExtra(DownloadService.VERSION, version);
|
|
410
|
-
intent.putExtra(DownloadService.SESSIONKEY, sessionKey);
|
|
411
|
-
intent.putExtra(DownloadService.CHECKSUM, checksum);
|
|
412
|
-
this.activity.startService(intent);
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
private File downloadFile(
|
|
416
|
-
final String id,
|
|
417
|
-
final String url,
|
|
418
|
-
final String dest
|
|
419
|
-
) throws IOException {
|
|
420
|
-
final URL u = new URL(url);
|
|
421
|
-
final URLConnection connection = u.openConnection();
|
|
422
|
-
final InputStream is = u.openStream();
|
|
423
|
-
final DataInputStream dis = new DataInputStream(is);
|
|
424
|
-
|
|
425
|
-
final File target = new File(this.documentsDir, dest);
|
|
426
|
-
target.getParentFile().mkdirs();
|
|
427
|
-
target.createNewFile();
|
|
428
|
-
final FileOutputStream fos = new FileOutputStream(target);
|
|
429
|
-
|
|
430
|
-
final long totalLength = connection.getContentLength();
|
|
431
|
-
final int bufferSize = 1024;
|
|
432
|
-
final byte[] buffer = new byte[bufferSize];
|
|
433
|
-
int length;
|
|
434
|
-
|
|
435
|
-
int bytesRead = bufferSize;
|
|
436
|
-
int percent = 0;
|
|
437
|
-
this.notifyDownload(id, 10);
|
|
438
|
-
while ((length = dis.read(buffer)) > 0) {
|
|
439
|
-
fos.write(buffer, 0, length);
|
|
440
|
-
final int newPercent = (int) ((bytesRead / (float) totalLength) * 100);
|
|
441
|
-
if (totalLength > 1 && newPercent != percent) {
|
|
442
|
-
percent = newPercent;
|
|
443
|
-
this.notifyDownload(id, this.calcTotalPercent(percent, 10, 70));
|
|
444
|
-
}
|
|
445
|
-
bytesRead += length;
|
|
446
|
-
}
|
|
447
|
-
return target;
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
private void deleteDirectory(final File file) throws IOException {
|
|
451
|
-
if (file.isDirectory()) {
|
|
452
|
-
final File[] entries = file.listFiles();
|
|
453
|
-
if (entries != null) {
|
|
454
|
-
for (final File entry : entries) {
|
|
455
|
-
this.deleteDirectory(entry);
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
if (!file.delete()) {
|
|
460
|
-
throw new IOException("Failed to delete: " + file);
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
private void setCurrentBundle(final File bundle) {
|
|
465
|
-
this.editor.putString(WebView.CAP_SERVER_PATH, bundle.getPath());
|
|
466
|
-
Log.i(TAG, "Current bundle set to: " + bundle);
|
|
467
|
-
this.editor.commit();
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
private String getChecksum(File file) throws IOException {
|
|
471
|
-
byte[] bytes = new byte[(int) file.length()];
|
|
472
|
-
try (FileInputStream fis = new FileInputStream(file)) {
|
|
473
|
-
fis.read(bytes);
|
|
474
|
-
}
|
|
475
|
-
CRC32 crc = new CRC32();
|
|
476
|
-
crc.update(bytes);
|
|
477
|
-
String enc = String.format("%08X", crc.getValue());
|
|
478
|
-
return enc.toLowerCase();
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
private void decryptFile(
|
|
482
|
-
final File file,
|
|
483
|
-
final String ivSessionKey,
|
|
484
|
-
final String version
|
|
485
|
-
) throws IOException {
|
|
486
|
-
// (str != null && !str.isEmpty())
|
|
487
|
-
if (
|
|
488
|
-
this.privateKey == null ||
|
|
489
|
-
this.privateKey.isEmpty() ||
|
|
490
|
-
ivSessionKey == null ||
|
|
491
|
-
ivSessionKey.isEmpty()
|
|
492
|
-
) {
|
|
493
|
-
return;
|
|
494
|
-
}
|
|
495
|
-
try {
|
|
496
|
-
String ivB64 = ivSessionKey.split(":")[0];
|
|
497
|
-
String sessionKeyB64 = ivSessionKey.split(":")[1];
|
|
498
|
-
byte[] iv = Base64.decode(ivB64.getBytes(), Base64.DEFAULT);
|
|
499
|
-
byte[] sessionKey = Base64.decode(
|
|
500
|
-
sessionKeyB64.getBytes(),
|
|
501
|
-
Base64.DEFAULT
|
|
502
|
-
);
|
|
503
|
-
PrivateKey pKey = CryptoCipher.stringToPrivateKey(this.privateKey);
|
|
504
|
-
byte[] decryptedSessionKey = CryptoCipher.decryptRSA(sessionKey, pKey);
|
|
505
|
-
SecretKey sKey = CryptoCipher.byteToSessionKey(decryptedSessionKey);
|
|
506
|
-
byte[] content = new byte[(int) file.length()];
|
|
507
|
-
BufferedInputStream bis = new BufferedInputStream(
|
|
508
|
-
new FileInputStream(file)
|
|
509
|
-
);
|
|
510
|
-
DataInputStream dis = new DataInputStream(bis);
|
|
511
|
-
dis.readFully(content);
|
|
512
|
-
dis.close();
|
|
513
|
-
byte[] decrypted = CryptoCipher.decryptAES(content, sKey, iv);
|
|
514
|
-
// write the decrypted string to the file
|
|
515
|
-
FileOutputStream fos = new FileOutputStream(file.getAbsolutePath());
|
|
516
|
-
fos.write(decrypted);
|
|
517
|
-
fos.close();
|
|
518
|
-
} catch (GeneralSecurityException e) {
|
|
519
|
-
Log.i(TAG, "decryptFile fail");
|
|
520
|
-
this.sendStats("decrypt_fail", version);
|
|
521
|
-
e.printStackTrace();
|
|
522
|
-
throw new IOException("GeneralSecurityException");
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
public void downloadBackground(
|
|
527
|
-
final String url,
|
|
528
|
-
final String version,
|
|
529
|
-
final String sessionKey,
|
|
530
|
-
final String checksum
|
|
531
|
-
) {
|
|
532
|
-
final String id = this.randomString(10);
|
|
533
|
-
this.saveBundleInfo(
|
|
534
|
-
id,
|
|
535
|
-
new BundleInfo(
|
|
536
|
-
id,
|
|
537
|
-
version,
|
|
538
|
-
BundleStatus.DOWNLOADING,
|
|
539
|
-
new Date(System.currentTimeMillis()),
|
|
540
|
-
""
|
|
541
|
-
)
|
|
542
|
-
);
|
|
543
|
-
this.notifyDownload(id, 0);
|
|
544
|
-
this.notifyDownload(id, 5);
|
|
545
|
-
this.downloadFileBackground(
|
|
546
|
-
id,
|
|
547
|
-
url,
|
|
548
|
-
version,
|
|
549
|
-
sessionKey,
|
|
550
|
-
checksum,
|
|
551
|
-
this.randomString(10)
|
|
552
|
-
);
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
public BundleInfo download(
|
|
556
|
-
final String url,
|
|
557
|
-
final String version,
|
|
558
|
-
final String sessionKey,
|
|
559
|
-
final String checksum
|
|
560
|
-
) throws IOException {
|
|
561
|
-
final String id = this.randomString(10);
|
|
562
|
-
this.saveBundleInfo(
|
|
563
|
-
id,
|
|
564
|
-
new BundleInfo(
|
|
565
|
-
id,
|
|
566
|
-
version,
|
|
567
|
-
BundleStatus.DOWNLOADING,
|
|
568
|
-
new Date(System.currentTimeMillis()),
|
|
569
|
-
""
|
|
570
|
-
)
|
|
571
|
-
);
|
|
572
|
-
this.notifyDownload(id, 0);
|
|
573
|
-
final String idName = bundleDirectory + "/" + id;
|
|
574
|
-
this.notifyDownload(id, 5);
|
|
575
|
-
final String dest = this.randomString(10);
|
|
576
|
-
final File downloaded = this.downloadFile(id, url, dest);
|
|
577
|
-
this.finishDownload(id, dest, version, sessionKey, checksum, false);
|
|
578
|
-
BundleInfo info = new BundleInfo(
|
|
579
|
-
id,
|
|
580
|
-
version,
|
|
581
|
-
BundleStatus.PENDING,
|
|
582
|
-
new Date(System.currentTimeMillis()),
|
|
583
|
-
checksum
|
|
584
|
-
);
|
|
585
|
-
this.saveBundleInfo(id, info);
|
|
586
|
-
return info;
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
public List<BundleInfo> list() {
|
|
590
|
-
final List<BundleInfo> res = new ArrayList<>();
|
|
591
|
-
final File destHot = new File(this.documentsDir, bundleDirectory);
|
|
592
|
-
Log.d(TAG, "list File : " + destHot.getPath());
|
|
593
|
-
if (destHot.exists()) {
|
|
594
|
-
for (final File i : destHot.listFiles()) {
|
|
595
|
-
final String id = i.getName();
|
|
596
|
-
res.add(this.getBundleInfo(id));
|
|
597
|
-
}
|
|
598
|
-
} else {
|
|
599
|
-
Log.i(TAG, "No versions available to list" + destHot);
|
|
600
|
-
}
|
|
601
|
-
return res;
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
public Boolean delete(final String id, final Boolean removeInfo)
|
|
605
|
-
throws IOException {
|
|
606
|
-
final BundleInfo deleted = this.getBundleInfo(id);
|
|
607
|
-
if (deleted.isBuiltin() || this.getCurrentBundleId().equals(id)) {
|
|
608
|
-
Log.e(TAG, "Cannot delete " + id);
|
|
609
|
-
return false;
|
|
610
|
-
}
|
|
611
|
-
final File bundle = new File(this.documentsDir, bundleDirectory + "/" + id);
|
|
612
|
-
if (bundle.exists()) {
|
|
613
|
-
this.deleteDirectory(bundle);
|
|
614
|
-
if (removeInfo) {
|
|
615
|
-
this.removeBundleInfo(id);
|
|
616
|
-
} else {
|
|
617
|
-
this.saveBundleInfo(id, deleted.setStatus(BundleStatus.DELETED));
|
|
618
|
-
}
|
|
619
|
-
return true;
|
|
620
|
-
}
|
|
621
|
-
Log.e(TAG, "bundle removed: " + deleted.getVersionName());
|
|
622
|
-
this.sendStats("delete", deleted.getVersionName());
|
|
623
|
-
return false;
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
public Boolean delete(final String id) throws IOException {
|
|
627
|
-
return this.delete(id, true);
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
private File getBundleDirectory(final String id) {
|
|
631
|
-
return new File(this.documentsDir, bundleDirectory + "/" + id);
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
private boolean bundleExists(final String id) {
|
|
635
|
-
final File bundle = this.getBundleDirectory(id);
|
|
636
|
-
final BundleInfo bundleInfo = this.getBundleInfo(id);
|
|
637
|
-
if (bundle == null || !bundle.exists() || bundleInfo.isDeleted()) {
|
|
638
|
-
return false;
|
|
639
|
-
}
|
|
640
|
-
return new File(bundle.getPath(), "/index.html").exists();
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
public Boolean set(final BundleInfo bundle) {
|
|
644
|
-
return this.set(bundle.getId());
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
public Boolean set(final String id) {
|
|
648
|
-
final BundleInfo newBundle = this.getBundleInfo(id);
|
|
649
|
-
if (newBundle.isBuiltin()) {
|
|
650
|
-
this.reset();
|
|
651
|
-
return true;
|
|
652
|
-
}
|
|
653
|
-
final File bundle = this.getBundleDirectory(id);
|
|
654
|
-
Log.i(TAG, "Setting next active bundle: " + id);
|
|
655
|
-
if (this.bundleExists(id)) {
|
|
656
|
-
this.setCurrentBundle(bundle);
|
|
657
|
-
this.setBundleStatus(id, BundleStatus.PENDING);
|
|
658
|
-
this.sendStats("set", newBundle.getVersionName());
|
|
659
|
-
return true;
|
|
660
|
-
}
|
|
661
|
-
this.setBundleStatus(id, BundleStatus.ERROR);
|
|
662
|
-
this.sendStats("set_fail", newBundle.getVersionName());
|
|
663
|
-
return false;
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
public void reset() {
|
|
667
|
-
this.reset(false);
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
public void setSuccess(final BundleInfo bundle, Boolean autoDeletePrevious) {
|
|
671
|
-
this.setBundleStatus(bundle.getId(), BundleStatus.SUCCESS);
|
|
672
|
-
final BundleInfo fallback = this.getFallbackBundle();
|
|
673
|
-
Log.d(CapacitorUpdater.TAG, "Fallback bundle is: " + fallback);
|
|
674
|
-
Log.i(
|
|
675
|
-
CapacitorUpdater.TAG,
|
|
676
|
-
"Version successfully loaded: " + bundle.getVersionName()
|
|
677
|
-
);
|
|
678
|
-
if (autoDeletePrevious && !fallback.isBuiltin()) {
|
|
679
|
-
try {
|
|
680
|
-
final Boolean res = this.delete(fallback.getId());
|
|
681
|
-
if (res) {
|
|
682
|
-
Log.i(
|
|
683
|
-
CapacitorUpdater.TAG,
|
|
684
|
-
"Deleted previous bundle: " + fallback.getVersionName()
|
|
685
|
-
);
|
|
686
|
-
}
|
|
687
|
-
} catch (final IOException e) {
|
|
688
|
-
Log.e(
|
|
689
|
-
CapacitorUpdater.TAG,
|
|
690
|
-
"Failed to delete previous bundle: " + fallback.getVersionName(),
|
|
691
|
-
e
|
|
692
|
-
);
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
this.setFallbackBundle(bundle);
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
public void setError(final BundleInfo bundle) {
|
|
699
|
-
this.setBundleStatus(bundle.getId(), BundleStatus.ERROR);
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
public void reset(final boolean internal) {
|
|
703
|
-
Log.d(CapacitorUpdater.TAG, "reset: " + internal);
|
|
704
|
-
this.setCurrentBundle(new File("public"));
|
|
705
|
-
this.setFallbackBundle(null);
|
|
706
|
-
this.setNextBundle(null);
|
|
707
|
-
if (!internal) {
|
|
708
|
-
this.sendStats("reset", this.getCurrentBundle().getVersionName());
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
private JSONObject createInfoObject() throws JSONException {
|
|
713
|
-
JSONObject json = new JSONObject();
|
|
714
|
-
json.put("platform", "android");
|
|
715
|
-
json.put("device_id", this.deviceID);
|
|
716
|
-
json.put("app_id", this.appId);
|
|
717
|
-
json.put("custom_id", this.customId);
|
|
718
|
-
json.put("version_build", this.versionBuild);
|
|
719
|
-
json.put("version_code", this.versionCode);
|
|
720
|
-
json.put("version_os", this.versionOs);
|
|
721
|
-
json.put("version_name", this.getCurrentBundle().getVersionName());
|
|
722
|
-
json.put("plugin_version", this.PLUGIN_VERSION);
|
|
723
|
-
json.put("is_emulator", this.isEmulator());
|
|
724
|
-
json.put("is_prod", this.isProd());
|
|
725
|
-
return json;
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
private JSObject createError(String message, VolleyError error) {
|
|
729
|
-
NetworkResponse response = error.networkResponse;
|
|
730
|
-
final JSObject retError = new JSObject();
|
|
731
|
-
retError.put("error", "response_error");
|
|
732
|
-
if (response != null) {
|
|
733
|
-
try {
|
|
734
|
-
String json = new String(
|
|
735
|
-
response.data,
|
|
736
|
-
HttpHeaderParser.parseCharset(response.headers)
|
|
737
|
-
);
|
|
738
|
-
retError.put("message", message + ": " + json);
|
|
739
|
-
} catch (UnsupportedEncodingException e) {
|
|
740
|
-
retError.put("message", message + ": " + e.toString());
|
|
741
|
-
}
|
|
742
|
-
} else {
|
|
743
|
-
retError.put("message", message + ": " + error.toString());
|
|
744
|
-
}
|
|
745
|
-
Log.e(TAG, message + ": " + retError);
|
|
746
|
-
return retError;
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
public void getLatest(final String updateUrl, final Callback callback) {
|
|
750
|
-
JSONObject json = null;
|
|
751
|
-
try {
|
|
752
|
-
json = this.createInfoObject();
|
|
753
|
-
} catch (JSONException e) {
|
|
754
|
-
Log.e(TAG, "Error getLatest JSONException", e);
|
|
755
|
-
e.printStackTrace();
|
|
756
|
-
final JSObject retError = new JSObject();
|
|
757
|
-
retError.put("message", "Cannot get info: " + e.toString());
|
|
758
|
-
retError.put("error", "json_error");
|
|
759
|
-
callback.callback(retError);
|
|
760
|
-
return;
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
Log.i(CapacitorUpdater.TAG, "Auto-update parameters: " + json);
|
|
764
|
-
// Building a request
|
|
765
|
-
JsonObjectRequest request = new JsonObjectRequest(
|
|
766
|
-
Request.Method.POST,
|
|
767
|
-
updateUrl,
|
|
768
|
-
json,
|
|
769
|
-
new Response.Listener<JSONObject>() {
|
|
770
|
-
@Override
|
|
771
|
-
public void onResponse(JSONObject res) {
|
|
772
|
-
final JSObject ret = new JSObject();
|
|
773
|
-
Iterator<String> keys = res.keys();
|
|
774
|
-
while (keys.hasNext()) {
|
|
775
|
-
String key = keys.next();
|
|
776
|
-
if (res.has(key)) {
|
|
777
|
-
try {
|
|
778
|
-
if ("session_key".equals(key)) {
|
|
779
|
-
ret.put("sessionKey", res.get(key));
|
|
780
|
-
} else {
|
|
781
|
-
ret.put(key, res.get(key));
|
|
782
|
-
}
|
|
783
|
-
} catch (JSONException e) {
|
|
784
|
-
e.printStackTrace();
|
|
785
|
-
final JSObject retError = new JSObject();
|
|
786
|
-
retError.put("message", "Cannot set info: " + e.toString());
|
|
787
|
-
retError.put("error", "response_error");
|
|
788
|
-
callback.callback(retError);
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
callback.callback(ret);
|
|
793
|
-
}
|
|
794
|
-
},
|
|
795
|
-
new Response.ErrorListener() {
|
|
796
|
-
@Override
|
|
797
|
-
public void onErrorResponse(VolleyError error) {
|
|
798
|
-
callback.callback(
|
|
799
|
-
CapacitorUpdater.this.createError("Error get latest", error)
|
|
800
|
-
);
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
);
|
|
804
|
-
request.setRetryPolicy(
|
|
805
|
-
new DefaultRetryPolicy(
|
|
806
|
-
this.timeout,
|
|
807
|
-
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
|
|
808
|
-
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT
|
|
809
|
-
)
|
|
810
|
-
);
|
|
811
|
-
this.requestQueue.add(request);
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
public void setChannel(final String channel, final Callback callback) {
|
|
815
|
-
String channelUrl = this.channelUrl;
|
|
816
|
-
if (
|
|
817
|
-
channelUrl == null || "".equals(channelUrl) || channelUrl.length() == 0
|
|
818
|
-
) {
|
|
819
|
-
Log.e(TAG, "Channel URL is not set");
|
|
820
|
-
final JSObject retError = new JSObject();
|
|
821
|
-
retError.put("message", "channelUrl missing");
|
|
822
|
-
retError.put("error", "missing_config");
|
|
823
|
-
callback.callback(retError);
|
|
824
|
-
return;
|
|
825
|
-
}
|
|
826
|
-
JSONObject json = null;
|
|
827
|
-
try {
|
|
828
|
-
json = this.createInfoObject();
|
|
829
|
-
json.put("channel", channel);
|
|
830
|
-
} catch (JSONException e) {
|
|
831
|
-
Log.e(TAG, "Error setChannel JSONException", e);
|
|
832
|
-
e.printStackTrace();
|
|
833
|
-
final JSObject retError = new JSObject();
|
|
834
|
-
retError.put("message", "Cannot get info: " + e.toString());
|
|
835
|
-
retError.put("error", "json_error");
|
|
836
|
-
callback.callback(retError);
|
|
837
|
-
return;
|
|
838
|
-
}
|
|
839
|
-
// Building a request
|
|
840
|
-
JsonObjectRequest request = new JsonObjectRequest(
|
|
841
|
-
Request.Method.POST,
|
|
842
|
-
channelUrl,
|
|
843
|
-
json,
|
|
844
|
-
new Response.Listener<JSONObject>() {
|
|
845
|
-
@Override
|
|
846
|
-
public void onResponse(JSONObject res) {
|
|
847
|
-
final JSObject ret = new JSObject();
|
|
848
|
-
Iterator<String> keys = res.keys();
|
|
849
|
-
while (keys.hasNext()) {
|
|
850
|
-
String key = keys.next();
|
|
851
|
-
if (res.has(key)) {
|
|
852
|
-
try {
|
|
853
|
-
ret.put(key, res.get(key));
|
|
854
|
-
} catch (JSONException e) {
|
|
855
|
-
e.printStackTrace();
|
|
856
|
-
final JSObject retError = new JSObject();
|
|
857
|
-
retError.put("message", "Cannot set channel: " + e.toString());
|
|
858
|
-
retError.put("error", "response_error");
|
|
859
|
-
callback.callback(ret);
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
Log.i(TAG, "Channel set to \"" + channel);
|
|
864
|
-
callback.callback(ret);
|
|
865
|
-
}
|
|
866
|
-
},
|
|
867
|
-
new Response.ErrorListener() {
|
|
868
|
-
@Override
|
|
869
|
-
public void onErrorResponse(VolleyError error) {
|
|
870
|
-
callback.callback(
|
|
871
|
-
CapacitorUpdater.this.createError("Error set channel", error)
|
|
872
|
-
);
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
);
|
|
876
|
-
request.setRetryPolicy(
|
|
877
|
-
new DefaultRetryPolicy(
|
|
878
|
-
this.timeout,
|
|
879
|
-
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
|
|
880
|
-
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT
|
|
881
|
-
)
|
|
882
|
-
);
|
|
883
|
-
this.requestQueue.add(request);
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
public void getChannel(final Callback callback) {
|
|
887
|
-
String channelUrl = this.channelUrl;
|
|
888
|
-
if (
|
|
889
|
-
channelUrl == null || "".equals(channelUrl) || channelUrl.length() == 0
|
|
890
|
-
) {
|
|
891
|
-
Log.e(TAG, "Channel URL is not set");
|
|
892
|
-
final JSObject retError = new JSObject();
|
|
893
|
-
retError.put("message", "Channel URL is not set");
|
|
894
|
-
retError.put("error", "missing_config");
|
|
895
|
-
callback.callback(retError);
|
|
896
|
-
return;
|
|
897
|
-
}
|
|
898
|
-
JSONObject json = null;
|
|
899
|
-
try {
|
|
900
|
-
json = this.createInfoObject();
|
|
901
|
-
} catch (JSONException e) {
|
|
902
|
-
Log.e(TAG, "Error getChannel JSONException", e);
|
|
903
|
-
e.printStackTrace();
|
|
904
|
-
final JSObject retError = new JSObject();
|
|
905
|
-
retError.put("message", "Cannot get info: " + e.toString());
|
|
906
|
-
retError.put("error", "json_error");
|
|
907
|
-
callback.callback(retError);
|
|
908
|
-
return;
|
|
909
|
-
}
|
|
910
|
-
// Building a request
|
|
911
|
-
JsonObjectRequest request = new JsonObjectRequest(
|
|
912
|
-
Request.Method.PUT,
|
|
913
|
-
channelUrl,
|
|
914
|
-
json,
|
|
915
|
-
new Response.Listener<JSONObject>() {
|
|
916
|
-
@Override
|
|
917
|
-
public void onResponse(JSONObject res) {
|
|
918
|
-
final JSObject ret = new JSObject();
|
|
919
|
-
Iterator<String> keys = res.keys();
|
|
920
|
-
while (keys.hasNext()) {
|
|
921
|
-
String key = keys.next();
|
|
922
|
-
if (res.has(key)) {
|
|
923
|
-
try {
|
|
924
|
-
ret.put(key, res.get(key));
|
|
925
|
-
} catch (JSONException e) {
|
|
926
|
-
e.printStackTrace();
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
Log.i(TAG, "Channel get to \"" + ret);
|
|
931
|
-
callback.callback(ret);
|
|
932
|
-
}
|
|
933
|
-
},
|
|
934
|
-
new Response.ErrorListener() {
|
|
935
|
-
@Override
|
|
936
|
-
public void onErrorResponse(VolleyError error) {
|
|
937
|
-
callback.callback(
|
|
938
|
-
CapacitorUpdater.this.createError("Error get channel", error)
|
|
939
|
-
);
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
);
|
|
943
|
-
request.setRetryPolicy(
|
|
944
|
-
new DefaultRetryPolicy(
|
|
945
|
-
this.timeout,
|
|
946
|
-
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
|
|
947
|
-
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT
|
|
948
|
-
)
|
|
949
|
-
);
|
|
950
|
-
this.requestQueue.add(request);
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
public void sendStats(final String action, final String versionName) {
|
|
954
|
-
String statsUrl = this.statsUrl;
|
|
955
|
-
if (statsUrl == null || "".equals(statsUrl) || statsUrl.length() == 0) {
|
|
956
|
-
return;
|
|
957
|
-
}
|
|
958
|
-
JSONObject json = null;
|
|
959
|
-
try {
|
|
960
|
-
json = this.createInfoObject();
|
|
961
|
-
json.put("action", action);
|
|
962
|
-
} catch (JSONException e) {
|
|
963
|
-
Log.e(TAG, "Error sendStats JSONException", e);
|
|
964
|
-
e.printStackTrace();
|
|
965
|
-
return;
|
|
966
|
-
}
|
|
967
|
-
// Building a request
|
|
968
|
-
JsonObjectRequest request = new JsonObjectRequest(
|
|
969
|
-
Request.Method.POST,
|
|
970
|
-
statsUrl,
|
|
971
|
-
json,
|
|
972
|
-
new Response.Listener<JSONObject>() {
|
|
973
|
-
@Override
|
|
974
|
-
public void onResponse(JSONObject response) {
|
|
975
|
-
Log.i(
|
|
976
|
-
TAG,
|
|
977
|
-
"Stats send for \"" + action + "\", version " + versionName
|
|
978
|
-
);
|
|
979
|
-
}
|
|
980
|
-
},
|
|
981
|
-
new Response.ErrorListener() {
|
|
982
|
-
@Override
|
|
983
|
-
public void onErrorResponse(VolleyError error) {
|
|
984
|
-
CapacitorUpdater.this.createError("Error send stats", error);
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
);
|
|
988
|
-
request.setRetryPolicy(
|
|
989
|
-
new DefaultRetryPolicy(
|
|
990
|
-
this.timeout,
|
|
991
|
-
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
|
|
992
|
-
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT
|
|
993
|
-
)
|
|
994
|
-
);
|
|
995
|
-
this.requestQueue.add(request);
|
|
996
|
-
}
|
|
997
|
-
|
|
998
|
-
public BundleInfo getBundleInfo(final String id) {
|
|
999
|
-
String trueId = BundleInfo.VERSION_UNKNOWN;
|
|
1000
|
-
if (id != null) {
|
|
1001
|
-
trueId = id;
|
|
1002
|
-
}
|
|
1003
|
-
Log.d(TAG, "Getting info for bundle [" + trueId + "]");
|
|
1004
|
-
BundleInfo result;
|
|
1005
|
-
if (BundleInfo.ID_BUILTIN.equals(trueId)) {
|
|
1006
|
-
result = new BundleInfo(trueId, null, BundleStatus.SUCCESS, "", "");
|
|
1007
|
-
} else if (BundleInfo.VERSION_UNKNOWN.equals(trueId)) {
|
|
1008
|
-
result = new BundleInfo(trueId, null, BundleStatus.ERROR, "", "");
|
|
1009
|
-
} else {
|
|
1010
|
-
try {
|
|
1011
|
-
String stored = this.prefs.getString(trueId + INFO_SUFFIX, "");
|
|
1012
|
-
result = BundleInfo.fromJSON(stored);
|
|
1013
|
-
} catch (JSONException e) {
|
|
1014
|
-
Log.e(TAG, "Failed to parse info for bundle [" + trueId + "] ", e);
|
|
1015
|
-
result = new BundleInfo(trueId, null, BundleStatus.PENDING, "", "");
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1018
|
-
// Log.d(TAG, "Returning info [" + trueId + "] " + result);
|
|
1019
|
-
return result;
|
|
1020
|
-
}
|
|
1021
|
-
|
|
1022
|
-
public BundleInfo getBundleInfoByName(final String versionName) {
|
|
1023
|
-
final List<BundleInfo> installed = this.list();
|
|
1024
|
-
for (final BundleInfo i : installed) {
|
|
1025
|
-
if (i.getVersionName().equals(versionName)) {
|
|
1026
|
-
return i;
|
|
1027
|
-
}
|
|
1028
|
-
}
|
|
1029
|
-
return null;
|
|
1030
|
-
}
|
|
1031
|
-
|
|
1032
|
-
private void removeBundleInfo(final String id) {
|
|
1033
|
-
this.saveBundleInfo(id, null);
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
|
-
private void saveBundleInfo(final String id, final BundleInfo info) {
|
|
1037
|
-
if (
|
|
1038
|
-
id == null || (info != null && (info.isBuiltin() || info.isUnknown()))
|
|
1039
|
-
) {
|
|
1040
|
-
Log.d(TAG, "Not saving info for bundle: [" + id + "] " + info);
|
|
1041
|
-
return;
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
|
-
if (info == null) {
|
|
1045
|
-
Log.d(TAG, "Removing info for bundle [" + id + "]");
|
|
1046
|
-
this.editor.remove(id + INFO_SUFFIX);
|
|
1047
|
-
} else {
|
|
1048
|
-
final BundleInfo update = info.setId(id);
|
|
1049
|
-
Log.d(TAG, "Storing info for bundle [" + id + "] " + update.toString());
|
|
1050
|
-
this.editor.putString(id + INFO_SUFFIX, update.toString());
|
|
1051
|
-
}
|
|
1052
|
-
this.editor.commit();
|
|
1053
|
-
}
|
|
1054
|
-
|
|
1055
|
-
public void setVersionName(final String id, final String name) {
|
|
1056
|
-
if (id != null) {
|
|
1057
|
-
Log.d(TAG, "Setting name for bundle [" + id + "] to " + name);
|
|
1058
|
-
BundleInfo info = this.getBundleInfo(id);
|
|
1059
|
-
this.saveBundleInfo(id, info.setVersionName(name));
|
|
1060
|
-
}
|
|
1061
|
-
}
|
|
1062
|
-
|
|
1063
|
-
private void setBundleStatus(final String id, final BundleStatus status) {
|
|
1064
|
-
if (id != null && status != null) {
|
|
1065
|
-
BundleInfo info = this.getBundleInfo(id);
|
|
1066
|
-
Log.d(TAG, "Setting status for bundle [" + id + "] to " + status);
|
|
1067
|
-
this.saveBundleInfo(id, info.setStatus(status));
|
|
1068
|
-
}
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
|
-
private String getCurrentBundleId() {
|
|
1072
|
-
if (this.isUsingBuiltin()) {
|
|
1073
|
-
return BundleInfo.ID_BUILTIN;
|
|
1074
|
-
} else {
|
|
1075
|
-
final String path = this.getCurrentBundlePath();
|
|
1076
|
-
return path.substring(path.lastIndexOf('/') + 1);
|
|
1077
|
-
}
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
public BundleInfo getCurrentBundle() {
|
|
1081
|
-
return this.getBundleInfo(this.getCurrentBundleId());
|
|
1082
|
-
}
|
|
1083
|
-
|
|
1084
|
-
public String getCurrentBundlePath() {
|
|
1085
|
-
String path = this.prefs.getString(WebView.CAP_SERVER_PATH, "public");
|
|
1086
|
-
if ("".equals(path.trim())) {
|
|
1087
|
-
return "public";
|
|
1088
|
-
}
|
|
1089
|
-
return path;
|
|
1090
|
-
}
|
|
1091
|
-
|
|
1092
|
-
public Boolean isUsingBuiltin() {
|
|
1093
|
-
return this.getCurrentBundlePath().equals("public");
|
|
1094
|
-
}
|
|
1095
|
-
|
|
1096
|
-
public BundleInfo getFallbackBundle() {
|
|
1097
|
-
final String id =
|
|
1098
|
-
this.prefs.getString(FALLBACK_VERSION, BundleInfo.ID_BUILTIN);
|
|
1099
|
-
return this.getBundleInfo(id);
|
|
1100
|
-
}
|
|
1101
|
-
|
|
1102
|
-
private void setFallbackBundle(final BundleInfo fallback) {
|
|
1103
|
-
this.editor.putString(
|
|
1104
|
-
FALLBACK_VERSION,
|
|
1105
|
-
fallback == null ? BundleInfo.ID_BUILTIN : fallback.getId()
|
|
1106
|
-
);
|
|
1107
|
-
this.editor.commit();
|
|
1108
|
-
}
|
|
1109
|
-
|
|
1110
|
-
public BundleInfo getNextBundle() {
|
|
1111
|
-
final String id = this.prefs.getString(NEXT_VERSION, null);
|
|
1112
|
-
if (id == null) return null;
|
|
1113
|
-
return this.getBundleInfo(id);
|
|
1114
|
-
}
|
|
1115
|
-
|
|
1116
|
-
public boolean setNextBundle(final String next) {
|
|
1117
|
-
if (next == null) {
|
|
1118
|
-
this.editor.remove(NEXT_VERSION);
|
|
1119
|
-
} else {
|
|
1120
|
-
final BundleInfo newBundle = this.getBundleInfo(next);
|
|
1121
|
-
if (!newBundle.isBuiltin() && !this.bundleExists(next)) {
|
|
1122
|
-
return false;
|
|
1123
|
-
}
|
|
1124
|
-
this.editor.putString(NEXT_VERSION, next);
|
|
1125
|
-
this.setBundleStatus(next, BundleStatus.PENDING);
|
|
1126
|
-
}
|
|
1127
|
-
this.editor.commit();
|
|
1128
|
-
return true;
|
|
1129
|
-
}
|
|
1130
|
-
}
|