html2apk 0.2.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.
- package/README.md +323 -12
- package/examples/minimal/app.json +2 -0
- package/examples/minimal/dist/MeuApp-1.0.0-debug.apk +0 -0
- package/package.json +3 -2
- package/src/cli/index.js +10 -1
- package/src/cordova/config-xml.js +59 -1
- package/src/cordova/project.js +19 -1
- package/src/core/build-apk.js +399 -23
- package/src/core/config.js +47 -1
- package/src/core/defaults.js +6 -0
- package/src/desktop/main.js +152 -2
- package/src/desktop/preload.js +14 -0
- package/src/desktop/renderer/index.html +20 -2
- package/src/desktop/renderer/renderer.js +1254 -41
- package/src/desktop/renderer/styles.css +98 -2
- package/src/templates/cordova-plugin-html2apk-bridge/plugin.xml +4 -0
- package/src/templates/cordova-plugin-html2apk-bridge/src/android/Html2ApkBridge.java +1770 -38
- package/src/templates/cordova-plugin-html2apk-bridge/src/android/NotificationReceiver.java +19 -5
- package/src/templates/cordova-plugin-html2apk-bridge/src/android/NotificationStore.java +13 -1
- package/src/templates/cordova-plugin-html2apk-bridge/www/html2apk-bridge.js +516 -3
- package/src/templates/html2apk-auto-theme.js +144 -0
- package/src/templates/html2apk-onesignal.js +155 -0
- package/src/utils/command-runner.js +3 -0
package/src/desktop/main.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
const nodeFs = require("fs");
|
|
3
4
|
const fs = require("fs/promises");
|
|
4
5
|
const path = require("path");
|
|
5
6
|
const { spawn } = require("child_process");
|
|
6
7
|
const { app, BrowserWindow, dialog, ipcMain, screen, shell } = require("electron");
|
|
7
|
-
const { buildApk } = require("../core/build-apk");
|
|
8
|
+
const { buildApk, runDebugUsb } = require("../core/build-apk");
|
|
8
9
|
const {
|
|
9
10
|
REQUIRED_ANDROID_BUILD_TOOLS,
|
|
10
11
|
REQUIRED_ANDROID_PLATFORM,
|
|
@@ -13,6 +14,9 @@ const {
|
|
|
13
14
|
const { runDoctor, formatDoctorReport } = require("../runtime-manager/doctor");
|
|
14
15
|
|
|
15
16
|
let mainWindow = null;
|
|
17
|
+
let projectWatcher = null;
|
|
18
|
+
let projectWatchTimer = null;
|
|
19
|
+
let watchedProjectRoot = null;
|
|
16
20
|
const smokeTest = process.env.HTML2APK_DESKTOP_SMOKE === "1";
|
|
17
21
|
const APP_ID = "dev.caiomultiversando.html2apk";
|
|
18
22
|
const APP_NAME = "html2apk";
|
|
@@ -22,6 +26,7 @@ const WINDOW_BACKGROUNDS = {
|
|
|
22
26
|
};
|
|
23
27
|
|
|
24
28
|
app.commandLine.appendSwitch("disable-crash-reporter");
|
|
29
|
+
process.title = APP_NAME;
|
|
25
30
|
app.setName(APP_NAME);
|
|
26
31
|
|
|
27
32
|
if (process.platform === "win32") {
|
|
@@ -134,6 +139,90 @@ async function inspectProject(projectRoot) {
|
|
|
134
139
|
};
|
|
135
140
|
}
|
|
136
141
|
|
|
142
|
+
function shouldIgnoreProjectWatchPath(fileName) {
|
|
143
|
+
const normalized = String(fileName || "").replace(/\\/g, "/");
|
|
144
|
+
return normalized
|
|
145
|
+
&& (normalized.includes(".html2apk-doctor-")
|
|
146
|
+
|| normalized.startsWith("dist/")
|
|
147
|
+
|| normalized === "dist"
|
|
148
|
+
|| normalized.startsWith("node_modules/")
|
|
149
|
+
|| normalized === "node_modules"
|
|
150
|
+
|| normalized.startsWith(".git/")
|
|
151
|
+
|| normalized === ".git");
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function stopProjectWatcher() {
|
|
155
|
+
if (projectWatchTimer) {
|
|
156
|
+
clearTimeout(projectWatchTimer);
|
|
157
|
+
projectWatchTimer = null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (projectWatcher) {
|
|
161
|
+
projectWatcher.close();
|
|
162
|
+
projectWatcher = null;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
watchedProjectRoot = null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function startProjectWatcher(projectRoot, sender) {
|
|
169
|
+
stopProjectWatcher();
|
|
170
|
+
watchedProjectRoot = projectRoot;
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
projectWatcher = nodeFs.watch(projectRoot, { recursive: true }, (eventType, fileName) => {
|
|
174
|
+
if (shouldIgnoreProjectWatchPath(fileName)) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const changedPath = path.join(projectRoot, String(fileName || ""));
|
|
179
|
+
if (projectWatchTimer) {
|
|
180
|
+
clearTimeout(projectWatchTimer);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
projectWatchTimer = setTimeout(async () => {
|
|
184
|
+
projectWatchTimer = null;
|
|
185
|
+
try {
|
|
186
|
+
if (watchedProjectRoot !== projectRoot) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const project = await inspectProject(projectRoot);
|
|
191
|
+
sender.send("project:changed", {
|
|
192
|
+
eventType,
|
|
193
|
+
changedPath,
|
|
194
|
+
project,
|
|
195
|
+
time: new Date().toISOString()
|
|
196
|
+
});
|
|
197
|
+
} catch (error) {
|
|
198
|
+
sender.send("project:watch-error", {
|
|
199
|
+
message: error.message,
|
|
200
|
+
time: new Date().toISOString()
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}, 350);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
projectWatcher.on("error", (error) => {
|
|
207
|
+
sender.send("project:watch-error", {
|
|
208
|
+
message: error.message,
|
|
209
|
+
time: new Date().toISOString()
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
ok: true,
|
|
215
|
+
projectRoot
|
|
216
|
+
};
|
|
217
|
+
} catch (error) {
|
|
218
|
+
stopProjectWatcher();
|
|
219
|
+
return {
|
|
220
|
+
ok: false,
|
|
221
|
+
message: error.message
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
137
226
|
function cleanBuildOptions(options = {}) {
|
|
138
227
|
const output = {
|
|
139
228
|
projectRoot: options.projectRoot,
|
|
@@ -141,7 +230,7 @@ function cleanBuildOptions(options = {}) {
|
|
|
141
230
|
release: Boolean(options.release)
|
|
142
231
|
};
|
|
143
232
|
|
|
144
|
-
for (const key of ["mode", "appName", "packageId", "version", "icon", "androidPlatform", "minSdkVersion", "themeColor", "orientation", "permissions"]) {
|
|
233
|
+
for (const key of ["mode", "appName", "packageId", "version", "icon", "androidPlatform", "minSdkVersion", "themeColor", "themeMode", "theme", "oneSignalAppId", "orientation", "permissions", "deepLinks"]) {
|
|
145
234
|
if (Array.isArray(options[key]) || options[key]) {
|
|
146
235
|
output[key] = options[key];
|
|
147
236
|
}
|
|
@@ -350,6 +439,10 @@ async function installWithWingetAndroidCli(sender) {
|
|
|
350
439
|
}
|
|
351
440
|
|
|
352
441
|
app.whenReady().then(() => {
|
|
442
|
+
app.setName(APP_NAME);
|
|
443
|
+
if (process.platform === "win32") {
|
|
444
|
+
app.setAppUserModelId(APP_ID);
|
|
445
|
+
}
|
|
353
446
|
createWindow();
|
|
354
447
|
|
|
355
448
|
app.on("activate", () => {
|
|
@@ -360,6 +453,7 @@ app.whenReady().then(() => {
|
|
|
360
453
|
});
|
|
361
454
|
|
|
362
455
|
app.on("window-all-closed", () => {
|
|
456
|
+
stopProjectWatcher();
|
|
363
457
|
if (process.platform !== "darwin") {
|
|
364
458
|
app.quit();
|
|
365
459
|
}
|
|
@@ -435,6 +529,15 @@ ipcMain.handle("project:inspect", async (_event, projectRoot) => {
|
|
|
435
529
|
return inspectProject(projectRoot);
|
|
436
530
|
});
|
|
437
531
|
|
|
532
|
+
ipcMain.handle("project:watch", async (event, projectRoot) => {
|
|
533
|
+
return startProjectWatcher(projectRoot, event.sender);
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
ipcMain.handle("project:unwatch", async () => {
|
|
537
|
+
stopProjectWatcher();
|
|
538
|
+
return { ok: true };
|
|
539
|
+
});
|
|
540
|
+
|
|
438
541
|
ipcMain.handle("doctor:run", async (_event, projectRoot) => {
|
|
439
542
|
const report = await runDoctor({ projectRoot });
|
|
440
543
|
return {
|
|
@@ -512,6 +615,39 @@ ipcMain.handle("build:run", async (event, options) => {
|
|
|
512
615
|
}
|
|
513
616
|
});
|
|
514
617
|
|
|
618
|
+
ipcMain.handle("build:run-usb-debug", async (event, options) => {
|
|
619
|
+
const buildOptions = cleanBuildOptions(options);
|
|
620
|
+
const sendLog = (line, kind = "raw") => {
|
|
621
|
+
event.sender.send("build:log", {
|
|
622
|
+
line,
|
|
623
|
+
kind,
|
|
624
|
+
time: new Date().toISOString()
|
|
625
|
+
});
|
|
626
|
+
};
|
|
627
|
+
|
|
628
|
+
try {
|
|
629
|
+
sendLog("Starting html2apk USB debug build.", "system");
|
|
630
|
+
const result = await runDebugUsb({
|
|
631
|
+
...buildOptions,
|
|
632
|
+
release: false,
|
|
633
|
+
onLog: (line) => sendLog(line)
|
|
634
|
+
});
|
|
635
|
+
sendLog(`USB debug installed on device: ${result.device?.id || "Android device"}`, "success");
|
|
636
|
+
return {
|
|
637
|
+
ok: true,
|
|
638
|
+
result
|
|
639
|
+
};
|
|
640
|
+
} catch (error) {
|
|
641
|
+
sendLog(error.message, "error");
|
|
642
|
+
return {
|
|
643
|
+
ok: false,
|
|
644
|
+
message: error.message,
|
|
645
|
+
logs: error.logs || [],
|
|
646
|
+
buildDir: error.buildDir || null
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
});
|
|
650
|
+
|
|
515
651
|
ipcMain.handle("shell:open-path", async (_event, targetPath) => {
|
|
516
652
|
if (!targetPath) {
|
|
517
653
|
return false;
|
|
@@ -527,3 +663,17 @@ ipcMain.handle("shell:show-item", async (_event, targetPath) => {
|
|
|
527
663
|
shell.showItemInFolder(targetPath);
|
|
528
664
|
return true;
|
|
529
665
|
});
|
|
666
|
+
|
|
667
|
+
ipcMain.handle("shell:open-external", async (_event, targetUrl) => {
|
|
668
|
+
if (!targetUrl) {
|
|
669
|
+
return false;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
const url = new URL(String(targetUrl));
|
|
673
|
+
if (!["https:", "http:"].includes(url.protocol)) {
|
|
674
|
+
return false;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
await shell.openExternal(url.toString());
|
|
678
|
+
return true;
|
|
679
|
+
});
|
package/src/desktop/preload.js
CHANGED
|
@@ -7,11 +7,15 @@ contextBridge.exposeInMainWorld("html2apkDesktop", {
|
|
|
7
7
|
selectFolder: () => ipcRenderer.invoke("dialog:select-folder"),
|
|
8
8
|
selectIcon: () => ipcRenderer.invoke("dialog:select-icon"),
|
|
9
9
|
inspectProject: (projectRoot) => ipcRenderer.invoke("project:inspect", projectRoot),
|
|
10
|
+
watchProject: (projectRoot) => ipcRenderer.invoke("project:watch", projectRoot),
|
|
11
|
+
unwatchProject: () => ipcRenderer.invoke("project:unwatch"),
|
|
10
12
|
runDoctor: (projectRoot) => ipcRenderer.invoke("doctor:run", projectRoot),
|
|
11
13
|
installAndroidRequirements: () => ipcRenderer.invoke("install:android-requirements"),
|
|
12
14
|
runBuild: (options) => ipcRenderer.invoke("build:run", options),
|
|
15
|
+
runUsbDebugBuild: (options) => ipcRenderer.invoke("build:run-usb-debug", options),
|
|
13
16
|
openPath: (targetPath) => ipcRenderer.invoke("shell:open-path", targetPath),
|
|
14
17
|
showItem: (targetPath) => ipcRenderer.invoke("shell:show-item", targetPath),
|
|
18
|
+
openExternalUrl: (targetUrl) => ipcRenderer.invoke("shell:open-external", targetUrl),
|
|
15
19
|
minimizeWindow: () => ipcRenderer.invoke("window:minimize"),
|
|
16
20
|
toggleMaximizeWindow: () => ipcRenderer.invoke("window:toggle-maximize"),
|
|
17
21
|
closeWindow: () => ipcRenderer.invoke("window:close"),
|
|
@@ -26,5 +30,15 @@ contextBridge.exposeInMainWorld("html2apkDesktop", {
|
|
|
26
30
|
const handler = (_event, payload) => listener(payload);
|
|
27
31
|
ipcRenderer.on("install:log", handler);
|
|
28
32
|
return () => ipcRenderer.removeListener("install:log", handler);
|
|
33
|
+
},
|
|
34
|
+
onProjectChanged: (listener) => {
|
|
35
|
+
const handler = (_event, payload) => listener(payload);
|
|
36
|
+
ipcRenderer.on("project:changed", handler);
|
|
37
|
+
return () => ipcRenderer.removeListener("project:changed", handler);
|
|
38
|
+
},
|
|
39
|
+
onProjectWatchError: (listener) => {
|
|
40
|
+
const handler = (_event, payload) => listener(payload);
|
|
41
|
+
ipcRenderer.on("project:watch-error", handler);
|
|
42
|
+
return () => ipcRenderer.removeListener("project:watch-error", handler);
|
|
29
43
|
}
|
|
30
44
|
});
|
|
@@ -176,6 +176,13 @@
|
|
|
176
176
|
<span>cordova-android</span>
|
|
177
177
|
<input id="androidPlatformInput" type="text" placeholder="android@15.0.0">
|
|
178
178
|
</label>
|
|
179
|
+
<label class="field">
|
|
180
|
+
<span data-i18n="themeMode">Tema do APK</span>
|
|
181
|
+
<select id="themeModeInput">
|
|
182
|
+
<option value="fixed" data-i18n="themeModeFixed">Cor fixa</option>
|
|
183
|
+
<option value="auto" data-i18n="themeModeAuto">Automatico pela tela</option>
|
|
184
|
+
</select>
|
|
185
|
+
</label>
|
|
179
186
|
<label class="field color-field">
|
|
180
187
|
<span data-i18n="appThemeColor">Cor do tema do app</span>
|
|
181
188
|
<div class="color-picker">
|
|
@@ -183,6 +190,11 @@
|
|
|
183
190
|
<input id="themeColorTextInput" type="text" value="#126fff" maxlength="7">
|
|
184
191
|
</div>
|
|
185
192
|
</label>
|
|
193
|
+
<label class="field onesignal-field">
|
|
194
|
+
<span data-i18n="oneSignalAppId">OneSignal App ID</span>
|
|
195
|
+
<input id="oneSignalAppIdInput" type="text" placeholder="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx">
|
|
196
|
+
<small data-i18n="oneSignalAppIdHint">Opcional. Use o App ID do OneSignal, nao a REST API Key.</small>
|
|
197
|
+
</label>
|
|
186
198
|
<div class="field icon-field">
|
|
187
199
|
<span data-i18n="appIcon">Icone do app</span>
|
|
188
200
|
<div class="icon-picker">
|
|
@@ -275,6 +287,7 @@
|
|
|
275
287
|
</div>
|
|
276
288
|
<section class="action-strip">
|
|
277
289
|
<button id="buildButton" class="primary-action" type="button" disabled data-i18n="startBuild">Gerar APK</button>
|
|
290
|
+
<button id="usbDebugButton" class="secondary-action" type="button" disabled data-i18n="startUsbDebug">Testar no USB</button>
|
|
278
291
|
</section>
|
|
279
292
|
<div id="resultPanel" class="result-panel hidden">
|
|
280
293
|
<div>
|
|
@@ -292,8 +305,8 @@
|
|
|
292
305
|
<div class="success-panel">
|
|
293
306
|
<img src="../../../html2apk.png" alt="" class="success-icon">
|
|
294
307
|
<p class="eyebrow" data-i18n="successEyebrow">Concluido</p>
|
|
295
|
-
<h1 data-i18n="successTitle">APK gerado com sucesso</h1>
|
|
296
|
-
<p data-i18n="successText">Seu arquivo Android esta pronto na pasta dist.</p>
|
|
308
|
+
<h1 id="successTitle" data-i18n="successTitle">APK gerado com sucesso</h1>
|
|
309
|
+
<p id="successText" data-i18n="successText">Seu arquivo Android esta pronto na pasta dist.</p>
|
|
297
310
|
<code id="successApkPath">-</code>
|
|
298
311
|
<div class="result-actions">
|
|
299
312
|
<button id="successOpenDistButton" class="secondary-action" type="button" data-i18n="openDist">Abrir dist</button>
|
|
@@ -345,6 +358,11 @@
|
|
|
345
358
|
<strong data-i18n="helpDepsTitle">Dependencias no EXE</strong>
|
|
346
359
|
<p data-i18n="helpDeps">O executavel empacota html2apk, a interface e dependencias Node/Cordova. JDK e Android SDK ainda precisam existir na maquina por tamanho e licenca.</p>
|
|
347
360
|
</article>
|
|
361
|
+
<article class="creator-card">
|
|
362
|
+
<strong data-i18n="helpCreatorTitle">Criador</strong>
|
|
363
|
+
<p data-i18n="helpCreatorText">Conheca o Dev Caio Multiversando, criador desta linda aplicacao html2apk.</p>
|
|
364
|
+
<button id="devInstagramButton" class="secondary-action instagram-action" type="button" data-i18n="openInstagram">Conhecer o dev</button>
|
|
365
|
+
</article>
|
|
348
366
|
</div>
|
|
349
367
|
</section>
|
|
350
368
|
</main>
|