html2apk 0.3.0 → 0.4.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.
@@ -889,7 +889,12 @@ h2 {
889
889
  gap: 10px;
890
890
  }
891
891
 
892
- .code-card code {
892
+ .code-card-top {
893
+ display: grid;
894
+ gap: 8px;
895
+ }
896
+
897
+ .code-card-top code {
893
898
  overflow-wrap: anywhere;
894
899
  color: #d6e4f7;
895
900
  background: #0c1117;
@@ -897,7 +902,7 @@ h2 {
897
902
  padding: 10px;
898
903
  }
899
904
 
900
- .code-card span {
905
+ .code-card-top span {
901
906
  color: var(--blue);
902
907
  font-weight: 800;
903
908
  }
@@ -923,6 +928,59 @@ h2 {
923
928
  padding-top: 10px;
924
929
  }
925
930
 
931
+ .copy-example {
932
+ border-top: 1px solid var(--line);
933
+ padding-top: 12px;
934
+ display: grid;
935
+ gap: 8px;
936
+ }
937
+
938
+ .copy-example-header {
939
+ display: flex;
940
+ align-items: center;
941
+ justify-content: space-between;
942
+ gap: 10px;
943
+ }
944
+
945
+ .copy-example-header strong {
946
+ font-size: .88rem;
947
+ }
948
+
949
+ .copy-code-button {
950
+ min-height: 34px;
951
+ border: 1px solid var(--line);
952
+ border-radius: 8px;
953
+ padding: 0 12px;
954
+ background: var(--bg);
955
+ color: var(--blue);
956
+ font-weight: 800;
957
+ cursor: pointer;
958
+ }
959
+
960
+ .copy-code-button.copied {
961
+ color: var(--green);
962
+ }
963
+
964
+ .copy-code-button.copy-error {
965
+ color: var(--red);
966
+ }
967
+
968
+ .copy-example pre {
969
+ margin: 0;
970
+ max-height: 260px;
971
+ overflow: auto;
972
+ border-radius: 8px;
973
+ background: #0c1117;
974
+ }
975
+
976
+ .copy-example pre code {
977
+ display: block;
978
+ padding: 12px;
979
+ color: #d6e4f7;
980
+ font: 12px/1.55 Consolas, "Courier New", monospace;
981
+ white-space: pre;
982
+ }
983
+
926
984
  .success-view {
927
985
  min-height: 100%;
928
986
  display: none;
@@ -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,25 @@ 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_";
73
76
 
74
77
  private CallbackContext notificationPermissionCallback;
75
78
  private CallbackContext cameraPermissionCallback;
79
+ private CallbackContext pendingNotificationCallback;
80
+ private CallbackContext pendingFlashlightCallback;
76
81
  private CallbackContext microphonePermissionCallback;
77
82
  private CallbackContext pendingMicStartCallback;
78
83
  private CallbackContext filePickerCallback;
79
84
  private CallbackContext saveFileCallback;
80
85
  private CallbackContext folderPickerCallback;
81
86
  private JSONObject pendingSaveFile;
87
+ private JSONObject pendingNotificationOptions;
82
88
  private JSONObject initialNotification;
83
89
  private JSONObject initialLink;
90
+ private Boolean pendingFlashlightEnabled;
91
+ private boolean pendingNotificationSchedule;
92
+ private boolean pendingFlashlightToggle;
84
93
  private boolean overlaySettingsOpened;
85
94
  private boolean torchEnabled;
86
95
  private MediaRecorder micRecorder;
@@ -130,6 +139,9 @@ public class Html2ApkBridge extends CordovaPlugin {
130
139
  try {
131
140
  if ("notify".equals(action)) {
132
141
  JSONObject options = args.optJSONObject(0);
142
+ if (requestNotificationPermissionForAction(options == null ? new JSONObject() : options, false, callbackContext)) {
143
+ return true;
144
+ }
133
145
  showNotification(options == null ? new JSONObject() : options);
134
146
  callbackContext.success();
135
147
  return true;
@@ -137,6 +149,9 @@ public class Html2ApkBridge extends CordovaPlugin {
137
149
 
138
150
  if ("scheduleNotification".equals(action)) {
139
151
  JSONObject options = args.optJSONObject(0);
152
+ if (requestNotificationPermissionForAction(options == null ? new JSONObject() : options, true, callbackContext)) {
153
+ return true;
154
+ }
140
155
  callbackContext.success(scheduleNotification(options == null ? new JSONObject() : options));
141
156
  return true;
142
157
  }
@@ -183,14 +198,12 @@ public class Html2ApkBridge extends CordovaPlugin {
183
198
  }
184
199
 
185
200
  if ("flashlight".equals(action)) {
186
- setFlashlight(args.optBoolean(0, true));
187
- callbackContext.success(flashlightStatus());
201
+ setFlashlightWithPermission(args.optBoolean(0, true), false, callbackContext);
188
202
  return true;
189
203
  }
190
204
 
191
205
  if ("toggleFlashlight".equals(action)) {
192
- setFlashlight(!torchEnabled);
193
- callbackContext.success(flashlightStatus());
206
+ setFlashlightWithPermission(false, true, callbackContext);
194
207
  return true;
195
208
  }
196
209
 
@@ -343,7 +356,14 @@ public class Html2ApkBridge extends CordovaPlugin {
343
356
 
344
357
  if ("openExactAlarmSettings".equals(action)) {
345
358
  openExactAlarmSettings();
346
- callbackContext.success();
359
+ JSONObject result = new JSONObject();
360
+ result.put("permission", "android.permission.SCHEDULE_EXACT_ALARM");
361
+ result.put("required", Build.VERSION.SDK_INT >= Build.VERSION_CODES.S);
362
+ result.put("granted", canScheduleExactAlarms());
363
+ result.put("permissionGranted", canScheduleExactAlarms());
364
+ result.put("requiresSettings", Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !canScheduleExactAlarms());
365
+ result.put("settingsOpened", Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !canScheduleExactAlarms());
366
+ callbackContext.success(result);
347
367
  return true;
348
368
  }
349
369
 
@@ -354,11 +374,22 @@ public class Html2ApkBridge extends CordovaPlugin {
354
374
 
355
375
  if ("requestOverlayPermission".equals(action) || "openOverlaySettings".equals(action)) {
356
376
  openOverlaySettings();
357
- callbackContext.success(overlayPermissionStatus());
377
+ JSONObject result = overlayPermissionStatus();
378
+ result.put("requested", true);
379
+ result.put("settingsOpened", result.optBoolean("requiresSettings"));
380
+ callbackContext.success(result);
358
381
  return true;
359
382
  }
360
383
 
361
384
  if ("startFloatingIcon".equals(action)) {
385
+ if (!canDrawOverlays()) {
386
+ openOverlaySettings();
387
+ JSONObject result = overlayPermissionStatus();
388
+ result.put("requested", true);
389
+ result.put("requiresSettings", true);
390
+ callbackContext.success(result);
391
+ return true;
392
+ }
362
393
  startFloatingIcon();
363
394
  callbackContext.success(overlayPermissionStatus());
364
395
  return true;
@@ -391,14 +422,50 @@ public class Html2ApkBridge extends CordovaPlugin {
391
422
 
392
423
  @Override
393
424
  public void onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults) {
394
- if (requestCode == REQUEST_CAMERA && cameraPermissionCallback != null) {
425
+ if (requestCode == REQUEST_CAMERA) {
395
426
  boolean granted = grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED;
427
+ if (pendingFlashlightCallback != null) {
428
+ CallbackContext callback = pendingFlashlightCallback;
429
+ Boolean enabled = pendingFlashlightEnabled;
430
+ boolean toggle = pendingFlashlightToggle;
431
+ pendingFlashlightCallback = null;
432
+ pendingFlashlightEnabled = null;
433
+ pendingFlashlightToggle = false;
434
+
435
+ try {
436
+ if (!granted) {
437
+ JSONObject result = shouldOpenSettingsForRuntimePermission(Manifest.permission.CAMERA)
438
+ ? openSettingsForRuntimePermission(Manifest.permission.CAMERA, true, true)
439
+ : flashlightStatus();
440
+ result.put("requested", true);
441
+ result.put("granted", false);
442
+ callback.success(result);
443
+ return;
444
+ }
445
+
446
+ setFlashlight(toggle ? !torchEnabled : Boolean.TRUE.equals(enabled));
447
+ JSONObject result = flashlightStatus();
448
+ result.put("requested", true);
449
+ result.put("granted", true);
450
+ callback.success(result);
451
+ } catch (Exception error) {
452
+ callback.error(error.getMessage());
453
+ }
454
+ return;
455
+ }
456
+
457
+ if (cameraPermissionCallback == null) {
458
+ return;
459
+ }
460
+
396
461
  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);
462
+ JSONObject result = granted
463
+ ? runtimePermissionResult(Manifest.permission.CAMERA, true, true, true)
464
+ : (
465
+ shouldOpenSettingsForRuntimePermission(Manifest.permission.CAMERA)
466
+ ? openSettingsForRuntimePermission(Manifest.permission.CAMERA, true, true)
467
+ : runtimePermissionResult(Manifest.permission.CAMERA, true, true, false)
468
+ );
402
469
  cameraPermissionCallback.success(result);
403
470
  } catch (Exception error) {
404
471
  cameraPermissionCallback.error(error.getMessage());
@@ -423,7 +490,13 @@ public class Html2ApkBridge extends CordovaPlugin {
423
490
  if (shouldStartRecording && granted) {
424
491
  startMicRecorder(callback);
425
492
  } else {
426
- JSONObject result = microphoneStatus();
493
+ JSONObject result = granted
494
+ ? microphoneStatus()
495
+ : (
496
+ shouldOpenSettingsForRuntimePermission(Manifest.permission.RECORD_AUDIO)
497
+ ? openSettingsForRuntimePermission(Manifest.permission.RECORD_AUDIO, true, true)
498
+ : microphoneStatus()
499
+ );
427
500
  result.put("requested", true);
428
501
  result.put("granted", granted);
429
502
  callback.success(result);
@@ -434,13 +507,57 @@ public class Html2ApkBridge extends CordovaPlugin {
434
507
  return;
435
508
  }
436
509
 
437
- if (requestCode != REQUEST_POST_NOTIFICATIONS || notificationPermissionCallback == null) {
510
+ if (requestCode != REQUEST_POST_NOTIFICATIONS) {
438
511
  return;
439
512
  }
440
513
 
441
514
  boolean granted = grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED;
515
+ if (pendingNotificationCallback != null) {
516
+ CallbackContext callback = pendingNotificationCallback;
517
+ JSONObject options = pendingNotificationOptions == null ? new JSONObject() : pendingNotificationOptions;
518
+ boolean schedule = pendingNotificationSchedule;
519
+ pendingNotificationCallback = null;
520
+ pendingNotificationOptions = null;
521
+ pendingNotificationSchedule = false;
522
+
523
+ try {
524
+ if (!granted) {
525
+ JSONObject result = shouldOpenSettingsForRuntimePermission(Manifest.permission.POST_NOTIFICATIONS)
526
+ ? openSettingsForRuntimePermission(Manifest.permission.POST_NOTIFICATIONS, true, true)
527
+ : notificationPermissionStatus();
528
+ result.put("requested", true);
529
+ result.put("granted", false);
530
+ callback.success(result);
531
+ return;
532
+ }
533
+
534
+ if (schedule) {
535
+ JSONObject result = scheduleNotification(options);
536
+ result.put("requested", true);
537
+ result.put("granted", true);
538
+ callback.success(result);
539
+ } else {
540
+ showNotification(options);
541
+ callback.success();
542
+ }
543
+ } catch (Exception error) {
544
+ callback.error(error.getMessage());
545
+ }
546
+ return;
547
+ }
548
+
549
+ if (notificationPermissionCallback == null) {
550
+ return;
551
+ }
552
+
442
553
  try {
443
- JSONObject result = notificationPermissionStatus();
554
+ JSONObject result = granted
555
+ ? notificationPermissionStatus()
556
+ : (
557
+ shouldOpenSettingsForRuntimePermission(Manifest.permission.POST_NOTIFICATIONS)
558
+ ? openSettingsForRuntimePermission(Manifest.permission.POST_NOTIFICATIONS, true, true)
559
+ : notificationPermissionStatus()
560
+ );
444
561
  result.put("requested", true);
445
562
  result.put("granted", granted);
446
563
  notificationPermissionCallback.success(result);
@@ -472,6 +589,78 @@ public class Html2ApkBridge extends CordovaPlugin {
472
589
  return this.cordova.getActivity().getApplicationContext();
473
590
  }
474
591
 
592
+ private SharedPreferences preferencesStore() {
593
+ return context().getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
594
+ }
595
+
596
+ private boolean wasRuntimePermissionRequested(String permission) {
597
+ return preferencesStore().getBoolean(PREF_PERMISSION_PREFIX + permission, false);
598
+ }
599
+
600
+ private void rememberRuntimePermissionRequest(String permission) {
601
+ preferencesStore().edit().putBoolean(PREF_PERMISSION_PREFIX + permission, true).apply();
602
+ }
603
+
604
+ private boolean shouldOpenSettingsForRuntimePermission(String permission) {
605
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || !wasRuntimePermissionRequested(permission)) {
606
+ return false;
607
+ }
608
+
609
+ return !cordova.getActivity().shouldShowRequestPermissionRationale(permission);
610
+ }
611
+
612
+ private JSONObject runtimePermissionResult(String permission, boolean required, boolean requested, boolean granted) throws Exception {
613
+ JSONObject result = new JSONObject();
614
+ result.put("permission", permission);
615
+ result.put("required", required);
616
+ result.put("requested", requested);
617
+ result.put("granted", granted);
618
+ result.put("permissionGranted", granted);
619
+ result.put("requiresSettings", required && !granted && shouldOpenSettingsForRuntimePermission(permission));
620
+ result.put("settingsOpened", false);
621
+ return result;
622
+ }
623
+
624
+ private JSONObject openSettingsForRuntimePermission(String permission, boolean required, boolean requested) throws Exception {
625
+ JSONObject result = runtimePermissionResult(permission, required, requested, false);
626
+ openPermissionSettings(permission);
627
+ result.put("requiresSettings", true);
628
+ result.put("settingsOpened", true);
629
+ result.put("settingsScreen", Manifest.permission.POST_NOTIFICATIONS.equals(permission) ? "notifications" : "app");
630
+ result.put("telaConfiguracao", result.optString("settingsScreen"));
631
+ return result;
632
+ }
633
+
634
+ private void openPermissionSettings(String permission) {
635
+ if (Manifest.permission.POST_NOTIFICATIONS.equals(permission)) {
636
+ openNotificationSettings();
637
+ return;
638
+ }
639
+
640
+ openAppSettings();
641
+ }
642
+
643
+ private void openAppSettings() {
644
+ Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
645
+ intent.setData(Uri.parse("package:" + context().getPackageName()));
646
+ cordova.getActivity().startActivity(intent);
647
+ }
648
+
649
+ private void openNotificationSettings() {
650
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
651
+ openAppSettings();
652
+ return;
653
+ }
654
+
655
+ Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
656
+ intent.putExtra(Settings.EXTRA_APP_PACKAGE, context().getPackageName());
657
+ try {
658
+ cordova.getActivity().startActivity(intent);
659
+ } catch (ActivityNotFoundException error) {
660
+ openAppSettings();
661
+ }
662
+ }
663
+
475
664
  private boolean isFloatingMode() {
476
665
  return "floating".equals(preferences.getString("Html2ApkMode", ""));
477
666
  }
@@ -507,17 +696,24 @@ public class Html2ApkBridge extends CordovaPlugin {
507
696
  result.put("required", Build.VERSION.SDK_INT >= Build.VERSION_CODES.M);
508
697
  result.put("granted", canDrawOverlays());
509
698
  result.put("permission", "android.permission.SYSTEM_ALERT_WINDOW");
699
+ result.put("permissionGranted", canDrawOverlays());
700
+ result.put("requiresSettings", Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !canDrawOverlays());
701
+ result.put("settingsOpened", false);
510
702
  return result;
511
703
  }
512
704
 
513
705
  private void openOverlaySettings() {
514
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
706
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || canDrawOverlays()) {
515
707
  return;
516
708
  }
517
709
 
518
710
  Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
519
711
  intent.setData(Uri.parse("package:" + context().getPackageName()));
520
- cordova.getActivity().startActivity(intent);
712
+ try {
713
+ cordova.getActivity().startActivity(intent);
714
+ } catch (ActivityNotFoundException error) {
715
+ openAppSettings();
716
+ }
521
717
  }
522
718
 
523
719
  private void startFloatingIcon() throws Exception {
@@ -561,21 +757,42 @@ public class Html2ApkBridge extends CordovaPlugin {
561
757
  return;
562
758
  }
563
759
 
760
+ if (shouldOpenSettingsForRuntimePermission(Manifest.permission.POST_NOTIFICATIONS)) {
761
+ callbackContext.success(openSettingsForRuntimePermission(Manifest.permission.POST_NOTIFICATIONS, true, false));
762
+ return;
763
+ }
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
+ if (shouldOpenSettingsForRuntimePermission(Manifest.permission.POST_NOTIFICATIONS)) {
781
+ callbackContext.success(openSettingsForRuntimePermission(Manifest.permission.POST_NOTIFICATIONS, true, false));
782
+ return true;
783
+ }
784
+
785
+ pendingNotificationOptions = options;
786
+ pendingNotificationSchedule = schedule;
787
+ pendingNotificationCallback = callbackContext;
788
+ rememberRuntimePermissionRequest(Manifest.permission.POST_NOTIFICATIONS);
789
+ cordova.requestPermission(this, REQUEST_POST_NOTIFICATIONS, Manifest.permission.POST_NOTIFICATIONS);
790
+ return true;
574
791
  }
575
792
 
576
793
  private void showNotification(JSONObject options) throws Exception {
577
794
  if (!hasNotificationPermission()) {
578
- throw new Exception("POST_NOTIFICATIONS permission is not granted. Call solicitarPermissaoNotificacoes() first.");
795
+ throw new Exception("POST_NOTIFICATIONS permission is not granted.");
579
796
  }
580
797
 
581
798
  ensureNotificationChannel(context());
@@ -607,8 +824,15 @@ public class Html2ApkBridge extends CordovaPlugin {
607
824
  options.put("id", id);
608
825
  options.put("quando", when);
609
826
  options.put("when", when);
827
+ boolean exactRequested = wantsExactAlarm(options);
828
+ boolean exactAllowed = canScheduleExactAlarms();
829
+ boolean settingsOpened = false;
830
+ if (exactRequested && !exactAllowed) {
831
+ openExactAlarmSettings();
832
+ settingsOpened = true;
833
+ }
610
834
  NotificationStore.save(context(), id, when, options);
611
- NotificationReceiver.schedule(context(), id, when, options, canScheduleExactAlarms());
835
+ NotificationReceiver.schedule(context(), id, when, options, exactAllowed);
612
836
 
613
837
  JSONObject result = new JSONObject();
614
838
  result.put("id", id);
@@ -616,9 +840,24 @@ public class Html2ApkBridge extends CordovaPlugin {
616
840
  result.put("quando", when);
617
841
  result.put("repeating", repeatInterval(options) > 0);
618
842
  result.put("loop", loopNotifications(options).length() > 0);
843
+ result.put("exactRequested", exactRequested);
844
+ result.put("exatoSolicitado", exactRequested);
845
+ result.put("exactAllowed", exactAllowed);
846
+ result.put("exatoPermitido", exactAllowed);
847
+ result.put("requiresSettings", exactRequested && !exactAllowed);
848
+ result.put("settingsOpened", settingsOpened);
619
849
  return result;
620
850
  }
621
851
 
852
+ private boolean wantsExactAlarm(JSONObject options) {
853
+ return options.optBoolean("exato",
854
+ options.optBoolean("exact",
855
+ options.optBoolean("alarmeExato",
856
+ options.optBoolean("exactAlarm",
857
+ options.optBoolean("preciso",
858
+ options.optBoolean("precise", false))))));
859
+ }
860
+
622
861
  private void cancelNotification(Object input) throws Exception {
623
862
  int id = notificationIdFromObject(input);
624
863
  if (id == 0) {
@@ -635,14 +874,18 @@ public class Html2ApkBridge extends CordovaPlugin {
635
874
  }
636
875
 
637
876
  private void openExactAlarmSettings() {
638
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
877
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S || canScheduleExactAlarms()) {
639
878
  return;
640
879
  }
641
880
 
642
881
  Intent intent = new Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM);
643
882
  intent.setData(Uri.parse("package:" + context().getPackageName()));
644
883
  intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
645
- context().startActivity(intent);
884
+ try {
885
+ context().startActivity(intent);
886
+ } catch (ActivityNotFoundException error) {
887
+ openAppSettings();
888
+ }
646
889
  }
647
890
 
648
891
  private void vibrate(long ms) {
@@ -822,15 +1065,17 @@ public class Html2ApkBridge extends CordovaPlugin {
822
1065
 
823
1066
  private void requestCameraPermission(CallbackContext callbackContext) throws Exception {
824
1067
  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);
1068
+ callbackContext.success(runtimePermissionResult(Manifest.permission.CAMERA, true, false, true));
1069
+ return;
1070
+ }
1071
+
1072
+ if (shouldOpenSettingsForRuntimePermission(Manifest.permission.CAMERA)) {
1073
+ callbackContext.success(openSettingsForRuntimePermission(Manifest.permission.CAMERA, true, false));
830
1074
  return;
831
1075
  }
832
1076
 
833
1077
  cameraPermissionCallback = callbackContext;
1078
+ rememberRuntimePermissionRequest(Manifest.permission.CAMERA);
834
1079
  cordova.requestPermission(this, REQUEST_CAMERA, Manifest.permission.CAMERA);
835
1080
  }
836
1081
 
@@ -847,7 +1092,13 @@ public class Html2ApkBridge extends CordovaPlugin {
847
1092
  return;
848
1093
  }
849
1094
 
1095
+ if (shouldOpenSettingsForRuntimePermission(Manifest.permission.RECORD_AUDIO)) {
1096
+ callbackContext.success(openSettingsForRuntimePermission(Manifest.permission.RECORD_AUDIO, true, false));
1097
+ return;
1098
+ }
1099
+
850
1100
  microphonePermissionCallback = callbackContext;
1101
+ rememberRuntimePermissionRequest(Manifest.permission.RECORD_AUDIO);
851
1102
  cordova.requestPermission(this, REQUEST_RECORD_AUDIO, Manifest.permission.RECORD_AUDIO);
852
1103
  }
853
1104
 
@@ -857,6 +1108,8 @@ public class Html2ApkBridge extends CordovaPlugin {
857
1108
  result.put("required", true);
858
1109
  result.put("granted", hasMicrophonePermission());
859
1110
  result.put("permissionGranted", hasMicrophonePermission());
1111
+ result.put("requiresSettings", !hasMicrophonePermission() && shouldOpenSettingsForRuntimePermission(Manifest.permission.RECORD_AUDIO));
1112
+ result.put("settingsOpened", false);
860
1113
  result.put("recording", micRecorder != null);
861
1114
  result.put("gravando", micRecorder != null);
862
1115
  if (micRecorder != null) {
@@ -878,7 +1131,13 @@ public class Html2ApkBridge extends CordovaPlugin {
878
1131
  }
879
1132
 
880
1133
  if (!hasMicrophonePermission()) {
1134
+ if (shouldOpenSettingsForRuntimePermission(Manifest.permission.RECORD_AUDIO)) {
1135
+ callbackContext.success(openSettingsForRuntimePermission(Manifest.permission.RECORD_AUDIO, true, false));
1136
+ return;
1137
+ }
1138
+
881
1139
  pendingMicStartCallback = callbackContext;
1140
+ rememberRuntimePermissionRequest(Manifest.permission.RECORD_AUDIO);
882
1141
  cordova.requestPermission(this, REQUEST_RECORD_AUDIO, Manifest.permission.RECORD_AUDIO);
883
1142
  return;
884
1143
  }
@@ -1046,7 +1305,7 @@ public class Html2ApkBridge extends CordovaPlugin {
1046
1305
 
1047
1306
  private void setFlashlight(boolean enabled) throws Exception {
1048
1307
  if (!hasCameraPermission()) {
1049
- throw new Exception("CAMERA permission is not granted. Call solicitarPermissaoCamera() first.");
1308
+ throw new Exception("CAMERA permission is not granted.");
1050
1309
  }
1051
1310
 
1052
1311
  CameraManager manager = (CameraManager) context().getSystemService(Context.CAMERA_SERVICE);
@@ -1058,12 +1317,33 @@ public class Html2ApkBridge extends CordovaPlugin {
1058
1317
  torchEnabled = enabled;
1059
1318
  }
1060
1319
 
1320
+ private void setFlashlightWithPermission(boolean enabled, boolean toggle, CallbackContext callbackContext) throws Exception {
1321
+ if (!hasCameraPermission()) {
1322
+ if (shouldOpenSettingsForRuntimePermission(Manifest.permission.CAMERA)) {
1323
+ callbackContext.success(openSettingsForRuntimePermission(Manifest.permission.CAMERA, true, false));
1324
+ return;
1325
+ }
1326
+
1327
+ pendingFlashlightCallback = callbackContext;
1328
+ pendingFlashlightEnabled = enabled;
1329
+ pendingFlashlightToggle = toggle;
1330
+ rememberRuntimePermissionRequest(Manifest.permission.CAMERA);
1331
+ cordova.requestPermission(this, REQUEST_CAMERA, Manifest.permission.CAMERA);
1332
+ return;
1333
+ }
1334
+
1335
+ setFlashlight(toggle ? !torchEnabled : enabled);
1336
+ callbackContext.success(flashlightStatus());
1337
+ }
1338
+
1061
1339
  private JSONObject flashlightStatus() throws Exception {
1062
1340
  JSONObject result = new JSONObject();
1063
1341
  result.put("available", context().getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH));
1064
1342
  result.put("enabled", torchEnabled);
1065
1343
  result.put("permission", "android.permission.CAMERA");
1066
1344
  result.put("permissionGranted", hasCameraPermission());
1345
+ result.put("requiresSettings", !hasCameraPermission() && shouldOpenSettingsForRuntimePermission(Manifest.permission.CAMERA));
1346
+ result.put("settingsOpened", false);
1067
1347
  return result;
1068
1348
  }
1069
1349
 
@@ -7,6 +7,8 @@ var eventListeners = {};
7
7
  var initialNotification = null;
8
8
  var initialLink = null;
9
9
  var scheduledNotificationCounter = 0;
10
+ var deviceReady = typeof document === "undefined";
11
+ var deviceReadyCallbacks = [];
10
12
  var eventAliases = {
11
13
  "app:ready": "app:pronto",
12
14
  "app:paused": "app:pausado",
@@ -20,9 +22,36 @@ var eventAliases = {
20
22
  "notification:clicked": "notificacao:clicada"
21
23
  };
22
24
 
25
+ function markDeviceReady() {
26
+ if (deviceReady) {
27
+ return;
28
+ }
29
+
30
+ deviceReady = true;
31
+ deviceReadyCallbacks.splice(0).forEach(function (callback) {
32
+ callback();
33
+ });
34
+ }
35
+
36
+ function whenDeviceReady() {
37
+ if (deviceReady) {
38
+ return Promise.resolve();
39
+ }
40
+
41
+ return new Promise(function (resolve) {
42
+ deviceReadyCallbacks.push(resolve);
43
+ });
44
+ }
45
+
46
+ if (typeof document !== "undefined") {
47
+ document.addEventListener("deviceready", markDeviceReady, false);
48
+ }
49
+
23
50
  function call(action, args) {
24
- return new Promise(function (resolve, reject) {
25
- exec(resolve, reject, "Html2ApkBridge", action, args || []);
51
+ return whenDeviceReady().then(function () {
52
+ return new Promise(function (resolve, reject) {
53
+ exec(resolve, reject, "Html2ApkBridge", action, args || []);
54
+ });
26
55
  });
27
56
  }
28
57