html2apk 0.8.0 → 0.9.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 +125 -1
- package/package.json +1 -1
- package/src/desktop/main.js +376 -0
- package/src/desktop/preload.js +1 -0
- package/src/desktop/renderer/index.html +11 -5
- package/src/desktop/renderer/renderer.js +658 -35
- package/src/desktop/renderer/styles.css +186 -27
- package/src/templates/cordova-plugin-html2apk-bridge/plugin.xml +17 -0
- package/src/templates/cordova-plugin-html2apk-bridge/src/android/Html2ApkBridge.java +2055 -82
- package/src/templates/cordova-plugin-html2apk-bridge/src/android/xml/html2apk_file_paths.xml +6 -0
- package/src/templates/cordova-plugin-html2apk-bridge/www/html2apk-bridge.js +407 -2
- package/src/templates/html2apk-early-bridge.js +404 -2
- package/src/templates/html2apk-runtime-console.js +156 -4
package/README.md
CHANGED
|
@@ -297,6 +297,12 @@ Prioridade de configuracao:
|
|
|
297
297
|
2. `app.json` ou `config.json`.
|
|
298
298
|
3. Valores padrao do html2apk.
|
|
299
299
|
|
|
300
|
+
### Modo Flutuante E Sobreposicao
|
|
301
|
+
|
|
302
|
+
O icone flutuante usa a permissao especial `SYSTEM_ALERT_WINDOW`. A bridge nativa declara essa permissao no APK para que o Android liste o app na tela de `Aparecer sobre outros apps`.
|
|
303
|
+
|
|
304
|
+
Mesmo com a permissao declarada no manifesto, o usuario ainda precisa liberar manualmente nas configuracoes do Android. Depois de liberar, volte ao app e chame `iniciarIconeFlutuante()` novamente.
|
|
305
|
+
|
|
300
306
|
### Tema Automatico Do APK
|
|
301
307
|
|
|
302
308
|
Use `themeMode: "auto"` ou `theme: "auto"` para o APK adaptar as barras nativas do Android a cor que esta visivel na tela. O html2apk observa a tela do WebView e ajusta status bar/navigation bar em tempo real.
|
|
@@ -406,9 +412,23 @@ Exemplos de aliases:
|
|
|
406
412
|
| `pararMic()` | `stopMic()` |
|
|
407
413
|
| `lanterna()` | `flashlight()` |
|
|
408
414
|
| `alternarLanterna()` | `toggleFlashlight()` |
|
|
415
|
+
| `tirarFoto()` | `takePhoto()` / `capturePhoto()` |
|
|
416
|
+
| `capturarVideo()` | `captureVideo()` |
|
|
417
|
+
| `escanearQRCode()` | `scanQRCode()` |
|
|
409
418
|
| `escolherArquivo()` | `pickFile()` |
|
|
410
419
|
| `escolherImagem()` | `pickImage()` |
|
|
411
420
|
| `salvarArquivo()` | `saveFile()` |
|
|
421
|
+
| `lerArquivo()` | `readFile()` |
|
|
422
|
+
| `listarArquivos()` | `listFiles()` |
|
|
423
|
+
| `excluirArquivo()` | `deleteFile()` |
|
|
424
|
+
| `abrirArquivo()` | `openFile()` |
|
|
425
|
+
| `compartilharArquivo()` | `shareFile()` |
|
|
426
|
+
| `baixarArquivo()` | `downloadFile()` |
|
|
427
|
+
| `baixarBase64()` | `downloadBase64()` |
|
|
428
|
+
| `baixarArquivoLocal()` | `downloadLocalFile()` / `downloadFromFile()` |
|
|
429
|
+
| `definirPapelParede()` | `setWallpaper()` |
|
|
430
|
+
| `infoPapelParede()` | `wallpaperInfo()` |
|
|
431
|
+
| `abrirConfiguracaoPapelParede()` | `openWallpaperSettings()` |
|
|
412
432
|
| `compartilhar()` | `share()` |
|
|
413
433
|
| `copiarTexto()` | `copyText()` |
|
|
414
434
|
| `lerTextoCopiado()` | `readText()` |
|
|
@@ -422,11 +442,18 @@ Exemplos de aliases:
|
|
|
422
442
|
| `infoArmazenamento()` | `storageInfo()` |
|
|
423
443
|
| `infoDesempenho()` | `performanceInfo()` |
|
|
424
444
|
| `appsAbertos()` | `openAppsMemory()` |
|
|
445
|
+
| `obterLocalizacao()` | `getLocation()` |
|
|
446
|
+
| `acompanharLocalizacao()` | `watchLocation()` |
|
|
447
|
+
| `pararLocalizacao()` | `stopLocationWatch()` |
|
|
448
|
+
| `autenticarBiometria()` | `authenticateBiometric()` |
|
|
449
|
+
| `salvarSeguro()` | `saveSecure()` |
|
|
450
|
+
| `lerSeguro()` | `readSecure()` |
|
|
451
|
+
| `removerSeguro()` | `deleteSecure()` |
|
|
425
452
|
| `aoEvento()` | `onEvent()` |
|
|
426
453
|
| `aoMinimizar()` | `onMinimize()` |
|
|
427
454
|
| `obterLinkInicial()` | `getInitialLink()` |
|
|
428
455
|
|
|
429
|
-
Os eventos tambem aceitam aliases em ingles em `onEvent()`: `app:ready`, `app:background`, `app:resumed`, `back:button`, `link:opened`, `network:changed`, `battery:changed`, `notification:received` e `notification:clicked`.
|
|
456
|
+
Os eventos tambem aceitam aliases em ingles em `onEvent()`: `app:ready`, `app:background`, `app:resumed`, `back:button`, `link:opened`, `network:changed`, `battery:changed`, `location:changed`, `notification:received` e `notification:clicked`.
|
|
430
457
|
|
|
431
458
|
Como tratar retornos:
|
|
432
459
|
|
|
@@ -623,6 +650,103 @@ await salvarArquivo({
|
|
|
623
650
|
await compartilhar({ texto: "Veja isso", url: "https://exemplo.com" });
|
|
624
651
|
```
|
|
625
652
|
|
|
653
|
+
`salvarArquivo()` tem dois modos:
|
|
654
|
+
|
|
655
|
+
- `salvarArquivo({ nome, conteudo })` abre o seletor nativo para o usuario escolher onde salvar, como ja acontecia.
|
|
656
|
+
- `salvarArquivo("nomeArquivo", minhaVariavel)` salva direto no armazenamento app-scoped do APK. Use esse formato para CRUD interno.
|
|
657
|
+
|
|
658
|
+
CRUD de arquivos internos:
|
|
659
|
+
|
|
660
|
+
```js
|
|
661
|
+
await salvarArquivo("perfil.json", {
|
|
662
|
+
nome: "Ana",
|
|
663
|
+
plano: "premium"
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
const perfil = await lerArquivo("perfil.json");
|
|
667
|
+
console.log(perfil.nome);
|
|
668
|
+
|
|
669
|
+
const completo = await lerArquivoCompleto("perfil.json");
|
|
670
|
+
console.log(completo.uri, completo.tamanho);
|
|
671
|
+
|
|
672
|
+
const existe = await arquivoExiste("perfil.json");
|
|
673
|
+
const arquivos = await listarArquivos();
|
|
674
|
+
|
|
675
|
+
await abrirArquivo("perfil.json");
|
|
676
|
+
await compartilharArquivo("perfil.json");
|
|
677
|
+
await excluirArquivo("perfil.json");
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
Para baixar um arquivo e guardar no mesmo armazenamento:
|
|
681
|
+
|
|
682
|
+
```js
|
|
683
|
+
await baixarArquivo("https://exemplo.com/relatorio.pdf", "relatorio.pdf");
|
|
684
|
+
await abrirArquivo("relatorio.pdf");
|
|
685
|
+
|
|
686
|
+
await baixarBase64("foto.png", base64DaImagem, {
|
|
687
|
+
mimeType: "image/png"
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
const arquivo = await escolherArquivo();
|
|
691
|
+
if (arquivo) {
|
|
692
|
+
await baixarArquivoLocal(arquivo, "copia-" + arquivo.name);
|
|
693
|
+
}
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
Durante `baixarArquivo()`, `baixarBase64()` e `baixarArquivoLocal()`, o Android mostra uma notificacao de progresso quando a permissao `POST_NOTIFICATIONS` estiver liberada. No Android 13+, o html2apk pede essa permissao automaticamente; se o usuario negar, o download continua e o retorno vem com `notificationShown: false`.
|
|
697
|
+
|
|
698
|
+
Papel de parede:
|
|
699
|
+
|
|
700
|
+
```js
|
|
701
|
+
const foto = await tirarFoto({ base64: true });
|
|
702
|
+
|
|
703
|
+
await salvarArquivo("wallpaper.jpg", foto.base64, {
|
|
704
|
+
base64: true,
|
|
705
|
+
mimeType: "image/jpeg"
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
const resultado = await definirPapelParede("wallpaper.jpg", {
|
|
709
|
+
alvo: "inicio" // "inicio", "bloqueio" ou "ambos"
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
console.log(resultado.applied, resultado.systemApplied, resultado.lockApplied);
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
`definirPapelParede()` usa a API publica `WallpaperManager` do Android para imagem estatica. A entrada pode ser nome de arquivo salvo pelo app, `content://`/`file://`, data URL (`data:image/...;base64,...`) ou objeto `{ base64, mimeType }`. Video wallpaper e fundo de chamadas dependem do fluxo do sistema, live wallpaper ou app de telefone do fabricante; nesses casos use `infoPapelParede()` e `abrirConfiguracaoPapelParede()`.
|
|
716
|
+
|
|
717
|
+
Camera, QR Code, localizacao, biometria e storage seguro:
|
|
718
|
+
|
|
719
|
+
```js
|
|
720
|
+
const foto = await tirarFoto({ base64: true });
|
|
721
|
+
|
|
722
|
+
const qr = await escanearQRCode();
|
|
723
|
+
if (qr) {
|
|
724
|
+
console.log(qr.text);
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
const local = await obterLocalizacao({ altaPrecisao: true, timeoutMs: 10000 });
|
|
728
|
+
console.log(local.latitude, local.longitude);
|
|
729
|
+
|
|
730
|
+
const watch = await acompanharLocalizacao({ intervaloMs: 5000 });
|
|
731
|
+
const pararEvento = aoMudarLocalizacao((evento) => {
|
|
732
|
+
console.log(evento.latitude, evento.longitude);
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
await pararLocalizacao(watch.watchId);
|
|
736
|
+
pararEvento();
|
|
737
|
+
|
|
738
|
+
const bio = await autenticarBiometria({
|
|
739
|
+
titulo: "Confirmar acesso",
|
|
740
|
+
descricao: "Use a biometria do aparelho"
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
if (bio.authenticated) {
|
|
744
|
+
await salvarSeguro("token", "abc123");
|
|
745
|
+
const token = await lerSeguro("token");
|
|
746
|
+
await removerSeguro("token");
|
|
747
|
+
}
|
|
748
|
+
```
|
|
749
|
+
|
|
626
750
|
O retorno de arquivos tem este formato:
|
|
627
751
|
|
|
628
752
|
```json
|
package/package.json
CHANGED
package/src/desktop/main.js
CHANGED
|
@@ -395,6 +395,339 @@ function cleanBuildOptions(options = {}) {
|
|
|
395
395
|
return output;
|
|
396
396
|
}
|
|
397
397
|
|
|
398
|
+
function nativeFunctionLabHtml() {
|
|
399
|
+
return `<!doctype html>
|
|
400
|
+
<html lang="pt-BR">
|
|
401
|
+
<head>
|
|
402
|
+
<meta charset="utf-8">
|
|
403
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
404
|
+
<title>html2apk - Teste de funcoes</title>
|
|
405
|
+
<style>
|
|
406
|
+
:root {
|
|
407
|
+
color-scheme: light dark;
|
|
408
|
+
--bg: #f6f8fb;
|
|
409
|
+
--panel: #ffffff;
|
|
410
|
+
--text: #172033;
|
|
411
|
+
--muted: #65758b;
|
|
412
|
+
--line: #dbe3ee;
|
|
413
|
+
--blue: #126fff;
|
|
414
|
+
--green: #18864b;
|
|
415
|
+
--red: #c33b3b;
|
|
416
|
+
--code: #0c1117;
|
|
417
|
+
}
|
|
418
|
+
* { box-sizing: border-box; }
|
|
419
|
+
body {
|
|
420
|
+
margin: 0;
|
|
421
|
+
font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
422
|
+
background: var(--bg);
|
|
423
|
+
color: var(--text);
|
|
424
|
+
}
|
|
425
|
+
header {
|
|
426
|
+
position: sticky;
|
|
427
|
+
top: 0;
|
|
428
|
+
z-index: 2;
|
|
429
|
+
background: color-mix(in srgb, var(--panel) 92%, transparent);
|
|
430
|
+
border-bottom: 1px solid var(--line);
|
|
431
|
+
padding: 14px 16px;
|
|
432
|
+
backdrop-filter: blur(14px);
|
|
433
|
+
}
|
|
434
|
+
h1 { margin: 0; font-size: 1.2rem; }
|
|
435
|
+
header p { margin: 6px 0 0; color: var(--muted); line-height: 1.35; }
|
|
436
|
+
main { display: grid; gap: 14px; padding: 14px; }
|
|
437
|
+
section {
|
|
438
|
+
border: 1px solid var(--line);
|
|
439
|
+
border-radius: 8px;
|
|
440
|
+
background: var(--panel);
|
|
441
|
+
overflow: hidden;
|
|
442
|
+
}
|
|
443
|
+
h2 {
|
|
444
|
+
margin: 0;
|
|
445
|
+
padding: 13px 14px;
|
|
446
|
+
font-size: .98rem;
|
|
447
|
+
border-bottom: 1px solid var(--line);
|
|
448
|
+
}
|
|
449
|
+
.grid {
|
|
450
|
+
display: grid;
|
|
451
|
+
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
|
452
|
+
gap: 10px;
|
|
453
|
+
padding: 12px;
|
|
454
|
+
}
|
|
455
|
+
button {
|
|
456
|
+
min-height: 48px;
|
|
457
|
+
border: 1px solid var(--line);
|
|
458
|
+
border-radius: 8px;
|
|
459
|
+
background: #eef5ff;
|
|
460
|
+
color: var(--blue);
|
|
461
|
+
padding: 10px;
|
|
462
|
+
font-weight: 800;
|
|
463
|
+
text-align: left;
|
|
464
|
+
}
|
|
465
|
+
button:active { transform: translateY(1px); }
|
|
466
|
+
button small { display: block; margin-top: 4px; color: var(--muted); font-weight: 600; line-height: 1.3; }
|
|
467
|
+
.danger { background: #fff0f0; color: var(--red); }
|
|
468
|
+
.notice {
|
|
469
|
+
margin: 12px;
|
|
470
|
+
padding: 10px 12px;
|
|
471
|
+
border-radius: 8px;
|
|
472
|
+
background: #fff8df;
|
|
473
|
+
color: #745400;
|
|
474
|
+
line-height: 1.42;
|
|
475
|
+
}
|
|
476
|
+
@media (prefers-color-scheme: dark) {
|
|
477
|
+
:root {
|
|
478
|
+
--bg: #10141b;
|
|
479
|
+
--panel: #151b24;
|
|
480
|
+
--text: #e6edf7;
|
|
481
|
+
--muted: #9aa8ba;
|
|
482
|
+
--line: #283243;
|
|
483
|
+
}
|
|
484
|
+
button { background: #172642; }
|
|
485
|
+
.danger { background: #351c20; }
|
|
486
|
+
.notice { background: #2d2816; color: #f2cc60; }
|
|
487
|
+
}
|
|
488
|
+
</style>
|
|
489
|
+
</head>
|
|
490
|
+
<body>
|
|
491
|
+
<header>
|
|
492
|
+
<h1>Teste de funcoes html2apk</h1>
|
|
493
|
+
<p>Toque nos botoes para chamar as funcoes interpretadas do APK. Algumas abrem permissoes, camera, seletor de arquivo ou outro app Android.</p>
|
|
494
|
+
</header>
|
|
495
|
+
<main id="groups"></main>
|
|
496
|
+
<script>
|
|
497
|
+
(function () {
|
|
498
|
+
var state = {
|
|
499
|
+
scheduledId: null,
|
|
500
|
+
loopId: null,
|
|
501
|
+
watchId: null,
|
|
502
|
+
stopLocationEvent: null,
|
|
503
|
+
lastImage: null,
|
|
504
|
+
lastPhoto: null,
|
|
505
|
+
micRecording: false,
|
|
506
|
+
eventsReady: false
|
|
507
|
+
};
|
|
508
|
+
var sampleImageBase64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+/p9sAAAAASUVORK5CYII=";
|
|
509
|
+
|
|
510
|
+
function fn(name) {
|
|
511
|
+
if (typeof window[name] !== "function") {
|
|
512
|
+
throw new Error(name + " nao esta disponivel neste APK.");
|
|
513
|
+
}
|
|
514
|
+
return window[name];
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
function log(title, value, kind) {
|
|
518
|
+
var consoleKind = kind === "err" ? "error" : (kind === "ok" ? "ok" : "info");
|
|
519
|
+
var hasValue = value !== "" && typeof value !== "undefined";
|
|
520
|
+
if (window.Html2ApkRuntimeConsole && typeof window.Html2ApkRuntimeConsole.log === "function") {
|
|
521
|
+
window.Html2ApkRuntimeConsole.log(consoleKind, title, hasValue ? value : undefined);
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
if (consoleKind === "error") {
|
|
525
|
+
console.error(title, hasValue ? value : "");
|
|
526
|
+
} else if (consoleKind === "ok") {
|
|
527
|
+
console.log(title, hasValue ? value : "");
|
|
528
|
+
} else {
|
|
529
|
+
console.info(title, hasValue ? value : "");
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
async function run(id) {
|
|
534
|
+
var test = actions[id];
|
|
535
|
+
if (!test) {
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
log("Executando " + test.title, "", "");
|
|
539
|
+
try {
|
|
540
|
+
var result = await test.run();
|
|
541
|
+
log("OK: " + test.title, result, "ok");
|
|
542
|
+
} catch (error) {
|
|
543
|
+
log("ERRO: " + test.title, error, "err");
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
function registerEvents() {
|
|
548
|
+
if (state.eventsReady) {
|
|
549
|
+
return { registered: true, already: true };
|
|
550
|
+
}
|
|
551
|
+
state.eventsReady = true;
|
|
552
|
+
fn("aoEvento")("app:background", function (event) { log("evento app:background", event, "ok"); });
|
|
553
|
+
fn("aoEvento")("app:voltou", function (event) { log("evento app:voltou", event, "ok"); });
|
|
554
|
+
fn("aoEvento")("botao:voltar", function (event) { log("evento botao:voltar", event, "ok"); });
|
|
555
|
+
fn("aoEvento")("rede:mudou", function (event) { log("evento rede:mudou", event, "ok"); });
|
|
556
|
+
fn("aoEvento")("bateria:mudou", function (event) { log("evento bateria:mudou", event, "ok"); });
|
|
557
|
+
fn("aoClicarNotificacao")(function (event) { log("notificacao clicada", event, "ok"); });
|
|
558
|
+
return { registered: true };
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
var actions = {
|
|
562
|
+
toast: { title: "toast()", run: function () { return fn("toast")("Mensagem do app de teste"); } },
|
|
563
|
+
vibrar: { title: "vibrar()", run: function () { return fn("vibrar")(250); } },
|
|
564
|
+
copiarTexto: { title: "copiarTexto()", run: function () { return fn("copiarTexto")("Texto copiado pelo html2apk"); } },
|
|
565
|
+
lerTextoCopiado: { title: "lerTextoCopiado()", run: function () { return fn("lerTextoCopiado")(); } },
|
|
566
|
+
compartilharTexto: { title: "compartilharTexto()", run: function () { return fn("compartilharTexto")("Compartilhado pelo app de teste html2apk"); } },
|
|
567
|
+
compartilhar: { title: "compartilhar()", run: function () { return fn("compartilhar")({ texto: "Teste html2apk", url: "https://example.com" }); } },
|
|
568
|
+
|
|
569
|
+
notificar: { title: "notificar()", run: function () { return fn("notificar")({ titulo: "html2apk", texto: "Notificacao imediata", aoClicar: { funcao: "toast", argumentos: ["Notificacao clicada"] } }); } },
|
|
570
|
+
agendarNotificacao: { title: "agendarNotificacao()", run: async function () { var result = await fn("agendarNotificacao")({ titulo: "html2apk", texto: "Agendada para 10 segundos", quando: Date.now() + 10000 }); state.scheduledId = result && result.id; return result; } },
|
|
571
|
+
cancelarNotificacao: { title: "cancelarNotificacao()", run: function () { return fn("cancelarNotificacao")(state.scheduledId || 0); } },
|
|
572
|
+
agendarLoopNotificacoes: { title: "agendarLoopNotificacoes()", run: async function () { var result = await fn("agendarLoopNotificacoes")({ aCada: "30s", notificacoes: [{ titulo: "Loop 1", texto: "Primeiro item" }, { titulo: "Loop 2", texto: "Segundo item" }] }); state.loopId = result && result.id; return result; } },
|
|
573
|
+
cancelarLoopNotificacoes: { title: "cancelarLoopNotificacoes()", run: function () { return fn("cancelarLoopNotificacoes")(state.loopId || 0); } },
|
|
574
|
+
pushInfo: { title: "Push OneSignal", run: function () { if (typeof window.solicitarPermissaoPush !== "function") { return { available: false, message: "Configure OneSignal App ID no app real para testar push remoto." }; } return window.solicitarPermissaoPush(); } },
|
|
575
|
+
|
|
576
|
+
statusPermissoes: { title: "statusPermissoes()", run: function () { return fn("statusPermissoes")(["CAMERA", "RECORD_AUDIO", "ACCESS_FINE_LOCATION", "POST_NOTIFICATIONS", "SET_WALLPAPER"]); } },
|
|
577
|
+
permissaoNotificacao: { title: "solicitarPermissaoNotificacoes()", run: function () { return fn("solicitarPermissaoNotificacoes")(); } },
|
|
578
|
+
permissaoCamera: { title: "solicitarPermissaoCamera()", run: function () { return fn("solicitarPermissaoCamera")(); } },
|
|
579
|
+
permissaoMicrofone: { title: "solicitarPermissaoMicrofone()", run: function () { return fn("solicitarPermissaoMicrofone")(); } },
|
|
580
|
+
alarmeExato: { title: "podeAgendarNotificacaoExata()", run: function () { return fn("podeAgendarNotificacaoExata")(); } },
|
|
581
|
+
abrirAlarmeExato: { title: "abrirConfiguracaoAlarmeExato()", run: function () { return fn("abrirConfiguracaoAlarmeExato")(); } },
|
|
582
|
+
statusSobreposicao: { title: "statusPermissaoSobreposicao()", run: function () { return fn("statusPermissaoSobreposicao")(); } },
|
|
583
|
+
abrirSobreposicao: { title: "abrirConfiguracaoSobreposicao()", run: function () { return fn("abrirConfiguracaoSobreposicao")(); } },
|
|
584
|
+
|
|
585
|
+
fullscreenOn: { title: "fullscreen(true)", run: function () { return fn("fullscreen")(true); } },
|
|
586
|
+
fullscreenOff: { title: "fullscreen(false)", run: function () { return fn("fullscreen")(false); } },
|
|
587
|
+
telaAcordadaOn: { title: "manterTelaAcordada(true)", run: function () { return fn("manterTelaAcordada")(true); } },
|
|
588
|
+
telaAcordadaOff: { title: "manterTelaAcordada(false)", run: function () { return fn("manterTelaAcordada")(false); } },
|
|
589
|
+
brilhoTela: { title: "brilhoTela()", run: function () { return fn("brilhoTela")(0.72); } },
|
|
590
|
+
corTema: { title: "definirCorTema()", run: function () { return fn("definirCorTema")({ statusBarColor: "#126fff", navigationBarColor: "#10141b", darkIcons: false }); } },
|
|
591
|
+
lanterna: { title: "alternarLanterna()", run: function () { return fn("alternarLanterna")(); } },
|
|
592
|
+
statusLanterna: { title: "statusLanterna()", run: function () { return fn("statusLanterna")(); } },
|
|
593
|
+
iniciarIconeFlutuante: { title: "iniciarIconeFlutuante()", run: function () { return fn("iniciarIconeFlutuante")(); } },
|
|
594
|
+
pararIconeFlutuante: { title: "pararIconeFlutuante()", run: function () { return fn("pararIconeFlutuante")(); } },
|
|
595
|
+
|
|
596
|
+
tirarFoto: { title: "tirarFoto()", run: async function () { var result = await fn("tirarFoto")({ base64: true }); state.lastPhoto = result; return result; } },
|
|
597
|
+
capturarVideo: { title: "capturarVideo()", run: function () { return fn("capturarVideo")({ duracaoSegundos: 5 }); } },
|
|
598
|
+
escanearQRCode: { title: "escanearQRCode()", run: function () { return fn("escanearQRCode")(); } },
|
|
599
|
+
ouvirMic: { title: "ouvirMic()", run: async function () { state.micRecording = true; return fn("ouvirMic")(); } },
|
|
600
|
+
pararMic: { title: "pararMic()", run: async function () { state.micRecording = false; return fn("pararMic")(); } },
|
|
601
|
+
|
|
602
|
+
escolherImagem: { title: "escolherImagem()", run: async function () { var result = await fn("escolherImagem")(); state.lastImage = result; return result; } },
|
|
603
|
+
escolherImagens: { title: "escolherImagens()", run: function () { return fn("escolherImagens")({ multiplo: true }); } },
|
|
604
|
+
escolherArquivo: { title: "escolherArquivo()", run: function () { return fn("escolherArquivo")({ tipos: ["text/*", "application/json", "image/*"] }); } },
|
|
605
|
+
escolherArquivos: { title: "escolherArquivos()", run: function () { return fn("escolherArquivos")({ multiplo: true }); } },
|
|
606
|
+
escolherVideo: { title: "escolherVideo()", run: function () { return fn("escolherVideo")(); } },
|
|
607
|
+
escolherPasta: { title: "escolherPasta()", run: function () { return fn("escolherPasta")(); } },
|
|
608
|
+
salvarArquivoPicker: { title: "salvarArquivo({ nome })", run: function () { return fn("salvarArquivo")({ nome: "teste-html2apk.txt", mimeType: "text/plain", conteudo: "Arquivo salvo pelo teste html2apk" }); } },
|
|
609
|
+
salvarArquivoCrud: { title: "salvarArquivo('lab.json')", run: function () { return fn("salvarArquivo")("lab.json", { criadoEm: Date.now(), origem: "teste-funcoes" }); } },
|
|
610
|
+
lerArquivo: { title: "lerArquivo()", run: function () { return fn("lerArquivo")("lab.json"); } },
|
|
611
|
+
lerArquivoCompleto: { title: "lerArquivoCompleto()", run: function () { return fn("lerArquivoCompleto")("lab.json"); } },
|
|
612
|
+
listarArquivos: { title: "listarArquivos()", run: function () { return fn("listarArquivos")(); } },
|
|
613
|
+
infoArquivo: { title: "infoArquivo()", run: function () { return fn("infoArquivo")("lab.json"); } },
|
|
614
|
+
arquivoExiste: { title: "arquivoExiste()", run: function () { return fn("arquivoExiste")("lab.json"); } },
|
|
615
|
+
abrirArquivo: { title: "abrirArquivo()", run: function () { return fn("abrirArquivo")("lab.json"); } },
|
|
616
|
+
compartilharArquivo: { title: "compartilharArquivo()", run: function () { return fn("compartilharArquivo")("lab.json"); } },
|
|
617
|
+
baixarArquivo: { title: "baixarArquivo()", run: function () { return fn("baixarArquivo")("https://example.com/", "example.html"); } },
|
|
618
|
+
baixarBase64: { title: "baixarBase64()", run: function () { return fn("baixarBase64")("pixel-download.png", sampleImageBase64, { mimeType: "image/png" }); } },
|
|
619
|
+
baixarArquivoLocal: { title: "baixarArquivoLocal()", run: async function () { var file = await fn("escolherArquivo")(); if (!file) { return { canceled: true }; } return fn("baixarArquivoLocal")(file, "copia-" + (file.name || file.nome || "arquivo")); } },
|
|
620
|
+
excluirArquivo: { title: "excluirArquivo()", run: function () { return fn("excluirArquivo")("lab.json"); } },
|
|
621
|
+
|
|
622
|
+
abrirNoApp: { title: "abrirNoApp()", run: function () { return fn("abrirNoApp")("#teste-funcoes"); } },
|
|
623
|
+
abrirForaDoApp: { title: "abrirForaDoApp()", run: function () { return fn("abrirForaDoApp")("https://example.com"); } },
|
|
624
|
+
abrirUrl: { title: "abrirUrl()", run: function () { return fn("abrirUrl")("https://example.com"); } },
|
|
625
|
+
discar: { title: "discar()", run: function () { return fn("discar")("11999999999"); } },
|
|
626
|
+
abrirMapa: { title: "abrirMapa()", run: function () { return fn("abrirMapa")("Sao Paulo"); } },
|
|
627
|
+
abrirWhatsapp: { title: "abrirWhatsapp()", run: function () { return fn("abrirWhatsapp")("5511999999999", "Teste html2apk"); } },
|
|
628
|
+
|
|
629
|
+
infoDispositivo: { title: "infoDispositivo()", run: function () { return fn("infoDispositivo")(); } },
|
|
630
|
+
infoRede: { title: "infoRede()", run: function () { return fn("infoRede")(); } },
|
|
631
|
+
infoBateria: { title: "infoBateria()", run: function () { return fn("infoBateria")(); } },
|
|
632
|
+
infoMemoria: { title: "infoMemoria()", run: function () { return fn("infoMemoria")(); } },
|
|
633
|
+
infoArmazenamento: { title: "infoArmazenamento()", run: function () { return fn("infoArmazenamento")(); } },
|
|
634
|
+
infoDesempenho: { title: "infoDesempenho()", run: function () { return fn("infoDesempenho")(); } },
|
|
635
|
+
appsAbertos: { title: "appsAbertos()", run: function () { return fn("appsAbertos")(); } },
|
|
636
|
+
|
|
637
|
+
obterLocalizacao: { title: "obterLocalizacao()", run: function () { return fn("obterLocalizacao")({ altaPrecisao: true, timeoutMs: 10000 }); } },
|
|
638
|
+
acompanharLocalizacao: { title: "acompanharLocalizacao()", run: async function () { var result = await fn("acompanharLocalizacao")({ intervaloMs: 5000 }); state.watchId = result && result.watchId; return result; } },
|
|
639
|
+
pararLocalizacao: { title: "pararLocalizacao()", run: function () { return fn("pararLocalizacao")(state.watchId || ""); } },
|
|
640
|
+
aoMudarLocalizacao: { title: "aoMudarLocalizacao()", run: function () { if (state.stopLocationEvent) { state.stopLocationEvent(); } state.stopLocationEvent = fn("aoMudarLocalizacao")(function (event) { log("localizacao:mudou", event, "ok"); }); return { listening: true }; } },
|
|
641
|
+
autenticarBiometria: { title: "autenticarBiometria()", run: function () { return fn("autenticarBiometria")({ titulo: "Teste html2apk", descricao: "Confirme para testar a bridge" }); } },
|
|
642
|
+
salvarSeguro: { title: "salvarSeguro()", run: function () { return fn("salvarSeguro")("tokenTeste", { token: "abc123", criadoEm: Date.now() }); } },
|
|
643
|
+
lerSeguro: { title: "lerSeguro()", run: function () { return fn("lerSeguro")("tokenTeste"); } },
|
|
644
|
+
lerSeguroCompleto: { title: "lerSeguroCompleto()", run: function () { return fn("lerSeguroCompleto")("tokenTeste"); } },
|
|
645
|
+
listarSeguro: { title: "listarSeguro()", run: function () { return fn("listarSeguro")(); } },
|
|
646
|
+
removerSeguro: { title: "removerSeguro()", run: function () { return fn("removerSeguro")("tokenTeste"); } },
|
|
647
|
+
limparSeguro: { title: "limparSeguro()", run: function () { return fn("limparSeguro")(); } },
|
|
648
|
+
|
|
649
|
+
infoPapelParede: { title: "infoPapelParede()", run: function () { return fn("infoPapelParede")(); } },
|
|
650
|
+
definirPapelParede: { title: "definirPapelParede()", run: function () { return fn("definirPapelParede")({ base64: sampleImageBase64, mimeType: "image/png", alvo: "inicio" }); } },
|
|
651
|
+
abrirConfigPapel: { title: "abrirConfiguracaoPapelParede()", run: function () { return fn("abrirConfiguracaoPapelParede")(); } },
|
|
652
|
+
definirImagemEscolhida: { title: "imagem escolhida -> papel de parede", run: async function () { if (!state.lastImage) { state.lastImage = await fn("escolherImagem")(); } if (!state.lastImage || !state.lastImage.uri) { return { canceled: true }; } return fn("definirPapelParede")({ uri: state.lastImage.uri, alvo: "inicio", mimeType: state.lastImage.mimeType || "image/*" }); } },
|
|
653
|
+
|
|
654
|
+
registrarEventos: { title: "registrar eventos", run: function () { return registerEvents(); } },
|
|
655
|
+
obterNotificacaoInicial: { title: "obterNotificacaoInicial()", run: function () { return fn("obterNotificacaoInicial")(); } },
|
|
656
|
+
obterLinkInicial: { title: "obterLinkInicial()", run: function () { return fn("obterLinkInicial")(); } }
|
|
657
|
+
};
|
|
658
|
+
|
|
659
|
+
var groups = [
|
|
660
|
+
{ title: "Feedback e compartilhamento", ids: ["toast", "vibrar", "copiarTexto", "lerTextoCopiado", "compartilharTexto", "compartilhar"] },
|
|
661
|
+
{ title: "Notificacoes", ids: ["notificar", "agendarNotificacao", "cancelarNotificacao", "agendarLoopNotificacoes", "cancelarLoopNotificacoes", "pushInfo"] },
|
|
662
|
+
{ title: "Permissoes e configuracoes", ids: ["statusPermissoes", "permissaoNotificacao", "permissaoCamera", "permissaoMicrofone", "alarmeExato", "abrirAlarmeExato", "statusSobreposicao", "abrirSobreposicao"] },
|
|
663
|
+
{ title: "Tela e hardware", ids: ["fullscreenOn", "fullscreenOff", "telaAcordadaOn", "telaAcordadaOff", "brilhoTela", "corTema", "lanterna", "statusLanterna", "iniciarIconeFlutuante", "pararIconeFlutuante"] },
|
|
664
|
+
{ title: "Camera, QR Code e microfone", ids: ["tirarFoto", "capturarVideo", "escanearQRCode", "ouvirMic", "pararMic"] },
|
|
665
|
+
{ title: "Arquivos e midia", ids: ["escolherImagem", "escolherImagens", "escolherArquivo", "escolherArquivos", "escolherVideo", "escolherPasta", "salvarArquivoPicker", "salvarArquivoCrud", "lerArquivo", "lerArquivoCompleto", "listarArquivos", "infoArquivo", "arquivoExiste", "abrirArquivo", "compartilharArquivo", "baixarArquivo", "baixarBase64", "baixarArquivoLocal", "excluirArquivo"] },
|
|
666
|
+
{ title: "Abrir apps externos", ids: ["abrirNoApp", "abrirForaDoApp", "abrirUrl", "discar", "abrirMapa", "abrirWhatsapp"] },
|
|
667
|
+
{ title: "Diagnostico", ids: ["infoDispositivo", "infoRede", "infoBateria", "infoMemoria", "infoArmazenamento", "infoDesempenho", "appsAbertos"] },
|
|
668
|
+
{ title: "Localizacao e seguranca", ids: ["obterLocalizacao", "acompanharLocalizacao", "pararLocalizacao", "aoMudarLocalizacao", "autenticarBiometria", "salvarSeguro", "lerSeguro", "lerSeguroCompleto", "listarSeguro", "removerSeguro", "limparSeguro"] },
|
|
669
|
+
{ title: "Papel de parede", ids: ["infoPapelParede", "definirPapelParede", "abrirConfigPapel", "definirImagemEscolhida"] },
|
|
670
|
+
{ title: "Eventos", ids: ["registrarEventos", "obterNotificacaoInicial", "obterLinkInicial"] }
|
|
671
|
+
];
|
|
672
|
+
|
|
673
|
+
function render() {
|
|
674
|
+
var root = document.getElementById("groups");
|
|
675
|
+
root.innerHTML = groups.map(function (group) {
|
|
676
|
+
return "<section><h2>" + group.title + "</h2><div class='grid'>" + group.ids.map(function (id) {
|
|
677
|
+
var item = actions[id];
|
|
678
|
+
return "<button type='button' data-action='" + id + "'>" + item.title + "<small>Toque para executar</small></button>";
|
|
679
|
+
}).join("") + "</div></section>";
|
|
680
|
+
}).join("") + "<p class='notice'>Video wallpaper, push remoto e fundo de chamadas dependem de configuracao do sistema, OneSignal ou fabricante. O app mostra o retorno real quando a funcao nao puder agir direto.</p>";
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
document.addEventListener("click", function (event) {
|
|
684
|
+
var button = event.target.closest("[data-action]");
|
|
685
|
+
if (button) {
|
|
686
|
+
run(button.getAttribute("data-action"));
|
|
687
|
+
}
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
document.addEventListener("deviceready", function () {
|
|
691
|
+
log("deviceready", { ready: true }, "ok");
|
|
692
|
+
}, false);
|
|
693
|
+
|
|
694
|
+
render();
|
|
695
|
+
}());
|
|
696
|
+
</script>
|
|
697
|
+
</body>
|
|
698
|
+
</html>`;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
async function createNativeFunctionLabProject() {
|
|
702
|
+
const projectRoot = await fs.mkdtemp(path.join(app.getPath("temp"), "html2apk-function-lab-"));
|
|
703
|
+
const config = {
|
|
704
|
+
appName: "html2apk Function Lab",
|
|
705
|
+
packageId: "dev.html2apk.functionlab",
|
|
706
|
+
version: "1.0.0",
|
|
707
|
+
buildFormat: "apk",
|
|
708
|
+
mode: "standalone",
|
|
709
|
+
orientation: "vertical",
|
|
710
|
+
minSdkVersion: 24,
|
|
711
|
+
themeMode: "fixed",
|
|
712
|
+
themeColor: "#126fff",
|
|
713
|
+
showRuntimeLogs: true,
|
|
714
|
+
permissions: [
|
|
715
|
+
"INTERNET",
|
|
716
|
+
"POST_NOTIFICATIONS",
|
|
717
|
+
"VIBRATE",
|
|
718
|
+
"CAMERA",
|
|
719
|
+
"RECORD_AUDIO",
|
|
720
|
+
"ACCESS_FINE_LOCATION",
|
|
721
|
+
"ACCESS_COARSE_LOCATION",
|
|
722
|
+
"SET_WALLPAPER"
|
|
723
|
+
]
|
|
724
|
+
};
|
|
725
|
+
|
|
726
|
+
await fs.writeFile(path.join(projectRoot, "index.html"), nativeFunctionLabHtml(), "utf8");
|
|
727
|
+
await fs.writeFile(path.join(projectRoot, "app.json"), JSON.stringify(config, null, 2), "utf8");
|
|
728
|
+
return projectRoot;
|
|
729
|
+
}
|
|
730
|
+
|
|
398
731
|
function quoteForCmd(value) {
|
|
399
732
|
const text = String(value);
|
|
400
733
|
if (!text.length) {
|
|
@@ -837,6 +1170,49 @@ ipcMain.handle("build:run-usb-debug", async (event, options) => {
|
|
|
837
1170
|
}
|
|
838
1171
|
});
|
|
839
1172
|
|
|
1173
|
+
ipcMain.handle("codes:run-function-lab", async (event) => {
|
|
1174
|
+
const sendLog = (line, kind = "raw") => {
|
|
1175
|
+
event.sender.send("build:log", {
|
|
1176
|
+
line,
|
|
1177
|
+
kind,
|
|
1178
|
+
time: new Date().toISOString()
|
|
1179
|
+
});
|
|
1180
|
+
};
|
|
1181
|
+
let projectRoot = null;
|
|
1182
|
+
|
|
1183
|
+
try {
|
|
1184
|
+
sendLog("Creating html2apk interpreted functions test app.", "system");
|
|
1185
|
+
projectRoot = await createNativeFunctionLabProject();
|
|
1186
|
+
sendLog(`Function test project: ${projectRoot}`, "system");
|
|
1187
|
+
sendLog("USB required: connect an Android phone with USB debugging authorized.", "system");
|
|
1188
|
+
const result = await runDebugUsb({
|
|
1189
|
+
projectRoot,
|
|
1190
|
+
debug: false,
|
|
1191
|
+
release: false,
|
|
1192
|
+
showRuntimeLogs: true,
|
|
1193
|
+
onLog: (line) => sendLog(line)
|
|
1194
|
+
});
|
|
1195
|
+
sendLog(`Function test app opened on device: ${result.device?.id || "Android device"}`, "success");
|
|
1196
|
+
return {
|
|
1197
|
+
ok: true,
|
|
1198
|
+
result: {
|
|
1199
|
+
...result,
|
|
1200
|
+
projectRoot,
|
|
1201
|
+
distPath: path.join(projectRoot, "dist")
|
|
1202
|
+
}
|
|
1203
|
+
};
|
|
1204
|
+
} catch (error) {
|
|
1205
|
+
sendLog(error.message, "error");
|
|
1206
|
+
return {
|
|
1207
|
+
ok: false,
|
|
1208
|
+
message: error.message,
|
|
1209
|
+
logs: error.logs || [],
|
|
1210
|
+
buildDir: error.buildDir || null,
|
|
1211
|
+
projectRoot
|
|
1212
|
+
};
|
|
1213
|
+
}
|
|
1214
|
+
});
|
|
1215
|
+
|
|
840
1216
|
ipcMain.handle("shell:open-path", async (_event, targetPath) => {
|
|
841
1217
|
if (!targetPath) {
|
|
842
1218
|
return false;
|
package/src/desktop/preload.js
CHANGED
|
@@ -18,6 +18,7 @@ contextBridge.exposeInMainWorld("html2apkDesktop", {
|
|
|
18
18
|
installAndroidRequirements: () => ipcRenderer.invoke("install:android-requirements"),
|
|
19
19
|
runBuild: (options) => ipcRenderer.invoke("build:run", options),
|
|
20
20
|
runUsbDebugBuild: (options) => ipcRenderer.invoke("build:run-usb-debug", options),
|
|
21
|
+
runNativeFunctionLab: () => ipcRenderer.invoke("codes:run-function-lab"),
|
|
21
22
|
openPath: (targetPath) => ipcRenderer.invoke("shell:open-path", targetPath),
|
|
22
23
|
showItem: (targetPath) => ipcRenderer.invoke("shell:show-item", targetPath),
|
|
23
24
|
openExternalUrl: (targetUrl) => ipcRenderer.invoke("shell:open-external", targetUrl),
|
|
@@ -322,10 +322,9 @@
|
|
|
322
322
|
<strong id="currentFileName" data-i18n="noFileSelected">Nenhum arquivo selecionado</strong>
|
|
323
323
|
<span id="fileLanguageBadge">text</span>
|
|
324
324
|
</div>
|
|
325
|
-
<
|
|
326
|
-
|
|
327
|
-
<
|
|
328
|
-
<pre id="fileHighlight" class="syntax-preview"><code></code></pre>
|
|
325
|
+
<div class="code-editor-shell">
|
|
326
|
+
<pre id="fileHighlight" class="file-editor-highlight" aria-hidden="true"><code></code></pre>
|
|
327
|
+
<textarea id="fileEditorInput" spellcheck="false" disabled placeholder="index.html"></textarea>
|
|
329
328
|
</div>
|
|
330
329
|
</section>
|
|
331
330
|
</div>
|
|
@@ -404,9 +403,16 @@
|
|
|
404
403
|
<p class="eyebrow" data-i18n="codesEyebrow">Bridge nativa</p>
|
|
405
404
|
<h1 data-i18n="codesTitle">Codigos interpretados</h1>
|
|
406
405
|
</div>
|
|
406
|
+
<button id="nativeFunctionLabButton" class="primary-action" type="button" data-i18n="testNativeFunctions">Testar funcoes</button>
|
|
407
407
|
</header>
|
|
408
408
|
<p class="view-intro" data-i18n="codesIntro">Estas funcoes chamadas no JavaScript do app sao interpretadas pelo plugin Cordova e executadas no Java Android.</p>
|
|
409
|
-
<div
|
|
409
|
+
<div class="code-browser">
|
|
410
|
+
<nav id="nativeCodeCategories" class="code-categories" aria-label="Categorias de codigos"></nav>
|
|
411
|
+
<section class="code-results">
|
|
412
|
+
<div id="nativeCodeSummary" class="code-summary"></div>
|
|
413
|
+
<div id="nativeCodeGrid" class="code-grid"></div>
|
|
414
|
+
</section>
|
|
415
|
+
</div>
|
|
410
416
|
</section>
|
|
411
417
|
|
|
412
418
|
<section id="view-help" class="view">
|