@capgo/capacitor-updater 3.3.12 → 4.0.0-alpha.1

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,48 +22,45 @@ 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 {
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 = "";
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 = "4.0.0-alpha.1";
50
+
51
+ public SharedPreferences.Editor editor;
52
+ public SharedPreferences prefs;
53
+
54
+ public RequestQueue requestQueue;
58
55
 
56
+ public File documentsDir;
57
+ public String versionBuild = "";
58
+ public String versionCode = "";
59
+ public String versionOs = "";
60
+
61
+ public String statsUrl = "";
59
62
  public String appId = "";
60
63
  public String deviceID = "";
61
- public final String pluginVersion = "3.3.12";
62
- public String statsUrl = "";
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
- }
74
64
 
75
65
  private final FilenameFilter filter = new FilenameFilter() {
76
66
  @Override
@@ -84,7 +74,7 @@ public class CapacitorUpdater {
84
74
  return (percent * (max - min)) / 100 + min;
85
75
  }
86
76
 
87
- void notifyDownload(final int percent) {
77
+ void notifyDownload(final String id, final int percent) {
88
78
  return;
89
79
  }
90
80
 
@@ -94,8 +84,9 @@ public class CapacitorUpdater {
94
84
  sb.append(AB.charAt(rnd.nextInt(AB.length())));
95
85
  return sb.toString();
96
86
  }
97
- private File unzip(final File zipFile, final String dest) throws IOException {
98
- final File targetDirectory = new File(this.context.getFilesDir() + "/" + dest);
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);
99
90
  final ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(zipFile)));
100
91
  try {
101
92
  int count;
@@ -104,7 +95,7 @@ public class CapacitorUpdater {
104
95
  final long lengthTotal = zipFile.length();
105
96
  long lengthRead = bufferSize;
106
97
  int percent = 0;
107
- this.notifyDownload(75);
98
+ this.notifyDownload(id, 75);
108
99
 
109
100
  ZipEntry entry;
110
101
  while ((entry = zis.getNextEntry()) != null) {
@@ -135,7 +126,7 @@ public class CapacitorUpdater {
135
126
  final int newPercent = (int)((lengthRead * 100) / lengthTotal);
136
127
  if (lengthTotal > 1 && newPercent != percent) {
137
128
  percent = newPercent;
138
- this.notifyDownload(this.calcTotalPercent(percent, 75, 90));
129
+ this.notifyDownload(id, this.calcTotalPercent(percent, 75, 90));
139
130
  }
140
131
 
141
132
  lengthRead += entry.getCompressedSize();
@@ -145,7 +136,7 @@ public class CapacitorUpdater {
145
136
  try {
146
137
  zis.close();
147
138
  } catch (final IOException e) {
148
- Log.e(this.TAG, "Failed to close zip input stream", e);
139
+ Log.e(TAG, "Failed to close zip input stream", e);
149
140
  }
150
141
  }
151
142
  }
@@ -154,14 +145,14 @@ public class CapacitorUpdater {
154
145
  if (!sourceFile.exists()) {
155
146
  throw new FileNotFoundException("Source file not found: " + sourceFile.getPath());
156
147
  }
157
- final File destinationFile = new File(this.context.getFilesDir() + "/" + dest);
148
+ final File destinationFile = new File(this.documentsDir, dest);
158
149
  destinationFile.getParentFile().mkdirs();
159
150
  final String[] entries = sourceFile.list(this.filter);
160
151
  if (entries == null || entries.length == 0) {
161
152
  throw new IOException("Source file was not a directory or was empty: " + sourceFile.getPath());
162
153
  }
163
- if (entries.length == 1 && !entries[0].equals("index.html")) {
164
- final File child = new File(sourceFile.getPath() + "/" + entries[0]);
154
+ if (entries.length == 1 && !"index.html".equals(entries[0])) {
155
+ final File child = new File(sourceFile, entries[0]);
165
156
  child.renameTo(destinationFile);
166
157
  } else {
167
158
  sourceFile.renameTo(destinationFile);
@@ -169,14 +160,14 @@ public class CapacitorUpdater {
169
160
  sourceFile.delete();
170
161
  }
171
162
 
172
- private File downloadFile(final String url, final String dest) throws IOException {
163
+ private File downloadFile(final String id, final String url, final String dest) throws IOException {
173
164
 
174
165
  final URL u = new URL(url);
175
166
  final URLConnection connection = u.openConnection();
176
167
  final InputStream is = u.openStream();
177
168
  final DataInputStream dis = new DataInputStream(is);
178
169
 
179
- final File target = new File(this.context.getFilesDir() + "/" + dest);
170
+ final File target = new File(this.documentsDir, dest);
180
171
  target.getParentFile().mkdirs();
181
172
  target.createNewFile();
182
173
  final FileOutputStream fos = new FileOutputStream(target);
@@ -188,13 +179,13 @@ public class CapacitorUpdater {
188
179
 
189
180
  int bytesRead = bufferSize;
190
181
  int percent = 0;
191
- this.notifyDownload(10);
182
+ this.notifyDownload(id, 10);
192
183
  while ((length = dis.read(buffer))>0) {
193
184
  fos.write(buffer, 0, length);
194
185
  final int newPercent = (int)((bytesRead * 100) / totalLength);
195
186
  if (totalLength > 1 && newPercent != percent) {
196
187
  percent = newPercent;
197
- this.notifyDownload(this.calcTotalPercent(percent, 10, 70));
188
+ this.notifyDownload(id, this.calcTotalPercent(percent, 10, 70));
198
189
  }
199
190
  bytesRead += length;
200
191
  }
@@ -211,202 +202,329 @@ public class CapacitorUpdater {
211
202
  }
212
203
  }
213
204
  if (!file.delete()) {
214
- throw new IOException("Failed to delete " + file);
205
+ throw new IOException("Failed to delete: " + file);
215
206
  }
216
207
  }
217
208
 
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;
234
- }
235
-
236
- public ArrayList<String> list() {
237
- final ArrayList<String> res = new ArrayList<String>();
238
- final File destHot = new File(this.context.getFilesDir() + "/" + this.basePathHot);
239
- Log.i(this.TAG, "list File : " + destHot.getPath());
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;
232
+ }
233
+
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());
240
238
  if (destHot.exists()) {
241
239
  for (final File i : destHot.listFiles()) {
242
- res.add(i.getName());
240
+ final String id = i.getName();
241
+ res.add(this.getBundleInfo(id));
243
242
  }
244
243
  } else {
245
- Log.i(this.TAG, "No version available" + destHot);
244
+ Log.i(TAG, "No versions available to list" + destHot);
246
245
  }
247
246
  return res;
248
247
  }
249
248
 
250
- public Boolean delete(final String version, final String versionName) throws IOException {
251
- final File destHot = new File(this.context.getFilesDir() + "/" + this.basePathHot + "/" + version);
252
- if (destHot.exists()) {
253
- this.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);
254
255
  return true;
255
256
  }
256
- Log.i(this.TAG, "Directory not removed: " + destHot.getPath());
257
- this.sendStats("delete", versionName);
257
+ Log.e(TAG, "Directory not removed: " + bundle.getPath());
258
+ this.sendStats("delete", deleted);
258
259
  return false;
259
260
  }
260
261
 
261
- public Boolean set(final String version, final String versionName) {
262
- final File destHot = new File(this.context.getFilesDir() + "/" + this.basePathHot + "/" + version);
263
- final File destIndex = new File(destHot.getPath() + "/index.html");
264
- if (destHot.exists() && destIndex.exists()) {
265
- this.editor.putString("lastPathHot", destHot.getPath());
266
- this.editor.putString("serverBasePath", destHot.getPath());
267
- this.editor.putString("versionName", versionName);
268
- this.editor.commit();
269
- this.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);
270
288
  return true;
271
289
  }
272
- this.sendStats("set_fail", versionName);
290
+ this.sendStats("set_fail", existing);
273
291
  return false;
274
292
  }
275
293
 
276
- public void getLatest(final String url, final Callback callback) {
277
- final String deviceID = this.getDeviceID();
278
- final String appId = this.getAppId();
279
- final String versionBuild = this.versionBuild;
280
- final String versionCode = this.versionCode;
281
- final String versionOs = this.versionOs;
282
- final String pluginVersion = this.pluginVersion;
283
- final String versionName = this.getVersionName().equals("") ? "builtin" : this.getVersionName();
284
- final StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
285
- new Response.Listener<String>() {
286
- @Override
287
- public void onResponse(final String response) {
288
- try {
289
- final JSONObject jsonObject = new JSONObject(response);
290
- callback.callback(jsonObject);
291
- } catch (final JSONException e) {
292
- Log.e(CapacitorUpdater.this.TAG, "Error parsing JSON", e);
293
- }
294
- }
295
- }, new Response.ErrorListener() {
296
- @Override
297
- public void onErrorResponse(final VolleyError error) {
298
- Log.e(CapacitorUpdater.this.TAG, "Error getting Latest" + error);
299
- }
300
- }) {
301
- @Override
302
- public Map<String, String> getHeaders() throws AuthFailureError {
303
- final Map<String, String> params = new HashMap<String, String>();
304
- params.put("cap_platform", "android");
305
- params.put("cap_device_id", deviceID);
306
- params.put("cap_app_id", appId);
307
- params.put("cap_version_build", versionBuild);
308
- params.put("cap_version_code", versionCode);
309
- params.put("cap_version_os", versionOs);
310
- params.put("cap_version_name", versionName);
311
- params.put("cap_plugin_version", pluginVersion);
312
- return params;
313
- }
314
- };
315
- final RequestQueue requestQueue = Volley.newRequestQueue(this.context);
316
- requestQueue.add(stringRequest);
294
+ public void commit(final BundleInfo bundle) {
295
+ this.setBundleStatus(bundle.getId(), BundleStatus.SUCCESS);
296
+ this.setFallbackVersion(bundle);
317
297
  }
318
298
 
319
- public String getLastPathHot() {
320
- return this.prefs.getString("lastPathHot", "public");
299
+ public void reset() {
300
+ this.reset(false);
321
301
  }
322
302
 
323
- public String getVersionName() {
324
- return this.prefs.getString("versionName", "");
303
+ public void rollback(final BundleInfo bundle) {
304
+ this.setBundleStatus(bundle.getId(), BundleStatus.ERROR);
325
305
  }
326
306
 
327
- public void reset() {
328
- final String version = this.prefs.getString("versionName", "");
329
- this.sendStats("reset", version);
330
- this.editor.putString("lastPathHot", "public");
331
- this.editor.putString("serverBasePath", "public");
332
- this.editor.putString("versionName", "");
333
- this.editor.commit();
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
+ }
334
314
  }
335
315
 
336
- public void sendStats(final String action, final String version) {
337
- if (this.getStatsUrl() == "") { return; }
338
- final URL url;
339
- final JSONObject json = new JSONObject();
340
- final String jsonString;
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();
341
324
  try {
342
- url = new URL(this.getStatsUrl());
325
+ JSONObject json = new JSONObject();
343
326
  json.put("platform", "android");
344
- 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);
345
332
  json.put("version_name", version);
346
- json.put("device_id", this.getDeviceID());
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");
365
+ json.put("device_id", this.deviceID);
366
+ json.put("app_id", this.appId);
347
367
  json.put("version_build", this.versionBuild);
348
368
  json.put("version_code", this.versionCode);
349
369
  json.put("version_os", this.versionOs);
350
- json.put("plugin_version", this.pluginVersion);
351
- json.put("app_id", this.getAppId());
352
- jsonString = json.toString();
353
- } catch (final Exception ex) {
354
- Log.e(this.TAG, "Error get stats", ex);
355
- return;
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);
356
395
  }
357
- new Thread(new Runnable(){
358
- @Override
359
- public void run() {
360
- HttpURLConnection con = null;
361
- try {
362
- con = (HttpURLConnection) url.openConnection();
363
- con.setRequestMethod("POST");
364
- con.setRequestProperty("Content-Type", "application/json");
365
- con.setRequestProperty("Accept", "application/json");
366
- con.setRequestProperty("Content-Length", Integer.toString(jsonString.getBytes().length));
367
- con.setDoOutput(true);
368
- con.setConnectTimeout(500);
369
- final DataOutputStream wr = new DataOutputStream (con.getOutputStream());
370
- wr.writeBytes(jsonString);
371
- wr.close();
372
- final int responseCode = con.getResponseCode();
373
- if (responseCode != 200) {
374
- Log.e(CapacitorUpdater.this.TAG, "Stats error responseCode: " + responseCode);
375
- } else {
376
- Log.i(CapacitorUpdater.this.TAG, "Stats send for \"" + action + "\", version " + version);
377
- }
378
- } catch (final Exception ex) {
379
- Log.e(CapacitorUpdater.this.TAG, "Error post stats", ex);
380
- } finally {
381
- if (con != null) {
382
- con.disconnect();
383
- }
384
- }
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, "");
385
413
  }
386
- }).start();
414
+ }
415
+
416
+ Log.d(TAG, "Returning info [" + id + "] " + result);
417
+ return result;
387
418
  }
388
419
 
389
- public String getStatsUrl() {
390
- return this.statsUrl;
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;
391
428
  }
392
429
 
393
- public void setStatsUrl(final String statsUrl) {
394
- this.statsUrl = statsUrl;
430
+ private void removeBundleInfo(final String id) {
431
+ this.saveBundleInfo(id, null);
395
432
  }
396
433
 
397
- public String getAppId() {
398
- return this.appId;
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);
437
+ return;
438
+ }
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();
399
449
  }
400
450
 
401
- public void setAppId(final String appId) {
402
- this.appId = appId;
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
+ }
403
457
  }
404
458
 
405
- public String getDeviceID() {
406
- return this.deviceID;
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
+ }
407
465
  }
408
466
 
409
- public void setDeviceID(final String deviceID) {
410
- this.deviceID = deviceID;
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
+ );
411
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;
521
+ }
522
+
523
+ this.editor.putString(NEXT_VERSION, next);
524
+ this.setBundleStatus(next, BundleStatus.PENDING);
525
+ }
526
+ this.editor.commit();
527
+ return true;
528
+ }
529
+
412
530
  }