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