html2apk 0.4.0 → 0.7.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 +116 -12
- package/examples/minimal/app.json +2 -0
- package/examples/minimal/dist/MeuApp-1.0.0-debug.apk +0 -0
- package/examples/minimal/dist/MeuApp-1.0.0-release.aab +0 -0
- package/package.json +1 -1
- package/src/cli/index.js +12 -1
- package/src/cordova/apk-finder.js +22 -10
- package/src/cordova/project.js +5 -0
- package/src/core/build-apk.js +102 -22
- package/src/core/config.js +16 -0
- package/src/core/defaults.js +2 -0
- package/src/desktop/main.js +191 -2
- package/src/desktop/preload.js +5 -0
- package/src/desktop/renderer/index.html +71 -0
- package/src/desktop/renderer/renderer.js +473 -12
- package/src/desktop/renderer/styles.css +189 -0
- package/src/templates/cordova-plugin-html2apk-bridge/plugin.xml +2 -0
- package/src/templates/cordova-plugin-html2apk-bridge/src/android/Html2ApkBridge.java +219 -20
- 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 +278 -11
- package/src/templates/html2apk-early-bridge.js +860 -0
- package/src/templates/html2apk-runtime-console.js +805 -0
|
@@ -492,6 +492,7 @@ h2 {
|
|
|
492
492
|
}
|
|
493
493
|
|
|
494
494
|
.icon-field,
|
|
495
|
+
.keystore-field,
|
|
495
496
|
.onesignal-field,
|
|
496
497
|
.permissions-field {
|
|
497
498
|
grid-column: span 2;
|
|
@@ -569,6 +570,24 @@ h2 {
|
|
|
569
570
|
gap: 10px;
|
|
570
571
|
}
|
|
571
572
|
|
|
573
|
+
.keystore-grid {
|
|
574
|
+
display: grid;
|
|
575
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
576
|
+
gap: 12px;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
.keystore-grid label {
|
|
580
|
+
display: grid;
|
|
581
|
+
gap: 8px;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
.inline-picker {
|
|
585
|
+
display: grid;
|
|
586
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
587
|
+
gap: 10px;
|
|
588
|
+
align-items: center;
|
|
589
|
+
}
|
|
590
|
+
|
|
572
591
|
.validation-panel {
|
|
573
592
|
margin-top: 16px;
|
|
574
593
|
border: 1px solid color-mix(in srgb, var(--red) 42%, var(--line));
|
|
@@ -621,6 +640,165 @@ h2 {
|
|
|
621
640
|
color: var(--muted);
|
|
622
641
|
}
|
|
623
642
|
|
|
643
|
+
.file-editor-layout {
|
|
644
|
+
display: grid;
|
|
645
|
+
grid-template-columns: minmax(220px, 290px) minmax(0, 1fr);
|
|
646
|
+
gap: 16px;
|
|
647
|
+
min-height: 620px;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
.file-tree-panel,
|
|
651
|
+
.file-editor-panel {
|
|
652
|
+
border: 1px solid var(--line);
|
|
653
|
+
background: var(--panel);
|
|
654
|
+
border-radius: 8px;
|
|
655
|
+
box-shadow: var(--shadow);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
.file-tree-panel {
|
|
659
|
+
padding: 14px;
|
|
660
|
+
overflow: auto;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
.file-tree-panel > strong {
|
|
664
|
+
display: block;
|
|
665
|
+
margin-bottom: 12px;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
.file-tree {
|
|
669
|
+
display: grid;
|
|
670
|
+
gap: 4px;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
.file-tree-empty {
|
|
674
|
+
color: var(--muted);
|
|
675
|
+
line-height: 1.45;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
.file-row {
|
|
679
|
+
width: 100%;
|
|
680
|
+
min-height: 34px;
|
|
681
|
+
border: 0;
|
|
682
|
+
border-radius: 7px;
|
|
683
|
+
padding: 0 8px;
|
|
684
|
+
background: transparent;
|
|
685
|
+
color: var(--text);
|
|
686
|
+
display: flex;
|
|
687
|
+
align-items: center;
|
|
688
|
+
gap: 8px;
|
|
689
|
+
text-align: left;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
.file-row:hover,
|
|
693
|
+
.file-row.active {
|
|
694
|
+
background: var(--blue-soft);
|
|
695
|
+
color: var(--blue);
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
.file-row:disabled {
|
|
699
|
+
cursor: not-allowed;
|
|
700
|
+
color: var(--muted);
|
|
701
|
+
opacity: .62;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
.folder-row {
|
|
705
|
+
margin-top: 8px;
|
|
706
|
+
min-height: 28px;
|
|
707
|
+
color: var(--muted);
|
|
708
|
+
font-size: 12px;
|
|
709
|
+
font-weight: 800;
|
|
710
|
+
text-transform: uppercase;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
.file-editor-panel {
|
|
714
|
+
display: grid;
|
|
715
|
+
grid-template-rows: auto minmax(280px, 1fr) minmax(180px, 240px);
|
|
716
|
+
overflow: hidden;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
.file-editor-meta {
|
|
720
|
+
min-height: 50px;
|
|
721
|
+
border-bottom: 1px solid var(--line);
|
|
722
|
+
padding: 12px 14px;
|
|
723
|
+
display: flex;
|
|
724
|
+
align-items: center;
|
|
725
|
+
justify-content: space-between;
|
|
726
|
+
gap: 12px;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
.file-editor-meta strong {
|
|
730
|
+
overflow: hidden;
|
|
731
|
+
text-overflow: ellipsis;
|
|
732
|
+
white-space: nowrap;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
#fileLanguageBadge {
|
|
736
|
+
flex: 0 0 auto;
|
|
737
|
+
min-width: 46px;
|
|
738
|
+
min-height: 26px;
|
|
739
|
+
padding: 5px 9px;
|
|
740
|
+
border-radius: 8px;
|
|
741
|
+
background: var(--blue-soft);
|
|
742
|
+
color: var(--blue);
|
|
743
|
+
text-align: center;
|
|
744
|
+
font-size: 12px;
|
|
745
|
+
font-weight: 800;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
#fileEditorInput {
|
|
749
|
+
width: 100%;
|
|
750
|
+
min-height: 0;
|
|
751
|
+
resize: none;
|
|
752
|
+
border: 0;
|
|
753
|
+
border-bottom: 1px solid var(--line);
|
|
754
|
+
outline: none;
|
|
755
|
+
padding: 16px;
|
|
756
|
+
background: var(--bg);
|
|
757
|
+
color: var(--text);
|
|
758
|
+
font: 13px/1.55 ui-monospace, SFMono-Regular, Consolas, monospace;
|
|
759
|
+
tab-size: 2;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
.syntax-preview-wrap {
|
|
763
|
+
min-height: 0;
|
|
764
|
+
display: grid;
|
|
765
|
+
grid-template-rows: auto minmax(0, 1fr);
|
|
766
|
+
background: color-mix(in srgb, var(--panel-soft) 55%, var(--panel));
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
.syntax-preview-wrap > strong {
|
|
770
|
+
padding: 10px 14px 0;
|
|
771
|
+
color: var(--muted);
|
|
772
|
+
font-size: 12px;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
.syntax-preview {
|
|
776
|
+
margin: 0;
|
|
777
|
+
padding: 12px 14px 16px;
|
|
778
|
+
overflow: auto;
|
|
779
|
+
color: var(--text);
|
|
780
|
+
font: 12px/1.55 ui-monospace, SFMono-Regular, Consolas, monospace;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
.syntax-token-tag,
|
|
784
|
+
.syntax-token-keyword {
|
|
785
|
+
color: var(--blue);
|
|
786
|
+
font-weight: 800;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
.syntax-token-string {
|
|
790
|
+
color: var(--green);
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
.syntax-token-comment {
|
|
794
|
+
color: var(--muted);
|
|
795
|
+
font-style: italic;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
.syntax-token-number {
|
|
799
|
+
color: var(--amber);
|
|
800
|
+
}
|
|
801
|
+
|
|
624
802
|
.preference-row {
|
|
625
803
|
min-height: 112px;
|
|
626
804
|
padding: 20px;
|
|
@@ -1340,11 +1518,22 @@ body.logs-visible .bottom-log-bar {
|
|
|
1340
1518
|
}
|
|
1341
1519
|
|
|
1342
1520
|
.icon-field,
|
|
1521
|
+
.keystore-field,
|
|
1343
1522
|
.onesignal-field,
|
|
1344
1523
|
.permissions-field {
|
|
1345
1524
|
grid-column: span 1;
|
|
1346
1525
|
}
|
|
1347
1526
|
|
|
1527
|
+
.keystore-grid,
|
|
1528
|
+
.inline-picker,
|
|
1529
|
+
.file-editor-layout {
|
|
1530
|
+
grid-template-columns: 1fr;
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
.file-editor-layout {
|
|
1534
|
+
min-height: 820px;
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1348
1537
|
.view-header,
|
|
1349
1538
|
.result-panel,
|
|
1350
1539
|
.progress-panel,
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
<config-file target="AndroidManifest.xml" parent="/manifest/application">
|
|
28
28
|
<service android:name="dev.html2apk.bridge.FloatingIconService" android:exported="false" />
|
|
29
29
|
<receiver android:name="dev.html2apk.bridge.NotificationReceiver" android:exported="false" />
|
|
30
|
+
<receiver android:name="dev.html2apk.bridge.NotificationClickReceiver" android:exported="false" />
|
|
30
31
|
<receiver android:name="dev.html2apk.bridge.BootReceiver" android:enabled="true" android:exported="true">
|
|
31
32
|
<intent-filter>
|
|
32
33
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
|
@@ -38,6 +39,7 @@
|
|
|
38
39
|
<source-file src="src/android/Html2ApkBridge.java" target-dir="app/src/main/java/dev/html2apk/bridge" />
|
|
39
40
|
<source-file src="src/android/FloatingIconService.java" target-dir="app/src/main/java/dev/html2apk/bridge" />
|
|
40
41
|
<source-file src="src/android/NotificationReceiver.java" target-dir="app/src/main/java/dev/html2apk/bridge" />
|
|
42
|
+
<source-file src="src/android/NotificationClickReceiver.java" target-dir="app/src/main/java/dev/html2apk/bridge" />
|
|
41
43
|
<source-file src="src/android/BootReceiver.java" target-dir="app/src/main/java/dev/html2apk/bridge" />
|
|
42
44
|
<source-file src="src/android/NotificationStore.java" target-dir="app/src/main/java/dev/html2apk/bridge" />
|
|
43
45
|
<framework src="androidx.core:core:1.12.0" />
|
|
@@ -73,6 +73,7 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
73
73
|
private static final int REQUEST_PICK_FOLDER = 7413;
|
|
74
74
|
private static final String PREFS_NAME = "html2apk_bridge";
|
|
75
75
|
private static final String PREF_PERMISSION_PREFIX = "permission_requested_";
|
|
76
|
+
private static Html2ApkBridge activeBridge;
|
|
76
77
|
|
|
77
78
|
private CallbackContext notificationPermissionCallback;
|
|
78
79
|
private CallbackContext cameraPermissionCallback;
|
|
@@ -100,6 +101,7 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
100
101
|
@Override
|
|
101
102
|
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
|
102
103
|
super.initialize(cordova, webView);
|
|
104
|
+
activeBridge = this;
|
|
103
105
|
handleNotificationIntent(cordova.getActivity().getIntent(), false);
|
|
104
106
|
handleLinkIntent(cordova.getActivity().getIntent(), false);
|
|
105
107
|
registerSystemReceiver();
|
|
@@ -131,6 +133,9 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
131
133
|
stopMicRecorderSilently();
|
|
132
134
|
unregisterSystemReceiver();
|
|
133
135
|
dispatchEvent("app:fechado", baseEvent("app:fechado"));
|
|
136
|
+
if (activeBridge == this) {
|
|
137
|
+
activeBridge = null;
|
|
138
|
+
}
|
|
134
139
|
super.onDestroy();
|
|
135
140
|
}
|
|
136
141
|
|
|
@@ -420,6 +425,22 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
420
425
|
return false;
|
|
421
426
|
}
|
|
422
427
|
|
|
428
|
+
private void rejectBusyCallback(CallbackContext callbackContext, String operation) {
|
|
429
|
+
callbackContext.error(operation + " is already waiting for Android. Wait for the previous call to finish.");
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
private boolean hasPendingNotificationPermissionRequest() {
|
|
433
|
+
return pendingNotificationCallback != null || notificationPermissionCallback != null;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
private boolean hasPendingCameraPermissionRequest() {
|
|
437
|
+
return pendingFlashlightCallback != null || cameraPermissionCallback != null;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
private boolean hasPendingMicrophonePermissionRequest() {
|
|
441
|
+
return pendingMicStartCallback != null || microphonePermissionCallback != null;
|
|
442
|
+
}
|
|
443
|
+
|
|
423
444
|
@Override
|
|
424
445
|
public void onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults) {
|
|
425
446
|
if (requestCode == REQUEST_CAMERA) {
|
|
@@ -756,9 +777,8 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
756
777
|
callbackContext.success(notificationPermissionStatus());
|
|
757
778
|
return;
|
|
758
779
|
}
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
callbackContext.success(openSettingsForRuntimePermission(Manifest.permission.POST_NOTIFICATIONS, true, false));
|
|
780
|
+
if (hasPendingNotificationPermissionRequest()) {
|
|
781
|
+
rejectBusyCallback(callbackContext, "POST_NOTIFICATIONS permission");
|
|
762
782
|
return;
|
|
763
783
|
}
|
|
764
784
|
|
|
@@ -776,9 +796,8 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
776
796
|
if (hasNotificationPermission()) {
|
|
777
797
|
return false;
|
|
778
798
|
}
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
callbackContext.success(openSettingsForRuntimePermission(Manifest.permission.POST_NOTIFICATIONS, true, false));
|
|
799
|
+
if (hasPendingNotificationPermissionRequest()) {
|
|
800
|
+
rejectBusyCallback(callbackContext, "POST_NOTIFICATIONS permission");
|
|
782
801
|
return true;
|
|
783
802
|
}
|
|
784
803
|
|
|
@@ -806,7 +825,7 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
806
825
|
.setContentText(text)
|
|
807
826
|
.setStyle(new NotificationCompat.BigTextStyle().bigText(text))
|
|
808
827
|
.setAutoCancel(true)
|
|
809
|
-
.setContentIntent(
|
|
828
|
+
.setContentIntent(createNotificationClickIntent(context(), id, detailPayload(options)))
|
|
810
829
|
.setPriority(NotificationCompat.PRIORITY_DEFAULT);
|
|
811
830
|
|
|
812
831
|
addNotificationActions(builder, context(), id, options);
|
|
@@ -1068,9 +1087,8 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
1068
1087
|
callbackContext.success(runtimePermissionResult(Manifest.permission.CAMERA, true, false, true));
|
|
1069
1088
|
return;
|
|
1070
1089
|
}
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
callbackContext.success(openSettingsForRuntimePermission(Manifest.permission.CAMERA, true, false));
|
|
1090
|
+
if (hasPendingCameraPermissionRequest()) {
|
|
1091
|
+
rejectBusyCallback(callbackContext, "CAMERA permission");
|
|
1074
1092
|
return;
|
|
1075
1093
|
}
|
|
1076
1094
|
|
|
@@ -1091,9 +1109,8 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
1091
1109
|
callbackContext.success(result);
|
|
1092
1110
|
return;
|
|
1093
1111
|
}
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
callbackContext.success(openSettingsForRuntimePermission(Manifest.permission.RECORD_AUDIO, true, false));
|
|
1112
|
+
if (hasPendingMicrophonePermissionRequest()) {
|
|
1113
|
+
rejectBusyCallback(callbackContext, "RECORD_AUDIO permission");
|
|
1097
1114
|
return;
|
|
1098
1115
|
}
|
|
1099
1116
|
|
|
@@ -1131,11 +1148,10 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
1131
1148
|
}
|
|
1132
1149
|
|
|
1133
1150
|
if (!hasMicrophonePermission()) {
|
|
1134
|
-
if (
|
|
1135
|
-
callbackContext
|
|
1151
|
+
if (hasPendingMicrophonePermissionRequest()) {
|
|
1152
|
+
rejectBusyCallback(callbackContext, "RECORD_AUDIO permission");
|
|
1136
1153
|
return;
|
|
1137
1154
|
}
|
|
1138
|
-
|
|
1139
1155
|
pendingMicStartCallback = callbackContext;
|
|
1140
1156
|
rememberRuntimePermissionRequest(Manifest.permission.RECORD_AUDIO);
|
|
1141
1157
|
cordova.requestPermission(this, REQUEST_RECORD_AUDIO, Manifest.permission.RECORD_AUDIO);
|
|
@@ -1319,11 +1335,10 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
1319
1335
|
|
|
1320
1336
|
private void setFlashlightWithPermission(boolean enabled, boolean toggle, CallbackContext callbackContext) throws Exception {
|
|
1321
1337
|
if (!hasCameraPermission()) {
|
|
1322
|
-
if (
|
|
1323
|
-
callbackContext
|
|
1338
|
+
if (hasPendingCameraPermissionRequest()) {
|
|
1339
|
+
rejectBusyCallback(callbackContext, "CAMERA permission");
|
|
1324
1340
|
return;
|
|
1325
1341
|
}
|
|
1326
|
-
|
|
1327
1342
|
pendingFlashlightCallback = callbackContext;
|
|
1328
1343
|
pendingFlashlightEnabled = enabled;
|
|
1329
1344
|
pendingFlashlightToggle = toggle;
|
|
@@ -1422,6 +1437,11 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
1422
1437
|
}
|
|
1423
1438
|
|
|
1424
1439
|
private void pickFile(JSONObject options, CallbackContext callbackContext) {
|
|
1440
|
+
if (filePickerCallback != null) {
|
|
1441
|
+
rejectBusyCallback(callbackContext, "File picker");
|
|
1442
|
+
return;
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1425
1445
|
JSONObject safeOptions = options == null ? new JSONObject() : options;
|
|
1426
1446
|
String kind = safeOptions.optString("tipo", safeOptions.optString("kind", "file"));
|
|
1427
1447
|
boolean multiple = safeOptions.optBoolean("multiplo", safeOptions.optBoolean("multiple", false));
|
|
@@ -1463,12 +1483,22 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
1463
1483
|
}
|
|
1464
1484
|
|
|
1465
1485
|
private void pickFolder(CallbackContext callbackContext) {
|
|
1486
|
+
if (folderPickerCallback != null) {
|
|
1487
|
+
rejectBusyCallback(callbackContext, "Folder picker");
|
|
1488
|
+
return;
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1466
1491
|
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
|
1467
1492
|
folderPickerCallback = callbackContext;
|
|
1468
1493
|
cordova.startActivityForResult(this, intent, REQUEST_PICK_FOLDER);
|
|
1469
1494
|
}
|
|
1470
1495
|
|
|
1471
1496
|
private void saveFile(JSONObject options, CallbackContext callbackContext) throws Exception {
|
|
1497
|
+
if (saveFileCallback != null) {
|
|
1498
|
+
rejectBusyCallback(callbackContext, "File save dialog");
|
|
1499
|
+
return;
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1472
1502
|
JSONObject safeOptions = options == null ? new JSONObject() : options;
|
|
1473
1503
|
String name = safeOptions.optString("nome", safeOptions.optString("name", "arquivo.txt"));
|
|
1474
1504
|
String mimeType = safeOptions.optString("mimeType", "text/plain");
|
|
@@ -2016,6 +2046,167 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
2016
2046
|
);
|
|
2017
2047
|
}
|
|
2018
2048
|
|
|
2049
|
+
static PendingIntent createNotificationClickIntent(Context context, int id, JSONObject detail) {
|
|
2050
|
+
if (shouldOpenAppForNotificationClick(detail)) {
|
|
2051
|
+
return createContentIntent(context, id, detail);
|
|
2052
|
+
}
|
|
2053
|
+
|
|
2054
|
+
Intent intent = new Intent(context, NotificationClickReceiver.class);
|
|
2055
|
+
intent.putExtra(EXTRA_NOTIFICATION_CLICKED, true);
|
|
2056
|
+
intent.putExtra(EXTRA_NOTIFICATION_DETAIL, detail == null ? "{}" : detail.toString());
|
|
2057
|
+
|
|
2058
|
+
return PendingIntent.getBroadcast(
|
|
2059
|
+
context,
|
|
2060
|
+
id,
|
|
2061
|
+
intent,
|
|
2062
|
+
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
|
|
2063
|
+
);
|
|
2064
|
+
}
|
|
2065
|
+
|
|
2066
|
+
static void handleNotificationClickBroadcast(Context context, JSONObject detail) {
|
|
2067
|
+
if (detail == null) {
|
|
2068
|
+
return;
|
|
2069
|
+
}
|
|
2070
|
+
|
|
2071
|
+
if (activeBridge != null && activeBridge.webView != null) {
|
|
2072
|
+
activeBridge.dispatchNotificationClick(detail);
|
|
2073
|
+
return;
|
|
2074
|
+
}
|
|
2075
|
+
|
|
2076
|
+
handleNativeNotificationAction(context, detail);
|
|
2077
|
+
}
|
|
2078
|
+
|
|
2079
|
+
private static void handleNativeNotificationAction(Context context, JSONObject detail) {
|
|
2080
|
+
JSONObject action = notificationClickAction(detail);
|
|
2081
|
+
if (action == null) {
|
|
2082
|
+
return;
|
|
2083
|
+
}
|
|
2084
|
+
|
|
2085
|
+
String functionName = action.optString("funcao",
|
|
2086
|
+
action.optString("functionName",
|
|
2087
|
+
action.optString("function",
|
|
2088
|
+
action.optString("fn", action.optString("nomeFuncao", "")))));
|
|
2089
|
+
|
|
2090
|
+
if (!isExternalUrlFunction(functionName)) {
|
|
2091
|
+
return;
|
|
2092
|
+
}
|
|
2093
|
+
|
|
2094
|
+
String url = firstActionArgument(action);
|
|
2095
|
+
if (url.length() == 0) {
|
|
2096
|
+
url = action.optString("url", action.optString("href", ""));
|
|
2097
|
+
}
|
|
2098
|
+
if (url.length() == 0) {
|
|
2099
|
+
return;
|
|
2100
|
+
}
|
|
2101
|
+
|
|
2102
|
+
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
|
2103
|
+
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
2104
|
+
try {
|
|
2105
|
+
context.startActivity(intent);
|
|
2106
|
+
} catch (Exception ignored) {
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
private static boolean isExternalUrlFunction(String functionName) {
|
|
2111
|
+
return "abrirForaDoApp".equals(functionName)
|
|
2112
|
+
|| "abrirUrlExterno".equals(functionName)
|
|
2113
|
+
|| "abrirUrl".equals(functionName)
|
|
2114
|
+
|| "openOutsideApp".equals(functionName)
|
|
2115
|
+
|| "openExternalUrl".equals(functionName)
|
|
2116
|
+
|| "openUrl".equals(functionName);
|
|
2117
|
+
}
|
|
2118
|
+
|
|
2119
|
+
private static String firstActionArgument(JSONObject action) {
|
|
2120
|
+
JSONArray args = action.optJSONArray("argumentos");
|
|
2121
|
+
if (args == null) {
|
|
2122
|
+
args = action.optJSONArray("args");
|
|
2123
|
+
}
|
|
2124
|
+
if (args == null) {
|
|
2125
|
+
args = action.optJSONArray("parametros");
|
|
2126
|
+
}
|
|
2127
|
+
if (args == null) {
|
|
2128
|
+
args = action.optJSONArray("params");
|
|
2129
|
+
}
|
|
2130
|
+
if (args == null || args.length() == 0) {
|
|
2131
|
+
return "";
|
|
2132
|
+
}
|
|
2133
|
+
return args.optString(0, "");
|
|
2134
|
+
}
|
|
2135
|
+
|
|
2136
|
+
private static JSONObject notificationClickAction(JSONObject detail) {
|
|
2137
|
+
if (detail == null) {
|
|
2138
|
+
return null;
|
|
2139
|
+
}
|
|
2140
|
+
|
|
2141
|
+
JSONObject action = detail.optJSONObject("action");
|
|
2142
|
+
if (action != null) {
|
|
2143
|
+
JSONObject nested = action.optJSONObject("aoClicar");
|
|
2144
|
+
if (nested == null) {
|
|
2145
|
+
nested = action.optJSONObject("onClick");
|
|
2146
|
+
}
|
|
2147
|
+
return nested == null ? action : nested;
|
|
2148
|
+
}
|
|
2149
|
+
|
|
2150
|
+
action = detail.optJSONObject("acao");
|
|
2151
|
+
if (action != null) {
|
|
2152
|
+
JSONObject nested = action.optJSONObject("aoClicar");
|
|
2153
|
+
if (nested == null) {
|
|
2154
|
+
nested = action.optJSONObject("onClick");
|
|
2155
|
+
}
|
|
2156
|
+
return nested == null ? action : nested;
|
|
2157
|
+
}
|
|
2158
|
+
|
|
2159
|
+
action = detail.optJSONObject("aoClicar");
|
|
2160
|
+
if (action == null) {
|
|
2161
|
+
action = detail.optJSONObject("onClick");
|
|
2162
|
+
}
|
|
2163
|
+
return action;
|
|
2164
|
+
}
|
|
2165
|
+
|
|
2166
|
+
private static boolean shouldOpenAppForNotificationClick(JSONObject detail) {
|
|
2167
|
+
Boolean open = openFlag(detail);
|
|
2168
|
+
JSONObject action;
|
|
2169
|
+
|
|
2170
|
+
if (open != null) {
|
|
2171
|
+
return open.booleanValue();
|
|
2172
|
+
}
|
|
2173
|
+
|
|
2174
|
+
action = detail == null ? null : detail.optJSONObject("action");
|
|
2175
|
+
open = openFlag(action);
|
|
2176
|
+
if (open != null) {
|
|
2177
|
+
return open.booleanValue();
|
|
2178
|
+
}
|
|
2179
|
+
|
|
2180
|
+
action = detail == null ? null : detail.optJSONObject("acao");
|
|
2181
|
+
open = openFlag(action);
|
|
2182
|
+
if (open != null) {
|
|
2183
|
+
return open.booleanValue();
|
|
2184
|
+
}
|
|
2185
|
+
|
|
2186
|
+
action = notificationClickAction(detail);
|
|
2187
|
+
open = openFlag(action);
|
|
2188
|
+
return open == null || open.booleanValue();
|
|
2189
|
+
}
|
|
2190
|
+
|
|
2191
|
+
private static Boolean openFlag(JSONObject object) {
|
|
2192
|
+
if (object == null) {
|
|
2193
|
+
return null;
|
|
2194
|
+
}
|
|
2195
|
+
if (object.has("open")) {
|
|
2196
|
+
return Boolean.valueOf(object.optBoolean("open", true));
|
|
2197
|
+
}
|
|
2198
|
+
if (object.has("abrir")) {
|
|
2199
|
+
return Boolean.valueOf(object.optBoolean("abrir", true));
|
|
2200
|
+
}
|
|
2201
|
+
if (object.has("openApp")) {
|
|
2202
|
+
return Boolean.valueOf(object.optBoolean("openApp", true));
|
|
2203
|
+
}
|
|
2204
|
+
if (object.has("abrirApp")) {
|
|
2205
|
+
return Boolean.valueOf(object.optBoolean("abrirApp", true));
|
|
2206
|
+
}
|
|
2207
|
+
return null;
|
|
2208
|
+
}
|
|
2209
|
+
|
|
2019
2210
|
static boolean canScheduleExactAlarms(Context context) {
|
|
2020
2211
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
|
|
2021
2212
|
return true;
|
|
@@ -2219,9 +2410,13 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
2219
2410
|
static JSONObject detailPayload(JSONObject options) throws Exception {
|
|
2220
2411
|
JSONObject detail = new JSONObject();
|
|
2221
2412
|
JSONObject click = options.optJSONObject("aoClicar");
|
|
2413
|
+
Boolean open = openFlag(options);
|
|
2222
2414
|
if (click == null) {
|
|
2223
2415
|
click = options.optJSONObject("onClick");
|
|
2224
2416
|
}
|
|
2417
|
+
if (open == null) {
|
|
2418
|
+
open = openFlag(click);
|
|
2419
|
+
}
|
|
2225
2420
|
|
|
2226
2421
|
detail.put("id", notificationId(options));
|
|
2227
2422
|
detail.put("title", title(options));
|
|
@@ -2232,6 +2427,10 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
2232
2427
|
detail.put("clickedAt", System.currentTimeMillis());
|
|
2233
2428
|
detail.put("onClick", click == null ? new JSONObject().put("action", "open-app") : click);
|
|
2234
2429
|
detail.put("aoClicar", click == null ? new JSONObject().put("acao", "abrir-app") : click);
|
|
2430
|
+
if (open != null) {
|
|
2431
|
+
detail.put("open", open.booleanValue());
|
|
2432
|
+
detail.put("abrir", open.booleanValue());
|
|
2433
|
+
}
|
|
2235
2434
|
return detail;
|
|
2236
2435
|
}
|
|
2237
2436
|
|
|
@@ -2259,7 +2458,7 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
2259
2458
|
builder.addAction(
|
|
2260
2459
|
context.getApplicationInfo().icon,
|
|
2261
2460
|
title,
|
|
2262
|
-
|
|
2461
|
+
createNotificationClickIntent(context, notificationId * 100 + index + 1, detail)
|
|
2263
2462
|
);
|
|
2264
2463
|
} catch (Exception ignored) {
|
|
2265
2464
|
}
|
package/src/templates/cordova-plugin-html2apk-bridge/src/android/NotificationClickReceiver.java
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
package dev.html2apk.bridge;
|
|
2
|
+
|
|
3
|
+
import android.content.BroadcastReceiver;
|
|
4
|
+
import android.content.Context;
|
|
5
|
+
import android.content.Intent;
|
|
6
|
+
|
|
7
|
+
import androidx.core.app.NotificationManagerCompat;
|
|
8
|
+
|
|
9
|
+
import org.json.JSONObject;
|
|
10
|
+
|
|
11
|
+
public class NotificationClickReceiver extends BroadcastReceiver {
|
|
12
|
+
@Override
|
|
13
|
+
public void onReceive(Context context, Intent intent) {
|
|
14
|
+
if (intent == null || !intent.getBooleanExtra(Html2ApkBridge.EXTRA_NOTIFICATION_CLICKED, false)) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
JSONObject detail;
|
|
19
|
+
try {
|
|
20
|
+
detail = new JSONObject(intent.getStringExtra(Html2ApkBridge.EXTRA_NOTIFICATION_DETAIL));
|
|
21
|
+
} catch (Exception ignored) {
|
|
22
|
+
detail = new JSONObject();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
Html2ApkBridge.handleNotificationClickBroadcast(context, detail);
|
|
26
|
+
NotificationManagerCompat.from(context).cancel(detail.optInt("id", 0));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -58,7 +58,7 @@ public class NotificationReceiver extends BroadcastReceiver {
|
|
|
58
58
|
.setContentText(text)
|
|
59
59
|
.setStyle(new NotificationCompat.BigTextStyle().bigText(text))
|
|
60
60
|
.setAutoCancel(true)
|
|
61
|
-
.setContentIntent(Html2ApkBridge.
|
|
61
|
+
.setContentIntent(Html2ApkBridge.createNotificationClickIntent(context, id, detail))
|
|
62
62
|
.setPriority(NotificationCompat.PRIORITY_DEFAULT);
|
|
63
63
|
|
|
64
64
|
Html2ApkBridge.addNotificationActions(builder, context, id, displayOptions);
|