html2apk 0.8.0 → 0.11.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.
@@ -712,7 +712,7 @@ h2 {
712
712
 
713
713
  .file-editor-panel {
714
714
  display: grid;
715
- grid-template-rows: auto minmax(280px, 1fr) minmax(180px, 240px);
715
+ grid-template-rows: auto minmax(0, 1fr);
716
716
  overflow: hidden;
717
717
  }
718
718
 
@@ -745,58 +745,96 @@ h2 {
745
745
  font-weight: 800;
746
746
  }
747
747
 
748
- #fileEditorInput {
749
- width: 100%;
748
+ .code-editor-shell {
749
+ position: relative;
750
750
  min-height: 0;
751
- resize: none;
751
+ overflow: hidden;
752
+ background: #0c1117;
753
+ }
754
+
755
+ #fileEditorInput,
756
+ .file-editor-highlight {
757
+ width: 100%;
752
758
  border: 0;
753
- border-bottom: 1px solid var(--line);
754
759
  outline: none;
755
- padding: 16px;
756
- background: var(--bg);
757
- color: var(--text);
758
- font: 13px/1.55 ui-monospace, SFMono-Regular, Consolas, monospace;
760
+ padding: 16px 18px;
761
+ font: 13px/1.55 "Cascadia Mono", ui-monospace, SFMono-Regular, Consolas, monospace;
759
762
  tab-size: 2;
763
+ white-space: pre-wrap;
764
+ overflow-wrap: anywhere;
760
765
  }
761
766
 
762
- .syntax-preview-wrap {
767
+ #fileEditorInput {
768
+ position: relative;
769
+ z-index: 2;
770
+ height: 100%;
763
771
  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));
772
+ resize: none;
773
+ background: transparent;
774
+ color: transparent;
775
+ -webkit-text-fill-color: transparent;
776
+ caret-color: #d6e4f7;
767
777
  }
768
778
 
769
- .syntax-preview-wrap > strong {
770
- padding: 10px 14px 0;
771
- color: var(--muted);
772
- font-size: 12px;
779
+ #fileEditorInput::selection {
780
+ background: rgba(88, 166, 255, 0.34);
773
781
  }
774
782
 
775
- .syntax-preview {
783
+ .file-editor-highlight {
784
+ position: absolute;
785
+ inset: 0;
786
+ z-index: 1;
776
787
  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;
788
+ min-height: 100%;
789
+ overflow: hidden;
790
+ color: #d6e4f7;
791
+ pointer-events: none;
792
+ }
793
+
794
+ .file-editor-highlight code {
795
+ display: block;
796
+ min-height: 100%;
797
+ }
798
+
799
+ #fileEditorInput:disabled {
800
+ cursor: not-allowed;
801
+ }
802
+
803
+ #fileEditorInput:disabled + .file-editor-highlight,
804
+ .code-editor-shell:has(#fileEditorInput:disabled) .file-editor-highlight {
805
+ opacity: .58;
806
+ }
807
+
808
+ #fileEditorInput::placeholder {
809
+ color: #64748b;
810
+ -webkit-text-fill-color: #64748b;
781
811
  }
782
812
 
783
813
  .syntax-token-tag,
784
814
  .syntax-token-keyword {
785
- color: var(--blue);
815
+ color: #79c0ff;
786
816
  font-weight: 800;
787
817
  }
788
818
 
789
819
  .syntax-token-string {
790
- color: var(--green);
820
+ color: #a5d6a7;
791
821
  }
792
822
 
793
823
  .syntax-token-comment {
794
- color: var(--muted);
824
+ color: #8b949e;
795
825
  font-style: italic;
796
826
  }
797
827
 
798
828
  .syntax-token-number {
799
- color: var(--amber);
829
+ color: #f2cc60;
830
+ }
831
+
832
+ .syntax-token-function {
833
+ color: #d2a8ff;
834
+ }
835
+
836
+ .syntax-token-operator {
837
+ color: #ffab70;
800
838
  }
801
839
 
802
840
  .preference-row {
@@ -989,17 +1027,26 @@ h2 {
989
1027
 
990
1028
  .log-console {
991
1029
  height: min(620px, calc(100vh - 230px));
1030
+ min-height: 0;
1031
+ box-sizing: border-box;
992
1032
  overflow: auto;
993
1033
  border: 1px solid var(--line);
994
1034
  border-radius: 8px;
995
1035
  background: #0c1117;
996
1036
  color: #d6e4f7;
997
- padding: 18px;
1037
+ padding: 18px 18px 34px;
1038
+ scroll-padding-bottom: 34px;
998
1039
  font-family: "Cascadia Mono", Consolas, monospace;
999
1040
  font-size: 13px;
1000
1041
  line-height: 1.55;
1001
1042
  }
1002
1043
 
1044
+ .log-console::after {
1045
+ content: "";
1046
+ display: block;
1047
+ height: 12px;
1048
+ }
1049
+
1003
1050
  .log-line {
1004
1051
  padding: 4px 0;
1005
1052
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
@@ -1053,6 +1100,110 @@ h2 {
1053
1100
  margin-bottom: 18px;
1054
1101
  }
1055
1102
 
1103
+ .code-browser {
1104
+ display: grid;
1105
+ grid-template-columns: minmax(220px, 270px) minmax(0, 1fr);
1106
+ gap: 16px;
1107
+ align-items: start;
1108
+ }
1109
+
1110
+ .code-categories {
1111
+ position: sticky;
1112
+ top: 0;
1113
+ display: grid;
1114
+ gap: 8px;
1115
+ max-height: calc(100vh - 160px - var(--visible-log-height));
1116
+ overflow: auto;
1117
+ padding-right: 2px;
1118
+ }
1119
+
1120
+ .code-category-button {
1121
+ width: 100%;
1122
+ min-height: 58px;
1123
+ border: 1px solid var(--line);
1124
+ border-radius: 8px;
1125
+ background: var(--panel);
1126
+ color: var(--text);
1127
+ padding: 10px 12px;
1128
+ display: grid;
1129
+ grid-template-columns: minmax(0, 1fr) auto;
1130
+ gap: 4px 10px;
1131
+ align-items: center;
1132
+ text-align: left;
1133
+ box-shadow: 0 14px 34px rgba(49, 86, 130, 0.08);
1134
+ transition: border-color 160ms ease, background 160ms ease, color 160ms ease, transform 160ms ease;
1135
+ }
1136
+
1137
+ .code-category-button:hover {
1138
+ border-color: color-mix(in srgb, var(--blue) 42%, var(--line));
1139
+ background: color-mix(in srgb, var(--blue-soft) 56%, var(--panel));
1140
+ transform: translateY(-1px);
1141
+ }
1142
+
1143
+ .code-category-button.active {
1144
+ border-color: var(--blue);
1145
+ background: var(--blue-soft);
1146
+ color: var(--blue);
1147
+ box-shadow: inset 4px 0 0 var(--blue), 0 14px 34px rgba(18, 111, 255, 0.12);
1148
+ }
1149
+
1150
+ .code-category-button strong {
1151
+ min-width: 0;
1152
+ overflow: hidden;
1153
+ text-overflow: ellipsis;
1154
+ white-space: nowrap;
1155
+ font-size: 14px;
1156
+ }
1157
+
1158
+ .code-category-button small {
1159
+ grid-column: 1 / -1;
1160
+ color: var(--muted);
1161
+ line-height: 1.35;
1162
+ }
1163
+
1164
+ .code-category-count {
1165
+ min-width: 28px;
1166
+ min-height: 24px;
1167
+ border-radius: 999px;
1168
+ display: inline-flex;
1169
+ align-items: center;
1170
+ justify-content: center;
1171
+ background: var(--panel-soft);
1172
+ color: var(--muted);
1173
+ font-size: 12px;
1174
+ font-weight: 900;
1175
+ }
1176
+
1177
+ .code-category-button.active .code-category-count {
1178
+ background: var(--panel);
1179
+ color: var(--blue);
1180
+ }
1181
+
1182
+ .code-results {
1183
+ min-width: 0;
1184
+ display: grid;
1185
+ gap: 14px;
1186
+ }
1187
+
1188
+ .code-summary {
1189
+ border: 1px solid var(--line);
1190
+ border-radius: 8px;
1191
+ background: color-mix(in srgb, var(--panel) 82%, var(--panel-soft));
1192
+ padding: 14px 16px;
1193
+ }
1194
+
1195
+ .code-summary strong {
1196
+ display: block;
1197
+ color: var(--text);
1198
+ margin-bottom: 4px;
1199
+ }
1200
+
1201
+ .code-summary p {
1202
+ margin: 0;
1203
+ color: var(--muted);
1204
+ line-height: 1.45;
1205
+ }
1206
+
1056
1207
  .code-grid {
1057
1208
  grid-template-columns: repeat(2, minmax(0, 1fr));
1058
1209
  }
@@ -1073,11 +1224,14 @@ h2 {
1073
1224
  }
1074
1225
 
1075
1226
  .code-card-top code {
1227
+ display: block;
1076
1228
  overflow-wrap: anywhere;
1077
1229
  color: #d6e4f7;
1078
1230
  background: #0c1117;
1079
1231
  border-radius: 7px;
1080
1232
  padding: 10px;
1233
+ font: 12px/1.55 "Cascadia Mono", Consolas, "Courier New", monospace;
1234
+ white-space: pre-wrap;
1081
1235
  }
1082
1236
 
1083
1237
  .code-card-top span {
@@ -1155,7 +1309,7 @@ h2 {
1155
1309
  display: block;
1156
1310
  padding: 12px;
1157
1311
  color: #d6e4f7;
1158
- font: 12px/1.55 Consolas, "Courier New", monospace;
1312
+ font: 12px/1.55 "Cascadia Mono", Consolas, "Courier New", monospace;
1159
1313
  white-space: pre;
1160
1314
  }
1161
1315
 
@@ -1262,9 +1416,10 @@ body.logs-visible .bottom-log-bar {
1262
1416
  }
1263
1417
 
1264
1418
  .bottom-log-console {
1265
- height: auto;
1419
+ height: 100%;
1266
1420
  min-height: 0;
1267
- padding: 10px 12px;
1421
+ padding: 10px 12px 26px;
1422
+ scroll-padding-bottom: 26px;
1268
1423
  font-size: 12px;
1269
1424
  line-height: 1.4;
1270
1425
  }
@@ -1517,6 +1672,20 @@ body.logs-visible .bottom-log-bar {
1517
1672
  grid-template-columns: 1fr;
1518
1673
  }
1519
1674
 
1675
+ .code-browser {
1676
+ grid-template-columns: 1fr;
1677
+ }
1678
+
1679
+ .code-categories {
1680
+ position: static;
1681
+ grid-auto-flow: column;
1682
+ grid-auto-columns: minmax(180px, 220px);
1683
+ overflow-x: auto;
1684
+ overflow-y: hidden;
1685
+ max-height: none;
1686
+ padding: 0 0 4px;
1687
+ }
1688
+
1520
1689
  .icon-field,
1521
1690
  .keystore-field,
1522
1691
  .onesignal-field,
@@ -55,29 +55,74 @@ function getAndroidSdkPath(env = process.env) {
55
55
  return defaultAndroidSdkCandidates(env).find(pathExists) || env.ANDROID_HOME || env.ANDROID_SDK_ROOT || null;
56
56
  }
57
57
 
58
- function getJavaHome(env = process.env) {
58
+ function getValidJavaHome(dir) {
59
+ if (!pathExists(dir)) {
60
+ return null;
61
+ }
62
+ const javaExe = process.platform === "win32" ? "java.exe" : "java";
63
+ if (pathExists(path.join(dir, "bin", javaExe))) {
64
+ return dir;
65
+ }
66
+ // Support macOS virtual machine package structures
67
+ const macHome = path.join(dir, "Contents", "Home");
68
+ if (pathExists(path.join(macHome, "bin", javaExe))) {
69
+ return macHome;
70
+ }
71
+ return null;
72
+ }
73
+
74
+ function getJavaHome(env = process.env, parentDirsOverride = null) {
59
75
  if (env.JAVA_HOME && pathExists(env.JAVA_HOME)) {
60
76
  return env.JAVA_HOME;
61
77
  }
62
78
 
63
- const candidates = [];
64
- if (process.platform === "win32") {
65
- candidates.push(
66
- latestSubdir(path.join(process.env.ProgramFiles || "C:\\Program Files", "Eclipse Adoptium")),
67
- latestSubdir(path.join(process.env.ProgramFiles || "C:\\Program Files", "Java"))
68
- );
69
- } else if (process.platform === "darwin") {
70
- candidates.push(
71
- latestSubdir("/Library/Java/JavaVirtualMachines")
72
- );
73
- } else {
74
- candidates.push(
75
- latestSubdir("/usr/lib/jvm"),
76
- latestSubdir("/usr/java")
77
- );
79
+ const parentDirs = parentDirsOverride || [];
80
+ if (!parentDirsOverride) {
81
+ if (process.platform === "win32") {
82
+ parentDirs.push(
83
+ path.join(process.env.ProgramFiles || "C:\\Program Files", "Eclipse Adoptium"),
84
+ path.join(process.env.ProgramFiles || "C:\\Program Files", "Java")
85
+ );
86
+ } else if (process.platform === "darwin") {
87
+ parentDirs.push(
88
+ "/Library/Java/JavaVirtualMachines"
89
+ );
90
+ } else {
91
+ parentDirs.push(
92
+ "/usr/lib/jvm",
93
+ "/usr/java"
94
+ );
95
+ }
96
+ }
97
+
98
+ for (const parent of parentDirs) {
99
+ if (!pathExists(parent)) {
100
+ continue;
101
+ }
102
+ try {
103
+ const entries = fs.readdirSync(parent)
104
+ .filter((name) => {
105
+ try {
106
+ return fs.statSync(path.join(parent, name)).isDirectory();
107
+ } catch {
108
+ return false;
109
+ }
110
+ })
111
+ .sort((a, b) => b.localeCompare(a, undefined, { numeric: true }));
112
+
113
+ for (const name of entries) {
114
+ const fullPath = path.join(parent, name);
115
+ const valid = getValidJavaHome(fullPath);
116
+ if (valid) {
117
+ return valid;
118
+ }
119
+ }
120
+ } catch {
121
+ // Ignore directory read errors and check next parent
122
+ }
78
123
  }
79
124
 
80
- return candidates.find(pathExists) || env.JAVA_HOME || null;
125
+ return env.JAVA_HOME || null;
81
126
  }
82
127
 
83
128
  function latestSubdir(parent) {
@@ -157,6 +202,23 @@ function getRuntimeEnvironment(env = process.env) {
157
202
  pathEntries.push(path.join(gradleHome, "bin"));
158
203
  }
159
204
 
205
+ // Ensure common system directories are present in PATH, since GUI/desktop
206
+ // launchers on Linux often inherit a minimal PATH that omits /usr/local/bin.
207
+ if (process.platform !== "win32") {
208
+ const systemDirs = [
209
+ "/usr/local/bin",
210
+ "/usr/local/sbin",
211
+ "/usr/bin",
212
+ "/usr/sbin"
213
+ ];
214
+ const currentPath = (nextEnv.PATH || "").split(pathDelimiter());
215
+ for (const dir of systemDirs) {
216
+ if (!currentPath.includes(dir) && pathExists(dir)) {
217
+ pathEntries.push(dir);
218
+ }
219
+ }
220
+ }
221
+
160
222
  prependToPath(nextEnv, pathEntries);
161
223
 
162
224
  return {
@@ -18,13 +18,40 @@
18
18
  <uses-permission android:name="android.permission.VIBRATE" />
19
19
  <uses-permission android:name="android.permission.CAMERA" />
20
20
  <uses-permission android:name="android.permission.RECORD_AUDIO" />
21
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
22
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
23
+ <uses-permission android:name="android.permission.USE_BIOMETRIC" />
24
+ <uses-permission android:name="android.permission.USE_FINGERPRINT" />
25
+ <uses-permission android:name="android.permission.SET_WALLPAPER" />
26
+ <uses-permission android:name="android.permission.INTERNET" />
21
27
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
28
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
29
+ <uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
30
+ <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
31
+ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
32
+ <uses-permission android:name="android.permission.NFC" />
33
+ <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
34
+ <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
35
+ <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
36
+ <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
37
+ <uses-feature android:name="android.hardware.camera" android:required="false" />
22
38
  <uses-feature android:name="android.hardware.camera.flash" android:required="false" />
39
+ <uses-feature android:name="android.hardware.bluetooth" android:required="false" />
40
+ <uses-feature android:name="android.hardware.nfc" android:required="false" />
23
41
  <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
24
42
  <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
25
43
  </config-file>
26
44
 
27
45
  <config-file target="AndroidManifest.xml" parent="/manifest/application">
46
+ <provider
47
+ android:name="androidx.core.content.FileProvider"
48
+ android:authorities="${applicationId}.html2apk.fileprovider"
49
+ android:exported="false"
50
+ android:grantUriPermissions="true">
51
+ <meta-data
52
+ android:name="android.support.FILE_PROVIDER_PATHS"
53
+ android:resource="@xml/html2apk_file_paths" />
54
+ </provider>
28
55
  <service android:name="dev.html2apk.bridge.FloatingIconService" android:exported="false" />
29
56
  <receiver android:name="dev.html2apk.bridge.NotificationReceiver" android:exported="false" />
30
57
  <receiver android:name="dev.html2apk.bridge.NotificationClickReceiver" android:exported="false" />
@@ -36,12 +63,35 @@
36
63
  </receiver>
37
64
  </config-file>
38
65
 
66
+ <config-file target="AndroidManifest.xml" parent="/manifest/application/activity">
67
+ <intent-filter>
68
+ <action android:name="android.intent.action.SEND" />
69
+ <category android:name="android.intent.category.DEFAULT" />
70
+ <data android:mimeType="text/plain" />
71
+ <data android:mimeType="image/*" />
72
+ <data android:mimeType="video/*" />
73
+ <data android:mimeType="application/pdf" />
74
+ <data android:mimeType="*/*" />
75
+ </intent-filter>
76
+ <intent-filter>
77
+ <action android:name="android.intent.action.SEND_MULTIPLE" />
78
+ <category android:name="android.intent.category.DEFAULT" />
79
+ <data android:mimeType="text/plain" />
80
+ <data android:mimeType="image/*" />
81
+ <data android:mimeType="video/*" />
82
+ <data android:mimeType="application/pdf" />
83
+ <data android:mimeType="*/*" />
84
+ </intent-filter>
85
+ </config-file>
86
+
39
87
  <source-file src="src/android/Html2ApkBridge.java" target-dir="app/src/main/java/dev/html2apk/bridge" />
40
88
  <source-file src="src/android/FloatingIconService.java" target-dir="app/src/main/java/dev/html2apk/bridge" />
41
89
  <source-file src="src/android/NotificationReceiver.java" target-dir="app/src/main/java/dev/html2apk/bridge" />
42
90
  <source-file src="src/android/NotificationClickReceiver.java" target-dir="app/src/main/java/dev/html2apk/bridge" />
43
91
  <source-file src="src/android/BootReceiver.java" target-dir="app/src/main/java/dev/html2apk/bridge" />
44
92
  <source-file src="src/android/NotificationStore.java" target-dir="app/src/main/java/dev/html2apk/bridge" />
93
+ <resource-file src="src/android/xml/html2apk_file_paths.xml" target="res/xml/html2apk_file_paths.xml" />
45
94
  <framework src="androidx.core:core:1.12.0" />
95
+ <framework src="com.google.mlkit:text-recognition:16.0.1" />
46
96
  </platform>
47
97
  </plugin>
@@ -2,6 +2,7 @@ package dev.html2apk.bridge;
2
2
 
3
3
  import android.app.Service;
4
4
  import android.content.Intent;
5
+ import android.content.SharedPreferences;
5
6
  import android.graphics.PixelFormat;
6
7
  import android.graphics.drawable.GradientDrawable;
7
8
  import android.os.Build;
@@ -14,9 +15,14 @@ import android.view.WindowManager;
14
15
  import android.widget.ImageView;
15
16
 
16
17
  public class FloatingIconService extends Service {
18
+ private static final String PREFS_NAME = "html2apk_bridge";
19
+ private static final String OPACITY_KEY = "floating_icon_opacity";
20
+ private static final String EXTRA_OPACITY = "opacity";
21
+
17
22
  private WindowManager windowManager;
18
23
  private View floatingView;
19
24
  private WindowManager.LayoutParams params;
25
+ private float opacity = 1f;
20
26
  private int startX;
21
27
  private int startY;
22
28
  private float touchStartX;
@@ -29,6 +35,11 @@ public class FloatingIconService extends Service {
29
35
 
30
36
  @Override
31
37
  public int onStartCommand(Intent intent, int flags, int startId) {
38
+ applyOptions(intent);
39
+ if (floatingView != null) {
40
+ applyFloatingIconStyle();
41
+ return START_STICKY;
42
+ }
32
43
  showFloatingIcon();
33
44
  return START_STICKY;
34
45
  }
@@ -60,6 +71,7 @@ public class FloatingIconService extends Service {
60
71
  int padding = dp(8);
61
72
  icon.setImageDrawable(getApplicationInfo().loadIcon(getPackageManager()));
62
73
  icon.setPadding(padding, padding, padding, padding);
74
+ icon.setAlpha(opacity);
63
75
 
64
76
  GradientDrawable background = new GradientDrawable();
65
77
  background.setShape(GradientDrawable.OVAL);
@@ -125,6 +137,23 @@ public class FloatingIconService extends Service {
125
137
  floatingView = null;
126
138
  }
127
139
 
140
+ private void applyOptions(Intent intent) {
141
+ SharedPreferences preferences = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
142
+ float nextOpacity = preferences.getFloat(OPACITY_KEY, 1f);
143
+ if (intent != null && intent.hasExtra(EXTRA_OPACITY)) {
144
+ nextOpacity = intent.getFloatExtra(EXTRA_OPACITY, nextOpacity);
145
+ nextOpacity = clampOpacity(nextOpacity);
146
+ preferences.edit().putFloat(OPACITY_KEY, nextOpacity).apply();
147
+ }
148
+ opacity = clampOpacity(nextOpacity);
149
+ }
150
+
151
+ private void applyFloatingIconStyle() {
152
+ if (floatingView != null) {
153
+ floatingView.setAlpha(opacity);
154
+ }
155
+ }
156
+
128
157
  private void openApp() {
129
158
  Intent launchIntent = getPackageManager().getLaunchIntentForPackage(getPackageName());
130
159
  if (launchIntent == null) {
@@ -138,4 +167,8 @@ public class FloatingIconService extends Service {
138
167
  private int dp(int value) {
139
168
  return Math.round(value * getResources().getDisplayMetrics().density);
140
169
  }
170
+
171
+ private float clampOpacity(float value) {
172
+ return Math.max(0.1f, Math.min(1f, value));
173
+ }
141
174
  }