html2apk 0.9.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 +217 -5
- package/package.json +15 -4
- package/src/desktop/main.js +551 -25
- package/src/desktop/renderer/index.html +1 -1
- package/src/desktop/renderer/renderer.js +517 -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 +33 -0
- package/src/templates/cordova-plugin-html2apk-bridge/src/android/FloatingIconService.java +33 -0
- package/src/templates/cordova-plugin-html2apk-bridge/src/android/Html2ApkBridge.java +5187 -1634
- package/src/templates/cordova-plugin-html2apk-bridge/www/html2apk-bridge.js +485 -12
- package/src/templates/html2apk-early-bridge.js +485 -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,90 @@ 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
|
+
},
|
|
585
676
|
{
|
|
586
677
|
syntax: { pt: "obterLinkInicial() / aoAbrirLink(fn)", en: "getInitialLink() / onOpenLink(fn)" },
|
|
587
678
|
java: "getInitialLink",
|
|
@@ -619,15 +710,15 @@ const nativeCodeEntries = [
|
|
|
619
710
|
},
|
|
620
711
|
{
|
|
621
712
|
syntax: { pt: "escolherImagem()", en: "pickImage()" },
|
|
622
|
-
java: "
|
|
623
|
-
description: { pt: "Abre o
|
|
713
|
+
java: "Photo Picker / ACTION_OPEN_DOCUMENT",
|
|
714
|
+
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
715
|
returns: { pt: "{ uri, name, nome, size, tamanho, mimeType } ou null.", en: "{ uri, name, nome, size, tamanho, mimeType } or null." },
|
|
625
|
-
handling: { pt: "
|
|
716
|
+
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
717
|
},
|
|
627
718
|
{
|
|
628
|
-
syntax: { pt: "escolherImagens({
|
|
629
|
-
java: "
|
|
630
|
-
description: { pt: "Abre
|
|
719
|
+
syntax: { pt: "escolherImagens({ multiplas: true })", en: "pickImages({ multiple: true })" },
|
|
720
|
+
java: "Photo Picker / ACTION_OPEN_DOCUMENT",
|
|
721
|
+
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
722
|
returns: { pt: "Array de arquivos; vazio se o usuario cancelar.", en: "Array of files; empty when the user cancels." },
|
|
632
723
|
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
724
|
},
|
|
@@ -656,7 +747,7 @@ const nativeCodeEntries = [
|
|
|
656
747
|
syntax: { pt: "escolherPasta()", en: "pickFolder()" },
|
|
657
748
|
java: "pickFolder",
|
|
658
749
|
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." },
|
|
750
|
+
returns: { pt: "{ uri, nome } ou objeto vazio se cancelar.", en: "{ uri, name } or an empty object when canceled." },
|
|
660
751
|
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
752
|
},
|
|
662
753
|
{
|
|
@@ -667,11 +758,60 @@ const nativeCodeEntries = [
|
|
|
667
758
|
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
759
|
},
|
|
669
760
|
{
|
|
670
|
-
syntax: { pt: "compartilhar({ texto, url })", en: "share({ text, url })" },
|
|
761
|
+
syntax: { pt: "compartilhar({ texto, url, arquivo, arquivos })", en: "share({ text, url, file, files })" },
|
|
671
762
|
java: "share",
|
|
672
|
-
description: { pt: "Abre a folha nativa
|
|
673
|
-
returns: { pt: "
|
|
674
|
-
handling: { pt: "
|
|
763
|
+
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." },
|
|
764
|
+
returns: { pt: "{ ok, shared, items, mimeType }.", en: "{ ok, shared, items, mimeType }." },
|
|
765
|
+
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." }
|
|
766
|
+
},
|
|
767
|
+
{
|
|
768
|
+
syntax: { pt: "aoReceberCompartilhamento(callback)", en: "onShareReceived(callback)" },
|
|
769
|
+
java: "ACTION_SEND / ACTION_SEND_MULTIPLE",
|
|
770
|
+
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." },
|
|
771
|
+
returns: { pt: "Callback recebe { tipo, uri, mimeType, texto, items }.", en: "Callback receives { type, uri, mimeType, text, items }." },
|
|
772
|
+
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." }
|
|
773
|
+
},
|
|
774
|
+
{
|
|
775
|
+
syntax: { pt: "procurarBT() / conectarBT(id) / enviarBT(objeto) / aoDarErroBT(callback)", en: "scanBluetooth() / connectBluetooth(id) / sendBluetooth(object) / onBluetoothError(callback)" },
|
|
776
|
+
java: "Bluetooth RFCOMM",
|
|
777
|
+
description: { pt: "Comunica dois apps html2apk por Bluetooth classico usando JSON.", en: "Communicates two html2apk apps over classic Bluetooth using JSON." },
|
|
778
|
+
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." },
|
|
779
|
+
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." }
|
|
780
|
+
},
|
|
781
|
+
{
|
|
782
|
+
syntax: { pt: "procurarWiFi() / conectarWiFi(id) / enviarWiFi(objeto) / aoDarErroWiFi(callback)", en: "scanWiFi() / connectWiFi(id) / sendWiFi(object) / onWiFiError(callback)" },
|
|
783
|
+
java: "NSD + Socket TCP local",
|
|
784
|
+
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." },
|
|
785
|
+
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." },
|
|
786
|
+
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." }
|
|
787
|
+
},
|
|
788
|
+
{
|
|
789
|
+
syntax: { pt: "ocr(imagem)", en: "recognizeText(image)" },
|
|
790
|
+
java: "ML Kit TextRecognition local",
|
|
791
|
+
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." },
|
|
792
|
+
returns: { pt: "{ texto, text, offline, blocks }.", en: "{ texto, text, offline, blocks }." },
|
|
793
|
+
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." }
|
|
794
|
+
},
|
|
795
|
+
{
|
|
796
|
+
syntax: { pt: "falar('Ola', { idioma: 'pt-BR', velocidade: 1 }) / pararFala()", en: "speak('Hello', { language: 'en-US', speed: 1 }) / stopSpeaking()" },
|
|
797
|
+
java: "TextToSpeech",
|
|
798
|
+
description: { pt: "Usa o motor TTS instalado no Android para falar texto.", en: "Uses Android's installed TTS engine to speak text." },
|
|
799
|
+
returns: { pt: "{ ok, speaking, idioma, velocidade }.", en: "{ ok, speaking, language, speed }." },
|
|
800
|
+
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." }
|
|
801
|
+
},
|
|
802
|
+
{
|
|
803
|
+
syntax: { pt: "ouvir({ idioma: 'pt-BR' })", en: "recognizeSpeech({ language: 'en-US' })" },
|
|
804
|
+
java: "RecognizerIntent",
|
|
805
|
+
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." },
|
|
806
|
+
returns: { pt: "{ texto, resultados, confidence }.", en: "{ text, results, confidence }." },
|
|
807
|
+
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." }
|
|
808
|
+
},
|
|
809
|
+
{
|
|
810
|
+
syntax: { pt: "share_me() / compartilharApp()", en: "share_me() / shareApp()" },
|
|
811
|
+
java: "shareCurrentApp",
|
|
812
|
+
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." },
|
|
813
|
+
returns: { pt: "{ shared, name, uri, size, packageName, splitApks, installableAsSingleApk }.", en: "{ shared, name, uri, size, packageName, splitApks, installableAsSingleApk }." },
|
|
814
|
+
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
815
|
},
|
|
676
816
|
{
|
|
677
817
|
syntax: { pt: "copiarTexto('texto') / lerTextoCopiado()", en: "copyText('text') / readText()" },
|
|
@@ -739,7 +879,7 @@ const nativeCodeEntries = [
|
|
|
739
879
|
{
|
|
740
880
|
syntax: { pt: "infoRede() / infoBateria()", en: "networkInfo() / batteryInfo()" },
|
|
741
881
|
java: "networkInfo/batteryInfo",
|
|
742
|
-
description: { pt: "Consulta
|
|
882
|
+
description: { pt: "Consulta conexão atual e bateria.", en: "Reads current connection and battery." },
|
|
743
883
|
returns: { pt: "rede: { online, tipo/type }; bateria: { level, charging }.", en: "network: { online, tipo/type }; battery: { level, charging }." },
|
|
744
884
|
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
885
|
},
|
|
@@ -774,9 +914,16 @@ const nativeCodeEntries = [
|
|
|
774
914
|
{
|
|
775
915
|
syntax: { pt: "iniciarIconeFlutuante() / pararIconeFlutuante()", en: "startFloatingIcon() / stopFloatingIcon()" },
|
|
776
916
|
java: "FloatingIconService",
|
|
777
|
-
description: { pt: "Controla o
|
|
778
|
-
returns: { pt: "
|
|
779
|
-
handling: { pt: "`iniciarIconeFlutuante()` abre a tela de
|
|
917
|
+
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." },
|
|
918
|
+
returns: { pt: "{ granted, requiresSettings, opacity } para iniciar/configurar; `pararIconeFlutuante` finaliza o serviço.", en: "{ granted, requiresSettings, opacity } for start/configure; `stopFloatingIcon` stops the service." },
|
|
919
|
+
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." }
|
|
920
|
+
},
|
|
921
|
+
{
|
|
922
|
+
syntax: { pt: "minimizarApp() / fecharApp()", en: "minimizeApp() / closeApp()" },
|
|
923
|
+
java: "Activity",
|
|
924
|
+
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." },
|
|
925
|
+
returns: { pt: "`minimizarApp`: { minimizado }. `fecharApp`: { fechado } antes de finalizar.", en: "`minimizeApp`: { minimized }. `closeApp`: { closed } before finishing." },
|
|
926
|
+
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
927
|
},
|
|
781
928
|
{
|
|
782
929
|
syntax: { pt: "tirarFoto() / capturarVideo()", en: "takePhoto() / captureVideo()" },
|
|
@@ -800,18 +947,18 @@ const nativeCodeEntries = [
|
|
|
800
947
|
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
948
|
},
|
|
802
949
|
{
|
|
803
|
-
syntax: { pt: "baixarArquivo(url, '
|
|
950
|
+
syntax: { pt: "baixarArquivo(url, 'foto.png', { galeria: true }) / abrirArquivo('foto.png')", en: "downloadFile(url, 'photo.png', { gallery: true }) / openFile('photo.png')" },
|
|
804
951
|
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: "
|
|
952
|
+
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." },
|
|
953
|
+
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`." },
|
|
954
|
+
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
955
|
},
|
|
809
956
|
{
|
|
810
|
-
syntax: { pt: "baixarBase64('foto.png', base64) / baixarArquivoLocal(arquivo, 'copia.pdf')", en: "downloadBase64('photo.png', base64) / downloadLocalFile(file, 'copy.pdf')" },
|
|
957
|
+
syntax: { pt: "baixarBase64('foto.png', base64, { galeria: true }) / baixarArquivoLocal(arquivo, 'copia.pdf')", en: "downloadBase64('photo.png', base64, { gallery: true }) / downloadLocalFile(file, 'copy.pdf')" },
|
|
811
958
|
java: "InputStream + NotificationCompat",
|
|
812
959
|
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: "
|
|
960
|
+
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." },
|
|
961
|
+
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
962
|
},
|
|
816
963
|
{
|
|
817
964
|
syntax: { pt: "obterLocalizacao() / acompanharLocalizacao()", en: "getLocation() / watchLocation()" },
|
|
@@ -885,6 +1032,7 @@ if (!info.videoSupported) {
|
|
|
885
1032
|
];
|
|
886
1033
|
|
|
887
1034
|
[
|
|
1035
|
+
"feedback",
|
|
888
1036
|
"feedback",
|
|
889
1037
|
"feedback",
|
|
890
1038
|
"notifications",
|
|
@@ -909,6 +1057,12 @@ if (!info.videoSupported) {
|
|
|
909
1057
|
"files",
|
|
910
1058
|
"share",
|
|
911
1059
|
"share",
|
|
1060
|
+
"share",
|
|
1061
|
+
"media",
|
|
1062
|
+
"media",
|
|
1063
|
+
"media",
|
|
1064
|
+
"share",
|
|
1065
|
+
"share",
|
|
912
1066
|
"device",
|
|
913
1067
|
"device",
|
|
914
1068
|
"device",
|
|
@@ -962,6 +1116,17 @@ const nativeCodeRecipes = [
|
|
|
962
1116
|
en: `await vibrate(250);`
|
|
963
1117
|
}
|
|
964
1118
|
},
|
|
1119
|
+
{
|
|
1120
|
+
when: { pt: "Para esperar entre duas linhas sem bloquear a interface.", en: "To wait between two lines without blocking the interface." },
|
|
1121
|
+
example: {
|
|
1122
|
+
pt: `await toast("Comecando");
|
|
1123
|
+
await aguardar(5000);
|
|
1124
|
+
await toast("Continuando");`,
|
|
1125
|
+
en: `await toast("Starting");
|
|
1126
|
+
await loading(5000);
|
|
1127
|
+
await toast("Continuing");`
|
|
1128
|
+
}
|
|
1129
|
+
},
|
|
965
1130
|
{
|
|
966
1131
|
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
1132
|
example: {
|
|
@@ -1186,6 +1351,111 @@ console.log(permissions);`
|
|
|
1186
1351
|
// stop();`
|
|
1187
1352
|
}
|
|
1188
1353
|
},
|
|
1354
|
+
{
|
|
1355
|
+
when: { pt: "Para reagir a cabo/dispositivo USB e notificacoes locais.", en: "To react to USB cable/device changes and local notifications." },
|
|
1356
|
+
example: {
|
|
1357
|
+
pt: `aoConectarUSB((dados) => {
|
|
1358
|
+
console.log("USB conectado", dados.origem, dados.dispositivo);
|
|
1359
|
+
});
|
|
1360
|
+
|
|
1361
|
+
aoDesconectarUSB(() => {
|
|
1362
|
+
console.log("USB desconectado");
|
|
1363
|
+
});
|
|
1364
|
+
|
|
1365
|
+
aoReceberNotificacao((dados) => {
|
|
1366
|
+
console.log("Notificação recebida", dados.titulo || dados.title);
|
|
1367
|
+
});`,
|
|
1368
|
+
en: `onUSBConnect((data) => {
|
|
1369
|
+
console.log("USB connected", data.source, data.device);
|
|
1370
|
+
});
|
|
1371
|
+
|
|
1372
|
+
onUSBDisconnect(() => {
|
|
1373
|
+
console.log("USB disconnected");
|
|
1374
|
+
});
|
|
1375
|
+
|
|
1376
|
+
onNotificationReceived((data) => {
|
|
1377
|
+
console.log("Notification received", data.title);
|
|
1378
|
+
});`
|
|
1379
|
+
}
|
|
1380
|
+
},
|
|
1381
|
+
{
|
|
1382
|
+
when: { pt: "Para reagir a fone, volume, teclado e orientação da tela.", en: "To react to headphones, volume, keyboard and screen orientation." },
|
|
1383
|
+
example: {
|
|
1384
|
+
pt: `aoConectarFone((dados) => {
|
|
1385
|
+
console.log("Fone conectado", dados.dispositivo);
|
|
1386
|
+
});
|
|
1387
|
+
|
|
1388
|
+
aoMudarVolume((dados) => {
|
|
1389
|
+
console.log("Volume de mídia", dados.midia.atual);
|
|
1390
|
+
});
|
|
1391
|
+
|
|
1392
|
+
aoAbrirTeclado((dados) => {
|
|
1393
|
+
console.log("Teclado abriu", dados.alturaTeclado);
|
|
1394
|
+
});
|
|
1395
|
+
|
|
1396
|
+
aoMudarOrientacao((dados) => {
|
|
1397
|
+
console.log("Orientação", dados.orientacao);
|
|
1398
|
+
});`,
|
|
1399
|
+
en: `onHeadphoneConnect((data) => {
|
|
1400
|
+
console.log("Headphone connected", data.device);
|
|
1401
|
+
});
|
|
1402
|
+
|
|
1403
|
+
onVolumeChange((data) => {
|
|
1404
|
+
console.log("Media volume", data.music.current);
|
|
1405
|
+
});
|
|
1406
|
+
|
|
1407
|
+
onKeyboardOpen((data) => {
|
|
1408
|
+
console.log("Keyboard opened", data.keyboardHeight);
|
|
1409
|
+
});
|
|
1410
|
+
|
|
1411
|
+
onOrientationChange((data) => {
|
|
1412
|
+
console.log("Orientation", data.orientation);
|
|
1413
|
+
});`
|
|
1414
|
+
}
|
|
1415
|
+
},
|
|
1416
|
+
{
|
|
1417
|
+
when: { pt: "Para interações legais com sensores, print e NFC.", en: "For playful interactions with sensors, screenshots and NFC." },
|
|
1418
|
+
example: {
|
|
1419
|
+
pt: `aoSacudirCelular((dados) => {
|
|
1420
|
+
console.log("Sacudiu", dados.forca);
|
|
1421
|
+
});
|
|
1422
|
+
|
|
1423
|
+
aoVirarCelularParaBaixo(() => {
|
|
1424
|
+
console.log("Tela virada para baixo");
|
|
1425
|
+
});
|
|
1426
|
+
|
|
1427
|
+
aoAproximarObjeto((dados) => {
|
|
1428
|
+
console.log("Algo chegou perto", dados.distancia);
|
|
1429
|
+
});
|
|
1430
|
+
|
|
1431
|
+
aoTirarPrint((dados) => {
|
|
1432
|
+
console.log("Print detectado", dados.uri);
|
|
1433
|
+
});
|
|
1434
|
+
|
|
1435
|
+
aoNFC((dados) => {
|
|
1436
|
+
console.log("Tag NFC", dados.id, dados.mensagens);
|
|
1437
|
+
});`,
|
|
1438
|
+
en: `onPhoneShake((data) => {
|
|
1439
|
+
console.log("Shaken", data.force);
|
|
1440
|
+
});
|
|
1441
|
+
|
|
1442
|
+
onPhoneFaceDown(() => {
|
|
1443
|
+
console.log("Phone is face down");
|
|
1444
|
+
});
|
|
1445
|
+
|
|
1446
|
+
onProximityNear((data) => {
|
|
1447
|
+
console.log("Something is near", data.distance);
|
|
1448
|
+
});
|
|
1449
|
+
|
|
1450
|
+
onScreenshot((data) => {
|
|
1451
|
+
console.log("Screenshot detected", data.uri);
|
|
1452
|
+
});
|
|
1453
|
+
|
|
1454
|
+
onNFC((data) => {
|
|
1455
|
+
console.log("NFC tag", data.id, data.messages);
|
|
1456
|
+
});`
|
|
1457
|
+
}
|
|
1458
|
+
},
|
|
1189
1459
|
{
|
|
1190
1460
|
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
1461
|
example: {
|
|
@@ -1318,7 +1588,7 @@ if (image) {
|
|
|
1318
1588
|
{
|
|
1319
1589
|
when: { pt: "Para galeria com selecao multipla, como anexar varias fotos.", en: "For multiple gallery selection, such as attaching many photos." },
|
|
1320
1590
|
example: {
|
|
1321
|
-
pt: `const imagens = await escolherImagens({
|
|
1591
|
+
pt: `const imagens = await escolherImagens({ multiplas: true });
|
|
1322
1592
|
|
|
1323
1593
|
for (const imagem of imagens) {
|
|
1324
1594
|
console.log(imagem.nome, imagem.tamanho);
|
|
@@ -1422,18 +1692,141 @@ if (saved.saved) {
|
|
|
1422
1692
|
}
|
|
1423
1693
|
},
|
|
1424
1694
|
{
|
|
1425
|
-
when: { pt: "Para abrir o compartilhamento nativo do Android com texto e
|
|
1695
|
+
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
1696
|
example: {
|
|
1427
|
-
pt: `await
|
|
1697
|
+
pt: `const imagem = await escolherImagem();
|
|
1698
|
+
|
|
1699
|
+
await compartilhar({
|
|
1428
1700
|
texto: "Veja esse app",
|
|
1429
|
-
url: "https://exemplo.com"
|
|
1701
|
+
url: "https://exemplo.com",
|
|
1702
|
+
arquivo: imagem
|
|
1430
1703
|
});`,
|
|
1431
|
-
en: `await
|
|
1704
|
+
en: `const image = await pickImage();
|
|
1705
|
+
|
|
1706
|
+
await share({
|
|
1432
1707
|
text: "Check this app",
|
|
1433
|
-
url: "https://example.com"
|
|
1708
|
+
url: "https://example.com",
|
|
1709
|
+
file: image
|
|
1434
1710
|
});`
|
|
1435
1711
|
}
|
|
1436
1712
|
},
|
|
1713
|
+
{
|
|
1714
|
+
when: { pt: "Para receber dados enviados pelo menu Compartilhar do Android.", en: "To receive data sent through Android's Share menu." },
|
|
1715
|
+
example: {
|
|
1716
|
+
pt: `aoReceberCompartilhamento((dados) => {
|
|
1717
|
+
console.log(dados.tipo, dados.uri || dados.texto);
|
|
1718
|
+
});
|
|
1719
|
+
|
|
1720
|
+
const inicial = await obterCompartilhamentoInicial();`,
|
|
1721
|
+
en: `onShareReceived((data) => {
|
|
1722
|
+
console.log(data.type, data.uri || data.text);
|
|
1723
|
+
});
|
|
1724
|
+
|
|
1725
|
+
const initial = await getInitialShare();`
|
|
1726
|
+
}
|
|
1727
|
+
},
|
|
1728
|
+
{
|
|
1729
|
+
when: { pt: "Para trocar objetos entre dois celulares com apps html2apk abertos.", en: "To exchange objects between two phones running html2apk apps." },
|
|
1730
|
+
example: {
|
|
1731
|
+
pt: `aoConectarBT((dispositivo) => {
|
|
1732
|
+
console.log("Conectado", dispositivo.nome);
|
|
1733
|
+
});
|
|
1734
|
+
|
|
1735
|
+
aoReceberDadosBT((dados) => {
|
|
1736
|
+
console.log("Recebido", dados);
|
|
1737
|
+
});
|
|
1738
|
+
|
|
1739
|
+
aoDarErroBT((erro) => {
|
|
1740
|
+
console.log("Erro Bluetooth", erro.mensagem || erro.message);
|
|
1741
|
+
});
|
|
1742
|
+
|
|
1743
|
+
const lista = await procurarBT();
|
|
1744
|
+
await conectarBT(lista[0].id);
|
|
1745
|
+
await enviarBT({ mensagem: "Ola" });`,
|
|
1746
|
+
en: `onBluetoothConnect((device) => {
|
|
1747
|
+
console.log("Connected", device.name);
|
|
1748
|
+
});
|
|
1749
|
+
|
|
1750
|
+
onBluetoothData((data) => {
|
|
1751
|
+
console.log("Received", data);
|
|
1752
|
+
});
|
|
1753
|
+
|
|
1754
|
+
onBluetoothError((error) => {
|
|
1755
|
+
console.log("Bluetooth error", error.message);
|
|
1756
|
+
});
|
|
1757
|
+
|
|
1758
|
+
const list = await scanBluetooth();
|
|
1759
|
+
await connectBluetooth(list[0].id);
|
|
1760
|
+
await sendBluetooth({ message: "Hello" });`
|
|
1761
|
+
}
|
|
1762
|
+
},
|
|
1763
|
+
{
|
|
1764
|
+
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." },
|
|
1765
|
+
example: {
|
|
1766
|
+
pt: `aoConectarWiFi((dispositivo) => {
|
|
1767
|
+
console.log("Conectado por Wi-Fi", dispositivo.nome || dispositivo.host);
|
|
1768
|
+
});
|
|
1769
|
+
|
|
1770
|
+
aoReceberDadosWiFi((dados) => {
|
|
1771
|
+
console.log("Recebido por Wi-Fi", dados);
|
|
1772
|
+
});
|
|
1773
|
+
|
|
1774
|
+
aoDarErroWiFi((erro) => {
|
|
1775
|
+
console.log("Erro Wi-Fi", erro.mensagem || erro.message);
|
|
1776
|
+
});
|
|
1777
|
+
|
|
1778
|
+
const lista = await procurarWiFi();
|
|
1779
|
+
await conectarWiFi(lista[0].id);
|
|
1780
|
+
await enviarWiFi({ mensagem: "Ola por Wi-Fi" });`,
|
|
1781
|
+
en: `onWiFiConnect((device) => {
|
|
1782
|
+
console.log("Connected over Wi-Fi", device.name || device.host);
|
|
1783
|
+
});
|
|
1784
|
+
|
|
1785
|
+
onWiFiData((data) => {
|
|
1786
|
+
console.log("Received over Wi-Fi", data);
|
|
1787
|
+
});
|
|
1788
|
+
|
|
1789
|
+
onWiFiError((error) => {
|
|
1790
|
+
console.log("Wi-Fi error", error.message);
|
|
1791
|
+
});
|
|
1792
|
+
|
|
1793
|
+
const list = await scanWiFi();
|
|
1794
|
+
await connectWiFi(list[0].id);
|
|
1795
|
+
await sendWiFi({ message: "Hello over Wi-Fi" });`
|
|
1796
|
+
}
|
|
1797
|
+
},
|
|
1798
|
+
{
|
|
1799
|
+
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." },
|
|
1800
|
+
example: {
|
|
1801
|
+
pt: `const imagem = await escolherImagem();
|
|
1802
|
+
const resultado = await ocr(imagem);
|
|
1803
|
+
|
|
1804
|
+
console.log(resultado.texto);`,
|
|
1805
|
+
en: `const image = await pickImage();
|
|
1806
|
+
const result = await recognizeText(image);
|
|
1807
|
+
|
|
1808
|
+
console.log(result.text);`
|
|
1809
|
+
}
|
|
1810
|
+
},
|
|
1811
|
+
{
|
|
1812
|
+
when: { pt: "Para falar texto e ouvir uma frase do usuario.", en: "To speak text and listen to a user phrase." },
|
|
1813
|
+
example: {
|
|
1814
|
+
pt: `await falar("Ola mundo", {
|
|
1815
|
+
idioma: "pt-BR",
|
|
1816
|
+
velocidade: 1
|
|
1817
|
+
});
|
|
1818
|
+
|
|
1819
|
+
const voz = await ouvir({ idioma: "pt-BR" });
|
|
1820
|
+
console.log(voz.texto);`,
|
|
1821
|
+
en: `await speak("Hello world", {
|
|
1822
|
+
language: "en-US",
|
|
1823
|
+
speed: 1
|
|
1824
|
+
});
|
|
1825
|
+
|
|
1826
|
+
const voice = await recognizeSpeech({ language: "en-US" });
|
|
1827
|
+
console.log(voice.text);`
|
|
1828
|
+
}
|
|
1829
|
+
},
|
|
1437
1830
|
{
|
|
1438
1831
|
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
1832
|
example: {
|
|
@@ -1618,23 +2011,46 @@ for (const app of result.apps) {
|
|
|
1618
2011
|
}`
|
|
1619
2012
|
}
|
|
1620
2013
|
},
|
|
2014
|
+
{
|
|
2015
|
+
when: { pt: "Para ajustar volume e capturar a tela atual do app.", en: "To adjust volume and capture the current app screen." },
|
|
2016
|
+
example: {
|
|
2017
|
+
pt: `const volume = await volumeAtual();
|
|
2018
|
+
console.log(volume.midia.atual, volume.midia.maximo);
|
|
2019
|
+
|
|
2020
|
+
await definirVolume("midia", 0.5, { mostrarUI: true });
|
|
2021
|
+
|
|
2022
|
+
const imagem = await capturarTela();
|
|
2023
|
+
document.querySelector("img.preview").src = imagem.dataUrl;`,
|
|
2024
|
+
en: `const volume = await getVolume();
|
|
2025
|
+
console.log(volume.music.current, volume.music.max);
|
|
2026
|
+
|
|
2027
|
+
await setVolume("music", 0.5, { showUi: true });
|
|
2028
|
+
|
|
2029
|
+
const image = await captureScreen();
|
|
2030
|
+
document.querySelector("img.preview").src = image.dataUrl;`
|
|
2031
|
+
}
|
|
2032
|
+
},
|
|
1621
2033
|
{
|
|
1622
2034
|
when: { pt: "Para apps que precisam mostrar/esconder o icone flutuante.", en: "For apps that need to show/hide the floating icon." },
|
|
1623
2035
|
example: {
|
|
1624
|
-
pt: `const status = await iniciarIconeFlutuante();
|
|
2036
|
+
pt: `const status = await iniciarIconeFlutuante({ opacidade: 0.85 });
|
|
1625
2037
|
|
|
1626
2038
|
if (status.requiresSettings) {
|
|
1627
2039
|
console.log("O Android abriu a tela de sobreposicao");
|
|
1628
2040
|
}
|
|
1629
2041
|
|
|
2042
|
+
await definirOpacidadeIconeFlutuante(0.55);
|
|
2043
|
+
|
|
1630
2044
|
// Para desligar:
|
|
1631
2045
|
// await pararIconeFlutuante();`,
|
|
1632
|
-
en: `const status = await startFloatingIcon();
|
|
2046
|
+
en: `const status = await startFloatingIcon({ opacity: 0.85 });
|
|
1633
2047
|
|
|
1634
2048
|
if (status.requiresSettings) {
|
|
1635
2049
|
console.log("Android opened the draw-over-apps screen");
|
|
1636
2050
|
}
|
|
1637
2051
|
|
|
2052
|
+
await setFloatingIconOpacity(0.55);
|
|
2053
|
+
|
|
1638
2054
|
// To turn it off:
|
|
1639
2055
|
// await stopFloatingIcon();`
|
|
1640
2056
|
}
|
|
@@ -1708,6 +2124,12 @@ const files = await listFiles();`
|
|
|
1708
2124
|
"relatorio.pdf"
|
|
1709
2125
|
);
|
|
1710
2126
|
|
|
2127
|
+
await baixarArquivo(
|
|
2128
|
+
"https://exemplo.com/foto.png",
|
|
2129
|
+
"foto.png",
|
|
2130
|
+
{ galeria: true }
|
|
2131
|
+
);
|
|
2132
|
+
|
|
1711
2133
|
await abrirArquivo("relatorio.pdf");
|
|
1712
2134
|
// await compartilharArquivo("relatorio.pdf");`,
|
|
1713
2135
|
en: `await downloadFile(
|
|
@@ -1715,6 +2137,12 @@ await abrirArquivo("relatorio.pdf");
|
|
|
1715
2137
|
"report.pdf"
|
|
1716
2138
|
);
|
|
1717
2139
|
|
|
2140
|
+
await downloadFile(
|
|
2141
|
+
"https://example.com/photo.png",
|
|
2142
|
+
"photo.png",
|
|
2143
|
+
{ gallery: true }
|
|
2144
|
+
);
|
|
2145
|
+
|
|
1718
2146
|
await openFile("report.pdf");
|
|
1719
2147
|
// await shareFile("report.pdf");`
|
|
1720
2148
|
}
|
|
@@ -1723,7 +2151,8 @@ await openFile("report.pdf");
|
|
|
1723
2151
|
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
2152
|
example: {
|
|
1725
2153
|
pt: `await baixarBase64("pixel.png", base64, {
|
|
1726
|
-
mimeType: "image/png"
|
|
2154
|
+
mimeType: "image/png",
|
|
2155
|
+
galeria: true
|
|
1727
2156
|
});
|
|
1728
2157
|
|
|
1729
2158
|
const arquivo = await escolherArquivo();
|
|
@@ -1731,7 +2160,8 @@ if (arquivo) {
|
|
|
1731
2160
|
await baixarArquivoLocal(arquivo, "copia-" + arquivo.name);
|
|
1732
2161
|
}`,
|
|
1733
2162
|
en: `await downloadBase64("pixel.png", base64, {
|
|
1734
|
-
mimeType: "image/png"
|
|
2163
|
+
mimeType: "image/png",
|
|
2164
|
+
gallery: true
|
|
1735
2165
|
});
|
|
1736
2166
|
|
|
1737
2167
|
const file = await pickFile();
|
|
@@ -2008,6 +2438,9 @@ function appendLogTo(container, line, kind) {
|
|
|
2008
2438
|
}
|
|
2009
2439
|
|
|
2010
2440
|
container.scrollTop = container.scrollHeight;
|
|
2441
|
+
requestAnimationFrame(() => {
|
|
2442
|
+
container.scrollTop = container.scrollHeight;
|
|
2443
|
+
});
|
|
2011
2444
|
}
|
|
2012
2445
|
|
|
2013
2446
|
function appendLog(line, kind = "raw") {
|
|
@@ -2437,10 +2870,59 @@ async function createNewProjectFile() {
|
|
|
2437
2870
|
}
|
|
2438
2871
|
}
|
|
2439
2872
|
|
|
2873
|
+
const nativeCodeRecipeCache = new Map();
|
|
2874
|
+
|
|
2875
|
+
function localizedRecipeText(value) {
|
|
2876
|
+
if (!value) {
|
|
2877
|
+
return "";
|
|
2878
|
+
}
|
|
2879
|
+
|
|
2880
|
+
if (typeof value === "string") {
|
|
2881
|
+
return value;
|
|
2882
|
+
}
|
|
2883
|
+
|
|
2884
|
+
return [value.pt, value.en].filter(Boolean).join("\n");
|
|
2885
|
+
}
|
|
2886
|
+
|
|
2887
|
+
function recipeSearchText(recipe) {
|
|
2888
|
+
return [
|
|
2889
|
+
localizedRecipeText(recipe && recipe.when),
|
|
2890
|
+
localizedRecipeText(recipe && recipe.example)
|
|
2891
|
+
].join("\n");
|
|
2892
|
+
}
|
|
2893
|
+
|
|
2894
|
+
function primaryCodeKey(entry) {
|
|
2895
|
+
const syntax = entry && entry.syntax
|
|
2896
|
+
? entry.syntax.pt || entry.syntax.en || ""
|
|
2897
|
+
: "";
|
|
2898
|
+
const match = String(syntax).match(/([A-Za-z_$][\w$]*)\s*\(/);
|
|
2899
|
+
return match ? match[1] : "";
|
|
2900
|
+
}
|
|
2901
|
+
|
|
2902
|
+
function findRecipeForEntry(entry) {
|
|
2903
|
+
const key = primaryCodeKey(entry);
|
|
2904
|
+
|
|
2905
|
+
if (!key) {
|
|
2906
|
+
return null;
|
|
2907
|
+
}
|
|
2908
|
+
|
|
2909
|
+
if (nativeCodeRecipeCache.has(key)) {
|
|
2910
|
+
return nativeCodeRecipeCache.get(key);
|
|
2911
|
+
}
|
|
2912
|
+
|
|
2913
|
+
const normalizedKey = key.toLowerCase();
|
|
2914
|
+
const recipe = nativeCodeRecipes.find((item) => {
|
|
2915
|
+
return recipeSearchText(item).toLowerCase().includes(normalizedKey);
|
|
2916
|
+
}) || null;
|
|
2917
|
+
|
|
2918
|
+
nativeCodeRecipeCache.set(key, recipe);
|
|
2919
|
+
return recipe;
|
|
2920
|
+
}
|
|
2921
|
+
|
|
2440
2922
|
function recipeForCode(index) {
|
|
2441
2923
|
const language = currentLanguage();
|
|
2442
2924
|
const entry = nativeCodeEntries[index] || {};
|
|
2443
|
-
const recipe = entry.recipe ||
|
|
2925
|
+
const recipe = entry.recipe || findRecipeForEntry(entry) || {};
|
|
2444
2926
|
return {
|
|
2445
2927
|
when: recipe.when ? recipe.when[language] || recipe.when.pt : "",
|
|
2446
2928
|
example: recipe.example ? recipe.example[language] || recipe.example.pt : ""
|
|
@@ -3603,7 +4085,7 @@ async function init() {
|
|
|
3603
4085
|
elements.iconPreview.src = iconPreviewPath(state.defaultIconPath);
|
|
3604
4086
|
}
|
|
3605
4087
|
} catch {
|
|
3606
|
-
elements.appVersion.textContent = "v0.
|
|
4088
|
+
elements.appVersion.textContent = "v0.11.0";
|
|
3607
4089
|
}
|
|
3608
4090
|
|
|
3609
4091
|
setTimeout(finishBoot, 1800);
|