html2apk 0.11.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 +42 -13
- package/package.json +5 -4
- package/src/desktop/main.js +11 -5
- package/src/desktop/renderer/index.html +1 -1
- package/src/desktop/renderer/renderer.js +113 -1
- package/src/templates/cordova-plugin-html2apk-bridge/plugin.xml +1 -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 +1 -7
- package/src/templates/cordova-plugin-html2apk-bridge/src/android/Html2ApkBridge.java +173 -2
- package/src/templates/cordova-plugin-html2apk-bridge/www/html2apk-bridge.js +43 -0
- package/src/templates/html2apk-early-bridge.js +43 -0
package/README.md
CHANGED
|
@@ -528,6 +528,9 @@ Exemplos de aliases:
|
|
|
528
528
|
| `acompanharLocalizacao()` | `watchLocation()` |
|
|
529
529
|
| `pararLocalizacao()` | `stopLocationWatch()` |
|
|
530
530
|
| `autenticarBiometria()` | `authenticateBiometric()` |
|
|
531
|
+
| `solicitarBloqueio()` | `requestDeviceLock()` |
|
|
532
|
+
| `solicitarSegundoPlano()` | `requestBackgroundExecution()` |
|
|
533
|
+
| `configurarInicioAutomatico()` | `setAutoStartOnBoot()` |
|
|
531
534
|
| `salvarSeguro()` | `saveSecure()` |
|
|
532
535
|
| `lerSeguro()` | `readSecure()` |
|
|
533
536
|
| `removerSeguro()` | `deleteSecure()` |
|
|
@@ -741,11 +744,11 @@ Use OneSignal para pushes enviados remotamente pelo painel/API do OneSignal. Use
|
|
|
741
744
|
Arquivos, galeria e compartilhamento:
|
|
742
745
|
|
|
743
746
|
```js
|
|
744
|
-
const imagem = await escolherImagem();
|
|
745
|
-
const imagens = await escolherImagens({ multiplas: true });
|
|
747
|
+
const imagem = await escolherImagem(); // Retorna: { uri, nome, tamanho, mimeType }
|
|
748
|
+
const imagens = await escolherImagens({ multiplas: true }); // Array de objetos
|
|
746
749
|
const pdf = await escolherArquivo({ tipos: ["application/pdf"] });
|
|
747
750
|
const arquivos = await escolherArquivos({ multiplo: true });
|
|
748
|
-
const pasta = await escolherPasta();
|
|
751
|
+
const pasta = await escolherPasta(); // Retorna: { uri, nome, treeUri }
|
|
749
752
|
|
|
750
753
|
await salvarArquivo({
|
|
751
754
|
nome: "relatorio.txt",
|
|
@@ -765,11 +768,11 @@ aoReceberCompartilhamento((dados) => {
|
|
|
765
768
|
});
|
|
766
769
|
|
|
767
770
|
const texto = await ocr(imagem);
|
|
768
|
-
console.log(texto.texto);
|
|
771
|
+
console.log(texto.texto); // Retorna: { texto, blocos: [...] }
|
|
769
772
|
|
|
770
773
|
await falar("Ola mundo", { idioma: "pt-BR", velocidade: 1 });
|
|
771
774
|
const voz = await ouvir({ idioma: "pt-BR" });
|
|
772
|
-
console.log(voz.texto);
|
|
775
|
+
console.log(voz.texto); // Retorna: { texto, error }
|
|
773
776
|
|
|
774
777
|
aoConectarBT((dispositivo) => {
|
|
775
778
|
console.log("Bluetooth conectado", dispositivo.nome);
|
|
@@ -783,7 +786,7 @@ aoDarErroBT((erro) => {
|
|
|
783
786
|
console.log("Erro Bluetooth", erro.mensagem || erro.message);
|
|
784
787
|
});
|
|
785
788
|
|
|
786
|
-
const dispositivos = await procurarBT();
|
|
789
|
+
const dispositivos = await procurarBT(); // Retorna: [{ id, nome, host }, ...]
|
|
787
790
|
if (dispositivos[0]) {
|
|
788
791
|
await conectarBT(dispositivos[0].id);
|
|
789
792
|
await enviarBT({ mensagem: "Ola por Bluetooth" });
|
|
@@ -801,7 +804,7 @@ aoDarErroWiFi((erro) => {
|
|
|
801
804
|
console.log("Erro Wi-Fi", erro.mensagem || erro.message);
|
|
802
805
|
});
|
|
803
806
|
|
|
804
|
-
const dispositivosWifi = await procurarWiFi();
|
|
807
|
+
const dispositivosWifi = await procurarWiFi(); // Retorna: [{ id, nome, host, porta }, ...]
|
|
805
808
|
if (dispositivosWifi[0]) {
|
|
806
809
|
await conectarWiFi(dispositivosWifi[0].id);
|
|
807
810
|
await enviarWiFi({ mensagem: "Ola por Wi-Fi" });
|
|
@@ -882,7 +885,7 @@ await salvarArquivo("wallpaper.jpg", foto.base64, {
|
|
|
882
885
|
|
|
883
886
|
const resultado = await definirPapelParede("wallpaper.jpg", {
|
|
884
887
|
alvo: "inicio" // "inicio", "bloqueio" ou "ambos"
|
|
885
|
-
});
|
|
888
|
+
}); // Retorna: { applied, systemApplied, lockApplied, error }
|
|
886
889
|
|
|
887
890
|
console.log(resultado.applied, resultado.systemApplied, resultado.lockApplied);
|
|
888
891
|
```
|
|
@@ -892,17 +895,19 @@ console.log(resultado.applied, resultado.systemApplied, resultado.lockApplied);
|
|
|
892
895
|
Camera, QR Code, localizacao, biometria e storage seguro:
|
|
893
896
|
|
|
894
897
|
```js
|
|
895
|
-
const foto = await tirarFoto({ base64: true });
|
|
898
|
+
const foto = await tirarFoto({ base64: true }); // Retorna: { base64, mimeType, uri }
|
|
896
899
|
|
|
897
|
-
const qr = await escanearQRCode();
|
|
900
|
+
const qr = await escanearQRCode(); // Retorna: { text, format, cancelled }
|
|
898
901
|
if (qr) {
|
|
899
902
|
console.log(qr.text);
|
|
900
903
|
}
|
|
901
904
|
|
|
902
905
|
const local = await obterLocalizacao({ altaPrecisao: true, timeoutMs: 10000 });
|
|
906
|
+
// Retorna: { latitude, longitude, precisao, altitude, error }
|
|
903
907
|
console.log(local.latitude, local.longitude);
|
|
904
908
|
|
|
905
|
-
const watch = await acompanharLocalizacao({ intervaloMs: 5000 });
|
|
909
|
+
const watch = await acompanharLocalizacao({ intervaloMs: 5000 });
|
|
910
|
+
// Retorna: { watchId, error }
|
|
906
911
|
const pararEvento = aoMudarLocalizacao((evento) => {
|
|
907
912
|
console.log(evento.latitude, evento.longitude);
|
|
908
913
|
});
|
|
@@ -913,13 +918,36 @@ pararEvento();
|
|
|
913
918
|
const bio = await autenticarBiometria({
|
|
914
919
|
titulo: "Confirmar acesso",
|
|
915
920
|
descricao: "Use a biometria do aparelho"
|
|
916
|
-
});
|
|
921
|
+
}); // Retorna: { authenticated, supported, canceled, message }
|
|
917
922
|
|
|
918
923
|
if (bio.authenticated) {
|
|
919
924
|
await salvarSeguro("token", "abc123");
|
|
920
925
|
const token = await lerSeguro("token");
|
|
921
926
|
await removerSeguro("token");
|
|
922
927
|
}
|
|
928
|
+
|
|
929
|
+
const auth = await solicitarBloqueio({
|
|
930
|
+
titulo: "Acesso Restrito",
|
|
931
|
+
descricao: "Confirme sua senha de tela"
|
|
932
|
+
}); // Retorna: { autenticado, suportado, cancelado, mensagem }
|
|
933
|
+
|
|
934
|
+
if (auth.autenticado) {
|
|
935
|
+
// Acesso permitido
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
const bg = await solicitarSegundoPlano();
|
|
939
|
+
// Retorna: { ok, abriuInicioAutomatico, abriuOtimizacaoBateria }
|
|
940
|
+
if (bg.ok) {
|
|
941
|
+
toast("Obrigado por permitir rodar em segundo plano!");
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
// Para abrir o aplicativo sozinho quando o aparelho for ligado:
|
|
945
|
+
await configurarInicioAutomatico(true); // Retorna: { ok, enabled }
|
|
946
|
+
|
|
947
|
+
const inicio = await obterLinkInicial(); // Retorna string: "html2apk://boot" ou "https://..."
|
|
948
|
+
if (inicio === "html2apk://boot") {
|
|
949
|
+
console.log("App abriu sozinho pelo boot do aparelho");
|
|
950
|
+
}
|
|
923
951
|
```
|
|
924
952
|
|
|
925
953
|
O retorno de arquivos tem este formato:
|
|
@@ -938,7 +966,8 @@ O retorno de arquivos tem este formato:
|
|
|
938
966
|
Microfone:
|
|
939
967
|
|
|
940
968
|
```js
|
|
941
|
-
const inicio = await ouvirMic();
|
|
969
|
+
const inicio = await ouvirMic();
|
|
970
|
+
// Retorna: { recording: true, settingsOpened: boolean, error: string }
|
|
942
971
|
if (inicio.settingsOpened) {
|
|
943
972
|
console.log("Libere Microfone nas configuracoes e tente novamente");
|
|
944
973
|
} else {
|
package/package.json
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "html2apk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"author": {
|
|
5
|
-
|
|
3
|
+
"version": "12.0.0",
|
|
4
|
+
"author": {
|
|
5
|
+
"name": "Dev Caio Multiversando",
|
|
6
|
+
"email": "dev@caio.local"
|
|
7
|
+
},
|
|
6
8
|
"description": "Node CLI and library to turn an HTML/CSS/JS folder into an Android APK through Cordova.",
|
|
7
9
|
"main": "index.js",
|
|
8
10
|
"bin": {
|
|
@@ -25,7 +27,6 @@
|
|
|
25
27
|
"desktop": "node bin/html2apk-desktop.js",
|
|
26
28
|
"build-desktop-win": "node scripts/build-desktop-portable.js",
|
|
27
29
|
"build-desktop-deb": "npx electron-builder --linux deb",
|
|
28
|
-
|
|
29
30
|
"test": "node --test test/config.test.js",
|
|
30
31
|
"build-win": "pkg . --targets node20-win-x64 --output dist/html2apk.exe",
|
|
31
32
|
"build-linux": "pkg . --targets node20-linux-x64 --output dist/html2apk-linux",
|
package/src/desktop/main.js
CHANGED
|
@@ -995,7 +995,12 @@ function nativeFunctionLabHtml() {
|
|
|
995
995
|
acompanharLocalizacao: { title: "acompanharLocalizacao()", run: async function () { var result = await fn("acompanharLocalizacao")({ intervaloMs: 5000 }); state.watchId = result && result.watchId; return result; } },
|
|
996
996
|
pararLocalizacao: { title: "pararLocalizacao()", run: function () { return fn("pararLocalizacao")(state.watchId || ""); } },
|
|
997
997
|
aoMudarLocalizacao: { title: "aoMudarLocalizacao()", run: function () { if (state.stopLocationEvent) { state.stopLocationEvent(); } state.stopLocationEvent = fn("aoMudarLocalizacao")(function (event) { log("localizacao:mudou", event, "ok"); }); return { listening: true }; } },
|
|
998
|
+
medirVelocidade: { title: "medirVelocidade()", run: async function () { if (state.pararMedicao) { await state.pararMedicao(); } state.pararMedicao = await fn("medirVelocidade")(function(kmh, local) { log("medirVelocidade", { kmh: kmh, original: local }, "ok"); }); return { measuring: true }; } },
|
|
999
|
+
pararVelocidade: { title: "parar medidor de velocidade", run: async function () { if (state.pararMedicao) { await state.pararMedicao(); state.pararMedicao = null; return { stopped: true }; } return { stopped: false }; } },
|
|
998
1000
|
autenticarBiometria: { title: "autenticarBiometria()", run: function () { return fn("autenticarBiometria")({ titulo: "Teste html2apk", descricao: "Confirme para testar a bridge" }); } },
|
|
1001
|
+
solicitarBloqueio: { title: "solicitarBloqueio()", run: function () { return fn("solicitarBloqueio")({ titulo: "Acesso Restrito", descricao: "Confirme a senha de tela" }); } },
|
|
1002
|
+
solicitarSegundoPlano: { title: "solicitarSegundoPlano()", run: function () { return fn("solicitarSegundoPlano")(); } },
|
|
1003
|
+
configurarInicioAutomatico: { title: "configurarInicioAutomatico()", run: function () { state.autoStart = !state.autoStart; return fn("configurarInicioAutomatico")(state.autoStart); } },
|
|
999
1004
|
salvarSeguro: { title: "salvarSeguro()", run: function () { return fn("salvarSeguro")("tokenTeste", { token: "abc123", criadoEm: Date.now() }); } },
|
|
1000
1005
|
lerSeguro: { title: "lerSeguro()", run: function () { return fn("lerSeguro")("tokenTeste"); } },
|
|
1001
1006
|
lerSeguroCompleto: { title: "lerSeguroCompleto()", run: function () { return fn("lerSeguroCompleto")("tokenTeste"); } },
|
|
@@ -1008,9 +1013,10 @@ function nativeFunctionLabHtml() {
|
|
|
1008
1013
|
abrirConfigPapel: { title: "abrirConfiguracaoPapelParede()", run: function () { return fn("abrirConfiguracaoPapelParede")(); } },
|
|
1009
1014
|
definirImagemEscolhida: { title: "imagem escolhida -> papel de parede", run: async function () { if (!state.lastImage) { state.lastImage = await fn("escolherImagem")(); } if (!state.lastImage || !state.lastImage.uri) { return { canceled: true }; } return fn("definirPapelParede")({ uri: state.lastImage.uri, alvo: "inicio", mimeType: state.lastImage.mimeType || "image/*" }); } },
|
|
1010
1015
|
|
|
1011
|
-
registrarEventos: { title: "
|
|
1016
|
+
registrarEventos: { title: "registrarEventos()", run: function () { return fn("registrarEventos")({ aoClicarNotificacao: function (e) { alert("Clicou na notificacao: " + JSON.stringify(e)); }, aoMudarEstadoApp: function (e) { console.log("Estado mudou:", e); } }); } },
|
|
1012
1017
|
obterNotificacaoInicial: { title: "obterNotificacaoInicial()", run: function () { return fn("obterNotificacaoInicial")(); } },
|
|
1013
|
-
obterLinkInicial: { title: "obterLinkInicial()", run: function () { return fn("obterLinkInicial")(); } }
|
|
1018
|
+
obterLinkInicial: { title: "obterLinkInicial()", run: function () { return fn("obterLinkInicial")(); } },
|
|
1019
|
+
aoLigarDispositivo: { title: "aoLigarDispositivo()", run: function () { return fn("aoLigarDispositivo")(() => alert("App iniciou via boot!")); } }
|
|
1014
1020
|
};
|
|
1015
1021
|
|
|
1016
1022
|
var groups = [
|
|
@@ -1025,9 +1031,9 @@ function nativeFunctionLabHtml() {
|
|
|
1025
1031
|
{ title: "Arquivos e midia", ids: ["escolherImagem", "escolherImagens", "escolherArquivo", "escolherArquivos", "escolherVideo", "escolherPasta", "salvarArquivoPicker", "salvarArquivoCrud", "lerArquivo", "lerArquivoCompleto", "listarArquivos", "infoArquivo", "arquivoExiste", "abrirArquivo", "compartilharArquivo", "baixarArquivo", "baixarBase64", "baixarArquivoLocal", "excluirArquivo"] },
|
|
1026
1032
|
{ title: "Abrir apps externos", ids: ["abrirNoApp", "abrirForaDoApp", "abrirUrl", "abrirUrlExterno", "discar", "abrirMapa", "abrirWhatsapp"] },
|
|
1027
1033
|
{ title: "Diagnostico", ids: ["infoDispositivo", "infoRede", "infoBateria", "infoMemoria", "infoArmazenamento", "infoDesempenho", "appsAbertos", "infoAppsAbertos"] },
|
|
1028
|
-
{ title: "Localizacao e seguranca", ids: ["obterLocalizacao", "acompanharLocalizacao", "pararLocalizacao", "aoMudarLocalizacao", "autenticarBiometria", "salvarSeguro", "lerSeguro", "lerSeguroCompleto", "listarSeguro", "removerSeguro", "limparSeguro"] },
|
|
1034
|
+
{ title: "Localizacao e seguranca", ids: ["obterLocalizacao", "acompanharLocalizacao", "pararLocalizacao", "aoMudarLocalizacao", "medirVelocidade", "pararVelocidade", "autenticarBiometria", "solicitarBloqueio", "solicitarSegundoPlano", "configurarInicioAutomatico", "salvarSeguro", "lerSeguro", "lerSeguroCompleto", "listarSeguro", "removerSeguro", "limparSeguro"] },
|
|
1029
1035
|
{ title: "Papel de parede", ids: ["infoPapelParede", "definirPapelParede", "abrirConfigPapel", "definirImagemEscolhida"] },
|
|
1030
|
-
{ title: "Eventos", ids: ["registrarEventos", "obterNotificacaoInicial", "obterLinkInicial"] }
|
|
1036
|
+
{ title: "Eventos", ids: ["registrarEventos", "obterNotificacaoInicial", "obterLinkInicial", "aoLigarDispositivo"] }
|
|
1031
1037
|
];
|
|
1032
1038
|
|
|
1033
1039
|
var safeActionIds = [
|
|
@@ -1051,7 +1057,7 @@ function nativeFunctionLabHtml() {
|
|
|
1051
1057
|
"abrirSobreposicao", "abrirConfigPapel", "escolherImagem", "escolherImagens",
|
|
1052
1058
|
"escolherArquivo", "escolherArquivos", "escolherVideo", "escolherPasta",
|
|
1053
1059
|
"salvarArquivoPicker", "baixarArquivoLocal", "tirarFoto", "capturarVideo",
|
|
1054
|
-
"escanearQRCode", "ouvir", "autenticarBiometria"
|
|
1060
|
+
"escanearQRCode", "ouvir", "autenticarBiometria", "solicitarBloqueio"
|
|
1055
1061
|
];
|
|
1056
1062
|
var dangerActionIds = ["fecharApp", "minimizarApp", "limparSeguro", "excluirArquivo", "pararIconeFlutuante"];
|
|
1057
1063
|
var initialSmokeIds = ["registrarEventos", "statusPermissoes", "infoDispositivo", "infoRede", "infoBateria", "volumeAtual"];
|
|
@@ -673,6 +673,20 @@ const nativeCodeEntries = [
|
|
|
673
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
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
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
|
+
},
|
|
676
690
|
{
|
|
677
691
|
syntax: { pt: "obterLinkInicial() / aoAbrirLink(fn)", en: "getInitialLink() / onOpenLink(fn)" },
|
|
678
692
|
java: "getInitialLink",
|
|
@@ -974,6 +988,27 @@ const nativeCodeEntries = [
|
|
|
974
988
|
returns: { pt: "{ supported, authenticated, canceled, message }.", en: "{ supported, authenticated, canceled, message }." },
|
|
975
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." }
|
|
976
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
|
+
},
|
|
977
1012
|
{
|
|
978
1013
|
syntax: { pt: "salvarSeguro('token', valor) / lerSeguro('token')", en: "saveSecure('token', value) / readSecure('token')" },
|
|
979
1014
|
java: "Android Keystore",
|
|
@@ -2226,6 +2261,83 @@ if (bio.authenticated) {
|
|
|
2226
2261
|
}`
|
|
2227
2262
|
}
|
|
2228
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
|
+
},
|
|
2229
2341
|
{
|
|
2230
2342
|
when: { pt: "Para guardar tokens ou preferencias sensiveis cifradas pelo Android Keystore.", en: "To store tokens or sensitive preferences encrypted by Android Keystore." },
|
|
2231
2343
|
example: {
|
|
@@ -4085,7 +4197,7 @@ async function init() {
|
|
|
4085
4197
|
elements.iconPreview.src = iconPreviewPath(state.defaultIconPath);
|
|
4086
4198
|
}
|
|
4087
4199
|
} catch {
|
|
4088
|
-
elements.appVersion.textContent = "
|
|
4200
|
+
elements.appVersion.textContent = "v12.0.0";
|
|
4089
4201
|
}
|
|
4090
4202
|
|
|
4091
4203
|
setTimeout(finishBoot, 1800);
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
<uses-feature android:name="android.hardware.nfc" android:required="false" />
|
|
41
41
|
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
|
42
42
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
|
43
|
+
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
|
43
44
|
</config-file>
|
|
44
45
|
|
|
45
46
|
<config-file target="AndroidManifest.xml" parent="/manifest/application">
|
|
@@ -16,5 +16,18 @@ public class BootReceiver extends BroadcastReceiver {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
NotificationStore.rescheduleAll(context, exactAllowed);
|
|
19
|
+
|
|
20
|
+
android.content.SharedPreferences prefs = context.getSharedPreferences(Html2ApkBridge.PREFS_NAME, Context.MODE_PRIVATE);
|
|
21
|
+
if (prefs.getBoolean("html2apk_boot_start", false)) {
|
|
22
|
+
try {
|
|
23
|
+
Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
|
|
24
|
+
if (launchIntent != null) {
|
|
25
|
+
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
26
|
+
launchIntent.putExtra(Html2ApkBridge.EXTRA_INITIAL_LINK, "html2apk://boot");
|
|
27
|
+
context.startActivity(launchIntent);
|
|
28
|
+
}
|
|
29
|
+
} catch (Exception ignored) {
|
|
30
|
+
}
|
|
31
|
+
}
|
|
19
32
|
}
|
|
20
33
|
}
|
|
@@ -68,16 +68,10 @@ public class FloatingIconService extends Service {
|
|
|
68
68
|
|
|
69
69
|
ImageView icon = new ImageView(this);
|
|
70
70
|
int size = dp(58);
|
|
71
|
-
int padding = dp(8);
|
|
72
71
|
icon.setImageDrawable(getApplicationInfo().loadIcon(getPackageManager()));
|
|
73
|
-
icon.
|
|
72
|
+
icon.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
|
74
73
|
icon.setAlpha(opacity);
|
|
75
74
|
|
|
76
|
-
GradientDrawable background = new GradientDrawable();
|
|
77
|
-
background.setShape(GradientDrawable.OVAL);
|
|
78
|
-
background.setColor(0xff126fff);
|
|
79
|
-
icon.setBackground(background);
|
|
80
|
-
|
|
81
75
|
int windowType = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
|
|
82
76
|
? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
|
|
83
77
|
: WindowManager.LayoutParams.TYPE_PHONE;
|
|
@@ -150,6 +150,7 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
150
150
|
private static final int REQUEST_CAPTURE_PHOTO = 7414;
|
|
151
151
|
private static final int REQUEST_CAPTURE_VIDEO = 7415;
|
|
152
152
|
private static final int REQUEST_SPEECH_RECOGNITION = 7416;
|
|
153
|
+
private static final int REQUEST_DEVICE_CREDENTIAL = 7417;
|
|
153
154
|
private static final String PREFS_NAME = "html2apk_bridge";
|
|
154
155
|
private static final String PREF_PERMISSION_PREFIX = "permission_requested_";
|
|
155
156
|
private static final String STORED_FILES_DIR = "html2apk-files";
|
|
@@ -176,6 +177,7 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
176
177
|
private CallbackContext mediaCaptureCallback;
|
|
177
178
|
private CallbackContext pendingLocationCallback;
|
|
178
179
|
private CallbackContext biometricCallback;
|
|
180
|
+
private CallbackContext deviceCredentialCallback;
|
|
179
181
|
private CallbackContext pendingDownloadCallback;
|
|
180
182
|
private CallbackContext speechRecognitionCallback;
|
|
181
183
|
private CallbackContext pendingSpeakCallback;
|
|
@@ -804,6 +806,21 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
804
806
|
return true;
|
|
805
807
|
}
|
|
806
808
|
|
|
809
|
+
if ("requestDeviceLock".equals(action)) {
|
|
810
|
+
requestDeviceLock(args.optJSONObject(0), callbackContext);
|
|
811
|
+
return true;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
if ("requestBackgroundExecution".equals(action)) {
|
|
815
|
+
requestBackgroundExecution(args.optJSONObject(0), callbackContext);
|
|
816
|
+
return true;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
if ("setAutoStartOnBoot".equals(action)) {
|
|
820
|
+
setAutoStartOnBoot(args.optJSONObject(0), callbackContext);
|
|
821
|
+
return true;
|
|
822
|
+
}
|
|
823
|
+
|
|
807
824
|
if ("saveSecureItem".equals(action)) {
|
|
808
825
|
callbackContext.success(saveSecureItem(args.optJSONObject(0)));
|
|
809
826
|
return true;
|
|
@@ -1109,6 +1126,11 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
1109
1126
|
|
|
1110
1127
|
if (requestCode == REQUEST_CAPTURE_PHOTO || requestCode == REQUEST_CAPTURE_VIDEO) {
|
|
1111
1128
|
handleMediaCaptureResult(resultCode, intent);
|
|
1129
|
+
return;
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
if (requestCode == REQUEST_DEVICE_CREDENTIAL) {
|
|
1133
|
+
handleDeviceCredentialResult(resultCode);
|
|
1112
1134
|
}
|
|
1113
1135
|
}
|
|
1114
1136
|
|
|
@@ -2169,12 +2191,20 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
2169
2191
|
intent.putExtra(Intent.EXTRA_TITLE, title);
|
|
2170
2192
|
if (streams.size() == 1) {
|
|
2171
2193
|
intent.putExtra(Intent.EXTRA_STREAM, streams.get(0));
|
|
2194
|
+
intent.setClipData(ClipData.newRawUri("", streams.get(0)));
|
|
2172
2195
|
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
|
2173
2196
|
} else if (streams.size() > 1) {
|
|
2174
2197
|
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, streams);
|
|
2198
|
+
ClipData clipData = ClipData.newRawUri("", streams.get(0));
|
|
2199
|
+
for (int i = 1; i < streams.size(); i++) {
|
|
2200
|
+
clipData.addItem(new ClipData.Item(streams.get(i)));
|
|
2201
|
+
}
|
|
2202
|
+
intent.setClipData(clipData);
|
|
2175
2203
|
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
|
2176
2204
|
}
|
|
2177
|
-
|
|
2205
|
+
Intent chooser = Intent.createChooser(intent, title);
|
|
2206
|
+
chooser.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
|
2207
|
+
cordova.getActivity().startActivity(chooser);
|
|
2178
2208
|
|
|
2179
2209
|
JSONObject result = new JSONObject();
|
|
2180
2210
|
result.put("ok", true);
|
|
@@ -3709,12 +3739,16 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
3709
3739
|
Intent intent = new Intent(Intent.ACTION_SEND);
|
|
3710
3740
|
intent.setType("application/vnd.android.package-archive");
|
|
3711
3741
|
intent.putExtra(Intent.EXTRA_STREAM, uri);
|
|
3742
|
+
intent.setClipData(ClipData.newRawUri("", uri));
|
|
3712
3743
|
intent.putExtra(Intent.EXTRA_TITLE, title);
|
|
3713
3744
|
if (text.length() > 0) {
|
|
3714
3745
|
intent.putExtra(Intent.EXTRA_TEXT, text);
|
|
3715
3746
|
}
|
|
3716
3747
|
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
|
3717
|
-
|
|
3748
|
+
|
|
3749
|
+
Intent chooser = Intent.createChooser(intent, title);
|
|
3750
|
+
chooser.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
|
3751
|
+
cordova.getActivity().startActivity(chooser);
|
|
3718
3752
|
|
|
3719
3753
|
JSONObject result = storedFileResult(outputFile, "application/vnd.android.package-archive", "apk");
|
|
3720
3754
|
String[] splitSourceDirs = appInfo.splitSourceDirs;
|
|
@@ -6180,6 +6214,143 @@ public class Html2ApkBridge extends CordovaPlugin {
|
|
|
6180
6214
|
biometricCallback = null;
|
|
6181
6215
|
}
|
|
6182
6216
|
|
|
6217
|
+
private void requestDeviceLock(final JSONObject options, final CallbackContext callbackContext) throws Exception {
|
|
6218
|
+
if (deviceCredentialCallback != null) {
|
|
6219
|
+
rejectBusyCallback(callbackContext, "Device lock authentication");
|
|
6220
|
+
return;
|
|
6221
|
+
}
|
|
6222
|
+
|
|
6223
|
+
KeyguardManager keyguardManager = (KeyguardManager) context().getSystemService(Context.KEYGUARD_SERVICE);
|
|
6224
|
+
if (keyguardManager == null || !keyguardManager.isDeviceSecure()) {
|
|
6225
|
+
JSONObject result = new JSONObject();
|
|
6226
|
+
result.put("supported", false);
|
|
6227
|
+
result.put("suportado", false);
|
|
6228
|
+
result.put("authenticated", false);
|
|
6229
|
+
result.put("autenticado", false);
|
|
6230
|
+
result.put("canceled", false);
|
|
6231
|
+
result.put("cancelado", false);
|
|
6232
|
+
result.put("message", "Device secure lock screen is not configured.");
|
|
6233
|
+
result.put("mensagem", "A tela de bloqueio do dispositivo nao esta configurada.");
|
|
6234
|
+
callbackContext.success(result);
|
|
6235
|
+
return;
|
|
6236
|
+
}
|
|
6237
|
+
|
|
6238
|
+
final JSONObject safeOptions = options == null ? new JSONObject() : options;
|
|
6239
|
+
String title = safeOptions.optString("titulo", safeOptions.optString("title", "Autenticacao"));
|
|
6240
|
+
String description = safeOptions.optString("descricao", safeOptions.optString("description", ""));
|
|
6241
|
+
|
|
6242
|
+
Intent intent = keyguardManager.createConfirmDeviceCredentialIntent(title, description);
|
|
6243
|
+
if (intent == null) {
|
|
6244
|
+
JSONObject result = new JSONObject();
|
|
6245
|
+
result.put("supported", false);
|
|
6246
|
+
result.put("suportado", false);
|
|
6247
|
+
result.put("authenticated", false);
|
|
6248
|
+
result.put("autenticado", false);
|
|
6249
|
+
result.put("canceled", false);
|
|
6250
|
+
result.put("cancelado", false);
|
|
6251
|
+
result.put("message", "Could not create device credential intent.");
|
|
6252
|
+
result.put("mensagem", "Nao foi possivel criar a tela de autenticacao.");
|
|
6253
|
+
callbackContext.success(result);
|
|
6254
|
+
return;
|
|
6255
|
+
}
|
|
6256
|
+
|
|
6257
|
+
deviceCredentialCallback = callbackContext;
|
|
6258
|
+
cordova.setActivityResultCallback(this);
|
|
6259
|
+
cordova.getActivity().startActivityForResult(intent, REQUEST_DEVICE_CREDENTIAL);
|
|
6260
|
+
}
|
|
6261
|
+
|
|
6262
|
+
private void handleDeviceCredentialResult(int resultCode) {
|
|
6263
|
+
CallbackContext callback = deviceCredentialCallback;
|
|
6264
|
+
deviceCredentialCallback = null;
|
|
6265
|
+
if (callback == null) {
|
|
6266
|
+
return;
|
|
6267
|
+
}
|
|
6268
|
+
|
|
6269
|
+
try {
|
|
6270
|
+
boolean authenticated = resultCode == Activity.RESULT_OK;
|
|
6271
|
+
boolean canceled = resultCode == Activity.RESULT_CANCELED;
|
|
6272
|
+
JSONObject result = new JSONObject();
|
|
6273
|
+
result.put("supported", true);
|
|
6274
|
+
result.put("suportado", true);
|
|
6275
|
+
result.put("authenticated", authenticated);
|
|
6276
|
+
result.put("autenticado", authenticated);
|
|
6277
|
+
result.put("canceled", canceled);
|
|
6278
|
+
result.put("cancelado", canceled);
|
|
6279
|
+
result.put("message", authenticated ? "" : "Authentication failed or canceled.");
|
|
6280
|
+
result.put("mensagem", authenticated ? "" : "Autenticacao falhou ou cancelada.");
|
|
6281
|
+
callback.success(result);
|
|
6282
|
+
} catch (Exception error) {
|
|
6283
|
+
callback.error(error.getMessage());
|
|
6284
|
+
}
|
|
6285
|
+
}
|
|
6286
|
+
|
|
6287
|
+
private void requestBackgroundExecution(final JSONObject options, final CallbackContext callbackContext) throws Exception {
|
|
6288
|
+
boolean openedAutoStart = false;
|
|
6289
|
+
boolean openedBatteryOpt = false;
|
|
6290
|
+
|
|
6291
|
+
try {
|
|
6292
|
+
Intent intent = new Intent();
|
|
6293
|
+
String manufacturer = Build.MANUFACTURER;
|
|
6294
|
+
if ("xiaomi".equalsIgnoreCase(manufacturer)) {
|
|
6295
|
+
intent.setComponent(new android.content.ComponentName("com.miui.securitycenter", "com.miui.permcenter.autostart.AutoStartManagementActivity"));
|
|
6296
|
+
} else if ("oppo".equalsIgnoreCase(manufacturer)) {
|
|
6297
|
+
intent.setComponent(new android.content.ComponentName("com.coloros.safecenter", "com.coloros.safecenter.permission.startup.StartupAppListActivity"));
|
|
6298
|
+
} else if ("vivo".equalsIgnoreCase(manufacturer)) {
|
|
6299
|
+
intent.setComponent(new android.content.ComponentName("com.vivo.permissionmanager", "com.vivo.permissionmanager.activity.BgStartUpManagerActivity"));
|
|
6300
|
+
} else if ("Letv".equalsIgnoreCase(manufacturer)) {
|
|
6301
|
+
intent.setComponent(new android.content.ComponentName("com.letv.android.letvsafe", "com.letv.android.letvsafe.AutobootManageActivity"));
|
|
6302
|
+
} else if ("Honor".equalsIgnoreCase(manufacturer)) {
|
|
6303
|
+
intent.setComponent(new android.content.ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.optimize.process.ProtectActivity"));
|
|
6304
|
+
} else if ("huawei".equalsIgnoreCase(manufacturer)) {
|
|
6305
|
+
intent.setComponent(new android.content.ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.startupmgr.ui.StartupNormalAppListActivity"));
|
|
6306
|
+
}
|
|
6307
|
+
|
|
6308
|
+
java.util.List<android.content.pm.ResolveInfo> list = context().getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
|
|
6309
|
+
if (list.size() > 0) {
|
|
6310
|
+
cordova.getActivity().startActivity(intent);
|
|
6311
|
+
openedAutoStart = true;
|
|
6312
|
+
}
|
|
6313
|
+
} catch (Exception e) {
|
|
6314
|
+
// Ignorar falhas do AutoStart
|
|
6315
|
+
}
|
|
6316
|
+
|
|
6317
|
+
try {
|
|
6318
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
6319
|
+
android.os.PowerManager pm = (android.os.PowerManager) context().getSystemService(Context.POWER_SERVICE);
|
|
6320
|
+
String packageName = context().getPackageName();
|
|
6321
|
+
if (pm != null && !pm.isIgnoringBatteryOptimizations(packageName)) {
|
|
6322
|
+
Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
|
|
6323
|
+
intent.setData(Uri.parse("package:" + packageName));
|
|
6324
|
+
cordova.getActivity().startActivity(intent);
|
|
6325
|
+
openedBatteryOpt = true;
|
|
6326
|
+
}
|
|
6327
|
+
}
|
|
6328
|
+
} catch (Exception e) {
|
|
6329
|
+
// Ignorar falhas da bateria
|
|
6330
|
+
}
|
|
6331
|
+
|
|
6332
|
+
JSONObject result = new JSONObject();
|
|
6333
|
+
result.put("openedAutoStart", openedAutoStart);
|
|
6334
|
+
result.put("abriuInicioAutomatico", openedAutoStart);
|
|
6335
|
+
result.put("openedBatteryOptimization", openedBatteryOpt);
|
|
6336
|
+
result.put("abriuOtimizacaoBateria", openedBatteryOpt);
|
|
6337
|
+
result.put("ok", openedAutoStart || openedBatteryOpt);
|
|
6338
|
+
|
|
6339
|
+
callbackContext.success(result);
|
|
6340
|
+
}
|
|
6341
|
+
|
|
6342
|
+
private void setAutoStartOnBoot(final JSONObject options, final CallbackContext callbackContext) {
|
|
6343
|
+
boolean enable = options != null && options.optBoolean("ativar", options.optBoolean("enable", false));
|
|
6344
|
+
SharedPreferences prefs = context().getSharedPreferences(Html2ApkBridge.PREFS_NAME, Context.MODE_PRIVATE);
|
|
6345
|
+
prefs.edit().putBoolean("html2apk_boot_start", enable).apply();
|
|
6346
|
+
|
|
6347
|
+
JSONObject result = new JSONObject();
|
|
6348
|
+
result.put("ok", true);
|
|
6349
|
+
result.put("enabled", enable);
|
|
6350
|
+
result.put("ativado", enable);
|
|
6351
|
+
callbackContext.success(result);
|
|
6352
|
+
}
|
|
6353
|
+
|
|
6183
6354
|
private JSONObject permissionStatus(JSONArray requested) throws Exception {
|
|
6184
6355
|
JSONArray permissions = requested == null ? new JSONArray() : requested;
|
|
6185
6356
|
JSONObject result = new JSONObject();
|
|
@@ -1327,9 +1327,47 @@ var api = {
|
|
|
1327
1327
|
aoMudarLocalizacao: function (listener) {
|
|
1328
1328
|
return onEvent("localizacao:mudou", listener);
|
|
1329
1329
|
},
|
|
1330
|
+
medirVelocidade: function (callback, options) {
|
|
1331
|
+
var listener = function (local) {
|
|
1332
|
+
if (typeof callback !== "function") return;
|
|
1333
|
+
var ms = typeof local.velocidade === "number" ? local.velocidade : 0;
|
|
1334
|
+
var kmh = ms * 3.6;
|
|
1335
|
+
callback(kmh, local);
|
|
1336
|
+
};
|
|
1337
|
+
var stopEvent = api.aoMudarLocalizacao(listener);
|
|
1338
|
+
return api.acompanharLocalizacao(Object.assign({ altaPrecisao: true, intervaloMs: 2000 }, options || {}))
|
|
1339
|
+
.then(function (result) {
|
|
1340
|
+
var watchId = result && result.watchId;
|
|
1341
|
+
return function pararMedicao() {
|
|
1342
|
+
stopEvent();
|
|
1343
|
+
if (watchId) {
|
|
1344
|
+
return api.pararLocalizacao(watchId);
|
|
1345
|
+
}
|
|
1346
|
+
return Promise.resolve();
|
|
1347
|
+
};
|
|
1348
|
+
});
|
|
1349
|
+
},
|
|
1330
1350
|
autenticarBiometria: function (options) {
|
|
1331
1351
|
return call("authenticateBiometric", [options || {}]);
|
|
1332
1352
|
},
|
|
1353
|
+
solicitarBloqueio: function (options) {
|
|
1354
|
+
return call("requestDeviceLock", [options || {}]);
|
|
1355
|
+
},
|
|
1356
|
+
solicitarSegundoPlano: function (options) {
|
|
1357
|
+
return call("requestBackgroundExecution", [options || {}]);
|
|
1358
|
+
},
|
|
1359
|
+
configurarInicioAutomatico: function (enable) {
|
|
1360
|
+
return call("setAutoStartOnBoot", [{ ativar: !!enable }]);
|
|
1361
|
+
},
|
|
1362
|
+
aoLigarDispositivo: function (callback) {
|
|
1363
|
+
if (typeof callback === "function") {
|
|
1364
|
+
call("getInitialLink").then(function(link) {
|
|
1365
|
+
if (link === "html2apk://boot") {
|
|
1366
|
+
callback();
|
|
1367
|
+
}
|
|
1368
|
+
});
|
|
1369
|
+
}
|
|
1370
|
+
},
|
|
1333
1371
|
salvarSeguro: function (keyOrOptions, value, options) {
|
|
1334
1372
|
return call("saveSecureItem", [secureItemOptions(keyOrOptions, value, options)]);
|
|
1335
1373
|
},
|
|
@@ -1649,7 +1687,12 @@ Object.assign(api, {
|
|
|
1649
1687
|
watchLocation: api.acompanharLocalizacao,
|
|
1650
1688
|
stopLocationWatch: api.pararLocalizacao,
|
|
1651
1689
|
onLocationChange: api.aoMudarLocalizacao,
|
|
1690
|
+
measureSpeed: api.medirVelocidade,
|
|
1652
1691
|
authenticateBiometric: api.autenticarBiometria,
|
|
1692
|
+
requestDeviceLock: api.solicitarBloqueio,
|
|
1693
|
+
requestBackgroundExecution: api.solicitarSegundoPlano,
|
|
1694
|
+
setAutoStartOnBoot: api.configurarInicioAutomatico,
|
|
1695
|
+
onDeviceBoot: api.aoLigarDispositivo,
|
|
1653
1696
|
saveSecure: api.salvarSeguro,
|
|
1654
1697
|
secureSet: api.salvarSeguro,
|
|
1655
1698
|
readSecure: api.lerSeguro,
|
|
@@ -1308,9 +1308,47 @@
|
|
|
1308
1308
|
aoMudarLocalizacao: function (listener) {
|
|
1309
1309
|
return onEvent("localizacao:mudou", listener);
|
|
1310
1310
|
},
|
|
1311
|
+
medirVelocidade: function (callback, options) {
|
|
1312
|
+
var listener = function (local) {
|
|
1313
|
+
if (typeof callback !== "function") return;
|
|
1314
|
+
var ms = typeof local.velocidade === "number" ? local.velocidade : 0;
|
|
1315
|
+
var kmh = ms * 3.6;
|
|
1316
|
+
callback(kmh, local);
|
|
1317
|
+
};
|
|
1318
|
+
var stopEvent = api.aoMudarLocalizacao(listener);
|
|
1319
|
+
return api.acompanharLocalizacao(Object.assign({ altaPrecisao: true, intervaloMs: 2000 }, options || {}))
|
|
1320
|
+
.then(function (result) {
|
|
1321
|
+
var watchId = result && result.watchId;
|
|
1322
|
+
return function pararMedicao() {
|
|
1323
|
+
stopEvent();
|
|
1324
|
+
if (watchId) {
|
|
1325
|
+
return api.pararLocalizacao(watchId);
|
|
1326
|
+
}
|
|
1327
|
+
return Promise.resolve();
|
|
1328
|
+
};
|
|
1329
|
+
});
|
|
1330
|
+
},
|
|
1311
1331
|
autenticarBiometria: function (options) {
|
|
1312
1332
|
return call("authenticateBiometric", [options || {}]);
|
|
1313
1333
|
},
|
|
1334
|
+
solicitarBloqueio: function (options) {
|
|
1335
|
+
return call("requestDeviceLock", [options || {}]);
|
|
1336
|
+
},
|
|
1337
|
+
solicitarSegundoPlano: function (options) {
|
|
1338
|
+
return call("requestBackgroundExecution", [options || {}]);
|
|
1339
|
+
},
|
|
1340
|
+
configurarInicioAutomatico: function (enable) {
|
|
1341
|
+
return call("setAutoStartOnBoot", [{ ativar: !!enable }]);
|
|
1342
|
+
},
|
|
1343
|
+
aoLigarDispositivo: function (callback) {
|
|
1344
|
+
if (typeof callback === "function") {
|
|
1345
|
+
call("getInitialLink").then(function(link) {
|
|
1346
|
+
if (link === "html2apk://boot") {
|
|
1347
|
+
callback();
|
|
1348
|
+
}
|
|
1349
|
+
});
|
|
1350
|
+
}
|
|
1351
|
+
},
|
|
1314
1352
|
salvarSeguro: function (keyOrOptions, value, options) {
|
|
1315
1353
|
return call("saveSecureItem", [secureItemOptions(keyOrOptions, value, options)]);
|
|
1316
1354
|
},
|
|
@@ -1621,7 +1659,12 @@
|
|
|
1621
1659
|
watchLocation: api.acompanharLocalizacao,
|
|
1622
1660
|
stopLocationWatch: api.pararLocalizacao,
|
|
1623
1661
|
onLocationChange: api.aoMudarLocalizacao,
|
|
1662
|
+
measureSpeed: api.medirVelocidade,
|
|
1624
1663
|
authenticateBiometric: api.autenticarBiometria,
|
|
1664
|
+
requestDeviceLock: api.solicitarBloqueio,
|
|
1665
|
+
requestBackgroundExecution: api.solicitarSegundoPlano,
|
|
1666
|
+
setAutoStartOnBoot: api.configurarInicioAutomatico,
|
|
1667
|
+
onDeviceBoot: api.aoLigarDispositivo,
|
|
1625
1668
|
saveSecure: api.salvarSeguro,
|
|
1626
1669
|
secureSet: api.salvarSeguro,
|
|
1627
1670
|
readSecure: api.lerSeguro,
|