html2apk 0.1.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.
Files changed (36) hide show
  1. package/README.md +472 -0
  2. package/bin/html2apk-desktop.js +23 -0
  3. package/bin/html2apk.js +19 -0
  4. package/examples/minimal/app.json +27 -0
  5. package/examples/minimal/dist/MeuApp-1.0.0-debug.apk +0 -0
  6. package/examples/minimal/index.html +41 -0
  7. package/html2apk.png +0 -0
  8. package/index.js +3 -0
  9. package/package.json +76 -0
  10. package/src/android/README.md +7 -0
  11. package/src/bridge/install-bridge.js +16 -0
  12. package/src/cli/index.js +163 -0
  13. package/src/cordova/apk-finder.js +45 -0
  14. package/src/cordova/config-xml.js +110 -0
  15. package/src/cordova/project.js +56 -0
  16. package/src/core/build-apk.js +189 -0
  17. package/src/core/config.js +99 -0
  18. package/src/core/defaults.js +37 -0
  19. package/src/core/validation.js +58 -0
  20. package/src/desktop/main.js +522 -0
  21. package/src/desktop/preload.js +30 -0
  22. package/src/desktop/renderer/index.html +323 -0
  23. package/src/desktop/renderer/renderer.js +1074 -0
  24. package/src/desktop/renderer/styles.css +1208 -0
  25. package/src/index.js +12 -0
  26. package/src/runtime-manager/doctor.js +164 -0
  27. package/src/runtime-manager/index.js +190 -0
  28. package/src/templates/cordova-plugin-html2apk-bridge/package.json +16 -0
  29. package/src/templates/cordova-plugin-html2apk-bridge/plugin.xml +39 -0
  30. package/src/templates/cordova-plugin-html2apk-bridge/src/android/BootReceiver.java +20 -0
  31. package/src/templates/cordova-plugin-html2apk-bridge/src/android/Html2ApkBridge.java +375 -0
  32. package/src/templates/cordova-plugin-html2apk-bridge/src/android/NotificationReceiver.java +112 -0
  33. package/src/templates/cordova-plugin-html2apk-bridge/src/android/NotificationStore.java +91 -0
  34. package/src/templates/cordova-plugin-html2apk-bridge/www/html2apk-bridge.js +129 -0
  35. package/src/utils/command-runner.js +124 -0
  36. package/src/utils/fs-extra.js +111 -0
@@ -0,0 +1,375 @@
1
+ package dev.html2apk.bridge;
2
+
3
+ import android.Manifest;
4
+ import android.app.Activity;
5
+ import android.app.AlarmManager;
6
+ import android.app.NotificationChannel;
7
+ import android.app.NotificationManager;
8
+ import android.app.PendingIntent;
9
+ import android.content.Context;
10
+ import android.content.Intent;
11
+ import android.content.pm.PackageManager;
12
+ import android.net.Uri;
13
+ import android.os.Build;
14
+ import android.os.VibrationEffect;
15
+ import android.os.Vibrator;
16
+ import android.provider.Settings;
17
+ import android.view.View;
18
+ import android.widget.Toast;
19
+
20
+ import androidx.core.app.NotificationCompat;
21
+ import androidx.core.app.NotificationManagerCompat;
22
+
23
+ import org.apache.cordova.CallbackContext;
24
+ import org.apache.cordova.CordovaInterface;
25
+ import org.apache.cordova.CordovaPlugin;
26
+ import org.apache.cordova.CordovaWebView;
27
+ import org.json.JSONArray;
28
+ import org.json.JSONObject;
29
+
30
+ public class Html2ApkBridge extends CordovaPlugin {
31
+ static final String CHANNEL_ID = "html2apk_default";
32
+ static final String EXTRA_NOTIFICATION_CLICKED = "html2apk_notification_clicked";
33
+ static final String EXTRA_NOTIFICATION_DETAIL = "html2apk_notification_detail";
34
+
35
+ private static final int REQUEST_POST_NOTIFICATIONS = 7311;
36
+
37
+ private CallbackContext notificationPermissionCallback;
38
+ private JSONObject initialNotification;
39
+
40
+ @Override
41
+ public void initialize(CordovaInterface cordova, CordovaWebView webView) {
42
+ super.initialize(cordova, webView);
43
+ handleNotificationIntent(cordova.getActivity().getIntent(), false);
44
+ }
45
+
46
+ @Override
47
+ public void onNewIntent(Intent intent) {
48
+ handleNotificationIntent(intent, true);
49
+ }
50
+
51
+ @Override
52
+ public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
53
+ try {
54
+ if ("notify".equals(action)) {
55
+ JSONObject options = args.optJSONObject(0);
56
+ showNotification(options == null ? new JSONObject() : options);
57
+ callbackContext.success();
58
+ return true;
59
+ }
60
+
61
+ if ("scheduleNotification".equals(action)) {
62
+ JSONObject options = args.optJSONObject(0);
63
+ scheduleNotification(options == null ? new JSONObject() : options);
64
+ callbackContext.success();
65
+ return true;
66
+ }
67
+
68
+ if ("vibrate".equals(action)) {
69
+ vibrate(args.optLong(0, 200));
70
+ callbackContext.success();
71
+ return true;
72
+ }
73
+
74
+ if ("toast".equals(action)) {
75
+ toast(args.optString(0, ""));
76
+ callbackContext.success();
77
+ return true;
78
+ }
79
+
80
+ if ("fullscreen".equals(action)) {
81
+ setFullscreen(args.optBoolean(0, true));
82
+ callbackContext.success();
83
+ return true;
84
+ }
85
+
86
+ if ("requestNotificationPermission".equals(action)) {
87
+ requestNotificationPermission(callbackContext);
88
+ return true;
89
+ }
90
+
91
+ if ("notificationPermissionStatus".equals(action)) {
92
+ callbackContext.success(notificationPermissionStatus());
93
+ return true;
94
+ }
95
+
96
+ if ("canScheduleExactAlarms".equals(action)) {
97
+ callbackContext.success(canScheduleExactAlarms() ? 1 : 0);
98
+ return true;
99
+ }
100
+
101
+ if ("openExactAlarmSettings".equals(action)) {
102
+ openExactAlarmSettings();
103
+ callbackContext.success();
104
+ return true;
105
+ }
106
+
107
+ if ("getInitialNotification".equals(action)) {
108
+ callbackContext.success(initialNotification == null ? new JSONObject() : initialNotification);
109
+ initialNotification = null;
110
+ return true;
111
+ }
112
+ } catch (Exception error) {
113
+ callbackContext.error(error.getMessage());
114
+ return true;
115
+ }
116
+
117
+ return false;
118
+ }
119
+
120
+ @Override
121
+ public void onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults) {
122
+ if (requestCode != REQUEST_POST_NOTIFICATIONS || notificationPermissionCallback == null) {
123
+ return;
124
+ }
125
+
126
+ boolean granted = grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED;
127
+ try {
128
+ JSONObject result = notificationPermissionStatus();
129
+ result.put("requested", true);
130
+ result.put("granted", granted);
131
+ notificationPermissionCallback.success(result);
132
+ } catch (Exception error) {
133
+ notificationPermissionCallback.error(error.getMessage());
134
+ } finally {
135
+ notificationPermissionCallback = null;
136
+ }
137
+ }
138
+
139
+ private Context context() {
140
+ return this.cordova.getActivity().getApplicationContext();
141
+ }
142
+
143
+ static void ensureNotificationChannel(Context context) {
144
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
145
+ NotificationChannel channel = new NotificationChannel(
146
+ CHANNEL_ID,
147
+ "html2apk",
148
+ NotificationManager.IMPORTANCE_DEFAULT
149
+ );
150
+ channel.setDescription("Default channel for html2apk notifications.");
151
+ NotificationManager manager = context.getSystemService(NotificationManager.class);
152
+ if (manager != null) {
153
+ manager.createNotificationChannel(channel);
154
+ }
155
+ }
156
+ }
157
+
158
+ private boolean hasNotificationPermission() {
159
+ return Build.VERSION.SDK_INT < 33
160
+ || context().checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED;
161
+ }
162
+
163
+ private void requestNotificationPermission(CallbackContext callbackContext) throws Exception {
164
+ if (Build.VERSION.SDK_INT < 33 || hasNotificationPermission()) {
165
+ callbackContext.success(notificationPermissionStatus());
166
+ return;
167
+ }
168
+
169
+ notificationPermissionCallback = callbackContext;
170
+ cordova.requestPermission(this, REQUEST_POST_NOTIFICATIONS, Manifest.permission.POST_NOTIFICATIONS);
171
+ }
172
+
173
+ private JSONObject notificationPermissionStatus() throws Exception {
174
+ JSONObject result = new JSONObject();
175
+ result.put("required", Build.VERSION.SDK_INT >= 33);
176
+ result.put("granted", hasNotificationPermission());
177
+ result.put("permission", "android.permission.POST_NOTIFICATIONS");
178
+ return result;
179
+ }
180
+
181
+ private void showNotification(JSONObject options) throws Exception {
182
+ if (!hasNotificationPermission()) {
183
+ throw new Exception("POST_NOTIFICATIONS permission is not granted. Call solicitarPermissaoNotificacoes() first.");
184
+ }
185
+
186
+ ensureNotificationChannel(context());
187
+ int id = notificationId(options);
188
+ String title = title(options);
189
+ String text = text(options);
190
+
191
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(context(), CHANNEL_ID)
192
+ .setSmallIcon(context().getApplicationInfo().icon)
193
+ .setContentTitle(title)
194
+ .setContentText(text)
195
+ .setStyle(new NotificationCompat.BigTextStyle().bigText(text))
196
+ .setAutoCancel(true)
197
+ .setContentIntent(createContentIntent(context(), id, detailPayload(options)))
198
+ .setPriority(NotificationCompat.PRIORITY_DEFAULT);
199
+
200
+ NotificationManagerCompat.from(context()).notify(id, builder.build());
201
+ }
202
+
203
+ private void scheduleNotification(JSONObject options) throws Exception {
204
+ long when = options.optLong("quando", options.optLong("when", System.currentTimeMillis() + 60000));
205
+ if (when < System.currentTimeMillis()) {
206
+ when = System.currentTimeMillis() + 1000;
207
+ }
208
+
209
+ int id = notificationId(options);
210
+ NotificationStore.save(context(), id, when, options);
211
+ NotificationReceiver.schedule(context(), id, when, options, canScheduleExactAlarms());
212
+ }
213
+
214
+ private boolean canScheduleExactAlarms() {
215
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
216
+ return true;
217
+ }
218
+
219
+ AlarmManager alarmManager = (AlarmManager) context().getSystemService(Context.ALARM_SERVICE);
220
+ return alarmManager != null && alarmManager.canScheduleExactAlarms();
221
+ }
222
+
223
+ private void openExactAlarmSettings() {
224
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
225
+ return;
226
+ }
227
+
228
+ Intent intent = new Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM);
229
+ intent.setData(Uri.parse("package:" + context().getPackageName()));
230
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
231
+ context().startActivity(intent);
232
+ }
233
+
234
+ private void vibrate(long ms) {
235
+ Vibrator vibrator = (Vibrator) context().getSystemService(Context.VIBRATOR_SERVICE);
236
+ if (vibrator == null) {
237
+ return;
238
+ }
239
+
240
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
241
+ vibrator.vibrate(VibrationEffect.createOneShot(ms, VibrationEffect.DEFAULT_AMPLITUDE));
242
+ } else {
243
+ vibrator.vibrate(ms);
244
+ }
245
+ }
246
+
247
+ private void toast(final String message) {
248
+ this.cordova.getActivity().runOnUiThread(new Runnable() {
249
+ @Override
250
+ public void run() {
251
+ Toast.makeText(context(), message, Toast.LENGTH_SHORT).show();
252
+ }
253
+ });
254
+ }
255
+
256
+ private void setFullscreen(final boolean enabled) {
257
+ this.cordova.getActivity().runOnUiThread(new Runnable() {
258
+ @Override
259
+ public void run() {
260
+ View decor = cordova.getActivity().getWindow().getDecorView();
261
+ if (enabled) {
262
+ decor.setSystemUiVisibility(
263
+ View.SYSTEM_UI_FLAG_FULLSCREEN
264
+ | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
265
+ | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
266
+ | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
267
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
268
+ | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
269
+ );
270
+ } else {
271
+ decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
272
+ }
273
+ }
274
+ });
275
+ }
276
+
277
+ private void handleNotificationIntent(Intent intent, boolean dispatchToJs) {
278
+ if (intent == null || !intent.getBooleanExtra(EXTRA_NOTIFICATION_CLICKED, false)) {
279
+ return;
280
+ }
281
+
282
+ JSONObject detail = parseDetail(intent.getStringExtra(EXTRA_NOTIFICATION_DETAIL));
283
+ initialNotification = detail;
284
+
285
+ if (dispatchToJs) {
286
+ dispatchNotificationClick(detail);
287
+ }
288
+
289
+ intent.removeExtra(EXTRA_NOTIFICATION_CLICKED);
290
+ intent.removeExtra(EXTRA_NOTIFICATION_DETAIL);
291
+ }
292
+
293
+ private void dispatchNotificationClick(JSONObject detail) {
294
+ if (detail == null || webView == null) {
295
+ return;
296
+ }
297
+
298
+ final String script = "(function(){var detail=" + detail.toString()
299
+ + ";window.dispatchEvent(new CustomEvent('html2apk:notification',{detail:detail}));"
300
+ + "if(window.Html2ApkNative&&typeof window.Html2ApkNative.__emitNotificationClick==='function'){"
301
+ + "window.Html2ApkNative.__emitNotificationClick(detail);}})();";
302
+
303
+ cordova.getActivity().runOnUiThread(new Runnable() {
304
+ @Override
305
+ public void run() {
306
+ webView.getEngine().evaluateJavascript(script, null);
307
+ }
308
+ });
309
+ }
310
+
311
+ private JSONObject parseDetail(String raw) {
312
+ try {
313
+ if (raw == null || raw.length() == 0) {
314
+ return null;
315
+ }
316
+ return new JSONObject(raw);
317
+ } catch (Exception ignored) {
318
+ return null;
319
+ }
320
+ }
321
+
322
+ static PendingIntent createContentIntent(Context context, int id, JSONObject detail) {
323
+ Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
324
+ if (launchIntent == null) {
325
+ launchIntent = new Intent();
326
+ launchIntent.setPackage(context.getPackageName());
327
+ }
328
+
329
+ launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
330
+ launchIntent.putExtra(EXTRA_NOTIFICATION_CLICKED, true);
331
+ launchIntent.putExtra(EXTRA_NOTIFICATION_DETAIL, detail == null ? "{}" : detail.toString());
332
+
333
+ return PendingIntent.getActivity(
334
+ context,
335
+ id,
336
+ launchIntent,
337
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
338
+ );
339
+ }
340
+
341
+ static JSONObject detailPayload(JSONObject options) throws Exception {
342
+ JSONObject detail = new JSONObject();
343
+ JSONObject click = options.optJSONObject("aoClicar");
344
+ if (click == null) {
345
+ click = options.optJSONObject("onClick");
346
+ }
347
+
348
+ detail.put("id", notificationId(options));
349
+ detail.put("title", title(options));
350
+ detail.put("titulo", title(options));
351
+ detail.put("text", text(options));
352
+ detail.put("texto", text(options));
353
+ detail.put("when", options.optLong("quando", options.optLong("when", System.currentTimeMillis())));
354
+ detail.put("clickedAt", System.currentTimeMillis());
355
+ detail.put("onClick", click == null ? new JSONObject().put("action", "open-app") : click);
356
+ detail.put("aoClicar", click == null ? new JSONObject().put("acao", "abrir-app") : click);
357
+ return detail;
358
+ }
359
+
360
+ static int notificationId(JSONObject options) {
361
+ int id = options.optInt("id", 0);
362
+ if (id != 0) {
363
+ return id;
364
+ }
365
+ return (int) (System.currentTimeMillis() & 0x0fffffff);
366
+ }
367
+
368
+ static String title(JSONObject options) {
369
+ return options.optString("titulo", options.optString("title", "Notificacao"));
370
+ }
371
+
372
+ static String text(JSONObject options) {
373
+ return options.optString("texto", options.optString("text", ""));
374
+ }
375
+ }
@@ -0,0 +1,112 @@
1
+ package dev.html2apk.bridge;
2
+
3
+ import android.Manifest;
4
+ import android.app.AlarmManager;
5
+ import android.app.NotificationManager;
6
+ import android.app.PendingIntent;
7
+ import android.content.BroadcastReceiver;
8
+ import android.content.Context;
9
+ import android.content.Intent;
10
+ import android.content.pm.PackageManager;
11
+ import android.os.Build;
12
+
13
+ import androidx.core.app.NotificationCompat;
14
+ import androidx.core.app.NotificationManagerCompat;
15
+
16
+ import org.json.JSONObject;
17
+
18
+ public class NotificationReceiver extends BroadcastReceiver {
19
+ private static final String EXTRA_NOTIFICATION_ID = "html2apk_notification_id";
20
+ private static final String EXTRA_NOTIFICATION_OPTIONS = "html2apk_notification_options";
21
+
22
+ @Override
23
+ public void onReceive(Context context, Intent intent) {
24
+ if (Build.VERSION.SDK_INT >= 33 && context.checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
25
+ return;
26
+ }
27
+
28
+ JSONObject options = parseOptions(intent);
29
+ int id = intent.getIntExtra(EXTRA_NOTIFICATION_ID, Html2ApkBridge.notificationId(options));
30
+ NotificationStore.remove(context, id);
31
+
32
+ Html2ApkBridge.ensureNotificationChannel(context);
33
+
34
+ String title = Html2ApkBridge.title(options);
35
+ String text = Html2ApkBridge.text(options);
36
+ JSONObject detail;
37
+ try {
38
+ detail = Html2ApkBridge.detailPayload(options);
39
+ } catch (Exception error) {
40
+ detail = new JSONObject();
41
+ }
42
+
43
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(context, Html2ApkBridge.CHANNEL_ID)
44
+ .setSmallIcon(context.getApplicationInfo().icon)
45
+ .setContentTitle(title)
46
+ .setContentText(text)
47
+ .setStyle(new NotificationCompat.BigTextStyle().bigText(text))
48
+ .setAutoCancel(true)
49
+ .setContentIntent(Html2ApkBridge.createContentIntent(context, id, detail))
50
+ .setPriority(NotificationCompat.PRIORITY_DEFAULT);
51
+
52
+ NotificationManagerCompat.from(context).notify(id, builder.build());
53
+ }
54
+
55
+ static void schedule(Context context, int id, long when, JSONObject options, boolean exactAllowed) {
56
+ Intent intent = new Intent(context, NotificationReceiver.class);
57
+ intent.putExtra(EXTRA_NOTIFICATION_ID, id);
58
+ intent.putExtra(EXTRA_NOTIFICATION_OPTIONS, options.toString());
59
+
60
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(
61
+ context,
62
+ id,
63
+ intent,
64
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
65
+ );
66
+
67
+ AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
68
+ if (alarmManager == null) {
69
+ return;
70
+ }
71
+
72
+ if (exactAllowed) {
73
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
74
+ alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, when, pendingIntent);
75
+ } else {
76
+ alarmManager.setExact(AlarmManager.RTC_WAKEUP, when, pendingIntent);
77
+ }
78
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
79
+ alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, when, pendingIntent);
80
+ } else {
81
+ alarmManager.set(AlarmManager.RTC_WAKEUP, when, pendingIntent);
82
+ }
83
+ }
84
+
85
+ static void cancel(Context context, int id) {
86
+ Intent intent = new Intent(context, NotificationReceiver.class);
87
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(
88
+ context,
89
+ id,
90
+ intent,
91
+ PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_IMMUTABLE
92
+ );
93
+
94
+ if (pendingIntent == null) {
95
+ return;
96
+ }
97
+
98
+ AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
99
+ if (alarmManager != null) {
100
+ alarmManager.cancel(pendingIntent);
101
+ }
102
+ pendingIntent.cancel();
103
+ }
104
+
105
+ private static JSONObject parseOptions(Intent intent) {
106
+ try {
107
+ return new JSONObject(intent.getStringExtra(EXTRA_NOTIFICATION_OPTIONS));
108
+ } catch (Exception ignored) {
109
+ return new JSONObject();
110
+ }
111
+ }
112
+ }
@@ -0,0 +1,91 @@
1
+ package dev.html2apk.bridge;
2
+
3
+ import android.content.Context;
4
+ import android.content.SharedPreferences;
5
+
6
+ import org.json.JSONArray;
7
+ import org.json.JSONObject;
8
+
9
+ public final class NotificationStore {
10
+ private static final String PREFS = "html2apk_notifications";
11
+ private static final String KEY_ITEMS = "items";
12
+
13
+ private NotificationStore() {}
14
+
15
+ static void save(Context context, int id, long when, JSONObject options) throws Exception {
16
+ JSONArray items = all(context);
17
+ JSONArray next = new JSONArray();
18
+
19
+ for (int index = 0; index < items.length(); index += 1) {
20
+ JSONObject item = items.optJSONObject(index);
21
+ if (item != null && item.optInt("id") != id) {
22
+ next.put(item);
23
+ }
24
+ }
25
+
26
+ JSONObject item = new JSONObject();
27
+ item.put("id", id);
28
+ item.put("when", when);
29
+ item.put("options", options);
30
+ next.put(item);
31
+ write(context, next);
32
+ }
33
+
34
+ static void remove(Context context, int id) {
35
+ try {
36
+ JSONArray items = all(context);
37
+ JSONArray next = new JSONArray();
38
+
39
+ for (int index = 0; index < items.length(); index += 1) {
40
+ JSONObject item = items.optJSONObject(index);
41
+ if (item != null && item.optInt("id") != id) {
42
+ next.put(item);
43
+ }
44
+ }
45
+
46
+ write(context, next);
47
+ } catch (Exception ignored) {
48
+ }
49
+ }
50
+
51
+ static void rescheduleAll(Context context, boolean exactAllowed) {
52
+ try {
53
+ long now = System.currentTimeMillis();
54
+ JSONArray items = all(context);
55
+ JSONArray futureItems = new JSONArray();
56
+
57
+ for (int index = 0; index < items.length(); index += 1) {
58
+ JSONObject item = items.optJSONObject(index);
59
+ if (item == null) {
60
+ continue;
61
+ }
62
+
63
+ int id = item.optInt("id");
64
+ long when = item.optLong("when");
65
+ JSONObject options = item.optJSONObject("options");
66
+ if (options == null || when <= now) {
67
+ continue;
68
+ }
69
+
70
+ NotificationReceiver.schedule(context, id, when, options, exactAllowed);
71
+ futureItems.put(item);
72
+ }
73
+
74
+ write(context, futureItems);
75
+ } catch (Exception ignored) {
76
+ }
77
+ }
78
+
79
+ private static JSONArray all(Context context) throws Exception {
80
+ String raw = prefs(context).getString(KEY_ITEMS, "[]");
81
+ return new JSONArray(raw);
82
+ }
83
+
84
+ private static void write(Context context, JSONArray items) {
85
+ prefs(context).edit().putString(KEY_ITEMS, items.toString()).apply();
86
+ }
87
+
88
+ private static SharedPreferences prefs(Context context) {
89
+ return context.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
90
+ }
91
+ }
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+
3
+ var exec = require("cordova/exec");
4
+
5
+ var notificationListeners = [];
6
+ var initialNotification = null;
7
+
8
+ function call(action, args) {
9
+ return new Promise(function (resolve, reject) {
10
+ exec(resolve, reject, "Html2ApkBridge", action, args || []);
11
+ });
12
+ }
13
+
14
+ function normalizeNotificationOptions(messageOrOptions) {
15
+ if (typeof messageOrOptions === "string") {
16
+ return {
17
+ title: "Notificacao",
18
+ text: messageOrOptions,
19
+ onClick: {
20
+ action: "open-app"
21
+ }
22
+ };
23
+ }
24
+
25
+ var options = messageOrOptions || {};
26
+ if (!options.onClick && !options.aoClicar) {
27
+ options.onClick = {
28
+ action: "open-app"
29
+ };
30
+ }
31
+ return options;
32
+ }
33
+
34
+ function emitNotificationClick(detail) {
35
+ initialNotification = detail || null;
36
+
37
+ notificationListeners.slice().forEach(function (listener) {
38
+ try {
39
+ listener(initialNotification);
40
+ } catch (error) {
41
+ setTimeout(function () {
42
+ throw error;
43
+ }, 0);
44
+ }
45
+ });
46
+ }
47
+
48
+ var api = {
49
+ notificar: function (messageOrOptions) {
50
+ return call("notify", [normalizeNotificationOptions(messageOrOptions)]);
51
+ },
52
+ agendarNotificacao: function (options) {
53
+ return call("scheduleNotification", [normalizeNotificationOptions(options || {})]);
54
+ },
55
+ vibrar: function (ms) {
56
+ return call("vibrate", [Number(ms) || 200]);
57
+ },
58
+ toast: function (message) {
59
+ return call("toast", [String(message || "")]);
60
+ },
61
+ fullscreen: function (enabled) {
62
+ return call("fullscreen", [Boolean(enabled)]);
63
+ },
64
+ solicitarPermissaoNotificacoes: function () {
65
+ return call("requestNotificationPermission");
66
+ },
67
+ statusPermissaoNotificacoes: function () {
68
+ return call("notificationPermissionStatus");
69
+ },
70
+ podeAgendarNotificacaoExata: function () {
71
+ return call("canScheduleExactAlarms");
72
+ },
73
+ abrirConfiguracaoAlarmeExato: function () {
74
+ return call("openExactAlarmSettings");
75
+ },
76
+ obterNotificacaoInicial: function () {
77
+ return call("getInitialNotification").then(function (notification) {
78
+ initialNotification = notification && notification.id ? notification : null;
79
+ return initialNotification;
80
+ });
81
+ },
82
+ aoClicarNotificacao: function (listener) {
83
+ if (typeof listener !== "function") {
84
+ throw new TypeError("listener must be a function");
85
+ }
86
+
87
+ notificationListeners.push(listener);
88
+ if (initialNotification) {
89
+ listener(initialNotification);
90
+ }
91
+
92
+ return function unsubscribe() {
93
+ notificationListeners = notificationListeners.filter(function (item) {
94
+ return item !== listener;
95
+ });
96
+ };
97
+ },
98
+ __emitNotificationClick: function (detail) {
99
+ emitNotificationClick(detail);
100
+ }
101
+ };
102
+
103
+ if (typeof window !== "undefined") {
104
+ window.notificar = api.notificar;
105
+ window.agendarNotificacao = api.agendarNotificacao;
106
+ window.vibrar = api.vibrar;
107
+ window.toast = api.toast;
108
+ window.fullscreen = api.fullscreen;
109
+ window.solicitarPermissaoNotificacoes = api.solicitarPermissaoNotificacoes;
110
+ window.statusPermissaoNotificacoes = api.statusPermissaoNotificacoes;
111
+ window.podeAgendarNotificacaoExata = api.podeAgendarNotificacaoExata;
112
+ window.abrirConfiguracaoAlarmeExato = api.abrirConfiguracaoAlarmeExato;
113
+ window.obterNotificacaoInicial = api.obterNotificacaoInicial;
114
+ window.aoClicarNotificacao = api.aoClicarNotificacao;
115
+
116
+ window.addEventListener("html2apk:notification", function (event) {
117
+ emitNotificationClick(event.detail);
118
+ });
119
+
120
+ document.addEventListener("deviceready", function () {
121
+ api.obterNotificacaoInicial().then(function (notification) {
122
+ if (notification) {
123
+ emitNotificationClick(notification);
124
+ }
125
+ });
126
+ }, false);
127
+ }
128
+
129
+ module.exports = api;