@capgo/capacitor-updater 3.2.0 → 3.2.1-alpha.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.
@@ -1,27 +1,20 @@
1
1
  package ee.forgr.capacitor_updater;
2
2
 
3
- import android.app.Activity;
4
- import android.content.Context;
5
3
  import android.content.SharedPreferences;
6
- import android.content.pm.PackageInfo;
7
- import android.content.pm.PackageManager;
8
- import android.os.Build;
9
4
  import android.util.Log;
10
5
 
11
- import com.android.volley.AuthFailureError;
12
6
  import com.android.volley.Request;
13
7
  import com.android.volley.RequestQueue;
14
8
  import com.android.volley.Response;
15
9
  import com.android.volley.VolleyError;
16
- import com.android.volley.toolbox.StringRequest;
17
- import com.android.volley.toolbox.Volley;
10
+ import com.android.volley.toolbox.JsonObjectRequest;
11
+ import com.getcapacitor.plugin.WebView;
18
12
 
19
13
  import org.json.JSONException;
20
14
  import org.json.JSONObject;
21
15
 
22
16
  import java.io.BufferedInputStream;
23
17
  import java.io.DataInputStream;
24
- import java.io.DataOutputStream;
25
18
  import java.io.File;
26
19
  import java.io.FileInputStream;
27
20
  import java.io.FileNotFoundException;
@@ -29,387 +22,509 @@ import java.io.FileOutputStream;
29
22
  import java.io.FilenameFilter;
30
23
  import java.io.IOException;
31
24
  import java.io.InputStream;
32
- import java.net.HttpURLConnection;
33
25
  import java.net.URL;
34
26
  import java.net.URLConnection;
35
27
  import java.security.SecureRandom;
36
- import java.util.HashMap;
37
- import java.util.Map;
28
+ import java.util.ArrayList;
29
+ import java.util.Date;
30
+ import java.util.List;
38
31
  import java.util.zip.ZipEntry;
39
32
  import java.util.zip.ZipInputStream;
40
- import java.util.ArrayList;
41
- import android.provider.Settings.Secure;
42
33
 
43
34
  interface Callback {
44
35
  void callback(JSONObject jsonObject);
45
36
  }
46
37
 
47
38
  public class CapacitorUpdater {
39
+ private static final String AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
40
+ private static final SecureRandom rnd = new SecureRandom();
41
+
42
+ private static final String INFO_SUFFIX = "_info";
43
+
44
+ private static final String FALLBACK_VERSION = "pastVersion";
45
+ private static final String NEXT_VERSION = "nextVersion";
46
+ private static final String bundleDirectory = "versions";
47
+
48
+ public static final String TAG = "Capacitor-updater";
49
+ public static final String pluginVersion = "3.2.1-alpha.0";
50
+
51
+ public SharedPreferences.Editor editor;
52
+ public SharedPreferences prefs;
53
+
54
+ public RequestQueue requestQueue;
55
+
56
+ public File documentsDir;
57
+ public String versionBuild = "";
58
+ public String versionCode = "";
59
+ public String versionOs = "";
60
+
48
61
  public String statsUrl = "";
49
62
  public String appId = "";
50
63
  public String deviceID = "";
51
- private String pluginVersion = "3.2.0";
52
64
 
53
-
54
- private FilenameFilter filter = new FilenameFilter() {
65
+ private final FilenameFilter filter = new FilenameFilter() {
55
66
  @Override
56
- public boolean accept(File f, String name) {
67
+ public boolean accept(final File f, final String name) {
57
68
  // ignore directories generated by mac os x
58
69
  return !name.startsWith("__MACOSX") && !name.startsWith(".") && !name.startsWith(".DS_Store");
59
70
  }
60
71
  };
61
- private final CapacitorUpdaterPlugin plugin;
62
- private String versionBuild = "";
63
- private String versionCode = "";
64
- private String versionOs = "";
65
- private String TAG = "Capacitor-updater";
66
- private Context context;
67
- private String basePathHot = "versions";
68
- private SharedPreferences prefs;
69
- private SharedPreferences.Editor editor;
70
-
71
- static final String AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
72
- static SecureRandom rnd = new SecureRandom();
73
-
74
- private int calcTotalPercent(int percent, int min, int max) {
72
+
73
+ private int calcTotalPercent(final int percent, final int min, final int max) {
75
74
  return (percent * (max - min)) / 100 + min;
76
75
  }
77
76
 
78
- private String randomString(int len){
79
- StringBuilder sb = new StringBuilder(len);
77
+ void notifyDownload(final String id, final int percent) {
78
+ return;
79
+ }
80
+
81
+ private String randomString(final int len){
82
+ final StringBuilder sb = new StringBuilder(len);
80
83
  for(int i = 0; i < len; i++)
81
84
  sb.append(AB.charAt(rnd.nextInt(AB.length())));
82
85
  return sb.toString();
83
86
  }
84
- public CapacitorUpdater (Context context) throws PackageManager.NameNotFoundException {
85
- this.context = context;
86
- this.plugin = new CapacitorUpdaterPlugin();
87
- this.prefs = context.getSharedPreferences("CapWebViewSettings", Activity.MODE_PRIVATE);
88
- this.editor = prefs.edit();
89
- this.versionOs = Build.VERSION.RELEASE;
90
- this.deviceID = Secure.getString(context.getContentResolver(), Secure.ANDROID_ID);
91
- PackageInfo pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
92
- this.versionBuild = pInfo.versionName;
93
- this.versionCode = Integer.toString(pInfo.versionCode);
94
- }
95
- public CapacitorUpdater (Context context, CapacitorUpdaterPlugin plugin) throws PackageManager.NameNotFoundException {
96
- this.context = context;
97
- this.plugin = plugin;
98
- this.prefs = context.getSharedPreferences("CapWebViewSettings", Activity.MODE_PRIVATE);
99
- this.editor = prefs.edit();
100
- this.versionOs = Build.VERSION.RELEASE;
101
- this.deviceID = Secure.getString(context.getContentResolver(), Secure.ANDROID_ID);
102
- PackageInfo pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
103
- this.versionBuild = pInfo.versionName;
104
- this.versionCode = Integer.toString(pInfo.versionCode);
105
- }
106
-
107
- private Boolean unzip(String source, String dest) {
108
- File zipFile = new File(this.context.getFilesDir() + "/" + source);
109
- File targetDirectory = new File(this.context.getFilesDir() + "/" + dest);
110
- ZipInputStream zis = null;
111
- try {
112
- zis = new ZipInputStream(
113
- new BufferedInputStream(new FileInputStream(zipFile)));
114
- } catch (FileNotFoundException e) {
115
- e.printStackTrace();
116
- return false;
117
- }
87
+
88
+ private File unzip(final String id, final File zipFile, final String dest) throws IOException {
89
+ final File targetDirectory = new File(this.documentsDir, dest);
90
+ final ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(zipFile)));
118
91
  try {
119
- ZipEntry ze;
120
92
  int count;
121
- int buffLength = 8192;
122
- byte[] buffer = new byte[buffLength];
123
- long totalLength = zipFile.length();
124
- long readLength = buffLength;
93
+ final int bufferSize = 8192;
94
+ final byte[] buffer = new byte[bufferSize];
95
+ final long lengthTotal = zipFile.length();
96
+ long lengthRead = bufferSize;
125
97
  int percent = 0;
126
- this.plugin.notifyDownload(75);
127
- while ((ze = zis.getNextEntry()) != null) {
128
- File file = new File(targetDirectory, ze.getName());
129
- String canonicalPath = file.getCanonicalPath();
130
- String canonicalDir = (new File(String.valueOf(targetDirectory))).getCanonicalPath();
131
- File dir = ze.isDirectory() ? file : file.getParentFile();
98
+ this.notifyDownload(id, 75);
99
+
100
+ ZipEntry entry;
101
+ while ((entry = zis.getNextEntry()) != null) {
102
+ final File file = new File(targetDirectory, entry.getName());
103
+ final String canonicalPath = file.getCanonicalPath();
104
+ final String canonicalDir = (new File(String.valueOf(targetDirectory))).getCanonicalPath();
105
+ final File dir = entry.isDirectory() ? file : file.getParentFile();
106
+
132
107
  if (!canonicalPath.startsWith(canonicalDir)) {
133
108
  throw new FileNotFoundException("SecurityException, Failed to ensure directory is the start path : " +
134
109
  canonicalDir + " of " + canonicalPath);
135
110
  }
136
- if (!dir.isDirectory() && !dir.mkdirs())
111
+
112
+ if (!dir.isDirectory() && !dir.mkdirs()) {
137
113
  throw new FileNotFoundException("Failed to ensure directory: " +
138
114
  dir.getAbsolutePath());
139
- if (ze.isDirectory())
115
+ }
116
+
117
+ if (entry.isDirectory()) {
140
118
  continue;
141
- FileOutputStream fileOut = new FileOutputStream(file);
142
- try {
119
+ }
120
+
121
+ try(final FileOutputStream outputStream = new FileOutputStream(file)) {
143
122
  while ((count = zis.read(buffer)) != -1)
144
- fileOut.write(buffer, 0, count);
145
- } finally {
146
- fileOut.close();
123
+ outputStream.write(buffer, 0, count);
147
124
  }
148
- int newPercent = (int)((readLength * 100) / totalLength);
149
- if (totalLength > 1 && newPercent != percent) {
125
+
126
+ final int newPercent = (int)((lengthRead * 100) / lengthTotal);
127
+ if (lengthTotal > 1 && newPercent != percent) {
150
128
  percent = newPercent;
151
- this.plugin.notifyDownload(calcTotalPercent((int)percent, 75, 90));
129
+ this.notifyDownload(id, this.calcTotalPercent(percent, 75, 90));
152
130
  }
153
- readLength += ze.getCompressedSize();
131
+
132
+ lengthRead += entry.getCompressedSize();
154
133
  }
155
- } catch (Exception e) {
156
- Log.i(TAG, "unzip error", e);
157
- return false;
134
+ return targetDirectory;
158
135
  } finally {
159
136
  try {
160
137
  zis.close();
161
- } catch (IOException e) {
162
- e.printStackTrace();
163
- return false;
138
+ } catch (final IOException e) {
139
+ Log.e(TAG, "Failed to close zip input stream", e);
164
140
  }
165
- return true;
166
141
  }
167
142
  }
168
143
 
169
- private Boolean flattenAssets(String source, String dest) {
170
- File current = new File(this.context.getFilesDir() + "/" + source);
171
- if (!current.exists()) {
172
- return false;
144
+ private void flattenAssets(final File sourceFile, final String dest) throws IOException {
145
+ if (!sourceFile.exists()) {
146
+ throw new FileNotFoundException("Source file not found: " + sourceFile.getPath());
173
147
  }
174
- File fDest = new File(this.context.getFilesDir() + "/" + dest);
175
- fDest.getParentFile().mkdirs();
176
- String[] pathsName = current.list(filter);
177
- if (pathsName == null || pathsName.length == 0) {
178
- return false;
148
+ final File destinationFile = new File(this.documentsDir, dest);
149
+ destinationFile.getParentFile().mkdirs();
150
+ final String[] entries = sourceFile.list(this.filter);
151
+ if (entries == null || entries.length == 0) {
152
+ throw new IOException("Source file was not a directory or was empty: " + sourceFile.getPath());
179
153
  }
180
- if (pathsName.length == 1 && !pathsName[0].equals("index.html")) {
181
- File newFlat = new File(current.getPath() + "/" + pathsName[0]);
182
- newFlat.renameTo(fDest);
154
+ if (entries.length == 1 && !"index.html".equals(entries[0])) {
155
+ final File child = new File(sourceFile, entries[0]);
156
+ child.renameTo(destinationFile);
183
157
  } else {
184
- current.renameTo(fDest);
158
+ sourceFile.renameTo(destinationFile);
185
159
  }
186
- current.delete();
187
- return true;
160
+ sourceFile.delete();
188
161
  }
189
162
 
190
- private Boolean downloadFile(String url, String dest) throws JSONException {
191
- try {
192
- URL u = new URL(url);
193
- URLConnection uc = u.openConnection();
194
- InputStream is = u.openStream();
195
- DataInputStream dis = new DataInputStream(is);
196
- long totalLength = uc.getContentLength();
197
- int buffLength = 1024;
198
- byte[] buffer = new byte[buffLength];
199
- int length;
200
- File downFile = new File(this.context.getFilesDir() + "/" + dest);
201
- downFile.getParentFile().mkdirs();
202
- downFile.createNewFile();
203
- FileOutputStream fos = new FileOutputStream(downFile);
204
- int readLength = buffLength;
205
- int percent = 0;
206
- this.plugin.notifyDownload(10);
207
- while ((length = dis.read(buffer))>0) {
208
- fos.write(buffer, 0, length);
209
- int newPercent = (int)((readLength * 100) / totalLength);
210
- if (totalLength > 1 && newPercent != percent) {
211
- percent = newPercent;
212
- this.plugin.notifyDownload(calcTotalPercent(percent, 10, 70));
213
- }
214
- readLength += length;
163
+ private File downloadFile(final String id, final String url, final String dest) throws IOException {
164
+
165
+ final URL u = new URL(url);
166
+ final URLConnection connection = u.openConnection();
167
+ final InputStream is = u.openStream();
168
+ final DataInputStream dis = new DataInputStream(is);
169
+
170
+ final File target = new File(this.documentsDir, dest);
171
+ target.getParentFile().mkdirs();
172
+ target.createNewFile();
173
+ final FileOutputStream fos = new FileOutputStream(target);
174
+
175
+ final long totalLength = connection.getContentLength();
176
+ final int bufferSize = 1024;
177
+ final byte[] buffer = new byte[bufferSize];
178
+ int length;
179
+
180
+ int bytesRead = bufferSize;
181
+ int percent = 0;
182
+ this.notifyDownload(id, 10);
183
+ while ((length = dis.read(buffer))>0) {
184
+ fos.write(buffer, 0, length);
185
+ final int newPercent = (int)((bytesRead * 100) / totalLength);
186
+ if (totalLength > 1 && newPercent != percent) {
187
+ percent = newPercent;
188
+ this.notifyDownload(id, this.calcTotalPercent(percent, 10, 70));
215
189
  }
216
- } catch (Exception e) {
217
- Log.e(TAG, "downloadFile error", e);
218
- return false;
190
+ bytesRead += length;
219
191
  }
220
- return true;
192
+ return target;
221
193
  }
222
194
 
223
- private void deleteDirectory(File file) throws IOException {
195
+ private void deleteDirectory(final File file) throws IOException {
224
196
  if (file.isDirectory()) {
225
- File[] entries = file.listFiles();
197
+ final File[] entries = file.listFiles();
226
198
  if (entries != null) {
227
- for (File entry : entries) {
228
- deleteDirectory(entry);
199
+ for (final File entry : entries) {
200
+ this.deleteDirectory(entry);
229
201
  }
230
202
  }
231
203
  }
232
204
  if (!file.delete()) {
233
- throw new IOException("Failed to delete " + file);
205
+ throw new IOException("Failed to delete: " + file);
234
206
  }
235
207
  }
236
208
 
237
- public String download(String url) {
238
- try {
239
- this.plugin.notifyDownload(0);
240
- String folderNameZip = this.randomString(10);
241
- File fileZip = new File(this.context.getFilesDir() + "/" + folderNameZip);
242
- String folderNameUnZip = this.randomString(10);
243
- String version = this.randomString(10);
244
- String folderName = basePathHot + "/" + version;
245
- this.plugin.notifyDownload(5);
246
- Boolean downloaded = this.downloadFile(url, folderNameZip);
247
- if(!downloaded) return "";
248
- this.plugin.notifyDownload(71);
249
- Boolean unzipped = this.unzip(folderNameZip, folderNameUnZip);
250
- if(!unzipped) return "";
251
- fileZip.delete();
252
- this.plugin.notifyDownload(91);
253
- Boolean flatt = this.flattenAssets(folderNameUnZip, folderName);
254
- if(!flatt) return "";
255
- this.plugin.notifyDownload(100);
256
- return version;
257
- } catch (Exception e) {
258
- Log.e(TAG, "updateApp error", e);
259
- return "";
260
- }
209
+ private void setCurrentBundle(final File bundle) {
210
+ this.editor.putString(WebView.CAP_SERVER_PATH, bundle.getPath());
211
+ Log.i(TAG, "Current bundle set to: " + bundle);
212
+ this.editor.commit();
213
+ }
214
+
215
+ public BundleInfo download(final String url, final String version) throws IOException {
216
+ final String id = this.randomString(10);
217
+ this.saveBundleInfo(id, new BundleInfo(id, version, BundleStatus.DOWNLOADING, new Date(System.currentTimeMillis())));
218
+ this.notifyDownload(id, 0);
219
+ final String idName = bundleDirectory + "/" + id;
220
+ this.notifyDownload(id, 5);
221
+ final File downloaded = this.downloadFile(id, url, this.randomString(10));
222
+ this.notifyDownload(id, 71);
223
+ final File unzipped = this.unzip(id, downloaded, this.randomString(10));
224
+ downloaded.delete();
225
+ this.notifyDownload(id, 91);
226
+ this.flattenAssets(unzipped, idName);
227
+ this.notifyDownload(id, 100);
228
+ this.saveBundleInfo(id, null);
229
+ BundleInfo info = new BundleInfo(id, version, BundleStatus.PENDING, new Date(System.currentTimeMillis()));
230
+ this.saveBundleInfo(id, info);
231
+ return info;
261
232
  }
262
233
 
263
- public ArrayList<String> list() {
264
- ArrayList<String> res = new ArrayList<String>();
265
- File destHot = new File(this.context.getFilesDir() + "/" + basePathHot);
266
- Log.i(TAG, "list File : " + destHot.getPath());
234
+ public List<BundleInfo> list() {
235
+ final List<BundleInfo> res = new ArrayList<>();
236
+ final File destHot = new File(this.documentsDir, bundleDirectory);
237
+ Log.d(TAG, "list File : " + destHot.getPath());
267
238
  if (destHot.exists()) {
268
- for (File i : destHot.listFiles()) {
269
- res.add(i.getName());
239
+ for (final File i : destHot.listFiles()) {
240
+ final String id = i.getName();
241
+ res.add(this.getBundleInfo(id));
270
242
  }
271
243
  } else {
272
- Log.i(TAG, "No version available" + destHot);
244
+ Log.i(TAG, "No versions available to list" + destHot);
273
245
  }
274
246
  return res;
275
247
  }
276
248
 
277
- public Boolean delete(String version, String versionName) throws IOException {
278
- File destHot = new File(this.context.getFilesDir() + "/" + basePathHot + "/" + version);
279
- if (destHot.exists()) {
280
- deleteDirectory(destHot);
249
+ public Boolean delete(final String id) throws IOException {
250
+ final BundleInfo deleted = this.getBundleInfo(id);
251
+ final File bundle = new File(this.documentsDir, bundleDirectory + "/" + id);
252
+ if (bundle.exists()) {
253
+ this.deleteDirectory(bundle);
254
+ this.removeBundleInfo(id);
281
255
  return true;
282
256
  }
283
- Log.i(TAG, "Directory not removed: " + destHot.getPath());
284
- this.sendStats("delete", versionName);
257
+ Log.e(TAG, "Directory not removed: " + bundle.getPath());
258
+ this.sendStats("delete", deleted);
285
259
  return false;
286
260
  }
287
261
 
288
- public Boolean set(String version, String versionName) {
289
- File destHot = new File(this.context.getFilesDir() + "/" + basePathHot + "/" + version);
290
- File destIndex = new File(destHot.getPath() + "/index.html");
291
- if (destHot.exists() && destIndex.exists()) {
292
- editor.putString("lastPathHot", destHot.getPath());
293
- editor.putString("serverBasePath", destHot.getPath());
294
- editor.putString("versionName", versionName);
295
- editor.commit();
296
- sendStats("set", versionName);
262
+ private File getBundleDirectory(final String id) {
263
+ return new File(this.documentsDir, bundleDirectory + "/" + id);
264
+ }
265
+
266
+ private boolean bundleExists(final File bundle) {
267
+ if(bundle == null || !bundle.exists()) {
268
+ return false;
269
+ }
270
+
271
+ return new File(bundle.getPath(), "/index.html").exists();
272
+ }
273
+
274
+ public Boolean set(final BundleInfo bundle) {
275
+ return this.set(bundle.getId());
276
+ }
277
+
278
+ public Boolean set(final String id) {
279
+
280
+ final BundleInfo existing = this.getBundleInfo(id);
281
+ final File bundle = this.getBundleDirectory(id);
282
+
283
+ Log.i(TAG, "Setting next active bundle: " + existing);
284
+ if (this.bundleExists(bundle)) {
285
+ this.setCurrentBundle(bundle);
286
+ this.setBundleStatus(id, BundleStatus.PENDING);
287
+ this.sendStats("set", existing);
297
288
  return true;
298
289
  }
299
- sendStats("set_fail", versionName);
290
+ this.sendStats("set_fail", existing);
300
291
  return false;
301
292
  }
302
293
 
303
- public void getLatest(String url, Callback callback) {
304
- String deviceID = this.deviceID;
305
- String appId = this.appId;
306
- String versionBuild = this.versionBuild;
307
- String versionCode = this.versionCode;
308
- String versionOs = this.versionOs;
309
- String pluginVersion = this.pluginVersion;
310
- String versionName = getVersionName().equals("") ? "builtin" : getVersionName();
311
- StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
312
- new Response.Listener<String>() {
313
- @Override
314
- public void onResponse(String response) {
315
- try {
316
- JSONObject jsonObject = new JSONObject(response);
317
- callback.callback(jsonObject);
318
- } catch (JSONException e) {
319
- e.printStackTrace();
320
- }
321
- }
322
- }, new Response.ErrorListener() {
323
- @Override
324
- public void onErrorResponse(VolleyError error) {
325
- Log.e(TAG, "Error getting Latest" + error);
326
- }
327
- }) {
328
- @Override
329
- public Map<String, String> getHeaders() throws AuthFailureError {
330
- Map<String, String> params = new HashMap<String, String>();
331
- params.put("cap_platform", "android");
332
- params.put("cap_device_id", deviceID);
333
- params.put("cap_app_id", appId);
334
- params.put("cap_version_build", versionBuild);
335
- params.put("cap_version_code", versionCode);
336
- params.put("cap_version_os", versionOs);
337
- params.put("cap_version_name", versionName);
338
- params.put("cap_plugin_version", pluginVersion);
339
- return params;
340
- }
341
- };
342
- RequestQueue requestQueue = Volley.newRequestQueue(this.context);
343
- requestQueue.add(stringRequest);
294
+ public void commit(final BundleInfo bundle) {
295
+ this.setBundleStatus(bundle.getId(), BundleStatus.SUCCESS);
296
+ this.setFallbackVersion(bundle);
344
297
  }
345
298
 
346
- public String getLastPathHot() {
347
- return prefs.getString("lastPathHot", "public");
299
+ public void reset() {
300
+ this.reset(false);
348
301
  }
349
302
 
350
- public String getVersionName() {
351
- return prefs.getString("versionName", "");
303
+ public void rollback(final BundleInfo bundle) {
304
+ this.setBundleStatus(bundle.getId(), BundleStatus.ERROR);
352
305
  }
353
306
 
354
- public void reset() {
355
- String version = prefs.getString("versionName", "");
356
- this.sendStats("reset", version);
357
- editor.putString("lastPathHot", "public");
358
- editor.putString("serverBasePath", "public");
359
- editor.putString("versionName", "");
360
- editor.commit();
361
- }
362
-
363
- public void sendStats(String action, String version) {
364
- if (statsUrl == "") { return; }
365
- URL url;
366
- JSONObject json = new JSONObject();
367
- String jsonString;
307
+ public void reset(final boolean internal) {
308
+ this.setCurrentBundle(new File("public"));
309
+ this.setFallbackVersion(null);
310
+ this.setNextVersion(null);
311
+ if(!internal) {
312
+ this.sendStats("reset", this.getCurrentBundle());
313
+ }
314
+ }
315
+
316
+ public void getLatest(final String updateUrl, final Callback callback) {
317
+ final String deviceID = this.deviceID;
318
+ final String appId = this.appId;
319
+ final String versionBuild = this.versionBuild;
320
+ final String versionCode = this.versionCode;
321
+ final String versionOs = this.versionOs;
322
+ final String pluginVersion = CapacitorUpdater.pluginVersion;
323
+ final String version = this.getCurrentBundle().getId();
368
324
  try {
369
- url = new URL(statsUrl);
325
+ JSONObject json = new JSONObject();
370
326
  json.put("platform", "android");
371
- json.put("action", action);
327
+ json.put("device_id", deviceID);
328
+ json.put("app_id", appId);
329
+ json.put("version_build", versionBuild);
330
+ json.put("version_code", versionCode);
331
+ json.put("version_os", versionOs);
372
332
  json.put("version_name", version);
333
+ json.put("plugin_version", pluginVersion);
334
+
335
+ // Building a request
336
+ JsonObjectRequest request = new JsonObjectRequest(
337
+ Request.Method.POST,
338
+ updateUrl,
339
+ json,
340
+ new Response.Listener<JSONObject>() {
341
+ @Override
342
+ public void onResponse(JSONObject response) {
343
+ callback.callback(response);
344
+ }
345
+ },
346
+ new Response.ErrorListener(){
347
+ @Override
348
+ public void onErrorResponse(VolleyError error) {
349
+ Log.e(TAG, "Error getting Latest " + error);
350
+ }
351
+ });
352
+ this.requestQueue.add(request);
353
+ } catch(JSONException ex){
354
+ // Catch if something went wrong with the params
355
+ Log.e(TAG, "Error getLatest JSONException " + ex);
356
+ }
357
+ }
358
+
359
+ public void sendStats(final String action, final BundleInfo bundle) {
360
+ String statsUrl = this.statsUrl;
361
+ if (statsUrl == null || "".equals(statsUrl) || statsUrl.length() == 0) { return; }
362
+ try {
363
+ JSONObject json = new JSONObject();
364
+ json.put("platform", "android");
373
365
  json.put("device_id", this.deviceID);
366
+ json.put("app_id", this.appId);
374
367
  json.put("version_build", this.versionBuild);
375
368
  json.put("version_code", this.versionCode);
376
369
  json.put("version_os", this.versionOs);
377
- json.put("plugin_version", this.pluginVersion);
378
- json.put("app_id", this.appId);
379
- jsonString = json.toString();
380
- } catch (Exception ex) {
381
- Log.e(TAG, "Error get stats", ex);
370
+ json.put("version_name", bundle.getVersionName());
371
+ json.put("plugin_version", pluginVersion);
372
+ json.put("action", action);
373
+
374
+ // Building a request
375
+ JsonObjectRequest request = new JsonObjectRequest(
376
+ Request.Method.POST,
377
+ statsUrl,
378
+ json,
379
+ new Response.Listener<JSONObject>() {
380
+ @Override
381
+ public void onResponse(JSONObject response) {
382
+ Log.i(TAG, "Stats send for \"" + action + "\", version " + bundle.getVersionName());
383
+ }
384
+ },
385
+ new Response.ErrorListener(){
386
+ @Override
387
+ public void onErrorResponse(VolleyError error) {
388
+ Log.i(TAG, "Stats send for \"" + action + "\", version " + bundle.getVersionName());
389
+ }
390
+ });
391
+ this.requestQueue.add(request);
392
+ } catch(JSONException ex){
393
+ // Catch if something went wrong with the params
394
+ Log.e(TAG, "Error sendStats JSONException " + ex);
395
+ }
396
+ }
397
+
398
+ public BundleInfo getBundleInfo(String id) {
399
+ if(id == null) {
400
+ id = BundleInfo.VERSION_UNKNOWN;
401
+ }
402
+ Log.d(TAG, "Getting info for bundle [" + id + "]");
403
+ BundleInfo result;
404
+ if(BundleInfo.ID_BUILTIN.equals(id)) {
405
+ result = new BundleInfo(id, (String) null, BundleStatus.SUCCESS, "");
406
+ } else {
407
+ try {
408
+ String stored = this.prefs.getString(id + INFO_SUFFIX, "");
409
+ result = BundleInfo.fromJSON(stored);
410
+ } catch (JSONException e) {
411
+ Log.e(TAG, "Failed to parse info for bundle [" + id + "] ", e);
412
+ result = new BundleInfo(id, (String) null, BundleStatus.PENDING, "");
413
+ }
414
+ }
415
+
416
+ Log.d(TAG, "Returning info [" + id + "] " + result);
417
+ return result;
418
+ }
419
+
420
+ public BundleInfo getBundleInfoByName(final String versionName) {
421
+ final List<BundleInfo> installed = this.list();
422
+ for(final BundleInfo i : installed) {
423
+ if(i.getVersionName().equals(versionName)) {
424
+ return i;
425
+ }
426
+ }
427
+ return null;
428
+ }
429
+
430
+ private void removeBundleInfo(final String id) {
431
+ this.saveBundleInfo(id, null);
432
+ }
433
+
434
+ private void saveBundleInfo(final String id, final BundleInfo info) {
435
+ if(id == null || (info != null && (info.isBuiltin() || info.isUnknown()))) {
436
+ Log.d(TAG, "Not saving info for bundle: [" + id + "] " + info);
382
437
  return;
383
438
  }
384
- new Thread(new Runnable(){
385
- @Override
386
- public void run() {
387
- HttpURLConnection con = null;
388
- try {
389
- con = (HttpURLConnection) url.openConnection();
390
- con.setRequestMethod("POST");
391
- con.setRequestProperty("Content-Type", "application/json");
392
- con.setRequestProperty("Accept", "application/json");
393
- con.setRequestProperty("Content-Length", Integer.toString(jsonString.getBytes().length));
394
- con.setDoOutput(true);
395
- con.setConnectTimeout(500);
396
- DataOutputStream wr = new DataOutputStream (con.getOutputStream());
397
- wr.writeBytes(jsonString);
398
- wr.close();
399
- int responseCode = con.getResponseCode();
400
- if (responseCode != 200) {
401
- Log.e(TAG, "Stats error responseCode: " + responseCode);
402
- } else {
403
- Log.i(TAG, "Stats send for \"" + action + "\", version " + version);
404
- }
405
- } catch (Exception ex) {
406
- Log.e(TAG, "Error post stats", ex);
407
- } finally {
408
- if (con != null) {
409
- con.disconnect();
410
- }
411
- }
439
+
440
+ if(info == null) {
441
+ Log.d(TAG, "Removing info for bundle [" + id + "]");
442
+ this.editor.remove(id + INFO_SUFFIX);
443
+ } else {
444
+ final BundleInfo update = info.setId(id);
445
+ Log.d(TAG, "Storing info for bundle [" + id + "] " + update.toString());
446
+ this.editor.putString(id + INFO_SUFFIX, update.toString());
447
+ }
448
+ this.editor.commit();
449
+ }
450
+
451
+ public void setVersionName(final String id, final String name) {
452
+ if(id != null) {
453
+ Log.d(TAG, "Setting name for bundle [" + id + "] to " + name);
454
+ BundleInfo info = this.getBundleInfo(id);
455
+ this.saveBundleInfo(id, info.setVersionName(name));
456
+ }
457
+ }
458
+
459
+ private void setBundleStatus(final String id, final BundleStatus status) {
460
+ if(id != null && status != null) {
461
+ BundleInfo info = this.getBundleInfo(id);
462
+ Log.d(TAG, "Setting status for bundle [" + id + "] to " + status);
463
+ this.saveBundleInfo(id, info.setStatus(status));
464
+ }
465
+ }
466
+
467
+ private String getCurrentBundleId() {
468
+ if(this.isUsingBuiltin()) {
469
+ return BundleInfo.ID_BUILTIN;
470
+ } else {
471
+ final String path = this.getCurrentBundlePath();
472
+ return path.substring(path.lastIndexOf('/') + 1);
473
+ }
474
+ }
475
+
476
+ public BundleInfo getCurrentBundle() {
477
+ return this.getBundleInfo(this.getCurrentBundleId());
478
+ }
479
+
480
+ public String getCurrentBundlePath() {
481
+ String path = this.prefs.getString(WebView.CAP_SERVER_PATH, "public");
482
+ if("".equals(path.trim())) {
483
+ return "public";
484
+ }
485
+ return path;
486
+ }
487
+
488
+ public Boolean isUsingBuiltin() {
489
+ return this.getCurrentBundlePath().equals("public");
490
+ }
491
+
492
+ public BundleInfo getFallbackVersion() {
493
+ final String id = this.prefs.getString(FALLBACK_VERSION, BundleInfo.ID_BUILTIN);
494
+ return this.getBundleInfo(id);
495
+ }
496
+
497
+ private void setFallbackVersion(final BundleInfo fallback) {
498
+ this.editor.putString(FALLBACK_VERSION,
499
+ fallback == null
500
+ ? BundleInfo.ID_BUILTIN
501
+ : fallback.getId()
502
+ );
503
+ }
504
+
505
+ public BundleInfo getNextVersion() {
506
+ final String id = this.prefs.getString(NEXT_VERSION, "");
507
+ if(id != "") {
508
+ return this.getBundleInfo(id);
509
+ } else {
510
+ return null;
511
+ }
512
+ }
513
+
514
+ public boolean setNextVersion(final String next) {
515
+ if (next == null) {
516
+ this.editor.remove(NEXT_VERSION);
517
+ } else {
518
+ final File bundle = this.getBundleDirectory(next);
519
+ if (!this.bundleExists(bundle)) {
520
+ return false;
412
521
  }
413
- }).start();
522
+
523
+ this.editor.putString(NEXT_VERSION, next);
524
+ this.setBundleStatus(next, BundleStatus.PENDING);
525
+ }
526
+ this.editor.commit();
527
+ return true;
414
528
  }
529
+
415
530
  }