html2apk 0.9.0 → 12.0.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 +253 -12
- package/package.json +16 -4
- package/src/desktop/main.js +561 -29
- package/src/desktop/renderer/index.html +1 -1
- package/src/desktop/renderer/renderer.js +629 -35
- package/src/desktop/renderer/styles.css +13 -3
- package/src/runtime-manager/index.js +79 -17
- package/src/templates/cordova-plugin-html2apk-bridge/plugin.xml +34 -0
- package/src/templates/cordova-plugin-html2apk-bridge/src/android/BootReceiver.java +13 -0
- package/src/templates/cordova-plugin-html2apk-bridge/src/android/FloatingIconService.java +34 -7
- package/src/templates/cordova-plugin-html2apk-bridge/src/android/Html2ApkBridge.java +5365 -1641
- package/src/templates/cordova-plugin-html2apk-bridge/www/html2apk-bridge.js +528 -12
- package/src/templates/html2apk-early-bridge.js +528 -12
- package/src/templates/html2apk-runtime-console.js +169 -29
- package/examples/minimal/dist/MeuApp-1.0.0-debug.apk +0 -0
- package/examples/minimal/dist/MeuApp-1.0.0-release.aab +0 -0
|
@@ -526,6 +526,13 @@ const nativeCodeEntries = [
|
|
|
526
526
|
returns: { pt: "Promise<void>.", en: "Promise<void>." },
|
|
527
527
|
handling: { pt: "Limite duracoes longas e deixe a permissao `VIBRATE` no app; se falhar, siga sem bloquear o fluxo principal.", en: "Keep long durations under control and include the `VIBRATE` permission; if it fails, continue without blocking the main flow." }
|
|
528
528
|
},
|
|
529
|
+
{
|
|
530
|
+
syntax: { pt: "aguardar(5000) / loading(5000)", en: "loading(5000)" },
|
|
531
|
+
java: "JavaScript timer",
|
|
532
|
+
description: { pt: "Cria uma pausa assincrona entre linhas de codigo usando Promise.", en: "Creates an asynchronous pause between code lines using a Promise." },
|
|
533
|
+
returns: { pt: "{ ok, ms } depois do intervalo.", en: "{ ok, ms } after the interval." },
|
|
534
|
+
handling: { pt: "Use com `await` para esperar sem travar a WebView. O tempo e em milissegundos.", en: "Use with `await` to wait without blocking the WebView. The time is in milliseconds." }
|
|
535
|
+
},
|
|
529
536
|
{
|
|
530
537
|
syntax: { pt: "notificar({ titulo, texto, aoClicar?, acoes?, open? })", en: "notify({ title, text, onClick?, actions?, open? })" },
|
|
531
538
|
java: "notify",
|
|
@@ -571,7 +578,7 @@ const nativeCodeEntries = [
|
|
|
571
578
|
{
|
|
572
579
|
syntax: { pt: "aoEvento('app:background', fn)", en: "onEvent('app:background', fn)" },
|
|
573
580
|
java: "dispatchEvent",
|
|
574
|
-
description: { pt: "Escuta eventos nativos: app:pronto, app:background, app:voltou, botao:voltar, link:aberto, rede:mudou, bateria:mudou, notificacao:recebida e notificacao:clicada.", en: "Listens to native events: app:ready, app:background, app:resumed, back:button, link:opened, network:changed, battery:changed, notification:received and notification:clicked." },
|
|
581
|
+
description: { pt: "Escuta eventos nativos: app:pronto, app:background, app:voltou, botao:voltar, link:aberto, rede:mudou, bateria:mudou, usb:conectado, fone:conectado, volume:mudou, teclado:abriu, orientacao:mudou, celular:sacudido, print:tela, nfc:recebido, notificacao:recebida e notificacao:clicada.", en: "Listens to native events: app:ready, app:background, app:resumed, back:button, link:opened, network:changed, battery:changed, usb:connected, headphone:connected, volume:changed, keyboard:opened, orientation:changed, phone:shaken, screenshot:taken, nfc:received, notification:received and notification:clicked." },
|
|
575
582
|
returns: { pt: "Funcao para cancelar a escuta.", en: "Unsubscribe function." },
|
|
576
583
|
handling: { pt: "Guarde o retorno em `parar` e chame quando a tela/componente for desmontado para evitar escutas duplicadas.", en: "Store the return value as `stop` and call it when the screen/component unmounts to avoid duplicate listeners." }
|
|
577
584
|
},
|
|
@@ -582,6 +589,104 @@ const nativeCodeEntries = [
|
|
|
582
589
|
returns: { pt: "Funcao para cancelar a escuta.", en: "Unsubscribe function." },
|
|
583
590
|
handling: { pt: "Pause timers, audio ou leitura pesada ao minimizar; ao voltar, confira se dados precisam ser recarregados.", en: "Pause timers, audio or heavy reads on minimize; on resume, check whether data should be refreshed." }
|
|
584
591
|
},
|
|
592
|
+
{
|
|
593
|
+
syntax: { pt: "aoConectarUSB(fn) / aoDesconectarUSB(fn)", en: "onUSBConnect(fn) / onUSBDisconnect(fn)" },
|
|
594
|
+
java: "UsbManager + BatteryManager",
|
|
595
|
+
description: { pt: "Escuta conexão/desconexão USB por energia USB ou dispositivo USB/OTG anexado ao Android.", en: "Listens for USB connect/disconnect from USB power or USB/OTG devices attached to Android." },
|
|
596
|
+
returns: { pt: "Callback recebe { conectado, origem, dispositivo? }. Retorna função para cancelar.", en: "Callback receives { connected, source, device? }. Returns an unsubscribe function." },
|
|
597
|
+
handling: { pt: "Para cabo no computador, o evento vem como origem `power`; para OTG, vem com dados do dispositivo quando o Android entregar.", en: "For a computer cable, the event source is `power`; for OTG, device data is included when Android provides it." }
|
|
598
|
+
},
|
|
599
|
+
{
|
|
600
|
+
syntax: { pt: "aoConectarFone(fn) / aoDesconectarFone(fn)", en: "onHeadphoneConnect(fn) / onHeadphoneDisconnect(fn)" },
|
|
601
|
+
java: "AudioManager",
|
|
602
|
+
description: { pt: "Escuta fone com fio, Bluetooth, USB headset e aparelhos de áudio reconhecidos pelo Android.", en: "Listens for wired, Bluetooth, USB headset and other Android audio output devices." },
|
|
603
|
+
returns: { pt: "Callback recebe { conectado, dispositivo? }. Retorna função para cancelar.", en: "Callback receives { connected, device? }. Returns an unsubscribe function." },
|
|
604
|
+
handling: { pt: "Bluetooth pode chegar pelo callback de áudio quando vira saída de som; pare a escuta ao sair da tela.", en: "Bluetooth may arrive through the audio callback when it becomes an output device; stop listening when the screen unmounts." }
|
|
605
|
+
},
|
|
606
|
+
{
|
|
607
|
+
syntax: { pt: "aoMudarVolume(fn)", en: "onVolumeChange(fn)" },
|
|
608
|
+
java: "AudioManager + Settings Observer",
|
|
609
|
+
description: { pt: "Escuta mudanças nos volumes de mídia, toque, notificação, alarme e chamada.", en: "Listens for changes in media, ring, notification, alarm and voice-call volumes." },
|
|
610
|
+
returns: { pt: "Callback recebe volumes atuais e máximos por stream. Retorna função para cancelar.", en: "Callback receives current and max volumes by stream. Returns an unsubscribe function." },
|
|
611
|
+
handling: { pt: "O Android pode agrupar streams dependendo do modo de som do aparelho; trate como estado atual, não como histórico completo.", en: "Android may group streams depending on device sound mode; treat it as current state, not complete history." }
|
|
612
|
+
},
|
|
613
|
+
{
|
|
614
|
+
syntax: { pt: "volumeAtual() / definirVolume('midia', 0.5)", en: "getVolume() / setVolume('music', 0.5)" },
|
|
615
|
+
java: "AudioManager",
|
|
616
|
+
description: { pt: "Consulta e controla volumes de mídia, toque, notificação, alarme, chamada e sistema.", en: "Reads and controls media, ring, notification, alarm, voice-call and system volumes." },
|
|
617
|
+
returns: { pt: "`volumeAtual` retorna volumes atuais/máximos por stream. `definirVolume`, `aumentarVolume` e `diminuirVolume` retornam o novo estado.", en: "`getVolume` returns current/max volumes by stream. `setVolume`, `increaseVolume` and `decreaseVolume` return the new state." },
|
|
618
|
+
handling: { pt: "Use valores entre 0 e 1 para porcentagem ou inteiros para passos absolutos. Passe `{ mostrarUI: true }` se quiser exibir a barra nativa.", en: "Use values from 0 to 1 for percentage or integers for absolute steps. Pass `{ showUi: true }` to show the native volume panel." }
|
|
619
|
+
},
|
|
620
|
+
{
|
|
621
|
+
syntax: { pt: "aoAbrirTeclado(fn) / aoFecharTeclado(fn)", en: "onKeyboardOpen(fn) / onKeyboardClose(fn)" },
|
|
622
|
+
java: "ViewTreeObserver",
|
|
623
|
+
description: { pt: "Detecta abertura/fechamento do teclado pela área visível da WebView.", en: "Detects keyboard open/close through the visible WebView area." },
|
|
624
|
+
returns: { pt: "Callback recebe { aberto, alturaTeclado }. Retorna função para cancelar.", en: "Callback receives { open, keyboardHeight }. Returns an unsubscribe function." },
|
|
625
|
+
handling: { pt: "É uma heurística visual; modo tela cheia, teclado flutuante ou fabricante podem alterar a altura detectada.", en: "This is a visual heuristic; fullscreen mode, floating keyboards or vendors may change detected height." }
|
|
626
|
+
},
|
|
627
|
+
{
|
|
628
|
+
syntax: { pt: "aoMudarOrientacao(fn)", en: "onOrientationChange(fn)" },
|
|
629
|
+
java: "ViewTreeObserver + Configuration",
|
|
630
|
+
description: { pt: "Escuta troca entre portrait e landscape enquanto a WebView muda de tamanho.", en: "Listens for portrait/landscape changes while the WebView changes size." },
|
|
631
|
+
returns: { pt: "Callback recebe { orientacao, largura, altura }. Retorna função para cancelar.", en: "Callback receives { orientation, width, height }. Returns an unsubscribe function." },
|
|
632
|
+
handling: { pt: "Se o app travar orientação no config, o evento naturalmente não muda.", en: "If the app locks orientation in config, the event naturally does not change." }
|
|
633
|
+
},
|
|
634
|
+
{
|
|
635
|
+
syntax: { pt: "aoSacudirCelular(fn) / aoVirarCelularParaBaixo(fn)", en: "onPhoneShake(fn) / onPhoneFaceDown(fn)" },
|
|
636
|
+
java: "SensorManager",
|
|
637
|
+
description: { pt: "Escuta acelerômetro para detectar sacudida forte e tela virada para baixo.", en: "Uses the accelerometer to detect a strong shake and face-down posture." },
|
|
638
|
+
returns: { pt: "Callback recebe leituras x/y/z e força quando fizer sentido. Retorna função para cancelar.", en: "Callback receives x/y/z readings and force when relevant. Returns an unsubscribe function." },
|
|
639
|
+
handling: { pt: "Sensores variam por aparelho; use para interações leves e sempre mantenha um botão alternativo.", en: "Sensors vary by device; use for lightweight interactions and always keep a button fallback." }
|
|
640
|
+
},
|
|
641
|
+
{
|
|
642
|
+
syntax: { pt: "aoAproximarObjeto(fn)", en: "onProximityNear(fn)" },
|
|
643
|
+
java: "SensorManager",
|
|
644
|
+
description: { pt: "Escuta o sensor de proximidade quando algo se aproxima da tela.", en: "Listens to the proximity sensor when something gets near the screen." },
|
|
645
|
+
returns: { pt: "Callback recebe { perto, distancia, alcanceMaximo }. Retorna função para cancelar.", en: "Callback receives { near, distance, maximumRange }. Returns an unsubscribe function." },
|
|
646
|
+
handling: { pt: "Nem todo aparelho tem sensor de proximidade; trate ausência simplesmente como evento que nunca dispara.", en: "Not every device has a proximity sensor; treat absence as an event that simply never fires." }
|
|
647
|
+
},
|
|
648
|
+
{
|
|
649
|
+
syntax: { pt: "aoTirarPrint(fn)", en: "onScreenshot(fn)" },
|
|
650
|
+
java: "MediaStore Observer",
|
|
651
|
+
description: { pt: "Tenta detectar captura de tela observando novas imagens com nome/pasta de screenshot.", en: "Tries to detect screenshots by observing new images with screenshot-like names/folders." },
|
|
652
|
+
returns: { pt: "Callback recebe { uri, nome, caminho? }. Retorna função para cancelar.", en: "Callback receives { uri, name, path? }. Returns an unsubscribe function." },
|
|
653
|
+
handling: { pt: "Android moderno limita leitura de midia; alguns fabricantes mudam o nome da pasta, então esse evento é melhor esforço.", en: "Modern Android limits media reads; some vendors rename folders, so this event is best-effort." }
|
|
654
|
+
},
|
|
655
|
+
{
|
|
656
|
+
syntax: { pt: "capturarTela() / tirarPrint()", en: "captureScreen() / takeScreenshot()" },
|
|
657
|
+
java: "View.draw + Bitmap",
|
|
658
|
+
description: { pt: "Captura a tela atual do próprio app/WebView e devolve imagem em base64.", en: "Captures the current app/WebView screen and returns a base64 image." },
|
|
659
|
+
returns: { pt: "{ base64, dataUrl, width, height, mimeType, formato }.", en: "{ base64, dataUrl, width, height, mimeType, format }." },
|
|
660
|
+
handling: { pt: "Não captura outros apps nem áreas protegidas do sistema. Use depois da tela renderizar para evitar imagem vazia.", en: "Does not capture other apps or protected system areas. Call it after the screen renders to avoid an empty image." }
|
|
661
|
+
},
|
|
662
|
+
{
|
|
663
|
+
syntax: { pt: "aoNFC(fn)", en: "onNFC(fn)" },
|
|
664
|
+
java: "NfcAdapter",
|
|
665
|
+
description: { pt: "Escuta tags NFC enquanto o app está aberto em primeiro plano.", en: "Listens for NFC tags while the app is open in the foreground." },
|
|
666
|
+
returns: { pt: "Callback recebe { id, tecnologias, mensagens }. Retorna função para cancelar.", en: "Callback receives { id, technologies, messages }. Returns an unsubscribe function." },
|
|
667
|
+
handling: { pt: "Exige aparelho com NFC ligado. Tags que abrem o app enquanto ele estava fechado podem precisar de fluxo inicial em uma evolução futura.", en: "Requires a device with NFC enabled. Tags that launch the app from closed state may need an initial-flow helper in a future iteration." }
|
|
668
|
+
},
|
|
669
|
+
{
|
|
670
|
+
syntax: { pt: "aoReceberNotificacao(fn)", en: "onNotificationReceived(fn)" },
|
|
671
|
+
java: "dispatchEvent",
|
|
672
|
+
description: { pt: "Escuta quando uma notificação local do app é emitida pela bridge.", en: "Listens when a local app notification is emitted by the bridge." },
|
|
673
|
+
returns: { pt: "Callback recebe os dados da notificação. Retorna função para cancelar.", en: "Callback receives the notification data. Returns an unsubscribe function." },
|
|
674
|
+
handling: { pt: "Use para atualizar tela/estado quando `notificar()` ou uma notificacao agendada passar pela bridge. Para clique, use `aoClicarNotificacao()`.", en: "Use it to update UI/state when `notify()` or a scheduled notification goes through the bridge. For clicks, use `onNotificationClick()`." }
|
|
675
|
+
},
|
|
676
|
+
{
|
|
677
|
+
syntax: { pt: "obterLinkInicial()", en: "getInitialLink()" },
|
|
678
|
+
java: "Intent / Activity",
|
|
679
|
+
description: { pt: "Retorna a URL que abriu o aplicativo (Deep Link), se houver.", en: "Returns the URL that opened the application (Deep Link), if any." },
|
|
680
|
+
returns: { pt: "String (ex: 'meuapp://conteudo/123').", en: "String (e.g. 'myapp://content/123')." },
|
|
681
|
+
handling: { pt: "Util para repassar campanhas de marketing no inicio do app.", en: "Useful for passing marketing campaigns at app startup." }
|
|
682
|
+
},
|
|
683
|
+
{
|
|
684
|
+
syntax: { pt: "aoLigarDispositivo(callback)", en: "onDeviceBoot(callback)" },
|
|
685
|
+
java: "Intent / BootReceiver",
|
|
686
|
+
description: { pt: "Executa uma funcao caso o aplicativo tenha sido aberto silenciosamente pelo boot do celular.", en: "Executes a function if the application was silently opened by the device boot." },
|
|
687
|
+
returns: { pt: "Nenhum.", en: "None." },
|
|
688
|
+
handling: { pt: "Depende de `configurarInicioAutomatico(true)` e permissao de sobreposicao no Android 10+.", en: "Depends on `setAutoStartOnBoot(true)` and overlay permission on Android 10+." }
|
|
689
|
+
},
|
|
585
690
|
{
|
|
586
691
|
syntax: { pt: "obterLinkInicial() / aoAbrirLink(fn)", en: "getInitialLink() / onOpenLink(fn)" },
|
|
587
692
|
java: "getInitialLink",
|
|
@@ -619,15 +724,15 @@ const nativeCodeEntries = [
|
|
|
619
724
|
},
|
|
620
725
|
{
|
|
621
726
|
syntax: { pt: "escolherImagem()", en: "pickImage()" },
|
|
622
|
-
java: "
|
|
623
|
-
description: { pt: "Abre o
|
|
727
|
+
java: "Photo Picker / ACTION_OPEN_DOCUMENT",
|
|
728
|
+
description: { pt: "Abre o Photo Picker moderno no Android 13+ e usa SAF automaticamente nos Androids antigos.", en: "Opens the modern Photo Picker on Android 13+ and automatically falls back to SAF on older Android versions." },
|
|
624
729
|
returns: { pt: "{ uri, name, nome, size, tamanho, mimeType } ou null.", en: "{ uri, name, nome, size, tamanho, mimeType } or null." },
|
|
625
|
-
handling: { pt: "
|
|
730
|
+
handling: { pt: "Nao pede permissao ampla de armazenamento quando o Photo Picker esta disponivel. Use `uri`, nao caminho absoluto.", en: "Does not request broad storage permission when Photo Picker is available. Use `uri`, not an absolute file path." }
|
|
626
731
|
},
|
|
627
732
|
{
|
|
628
|
-
syntax: { pt: "escolherImagens({
|
|
629
|
-
java: "
|
|
630
|
-
description: { pt: "Abre
|
|
733
|
+
syntax: { pt: "escolherImagens({ multiplas: true })", en: "pickImages({ multiple: true })" },
|
|
734
|
+
java: "Photo Picker / ACTION_OPEN_DOCUMENT",
|
|
735
|
+
description: { pt: "Abre selecao multipla de imagens usando Photo Picker moderno quando possivel.", en: "Opens multiple image selection using the modern Photo Picker when possible." },
|
|
631
736
|
returns: { pt: "Array de arquivos; vazio se o usuario cancelar.", en: "Array of files; empty when the user cancels." },
|
|
632
737
|
handling: { pt: "Sempre trate como array. Limite quantidade/tamanho antes de enviar ou processar.", en: "Always handle it as an array. Limit quantity/size before uploading or processing." }
|
|
633
738
|
},
|
|
@@ -656,7 +761,7 @@ const nativeCodeEntries = [
|
|
|
656
761
|
syntax: { pt: "escolherPasta()", en: "pickFolder()" },
|
|
657
762
|
java: "pickFolder",
|
|
658
763
|
description: { pt: "Abre o seletor nativo de pasta quando o Android permitir.", en: "Opens the native folder picker when Android allows it." },
|
|
659
|
-
returns: { pt: "{ uri } ou objeto vazio se cancelar.", en: "{ uri } or an empty object when canceled." },
|
|
764
|
+
returns: { pt: "{ uri, nome } ou objeto vazio se cancelar.", en: "{ uri, name } or an empty object when canceled." },
|
|
660
765
|
handling: { pt: "Confira se `uri` existe antes de salvar. Android moderno entrega URI de documento, nao caminho real.", en: "Check that `uri` exists before saving. Modern Android returns a document URI, not a real path." }
|
|
661
766
|
},
|
|
662
767
|
{
|
|
@@ -667,11 +772,60 @@ const nativeCodeEntries = [
|
|
|
667
772
|
handling: { pt: "Depois do retorno, confira `saved === true`. Para binario, envie `base64`; para texto, use `conteudo`/`content`.", en: "After the return, check `saved === true`. For binary data, send `base64`; for text, use `content`/`conteudo`." }
|
|
668
773
|
},
|
|
669
774
|
{
|
|
670
|
-
syntax: { pt: "compartilhar({ texto, url })", en: "share({ text, url })" },
|
|
775
|
+
syntax: { pt: "compartilhar({ texto, url, arquivo, arquivos })", en: "share({ text, url, file, files })" },
|
|
671
776
|
java: "share",
|
|
672
|
-
description: { pt: "Abre a folha nativa
|
|
673
|
-
returns: { pt: "
|
|
674
|
-
handling: { pt: "
|
|
777
|
+
description: { pt: "Abre a folha nativa para texto, link, imagem, video, PDF, arquivo unico ou multiplos arquivos.", en: "Opens the native share sheet for text, link, image, video, PDF, one file or multiple files." },
|
|
778
|
+
returns: { pt: "{ ok, shared, items, mimeType }.", en: "{ ok, shared, items, mimeType }." },
|
|
779
|
+
handling: { pt: "Aceita objeto retornado por `escolherArquivo()`, URI ou nome salvo no armazenamento do app.", en: "Accepts an object returned by `pickFile()`, a URI or a file name saved in app storage." }
|
|
780
|
+
},
|
|
781
|
+
{
|
|
782
|
+
syntax: { pt: "aoReceberCompartilhamento(callback)", en: "onShareReceived(callback)" },
|
|
783
|
+
java: "ACTION_SEND / ACTION_SEND_MULTIPLE",
|
|
784
|
+
description: { pt: "Permite que o app criado apareca no menu Compartilhar do Android e receba texto, imagem, video, PDF ou arquivo.", en: "Lets the generated app appear in Android's Share menu and receive text, image, video, PDF or file data." },
|
|
785
|
+
returns: { pt: "Callback recebe { tipo, uri, mimeType, texto, items }.", en: "Callback receives { type, uri, mimeType, text, items }." },
|
|
786
|
+
handling: { pt: "Use `obterCompartilhamentoInicial()` no boot e `aoReceberCompartilhamento()` para intents recebidas com o app aberto.", en: "Use `getInitialShare()` on boot and `onShareReceived()` for intents received while the app is open." }
|
|
787
|
+
},
|
|
788
|
+
{
|
|
789
|
+
syntax: { pt: "procurarBT() / conectarBT(id) / enviarBT(objeto) / aoDarErroBT(callback)", en: "scanBluetooth() / connectBluetooth(id) / sendBluetooth(object) / onBluetoothError(callback)" },
|
|
790
|
+
java: "Bluetooth RFCOMM",
|
|
791
|
+
description: { pt: "Comunica dois apps html2apk por Bluetooth classico usando JSON.", en: "Communicates two html2apk apps over classic Bluetooth using JSON." },
|
|
792
|
+
returns: { pt: "`procurarBT` retorna lista de dispositivos; `conectarBT` retorna o dispositivo conectado; `enviarBT` retorna { ok, enviado }; `aoDarErroBT` recebe o erro.", en: "`scanBluetooth` returns a device list; `connectBluetooth` returns the connected device; `sendBluetooth` returns { ok, sent }; `onBluetoothError` receives the error." },
|
|
793
|
+
handling: { pt: "No aparelho que recebe, registre `aoConectarBT()`, `aoReceberDadosBT()` e `aoDarErroBT()`. Para descoberta, o outro aparelho precisa estar pareado ou visivel no Bluetooth do Android.", en: "On the receiving device, register `onBluetoothConnect()`, `onBluetoothData()` and `onBluetoothError()`. For discovery, the other device must be paired or visible in Android Bluetooth." }
|
|
794
|
+
},
|
|
795
|
+
{
|
|
796
|
+
syntax: { pt: "procurarWiFi() / conectarWiFi(id) / enviarWiFi(objeto) / aoDarErroWiFi(callback)", en: "scanWiFi() / connectWiFi(id) / sendWiFi(object) / onWiFiError(callback)" },
|
|
797
|
+
java: "NSD + Socket TCP local",
|
|
798
|
+
description: { pt: "Comunica dois apps html2apk pela mesma rede Wi-Fi ou hotspot usando JSON.", en: "Communicates two html2apk apps on the same Wi-Fi network or hotspot using JSON." },
|
|
799
|
+
returns: { pt: "`procurarWiFi` retorna lista de dispositivos; `conectarWiFi` retorna o dispositivo conectado; `enviarWiFi` retorna { ok, enviado }; `aoDarErroWiFi` recebe o erro.", en: "`scanWiFi` returns a device list; `connectWiFi` returns the connected device; `sendWiFi` returns { ok, sent }; `onWiFiError` receives the error." },
|
|
800
|
+
handling: { pt: "No aparelho que recebe, registre `aoConectarWiFi()`, `aoReceberDadosWiFi()` e `aoDarErroWiFi()`. Os dois aparelhos precisam estar na mesma rede local ou hotspot.", en: "On the receiving device, register `onWiFiConnect()`, `onWiFiData()` and `onWiFiError()`. Both devices must be on the same local network or hotspot." }
|
|
801
|
+
},
|
|
802
|
+
{
|
|
803
|
+
syntax: { pt: "ocr(imagem)", en: "recognizeText(image)" },
|
|
804
|
+
java: "ML Kit TextRecognition local",
|
|
805
|
+
description: { pt: "Reconhece texto em imagem usando ML Kit local, sem enviar dados para servidor.", en: "Recognizes text in an image using local ML Kit, without sending data to a server." },
|
|
806
|
+
returns: { pt: "{ texto, text, offline, blocks }.", en: "{ texto, text, offline, blocks }." },
|
|
807
|
+
handling: { pt: "Aceita objeto de `escolherImagem()`, URI, base64 ou nome salvo. O modelo latino atende portugues.", en: "Accepts a `pickImage()` object, URI, base64 or saved file name. The Latin model supports Portuguese." }
|
|
808
|
+
},
|
|
809
|
+
{
|
|
810
|
+
syntax: { pt: "falar('Ola', { idioma: 'pt-BR', velocidade: 1 }) / pararFala()", en: "speak('Hello', { language: 'en-US', speed: 1 }) / stopSpeaking()" },
|
|
811
|
+
java: "TextToSpeech",
|
|
812
|
+
description: { pt: "Usa o motor TTS instalado no Android para falar texto.", en: "Uses Android's installed TTS engine to speak text." },
|
|
813
|
+
returns: { pt: "{ ok, speaking, idioma, velocidade }.", en: "{ ok, speaking, language, speed }." },
|
|
814
|
+
handling: { pt: "Se o idioma nao estiver instalado/suportado, a Promise rejeita. Use `pararFala()` para interromper.", en: "If the language is not installed/supported, the Promise rejects. Use `stopSpeaking()` to interrupt." }
|
|
815
|
+
},
|
|
816
|
+
{
|
|
817
|
+
syntax: { pt: "ouvir({ idioma: 'pt-BR' })", en: "recognizeSpeech({ language: 'en-US' })" },
|
|
818
|
+
java: "RecognizerIntent",
|
|
819
|
+
description: { pt: "Abre o reconhecimento de voz nativo do Android e retorna o texto reconhecido.", en: "Opens Android native speech recognition and returns recognized text." },
|
|
820
|
+
returns: { pt: "{ texto, resultados, confidence }.", en: "{ text, results, confidence }." },
|
|
821
|
+
handling: { pt: "Use `idioma: 'auto'` ou omita idioma para deixar o Android escolher. Depende do servico de voz disponivel no aparelho.", en: "Use `language: 'auto'` or omit language to let Android choose. Depends on the voice service available on the device." }
|
|
822
|
+
},
|
|
823
|
+
{
|
|
824
|
+
syntax: { pt: "share_me() / compartilharApp()", en: "share_me() / shareApp()" },
|
|
825
|
+
java: "shareCurrentApp",
|
|
826
|
+
description: { pt: "Compartilha o APK do proprio app aberto usando a folha nativa do Android.", en: "Shares the APK of the currently open app through the native Android share sheet." },
|
|
827
|
+
returns: { pt: "{ shared, name, uri, size, packageName, splitApks, installableAsSingleApk }.", en: "{ shared, name, uri, size, packageName, splitApks, installableAsSingleApk }." },
|
|
828
|
+
handling: { pt: "Funciona melhor em APK unico gerado pelo html2apk. Se o app veio de AAB/loja com split APKs, o retorno avisa que compartilhar apenas o APK base pode nao reinstalar tudo.", en: "Works best with a single APK generated by html2apk. If the app came from an AAB/store with split APKs, the return warns that sharing only the base APK may not reinstall everything." }
|
|
675
829
|
},
|
|
676
830
|
{
|
|
677
831
|
syntax: { pt: "copiarTexto('texto') / lerTextoCopiado()", en: "copyText('text') / readText()" },
|
|
@@ -739,7 +893,7 @@ const nativeCodeEntries = [
|
|
|
739
893
|
{
|
|
740
894
|
syntax: { pt: "infoRede() / infoBateria()", en: "networkInfo() / batteryInfo()" },
|
|
741
895
|
java: "networkInfo/batteryInfo",
|
|
742
|
-
description: { pt: "Consulta
|
|
896
|
+
description: { pt: "Consulta conexão atual e bateria.", en: "Reads current connection and battery." },
|
|
743
897
|
returns: { pt: "rede: { online, tipo/type }; bateria: { level, charging }.", en: "network: { online, tipo/type }; battery: { level, charging }." },
|
|
744
898
|
handling: { pt: "Combine com `aoEvento('rede:mudou')` e `aoEvento('bateria:mudou')` para atualizar a tela sem ficar consultando em loop.", en: "Combine with `onEvent('network:changed')` and `onEvent('battery:changed')` to update the UI without polling." }
|
|
745
899
|
},
|
|
@@ -774,9 +928,16 @@ const nativeCodeEntries = [
|
|
|
774
928
|
{
|
|
775
929
|
syntax: { pt: "iniciarIconeFlutuante() / pararIconeFlutuante()", en: "startFloatingIcon() / stopFloatingIcon()" },
|
|
776
930
|
java: "FloatingIconService",
|
|
777
|
-
description: { pt: "Controla o
|
|
778
|
-
returns: { pt: "
|
|
779
|
-
handling: { pt: "`iniciarIconeFlutuante()` abre a tela de
|
|
931
|
+
description: { pt: "Controla o ícone flutuante e permite ajustar opacidade quando a sobreposição estiver liberada no Android.", en: "Controls the floating icon and allows opacity changes when draw-over-apps is allowed on Android." },
|
|
932
|
+
returns: { pt: "{ granted, requiresSettings, opacity } para iniciar/configurar; `pararIconeFlutuante` finaliza o serviço.", en: "{ granted, requiresSettings, opacity } for start/configure; `stopFloatingIcon` stops the service." },
|
|
933
|
+
handling: { pt: "`iniciarIconeFlutuante()` abre a tela de permissão automaticamente se faltar sobreposição. Use `definirOpacidadeIconeFlutuante(0.6)` para mudar sem recriar outro fluxo.", en: "`startFloatingIcon()` opens the permission screen automatically if draw-over-apps is missing. Use `setFloatingIconOpacity(0.6)` to change it without creating another flow." }
|
|
934
|
+
},
|
|
935
|
+
{
|
|
936
|
+
syntax: { pt: "minimizarApp() / fecharApp()", en: "minimizeApp() / closeApp()" },
|
|
937
|
+
java: "Activity",
|
|
938
|
+
description: { pt: "Envia o app para segundo plano ou fecha a Activity atual.", en: "Sends the app to the background or closes the current Activity." },
|
|
939
|
+
returns: { pt: "`minimizarApp`: { minimizado }. `fecharApp`: { fechado } antes de finalizar.", en: "`minimizeApp`: { minimized }. `closeApp`: { closed } before finishing." },
|
|
940
|
+
handling: { pt: "Use com uma ação clara do usuário. `fecharApp()` encerra a tela do APK, então salve estado antes.", en: "Use from an explicit user action. `closeApp()` finishes the APK screen, so save state first." }
|
|
780
941
|
},
|
|
781
942
|
{
|
|
782
943
|
syntax: { pt: "tirarFoto() / capturarVideo()", en: "takePhoto() / captureVideo()" },
|
|
@@ -800,18 +961,18 @@ const nativeCodeEntries = [
|
|
|
800
961
|
handling: { pt: "A chamada antiga `salvarArquivo({ nome, conteudo })` continua abrindo o seletor nativo. Use string no primeiro argumento para o CRUD interno.", en: "The old `saveFile({ name, content })` call still opens the native picker. Use a string first argument for internal CRUD." }
|
|
801
962
|
},
|
|
802
963
|
{
|
|
803
|
-
syntax: { pt: "baixarArquivo(url, '
|
|
964
|
+
syntax: { pt: "baixarArquivo(url, 'foto.png', { galeria: true }) / abrirArquivo('foto.png')", en: "downloadFile(url, 'photo.png', { gallery: true }) / openFile('photo.png')" },
|
|
804
965
|
java: "HttpURLConnection + NotificationCompat",
|
|
805
|
-
description: { pt: "Baixa por URL para o armazenamento persistente e mostra notificacao Android com barra de progresso.", en: "Downloads from a URL to persistent storage and shows an Android progress notification." },
|
|
806
|
-
returns: { pt: "`baixarArquivo` retorna metadados, tamanho, origem e
|
|
807
|
-
handling: { pt: "
|
|
966
|
+
description: { pt: "Baixa por URL para o armazenamento persistente do app e mostra notificacao Android com barra de progresso.", en: "Downloads from a URL to the app's persistent storage and shows an Android progress notification." },
|
|
967
|
+
returns: { pt: "`baixarArquivo` retorna metadados, tamanho, origem, notificacao e, com `{ galeria: true }`, `publicUri`.", en: "`downloadFile` returns metadata, size, source, notification state and, with `{ gallery: true }`, `publicUri`." },
|
|
968
|
+
handling: { pt: "Sem `{ galeria: true }`, imagens ficam privadas do app e nao aparecem na galeria. No Android 13+, se a permissao de notificacao for negada, o download continua.", en: "Without `{ gallery: true }`, images stay private to the app and do not appear in the gallery. On Android 13+, if notification permission is denied, the download continues." }
|
|
808
969
|
},
|
|
809
970
|
{
|
|
810
|
-
syntax: { pt: "baixarBase64('foto.png', base64) / baixarArquivoLocal(arquivo, 'copia.pdf')", en: "downloadBase64('photo.png', base64) / downloadLocalFile(file, 'copy.pdf')" },
|
|
971
|
+
syntax: { pt: "baixarBase64('foto.png', base64, { galeria: true }) / baixarArquivoLocal(arquivo, 'copia.pdf')", en: "downloadBase64('photo.png', base64, { gallery: true }) / downloadLocalFile(file, 'copy.pdf')" },
|
|
811
972
|
java: "InputStream + NotificationCompat",
|
|
812
973
|
description: { pt: "Cria um download a partir de base64, data URL, URI/caminho de arquivo ou objeto retornado por `escolherArquivo()`.", en: "Creates a download from base64, data URL, file URI/path or an object returned by `pickFile()`." },
|
|
813
|
-
returns: { pt: "Metadados do arquivo salvo, `sourceType`, `notificationShown
|
|
814
|
-
handling: { pt: "
|
|
974
|
+
returns: { pt: "Metadados do arquivo salvo, `sourceType`, `notificationShown`, permissao de notificacao e publicacao opcional.", en: "Saved file metadata, `sourceType`, `notificationShown`, notification permission and optional public publication." },
|
|
975
|
+
handling: { pt: "Para imagem/video aparecer na galeria, passe `{ galeria: true }`. Para esconder a notificacao, passe `{ notificacao: false }`.", en: "For image/video to appear in the gallery, pass `{ gallery: true }`. To hide the notification, pass `{ notification: false }`." }
|
|
815
976
|
},
|
|
816
977
|
{
|
|
817
978
|
syntax: { pt: "obterLocalizacao() / acompanharLocalizacao()", en: "getLocation() / watchLocation()" },
|
|
@@ -827,6 +988,27 @@ const nativeCodeEntries = [
|
|
|
827
988
|
returns: { pt: "{ supported, authenticated, canceled, message }.", en: "{ supported, authenticated, canceled, message }." },
|
|
828
989
|
handling: { pt: "Funciona em Android 9+. Se `supported` vier falso, use PIN/senha do proprio app como fallback.", en: "Works on Android 9+. If `supported` is false, use your app's own PIN/password fallback." }
|
|
829
990
|
},
|
|
991
|
+
{
|
|
992
|
+
syntax: { pt: "solicitarBloqueio({ titulo })", en: "requestDeviceLock({ title })" },
|
|
993
|
+
java: "KeyguardManager",
|
|
994
|
+
description: { pt: "Abre a tela de bloqueio do Android (PIN, padrao, senha).", en: "Opens Android device lock screen (PIN, pattern, password)." },
|
|
995
|
+
returns: { pt: "{ supported, authenticated, canceled, message }.", en: "{ supported, authenticated, canceled, message }." },
|
|
996
|
+
handling: { pt: "Funciona se o usuario tiver senha cadastrada.", en: "Works if the user has a secure lock screen configured." }
|
|
997
|
+
},
|
|
998
|
+
{
|
|
999
|
+
syntax: { pt: "solicitarSegundoPlano()", en: "requestBackgroundExecution()" },
|
|
1000
|
+
java: "Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS",
|
|
1001
|
+
description: { pt: "Solicita ao sistema que o app funcione ativamente no background e inicie junto com o aparelho.", en: "Requests the system to run the app actively in the background and autostart on boot." },
|
|
1002
|
+
returns: { pt: "{ ok, openedAutoStart, openedBatteryOptimization }.", en: "{ ok, openedAutoStart, openedBatteryOptimization }." },
|
|
1003
|
+
handling: { pt: "Usa permissoes restritas. Play Store pode rejeitar apps sem justificativa valida para manter CPU ligada.", en: "Uses restricted permissions. Play Store might reject apps without a valid reason to keep CPU awake." }
|
|
1004
|
+
},
|
|
1005
|
+
{
|
|
1006
|
+
syntax: { pt: "configurarInicioAutomatico(true/false)", en: "setAutoStartOnBoot(true/false)" },
|
|
1007
|
+
java: "BootReceiver -> Intent",
|
|
1008
|
+
description: { pt: "Define se o aplicativo deve ser aberto automaticamente quando o celular for ligado.", en: "Sets whether the app should automatically open when the device boots." },
|
|
1009
|
+
returns: { pt: "{ ok, enabled }.", en: "{ ok, enabled }." },
|
|
1010
|
+
handling: { pt: "Exige permissao de sobrepor a outros apps no Android 10+.", en: "Requires draw over other apps permission on Android 10+." }
|
|
1011
|
+
},
|
|
830
1012
|
{
|
|
831
1013
|
syntax: { pt: "salvarSeguro('token', valor) / lerSeguro('token')", en: "saveSecure('token', value) / readSecure('token')" },
|
|
832
1014
|
java: "Android Keystore",
|
|
@@ -885,6 +1067,7 @@ if (!info.videoSupported) {
|
|
|
885
1067
|
];
|
|
886
1068
|
|
|
887
1069
|
[
|
|
1070
|
+
"feedback",
|
|
888
1071
|
"feedback",
|
|
889
1072
|
"feedback",
|
|
890
1073
|
"notifications",
|
|
@@ -909,6 +1092,12 @@ if (!info.videoSupported) {
|
|
|
909
1092
|
"files",
|
|
910
1093
|
"share",
|
|
911
1094
|
"share",
|
|
1095
|
+
"share",
|
|
1096
|
+
"media",
|
|
1097
|
+
"media",
|
|
1098
|
+
"media",
|
|
1099
|
+
"share",
|
|
1100
|
+
"share",
|
|
912
1101
|
"device",
|
|
913
1102
|
"device",
|
|
914
1103
|
"device",
|
|
@@ -962,6 +1151,17 @@ const nativeCodeRecipes = [
|
|
|
962
1151
|
en: `await vibrate(250);`
|
|
963
1152
|
}
|
|
964
1153
|
},
|
|
1154
|
+
{
|
|
1155
|
+
when: { pt: "Para esperar entre duas linhas sem bloquear a interface.", en: "To wait between two lines without blocking the interface." },
|
|
1156
|
+
example: {
|
|
1157
|
+
pt: `await toast("Comecando");
|
|
1158
|
+
await aguardar(5000);
|
|
1159
|
+
await toast("Continuando");`,
|
|
1160
|
+
en: `await toast("Starting");
|
|
1161
|
+
await loading(5000);
|
|
1162
|
+
await toast("Continuing");`
|
|
1163
|
+
}
|
|
1164
|
+
},
|
|
965
1165
|
{
|
|
966
1166
|
when: { pt: "Para mostrar uma notificacao simples agora. `aoClicar`, `acoes` e `open` sao opcionais.", en: "To show a simple notification now. `onClick`, `actions` and `open` are optional." },
|
|
967
1167
|
example: {
|
|
@@ -1186,6 +1386,111 @@ console.log(permissions);`
|
|
|
1186
1386
|
// stop();`
|
|
1187
1387
|
}
|
|
1188
1388
|
},
|
|
1389
|
+
{
|
|
1390
|
+
when: { pt: "Para reagir a cabo/dispositivo USB e notificacoes locais.", en: "To react to USB cable/device changes and local notifications." },
|
|
1391
|
+
example: {
|
|
1392
|
+
pt: `aoConectarUSB((dados) => {
|
|
1393
|
+
console.log("USB conectado", dados.origem, dados.dispositivo);
|
|
1394
|
+
});
|
|
1395
|
+
|
|
1396
|
+
aoDesconectarUSB(() => {
|
|
1397
|
+
console.log("USB desconectado");
|
|
1398
|
+
});
|
|
1399
|
+
|
|
1400
|
+
aoReceberNotificacao((dados) => {
|
|
1401
|
+
console.log("Notificação recebida", dados.titulo || dados.title);
|
|
1402
|
+
});`,
|
|
1403
|
+
en: `onUSBConnect((data) => {
|
|
1404
|
+
console.log("USB connected", data.source, data.device);
|
|
1405
|
+
});
|
|
1406
|
+
|
|
1407
|
+
onUSBDisconnect(() => {
|
|
1408
|
+
console.log("USB disconnected");
|
|
1409
|
+
});
|
|
1410
|
+
|
|
1411
|
+
onNotificationReceived((data) => {
|
|
1412
|
+
console.log("Notification received", data.title);
|
|
1413
|
+
});`
|
|
1414
|
+
}
|
|
1415
|
+
},
|
|
1416
|
+
{
|
|
1417
|
+
when: { pt: "Para reagir a fone, volume, teclado e orientação da tela.", en: "To react to headphones, volume, keyboard and screen orientation." },
|
|
1418
|
+
example: {
|
|
1419
|
+
pt: `aoConectarFone((dados) => {
|
|
1420
|
+
console.log("Fone conectado", dados.dispositivo);
|
|
1421
|
+
});
|
|
1422
|
+
|
|
1423
|
+
aoMudarVolume((dados) => {
|
|
1424
|
+
console.log("Volume de mídia", dados.midia.atual);
|
|
1425
|
+
});
|
|
1426
|
+
|
|
1427
|
+
aoAbrirTeclado((dados) => {
|
|
1428
|
+
console.log("Teclado abriu", dados.alturaTeclado);
|
|
1429
|
+
});
|
|
1430
|
+
|
|
1431
|
+
aoMudarOrientacao((dados) => {
|
|
1432
|
+
console.log("Orientação", dados.orientacao);
|
|
1433
|
+
});`,
|
|
1434
|
+
en: `onHeadphoneConnect((data) => {
|
|
1435
|
+
console.log("Headphone connected", data.device);
|
|
1436
|
+
});
|
|
1437
|
+
|
|
1438
|
+
onVolumeChange((data) => {
|
|
1439
|
+
console.log("Media volume", data.music.current);
|
|
1440
|
+
});
|
|
1441
|
+
|
|
1442
|
+
onKeyboardOpen((data) => {
|
|
1443
|
+
console.log("Keyboard opened", data.keyboardHeight);
|
|
1444
|
+
});
|
|
1445
|
+
|
|
1446
|
+
onOrientationChange((data) => {
|
|
1447
|
+
console.log("Orientation", data.orientation);
|
|
1448
|
+
});`
|
|
1449
|
+
}
|
|
1450
|
+
},
|
|
1451
|
+
{
|
|
1452
|
+
when: { pt: "Para interações legais com sensores, print e NFC.", en: "For playful interactions with sensors, screenshots and NFC." },
|
|
1453
|
+
example: {
|
|
1454
|
+
pt: `aoSacudirCelular((dados) => {
|
|
1455
|
+
console.log("Sacudiu", dados.forca);
|
|
1456
|
+
});
|
|
1457
|
+
|
|
1458
|
+
aoVirarCelularParaBaixo(() => {
|
|
1459
|
+
console.log("Tela virada para baixo");
|
|
1460
|
+
});
|
|
1461
|
+
|
|
1462
|
+
aoAproximarObjeto((dados) => {
|
|
1463
|
+
console.log("Algo chegou perto", dados.distancia);
|
|
1464
|
+
});
|
|
1465
|
+
|
|
1466
|
+
aoTirarPrint((dados) => {
|
|
1467
|
+
console.log("Print detectado", dados.uri);
|
|
1468
|
+
});
|
|
1469
|
+
|
|
1470
|
+
aoNFC((dados) => {
|
|
1471
|
+
console.log("Tag NFC", dados.id, dados.mensagens);
|
|
1472
|
+
});`,
|
|
1473
|
+
en: `onPhoneShake((data) => {
|
|
1474
|
+
console.log("Shaken", data.force);
|
|
1475
|
+
});
|
|
1476
|
+
|
|
1477
|
+
onPhoneFaceDown(() => {
|
|
1478
|
+
console.log("Phone is face down");
|
|
1479
|
+
});
|
|
1480
|
+
|
|
1481
|
+
onProximityNear((data) => {
|
|
1482
|
+
console.log("Something is near", data.distance);
|
|
1483
|
+
});
|
|
1484
|
+
|
|
1485
|
+
onScreenshot((data) => {
|
|
1486
|
+
console.log("Screenshot detected", data.uri);
|
|
1487
|
+
});
|
|
1488
|
+
|
|
1489
|
+
onNFC((data) => {
|
|
1490
|
+
console.log("NFC tag", data.id, data.messages);
|
|
1491
|
+
});`
|
|
1492
|
+
}
|
|
1493
|
+
},
|
|
1189
1494
|
{
|
|
1190
1495
|
when: { pt: "Para pausar/resumir tarefas quando o usuario minimiza ou volta para o app.", en: "To pause/resume work when the user minimizes or returns to the app." },
|
|
1191
1496
|
example: {
|
|
@@ -1318,7 +1623,7 @@ if (image) {
|
|
|
1318
1623
|
{
|
|
1319
1624
|
when: { pt: "Para galeria com selecao multipla, como anexar varias fotos.", en: "For multiple gallery selection, such as attaching many photos." },
|
|
1320
1625
|
example: {
|
|
1321
|
-
pt: `const imagens = await escolherImagens({
|
|
1626
|
+
pt: `const imagens = await escolherImagens({ multiplas: true });
|
|
1322
1627
|
|
|
1323
1628
|
for (const imagem of imagens) {
|
|
1324
1629
|
console.log(imagem.nome, imagem.tamanho);
|
|
@@ -1422,18 +1727,141 @@ if (saved.saved) {
|
|
|
1422
1727
|
}
|
|
1423
1728
|
},
|
|
1424
1729
|
{
|
|
1425
|
-
when: { pt: "Para abrir o compartilhamento nativo do Android com texto e
|
|
1730
|
+
when: { pt: "Para abrir o compartilhamento nativo do Android com texto, link e arquivos.", en: "To open Android native sharing with text, link and files." },
|
|
1426
1731
|
example: {
|
|
1427
|
-
pt: `await
|
|
1732
|
+
pt: `const imagem = await escolherImagem();
|
|
1733
|
+
|
|
1734
|
+
await compartilhar({
|
|
1428
1735
|
texto: "Veja esse app",
|
|
1429
|
-
url: "https://exemplo.com"
|
|
1736
|
+
url: "https://exemplo.com",
|
|
1737
|
+
arquivo: imagem
|
|
1430
1738
|
});`,
|
|
1431
|
-
en: `await
|
|
1739
|
+
en: `const image = await pickImage();
|
|
1740
|
+
|
|
1741
|
+
await share({
|
|
1432
1742
|
text: "Check this app",
|
|
1433
|
-
url: "https://example.com"
|
|
1743
|
+
url: "https://example.com",
|
|
1744
|
+
file: image
|
|
1434
1745
|
});`
|
|
1435
1746
|
}
|
|
1436
1747
|
},
|
|
1748
|
+
{
|
|
1749
|
+
when: { pt: "Para receber dados enviados pelo menu Compartilhar do Android.", en: "To receive data sent through Android's Share menu." },
|
|
1750
|
+
example: {
|
|
1751
|
+
pt: `aoReceberCompartilhamento((dados) => {
|
|
1752
|
+
console.log(dados.tipo, dados.uri || dados.texto);
|
|
1753
|
+
});
|
|
1754
|
+
|
|
1755
|
+
const inicial = await obterCompartilhamentoInicial();`,
|
|
1756
|
+
en: `onShareReceived((data) => {
|
|
1757
|
+
console.log(data.type, data.uri || data.text);
|
|
1758
|
+
});
|
|
1759
|
+
|
|
1760
|
+
const initial = await getInitialShare();`
|
|
1761
|
+
}
|
|
1762
|
+
},
|
|
1763
|
+
{
|
|
1764
|
+
when: { pt: "Para trocar objetos entre dois celulares com apps html2apk abertos.", en: "To exchange objects between two phones running html2apk apps." },
|
|
1765
|
+
example: {
|
|
1766
|
+
pt: `aoConectarBT((dispositivo) => {
|
|
1767
|
+
console.log("Conectado", dispositivo.nome);
|
|
1768
|
+
});
|
|
1769
|
+
|
|
1770
|
+
aoReceberDadosBT((dados) => {
|
|
1771
|
+
console.log("Recebido", dados);
|
|
1772
|
+
});
|
|
1773
|
+
|
|
1774
|
+
aoDarErroBT((erro) => {
|
|
1775
|
+
console.log("Erro Bluetooth", erro.mensagem || erro.message);
|
|
1776
|
+
});
|
|
1777
|
+
|
|
1778
|
+
const lista = await procurarBT();
|
|
1779
|
+
await conectarBT(lista[0].id);
|
|
1780
|
+
await enviarBT({ mensagem: "Ola" });`,
|
|
1781
|
+
en: `onBluetoothConnect((device) => {
|
|
1782
|
+
console.log("Connected", device.name);
|
|
1783
|
+
});
|
|
1784
|
+
|
|
1785
|
+
onBluetoothData((data) => {
|
|
1786
|
+
console.log("Received", data);
|
|
1787
|
+
});
|
|
1788
|
+
|
|
1789
|
+
onBluetoothError((error) => {
|
|
1790
|
+
console.log("Bluetooth error", error.message);
|
|
1791
|
+
});
|
|
1792
|
+
|
|
1793
|
+
const list = await scanBluetooth();
|
|
1794
|
+
await connectBluetooth(list[0].id);
|
|
1795
|
+
await sendBluetooth({ message: "Hello" });`
|
|
1796
|
+
}
|
|
1797
|
+
},
|
|
1798
|
+
{
|
|
1799
|
+
when: { pt: "Para trocar objetos entre dois celulares na mesma rede Wi-Fi ou hotspot.", en: "To exchange objects between two phones on the same Wi-Fi network or hotspot." },
|
|
1800
|
+
example: {
|
|
1801
|
+
pt: `aoConectarWiFi((dispositivo) => {
|
|
1802
|
+
console.log("Conectado por Wi-Fi", dispositivo.nome || dispositivo.host);
|
|
1803
|
+
});
|
|
1804
|
+
|
|
1805
|
+
aoReceberDadosWiFi((dados) => {
|
|
1806
|
+
console.log("Recebido por Wi-Fi", dados);
|
|
1807
|
+
});
|
|
1808
|
+
|
|
1809
|
+
aoDarErroWiFi((erro) => {
|
|
1810
|
+
console.log("Erro Wi-Fi", erro.mensagem || erro.message);
|
|
1811
|
+
});
|
|
1812
|
+
|
|
1813
|
+
const lista = await procurarWiFi();
|
|
1814
|
+
await conectarWiFi(lista[0].id);
|
|
1815
|
+
await enviarWiFi({ mensagem: "Ola por Wi-Fi" });`,
|
|
1816
|
+
en: `onWiFiConnect((device) => {
|
|
1817
|
+
console.log("Connected over Wi-Fi", device.name || device.host);
|
|
1818
|
+
});
|
|
1819
|
+
|
|
1820
|
+
onWiFiData((data) => {
|
|
1821
|
+
console.log("Received over Wi-Fi", data);
|
|
1822
|
+
});
|
|
1823
|
+
|
|
1824
|
+
onWiFiError((error) => {
|
|
1825
|
+
console.log("Wi-Fi error", error.message);
|
|
1826
|
+
});
|
|
1827
|
+
|
|
1828
|
+
const list = await scanWiFi();
|
|
1829
|
+
await connectWiFi(list[0].id);
|
|
1830
|
+
await sendWiFi({ message: "Hello over Wi-Fi" });`
|
|
1831
|
+
}
|
|
1832
|
+
},
|
|
1833
|
+
{
|
|
1834
|
+
when: { pt: "Para reconhecer texto de uma foto sem enviar a imagem para servidor.", en: "To recognize text from a photo without sending the image to a server." },
|
|
1835
|
+
example: {
|
|
1836
|
+
pt: `const imagem = await escolherImagem();
|
|
1837
|
+
const resultado = await ocr(imagem);
|
|
1838
|
+
|
|
1839
|
+
console.log(resultado.texto);`,
|
|
1840
|
+
en: `const image = await pickImage();
|
|
1841
|
+
const result = await recognizeText(image);
|
|
1842
|
+
|
|
1843
|
+
console.log(result.text);`
|
|
1844
|
+
}
|
|
1845
|
+
},
|
|
1846
|
+
{
|
|
1847
|
+
when: { pt: "Para falar texto e ouvir uma frase do usuario.", en: "To speak text and listen to a user phrase." },
|
|
1848
|
+
example: {
|
|
1849
|
+
pt: `await falar("Ola mundo", {
|
|
1850
|
+
idioma: "pt-BR",
|
|
1851
|
+
velocidade: 1
|
|
1852
|
+
});
|
|
1853
|
+
|
|
1854
|
+
const voz = await ouvir({ idioma: "pt-BR" });
|
|
1855
|
+
console.log(voz.texto);`,
|
|
1856
|
+
en: `await speak("Hello world", {
|
|
1857
|
+
language: "en-US",
|
|
1858
|
+
speed: 1
|
|
1859
|
+
});
|
|
1860
|
+
|
|
1861
|
+
const voice = await recognizeSpeech({ language: "en-US" });
|
|
1862
|
+
console.log(voice.text);`
|
|
1863
|
+
}
|
|
1864
|
+
},
|
|
1437
1865
|
{
|
|
1438
1866
|
when: { pt: "Para copiar dados para a area de transferencia ou ler o que esta copiado.", en: "To copy data to the clipboard or read what is copied." },
|
|
1439
1867
|
example: {
|
|
@@ -1618,23 +2046,46 @@ for (const app of result.apps) {
|
|
|
1618
2046
|
}`
|
|
1619
2047
|
}
|
|
1620
2048
|
},
|
|
2049
|
+
{
|
|
2050
|
+
when: { pt: "Para ajustar volume e capturar a tela atual do app.", en: "To adjust volume and capture the current app screen." },
|
|
2051
|
+
example: {
|
|
2052
|
+
pt: `const volume = await volumeAtual();
|
|
2053
|
+
console.log(volume.midia.atual, volume.midia.maximo);
|
|
2054
|
+
|
|
2055
|
+
await definirVolume("midia", 0.5, { mostrarUI: true });
|
|
2056
|
+
|
|
2057
|
+
const imagem = await capturarTela();
|
|
2058
|
+
document.querySelector("img.preview").src = imagem.dataUrl;`,
|
|
2059
|
+
en: `const volume = await getVolume();
|
|
2060
|
+
console.log(volume.music.current, volume.music.max);
|
|
2061
|
+
|
|
2062
|
+
await setVolume("music", 0.5, { showUi: true });
|
|
2063
|
+
|
|
2064
|
+
const image = await captureScreen();
|
|
2065
|
+
document.querySelector("img.preview").src = image.dataUrl;`
|
|
2066
|
+
}
|
|
2067
|
+
},
|
|
1621
2068
|
{
|
|
1622
2069
|
when: { pt: "Para apps que precisam mostrar/esconder o icone flutuante.", en: "For apps that need to show/hide the floating icon." },
|
|
1623
2070
|
example: {
|
|
1624
|
-
pt: `const status = await iniciarIconeFlutuante();
|
|
2071
|
+
pt: `const status = await iniciarIconeFlutuante({ opacidade: 0.85 });
|
|
1625
2072
|
|
|
1626
2073
|
if (status.requiresSettings) {
|
|
1627
2074
|
console.log("O Android abriu a tela de sobreposicao");
|
|
1628
2075
|
}
|
|
1629
2076
|
|
|
2077
|
+
await definirOpacidadeIconeFlutuante(0.55);
|
|
2078
|
+
|
|
1630
2079
|
// Para desligar:
|
|
1631
2080
|
// await pararIconeFlutuante();`,
|
|
1632
|
-
en: `const status = await startFloatingIcon();
|
|
2081
|
+
en: `const status = await startFloatingIcon({ opacity: 0.85 });
|
|
1633
2082
|
|
|
1634
2083
|
if (status.requiresSettings) {
|
|
1635
2084
|
console.log("Android opened the draw-over-apps screen");
|
|
1636
2085
|
}
|
|
1637
2086
|
|
|
2087
|
+
await setFloatingIconOpacity(0.55);
|
|
2088
|
+
|
|
1638
2089
|
// To turn it off:
|
|
1639
2090
|
// await stopFloatingIcon();`
|
|
1640
2091
|
}
|
|
@@ -1708,6 +2159,12 @@ const files = await listFiles();`
|
|
|
1708
2159
|
"relatorio.pdf"
|
|
1709
2160
|
);
|
|
1710
2161
|
|
|
2162
|
+
await baixarArquivo(
|
|
2163
|
+
"https://exemplo.com/foto.png",
|
|
2164
|
+
"foto.png",
|
|
2165
|
+
{ galeria: true }
|
|
2166
|
+
);
|
|
2167
|
+
|
|
1711
2168
|
await abrirArquivo("relatorio.pdf");
|
|
1712
2169
|
// await compartilharArquivo("relatorio.pdf");`,
|
|
1713
2170
|
en: `await downloadFile(
|
|
@@ -1715,6 +2172,12 @@ await abrirArquivo("relatorio.pdf");
|
|
|
1715
2172
|
"report.pdf"
|
|
1716
2173
|
);
|
|
1717
2174
|
|
|
2175
|
+
await downloadFile(
|
|
2176
|
+
"https://example.com/photo.png",
|
|
2177
|
+
"photo.png",
|
|
2178
|
+
{ gallery: true }
|
|
2179
|
+
);
|
|
2180
|
+
|
|
1718
2181
|
await openFile("report.pdf");
|
|
1719
2182
|
// await shareFile("report.pdf");`
|
|
1720
2183
|
}
|
|
@@ -1723,7 +2186,8 @@ await openFile("report.pdf");
|
|
|
1723
2186
|
when: { pt: "Para transformar base64 ou um arquivo escolhido em download com barra de progresso.", en: "To turn base64 or a picked file into a download with a progress bar." },
|
|
1724
2187
|
example: {
|
|
1725
2188
|
pt: `await baixarBase64("pixel.png", base64, {
|
|
1726
|
-
mimeType: "image/png"
|
|
2189
|
+
mimeType: "image/png",
|
|
2190
|
+
galeria: true
|
|
1727
2191
|
});
|
|
1728
2192
|
|
|
1729
2193
|
const arquivo = await escolherArquivo();
|
|
@@ -1731,7 +2195,8 @@ if (arquivo) {
|
|
|
1731
2195
|
await baixarArquivoLocal(arquivo, "copia-" + arquivo.name);
|
|
1732
2196
|
}`,
|
|
1733
2197
|
en: `await downloadBase64("pixel.png", base64, {
|
|
1734
|
-
mimeType: "image/png"
|
|
2198
|
+
mimeType: "image/png",
|
|
2199
|
+
gallery: true
|
|
1735
2200
|
});
|
|
1736
2201
|
|
|
1737
2202
|
const file = await pickFile();
|
|
@@ -1796,6 +2261,83 @@ if (bio.authenticated) {
|
|
|
1796
2261
|
}`
|
|
1797
2262
|
}
|
|
1798
2263
|
},
|
|
2264
|
+
{
|
|
2265
|
+
when: { pt: "Para exigir a senha/PIN/padrao da tela de bloqueio do aparelho.", en: "To require the device's lock screen PIN/pattern/password." },
|
|
2266
|
+
example: {
|
|
2267
|
+
pt: `const auth = await solicitarBloqueio({
|
|
2268
|
+
titulo: "Acesso Restrito",
|
|
2269
|
+
descricao: "Confirme a senha de tela"
|
|
2270
|
+
});
|
|
2271
|
+
|
|
2272
|
+
if (auth.autenticado) {
|
|
2273
|
+
abrirNoApp("#/area-secreta");
|
|
2274
|
+
} else if (!auth.suportado) {
|
|
2275
|
+
toast("Aparelho sem senha configurada");
|
|
2276
|
+
}`,
|
|
2277
|
+
en: `const auth = await requestDeviceLock({
|
|
2278
|
+
title: "Restricted Access",
|
|
2279
|
+
description: "Confirm device password"
|
|
2280
|
+
});
|
|
2281
|
+
|
|
2282
|
+
if (auth.authenticated) {
|
|
2283
|
+
openInApp("#/secret-area");
|
|
2284
|
+
} else if (!auth.supported) {
|
|
2285
|
+
toast("Device has no secure lock screen");
|
|
2286
|
+
}`
|
|
2287
|
+
}
|
|
2288
|
+
},
|
|
2289
|
+
{
|
|
2290
|
+
when: { pt: "Para manter o app ativo em segundo plano ou inicia-lo ao ligar (alarmes, rastreadores).", en: "To keep the app active in background or start on boot (alarms, trackers)." },
|
|
2291
|
+
example: {
|
|
2292
|
+
pt: `const resultado = await solicitarSegundoPlano();
|
|
2293
|
+
|
|
2294
|
+
if (resultado.abriuInicioAutomatico) {
|
|
2295
|
+
toast("Ligue a chave do nosso aplicativo");
|
|
2296
|
+
} else if (resultado.abriuOtimizacaoBateria) {
|
|
2297
|
+
toast("Selecione 'Nao Otimizar' ou 'Sem Restricoes'");
|
|
2298
|
+
} else {
|
|
2299
|
+
toast("Tudo pronto, permissao ja ativa!");
|
|
2300
|
+
}`,
|
|
2301
|
+
en: `const result = await requestBackgroundExecution();
|
|
2302
|
+
|
|
2303
|
+
if (result.openedAutoStart) {
|
|
2304
|
+
toast("Turn on the switch for our application");
|
|
2305
|
+
} else if (result.openedBatteryOptimization) {
|
|
2306
|
+
toast("Select 'No Restrictions' or 'Don't Optimize'");
|
|
2307
|
+
} else {
|
|
2308
|
+
toast("All set, permission already granted!");
|
|
2309
|
+
}`
|
|
2310
|
+
}
|
|
2311
|
+
},
|
|
2312
|
+
{
|
|
2313
|
+
when: { pt: "Para fazer o app abrir sozinho na tela do usuario assim que o celular for ligado.", en: "To make the app automatically open on the user's screen as soon as the device boots." },
|
|
2314
|
+
example: {
|
|
2315
|
+
pt: `// 1. Opcional mas recomendado: pedir permissao de sobreposicao
|
|
2316
|
+
// para nao ser bloqueado no Android 10+
|
|
2317
|
+
await abrirSobreposicao();
|
|
2318
|
+
|
|
2319
|
+
// 2. Ligar a chave
|
|
2320
|
+
await configurarInicioAutomatico(true);
|
|
2321
|
+
|
|
2322
|
+
// 3. Ao iniciar seu app, use o ouvinte para se esconder:
|
|
2323
|
+
aoLigarDispositivo(async () => {
|
|
2324
|
+
console.log("App abriu sozinho pelo boot!");
|
|
2325
|
+
await minimizarApp(); // Esconde a tela na mesma hora
|
|
2326
|
+
});`,
|
|
2327
|
+
en: `// 1. Optional but recommended: request overlay permission
|
|
2328
|
+
// to avoid being blocked on Android 10+
|
|
2329
|
+
await openOverlay();
|
|
2330
|
+
|
|
2331
|
+
// 2. Turn on the switch
|
|
2332
|
+
await setAutoStartOnBoot(true);
|
|
2333
|
+
|
|
2334
|
+
// 3. When starting your app, use the listener:
|
|
2335
|
+
onDeviceBoot(async () => {
|
|
2336
|
+
console.log("App opened automatically by boot!");
|
|
2337
|
+
await minimizeApp(); // Hides the app silently
|
|
2338
|
+
});`
|
|
2339
|
+
}
|
|
2340
|
+
},
|
|
1799
2341
|
{
|
|
1800
2342
|
when: { pt: "Para guardar tokens ou preferencias sensiveis cifradas pelo Android Keystore.", en: "To store tokens or sensitive preferences encrypted by Android Keystore." },
|
|
1801
2343
|
example: {
|
|
@@ -2008,6 +2550,9 @@ function appendLogTo(container, line, kind) {
|
|
|
2008
2550
|
}
|
|
2009
2551
|
|
|
2010
2552
|
container.scrollTop = container.scrollHeight;
|
|
2553
|
+
requestAnimationFrame(() => {
|
|
2554
|
+
container.scrollTop = container.scrollHeight;
|
|
2555
|
+
});
|
|
2011
2556
|
}
|
|
2012
2557
|
|
|
2013
2558
|
function appendLog(line, kind = "raw") {
|
|
@@ -2437,10 +2982,59 @@ async function createNewProjectFile() {
|
|
|
2437
2982
|
}
|
|
2438
2983
|
}
|
|
2439
2984
|
|
|
2985
|
+
const nativeCodeRecipeCache = new Map();
|
|
2986
|
+
|
|
2987
|
+
function localizedRecipeText(value) {
|
|
2988
|
+
if (!value) {
|
|
2989
|
+
return "";
|
|
2990
|
+
}
|
|
2991
|
+
|
|
2992
|
+
if (typeof value === "string") {
|
|
2993
|
+
return value;
|
|
2994
|
+
}
|
|
2995
|
+
|
|
2996
|
+
return [value.pt, value.en].filter(Boolean).join("\n");
|
|
2997
|
+
}
|
|
2998
|
+
|
|
2999
|
+
function recipeSearchText(recipe) {
|
|
3000
|
+
return [
|
|
3001
|
+
localizedRecipeText(recipe && recipe.when),
|
|
3002
|
+
localizedRecipeText(recipe && recipe.example)
|
|
3003
|
+
].join("\n");
|
|
3004
|
+
}
|
|
3005
|
+
|
|
3006
|
+
function primaryCodeKey(entry) {
|
|
3007
|
+
const syntax = entry && entry.syntax
|
|
3008
|
+
? entry.syntax.pt || entry.syntax.en || ""
|
|
3009
|
+
: "";
|
|
3010
|
+
const match = String(syntax).match(/([A-Za-z_$][\w$]*)\s*\(/);
|
|
3011
|
+
return match ? match[1] : "";
|
|
3012
|
+
}
|
|
3013
|
+
|
|
3014
|
+
function findRecipeForEntry(entry) {
|
|
3015
|
+
const key = primaryCodeKey(entry);
|
|
3016
|
+
|
|
3017
|
+
if (!key) {
|
|
3018
|
+
return null;
|
|
3019
|
+
}
|
|
3020
|
+
|
|
3021
|
+
if (nativeCodeRecipeCache.has(key)) {
|
|
3022
|
+
return nativeCodeRecipeCache.get(key);
|
|
3023
|
+
}
|
|
3024
|
+
|
|
3025
|
+
const normalizedKey = key.toLowerCase();
|
|
3026
|
+
const recipe = nativeCodeRecipes.find((item) => {
|
|
3027
|
+
return recipeSearchText(item).toLowerCase().includes(normalizedKey);
|
|
3028
|
+
}) || null;
|
|
3029
|
+
|
|
3030
|
+
nativeCodeRecipeCache.set(key, recipe);
|
|
3031
|
+
return recipe;
|
|
3032
|
+
}
|
|
3033
|
+
|
|
2440
3034
|
function recipeForCode(index) {
|
|
2441
3035
|
const language = currentLanguage();
|
|
2442
3036
|
const entry = nativeCodeEntries[index] || {};
|
|
2443
|
-
const recipe = entry.recipe ||
|
|
3037
|
+
const recipe = entry.recipe || findRecipeForEntry(entry) || {};
|
|
2444
3038
|
return {
|
|
2445
3039
|
when: recipe.when ? recipe.when[language] || recipe.when.pt : "",
|
|
2446
3040
|
example: recipe.example ? recipe.example[language] || recipe.example.pt : ""
|
|
@@ -3603,7 +4197,7 @@ async function init() {
|
|
|
3603
4197
|
elements.iconPreview.src = iconPreviewPath(state.defaultIconPath);
|
|
3604
4198
|
}
|
|
3605
4199
|
} catch {
|
|
3606
|
-
elements.appVersion.textContent = "
|
|
4200
|
+
elements.appVersion.textContent = "v12.0.0";
|
|
3607
4201
|
}
|
|
3608
4202
|
|
|
3609
4203
|
setTimeout(finishBoot, 1800);
|