@capgo/capacitor-updater 3.3.11 → 4.0.0-alpha.10
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/LICENCE +656 -160
- package/README.md +225 -129
- package/android/src/main/java/ee/forgr/capacitor_updater/BundleInfo.java +130 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/BundleStatus.java +36 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdater.java +314 -198
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +481 -246
- package/dist/docs.json +607 -114
- package/dist/esm/definitions.d.ts +222 -74
- package/dist/esm/web.d.ts +19 -16
- package/dist/esm/web.js +32 -23
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +32 -23
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +32 -23
- package/dist/plugin.js.map +1 -1
- package/ios/Plugin/BundleInfo.swift +94 -0
- package/ios/Plugin/BundleStatus.swift +41 -0
- package/ios/Plugin/CapacitorUpdater.swift +319 -82
- package/ios/Plugin/CapacitorUpdaterPlugin.m +4 -2
- package/ios/Plugin/CapacitorUpdaterPlugin.swift +287 -173
- package/ios/Plugin/ObjectPreferences.swift +41 -0
- package/package.json +3 -2
|
@@ -5,12 +5,15 @@ 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;
|
|
8
9
|
import android.os.Bundle;
|
|
10
|
+
import android.provider.Settings;
|
|
9
11
|
import android.util.Log;
|
|
10
12
|
|
|
11
13
|
import androidx.annotation.NonNull;
|
|
12
14
|
import androidx.annotation.Nullable;
|
|
13
15
|
|
|
16
|
+
import com.android.volley.toolbox.Volley;
|
|
14
17
|
import com.getcapacitor.CapConfig;
|
|
15
18
|
import com.getcapacitor.JSArray;
|
|
16
19
|
import com.getcapacitor.JSObject;
|
|
@@ -18,389 +21,621 @@ import com.getcapacitor.Plugin;
|
|
|
18
21
|
import com.getcapacitor.PluginCall;
|
|
19
22
|
import com.getcapacitor.PluginMethod;
|
|
20
23
|
import com.getcapacitor.annotation.CapacitorPlugin;
|
|
24
|
+
import com.getcapacitor.plugin.WebView;
|
|
25
|
+
|
|
21
26
|
import io.github.g00fy2.versioncompare.Version;
|
|
22
27
|
|
|
23
28
|
import org.json.JSONException;
|
|
24
29
|
|
|
25
30
|
import java.io.IOException;
|
|
26
|
-
import java.util.
|
|
31
|
+
import java.util.Iterator;
|
|
32
|
+
import java.util.List;
|
|
27
33
|
|
|
28
34
|
@CapacitorPlugin(name = "CapacitorUpdater")
|
|
29
35
|
public class CapacitorUpdaterPlugin extends Plugin implements Application.ActivityLifecycleCallbacks {
|
|
30
|
-
private static final String
|
|
31
|
-
private static final String statsUrlDefault = "https://
|
|
32
|
-
private final String
|
|
33
|
-
private CapacitorUpdater implementation;
|
|
36
|
+
private static final String updateUrlDefault = "https://xvwzpoazmxkqosrdewyv.functions.supabase.co/updates";
|
|
37
|
+
private static final String statsUrlDefault = "https://xvwzpoazmxkqosrdewyv.functions.supabase.co/stats";
|
|
38
|
+
private static final String DELAY_UPDATE = "delayUpdate";
|
|
34
39
|
|
|
35
|
-
private SharedPreferences prefs;
|
|
36
40
|
private SharedPreferences.Editor editor;
|
|
41
|
+
private SharedPreferences prefs;
|
|
42
|
+
private CapacitorUpdater implementation;
|
|
37
43
|
|
|
38
|
-
private
|
|
39
|
-
private
|
|
44
|
+
private Integer appReadyTimeout = 10000;
|
|
45
|
+
private Boolean autoDeleteFailed = true;
|
|
46
|
+
private Boolean autoDeletePrevious = true;
|
|
40
47
|
private Boolean autoUpdate = false;
|
|
48
|
+
private String updateUrl = "";
|
|
49
|
+
private Version currentVersionNative;
|
|
41
50
|
private Boolean resetWhenUpdate = true;
|
|
42
51
|
|
|
52
|
+
private volatile Thread appReadyCheck;
|
|
53
|
+
|
|
43
54
|
@Override
|
|
44
55
|
public void load() {
|
|
45
56
|
super.load();
|
|
46
|
-
this.prefs = this.getContext().getSharedPreferences(
|
|
57
|
+
this.prefs = this.getContext().getSharedPreferences(WebView.WEBVIEW_PREFS_NAME, Activity.MODE_PRIVATE);
|
|
47
58
|
this.editor = this.prefs.edit();
|
|
59
|
+
|
|
48
60
|
try {
|
|
49
|
-
this.implementation = new CapacitorUpdater(
|
|
61
|
+
this.implementation = new CapacitorUpdater() {
|
|
50
62
|
@Override
|
|
51
|
-
public void notifyDownload(final int percent) {
|
|
52
|
-
CapacitorUpdaterPlugin.this.notifyDownload(percent);
|
|
63
|
+
public void notifyDownload(final String id, final int percent) {
|
|
64
|
+
CapacitorUpdaterPlugin.this.notifyDownload(id, percent);
|
|
53
65
|
}
|
|
54
66
|
};
|
|
55
67
|
final PackageInfo pInfo = this.getContext().getPackageManager().getPackageInfo(this.getContext().getPackageName(), 0);
|
|
68
|
+
this.implementation.versionBuild = pInfo.versionName;
|
|
69
|
+
this.implementation.versionCode = Integer.toString(pInfo.versionCode);
|
|
70
|
+
this.implementation.requestQueue = Volley.newRequestQueue(this.getContext());
|
|
56
71
|
this.currentVersionNative = new Version(pInfo.versionName);
|
|
57
72
|
} catch (final PackageManager.NameNotFoundException e) {
|
|
58
|
-
Log.e(
|
|
73
|
+
Log.e(CapacitorUpdater.TAG, "Error instantiating implementation", e);
|
|
59
74
|
return;
|
|
60
|
-
} catch (final Exception
|
|
61
|
-
Log.e(
|
|
75
|
+
} catch (final Exception e) {
|
|
76
|
+
Log.e(CapacitorUpdater.TAG, "Error getting current native app version", e);
|
|
62
77
|
return;
|
|
63
78
|
}
|
|
79
|
+
|
|
64
80
|
final CapConfig config = CapConfig.loadDefault(this.getActivity());
|
|
65
|
-
this.implementation.
|
|
66
|
-
this.implementation.
|
|
67
|
-
this.
|
|
81
|
+
this.implementation.appId = config.getString("appId", "");
|
|
82
|
+
this.implementation.statsUrl = this.getConfig().getString("statsUrl", statsUrlDefault);
|
|
83
|
+
this.implementation.documentsDir = this.getContext().getFilesDir();
|
|
84
|
+
this.implementation.prefs = this.getContext().getSharedPreferences(WebView.WEBVIEW_PREFS_NAME, Activity.MODE_PRIVATE);
|
|
85
|
+
this.implementation.editor = this.prefs.edit();
|
|
86
|
+
this.implementation.versionOs = Build.VERSION.RELEASE;
|
|
87
|
+
this.implementation.deviceID = Settings.Secure.getString(this.getContext().getContentResolver(), Settings.Secure.ANDROID_ID);
|
|
88
|
+
|
|
89
|
+
this.autoDeleteFailed = this.getConfig().getBoolean("autoDeleteFailed", true);
|
|
90
|
+
this.autoDeletePrevious = this.getConfig().getBoolean("autoDeletePrevious", true);
|
|
91
|
+
this.updateUrl = this.getConfig().getString("updateUrl", updateUrlDefault);
|
|
68
92
|
this.autoUpdate = this.getConfig().getBoolean("autoUpdate", false);
|
|
93
|
+
this.appReadyTimeout = this.getConfig().getInt("appReadyTimeout", 10000);
|
|
69
94
|
this.resetWhenUpdate = this.getConfig().getBoolean("resetWhenUpdate", true);
|
|
95
|
+
|
|
70
96
|
if (this.resetWhenUpdate) {
|
|
71
|
-
|
|
97
|
+
this.cleanupObsoleteVersions();
|
|
98
|
+
}
|
|
99
|
+
final Application application = (Application) this.getContext().getApplicationContext();
|
|
100
|
+
application.registerActivityLifecycleCallbacks(this);
|
|
101
|
+
|
|
102
|
+
this.onActivityStarted(this.getActivity());
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private void cleanupObsoleteVersions() {
|
|
106
|
+
try {
|
|
107
|
+
final Version previous = new Version(this.prefs.getString("LatestVersionNative", ""));
|
|
72
108
|
try {
|
|
73
|
-
if (!
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
this.
|
|
77
|
-
final
|
|
78
|
-
for (
|
|
109
|
+
if (!"".equals(previous.getOriginalString()) && this.currentVersionNative.getMajor() > previous.getMajor()) {
|
|
110
|
+
|
|
111
|
+
Log.i(CapacitorUpdater.TAG, "New native major version detected: " + this.currentVersionNative);
|
|
112
|
+
this.implementation.reset(true);
|
|
113
|
+
final List<BundleInfo> installed = this.implementation.list();
|
|
114
|
+
for (final BundleInfo bundle: installed) {
|
|
79
115
|
try {
|
|
80
|
-
|
|
81
|
-
this.implementation.delete(
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
Log.e(CapacitorUpdaterPlugin.this.TAG, "error deleting version", e);
|
|
116
|
+
Log.i(CapacitorUpdater.TAG, "Deleting obsolete bundle: " + bundle.getId());
|
|
117
|
+
this.implementation.delete(bundle.getId());
|
|
118
|
+
} catch (final Exception e) {
|
|
119
|
+
Log.e(CapacitorUpdater.TAG, "Failed to delete: " + bundle.getId(), e);
|
|
85
120
|
}
|
|
86
121
|
}
|
|
87
122
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
} catch (final Exception ex) {
|
|
91
|
-
Log.e(this.TAG, "Cannot get the current version " + ex.getMessage());
|
|
123
|
+
} catch (final Exception e) {
|
|
124
|
+
Log.e(CapacitorUpdater.TAG, "Could not determine the current version", e);
|
|
92
125
|
}
|
|
126
|
+
} catch(final Exception e) {
|
|
127
|
+
Log.e(CapacitorUpdater.TAG, "Error calculating previous native version", e);
|
|
93
128
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
application.registerActivityLifecycleCallbacks(this);
|
|
97
|
-
this.onActivityStarted(this.getActivity());
|
|
129
|
+
this.editor.putString("LatestVersionNative", this.currentVersionNative.toString());
|
|
130
|
+
this.editor.commit();
|
|
98
131
|
}
|
|
99
132
|
|
|
100
|
-
public void notifyDownload(final int percent) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
133
|
+
public void notifyDownload(final String id, final int percent) {
|
|
134
|
+
try {
|
|
135
|
+
final JSObject ret = new JSObject();
|
|
136
|
+
ret.put("percent", percent);
|
|
137
|
+
JSObject bundle = this.implementation.getBundleInfo(id).toJSON();
|
|
138
|
+
ret.put("bundle", bundle);
|
|
139
|
+
this.notifyListeners("download", ret);
|
|
140
|
+
if (percent == 100) {
|
|
141
|
+
this.notifyListeners("downloadComplete", bundle);
|
|
142
|
+
}
|
|
143
|
+
} catch (final Exception e) {
|
|
144
|
+
Log.e(CapacitorUpdater.TAG, "Could not notify listeners", e);
|
|
145
|
+
}
|
|
104
146
|
}
|
|
105
147
|
|
|
148
|
+
|
|
106
149
|
@PluginMethod
|
|
107
150
|
public void getId(final PluginCall call) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
151
|
+
try {
|
|
152
|
+
final JSObject ret = new JSObject();
|
|
153
|
+
ret.put("id", this.implementation.deviceID);
|
|
154
|
+
call.resolve(ret);
|
|
155
|
+
} catch (final Exception e) {
|
|
156
|
+
Log.e(CapacitorUpdater.TAG, "Could not get device id", e);
|
|
157
|
+
call.reject("Could not get device id", e);
|
|
158
|
+
}
|
|
111
159
|
}
|
|
112
160
|
|
|
113
161
|
@PluginMethod
|
|
114
162
|
public void getPluginVersion(final PluginCall call) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
163
|
+
try {
|
|
164
|
+
final JSObject ret = new JSObject();
|
|
165
|
+
ret.put("version", CapacitorUpdater.pluginVersion);
|
|
166
|
+
call.resolve(ret);
|
|
167
|
+
} catch (final Exception e) {
|
|
168
|
+
Log.e(CapacitorUpdater.TAG, "Could not get plugin version", e);
|
|
169
|
+
call.reject("Could not get plugin version", e);
|
|
170
|
+
}
|
|
118
171
|
}
|
|
119
172
|
|
|
120
173
|
@PluginMethod
|
|
121
174
|
public void download(final PluginCall call) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
175
|
+
final String url = call.getString("url");
|
|
176
|
+
final String version = call.getString("version");
|
|
177
|
+
if (url == null || version == null) {
|
|
178
|
+
call.reject("missing url or version");
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
try {
|
|
182
|
+
Log.i(CapacitorUpdater.TAG, "Downloading " + url);
|
|
183
|
+
new Thread(new Runnable(){
|
|
184
|
+
@Override
|
|
185
|
+
public void run() {
|
|
186
|
+
try {
|
|
187
|
+
|
|
188
|
+
final BundleInfo downloaded = CapacitorUpdaterPlugin.this.implementation.download(url, version);
|
|
189
|
+
call.resolve(downloaded.toJSON());
|
|
190
|
+
} catch (final IOException e) {
|
|
191
|
+
Log.e(CapacitorUpdater.TAG, "download failed", e);
|
|
192
|
+
call.reject("download failed", e);
|
|
193
|
+
}
|
|
134
194
|
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
195
|
+
}).start();
|
|
196
|
+
} catch (final Exception e) {
|
|
197
|
+
Log.e(CapacitorUpdater.TAG, "Failed to download " + url, e);
|
|
198
|
+
call.reject("Failed to download " + url, e);
|
|
199
|
+
}
|
|
137
200
|
}
|
|
138
201
|
|
|
139
202
|
private boolean _reload() {
|
|
140
|
-
final String
|
|
141
|
-
|
|
203
|
+
final String path = this.implementation.getCurrentBundlePath();
|
|
204
|
+
Log.i(CapacitorUpdater.TAG, "Reloading: " + path);
|
|
205
|
+
if(this.implementation.isUsingBuiltin()) {
|
|
206
|
+
this.bridge.setServerAssetPath(path);
|
|
207
|
+
} else {
|
|
208
|
+
this.bridge.setServerBasePath(path);
|
|
209
|
+
}
|
|
210
|
+
this.checkAppReady();
|
|
142
211
|
return true;
|
|
143
212
|
}
|
|
144
213
|
|
|
145
214
|
@PluginMethod
|
|
146
215
|
public void reload(final PluginCall call) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
216
|
+
try {
|
|
217
|
+
if (this._reload()) {
|
|
218
|
+
call.resolve();
|
|
219
|
+
} else {
|
|
220
|
+
call.reject("Reload failed");
|
|
221
|
+
}
|
|
222
|
+
} catch(final Exception e) {
|
|
223
|
+
Log.e(CapacitorUpdater.TAG, "Could not reload", e);
|
|
224
|
+
call.reject("Could not reload", e);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
@PluginMethod
|
|
229
|
+
public void next(final PluginCall call) {
|
|
230
|
+
final String id = call.getString("id");
|
|
231
|
+
|
|
232
|
+
try {
|
|
233
|
+
Log.i(CapacitorUpdater.TAG, "Setting next active id " + id);
|
|
234
|
+
if (!this.implementation.setNextVersion(id)) {
|
|
235
|
+
call.reject("Set next id failed. Bundle " + id + " does not exist.");
|
|
236
|
+
} else {
|
|
237
|
+
call.resolve(this.implementation.getBundleInfo(id).toJSON());
|
|
238
|
+
}
|
|
239
|
+
} catch (final Exception e) {
|
|
240
|
+
Log.e(CapacitorUpdater.TAG, "Could not set next id " + id, e);
|
|
241
|
+
call.reject("Could not set next id " + id, e);
|
|
151
242
|
}
|
|
152
243
|
}
|
|
153
244
|
|
|
154
245
|
@PluginMethod
|
|
155
246
|
public void set(final PluginCall call) {
|
|
156
|
-
final String
|
|
157
|
-
final String versionName = call.getString("versionName", version);
|
|
158
|
-
final Boolean res = this.implementation.set(version, versionName);
|
|
247
|
+
final String id = call.getString("id");
|
|
159
248
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
249
|
+
try {
|
|
250
|
+
Log.i(CapacitorUpdater.TAG, "Setting active bundle " + id);
|
|
251
|
+
if (!this.implementation.set(id)) {
|
|
252
|
+
Log.i(CapacitorUpdater.TAG, "No such bundle " + id);
|
|
253
|
+
call.reject("Update failed, id " + id + " does not exist.");
|
|
254
|
+
} else {
|
|
255
|
+
Log.i(CapacitorUpdater.TAG, "Bundle successfully set to" + id);
|
|
256
|
+
this.reload(call);
|
|
257
|
+
}
|
|
258
|
+
} catch(final Exception e) {
|
|
259
|
+
Log.e(CapacitorUpdater.TAG, "Could not set id " + id, e);
|
|
260
|
+
call.reject("Could not set id " + id, e);
|
|
164
261
|
}
|
|
165
262
|
}
|
|
166
263
|
|
|
167
264
|
@PluginMethod
|
|
168
265
|
public void delete(final PluginCall call) {
|
|
169
|
-
final String
|
|
266
|
+
final String id = call.getString("id");
|
|
267
|
+
Log.i(CapacitorUpdater.TAG, "Deleting id: " + id);
|
|
170
268
|
try {
|
|
171
|
-
final Boolean res = this.implementation.delete(
|
|
269
|
+
final Boolean res = this.implementation.delete(id);
|
|
172
270
|
if (res) {
|
|
173
271
|
call.resolve();
|
|
174
272
|
} else {
|
|
175
|
-
call.reject("Delete failed,
|
|
273
|
+
call.reject("Delete failed, id " + id + " does not exist");
|
|
176
274
|
}
|
|
177
|
-
} catch(final
|
|
178
|
-
Log.e(
|
|
179
|
-
call.reject("
|
|
275
|
+
} catch(final Exception e) {
|
|
276
|
+
Log.e(CapacitorUpdater.TAG, "Could not delete id " + id, e);
|
|
277
|
+
call.reject("Could not delete id " + id, e);
|
|
180
278
|
}
|
|
181
279
|
}
|
|
182
280
|
|
|
281
|
+
|
|
183
282
|
@PluginMethod
|
|
184
283
|
public void list(final PluginCall call) {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
if (toAutoUpdate && !version.equals("") && !versionName.equals("")) {
|
|
195
|
-
final Boolean res = this.implementation.set(version, versionName);
|
|
196
|
-
return res && this._reload();
|
|
284
|
+
try {
|
|
285
|
+
final List<BundleInfo> res = this.implementation.list();
|
|
286
|
+
final JSObject ret = new JSObject();
|
|
287
|
+
final JSArray values = new JSArray();
|
|
288
|
+
for (final BundleInfo bundle : res) {
|
|
289
|
+
values.put(bundle.toJSON());
|
|
290
|
+
}
|
|
291
|
+
ret.put("bundles", values);
|
|
292
|
+
call.resolve(ret);
|
|
197
293
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
// if the server is not ready yet, hot reload is not needed
|
|
202
|
-
this.bridge.setServerAssetPath(pathHot);
|
|
294
|
+
catch(final Exception e) {
|
|
295
|
+
Log.e(CapacitorUpdater.TAG, "Could not list bundles", e);
|
|
296
|
+
call.reject("Could not list bundles", e);
|
|
203
297
|
}
|
|
204
|
-
return true;
|
|
205
298
|
}
|
|
206
299
|
|
|
207
300
|
@PluginMethod
|
|
208
|
-
public void
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
301
|
+
public void getLatest(final PluginCall call) {
|
|
302
|
+
new Thread(new Runnable(){
|
|
303
|
+
@Override
|
|
304
|
+
public void run() {
|
|
305
|
+
CapacitorUpdaterPlugin.this.implementation.getLatest(CapacitorUpdaterPlugin.this.updateUrl, (res) -> {
|
|
306
|
+
final JSObject ret = new JSObject();
|
|
307
|
+
Iterator<String> keys = res.keys();
|
|
308
|
+
while(keys.hasNext()) {
|
|
309
|
+
String key = keys.next();
|
|
310
|
+
if (res.has(key)) {
|
|
311
|
+
try {
|
|
312
|
+
ret.put(key, res.get(key));
|
|
313
|
+
} catch (JSONException e) {
|
|
314
|
+
e.printStackTrace();
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
call.resolve(ret);
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
private boolean _reset(final Boolean toLastSuccessful) {
|
|
325
|
+
final BundleInfo fallback = this.implementation.getFallbackVersion();
|
|
326
|
+
this.implementation.reset();
|
|
327
|
+
|
|
328
|
+
if (toLastSuccessful && !fallback.isBuiltin()) {
|
|
329
|
+
Log.i(CapacitorUpdater.TAG, "Resetting to: " + fallback);
|
|
330
|
+
return this.implementation.set(fallback) && this._reload();
|
|
213
331
|
}
|
|
214
|
-
|
|
332
|
+
|
|
333
|
+
Log.i(CapacitorUpdater.TAG, "Resetting to native.");
|
|
334
|
+
return this._reload();
|
|
215
335
|
}
|
|
216
336
|
|
|
217
337
|
@PluginMethod
|
|
218
|
-
public void
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
338
|
+
public void reset(final PluginCall call) {
|
|
339
|
+
try {
|
|
340
|
+
final Boolean toLastSuccessful = call.getBoolean("toLastSuccessful", false);
|
|
341
|
+
if (this._reset(toLastSuccessful)) {
|
|
342
|
+
call.resolve();
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
call.reject("Reset failed");
|
|
346
|
+
}
|
|
347
|
+
catch(final Exception e) {
|
|
348
|
+
Log.e(CapacitorUpdater.TAG, "Reset failed", e);
|
|
349
|
+
call.reject("Reset failed", e);
|
|
350
|
+
}
|
|
223
351
|
}
|
|
224
352
|
|
|
225
353
|
@PluginMethod
|
|
226
354
|
public void current(final PluginCall call) {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
355
|
+
try {
|
|
356
|
+
final JSObject ret = new JSObject();
|
|
357
|
+
final BundleInfo bundle = this.implementation.getCurrentBundle();
|
|
358
|
+
ret.put("bundle", bundle.toJSON());
|
|
359
|
+
ret.put("native", this.currentVersionNative);
|
|
360
|
+
call.resolve(ret);
|
|
361
|
+
}
|
|
362
|
+
catch(final Exception e) {
|
|
363
|
+
Log.e(CapacitorUpdater.TAG, "Could not get current bundle", e);
|
|
364
|
+
call.reject("Could not get current bundle", e);
|
|
365
|
+
}
|
|
233
366
|
}
|
|
234
367
|
|
|
235
368
|
@PluginMethod
|
|
236
369
|
public void notifyAppReady(final PluginCall call) {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
370
|
+
try {
|
|
371
|
+
Log.i(CapacitorUpdater.TAG, "Current bundle loaded successfully. ['notifyAppReady()' was called]");
|
|
372
|
+
final BundleInfo bundle = this.implementation.getCurrentBundle();
|
|
373
|
+
this.implementation.commit(bundle);
|
|
374
|
+
call.resolve();
|
|
375
|
+
}
|
|
376
|
+
catch(final Exception e) {
|
|
377
|
+
Log.e(CapacitorUpdater.TAG, "Failed to notify app ready state. [Error calling 'notifyAppReady()']", e);
|
|
378
|
+
call.reject("Failed to commit app ready state.", e);
|
|
379
|
+
}
|
|
240
380
|
}
|
|
241
381
|
|
|
242
382
|
@PluginMethod
|
|
243
383
|
public void delayUpdate(final PluginCall call) {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
384
|
+
try {
|
|
385
|
+
Log.i(CapacitorUpdater.TAG, "Delay update.");
|
|
386
|
+
this.editor.putBoolean(DELAY_UPDATE, true);
|
|
387
|
+
this.editor.commit();
|
|
388
|
+
call.resolve();
|
|
389
|
+
}
|
|
390
|
+
catch(final Exception e) {
|
|
391
|
+
Log.e(CapacitorUpdater.TAG, "Failed to delay update", e);
|
|
392
|
+
call.reject("Failed to delay update", e);
|
|
393
|
+
}
|
|
247
394
|
}
|
|
248
395
|
|
|
249
396
|
@PluginMethod
|
|
250
397
|
public void cancelDelay(final PluginCall call) {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
398
|
+
try {
|
|
399
|
+
Log.i(CapacitorUpdater.TAG, "Cancel update delay.");
|
|
400
|
+
this.editor.putBoolean(DELAY_UPDATE, false);
|
|
401
|
+
this.editor.commit();
|
|
402
|
+
call.resolve();
|
|
403
|
+
}
|
|
404
|
+
catch(final Exception e) {
|
|
405
|
+
Log.e(CapacitorUpdater.TAG, "Failed to cancel update delay", e);
|
|
406
|
+
call.reject("Failed to cancel update delay", e);
|
|
407
|
+
}
|
|
254
408
|
}
|
|
255
409
|
|
|
256
|
-
|
|
410
|
+
private Boolean _isAutoUpdateEnabled() {
|
|
411
|
+
return CapacitorUpdaterPlugin.this.autoUpdate && !"".equals(CapacitorUpdaterPlugin.this.updateUrl);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
@PluginMethod
|
|
415
|
+
public void isAutoUpdateEnabled(final PluginCall call) {
|
|
416
|
+
try {
|
|
417
|
+
final JSObject ret = new JSObject();
|
|
418
|
+
ret.put("enabled", this._isAutoUpdateEnabled());
|
|
419
|
+
call.resolve(ret);
|
|
420
|
+
} catch (final Exception e) {
|
|
421
|
+
Log.e(CapacitorUpdater.TAG, "Could not get autoUpdate status", e);
|
|
422
|
+
call.reject("Could not get autoUpdate status", e);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
private void checkAppReady() {
|
|
427
|
+
try {
|
|
428
|
+
if(this.appReadyCheck != null) {
|
|
429
|
+
this.appReadyCheck.interrupt();
|
|
430
|
+
}
|
|
431
|
+
this.appReadyCheck = new Thread(new DeferredNotifyAppReadyCheck());
|
|
432
|
+
this.appReadyCheck.start();
|
|
433
|
+
} catch (final Exception e) {
|
|
434
|
+
Log.e(CapacitorUpdater.TAG, "Failed to start " + DeferredNotifyAppReadyCheck.class.getName(), e);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
@Override // appMovedToForeground
|
|
257
439
|
public void onActivityStarted(@NonNull final Activity activity) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
440
|
+
if (CapacitorUpdaterPlugin.this._isAutoUpdateEnabled()) {
|
|
441
|
+
new Thread(new Runnable(){
|
|
442
|
+
@Override
|
|
443
|
+
public void run() {
|
|
444
|
+
|
|
445
|
+
Log.i(CapacitorUpdater.TAG, "Check for update via: " + CapacitorUpdaterPlugin.this.updateUrl);
|
|
446
|
+
CapacitorUpdaterPlugin.this.implementation.getLatest(CapacitorUpdaterPlugin.this.updateUrl, (res) -> {
|
|
447
|
+
try {
|
|
448
|
+
if (res.has("message")) {
|
|
449
|
+
Log.i(CapacitorUpdater.TAG, "message: " + res.get("message"));
|
|
450
|
+
if (res.has("major") && res.getBoolean("major") && res.has("version")) {
|
|
451
|
+
final JSObject majorAvailable = new JSObject();
|
|
452
|
+
majorAvailable.put("version", (String) res.get("version"));
|
|
453
|
+
CapacitorUpdaterPlugin.this.notifyListeners("majorAvailable", majorAvailable);
|
|
454
|
+
}
|
|
455
|
+
return;
|
|
272
456
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
Log.i(CapacitorUpdaterPlugin.this.TAG, "Download version: " + newVersion + " failed");
|
|
289
|
-
return;
|
|
290
|
-
}
|
|
291
|
-
Log.i(CapacitorUpdaterPlugin.this.TAG, "New version: " + newVersion + " found. Current is " + (currentVersion.equals("") ? "builtin" : currentVersion) + ", next backgrounding will trigger update");
|
|
292
|
-
CapacitorUpdaterPlugin.this.editor.putString("nextVersion", dl);
|
|
293
|
-
CapacitorUpdaterPlugin.this.editor.putString("nextVersionName", (String) res.get("version"));
|
|
294
|
-
CapacitorUpdaterPlugin.this.editor.commit();
|
|
295
|
-
CapacitorUpdaterPlugin.this.notifyListeners("updateAvailable", ret);
|
|
296
|
-
} catch (final Exception e) {
|
|
297
|
-
Log.e(CapacitorUpdaterPlugin.this.TAG, "error downloading file", e);
|
|
457
|
+
final BundleInfo current = CapacitorUpdaterPlugin.this.implementation.getCurrentBundle();
|
|
458
|
+
final String latestVersionName = (String) res.get("version");
|
|
459
|
+
|
|
460
|
+
if (latestVersionName != null && !"".equals(latestVersionName) && !current.getVersionName().equals(latestVersionName)) {
|
|
461
|
+
|
|
462
|
+
final BundleInfo latest = CapacitorUpdaterPlugin.this.implementation.getBundleInfoByName(latestVersionName);
|
|
463
|
+
if(latest != null) {
|
|
464
|
+
if(latest.isErrorStatus()) {
|
|
465
|
+
Log.e(CapacitorUpdater.TAG, "Latest bundle already exists, and is in error state. Aborting update.");
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
if(latest.isDownloaded()){
|
|
469
|
+
Log.e(CapacitorUpdater.TAG, "Latest bundle already exists and download is NOT required. Update will occur next time app moves to background.");
|
|
470
|
+
CapacitorUpdaterPlugin.this.implementation.setNextVersion(latest.getId());
|
|
471
|
+
return;
|
|
298
472
|
}
|
|
299
473
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
new Thread(new Runnable(){
|
|
477
|
+
@Override
|
|
478
|
+
public void run() {
|
|
479
|
+
try {
|
|
480
|
+
Log.i(CapacitorUpdater.TAG, "New bundle: " + latestVersionName + " found. Current is: " + current.getVersionName() + ". Update will occur next time app moves to background.");
|
|
481
|
+
|
|
482
|
+
final String url = (String) res.get("url");
|
|
483
|
+
final BundleInfo next = CapacitorUpdaterPlugin.this.implementation.download(url, latestVersionName);
|
|
484
|
+
|
|
485
|
+
CapacitorUpdaterPlugin.this.implementation.setNextVersion(next.getId());
|
|
486
|
+
} catch (final Exception e) {
|
|
487
|
+
Log.e(CapacitorUpdater.TAG, "error downloading file", e);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}).start();
|
|
491
|
+
} else {
|
|
492
|
+
Log.i(CapacitorUpdater.TAG, "No need to update, " + current + " is the latest bundle.");
|
|
493
|
+
}
|
|
494
|
+
} catch (final JSONException e) {
|
|
495
|
+
Log.e(CapacitorUpdater.TAG, "error parsing JSON", e);
|
|
303
496
|
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
}).start();
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
this.checkAppReady();
|
|
310
503
|
}
|
|
311
504
|
|
|
312
|
-
@Override
|
|
505
|
+
@Override // appMovedToBackground
|
|
313
506
|
public void onActivityStopped(@NonNull final Activity activity) {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
}
|
|
324
|
-
final String nextVersionName = this.prefs.getString("nextVersionName", "");
|
|
325
|
-
final String pastVersion = this.prefs.getString("pastVersion", "");
|
|
326
|
-
final String pastVersionName = this.prefs.getString("pastVersionName", "");
|
|
327
|
-
final Boolean notifyAppReady = this.prefs.getBoolean("notifyAppReady", false);
|
|
328
|
-
final String tmpCurVersion = this.implementation.getLastPathHot();
|
|
329
|
-
final String curVersion = tmpCurVersion.substring(tmpCurVersion.lastIndexOf('/') +1);
|
|
330
|
-
final String curVersionName = this.implementation.getVersionName();
|
|
331
|
-
if (!nextVersion.equals("") && !nextVersionName.equals("")) {
|
|
332
|
-
final Boolean res = this.implementation.set(nextVersion, nextVersionName);
|
|
333
|
-
if (res && this._reload()) {
|
|
334
|
-
Log.i(this.TAG, "Auto update to version: " + nextVersionName);
|
|
335
|
-
this.editor.putString("LatestVersionAutoUpdate", nextVersion);
|
|
336
|
-
this.editor.putString("LatestVersionNameAutoUpdate", nextVersionName);
|
|
337
|
-
this.editor.putString("nextVersion", "");
|
|
338
|
-
this.editor.putString("nextVersionName", "");
|
|
339
|
-
this.editor.putString("pastVersion", curVersion);
|
|
340
|
-
this.editor.putString("pastVersionName", curVersionName);
|
|
341
|
-
this.editor.putBoolean("notifyAppReady", false);
|
|
342
|
-
this.editor.commit();
|
|
343
|
-
} else {
|
|
344
|
-
Log.i(this.TAG, "Auto update to version: " + nextVersionName + "Failed");
|
|
507
|
+
Log.i(CapacitorUpdater.TAG, "Checking for pending update");
|
|
508
|
+
try {
|
|
509
|
+
final Boolean delayUpdate = this.prefs.getBoolean(DELAY_UPDATE, false);
|
|
510
|
+
this.editor.putBoolean(DELAY_UPDATE, false);
|
|
511
|
+
this.editor.commit();
|
|
512
|
+
|
|
513
|
+
if (delayUpdate) {
|
|
514
|
+
Log.i(CapacitorUpdater.TAG, "Update delayed to next backgrounding");
|
|
515
|
+
return;
|
|
345
516
|
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
517
|
+
|
|
518
|
+
final BundleInfo fallback = this.implementation.getFallbackVersion();
|
|
519
|
+
final BundleInfo current = this.implementation.getCurrentBundle();
|
|
520
|
+
final BundleInfo next = this.implementation.getNextVersion();
|
|
521
|
+
|
|
522
|
+
final Boolean success = current.getStatus() == BundleStatus.SUCCESS;
|
|
523
|
+
|
|
524
|
+
Log.d(CapacitorUpdater.TAG, "Fallback bundle is: " + fallback);
|
|
525
|
+
Log.d(CapacitorUpdater.TAG, "Current bundle is: " + current);
|
|
526
|
+
|
|
527
|
+
if (next != null && !next.isErrorStatus() && (next.getId() != current.getId())) {
|
|
528
|
+
// There is a next bundle waiting for activation
|
|
529
|
+
Log.d(CapacitorUpdater.TAG, "Next bundle is: " + next.getVersionName());
|
|
530
|
+
if (this.implementation.set(next) && this._reload()) {
|
|
531
|
+
Log.i(CapacitorUpdater.TAG, "Updated to bundle: " + next.getVersionName());
|
|
532
|
+
this.implementation.setNextVersion(null);
|
|
361
533
|
} else {
|
|
362
|
-
Log.
|
|
534
|
+
Log.e(CapacitorUpdater.TAG, "Update to bundle: " + next.getVersionName() + " Failed!");
|
|
363
535
|
}
|
|
364
|
-
} else {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
536
|
+
} else if (!success) {
|
|
537
|
+
// There is a no next bundle, and the current bundle has failed
|
|
538
|
+
|
|
539
|
+
if(!current.isBuiltin()) {
|
|
540
|
+
// Don't try to roll back the builtin bundle. Nothing we can do.
|
|
541
|
+
|
|
542
|
+
this.implementation.rollback(current);
|
|
543
|
+
|
|
544
|
+
Log.i(CapacitorUpdater.TAG, "Update failed: 'notifyAppReady()' was never called.");
|
|
545
|
+
Log.i(CapacitorUpdater.TAG, "Bundle: " + current + ", is in error state.");
|
|
546
|
+
Log.i(CapacitorUpdater.TAG, "Will fallback to: " + fallback + " on application restart.");
|
|
547
|
+
Log.i(CapacitorUpdater.TAG, "Did you forget to call 'notifyAppReady()' in your Capacitor App code?");
|
|
548
|
+
final JSObject ret = new JSObject();
|
|
549
|
+
ret.put("bundle", current);
|
|
550
|
+
this.notifyListeners("updateFailed", ret);
|
|
551
|
+
this.implementation.sendStats("update_fail", current.getVersionName());
|
|
552
|
+
if (!fallback.isBuiltin() && !fallback.equals(current)) {
|
|
553
|
+
final Boolean res = this.implementation.set(fallback);
|
|
554
|
+
if (res && this._reload()) {
|
|
555
|
+
Log.i(CapacitorUpdater.TAG, "Revert to bundle: " + fallback.getVersionName());
|
|
556
|
+
} else {
|
|
557
|
+
Log.e(CapacitorUpdater.TAG, "Revert to bundle: " + fallback.getVersionName() + " Failed!");
|
|
558
|
+
}
|
|
559
|
+
} else {
|
|
560
|
+
if (this._reset(false)) {
|
|
561
|
+
Log.i(CapacitorUpdater.TAG, "Reverted to 'builtin' bundle.");
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if (this.autoDeleteFailed) {
|
|
566
|
+
Log.i(CapacitorUpdater.TAG, "Deleting failing bundle: " + current.getVersionName());
|
|
567
|
+
try {
|
|
568
|
+
final Boolean res = this.implementation.delete(current.getId());
|
|
569
|
+
if (res) {
|
|
570
|
+
Log.i(CapacitorUpdater.TAG, "Failed bundle deleted: " + current.getVersionName());
|
|
571
|
+
}
|
|
572
|
+
} catch (final IOException e) {
|
|
573
|
+
Log.e(CapacitorUpdater.TAG, "Failed to delete failed bundle: " + current.getVersionName(), e);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
} else {
|
|
577
|
+
// Nothing we can/should do by default if the 'builtin' bundle fails to call 'notifyAppReady()'.
|
|
369
578
|
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
if
|
|
376
|
-
Log.i(
|
|
579
|
+
|
|
580
|
+
} else if (!fallback.isBuiltin()) {
|
|
581
|
+
// There is a no next bundle, and the current bundle has succeeded
|
|
582
|
+
this.implementation.commit(current);
|
|
583
|
+
|
|
584
|
+
if(this.autoDeletePrevious) {
|
|
585
|
+
Log.i(CapacitorUpdater.TAG, "Bundle successfully loaded: " + current);
|
|
586
|
+
try {
|
|
587
|
+
final Boolean res = this.implementation.delete(fallback.getVersionName());
|
|
588
|
+
if (res) {
|
|
589
|
+
Log.i(CapacitorUpdater.TAG, "Deleted previous bundle: " + fallback.getVersionName());
|
|
590
|
+
}
|
|
591
|
+
} catch (final IOException e) {
|
|
592
|
+
Log.e(CapacitorUpdater.TAG, "Failed to delete previous bundle: " + fallback.getVersionName(), e);
|
|
593
|
+
}
|
|
377
594
|
}
|
|
378
|
-
} catch (final IOException e) {
|
|
379
|
-
Log.e(CapacitorUpdaterPlugin.this.TAG, "error deleting version", e);
|
|
380
595
|
}
|
|
381
|
-
}
|
|
382
|
-
|
|
596
|
+
}
|
|
597
|
+
catch(final Exception e) {
|
|
598
|
+
Log.e(CapacitorUpdater.TAG, "Error during onActivityStopped", e);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
private class DeferredNotifyAppReadyCheck implements Runnable {
|
|
603
|
+
@Override
|
|
604
|
+
public void run() {
|
|
383
605
|
try {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
606
|
+
Log.i(CapacitorUpdater.TAG, "Wait for " + CapacitorUpdaterPlugin.this.appReadyTimeout + "ms, then check for notifyAppReady");
|
|
607
|
+
Thread.sleep(CapacitorUpdaterPlugin.this.appReadyTimeout);
|
|
608
|
+
// Automatically roll back to fallback version if notifyAppReady has not been called yet
|
|
609
|
+
final BundleInfo current = CapacitorUpdaterPlugin.this.implementation.getCurrentBundle();
|
|
610
|
+
if(current.isBuiltin()) {
|
|
611
|
+
Log.i(CapacitorUpdater.TAG, "Built-in bundle is active. Nothing to do.");
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
if(BundleStatus.SUCCESS != current.getStatus()) {
|
|
616
|
+
Log.e(CapacitorUpdater.TAG, "notifyAppReady was not called, roll back current bundle: " + current.getId());
|
|
617
|
+
CapacitorUpdaterPlugin.this.implementation.rollback(current);
|
|
618
|
+
CapacitorUpdaterPlugin.this._reset(true);
|
|
619
|
+
} else {
|
|
620
|
+
Log.i(CapacitorUpdater.TAG, "notifyAppReady was called. This is fine: " + current.getId());
|
|
387
621
|
}
|
|
388
|
-
|
|
389
|
-
|
|
622
|
+
|
|
623
|
+
CapacitorUpdaterPlugin.this.appReadyCheck = null;
|
|
624
|
+
} catch (final InterruptedException e) {
|
|
625
|
+
Log.e(CapacitorUpdater.TAG, DeferredNotifyAppReadyCheck.class.getName() + " was interrupted.");
|
|
390
626
|
}
|
|
391
|
-
this.editor.putString("pastVersion", "");
|
|
392
|
-
this.editor.putString("pastVersionName", "");
|
|
393
|
-
this.editor.commit();
|
|
394
627
|
}
|
|
395
628
|
}
|
|
396
629
|
|
|
397
630
|
// not use but necessary here to remove warnings
|
|
398
631
|
@Override
|
|
399
632
|
public void onActivityResumed(@NonNull final Activity activity) {
|
|
633
|
+
// TODO: Implement background updating based on `backgroundUpdate` and `backgroundUpdateDelay` capacitor.config.ts settings
|
|
400
634
|
}
|
|
401
635
|
|
|
402
636
|
@Override
|
|
403
637
|
public void onActivityPaused(@NonNull final Activity activity) {
|
|
638
|
+
// TODO: Implement background updating based on `backgroundUpdate` and `backgroundUpdateDelay` capacitor.config.ts settings
|
|
404
639
|
}
|
|
405
640
|
@Override
|
|
406
641
|
public void onActivityCreated(@NonNull final Activity activity, @Nullable final Bundle savedInstanceState) {
|