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.
@@ -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
- setFlashlight(args.optBoolean(0, true));
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
- setFlashlight(!torchEnabled);
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
- callbackContext.success();
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
- callbackContext.success(overlayPermissionStatus());
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 && cameraPermissionCallback != null) {
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 = new JSONObject();
398
- result.put("permission", "android.permission.CAMERA");
399
- result.put("required", true);
400
- result.put("requested", true);
401
- result.put("granted", granted);
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 = microphoneStatus();
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 || notificationPermissionCallback == null) {
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 = notificationPermissionStatus();
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
- cordova.getActivity().startActivity(intent);
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
- JSONObject result = new JSONObject();
570
- result.put("required", Build.VERSION.SDK_INT >= 33);
571
- result.put("granted", hasNotificationPermission());
572
- result.put("permission", "android.permission.POST_NOTIFICATIONS");
573
- return result;
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. Call solicitarPermissaoNotificacoes() first.");
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(createContentIntent(context(), id, detailPayload(options)))
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, canScheduleExactAlarms());
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
- context().startActivity(intent);
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
- JSONObject result = new JSONObject();
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. Call solicitarPermissaoCamera() first.");
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
- createContentIntent(context, notificationId * 100 + index + 1, detail)
2406
+ createNotificationClickIntent(context, notificationId * 100 + index + 1, detail)
1983
2407
  );
1984
2408
  } catch (Exception ignored) {
1985
2409
  }