html2apk 0.3.0 → 0.5.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 +114 -31
- package/examples/minimal/dist/MeuApp-1.0.0-debug.apk +0 -0
- package/package.json +1 -1
- package/src/cordova/project.js +19 -1
- package/src/core/build-apk.js +268 -30
- package/src/desktop/main.js +132 -1
- package/src/desktop/preload.js +13 -0
- package/src/desktop/renderer/index.html +3 -2
- package/src/desktop/renderer/renderer.js +997 -39
- package/src/desktop/renderer/styles.css +60 -2
- package/src/templates/cordova-plugin-html2apk-bridge/plugin.xml +2 -0
- package/src/templates/cordova-plugin-html2apk-bridge/src/android/Html2ApkBridge.java +458 -34
- package/src/templates/cordova-plugin-html2apk-bridge/src/android/NotificationClickReceiver.java +28 -0
- package/src/templates/cordova-plugin-html2apk-bridge/src/android/NotificationReceiver.java +1 -1
- package/src/templates/cordova-plugin-html2apk-bridge/www/html2apk-bridge.js +306 -10
- package/src/templates/html2apk-early-bridge.js +860 -0
- package/src/templates/html2apk-onesignal.js +1 -1
- package/src/utils/command-runner.js +3 -0
|
@@ -14,6 +14,7 @@ import android.content.ClipboardManager;
|
|
|
14
14
|
import android.content.Context;
|
|
15
15
|
import android.content.Intent;
|
|
16
16
|
import android.content.IntentFilter;
|
|
17
|
+
import android.content.SharedPreferences;
|
|
17
18
|
import android.content.pm.ApplicationInfo;
|
|
18
19
|
import android.content.pm.PackageManager;
|
|
19
20
|
import android.database.Cursor;
|
|
@@ -70,17 +71,26 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
70
71
|
private static final int REQUEST_PICK_FILE = 7411;
|
|
71
72
|
private static final int REQUEST_SAVE_FILE = 7412;
|
|
72
73
|
private static final int REQUEST_PICK_FOLDER = 7413;
|
|
74
|
+
private static final String PREFS_NAME = "html2apk_bridge";
|
|
75
|
+
private static final String PREF_PERMISSION_PREFIX = "permission_requested_";
|
|
76
|
+
private static Html2ApkBridge activeBridge;
|
|
73
77
|
|
|
74
78
|
private CallbackContext notificationPermissionCallback;
|
|
75
79
|
private CallbackContext cameraPermissionCallback;
|
|
80
|
+
private CallbackContext pendingNotificationCallback;
|
|
81
|
+
private CallbackContext pendingFlashlightCallback;
|
|
76
82
|
private CallbackContext microphonePermissionCallback;
|
|
77
83
|
private CallbackContext pendingMicStartCallback;
|
|
78
84
|
private CallbackContext filePickerCallback;
|
|
79
85
|
private CallbackContext saveFileCallback;
|
|
80
86
|
private CallbackContext folderPickerCallback;
|
|
81
87
|
private JSONObject pendingSaveFile;
|
|
88
|
+
private JSONObject pendingNotificationOptions;
|
|
82
89
|
private JSONObject initialNotification;
|
|
83
90
|
private JSONObject initialLink;
|
|
91
|
+
private Boolean pendingFlashlightEnabled;
|
|
92
|
+
private boolean pendingNotificationSchedule;
|
|
93
|
+
private boolean pendingFlashlightToggle;
|
|
84
94
|
private boolean overlaySettingsOpened;
|
|
85
95
|
private boolean torchEnabled;
|
|
86
96
|
private MediaRecorder micRecorder;
|
|
@@ -91,6 +101,7 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
91
101
|
@Override
|
|
92
102
|
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
|
93
103
|
super.initialize(cordova, webView);
|
|
104
|
+
activeBridge = this;
|
|
94
105
|
handleNotificationIntent(cordova.getActivity().getIntent(), false);
|
|
95
106
|
handleLinkIntent(cordova.getActivity().getIntent(), false);
|
|
96
107
|
registerSystemReceiver();
|
|
@@ -122,6 +133,9 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
122
133
|
stopMicRecorderSilently();
|
|
123
134
|
unregisterSystemReceiver();
|
|
124
135
|
dispatchEvent("app:fechado", baseEvent("app:fechado"));
|
|
136
|
+
if (activeBridge == this) {
|
|
137
|
+
activeBridge = null;
|
|
138
|
+
}
|
|
125
139
|
super.onDestroy();
|
|
126
140
|
}
|
|
127
141
|
|
|
@@ -130,6 +144,9 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
130
144
|
try {
|
|
131
145
|
if ("notify".equals(action)) {
|
|
132
146
|
JSONObject options = args.optJSONObject(0);
|
|
147
|
+
if (requestNotificationPermissionForAction(options == null ? new JSONObject() : options, false, callbackContext)) {
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
133
150
|
showNotification(options == null ? new JSONObject() : options);
|
|
134
151
|
callbackContext.success();
|
|
135
152
|
return true;
|
|
@@ -137,6 +154,9 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
137
154
|
|
|
138
155
|
if ("scheduleNotification".equals(action)) {
|
|
139
156
|
JSONObject options = args.optJSONObject(0);
|
|
157
|
+
if (requestNotificationPermissionForAction(options == null ? new JSONObject() : options, true, callbackContext)) {
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
140
160
|
callbackContext.success(scheduleNotification(options == null ? new JSONObject() : options));
|
|
141
161
|
return true;
|
|
142
162
|
}
|
|
@@ -183,14 +203,12 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
183
203
|
}
|
|
184
204
|
|
|
185
205
|
if ("flashlight".equals(action)) {
|
|
186
|
-
|
|
187
|
-
callbackContext.success(flashlightStatus());
|
|
206
|
+
setFlashlightWithPermission(args.optBoolean(0, true), false, callbackContext);
|
|
188
207
|
return true;
|
|
189
208
|
}
|
|
190
209
|
|
|
191
210
|
if ("toggleFlashlight".equals(action)) {
|
|
192
|
-
|
|
193
|
-
callbackContext.success(flashlightStatus());
|
|
211
|
+
setFlashlightWithPermission(false, true, callbackContext);
|
|
194
212
|
return true;
|
|
195
213
|
}
|
|
196
214
|
|
|
@@ -343,7 +361,14 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
343
361
|
|
|
344
362
|
if ("openExactAlarmSettings".equals(action)) {
|
|
345
363
|
openExactAlarmSettings();
|
|
346
|
-
|
|
364
|
+
JSONObject result = new JSONObject();
|
|
365
|
+
result.put("permission", "android.permission.SCHEDULE_EXACT_ALARM");
|
|
366
|
+
result.put("required", Build.VERSION.SDK_INT >= Build.VERSION_CODES.S);
|
|
367
|
+
result.put("granted", canScheduleExactAlarms());
|
|
368
|
+
result.put("permissionGranted", canScheduleExactAlarms());
|
|
369
|
+
result.put("requiresSettings", Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !canScheduleExactAlarms());
|
|
370
|
+
result.put("settingsOpened", Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !canScheduleExactAlarms());
|
|
371
|
+
callbackContext.success(result);
|
|
347
372
|
return true;
|
|
348
373
|
}
|
|
349
374
|
|
|
@@ -354,11 +379,22 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
354
379
|
|
|
355
380
|
if ("requestOverlayPermission".equals(action) || "openOverlaySettings".equals(action)) {
|
|
356
381
|
openOverlaySettings();
|
|
357
|
-
|
|
382
|
+
JSONObject result = overlayPermissionStatus();
|
|
383
|
+
result.put("requested", true);
|
|
384
|
+
result.put("settingsOpened", result.optBoolean("requiresSettings"));
|
|
385
|
+
callbackContext.success(result);
|
|
358
386
|
return true;
|
|
359
387
|
}
|
|
360
388
|
|
|
361
389
|
if ("startFloatingIcon".equals(action)) {
|
|
390
|
+
if (!canDrawOverlays()) {
|
|
391
|
+
openOverlaySettings();
|
|
392
|
+
JSONObject result = overlayPermissionStatus();
|
|
393
|
+
result.put("requested", true);
|
|
394
|
+
result.put("requiresSettings", true);
|
|
395
|
+
callbackContext.success(result);
|
|
396
|
+
return true;
|
|
397
|
+
}
|
|
362
398
|
startFloatingIcon();
|
|
363
399
|
callbackContext.success(overlayPermissionStatus());
|
|
364
400
|
return true;
|
|
@@ -391,14 +427,50 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
391
427
|
|
|
392
428
|
@Override
|
|
393
429
|
public void onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults) {
|
|
394
|
-
if (requestCode == REQUEST_CAMERA
|
|
430
|
+
if (requestCode == REQUEST_CAMERA) {
|
|
395
431
|
boolean granted = grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED;
|
|
432
|
+
if (pendingFlashlightCallback != null) {
|
|
433
|
+
CallbackContext callback = pendingFlashlightCallback;
|
|
434
|
+
Boolean enabled = pendingFlashlightEnabled;
|
|
435
|
+
boolean toggle = pendingFlashlightToggle;
|
|
436
|
+
pendingFlashlightCallback = null;
|
|
437
|
+
pendingFlashlightEnabled = null;
|
|
438
|
+
pendingFlashlightToggle = false;
|
|
439
|
+
|
|
440
|
+
try {
|
|
441
|
+
if (!granted) {
|
|
442
|
+
JSONObject result = shouldOpenSettingsForRuntimePermission(Manifest.permission.CAMERA)
|
|
443
|
+
? openSettingsForRuntimePermission(Manifest.permission.CAMERA, true, true)
|
|
444
|
+
: flashlightStatus();
|
|
445
|
+
result.put("requested", true);
|
|
446
|
+
result.put("granted", false);
|
|
447
|
+
callback.success(result);
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
setFlashlight(toggle ? !torchEnabled : Boolean.TRUE.equals(enabled));
|
|
452
|
+
JSONObject result = flashlightStatus();
|
|
453
|
+
result.put("requested", true);
|
|
454
|
+
result.put("granted", true);
|
|
455
|
+
callback.success(result);
|
|
456
|
+
} catch (Exception error) {
|
|
457
|
+
callback.error(error.getMessage());
|
|
458
|
+
}
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
if (cameraPermissionCallback == null) {
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
|
|
396
466
|
try {
|
|
397
|
-
JSONObject result =
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
467
|
+
JSONObject result = granted
|
|
468
|
+
? runtimePermissionResult(Manifest.permission.CAMERA, true, true, true)
|
|
469
|
+
: (
|
|
470
|
+
shouldOpenSettingsForRuntimePermission(Manifest.permission.CAMERA)
|
|
471
|
+
? openSettingsForRuntimePermission(Manifest.permission.CAMERA, true, true)
|
|
472
|
+
: runtimePermissionResult(Manifest.permission.CAMERA, true, true, false)
|
|
473
|
+
);
|
|
402
474
|
cameraPermissionCallback.success(result);
|
|
403
475
|
} catch (Exception error) {
|
|
404
476
|
cameraPermissionCallback.error(error.getMessage());
|
|
@@ -423,7 +495,13 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
423
495
|
if (shouldStartRecording && granted) {
|
|
424
496
|
startMicRecorder(callback);
|
|
425
497
|
} else {
|
|
426
|
-
JSONObject result =
|
|
498
|
+
JSONObject result = granted
|
|
499
|
+
? microphoneStatus()
|
|
500
|
+
: (
|
|
501
|
+
shouldOpenSettingsForRuntimePermission(Manifest.permission.RECORD_AUDIO)
|
|
502
|
+
? openSettingsForRuntimePermission(Manifest.permission.RECORD_AUDIO, true, true)
|
|
503
|
+
: microphoneStatus()
|
|
504
|
+
);
|
|
427
505
|
result.put("requested", true);
|
|
428
506
|
result.put("granted", granted);
|
|
429
507
|
callback.success(result);
|
|
@@ -434,13 +512,57 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
434
512
|
return;
|
|
435
513
|
}
|
|
436
514
|
|
|
437
|
-
if (requestCode != REQUEST_POST_NOTIFICATIONS
|
|
515
|
+
if (requestCode != REQUEST_POST_NOTIFICATIONS) {
|
|
438
516
|
return;
|
|
439
517
|
}
|
|
440
518
|
|
|
441
519
|
boolean granted = grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED;
|
|
520
|
+
if (pendingNotificationCallback != null) {
|
|
521
|
+
CallbackContext callback = pendingNotificationCallback;
|
|
522
|
+
JSONObject options = pendingNotificationOptions == null ? new JSONObject() : pendingNotificationOptions;
|
|
523
|
+
boolean schedule = pendingNotificationSchedule;
|
|
524
|
+
pendingNotificationCallback = null;
|
|
525
|
+
pendingNotificationOptions = null;
|
|
526
|
+
pendingNotificationSchedule = false;
|
|
527
|
+
|
|
528
|
+
try {
|
|
529
|
+
if (!granted) {
|
|
530
|
+
JSONObject result = shouldOpenSettingsForRuntimePermission(Manifest.permission.POST_NOTIFICATIONS)
|
|
531
|
+
? openSettingsForRuntimePermission(Manifest.permission.POST_NOTIFICATIONS, true, true)
|
|
532
|
+
: notificationPermissionStatus();
|
|
533
|
+
result.put("requested", true);
|
|
534
|
+
result.put("granted", false);
|
|
535
|
+
callback.success(result);
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
if (schedule) {
|
|
540
|
+
JSONObject result = scheduleNotification(options);
|
|
541
|
+
result.put("requested", true);
|
|
542
|
+
result.put("granted", true);
|
|
543
|
+
callback.success(result);
|
|
544
|
+
} else {
|
|
545
|
+
showNotification(options);
|
|
546
|
+
callback.success();
|
|
547
|
+
}
|
|
548
|
+
} catch (Exception error) {
|
|
549
|
+
callback.error(error.getMessage());
|
|
550
|
+
}
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
if (notificationPermissionCallback == null) {
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
|
|
442
558
|
try {
|
|
443
|
-
JSONObject result =
|
|
559
|
+
JSONObject result = granted
|
|
560
|
+
? notificationPermissionStatus()
|
|
561
|
+
: (
|
|
562
|
+
shouldOpenSettingsForRuntimePermission(Manifest.permission.POST_NOTIFICATIONS)
|
|
563
|
+
? openSettingsForRuntimePermission(Manifest.permission.POST_NOTIFICATIONS, true, true)
|
|
564
|
+
: notificationPermissionStatus()
|
|
565
|
+
);
|
|
444
566
|
result.put("requested", true);
|
|
445
567
|
result.put("granted", granted);
|
|
446
568
|
notificationPermissionCallback.success(result);
|
|
@@ -472,6 +594,78 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
472
594
|
return this.cordova.getActivity().getApplicationContext();
|
|
473
595
|
}
|
|
474
596
|
|
|
597
|
+
private SharedPreferences preferencesStore() {
|
|
598
|
+
return context().getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
private boolean wasRuntimePermissionRequested(String permission) {
|
|
602
|
+
return preferencesStore().getBoolean(PREF_PERMISSION_PREFIX + permission, false);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
private void rememberRuntimePermissionRequest(String permission) {
|
|
606
|
+
preferencesStore().edit().putBoolean(PREF_PERMISSION_PREFIX + permission, true).apply();
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
private boolean shouldOpenSettingsForRuntimePermission(String permission) {
|
|
610
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || !wasRuntimePermissionRequested(permission)) {
|
|
611
|
+
return false;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
return !cordova.getActivity().shouldShowRequestPermissionRationale(permission);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
private JSONObject runtimePermissionResult(String permission, boolean required, boolean requested, boolean granted) throws Exception {
|
|
618
|
+
JSONObject result = new JSONObject();
|
|
619
|
+
result.put("permission", permission);
|
|
620
|
+
result.put("required", required);
|
|
621
|
+
result.put("requested", requested);
|
|
622
|
+
result.put("granted", granted);
|
|
623
|
+
result.put("permissionGranted", granted);
|
|
624
|
+
result.put("requiresSettings", required && !granted && shouldOpenSettingsForRuntimePermission(permission));
|
|
625
|
+
result.put("settingsOpened", false);
|
|
626
|
+
return result;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
private JSONObject openSettingsForRuntimePermission(String permission, boolean required, boolean requested) throws Exception {
|
|
630
|
+
JSONObject result = runtimePermissionResult(permission, required, requested, false);
|
|
631
|
+
openPermissionSettings(permission);
|
|
632
|
+
result.put("requiresSettings", true);
|
|
633
|
+
result.put("settingsOpened", true);
|
|
634
|
+
result.put("settingsScreen", Manifest.permission.POST_NOTIFICATIONS.equals(permission) ? "notifications" : "app");
|
|
635
|
+
result.put("telaConfiguracao", result.optString("settingsScreen"));
|
|
636
|
+
return result;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
private void openPermissionSettings(String permission) {
|
|
640
|
+
if (Manifest.permission.POST_NOTIFICATIONS.equals(permission)) {
|
|
641
|
+
openNotificationSettings();
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
openAppSettings();
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
private void openAppSettings() {
|
|
649
|
+
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
|
650
|
+
intent.setData(Uri.parse("package:" + context().getPackageName()));
|
|
651
|
+
cordova.getActivity().startActivity(intent);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
private void openNotificationSettings() {
|
|
655
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
|
656
|
+
openAppSettings();
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
|
|
661
|
+
intent.putExtra(Settings.EXTRA_APP_PACKAGE, context().getPackageName());
|
|
662
|
+
try {
|
|
663
|
+
cordova.getActivity().startActivity(intent);
|
|
664
|
+
} catch (ActivityNotFoundException error) {
|
|
665
|
+
openAppSettings();
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
475
669
|
private boolean isFloatingMode() {
|
|
476
670
|
return "floating".equals(preferences.getString("Html2ApkMode", ""));
|
|
477
671
|
}
|
|
@@ -507,17 +701,24 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
507
701
|
result.put("required", Build.VERSION.SDK_INT >= Build.VERSION_CODES.M);
|
|
508
702
|
result.put("granted", canDrawOverlays());
|
|
509
703
|
result.put("permission", "android.permission.SYSTEM_ALERT_WINDOW");
|
|
704
|
+
result.put("permissionGranted", canDrawOverlays());
|
|
705
|
+
result.put("requiresSettings", Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !canDrawOverlays());
|
|
706
|
+
result.put("settingsOpened", false);
|
|
510
707
|
return result;
|
|
511
708
|
}
|
|
512
709
|
|
|
513
710
|
private void openOverlaySettings() {
|
|
514
|
-
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
|
711
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || canDrawOverlays()) {
|
|
515
712
|
return;
|
|
516
713
|
}
|
|
517
714
|
|
|
518
715
|
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
|
|
519
716
|
intent.setData(Uri.parse("package:" + context().getPackageName()));
|
|
520
|
-
|
|
717
|
+
try {
|
|
718
|
+
cordova.getActivity().startActivity(intent);
|
|
719
|
+
} catch (ActivityNotFoundException error) {
|
|
720
|
+
openAppSettings();
|
|
721
|
+
}
|
|
521
722
|
}
|
|
522
723
|
|
|
523
724
|
private void startFloatingIcon() throws Exception {
|
|
@@ -562,20 +763,31 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
562
763
|
}
|
|
563
764
|
|
|
564
765
|
notificationPermissionCallback = callbackContext;
|
|
766
|
+
rememberRuntimePermissionRequest(Manifest.permission.POST_NOTIFICATIONS);
|
|
565
767
|
cordova.requestPermission(this, REQUEST_POST_NOTIFICATIONS, Manifest.permission.POST_NOTIFICATIONS);
|
|
566
768
|
}
|
|
567
769
|
|
|
568
770
|
private JSONObject notificationPermissionStatus() throws Exception {
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
771
|
+
boolean required = Build.VERSION.SDK_INT >= 33;
|
|
772
|
+
return runtimePermissionResult(Manifest.permission.POST_NOTIFICATIONS, required, false, hasNotificationPermission());
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
private boolean requestNotificationPermissionForAction(JSONObject options, boolean schedule, CallbackContext callbackContext) throws Exception {
|
|
776
|
+
if (hasNotificationPermission()) {
|
|
777
|
+
return false;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
pendingNotificationOptions = options;
|
|
781
|
+
pendingNotificationSchedule = schedule;
|
|
782
|
+
pendingNotificationCallback = callbackContext;
|
|
783
|
+
rememberRuntimePermissionRequest(Manifest.permission.POST_NOTIFICATIONS);
|
|
784
|
+
cordova.requestPermission(this, REQUEST_POST_NOTIFICATIONS, Manifest.permission.POST_NOTIFICATIONS);
|
|
785
|
+
return true;
|
|
574
786
|
}
|
|
575
787
|
|
|
576
788
|
private void showNotification(JSONObject options) throws Exception {
|
|
577
789
|
if (!hasNotificationPermission()) {
|
|
578
|
-
throw new Exception("POST_NOTIFICATIONS permission is not granted.
|
|
790
|
+
throw new Exception("POST_NOTIFICATIONS permission is not granted.");
|
|
579
791
|
}
|
|
580
792
|
|
|
581
793
|
ensureNotificationChannel(context());
|
|
@@ -589,7 +801,7 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
589
801
|
.setContentText(text)
|
|
590
802
|
.setStyle(new NotificationCompat.BigTextStyle().bigText(text))
|
|
591
803
|
.setAutoCancel(true)
|
|
592
|
-
.setContentIntent(
|
|
804
|
+
.setContentIntent(createNotificationClickIntent(context(), id, detailPayload(options)))
|
|
593
805
|
.setPriority(NotificationCompat.PRIORITY_DEFAULT);
|
|
594
806
|
|
|
595
807
|
addNotificationActions(builder, context(), id, options);
|
|
@@ -607,8 +819,15 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
607
819
|
options.put("id", id);
|
|
608
820
|
options.put("quando", when);
|
|
609
821
|
options.put("when", when);
|
|
822
|
+
boolean exactRequested = wantsExactAlarm(options);
|
|
823
|
+
boolean exactAllowed = canScheduleExactAlarms();
|
|
824
|
+
boolean settingsOpened = false;
|
|
825
|
+
if (exactRequested && !exactAllowed) {
|
|
826
|
+
openExactAlarmSettings();
|
|
827
|
+
settingsOpened = true;
|
|
828
|
+
}
|
|
610
829
|
NotificationStore.save(context(), id, when, options);
|
|
611
|
-
NotificationReceiver.schedule(context(), id, when, options,
|
|
830
|
+
NotificationReceiver.schedule(context(), id, when, options, exactAllowed);
|
|
612
831
|
|
|
613
832
|
JSONObject result = new JSONObject();
|
|
614
833
|
result.put("id", id);
|
|
@@ -616,9 +835,24 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
616
835
|
result.put("quando", when);
|
|
617
836
|
result.put("repeating", repeatInterval(options) > 0);
|
|
618
837
|
result.put("loop", loopNotifications(options).length() > 0);
|
|
838
|
+
result.put("exactRequested", exactRequested);
|
|
839
|
+
result.put("exatoSolicitado", exactRequested);
|
|
840
|
+
result.put("exactAllowed", exactAllowed);
|
|
841
|
+
result.put("exatoPermitido", exactAllowed);
|
|
842
|
+
result.put("requiresSettings", exactRequested && !exactAllowed);
|
|
843
|
+
result.put("settingsOpened", settingsOpened);
|
|
619
844
|
return result;
|
|
620
845
|
}
|
|
621
846
|
|
|
847
|
+
private boolean wantsExactAlarm(JSONObject options) {
|
|
848
|
+
return options.optBoolean("exato",
|
|
849
|
+
options.optBoolean("exact",
|
|
850
|
+
options.optBoolean("alarmeExato",
|
|
851
|
+
options.optBoolean("exactAlarm",
|
|
852
|
+
options.optBoolean("preciso",
|
|
853
|
+
options.optBoolean("precise", false))))));
|
|
854
|
+
}
|
|
855
|
+
|
|
622
856
|
private void cancelNotification(Object input) throws Exception {
|
|
623
857
|
int id = notificationIdFromObject(input);
|
|
624
858
|
if (id == 0) {
|
|
@@ -635,14 +869,18 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
635
869
|
}
|
|
636
870
|
|
|
637
871
|
private void openExactAlarmSettings() {
|
|
638
|
-
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
|
|
872
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S || canScheduleExactAlarms()) {
|
|
639
873
|
return;
|
|
640
874
|
}
|
|
641
875
|
|
|
642
876
|
Intent intent = new Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM);
|
|
643
877
|
intent.setData(Uri.parse("package:" + context().getPackageName()));
|
|
644
878
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
645
|
-
|
|
879
|
+
try {
|
|
880
|
+
context().startActivity(intent);
|
|
881
|
+
} catch (ActivityNotFoundException error) {
|
|
882
|
+
openAppSettings();
|
|
883
|
+
}
|
|
646
884
|
}
|
|
647
885
|
|
|
648
886
|
private void vibrate(long ms) {
|
|
@@ -822,15 +1060,12 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
822
1060
|
|
|
823
1061
|
private void requestCameraPermission(CallbackContext callbackContext) throws Exception {
|
|
824
1062
|
if (hasCameraPermission()) {
|
|
825
|
-
|
|
826
|
-
result.put("permission", "android.permission.CAMERA");
|
|
827
|
-
result.put("required", true);
|
|
828
|
-
result.put("granted", true);
|
|
829
|
-
callbackContext.success(result);
|
|
1063
|
+
callbackContext.success(runtimePermissionResult(Manifest.permission.CAMERA, true, false, true));
|
|
830
1064
|
return;
|
|
831
1065
|
}
|
|
832
1066
|
|
|
833
1067
|
cameraPermissionCallback = callbackContext;
|
|
1068
|
+
rememberRuntimePermissionRequest(Manifest.permission.CAMERA);
|
|
834
1069
|
cordova.requestPermission(this, REQUEST_CAMERA, Manifest.permission.CAMERA);
|
|
835
1070
|
}
|
|
836
1071
|
|
|
@@ -848,6 +1083,7 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
848
1083
|
}
|
|
849
1084
|
|
|
850
1085
|
microphonePermissionCallback = callbackContext;
|
|
1086
|
+
rememberRuntimePermissionRequest(Manifest.permission.RECORD_AUDIO);
|
|
851
1087
|
cordova.requestPermission(this, REQUEST_RECORD_AUDIO, Manifest.permission.RECORD_AUDIO);
|
|
852
1088
|
}
|
|
853
1089
|
|
|
@@ -857,6 +1093,8 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
857
1093
|
result.put("required", true);
|
|
858
1094
|
result.put("granted", hasMicrophonePermission());
|
|
859
1095
|
result.put("permissionGranted", hasMicrophonePermission());
|
|
1096
|
+
result.put("requiresSettings", !hasMicrophonePermission() && shouldOpenSettingsForRuntimePermission(Manifest.permission.RECORD_AUDIO));
|
|
1097
|
+
result.put("settingsOpened", false);
|
|
860
1098
|
result.put("recording", micRecorder != null);
|
|
861
1099
|
result.put("gravando", micRecorder != null);
|
|
862
1100
|
if (micRecorder != null) {
|
|
@@ -879,6 +1117,7 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
879
1117
|
|
|
880
1118
|
if (!hasMicrophonePermission()) {
|
|
881
1119
|
pendingMicStartCallback = callbackContext;
|
|
1120
|
+
rememberRuntimePermissionRequest(Manifest.permission.RECORD_AUDIO);
|
|
882
1121
|
cordova.requestPermission(this, REQUEST_RECORD_AUDIO, Manifest.permission.RECORD_AUDIO);
|
|
883
1122
|
return;
|
|
884
1123
|
}
|
|
@@ -1046,7 +1285,7 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
1046
1285
|
|
|
1047
1286
|
private void setFlashlight(boolean enabled) throws Exception {
|
|
1048
1287
|
if (!hasCameraPermission()) {
|
|
1049
|
-
throw new Exception("CAMERA permission is not granted.
|
|
1288
|
+
throw new Exception("CAMERA permission is not granted.");
|
|
1050
1289
|
}
|
|
1051
1290
|
|
|
1052
1291
|
CameraManager manager = (CameraManager) context().getSystemService(Context.CAMERA_SERVICE);
|
|
@@ -1058,12 +1297,28 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
1058
1297
|
torchEnabled = enabled;
|
|
1059
1298
|
}
|
|
1060
1299
|
|
|
1300
|
+
private void setFlashlightWithPermission(boolean enabled, boolean toggle, CallbackContext callbackContext) throws Exception {
|
|
1301
|
+
if (!hasCameraPermission()) {
|
|
1302
|
+
pendingFlashlightCallback = callbackContext;
|
|
1303
|
+
pendingFlashlightEnabled = enabled;
|
|
1304
|
+
pendingFlashlightToggle = toggle;
|
|
1305
|
+
rememberRuntimePermissionRequest(Manifest.permission.CAMERA);
|
|
1306
|
+
cordova.requestPermission(this, REQUEST_CAMERA, Manifest.permission.CAMERA);
|
|
1307
|
+
return;
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
setFlashlight(toggle ? !torchEnabled : enabled);
|
|
1311
|
+
callbackContext.success(flashlightStatus());
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1061
1314
|
private JSONObject flashlightStatus() throws Exception {
|
|
1062
1315
|
JSONObject result = new JSONObject();
|
|
1063
1316
|
result.put("available", context().getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH));
|
|
1064
1317
|
result.put("enabled", torchEnabled);
|
|
1065
1318
|
result.put("permission", "android.permission.CAMERA");
|
|
1066
1319
|
result.put("permissionGranted", hasCameraPermission());
|
|
1320
|
+
result.put("requiresSettings", !hasCameraPermission() && shouldOpenSettingsForRuntimePermission(Manifest.permission.CAMERA));
|
|
1321
|
+
result.put("settingsOpened", false);
|
|
1067
1322
|
return result;
|
|
1068
1323
|
}
|
|
1069
1324
|
|
|
@@ -1736,6 +1991,167 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
1736
1991
|
);
|
|
1737
1992
|
}
|
|
1738
1993
|
|
|
1994
|
+
static PendingIntent createNotificationClickIntent(Context context, int id, JSONObject detail) {
|
|
1995
|
+
if (shouldOpenAppForNotificationClick(detail)) {
|
|
1996
|
+
return createContentIntent(context, id, detail);
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
Intent intent = new Intent(context, NotificationClickReceiver.class);
|
|
2000
|
+
intent.putExtra(EXTRA_NOTIFICATION_CLICKED, true);
|
|
2001
|
+
intent.putExtra(EXTRA_NOTIFICATION_DETAIL, detail == null ? "{}" : detail.toString());
|
|
2002
|
+
|
|
2003
|
+
return PendingIntent.getBroadcast(
|
|
2004
|
+
context,
|
|
2005
|
+
id,
|
|
2006
|
+
intent,
|
|
2007
|
+
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
|
|
2008
|
+
);
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
static void handleNotificationClickBroadcast(Context context, JSONObject detail) {
|
|
2012
|
+
if (detail == null) {
|
|
2013
|
+
return;
|
|
2014
|
+
}
|
|
2015
|
+
|
|
2016
|
+
if (activeBridge != null && activeBridge.webView != null) {
|
|
2017
|
+
activeBridge.dispatchNotificationClick(detail);
|
|
2018
|
+
return;
|
|
2019
|
+
}
|
|
2020
|
+
|
|
2021
|
+
handleNativeNotificationAction(context, detail);
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
private static void handleNativeNotificationAction(Context context, JSONObject detail) {
|
|
2025
|
+
JSONObject action = notificationClickAction(detail);
|
|
2026
|
+
if (action == null) {
|
|
2027
|
+
return;
|
|
2028
|
+
}
|
|
2029
|
+
|
|
2030
|
+
String functionName = action.optString("funcao",
|
|
2031
|
+
action.optString("functionName",
|
|
2032
|
+
action.optString("function",
|
|
2033
|
+
action.optString("fn", action.optString("nomeFuncao", "")))));
|
|
2034
|
+
|
|
2035
|
+
if (!isExternalUrlFunction(functionName)) {
|
|
2036
|
+
return;
|
|
2037
|
+
}
|
|
2038
|
+
|
|
2039
|
+
String url = firstActionArgument(action);
|
|
2040
|
+
if (url.length() == 0) {
|
|
2041
|
+
url = action.optString("url", action.optString("href", ""));
|
|
2042
|
+
}
|
|
2043
|
+
if (url.length() == 0) {
|
|
2044
|
+
return;
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2047
|
+
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
|
2048
|
+
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
2049
|
+
try {
|
|
2050
|
+
context.startActivity(intent);
|
|
2051
|
+
} catch (Exception ignored) {
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
private static boolean isExternalUrlFunction(String functionName) {
|
|
2056
|
+
return "abrirForaDoApp".equals(functionName)
|
|
2057
|
+
|| "abrirUrlExterno".equals(functionName)
|
|
2058
|
+
|| "abrirUrl".equals(functionName)
|
|
2059
|
+
|| "openOutsideApp".equals(functionName)
|
|
2060
|
+
|| "openExternalUrl".equals(functionName)
|
|
2061
|
+
|| "openUrl".equals(functionName);
|
|
2062
|
+
}
|
|
2063
|
+
|
|
2064
|
+
private static String firstActionArgument(JSONObject action) {
|
|
2065
|
+
JSONArray args = action.optJSONArray("argumentos");
|
|
2066
|
+
if (args == null) {
|
|
2067
|
+
args = action.optJSONArray("args");
|
|
2068
|
+
}
|
|
2069
|
+
if (args == null) {
|
|
2070
|
+
args = action.optJSONArray("parametros");
|
|
2071
|
+
}
|
|
2072
|
+
if (args == null) {
|
|
2073
|
+
args = action.optJSONArray("params");
|
|
2074
|
+
}
|
|
2075
|
+
if (args == null || args.length() == 0) {
|
|
2076
|
+
return "";
|
|
2077
|
+
}
|
|
2078
|
+
return args.optString(0, "");
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2081
|
+
private static JSONObject notificationClickAction(JSONObject detail) {
|
|
2082
|
+
if (detail == null) {
|
|
2083
|
+
return null;
|
|
2084
|
+
}
|
|
2085
|
+
|
|
2086
|
+
JSONObject action = detail.optJSONObject("action");
|
|
2087
|
+
if (action != null) {
|
|
2088
|
+
JSONObject nested = action.optJSONObject("aoClicar");
|
|
2089
|
+
if (nested == null) {
|
|
2090
|
+
nested = action.optJSONObject("onClick");
|
|
2091
|
+
}
|
|
2092
|
+
return nested == null ? action : nested;
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
action = detail.optJSONObject("acao");
|
|
2096
|
+
if (action != null) {
|
|
2097
|
+
JSONObject nested = action.optJSONObject("aoClicar");
|
|
2098
|
+
if (nested == null) {
|
|
2099
|
+
nested = action.optJSONObject("onClick");
|
|
2100
|
+
}
|
|
2101
|
+
return nested == null ? action : nested;
|
|
2102
|
+
}
|
|
2103
|
+
|
|
2104
|
+
action = detail.optJSONObject("aoClicar");
|
|
2105
|
+
if (action == null) {
|
|
2106
|
+
action = detail.optJSONObject("onClick");
|
|
2107
|
+
}
|
|
2108
|
+
return action;
|
|
2109
|
+
}
|
|
2110
|
+
|
|
2111
|
+
private static boolean shouldOpenAppForNotificationClick(JSONObject detail) {
|
|
2112
|
+
Boolean open = openFlag(detail);
|
|
2113
|
+
JSONObject action;
|
|
2114
|
+
|
|
2115
|
+
if (open != null) {
|
|
2116
|
+
return open.booleanValue();
|
|
2117
|
+
}
|
|
2118
|
+
|
|
2119
|
+
action = detail == null ? null : detail.optJSONObject("action");
|
|
2120
|
+
open = openFlag(action);
|
|
2121
|
+
if (open != null) {
|
|
2122
|
+
return open.booleanValue();
|
|
2123
|
+
}
|
|
2124
|
+
|
|
2125
|
+
action = detail == null ? null : detail.optJSONObject("acao");
|
|
2126
|
+
open = openFlag(action);
|
|
2127
|
+
if (open != null) {
|
|
2128
|
+
return open.booleanValue();
|
|
2129
|
+
}
|
|
2130
|
+
|
|
2131
|
+
action = notificationClickAction(detail);
|
|
2132
|
+
open = openFlag(action);
|
|
2133
|
+
return open == null || open.booleanValue();
|
|
2134
|
+
}
|
|
2135
|
+
|
|
2136
|
+
private static Boolean openFlag(JSONObject object) {
|
|
2137
|
+
if (object == null) {
|
|
2138
|
+
return null;
|
|
2139
|
+
}
|
|
2140
|
+
if (object.has("open")) {
|
|
2141
|
+
return Boolean.valueOf(object.optBoolean("open", true));
|
|
2142
|
+
}
|
|
2143
|
+
if (object.has("abrir")) {
|
|
2144
|
+
return Boolean.valueOf(object.optBoolean("abrir", true));
|
|
2145
|
+
}
|
|
2146
|
+
if (object.has("openApp")) {
|
|
2147
|
+
return Boolean.valueOf(object.optBoolean("openApp", true));
|
|
2148
|
+
}
|
|
2149
|
+
if (object.has("abrirApp")) {
|
|
2150
|
+
return Boolean.valueOf(object.optBoolean("abrirApp", true));
|
|
2151
|
+
}
|
|
2152
|
+
return null;
|
|
2153
|
+
}
|
|
2154
|
+
|
|
1739
2155
|
static boolean canScheduleExactAlarms(Context context) {
|
|
1740
2156
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
|
|
1741
2157
|
return true;
|
|
@@ -1939,9 +2355,13 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
1939
2355
|
static JSONObject detailPayload(JSONObject options) throws Exception {
|
|
1940
2356
|
JSONObject detail = new JSONObject();
|
|
1941
2357
|
JSONObject click = options.optJSONObject("aoClicar");
|
|
2358
|
+
Boolean open = openFlag(options);
|
|
1942
2359
|
if (click == null) {
|
|
1943
2360
|
click = options.optJSONObject("onClick");
|
|
1944
2361
|
}
|
|
2362
|
+
if (open == null) {
|
|
2363
|
+
open = openFlag(click);
|
|
2364
|
+
}
|
|
1945
2365
|
|
|
1946
2366
|
detail.put("id", notificationId(options));
|
|
1947
2367
|
detail.put("title", title(options));
|
|
@@ -1952,6 +2372,10 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
1952
2372
|
detail.put("clickedAt", System.currentTimeMillis());
|
|
1953
2373
|
detail.put("onClick", click == null ? new JSONObject().put("action", "open-app") : click);
|
|
1954
2374
|
detail.put("aoClicar", click == null ? new JSONObject().put("acao", "abrir-app") : click);
|
|
2375
|
+
if (open != null) {
|
|
2376
|
+
detail.put("open", open.booleanValue());
|
|
2377
|
+
detail.put("abrir", open.booleanValue());
|
|
2378
|
+
}
|
|
1955
2379
|
return detail;
|
|
1956
2380
|
}
|
|
1957
2381
|
|
|
@@ -1979,7 +2403,7 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
1979
2403
|
builder.addAction(
|
|
1980
2404
|
context.getApplicationInfo().icon,
|
|
1981
2405
|
title,
|
|
1982
|
-
|
|
2406
|
+
createNotificationClickIntent(context, notificationId * 100 + index + 1, detail)
|
|
1983
2407
|
);
|
|
1984
2408
|
} catch (Exception ignored) {
|
|
1985
2409
|
}
|