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.
package/README.md CHANGED
@@ -4,6 +4,50 @@
4
4
 
5
5
  Use ele quando voce ja tem um app web, por exemplo uma pasta com `index.html`, `style.css`, `app.js` e imagens, e quer gerar um `.apk` instalavel no Android ou um `.aab` para loja.
6
6
 
7
+ ## Como contribuir sem quebrar o padrão
8
+
9
+ O html2apk agora é um projeto aberto, mas a regra mais importante para novas features é simples: antes de implementar, entenda como o código atual trabalha. Evite criar uma segunda arquitetura para resolver algo que a ponte existente já resolve.
10
+
11
+ Antes de mandar qualquer feature nova, o contribuinte precisa estudar o fluxo atual da aplicação e confirmar que a solução segue a mesma estratégia das funções existentes. Não envie código que cria atalhos, caminhos paralelos, outro padrão de bridge, outro jeito de tratar permissão ou outra forma de comunicar JavaScript com Java sem uma justificativa técnica muito clara. Primeiro adapte a ideia ao desenho que já funciona no projeto; só proponha uma abordagem nova se o padrão atual realmente não atender.
12
+
13
+ Exemplo de postura esperada: "quero colocar uma nova função no projeto; antes de escrever, vou ver como as outras funções foram colocadas, como o código lida com elas, qual é o fluxo completo e por que esse fluxo existe". Essa investigação vem antes da implementação. Ela mostra onde a nova função deve nascer, como deve normalizar argumentos, qual action deve chamar, como o Java deve responder, onde documentar e como testar. É esse cuidado que permite criar algo novo sem quebrar a aplicação.
14
+
15
+ As funções interpretadas seguem um caminho bem definido:
16
+
17
+ ```text
18
+ JavaScript do app
19
+ -> função global em português
20
+ -> aliases quando fizer sentido
21
+ -> normalização de argumentos
22
+ -> cordova.exec(action)
23
+ -> dispatcher Java
24
+ -> permissão/thread/subsistema Android
25
+ -> CallbackContext
26
+ -> Promise no JavaScript
27
+ ```
28
+
29
+ Quando uma feature nova entra, ela deve atravessar esse caminho em vez de criar uma forma paralela de comunicação. Isso evita duplicação, bugs difíceis de testar e diferença de comportamento entre o early bridge, o plugin Cordova, a interface desktop e o app final.
30
+
31
+ Checklist antes de abrir PR ou commit:
32
+
33
+ - Entenda a arquitetura existente antes de alterar arquivos. Se ainda não sabe por onde uma função interpretada nasce, passa pela bridge e chega ao Java, pare e leia o código antes de implementar.
34
+ - Confirme que a feature nova não está criando conflito com APIs, helpers, normalizadores, actions ou eventos que já existem.
35
+ - Evite estratégias novas desnecessárias. Se uma função parecida já usa `cordova.exec`, dispatcher Java, `CallbackContext`, permissão pendente ou evento `CustomEvent`, a nova feature deve seguir esse caminho.
36
+ - Leia funções parecidas antes de escrever código novo. Se for arquivo, veja `salvarArquivo`, `baixarArquivo` e `FileProvider`. Se for permissão, veja câmera, microfone, notificação e localização. Se for evento, veja `aoEvento`, notificação e compartilhamento recebido.
37
+ - Mantenha nomes em PT-BR como API principal e aliases em inglês apenas quando combinarem com o padrão existente.
38
+ - Adicione a função no early bridge e no plugin JS com a mesma assinatura pública.
39
+ - Reuse normalizadores e helpers existentes; não trate payload com string solta se o projeto já usa objeto estruturado.
40
+ - No Java, entre pelo dispatcher de `action` existente e retorne JSON consistente.
41
+ - Se precisar de permissão runtime, preserve callback pendente, busy state e abertura de configurações quando o Android exigir.
42
+ - Se tocar arquivos, preserve sanitização de nome, armazenamento interno, MediaStore quando aplicável e FileProvider.
43
+ - Se tocar operações longas, use a thread adequada e não trave a WebView.
44
+ - Atualize `plugin.xml` apenas com permissões, intent filters ou dependências realmente necessárias.
45
+ - Atualize runtime console, aba "Códigos interpretados", laboratório USB, README/SOBRE quando a feature for pública.
46
+ - Adicione ou ajuste testes em `test/config.test.js` para provar que JS, Java, UI e docs continuam alinhados.
47
+ - Rode `npm test` antes de enviar.
48
+
49
+ O objetivo não é impedir criatividade; é proteger o comportamento previsível da ferramenta. Uma contribuição boa parece nativa no projeto: usa os mesmos nomes, passa pelos mesmos pontos, retorna no mesmo formato e respeita as mesmas fronteiras entre build, desktop, bridge JS e Java Android.
50
+
7
51
  ## O Mais Importante
8
52
 
9
53
  Na pratica, voce vai usar estes comandos:
@@ -297,6 +341,19 @@ Prioridade de configuracao:
297
341
  2. `app.json` ou `config.json`.
298
342
  3. Valores padrao do html2apk.
299
343
 
344
+ ### Modo Flutuante E Sobreposicao
345
+
346
+ 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`.
347
+
348
+ 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.
349
+
350
+ O icone tambem aceita opacidade:
351
+
352
+ ```js
353
+ await iniciarIconeFlutuante({ opacidade: 0.85 });
354
+ await definirOpacidadeIconeFlutuante(0.55);
355
+ ```
356
+
300
357
  ### Tema Automatico Do APK
301
358
 
302
359
  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.
@@ -380,7 +437,7 @@ Retorno:
380
437
 
381
438
  ## Bridge Nativa
382
439
 
383
- A v0.1 instala um plugin Cordova local com uma API global simples para recursos Android. Todas as funcoes retornam `Promise`, exceto os ouvintes `aoEvento`/atalhos, que retornam uma funcao para cancelar a escuta.
440
+ A v0.1 instala um plugin Cordova local com uma API global simples para recursos Android. Todas as funções retornam `Promise`, exceto os ouvintes `aoEvento`/atalhos, que retornam uma função para cancelar a escuta.
384
441
 
385
442
  O html2apk injeta `html2apk-early-bridge.js` e `cordova.js` automaticamente no HTML inicial do APK. A bridge inicial cria as funcoes interpretadas antes dos scripts do seu projeto; se uma funcao nativa for chamada antes do `deviceready`, ela espera o Android ficar pronto antes de executar.
386
443
 
@@ -406,10 +463,47 @@ Exemplos de aliases:
406
463
  | `pararMic()` | `stopMic()` |
407
464
  | `lanterna()` | `flashlight()` |
408
465
  | `alternarLanterna()` | `toggleFlashlight()` |
466
+ | `tirarFoto()` | `takePhoto()` / `capturePhoto()` |
467
+ | `capturarVideo()` | `captureVideo()` |
468
+ | `escanearQRCode()` | `scanQRCode()` |
409
469
  | `escolherArquivo()` | `pickFile()` |
410
470
  | `escolherImagem()` | `pickImage()` |
471
+ | `escolherImagens()` | `pickImages()` |
472
+ | `escolherPasta()` | `pickFolder()` |
411
473
  | `salvarArquivo()` | `saveFile()` |
474
+ | `lerArquivo()` | `readFile()` |
475
+ | `listarArquivos()` | `listFiles()` |
476
+ | `excluirArquivo()` | `deleteFile()` |
477
+ | `abrirArquivo()` | `openFile()` |
478
+ | `compartilharArquivo()` | `shareFile()` |
479
+ | `baixarArquivo()` | `downloadFile()` |
480
+ | `baixarBase64()` | `downloadBase64()` |
481
+ | `baixarArquivoLocal()` | `downloadLocalFile()` / `downloadFromFile()` |
482
+ | `definirPapelParede()` | `setWallpaper()` |
483
+ | `infoPapelParede()` | `wallpaperInfo()` |
484
+ | `abrirConfiguracaoPapelParede()` | `openWallpaperSettings()` |
485
+ | `capturarTela()` / `tirarPrint()` | `captureScreen()` / `takeScreenshot()` |
412
486
  | `compartilhar()` | `share()` |
487
+ | `compartilharApp()` / `share_me()` | `shareApp()` |
488
+ | `aoReceberCompartilhamento()` | `onShareReceived()` |
489
+ | `obterCompartilhamentoInicial()` | `getInitialShare()` |
490
+ | `procurarBT()` | `scanBluetooth()` |
491
+ | `conectarBT()` | `connectBluetooth()` |
492
+ | `enviarBT()` | `sendBluetooth()` |
493
+ | `aoConectarBT()` | `onBluetoothConnect()` |
494
+ | `aoReceberDadosBT()` | `onBluetoothData()` |
495
+ | `aoDarErroBT()` | `onBluetoothError()` |
496
+ | `procurarWiFi()` | `scanWiFi()` |
497
+ | `conectarWiFi()` | `connectWiFi()` |
498
+ | `enviarWiFi()` | `sendWiFi()` |
499
+ | `aoConectarWiFi()` | `onWiFiConnect()` |
500
+ | `aoReceberDadosWiFi()` | `onWiFiData()` |
501
+ | `aoDarErroWiFi()` | `onWiFiError()` |
502
+ | `ocr()` | `recognizeText()` / `textFromImage()` |
503
+ | `falar()` | `speak()` / `textToSpeech()` |
504
+ | `pararFala()` | `stopSpeaking()` |
505
+ | `ouvir()` | `recognizeSpeech()` / `speechToText()` |
506
+ | `aguardar()` | `loading()` |
413
507
  | `copiarTexto()` | `copyText()` |
414
508
  | `lerTextoCopiado()` | `readText()` |
415
509
  | `abrirNoApp()` | `openInApp()` |
@@ -418,15 +512,44 @@ Exemplos de aliases:
418
512
  | `manterTelaLigada()` | `keepScreenOn()` |
419
513
  | `brilhoTela()` | `setScreenBrightness()` |
420
514
  | `definirCorTema()` | `setThemeColor()` |
515
+ | `volumeAtual()` | `getVolume()` |
516
+ | `definirVolume()` | `setVolume()` |
517
+ | `aumentarVolume()` | `increaseVolume()` |
518
+ | `diminuirVolume()` | `decreaseVolume()` |
421
519
  | `infoMemoria()` | `memoryInfo()` |
422
520
  | `infoArmazenamento()` | `storageInfo()` |
423
521
  | `infoDesempenho()` | `performanceInfo()` |
424
522
  | `appsAbertos()` | `openAppsMemory()` |
523
+ | `configurarIconeFlutuante()` | `configureFloatingIcon()` |
524
+ | `definirOpacidadeIconeFlutuante()` | `setFloatingIconOpacity()` |
525
+ | `minimizarApp()` | `minimizeApp()` |
526
+ | `fecharApp()` | `closeApp()` / `exitApp()` |
527
+ | `obterLocalizacao()` | `getLocation()` |
528
+ | `acompanharLocalizacao()` | `watchLocation()` |
529
+ | `pararLocalizacao()` | `stopLocationWatch()` |
530
+ | `autenticarBiometria()` | `authenticateBiometric()` |
531
+ | `salvarSeguro()` | `saveSecure()` |
532
+ | `lerSeguro()` | `readSecure()` |
533
+ | `removerSeguro()` | `deleteSecure()` |
425
534
  | `aoEvento()` | `onEvent()` |
426
535
  | `aoMinimizar()` | `onMinimize()` |
536
+ | `aoConectarUSB()` | `onUSBConnect()` |
537
+ | `aoDesconectarUSB()` | `onUSBDisconnect()` |
538
+ | `aoConectarFone()` | `onHeadphoneConnect()` |
539
+ | `aoDesconectarFone()` | `onHeadphoneDisconnect()` |
540
+ | `aoMudarVolume()` | `onVolumeChange()` |
541
+ | `aoAbrirTeclado()` | `onKeyboardOpen()` |
542
+ | `aoFecharTeclado()` | `onKeyboardClose()` |
543
+ | `aoSacudirCelular()` | `onPhoneShake()` |
544
+ | `aoVirarCelularParaBaixo()` | `onPhoneFaceDown()` |
545
+ | `aoAproximarObjeto()` | `onProximityNear()` |
546
+ | `aoTirarPrint()` | `onScreenshot()` |
547
+ | `aoMudarOrientacao()` | `onOrientationChange()` |
548
+ | `aoNFC()` | `onNFC()` |
549
+ | `aoReceberNotificacao()` | `onNotificationReceived()` |
427
550
  | `obterLinkInicial()` | `getInitialLink()` |
428
551
 
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`.
552
+ Os eventos tambem aceitam aliases em ingles em `onEvent()`: `app:ready`, `app:background`, `app:resumed`, `back:button`, `link:opened`, `share:received`, `sharing:received`, `bluetooth:connected`, `bluetooth:data`, `bluetooth:error`, `wifi:connected`, `wifi:data`, `wifi:error`, `usb:connected`, `usb:disconnected`, `headphone:connected`, `headphone:disconnected`, `volume:changed`, `keyboard:opened`, `keyboard:closed`, `phone:shaken`, `phone:faceDown`, `proximity:near`, `screenshot:taken`, `orientation:changed`, `nfc:received`, `network:changed`, `battery:changed`, `location:changed`, `notification:received` e `notification:clicked`.
430
553
 
431
554
  Como tratar retornos:
432
555
 
@@ -443,6 +566,7 @@ No seu JavaScript do app:
443
566
  ```js
444
567
  toast("Mensagem");
445
568
  vibrar(250);
569
+ await aguardar(5000);
446
570
 
447
571
  await notificar({
448
572
  titulo: "Pedido aprovado",
@@ -505,6 +629,14 @@ fullscreen(true);
505
629
 
506
630
  `notificar()` nao obriga clique, botao nem funcao. So `titulo` e `texto` ja geram uma notificacao normal. `aoClicar`, `acoes`/`actions` e `open` sao opcionais.
507
631
 
632
+ `aguardar(ms)` e `loading(ms)` pausam o fluxo com Promise, sem travar a WebView:
633
+
634
+ ```js
635
+ await toast("Comecando");
636
+ await aguardar(5000);
637
+ await toast("Continuando");
638
+ ```
639
+
508
640
  `agendarNotificacao()` agenda uma notificacao. Se voce passar um array para ela, ou usar `agendarNotificacoes()`, o html2apk agenda varias em sequencia. Cada item recebe `id` automatico se voce nao informar um.
509
641
 
510
642
  Em `aoClicar`, voce pode passar uma funcao diretamente:
@@ -610,9 +742,10 @@ Arquivos, galeria e compartilhamento:
610
742
 
611
743
  ```js
612
744
  const imagem = await escolherImagem();
613
- const imagens = await escolherImagens({ multiplo: true });
745
+ const imagens = await escolherImagens({ multiplas: true });
614
746
  const pdf = await escolherArquivo({ tipos: ["application/pdf"] });
615
747
  const arquivos = await escolherArquivos({ multiplo: true });
748
+ const pasta = await escolherPasta();
616
749
 
617
750
  await salvarArquivo({
618
751
  nome: "relatorio.txt",
@@ -620,7 +753,173 @@ await salvarArquivo({
620
753
  conteudo: "Conteudo salvo pelo app"
621
754
  });
622
755
 
623
- await compartilhar({ texto: "Veja isso", url: "https://exemplo.com" });
756
+ await compartilhar({
757
+ titulo: "Material",
758
+ texto: "Veja isso",
759
+ url: "https://exemplo.com",
760
+ arquivo: imagem
761
+ });
762
+
763
+ aoReceberCompartilhamento((dados) => {
764
+ console.log("Compartilhamento recebido", dados.tipo, dados.uri || dados.texto);
765
+ });
766
+
767
+ const texto = await ocr(imagem);
768
+ console.log(texto.texto);
769
+
770
+ await falar("Ola mundo", { idioma: "pt-BR", velocidade: 1 });
771
+ const voz = await ouvir({ idioma: "pt-BR" });
772
+ console.log(voz.texto);
773
+
774
+ aoConectarBT((dispositivo) => {
775
+ console.log("Bluetooth conectado", dispositivo.nome);
776
+ });
777
+
778
+ aoReceberDadosBT((dados) => {
779
+ console.log("Dados Bluetooth", dados);
780
+ });
781
+
782
+ aoDarErroBT((erro) => {
783
+ console.log("Erro Bluetooth", erro.mensagem || erro.message);
784
+ });
785
+
786
+ const dispositivos = await procurarBT();
787
+ if (dispositivos[0]) {
788
+ await conectarBT(dispositivos[0].id);
789
+ await enviarBT({ mensagem: "Ola por Bluetooth" });
790
+ }
791
+
792
+ aoConectarWiFi((dispositivo) => {
793
+ console.log("Wi-Fi conectado", dispositivo.nome || dispositivo.host);
794
+ });
795
+
796
+ aoReceberDadosWiFi((dados) => {
797
+ console.log("Dados Wi-Fi", dados);
798
+ });
799
+
800
+ aoDarErroWiFi((erro) => {
801
+ console.log("Erro Wi-Fi", erro.mensagem || erro.message);
802
+ });
803
+
804
+ const dispositivosWifi = await procurarWiFi();
805
+ if (dispositivosWifi[0]) {
806
+ await conectarWiFi(dispositivosWifi[0].id);
807
+ await enviarWiFi({ mensagem: "Ola por Wi-Fi" });
808
+ }
809
+
810
+ await share_me(); // compartilha o APK do proprio app aberto
811
+ ```
812
+
813
+ `escolherImagem()` e `escolherImagens()` usam o Android Photo Picker no Android 13+ e caem automaticamente para o Storage Access Framework em Android antigo. Quando o Photo Picker esta disponivel, o html2apk nao solicita permissao ampla de armazenamento. O retorno segue o mesmo formato dos seletores: `{ uri, nome, mimeType, tamanho }`.
814
+
815
+ `compartilhar()` aceita texto, link, imagem, video, PDF, arquivo salvo pelo app, URI `content://` e lista de arquivos em `arquivo`/`arquivos`. O retorno confirma a abertura do share sheet com `{ ok: true }`.
816
+
817
+ Apps gerados tambem entram no menu Compartilhar do Android para `text/plain`, `image/*`, `video/*`, `application/pdf` e `*/*`. Use `obterCompartilhamentoInicial()` no boot se o app foi aberto por compartilhamento, e `aoReceberCompartilhamento()` para receber novos intents enquanto ele ja esta aberto.
818
+
819
+ `ocr()` usa ML Kit local para reconhecimento de texto em imagens. O processamento fica offline no aparelho. `falar()` usa o TextToSpeech do Android e `ouvir()` usa o reconhecedor de voz do sistema; ambos aceitam `idioma: "pt-BR"` ou `idioma: "auto"`.
820
+
821
+ Bluetooth usa RFCOMM classico entre apps html2apk. O aparelho que vai receber registra `aoConectarBT()`, `aoReceberDadosBT()` e, se quiser tratar falhas, `aoDarErroBT()`; isso inicia o servidor interno. O outro chama `procurarBT()`, escolhe um `id`, chama `conectarBT(id)` e envia JSON com `enviarBT(objeto)`. Para aparecer na busca, o aparelho precisa estar pareado ou visivel nas configuracoes Bluetooth do Android.
822
+
823
+ Wi-Fi local usa descoberta NSD e socket TCP entre apps html2apk na mesma rede ou hotspot. O aparelho que vai receber registra `aoConectarWiFi()`, `aoReceberDadosWiFi()` e `aoDarErroWiFi()`; isso inicia o servidor interno e anuncia o app na rede local. O outro chama `procurarWiFi()`, escolhe um `id`, chama `conectarWiFi(id)` e envia JSON com `enviarWiFi(objeto)`. Nao e Wi-Fi Direct: os dois aparelhos precisam estar conectados na mesma rede local, ou um deles precisa estar no hotspot do outro.
824
+
825
+ `salvarArquivo()` tem dois modos:
826
+
827
+ - `salvarArquivo({ nome, conteudo })` abre o seletor nativo para o usuario escolher onde salvar, como ja acontecia.
828
+ - `salvarArquivo("nomeArquivo", minhaVariavel)` salva direto no armazenamento app-scoped do APK. Use esse formato para CRUD interno.
829
+
830
+ CRUD de arquivos internos:
831
+
832
+ ```js
833
+ await salvarArquivo("perfil.json", {
834
+ nome: "Ana",
835
+ plano: "premium"
836
+ });
837
+
838
+ const perfil = await lerArquivo("perfil.json");
839
+ console.log(perfil.nome);
840
+
841
+ const completo = await lerArquivoCompleto("perfil.json");
842
+ console.log(completo.uri, completo.tamanho);
843
+
844
+ const existe = await arquivoExiste("perfil.json");
845
+ const arquivos = await listarArquivos();
846
+
847
+ await abrirArquivo("perfil.json");
848
+ await compartilharArquivo("perfil.json");
849
+ await excluirArquivo("perfil.json");
850
+ ```
851
+
852
+ Para baixar um arquivo e guardar no mesmo armazenamento:
853
+
854
+ ```js
855
+ await baixarArquivo("https://exemplo.com/relatorio.pdf", "relatorio.pdf");
856
+ await abrirArquivo("relatorio.pdf");
857
+
858
+ await baixarBase64("foto.png", base64DaImagem, {
859
+ mimeType: "image/png",
860
+ galeria: true
861
+ });
862
+
863
+ const arquivo = await escolherArquivo();
864
+ if (arquivo) {
865
+ await baixarArquivoLocal(arquivo, "copia-" + arquivo.name);
866
+ }
867
+ ```
868
+
869
+ 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`.
870
+
871
+ Por padrao, o arquivo baixado fica no armazenamento do app para funcionar com `abrirArquivo()` e `compartilharArquivo()`. Para imagem ou video aparecer na galeria, passe `{ galeria: true }`; no Android 10+ o html2apk publica uma copia em `Pictures/html2apk` ou `Movies/html2apk` e retorna `publicUri`.
872
+
873
+ Papel de parede:
874
+
875
+ ```js
876
+ const foto = await tirarFoto({ base64: true });
877
+
878
+ await salvarArquivo("wallpaper.jpg", foto.base64, {
879
+ base64: true,
880
+ mimeType: "image/jpeg"
881
+ });
882
+
883
+ const resultado = await definirPapelParede("wallpaper.jpg", {
884
+ alvo: "inicio" // "inicio", "bloqueio" ou "ambos"
885
+ });
886
+
887
+ console.log(resultado.applied, resultado.systemApplied, resultado.lockApplied);
888
+ ```
889
+
890
+ `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()`.
891
+
892
+ Camera, QR Code, localizacao, biometria e storage seguro:
893
+
894
+ ```js
895
+ const foto = await tirarFoto({ base64: true });
896
+
897
+ const qr = await escanearQRCode();
898
+ if (qr) {
899
+ console.log(qr.text);
900
+ }
901
+
902
+ const local = await obterLocalizacao({ altaPrecisao: true, timeoutMs: 10000 });
903
+ console.log(local.latitude, local.longitude);
904
+
905
+ const watch = await acompanharLocalizacao({ intervaloMs: 5000 });
906
+ const pararEvento = aoMudarLocalizacao((evento) => {
907
+ console.log(evento.latitude, evento.longitude);
908
+ });
909
+
910
+ await pararLocalizacao(watch.watchId);
911
+ pararEvento();
912
+
913
+ const bio = await autenticarBiometria({
914
+ titulo: "Confirmar acesso",
915
+ descricao: "Use a biometria do aparelho"
916
+ });
917
+
918
+ if (bio.authenticated) {
919
+ await salvarSeguro("token", "abc123");
920
+ const token = await lerSeguro("token");
921
+ await removerSeguro("token");
922
+ }
624
923
  ```
625
924
 
626
925
  O retorno de arquivos tem este formato:
@@ -729,6 +1028,27 @@ const abertos = await appsAbertos();
729
1028
 
730
1029
  Por privacidade, Android moderno pode limitar essa lista ao proprio app e alguns processos visiveis ao APK. Entao essa funcao nao deve ser tratada como gerenciador completo de tarefas do sistema.
731
1030
 
1031
+ Tela, volume e ciclo do app:
1032
+
1033
+ ```js
1034
+ const volume = await volumeAtual();
1035
+ console.log(volume.midia.atual, volume.midia.maximo);
1036
+
1037
+ await definirVolume("midia", 0.5, { mostrarUI: true });
1038
+ await aumentarVolume("midia", 1);
1039
+ await diminuirVolume("midia", 1);
1040
+
1041
+ const imagem = await capturarTela();
1042
+ document.querySelector("img.preview").src = imagem.dataUrl;
1043
+
1044
+ await minimizarApp();
1045
+
1046
+ // Use somente depois de salvar estado importante:
1047
+ // await fecharApp();
1048
+ ```
1049
+
1050
+ `capturarTela()` captura a tela do proprio APK/WebView, nao outros apps ou areas protegidas do sistema. `definirVolume()` aceita porcentagem entre 0 e 1 ou passos absolutos do stream.
1051
+
732
1052
  Eventos nativos:
733
1053
 
734
1054
  ```js
@@ -741,11 +1061,27 @@ aoEvento("botao:voltar", console.log);
741
1061
  aoEvento("link:aberto", (evento) => console.log(evento.url));
742
1062
  aoEvento("rede:mudou", console.log);
743
1063
  aoEvento("bateria:mudou", console.log);
1064
+ aoConectarUSB((dados) => console.log("USB conectado", dados));
1065
+ aoDesconectarUSB(() => console.log("USB desconectado"));
1066
+ aoReceberNotificacao((dados) => console.log("Notificação recebida", dados));
744
1067
  aoEvento("notificacao:clicada", console.log);
1068
+ aoConectarFone((dados) => console.log("Fone conectado", dados.dispositivo));
1069
+ aoDesconectarFone(() => console.log("Fone desconectado"));
1070
+ aoMudarVolume((dados) => console.log("Volume de mídia", dados.midia.atual));
1071
+ aoAbrirTeclado((dados) => console.log("Teclado abriu", dados.alturaTeclado));
1072
+ aoFecharTeclado(() => console.log("Teclado fechou"));
1073
+ aoMudarOrientacao((dados) => console.log("Orientação", dados.orientacao));
1074
+ aoSacudirCelular((dados) => console.log("Sacudiu", dados.forca));
1075
+ aoVirarCelularParaBaixo(() => console.log("Tela para baixo"));
1076
+ aoAproximarObjeto((dados) => console.log("Objeto perto", dados.distancia));
1077
+ aoTirarPrint((dados) => console.log("Print detectado", dados.uri));
1078
+ aoNFC((dados) => console.log("Tag NFC", dados.id, dados.mensagens));
745
1079
 
746
1080
  parar();
747
1081
  ```
748
1082
 
1083
+ Eventos de sensor e sistema seguem limites do Android/fabricante. `aoTirarPrint()` observa a MediaStore e depende do nome/pasta da captura; pode não disparar em alguns aparelhos. `aoNFC()` escuta tags enquanto o app está aberto em primeiro plano e exige NFC ligado no aparelho.
1084
+
749
1085
  Deep links:
750
1086
 
751
1087
  ```js
package/package.json CHANGED
@@ -1,17 +1,20 @@
1
1
  {
2
2
  "name": "html2apk",
3
- "version": "0.8.0",
3
+ "version": "0.11.0",
4
+ "author": { "name": "Dev Caio Multiversando", "email": "dev@caio.local" },
5
+
4
6
  "description": "Node CLI and library to turn an HTML/CSS/JS folder into an Android APK through Cordova.",
5
7
  "main": "index.js",
6
8
  "bin": {
7
- "html2apk": "./bin/html2apk.js"
9
+ "html2apk": "bin/html2apk.js"
8
10
  },
9
11
  "type": "commonjs",
10
12
  "license": "MIT",
11
13
  "files": [
12
14
  "bin",
13
15
  "src",
14
- "examples",
16
+ "examples/minimal/app.json",
17
+ "examples/minimal/index.html",
15
18
  "index.js",
16
19
  "html2apk.png",
17
20
  "README.md"
@@ -21,7 +24,9 @@
21
24
  "doctor": "node bin/html2apk.js doctor",
22
25
  "desktop": "node bin/html2apk-desktop.js",
23
26
  "build-desktop-win": "node scripts/build-desktop-portable.js",
24
- "test": "node --test",
27
+ "build-desktop-deb": "npx electron-builder --linux deb",
28
+
29
+ "test": "node --test test/config.test.js",
25
30
  "build-win": "pkg . --targets node20-win-x64 --output dist/html2apk.exe",
26
31
  "build-linux": "pkg . --targets node20-linux-x64 --output dist/html2apk-linux",
27
32
  "build-mac": "pkg . --targets node20-macos-x64 --output dist/html2apk-macos"
@@ -60,6 +65,12 @@
60
65
  "portable"
61
66
  ],
62
67
  "icon": "html2apk.png"
68
+ },
69
+ "linux": {
70
+ "target": [
71
+ "deb"
72
+ ],
73
+ "icon": "html2apk.png"
63
74
  }
64
75
  },
65
76
  "devDependencies": {