@capgo/capacitor-updater 3.2.1 → 3.3.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.
package/README.md CHANGED
@@ -43,7 +43,7 @@ Create account in [capgo.app](https://capgo.app) and get your [API key](https://
43
43
  ```
44
44
  - Add to your main code
45
45
  ```javascript
46
- import { CapacitorUpdater } from 'capacitor-updater'
46
+ import { CapacitorUpdater } from '@capgo/capacitor-updater'
47
47
  CapacitorUpdater.notifyAppReady()
48
48
  // To let auto update know you app boot well.
49
49
  ```
@@ -64,7 +64,7 @@ install it when user background the app.
64
64
  In your main code :
65
65
 
66
66
  ```javascript
67
- import { CapacitorUpdater } from 'capacitor-updater'
67
+ import { CapacitorUpdater } from '@capgo/capacitor-updater'
68
68
  import { SplashScreen } from '@capacitor/splash-screen'
69
69
  import { App } from '@capacitor/app'
70
70
 
@@ -436,7 +436,7 @@ removeAllListeners() => Promise<void>
436
436
  ### Listen to download events
437
437
 
438
438
  ```javascript
439
- import { CapacitorUpdater } from 'capacitor-updater';
439
+ import { CapacitorUpdater } from '@capgo/capacitor-updater';
440
440
 
441
441
  CapacitorUpdater.addListener('download', (info: any) => {
442
442
  console.log('download was fired', info.percent);
@@ -45,11 +45,32 @@ interface Callback {
45
45
  }
46
46
 
47
47
  public class CapacitorUpdater {
48
- public String statsUrl = "";
48
+ static final String AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
49
+ static SecureRandom rnd = new SecureRandom();
50
+ private final String TAG = "Capacitor-updater";
51
+ private final Context context;
52
+ private final String basePathHot = "versions";
53
+ private final SharedPreferences prefs;
54
+ private final SharedPreferences.Editor editor;
55
+ private String versionBuild = "";
56
+ private String versionCode = "";
57
+ private String versionOs = "";
58
+
49
59
  public String appId = "";
50
60
  public String deviceID = "";
51
- private final String pluginVersion = "3.2.1";
61
+ public final String pluginVersion = "3.3.2";
62
+ public String statsUrl = "";
52
63
 
64
+ public CapacitorUpdater (final Context context) throws PackageManager.NameNotFoundException {
65
+ this.context = context;
66
+ this.prefs = this.context.getSharedPreferences("CapWebViewSettings", Activity.MODE_PRIVATE);
67
+ this.editor = this.prefs.edit();
68
+ this.versionOs = Build.VERSION.RELEASE;
69
+ this.deviceID = Secure.getString(context.getContentResolver(), Secure.ANDROID_ID);
70
+ final PackageInfo pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
71
+ this.versionBuild = pInfo.versionName;
72
+ this.versionCode = Integer.toString(pInfo.versionCode);
73
+ }
53
74
 
54
75
  private final FilenameFilter filter = new FilenameFilter() {
55
76
  @Override
@@ -58,163 +79,126 @@ public class CapacitorUpdater {
58
79
  return !name.startsWith("__MACOSX") && !name.startsWith(".") && !name.startsWith(".DS_Store");
59
80
  }
60
81
  };
61
- private final Context context;
62
- private final CapacitorUpdaterEvents events;
63
-
64
- private String versionBuild = "";
65
- private String versionCode = "";
66
- private String versionOs = "";
67
- private final String TAG = "Capacitor-updater";
68
- private final String basePathHot = "versions";
69
- private final SharedPreferences prefs;
70
- private final SharedPreferences.Editor editor;
71
-
72
- static final String AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
73
- static SecureRandom rnd = new SecureRandom();
74
82
 
75
83
  private int calcTotalPercent(final int percent, final int min, final int max) {
76
84
  return (percent * (max - min)) / 100 + min;
77
85
  }
78
86
 
87
+ void notifyDownload(final int percent) {
88
+ return;
89
+ }
90
+
79
91
  private String randomString(final int len){
80
92
  final StringBuilder sb = new StringBuilder(len);
81
93
  for(int i = 0; i < len; i++)
82
94
  sb.append(AB.charAt(rnd.nextInt(AB.length())));
83
95
  return sb.toString();
84
96
  }
85
-
86
- public CapacitorUpdater(final Context context) throws PackageManager.NameNotFoundException {
87
- this(context, new CapacitorUpdaterEvents() {});
88
- }
89
-
90
- public CapacitorUpdater (final Context context, final CapacitorUpdaterEvents events) throws PackageManager.NameNotFoundException {
91
- this.context = context;
92
- this.events = events;
93
-
94
- this.prefs = this.context.getSharedPreferences("CapWebViewSettings", Activity.MODE_PRIVATE);
95
- this.editor = this.prefs.edit();
96
- this.versionOs = Build.VERSION.RELEASE;
97
- this.deviceID = Secure.getString(context.getContentResolver(), Secure.ANDROID_ID);
98
-
99
- final PackageInfo pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
100
- this.versionBuild = pInfo.versionName;
101
- this.versionCode = Integer.toString(pInfo.versionCode);
102
- }
103
-
104
- private Boolean unzip(final String source, final String dest) {
105
- final File zipFile = new File(this.context.getFilesDir() + "/" + source);
97
+ private File unzip(final File zipFile, final String dest) throws IOException {
106
98
  final File targetDirectory = new File(this.context.getFilesDir() + "/" + dest);
107
- ZipInputStream zis = null;
108
- try {
109
- zis = new ZipInputStream(
110
- new BufferedInputStream(new FileInputStream(zipFile)));
111
- } catch (final FileNotFoundException e) {
112
- e.printStackTrace();
113
- return false;
114
- }
99
+ final ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(zipFile)));
115
100
  try {
116
- ZipEntry ze;
117
101
  int count;
118
- final int buffLength = 8192;
119
- final byte[] buffer = new byte[buffLength];
120
- final long totalLength = zipFile.length();
121
- long readLength = buffLength;
102
+ final int bufferSize = 8192;
103
+ final byte[] buffer = new byte[bufferSize];
104
+ final long lengthTotal = zipFile.length();
105
+ long lengthRead = bufferSize;
122
106
  int percent = 0;
123
- this.events.notifyDownload(75);
124
- while ((ze = zis.getNextEntry()) != null) {
125
- final File file = new File(targetDirectory, ze.getName());
107
+ this.notifyDownload(75);
108
+
109
+ ZipEntry entry;
110
+ while ((entry = zis.getNextEntry()) != null) {
111
+ final File file = new File(targetDirectory, entry.getName());
126
112
  final String canonicalPath = file.getCanonicalPath();
127
113
  final String canonicalDir = (new File(String.valueOf(targetDirectory))).getCanonicalPath();
128
- final File dir = ze.isDirectory() ? file : file.getParentFile();
114
+ final File dir = entry.isDirectory() ? file : file.getParentFile();
115
+
129
116
  if (!canonicalPath.startsWith(canonicalDir)) {
130
117
  throw new FileNotFoundException("SecurityException, Failed to ensure directory is the start path : " +
131
118
  canonicalDir + " of " + canonicalPath);
132
119
  }
133
- if (!dir.isDirectory() && !dir.mkdirs())
120
+
121
+ if (!dir.isDirectory() && !dir.mkdirs()) {
134
122
  throw new FileNotFoundException("Failed to ensure directory: " +
135
123
  dir.getAbsolutePath());
136
- if (ze.isDirectory())
124
+ }
125
+
126
+ if (entry.isDirectory()) {
137
127
  continue;
138
- final FileOutputStream fileOut = new FileOutputStream(file);
139
- try {
128
+ }
129
+
130
+ try(final FileOutputStream outputStream = new FileOutputStream(file)) {
140
131
  while ((count = zis.read(buffer)) != -1)
141
- fileOut.write(buffer, 0, count);
142
- } finally {
143
- fileOut.close();
132
+ outputStream.write(buffer, 0, count);
144
133
  }
145
- final int newPercent = (int)((readLength * 100) / totalLength);
146
- if (totalLength > 1 && newPercent != percent) {
134
+
135
+ final int newPercent = (int)((lengthRead * 100) / lengthTotal);
136
+ if (lengthTotal > 1 && newPercent != percent) {
147
137
  percent = newPercent;
148
- this.events.notifyDownload(this.calcTotalPercent(percent, 75, 90));
138
+ this.notifyDownload(this.calcTotalPercent(percent, 75, 90));
149
139
  }
150
- readLength += ze.getCompressedSize();
140
+
141
+ lengthRead += entry.getCompressedSize();
151
142
  }
152
- } catch (final Exception e) {
153
- Log.i(this.TAG, "unzip error", e);
154
- return false;
143
+ return targetDirectory;
155
144
  } finally {
156
145
  try {
157
146
  zis.close();
158
147
  } catch (final IOException e) {
159
- e.printStackTrace();
160
- return false;
148
+ Log.e(this.TAG, "Failed to close zip input stream", e);
161
149
  }
162
- return true;
163
150
  }
164
151
  }
165
152
 
166
- private Boolean flattenAssets(final String source, final String dest) {
167
- final File current = new File(this.context.getFilesDir() + "/" + source);
168
- if (!current.exists()) {
169
- return false;
153
+ private void flattenAssets(final File sourceFile, final String dest) throws IOException {
154
+ if (!sourceFile.exists()) {
155
+ throw new FileNotFoundException("Source file not found: " + sourceFile.getPath());
170
156
  }
171
- final File fDest = new File(this.context.getFilesDir() + "/" + dest);
172
- fDest.getParentFile().mkdirs();
173
- final String[] pathsName = current.list(this.filter);
174
- if (pathsName == null || pathsName.length == 0) {
175
- return false;
157
+ final File destinationFile = new File(this.context.getFilesDir() + "/" + dest);
158
+ destinationFile.getParentFile().mkdirs();
159
+ final String[] entries = sourceFile.list(this.filter);
160
+ if (entries == null || entries.length == 0) {
161
+ throw new IOException("Source file was not a directory or was empty: " + sourceFile.getPath());
176
162
  }
177
- if (pathsName.length == 1 && !pathsName[0].equals("index.html")) {
178
- final File newFlat = new File(current.getPath() + "/" + pathsName[0]);
179
- newFlat.renameTo(fDest);
163
+ if (entries.length == 1 && !entries[0].equals("index.html")) {
164
+ final File child = new File(sourceFile.getPath() + "/" + entries[0]);
165
+ child.renameTo(destinationFile);
180
166
  } else {
181
- current.renameTo(fDest);
167
+ sourceFile.renameTo(destinationFile);
182
168
  }
183
- current.delete();
184
- return true;
169
+ sourceFile.delete();
185
170
  }
186
171
 
187
- private Boolean downloadFile(final String url, final String dest) throws JSONException {
188
- try {
189
- final URL u = new URL(url);
190
- final URLConnection uc = u.openConnection();
191
- final InputStream is = u.openStream();
192
- final DataInputStream dis = new DataInputStream(is);
193
- final long totalLength = uc.getContentLength();
194
- final int buffLength = 1024;
195
- final byte[] buffer = new byte[buffLength];
196
- int length;
197
- final File downFile = new File(this.context.getFilesDir() + "/" + dest);
198
- downFile.getParentFile().mkdirs();
199
- downFile.createNewFile();
200
- final FileOutputStream fos = new FileOutputStream(downFile);
201
- int readLength = buffLength;
202
- int percent = 0;
203
- this.events.notifyDownload(10);
204
- while ((length = dis.read(buffer))>0) {
205
- fos.write(buffer, 0, length);
206
- final int newPercent = (int)((readLength * 100) / totalLength);
207
- if (totalLength > 1 && newPercent != percent) {
208
- percent = newPercent;
209
- this.events.notifyDownload(this.calcTotalPercent(percent, 10, 70));
210
- }
211
- readLength += length;
172
+ private File downloadFile(final String url, final String dest) throws IOException {
173
+
174
+ final URL u = new URL(url);
175
+ final URLConnection connection = u.openConnection();
176
+ final InputStream is = u.openStream();
177
+ final DataInputStream dis = new DataInputStream(is);
178
+
179
+ final File target = new File(this.context.getFilesDir() + "/" + dest);
180
+ target.getParentFile().mkdirs();
181
+ target.createNewFile();
182
+ final FileOutputStream fos = new FileOutputStream(target);
183
+
184
+ final long totalLength = connection.getContentLength();
185
+ final int bufferSize = 1024;
186
+ final byte[] buffer = new byte[bufferSize];
187
+ int length;
188
+
189
+ int bytesRead = bufferSize;
190
+ int percent = 0;
191
+ this.notifyDownload(10);
192
+ while ((length = dis.read(buffer))>0) {
193
+ fos.write(buffer, 0, length);
194
+ final int newPercent = (int)((bytesRead * 100) / totalLength);
195
+ if (totalLength > 1 && newPercent != percent) {
196
+ percent = newPercent;
197
+ this.notifyDownload(this.calcTotalPercent(percent, 10, 70));
212
198
  }
213
- } catch (final Exception e) {
214
- Log.e(this.TAG, "downloadFile error", e);
215
- return false;
199
+ bytesRead += length;
216
200
  }
217
- return true;
201
+ return target;
218
202
  }
219
203
 
220
204
  private void deleteDirectory(final File file) throws IOException {
@@ -231,30 +215,22 @@ public class CapacitorUpdater {
231
215
  }
232
216
  }
233
217
 
234
- public String download(final String url) {
235
- try {
236
- this.events.notifyDownload(0);
237
- final String folderNameZip = this.randomString(10);
238
- final File fileZip = new File(this.context.getFilesDir() + "/" + folderNameZip);
239
- final String folderNameUnZip = this.randomString(10);
240
- final String version = this.randomString(10);
241
- final String folderName = this.basePathHot + "/" + version;
242
- this.events.notifyDownload(5);
243
- final Boolean downloaded = this.downloadFile(url, folderNameZip);
244
- if(!downloaded) return "";
245
- this.events.notifyDownload(71);
246
- final Boolean unzipped = this.unzip(folderNameZip, folderNameUnZip);
247
- if(!unzipped) return "";
248
- fileZip.delete();
249
- this.events.notifyDownload(91);
250
- final Boolean flatt = this.flattenAssets(folderNameUnZip, folderName);
251
- if(!flatt) return "";
252
- this.events.notifyDownload(100);
253
- return version;
254
- } catch (final Exception e) {
255
- Log.e(this.TAG, "updateApp error", e);
256
- return "";
257
- }
218
+ public String download(final String url) throws IOException {
219
+ this.notifyDownload(0);
220
+ final String path = this.randomString(10);
221
+ final File zipFile = new File(this.context.getFilesDir() + "/" + path);
222
+ final String folderNameUnZip = this.randomString(10);
223
+ final String version = this.randomString(10);
224
+ final String folderName = this.basePathHot + "/" + version;
225
+ this.notifyDownload(5);
226
+ final File downloaded = this.downloadFile(url, path);
227
+ this.notifyDownload(71);
228
+ final File unzipped = this.unzip(downloaded, folderNameUnZip);
229
+ zipFile.delete();
230
+ this.notifyDownload(91);
231
+ this.flattenAssets(unzipped, folderName);
232
+ this.notifyDownload(100);
233
+ return version;
258
234
  }
259
235
 
260
236
  public ArrayList<String> list() {
@@ -298,8 +274,8 @@ public class CapacitorUpdater {
298
274
  }
299
275
 
300
276
  public void getLatest(final String url, final Callback callback) {
301
- final String deviceID = this.deviceID;
302
- final String appId = this.appId;
277
+ final String deviceID = this.getDeviceID();
278
+ final String appId = this.getAppId();
303
279
  final String versionBuild = this.versionBuild;
304
280
  final String versionCode = this.versionCode;
305
281
  final String versionOs = this.versionOs;
@@ -313,7 +289,7 @@ public class CapacitorUpdater {
313
289
  final JSONObject jsonObject = new JSONObject(response);
314
290
  callback.callback(jsonObject);
315
291
  } catch (final JSONException e) {
316
- e.printStackTrace();
292
+ Log.e(CapacitorUpdater.this.TAG, "Error parsing JSON", e);
317
293
  }
318
294
  }
319
295
  }, new Response.ErrorListener() {
@@ -358,21 +334,21 @@ public class CapacitorUpdater {
358
334
  }
359
335
 
360
336
  public void sendStats(final String action, final String version) {
361
- if (this.statsUrl == "") { return; }
337
+ if (this.getStatsUrl() == "") { return; }
362
338
  final URL url;
363
339
  final JSONObject json = new JSONObject();
364
340
  final String jsonString;
365
341
  try {
366
- url = new URL(this.statsUrl);
342
+ url = new URL(this.getStatsUrl());
367
343
  json.put("platform", "android");
368
344
  json.put("action", action);
369
345
  json.put("version_name", version);
370
- json.put("device_id", this.deviceID);
346
+ json.put("device_id", this.getDeviceID());
371
347
  json.put("version_build", this.versionBuild);
372
348
  json.put("version_code", this.versionCode);
373
349
  json.put("version_os", this.versionOs);
374
350
  json.put("plugin_version", this.pluginVersion);
375
- json.put("app_id", this.appId);
351
+ json.put("app_id", this.getAppId());
376
352
  jsonString = json.toString();
377
353
  } catch (final Exception ex) {
378
354
  Log.e(this.TAG, "Error get stats", ex);
@@ -409,4 +385,28 @@ public class CapacitorUpdater {
409
385
  }
410
386
  }).start();
411
387
  }
388
+
389
+ public String getStatsUrl() {
390
+ return this.statsUrl;
391
+ }
392
+
393
+ public void setStatsUrl(final String statsUrl) {
394
+ this.statsUrl = statsUrl;
395
+ }
396
+
397
+ public String getAppId() {
398
+ return this.appId;
399
+ }
400
+
401
+ public void setAppId(final String appId) {
402
+ this.appId = appId;
403
+ }
404
+
405
+ public String getDeviceID() {
406
+ return this.deviceID;
407
+ }
408
+
409
+ public void setDeviceID(final String deviceID) {
410
+ this.deviceID = deviceID;
411
+ }
412
412
  }
@@ -5,13 +5,11 @@ import android.app.Application;
5
5
  import android.content.SharedPreferences;
6
6
  import android.content.pm.PackageInfo;
7
7
  import android.content.pm.PackageManager;
8
- import android.os.Build;
9
8
  import android.os.Bundle;
10
9
  import android.util.Log;
11
10
 
12
11
  import androidx.annotation.NonNull;
13
12
  import androidx.annotation.Nullable;
14
- import androidx.annotation.RequiresApi;
15
13
 
16
14
  import com.getcapacitor.CapConfig;
17
15
  import com.getcapacitor.JSArray;
@@ -29,42 +27,43 @@ import java.util.ArrayList;
29
27
 
30
28
  @CapacitorPlugin(name = "CapacitorUpdater")
31
29
  public class CapacitorUpdaterPlugin extends Plugin implements Application.ActivityLifecycleCallbacks {
30
+ private static final String autoUpdateUrlDefault = "https://capgo.app/api/auto_update";
31
+ private static final String statsUrlDefault = "https://capgo.app/api/stats";
32
32
  private final String TAG = "Capacitor-updater";
33
33
  private CapacitorUpdater implementation;
34
+
34
35
  private SharedPreferences prefs;
35
36
  private SharedPreferences.Editor editor;
36
- private static final String autoUpdateUrlDefault = "https://capgo.app/api/auto_update";
37
- private static final String statsUrlDefault = "https://capgo.app/api/stats";
37
+
38
38
  private String autoUpdateUrl = "";
39
39
  private Version currentVersionNative;
40
40
  private Boolean autoUpdate = false;
41
41
  private Boolean resetWhenUpdate = true;
42
42
 
43
-
44
43
  @Override
45
44
  public void load() {
46
45
  super.load();
47
46
  this.prefs = this.getContext().getSharedPreferences("CapWebViewSettings", Activity.MODE_PRIVATE);
48
47
  this.editor = this.prefs.edit();
49
48
  try {
50
- this.implementation = new CapacitorUpdater(this.getContext(), new CapacitorUpdaterEvents() {
49
+ this.implementation = new CapacitorUpdater(this.getContext()) {
51
50
  @Override
52
51
  public void notifyDownload(final int percent) {
53
- CapacitorUpdaterPlugin.this.notifyDownload(percent);
52
+ this.notifyDownload(percent);
54
53
  }
55
- });
54
+ };
56
55
  final PackageInfo pInfo = this.getContext().getPackageManager().getPackageInfo(this.getContext().getPackageName(), 0);
57
56
  this.currentVersionNative = new Version(pInfo.versionName);
58
57
  } catch (final PackageManager.NameNotFoundException e) {
59
- e.printStackTrace();
58
+ Log.e(this.TAG, "Error instantiating implementation", e);
60
59
  return;
61
60
  } catch (final Exception ex) {
62
- Log.e(this.TAG, "Error get currentVersionNative", ex);
61
+ Log.e(this.TAG, "Error getting current native app version", ex);
63
62
  return;
64
63
  }
65
64
  final CapConfig config = CapConfig.loadDefault(this.getActivity());
66
- this.implementation.appId = config.getString("appId", "");
67
- this.implementation.statsUrl = this.getConfig().getString("statsUrl", statsUrlDefault);
65
+ this.implementation.setAppId(config.getString("appId", ""));
66
+ this.implementation.setStatsUrl(this.getConfig().getString("statsUrl", statsUrlDefault));
68
67
  this.autoUpdateUrl = this.getConfig().getString("autoUpdateUrl", autoUpdateUrlDefault);
69
68
  this.autoUpdate = this.getConfig().getBoolean("autoUpdate", false);
70
69
  this.resetWhenUpdate = this.getConfig().getBoolean("resetWhenUpdate", true);
@@ -78,16 +77,18 @@ public class CapacitorUpdaterPlugin extends Plugin implements Application.Activi
78
77
  final ArrayList<String> res = this.implementation.list();
79
78
  for (int i = 0; i < res.size(); i++) {
80
79
  try {
81
- this.implementation.delete(res.get(i), "");
80
+ final String version = res.get(i);
81
+ this.implementation.delete(version, "");
82
+ Log.i(this.TAG, "Deleted obsolete version: " + version);
82
83
  } catch (final IOException e) {
83
- e.printStackTrace();
84
+ Log.e(CapacitorUpdaterPlugin.this.TAG, "error deleting version", e);
84
85
  }
85
86
  }
86
87
  }
87
88
  this.editor.putString("LatestVersionNative", this.currentVersionNative.toString());
88
89
  this.editor.commit();
89
90
  } catch (final Exception ex) {
90
- Log.e("CapacitorUpdater", "Cannot get the current version " + ex.getMessage());
91
+ Log.e(this.TAG, "Cannot get the current version " + ex.getMessage());
91
92
  }
92
93
  }
93
94
  if (!this.autoUpdate || this.autoUpdateUrl.equals("")) return;
@@ -105,7 +106,14 @@ public class CapacitorUpdaterPlugin extends Plugin implements Application.Activi
105
106
  @PluginMethod
106
107
  public void getId(final PluginCall call) {
107
108
  final JSObject ret = new JSObject();
108
- ret.put("id", this.implementation.deviceID);
109
+ ret.put("id", this.implementation.getDeviceID());
110
+ call.resolve(ret);
111
+ }
112
+
113
+ @PluginMethod
114
+ public void getPluginVersion(final PluginCall call) {
115
+ final JSObject ret = new JSObject();
116
+ ret.put("version", this.implementation.pluginVersion);
109
117
  call.resolve(ret);
110
118
  }
111
119
 
@@ -114,14 +122,15 @@ public class CapacitorUpdaterPlugin extends Plugin implements Application.Activi
114
122
  new Thread(new Runnable(){
115
123
  @Override
116
124
  public void run() {
117
- final String url = call.getString("url");
118
- final String res = CapacitorUpdaterPlugin.this.implementation.download(url);
119
- if (!res.equals("")) {
125
+ try {
126
+ final String url = call.getString("url");
127
+ final String version = CapacitorUpdaterPlugin.this.implementation.download(url);
120
128
  final JSObject ret = new JSObject();
121
- ret.put("version", res);
129
+ ret.put("version", version);
122
130
  call.resolve(ret);
123
- } else {
124
- call.reject("download failed");
131
+ } catch (final IOException e) {
132
+ Log.e(CapacitorUpdaterPlugin.this.TAG, "download failed", e);
133
+ call.reject("download failed", e);
125
134
  }
126
135
  }
127
136
  }).start();
@@ -132,7 +141,7 @@ public class CapacitorUpdaterPlugin extends Plugin implements Application.Activi
132
141
  this.bridge.setServerBasePath(pathHot);
133
142
  return true;
134
143
  }
135
-
144
+
136
145
  @PluginMethod
137
146
  public void reload(final PluginCall call) {
138
147
  if (this._reload()) {
@@ -166,7 +175,7 @@ public class CapacitorUpdaterPlugin extends Plugin implements Application.Activi
166
175
  call.reject("Delete failed, version " + version + " doesn't exist");
167
176
  }
168
177
  } catch(final IOException ex) {
169
- Log.e("CapacitorUpdater", "An unexpected error occurred during deletion of folder. Message: " + ex.getMessage());
178
+ Log.e(this.TAG, "An unexpected error occurred during deletion of folder. Message: " + ex.getMessage());
170
179
  call.reject("An unexpected error occurred during deletion of folder.");
171
180
  }
172
181
  }
@@ -247,17 +256,8 @@ public class CapacitorUpdaterPlugin extends Plugin implements Application.Activi
247
256
  @Override
248
257
  public void onActivityStarted(@NonNull final Activity activity) {
249
258
  // disableRevert disableBreaking
250
- String currentVersionNative = "";
251
- try {
252
- final PackageInfo pInfo = this.getContext().getPackageManager().getPackageInfo(this.getContext().getPackageName(), 0);
253
- currentVersionNative = pInfo.versionName;
254
- } catch (final Exception ex) {
255
- Log.e(this.TAG, "Error get stats", ex);
256
- return;
257
- }
258
259
  Log.i(this.TAG, "Check for update in the server");
259
260
  if (this.autoUpdateUrl.equals("")) return;
260
- final String finalCurrentVersionNative = currentVersionNative;
261
261
  new Thread(new Runnable(){
262
262
  @Override
263
263
  public void run() {
@@ -282,7 +282,8 @@ public class CapacitorUpdaterPlugin extends Plugin implements Application.Activi
282
282
  @Override
283
283
  public void run() {
284
284
  try {
285
- final String dl = CapacitorUpdaterPlugin.this.implementation.download((String) res.get("url"));
285
+ final String url = (String) res.get("url");
286
+ final String dl = CapacitorUpdaterPlugin.this.implementation.download(url);
286
287
  if (dl.equals("")) {
287
288
  Log.i(CapacitorUpdaterPlugin.this.TAG, "Download version: " + newVersion + " failed");
288
289
  return;
@@ -292,8 +293,8 @@ public class CapacitorUpdaterPlugin extends Plugin implements Application.Activi
292
293
  CapacitorUpdaterPlugin.this.editor.putString("nextVersionName", (String) res.get("version"));
293
294
  CapacitorUpdaterPlugin.this.editor.commit();
294
295
  CapacitorUpdaterPlugin.this.notifyListeners("updateAvailable", ret);
295
- } catch (final JSONException e) {
296
- e.printStackTrace();
296
+ } catch (final Exception e) {
297
+ Log.e(CapacitorUpdaterPlugin.this.TAG, "error downloading file", e);
297
298
  }
298
299
  }
299
300
  }).start();
@@ -301,7 +302,7 @@ public class CapacitorUpdaterPlugin extends Plugin implements Application.Activi
301
302
  Log.i(CapacitorUpdaterPlugin.this.TAG, "No need to update, " + currentVersion + " is the latest");
302
303
  }
303
304
  } catch (final JSONException e) {
304
- e.printStackTrace();
305
+ Log.e(CapacitorUpdaterPlugin.this.TAG, "error parsing JSON", e);
305
306
  }
306
307
  });
307
308
  }
@@ -371,20 +372,20 @@ public class CapacitorUpdaterPlugin extends Plugin implements Application.Activi
371
372
  try {
372
373
  final Boolean res = this.implementation.delete(curVersion, curVersionName);
373
374
  if (res) {
374
- Log.i(this.TAG, "Delete failing version: " + curVersionName);
375
+ Log.i(this.TAG, "Deleted failing version: " + curVersionName);
375
376
  }
376
377
  } catch (final IOException e) {
377
- e.printStackTrace();
378
+ Log.e(CapacitorUpdaterPlugin.this.TAG, "error deleting version", e);
378
379
  }
379
380
  } else if (!pastVersion.equals("")) {
380
381
  Log.i(this.TAG, "Validated version: " + curVersionName);
381
382
  try {
382
383
  final Boolean res = this.implementation.delete(pastVersion, pastVersionName);
383
384
  if (res) {
384
- Log.i(this.TAG, "Delete past version: " + pastVersionName);
385
+ Log.i(this.TAG, "Deleted past version: " + pastVersionName);
385
386
  }
386
387
  } catch (final IOException e) {
387
- e.printStackTrace();
388
+ Log.e(CapacitorUpdaterPlugin.this.TAG, "error deleting version", e);
388
389
  }
389
390
  this.editor.putString("pastVersion", "");
390
391
  this.editor.putString("pastVersionName", "");
@@ -36,13 +36,33 @@ extension Bundle {
36
36
  }
37
37
  }
38
38
 
39
+ enum CustomError: Error {
40
+ // Throw when an unzip fail
41
+ case cannotUnzip
42
+
43
+ // Throw in all other cases
44
+ case unexpected(code: Int)
45
+ }
46
+
47
+ extension CustomError: LocalizedError {
48
+ public var errorDescription: String? {
49
+ switch self {
50
+ case .cannotUnzip:
51
+ return NSLocalizedString(
52
+ "The file cannot be unzip",
53
+ comment: "Invalid zip"
54
+ )
55
+ case .unexpected(_):
56
+ return NSLocalizedString(
57
+ "An unexpected error occurred.",
58
+ comment: "Unexpected Error"
59
+ )
60
+ }
61
+ }
62
+ }
63
+
39
64
  @objc public class CapacitorUpdater: NSObject {
40
65
 
41
- public var statsUrl = ""
42
- public var appId = ""
43
- public var deviceID = UIDevice.current.identifierForVendor?.uuidString ?? ""
44
- public var notifyDownload: (Int) -> Void = { _ in }
45
- public var pluginVersion = "3.2.1"
46
66
  private var versionBuild = Bundle.main.releaseVersionNumber ?? ""
47
67
  private var versionCode = Bundle.main.buildVersionNumber ?? ""
48
68
  private var versionOs = ProcessInfo().operatingSystemVersion.getFullVersion()
@@ -53,11 +73,17 @@ extension Bundle {
53
73
  private let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
54
74
  private let libraryUrl = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first!
55
75
 
56
- @objc private func calcTotalPercent(percent: Int, min: Int, max: Int) -> Int {
76
+ public var statsUrl = ""
77
+ public var appId = ""
78
+ public var deviceID = UIDevice.current.identifierForVendor?.uuidString ?? ""
79
+ public var notifyDownload: (Int) -> Void = { _ in }
80
+ public var pluginVersion = "3.2.0"
81
+
82
+ private func calcTotalPercent(percent: Int, min: Int, max: Int) -> Int {
57
83
  return (percent * (max - min)) / 100 + min;
58
84
  }
59
85
 
60
- @objc private func randomString(length: Int) -> String {
86
+ private func randomString(length: Int) -> String {
61
87
  let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
62
88
  return String((0..<length).map{ _ in letters.randomElement()! })
63
89
  }
@@ -101,17 +127,19 @@ extension Bundle {
101
127
  }
102
128
  }
103
129
 
104
- private func saveDownloaded(sourceZip: URL, version: String, base: URL) {
130
+ private func saveDownloaded(sourceZip: URL, version: String, base: URL) throws {
105
131
  prepareFolder(source: base)
106
132
  let destHot = base.appendingPathComponent(version)
107
133
  let destUnZip = documentsUrl.appendingPathComponent(randomString(length: 10))
108
- SSZipArchive.unzipFile(atPath: sourceZip.path, toDestination: destUnZip.path)
134
+ if (!SSZipArchive.unzipFile(atPath: sourceZip.path, toDestination: destUnZip.path)) {
135
+ throw CustomError.cannotUnzip
136
+ }
109
137
  if (unflatFolder(source: destUnZip, dest: destHot)) {
110
138
  deleteFolder(source: destUnZip)
111
139
  }
112
140
  }
113
141
 
114
- @objc public func getLatest(url: URL) -> AppVersion? {
142
+ public func getLatest(url: URL) -> AppVersion? {
115
143
  let semaphore = DispatchSemaphore(value: 0)
116
144
  let latest = AppVersion()
117
145
  let headers: HTTPHeaders = [
@@ -150,9 +178,10 @@ extension Bundle {
150
178
  return latest.url != "" ? latest : nil
151
179
  }
152
180
 
153
- @objc public func download(url: URL) -> String? {
181
+ public func download(url: URL) throws -> String {
154
182
  let semaphore = DispatchSemaphore(value: 0)
155
- var version: String? = nil
183
+ var version: String = ""
184
+ var mainError: NSError? = nil
156
185
  let destination: DownloadRequest.Destination = { _, _ in
157
186
  let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
158
187
  let fileURL = documentsURL.appendingPathComponent(self.randomString(length: 10))
@@ -171,24 +200,32 @@ extension Bundle {
171
200
  case .success:
172
201
  self.notifyDownload(71);
173
202
  version = self.randomString(length: 10)
174
- self.saveDownloaded(sourceZip: fileURL, version: version!, base: self.documentsUrl.appendingPathComponent(self.basePathHot))
175
- self.notifyDownload(85);
176
- self.saveDownloaded(sourceZip: fileURL, version: version!, base: self.libraryUrl.appendingPathComponent(self.basePathPersist))
177
- self.notifyDownload(100);
178
- self.deleteFolder(source: fileURL)
203
+ do {
204
+ try self.saveDownloaded(sourceZip: fileURL, version: version, base: self.documentsUrl.appendingPathComponent(self.basePathHot))
205
+ self.notifyDownload(85);
206
+ try self.saveDownloaded(sourceZip: fileURL, version: version, base: self.libraryUrl.appendingPathComponent(self.basePathPersist))
207
+ self.notifyDownload(100);
208
+ self.deleteFolder(source: fileURL)
209
+ } catch {
210
+ print("✨ Capacitor-updater: download unzip error", error)
211
+ mainError = error as NSError
212
+ }
179
213
  case let .failure(error):
180
214
  print("✨ Capacitor-updater: download error", error)
181
- version = nil
215
+ mainError = error as NSError
182
216
  }
183
217
  }
184
218
  semaphore.signal()
185
219
  }
186
220
  self.notifyDownload(0);
187
221
  semaphore.wait()
222
+ if (mainError != nil) {
223
+ throw mainError!
224
+ }
188
225
  return version
189
226
  }
190
227
 
191
- @objc public func list() -> [String] {
228
+ public func list() -> [String] {
192
229
  let dest = documentsUrl.appendingPathComponent(basePathHot)
193
230
  do {
194
231
  let files = try FileManager.default.contentsOfDirectory(atPath: dest.path)
@@ -199,7 +236,7 @@ extension Bundle {
199
236
  }
200
237
  }
201
238
 
202
- @objc public func delete(version: String, versionName: String) -> Bool {
239
+ public func delete(version: String, versionName: String) -> Bool {
203
240
  let destHot = documentsUrl.appendingPathComponent(basePathHot).appendingPathComponent(version)
204
241
  let destPersist = libraryUrl.appendingPathComponent(basePathPersist).appendingPathComponent(version)
205
242
  do {
@@ -217,7 +254,7 @@ extension Bundle {
217
254
  return true
218
255
  }
219
256
 
220
- @objc public func set(version: String, versionName: String) -> Bool {
257
+ public func set(version: String, versionName: String) -> Bool {
221
258
  let destHot = documentsUrl.appendingPathComponent(basePathHot).appendingPathComponent(version)
222
259
  let indexHot = destHot.appendingPathComponent("index.html")
223
260
  let destHotPersist = libraryUrl.appendingPathComponent(basePathPersist).appendingPathComponent(version)
@@ -233,19 +270,19 @@ extension Bundle {
233
270
  return false
234
271
  }
235
272
 
236
- @objc public func getLastPathHot() -> String {
273
+ public func getLastPathHot() -> String {
237
274
  return UserDefaults.standard.string(forKey: "lastPathHot") ?? ""
238
275
  }
239
276
 
240
- @objc public func getVersionName() -> String {
277
+ public func getVersionName() -> String {
241
278
  return UserDefaults.standard.string(forKey: "versionName") ?? ""
242
279
  }
243
280
 
244
- @objc public func getLastPathPersist() -> String {
281
+ public func getLastPathPersist() -> String {
245
282
  return UserDefaults.standard.string(forKey: "lastPathPersist") ?? ""
246
283
  }
247
284
 
248
- @objc public func reset() {
285
+ public func reset() {
249
286
  let version = UserDefaults.standard.string(forKey: "versionName") ?? ""
250
287
  sendStats(action: "reset", version: version)
251
288
  UserDefaults.standard.set("", forKey: "lastPathHot")
@@ -254,7 +291,7 @@ extension Bundle {
254
291
  UserDefaults.standard.synchronize()
255
292
  }
256
293
 
257
- @objc func sendStats(action: String, version: String) {
294
+ func sendStats(action: String, version: String) {
258
295
  if (statsUrl == "") { return }
259
296
  let parameters: [String: String] = [
260
297
  "platform": "ios",
@@ -15,4 +15,5 @@ CAP_PLUGIN(CapacitorUpdaterPlugin, "CapacitorUpdater",
15
15
  CAP_PLUGIN_METHOD(notifyAppReady, CAPPluginReturnPromise);
16
16
  CAP_PLUGIN_METHOD(delayUpdate, CAPPluginReturnPromise);
17
17
  CAP_PLUGIN_METHOD(getId, CAPPluginReturnPromise);
18
+ CAP_PLUGIN_METHOD(getPluginVersion, CAPPluginReturnPromise);
18
19
  )
@@ -65,16 +65,20 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
65
65
  @objc func getId(_ call: CAPPluginCall) {
66
66
  call.resolve(["id": implementation.deviceID])
67
67
  }
68
+
69
+ @objc func getPluginVersion(_ call: CAPPluginCall) {
70
+ call.resolve(["version": implementation.pluginVersion])
71
+ }
68
72
 
69
73
  @objc func download(_ call: CAPPluginCall) {
70
74
  let url = URL(string: call.getString("url") ?? "")
71
- let res = implementation.download(url: url!)
72
- if ((res) != nil) {
75
+ do {
76
+ let res = try implementation.download(url: url!)
73
77
  call.resolve([
74
- "version": res!
78
+ "version": res
75
79
  ])
76
- } else {
77
- call.reject("download failed")
80
+ } catch {
81
+ call.reject("download failed", error.localizedDescription)
78
82
  }
79
83
  }
80
84
 
@@ -221,14 +225,14 @@ public class CapacitorUpdaterPlugin: CAPPlugin {
221
225
  print("✨ Capacitor-updater: Cannot get version \(failingVersion) \(newVersion)")
222
226
  }
223
227
  if (newVersion != "0.0.0" && newVersion != failingVersion) {
224
- let dlOp = self.implementation.download(url: downloadUrl)
225
- if let dl = dlOp {
228
+ do {
229
+ let dl = try self.implementation.download(url: downloadUrl)
226
230
  print("✨ Capacitor-updater: New version: \(newVersion) found. Current is \(currentVersion == "" ? "builtin" : currentVersion), next backgrounding will trigger update")
227
231
  UserDefaults.standard.set(dl, forKey: "nextVersion")
228
232
  UserDefaults.standard.set(newVersion.description, forKey: "nextVersionName")
229
233
  self.notifyListeners("updateAvailable", data: ["version": newVersion])
230
- } else {
231
- print("✨ Capacitor-updater: Download version \(newVersion) fail")
234
+ } catch {
235
+ print("✨ Capacitor-updater: Download version \(newVersion) fail", error.localizedDescription)
232
236
  }
233
237
  } else {
234
238
  print("✨ Capacitor-updater: No need to update, \(currentVersion) is the latest")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-updater",
3
- "version": "3.2.1",
3
+ "version": "3.3.2",
4
4
  "license": "AGPL-3.0-only",
5
5
  "description": "OTA update for capacitor apps",
6
6
  "main": "dist/plugin.cjs.js",
@@ -1,11 +0,0 @@
1
- package ee.forgr.capacitor_updater;
2
-
3
- public interface CapacitorUpdaterEvents {
4
- /**
5
- * Notify listeners of download progress.
6
- * @param percent Current percentage as an integer (e.g.: N out of 100)
7
- */
8
- default void notifyDownload(final int percent) {
9
- return;
10
- }
11
- }