Comece aqui
Como escrever HTML para o seu app
O que o runtime do app expõe à sua página - contatos, SMS, notificações, informações do dispositivo, biometria - e as regras para chamá-lo corretamente
1Regra 1 - sempre verifique primeiro#
window.WebToApk só existe dentro do app compilado. Em um navegador, ele não existe.
Por isso, sempre verifique antes de chamar. Se não verificar, sua página quebra em todo lugar, exceto dentro do app.
Algumas coisas não precisam de verificação: navigator.vibrate(), speechSynthesis e Notification já funcionam - o app as conecta para você. Basta usar o código web normal.
// GOOD — safe everywhere
if (window.WebToApk) {
WebToApk.playClick();
}
// BAD — this breaks your page in a browser
WebToApk.playClick();2Regra 2 - algumas respostas voltam depois#
Ler contatos, SMS ou o histórico de chamadas leva tempo. Por isso essas chamadas não respondem na hora. Se respondessem, seu app travaria.
Você dá um nome à chamada (um id). A resposta chega depois, como um evento. Você escuta esse evento.
Nomes dos eventos: appmint:contacts, appmint:calllog, appmint:sms, appmint:sms-received, appmint:notification-action.
// 1. Listen for the answer
window.addEventListener('appmint:contacts', function (e) {
if (e.detail.error) {
alert('Problem: ' + e.detail.error);
return;
}
console.log(e.detail.contacts);
});
// 2. Ask the question ('my1' is any name you choose)
WebToApk.listContacts('my1', 50, 0);3Regra 3 - duas chaves precisam estar ATIVADAS#
Um recurso do celular precisa de DUAS coisas:
- Você ativou isso na Etapa 4 (Access) ao compilar o app. Se não, recebe o erro 'not_enabled' e o usuário não vê nada.
- O usuário disse Sim no celular. Se ele disse Não, você recebe o erro 'permission_denied'.
Trate sempre os dois erros, para o usuário saber o que aconteceu.
Jeito mais fácil: pickContact() e composeSms() NÃO precisam de nenhuma permissão. Use-os quando servirem - fazem o mesmo trabalho com menos complicação.
window.addEventListener('appmint:contacts', function (e) {
if (e.detail.error === 'not_enabled') {
alert('Turn on Contacts when you build the app');
} else if (e.detail.error === 'permission_denied') {
alert('Please allow contacts access');
} else {
showContacts(e.detail.contacts);
}
});4Notificações com imagens e botões#
notify() recebe uma lista de opções. Você pode adicionar uma imagem grande, um ícone, botões e uma barra de progresso.
channel pode ser: urgent, default, quiet ou ongoing.
Use ongoing:true para uma notificação que o usuário não consegue deslizar para remover. Atenção: ele ainda pode removê-la nas configurações do Android. Nenhum app consegue criar uma que seja impossível de remover.
Use tag para dar um nome a ela. Enviar de novo com a mesma tag substitui a antiga em vez de adicionar uma nova.
WebToApk.notify(JSON.stringify({
title: 'Order shipped',
body: 'Arriving Tuesday',
channel: 'urgent',
image: 'https://mysite.com/box.jpg',
actions: [{ id: 'track', label: 'Track' }],
tag: 'order-482'
}));
// Know which button the user pressed
window.addEventListener('appmint:notification-action', function (e) {
if (e.detail.actionId === 'track') showTracking();
});5Informações do celular e impressão digital#
getDeviceInfo() não exige permissão. Ele dá a versão do Android, o ID da versão (buildId), o modelo do celular, o tamanho da tela, o nível da bateria e mais.
Sobre o ID do dispositivo: NÃO existe IMEI nem número de série. O Android bloqueou isso para todos os apps a partir do Android 10. Use getInstallId() - um ID fixo desta instalação que continua o mesmo depois das atualizações.
var info = JSON.parse(WebToApk.getDeviceInfo());
info.android.release; // "14"
info.android.buildId; // "TQ3A.230805.001" <- version ID
info.hardware.model; // "Pixel 7"
info.runtime.batteryLevel; // 82
// Fingerprint
window.__webToApkAuth = window.__webToApkAuth || {};
window.__webToApkAuth['unlock'] = function (json) {
var r = JSON.parse(json);
if (r.ok) showMyApp();
};
WebToApk.authenticateBiometricEx('unlock', JSON.stringify({
title: 'Unlock',
allowDeviceCredential: true
}));6Faça sua página preencher a tela#
Quando você ativa Fullscreen, o app oculta as barras do Android para você. Mas adicione este CSS para que seu conteúdo não fique escondido sob o notch do celular.
Importante: Fullscreen oculta as barras do ANDROID (relógio, bateria, botões de voltar). A barra colorida do próprio app no topo é outra chave, chamada 'Show top bar'. Se você ainda vê uma barra depois de ativar Fullscreen, é essa que deve desativar.
Além disso: teste seu app girado. O notch vai para o lado na horizontal.
body {
padding-top: env(safe-area-inset-top);
padding-bottom: env(safe-area-inset-bottom);
min-height: 100vh; /* old phones */
min-height: 100dvh; /* correct when bars are hidden */
}7Os downloads mantêm o nome de arquivo que você escolheu#
Seu código de download normal já funciona. Quando sua página salva um arquivo - um relatório em PDF, uma exportação CSV, um backup - o app o captura e abre a tela 'Save as…' do Android com o SEU nome de arquivo já preenchido.
Isso cobre todas as formas usuais: <a download="...">, a.click() pelo código, html2pdf / jsPDF, saveAs() do FileSaver.js e window.open() em uma URL blob. Um objeto File já traz o próprio nome.
Se preferir pedir diretamente, em vez de montar uma tag <a>, use AppMint.downloadFile().
Um detalhe: o nome existe só no JavaScript da sua página - o Android nunca o vê sozinho. Então defina download= (ou passe um nome) sempre, senão o arquivo é salvo como 'download'.
// The usual way — saves as Site_Report_2026-08.pdf
const blob = await html2pdf().from(el).outputPdf('blob');
const a = document.createElement('a');
a.download = 'Site_Report_2026-08.pdf';
a.href = URL.createObjectURL(blob);
a.click();
// Or ask directly
AppMint.downloadFile(base64String, 'Ledger_Q3.csv', 'text/csv');
AppMint.saveBlob(myBlob, 'Backup.json');8Faça o botão Voltar fazer o que o SEU app espera#
Se seu app troca de tela mostrando e ocultando elementos (sem mudar a URL, sem history.pushState), o botão Voltar do Android não enxerga essas telas - o histórico está vazio, então o Voltar fecha o app de qualquer lugar.
Registre um handler de Voltar e decida você mesmo: retorne true quando tratou o toque (fechou um menu, voltou uma tela), retorne false para deixar o comportamento normal seguir - voltar no histórico da página, depois a confirmação de saída, depois sair.
Apps que usam history.pushState em cada tela não precisam disso: o Voltar já percorre o histórico deles. E não se preocupe com o app travar - se seu handler travar, o Voltar do celular volta a funcionar nativamente após um instante.
AppMint.setBackHandler(() => {
if (isMenuOpen) { closeMenu(); return true; } // consumed
if (screen !== 'home') { goTo('home'); return true; }
return false; // nothing open — normal exit behaviour
});
// Or listen instead: e.preventDefault() consumes the press
window.addEventListener('appmint:back', e => {
if (closeTopmost()) e.preventDefault();
});9Receba o que as pessoas compartilham com seu app - links, fotos, vídeos, arquivos#
Ative 'Receive shares from other apps' no assistente de compilação e marque o que seu app aceita: texto e links, arquivos de dados (JSON, CSV, TXT, XML, Markdown, GPX, TCX, FIT), fotos, vídeos, áudio, documentos (PDF, Word, Excel, PowerPoint). Seu app então aparece no menu Compartilhar de todos os outros apps exatamente para esses tipos - um app de notas para texto, um player para vídeos.
Um link ou texto (compartilhar um vídeo do YouTube pelo app do YouTube envia o link) é lido com WebToApk.getSharedText() e anunciado como appmint:shared. Um arquivo é solicitado com AppMint.getOpenedFile() - peça quando estiver pronto, o arquivo espera por você. Isso importa para apps React, Vue e Angular: eles terminam de iniciar DEPOIS de a página carregar, então um app que só escutava o evento perdia o arquivo e mostrava apenas a tela inicial.
f.kind informa o que chegou: image, video, audio, document ou data. Fotos, vídeos e áudio chegam como f.url - um endereço que pode ser transmitido e navegado, que você coloca direto em <video src>, <img src> ou <audio src>, então um clipe de 300 MB nunca precisa caber na memória; f.getFile() busca o File quando você realmente precisa dos bytes. Arquivos de dados e documentos também vêm com f.file, além de f.text para formatos de texto e f.base64 para binários pequenos como atalhos. Uma foto compartilhada com legenda entrega a legenda também como texto compartilhado.
// A link or text someone shared
const shared = JSON.parse(WebToApk.getSharedText() || 'null');
if (shared) saveLink(shared.url || shared.text);
window.addEventListener('appmint:shared', e => saveLink(e.detail.url || e.detail.text));
// A photo, video, audio clip, document or data file — ask on startup, however late
const f = await AppMint.getOpenedFile(); // null if opened normally
if (f) {
if (f.kind === 'video') player.src = f.url; // streams, seekable
else if (f.kind === 'image') img.src = f.url;
else if (f.kind === 'document') await upload(await f.getFile()); // a real File
else importActivity(f.text ?? await f.file.text()); // data files
}
// Or listen, if you prefer
window.addEventListener('appmint:fileopen', e => handle(e.detail));10Sensores Bluetooth - Web Bluetooth padrão#
Cintas de frequência cardíaca, sensores de cadência e velocidade, medidores de potência, rolos inteligentes, balanças - qualquer coisa que fale Bluetooth LE.
Use navigator.bluetooth, exatamente o mesmo código que você escreveria para o Chrome no computador. O AppMint fornece isso dentro do app; um WebView comum do Android não tem, e é por isso que esse código não faz nada em outros criadores de app.
O seletor de dispositivos é desenhado pelo app, como o do navegador - sua página só alcança o dispositivo que o usuário escolheu.
Ative a permissão de Bluetooth na Etapa 4 (Access). Se seu código mencionar navigator.bluetooth, o AppMint a ativa para você quando você escolhe seu ZIP.
Indisponíveis: getDevices(), watchAdvertisements() e requestLEScan() - elas rejeitam com NotSupportedError, então verifique antes de usar.
const device = await navigator.bluetooth.requestDevice({
filters: [{ services: ['heart_rate'] }]
});
const server = await device.gatt.connect();
const service = await server.getPrimaryService('heart_rate');
const chr = await service.getCharacteristic('heart_rate_measurement');
chr.addEventListener('characteristicvaluechanged', e => {
const v = e.target.value; // a DataView
const bpm = (v.getUint8(0) & 1) ? v.getUint16(1, true) : v.getUint8(1);
document.getElementById('bpm').textContent = bpm;
});
await chr.startNotifications();
device.addEventListener('gattserverdisconnected', () => showReconnect());11Upload de fotos - o seletor de fotos do Android, sem permissão#
Use um input de arquivo normal. Não precisa adicionar mais nada.
No app, <input type="file" accept="image/*"> abre o seletor de fotos do Android: as fotos do usuário em grade, e sua página recebe só as que ele escolher. Sem permissão de armazenamento, sem o popup 'Allow access to photos?'. Funciona também em celulares antigos - o Google Play services instala o seletor neles.
- accept="image/*" - somente fotos
- accept="video/*" - somente vídeos
- accept="image/*,video/*" - ambos
- adicione multiple - o usuário pode escolher vários
- adicione capture="environment" - abre a câmera no lugar (exige Camera marcada na Etapa 4 (Access))
Misture outros arquivos (accept="image/*,.pdf") e o seletor de arquivos normal abre no lugar, para que o PDF também possa ser escolhido.
<input type="file" id="photos" accept="image/*" multiple>
<script>
document.getElementById('photos').onchange = function (e) {
for (const file of e.target.files) {
const img = document.createElement('img');
img.src = URL.createObjectURL(file);
document.body.appendChild(img);
}
};
</script>12Leia um QR code ou código de barras - uma linha#
AppMint.scanCode() abre a tela do scanner do Google. O usuário aponta a câmera e você recebe o código de volta.
Sem permissão de câmera. Sem elemento de vídeo. Nada para desenhar. O scanner dá zoom sozinho em códigos pequenos. Na primeira vez em um celular, o Google Play services baixa o scanner (alguns segundos).
A resposta traz value (o texto do código) e format (qr_code, ean_13, …). Para QR codes comuns você também recebe as partes prontas: url, wifi (ssid, password), phone, email, sms, geo, contact.
Se ok for false, code informa o motivo: cancelled (o usuário fechou), unavailable (sem Google Play services, ou o download falhou), busy (já há um scan aberto).
AppMint.scanCode só existe dentro do app - verifique antes.
async function scan() {
if (!window.AppMint || !AppMint.scanCode) return; // in a browser
const r = await AppMint.scanCode({ formats: ['qr_code'] });
if (!r.ok) return; // closed, or no scanner
console.log(r.value); // the text inside the code
if (r.url) location.href = r.url.url;
}
// Any code type: AppMint.scanCode()
// Let the user type it when it will not scan: AppMint.scanCode({ manualInput: true })13Passkeys - entre com impressão digital ou rosto, sem senha#
Se seu site já tem passkeys, elas agora funcionam dentro do app também. Um WebView normal do Android não faz passkeys; o AppMint conecta navigator.credentials ao gerenciador de passkeys do Android, então bibliotecas como SimpleWebAuthn, Hanko, Corbado ou Auth0 funcionam sem alterações.
Você precisa de um servidor que gere as opções da passkey e verifique a resposta (por exemplo, o SimpleWebAuthn no seu backend). Uma passkey não pode ser verificada dentro da página.
Duas coisas para configurar uma vez:
- Seu site precisa declarar que confia no app. Coloque o arquivo de
AppMint.passkey.assetLinks() em https://YOUR-DOMAIN/.well-known/assetlinks.json. Pegue-o do app instalado a PARTIR do Google Play - o Play assina seu app com uma chave própria, então o arquivo de um APK de teste não vai corresponder para os usuários do Play. O domínio deve ser exatamente o rpID do seu servidor (com www e sem www são diferentes).
- Seu servidor precisa aceitar a origem do app. A resposta do app diz origin 'android:apk-key-hash:…' em vez de https://… - pegue esse valor em
AppMint.passkey.origin() e adicione às origens permitidas do seu servidor.
Erros: NotAllowedError = o usuário cancelou ou não tem passkey neste celular. InvalidStateError = essa passkey já existe. SecurityError = o assetlinks.json não lista o app.
As passkeys exigem Android 9 ou mais recente com Google Play services - AppMint.passkey.isAvailable() informa. O preenchimento automático de passkey em um campo de texto (mediation: 'conditional') não está disponível em apps; inicie o login por um botão.
// Your normal passkey code works as it is:
const cred = await navigator.credentials.get({ publicKey: options });
// Or with your server's JSON options (SimpleWebAuthn style):
const options = await (await fetch('/passkey/login-options')).json();
const answer = await AppMint.passkey.get(options);
await fetch('/passkey/login-verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(answer)
});
// Once, on the Play-installed app — copy these to your server setup:
console.log(JSON.stringify(AppMint.passkey.assetLinks(), null, 2));
console.log(AppMint.passkey.origin());14Widget na tela inicial - mostre seus dados na tela inicial#
Seu app tem um widget para a tela inicial. O usuário o adiciona pela lista de widgets do celular (toque e segure a tela inicial, depois Widgets), ou você abre a tela de adicionar com AppMint.widget.requestPin().
O widget fica vazio ("Nothing to show yet") até a sua página preenchê-lo. Ele é um pequeno cartão do Android, não uma página web, então não consegue exibir seu HTML. Você dá um título, um valor grande, até seis linhas curtas e cores.
Chame AppMint.widget.set() quando o app abrir e de novo sempre que os dados exibidos mudarem. O widget guarda o último cartão, até depois de reiniciar. route é para onde um toque no widget leva dentro do seu app.
function updateWidget() {
if (!window.AppMint || !AppMint.widget) return; // in a browser
AppMint.widget.set({
title: 'Today',
value: '3 events',
lines: ['09:00 Dentist', '13:00 Lunch with Sam', '18:30 Gym'],
route: '/',
bg: '#0E7C86', ink: '#FFFFFF', accent: '#FFE08A'
});
}
updateWidget(); // on start-up, and after every change
// An "Add to home screen" button:
async function addWidget() {
if ((await AppMint.widget.placedCount()) > 0) return;
await AppMint.widget.requestPin();
}15Proteção contra cópia e menu de toque longo#
Duas chaves na Etapa 3 (Integrate), em Display Options. Ambas começam desativadas.
- Menu de toque longo em links e imagens: mantenha pressionado um link ou uma imagem para abrir, copiar, compartilhar ou salvar. Desativado por padrão, então um toque longo em um link não faz nada. Uma página ainda pode recusar o menu em um elemento cancelando o evento contextmenu.
- Proteção contra cópia: ninguém consegue selecionar, copiar ou recortar o texto do seu app. Digitar em formulários continua funcionando. Ela também mantém o menu de toque longo desativado.
Sua página consegue saber: AppMint.copyProtection é true quando a proteção está ativa e não existe quando está desativada (e no navegador). É somente leitura. Oculte seus próprios botões Copiar quando ela estiver ativa, porque não copiariam nada.
const protectedApp = !!(window.AppMint && window.AppMint.copyProtection === true);
// A Copy button would copy nothing, so hide it
document.querySelectorAll('[data-copy-button]').forEach(btn => {
btn.hidden = protectedApp;
});16{exampleCount} exemplos prontos - abra-os aqui#
Código funcional para copiar: enviar um SMS, ler contatos, mostrar uma notificação com imagem, bloqueio por impressão digital, informações do celular, salvar arquivos, sensores Bluetooth e mais. Cada um é curto e explicado em palavras simples.
Abre dentro do AppMint. Você pode pesquisar nele, e todo bloco de código tem um botão Copiar.
A mesma tela também tem 'All methods' - todas as {methodCount} coisas que seu app pode chamar, e cada uma vem com um exemplo funcional para copiar. Essa lista é gerada a partir do próprio runtime do app, então sempre corresponde exatamente ao que seu app consegue fazer.
17Salve os exemplos no seu celular ou computador#
Salva três arquivos em Downloads/AppMint/Guides:
- Exemplos - os {exampleCount} exemplos acima
- Lista de métodos - todos os {methodCount} métodos
- index.html - uma página de teste pronta
A página de teste tem botões para informações do dispositivo, vibração, notificações e o seletor de contatos. Compile-a como app para ver tudo funcionando e depois transforme-a no seu próprio app. Ela também abre normalmente em um navegador comum - nada quebra, apenas informa que não está dentro do app.
No app, o botão desta etapa salva esses arquivos no seu celular. Aqui eles são downloads diretos.
AppMint-examples.md AppMint-all-methods.md index.html18Despertador que toca sobre a tela de bloqueio#
Marque Alarm clock na etapa Access. scheduleNotificationEx com alarm: true toca seu som até o usuário dispensar ou adiar, mesmo com o app fechado. A repetição pode ser diária, semanal, em dias úteis, mensal ou anual, ou você escolhe os dias da semana.
Antes de agendar, consulte getAlarmSupport(). Ele responde 'ready', 'not_enabled', 'needs_exact_alarm' ou 'needs_notification_permission'. Depois stopAlarm(id), snoozeAlarm(id, minutes) e isAlarmRinging(id) controlam um alarme tocando.
const B = window.WebToApk;
if (B && B.getAlarmSupport && B.getAlarmSupport() === 'ready') {
B.scheduleNotificationEx('wake', JSON.stringify({
at: firstAtMs, alarm: true, title: 'Wake up', repeat: 'weekdays', sound: '/audio/alarm.mp3'
}));
}
document.getElementById('snooze').onclick = () => B.snoozeAlarm('wake', 10);19Volume do sistema#
getVolume(stream) lê o volume de mídia, alarme, toque, notificação ou chamada como JSON. setVolume(stream, percent, showUi) o altera. watchVolume(true) envia um evento appmint:volume a cada mudança. Silenciar o volume do toque exige acesso a Não perturbe: requestDndAccess().
if (window.WebToApk && WebToApk.getVolume) {
const v = JSON.parse(WebToApk.getVolume('alarm'));
if (v.percent === 0) alert('Alarm volume is off');
window.addEventListener('appmint:volume', (e) => console.log(e.detail.stream, e.detail.percent));
WebToApk.watchVolume(true);
}20Serviço em segundo plano, localização em segundo plano, geofences e tarefas#
startBackgroundService mantém sua página, o áudio e os temporizadores rodando enquanto o usuário está fora, com uma notificação. requestBackgroundLocation pede 'Allow all the time'. addGeofence dispara enter, exit ou dwell com o app fechado. AppMint.background.register executa o código da sua própria página a cada 15 minutos ou mais. Todos voltam a funcionar depois de reiniciar o celular.
Elas precisam da respectiva chave no assistente (etapa Access); sem ela, a chamada responde { ok: false, reason: 'not_enabled' }.
const r = JSON.parse(WebToApk.startBackgroundService(JSON.stringify({ title: 'Syncing', text: 'Keeping data fresh' })));
if (!r.ok) console.warn(r.reason);
if (AppMint.background.isBackgroundRun()) {
window.addEventListener('appmint:background', async (e) => {
await refreshData();
AppMint.background.done(e.detail.runId, { ok: true });
});
} else {
AppMint.background.register('refresh', { intervalMinutes: 60 });
}21Relatórios de falhas#
Marque Crash reports na etapa Integrate. Erros não tratados são registrados para você. logError(message, stack) registra um que você capturou, setCrashUserId(id) marca os relatórios com o seu próprio ID de usuário (nunca um e-mail) e getLastCrash() devolve o crash mais recente em JSON, ou uma string vazia. Veja os relatórios em Update the App, depois seu app, depois Crash reports.
if (window.WebToApk && WebToApk.logError) {
try { risky(); } catch (err) { WebToApk.logError(err.message, err.stack || ''); }
WebToApk.setCrashUserId('u_42');
const last = WebToApk.getLastCrash(); // '' when none
}22Integridade do dispositivo e Play Integrity#
Marque Device integrity checks na etapa Integrate. getDeviceIntegrity() devolve sinais de root, emulador e depurador em JSON; é um aviso, não uma prova. Para um veredito confiável, chame requestIntegrityToken(nonce, '') e envie o token do evento appmint:integrity para o SEU servidor, que pede o veredito ao Google Play.
const d = JSON.parse(WebToApk.getDeviceIntegrity());
if (d.rooted || d.emulator) alert('This phone is modified');
window.addEventListener('appmint:integrity', (e) => sendToServer(e.detail.token));
WebToApk.requestIntegrityToken(serverNonceBase64, '');23Ícones alternativos do app e TV#
Adicione até 4 ícones alternativos na etapa Basic. setAppIcon(name) troca o ícone do launcher ('default' volta ao original), getAppIcon() informa o atual e getAvailableIcons() lista todos. isTv() informa que o app está em uma televisão, para você mostrar um layout próprio para controle remoto.
if (window.WebToApk && WebToApk.setAppIcon) WebToApk.setAppIcon('dark');
if (window.WebToApk && WebToApk.isTv && WebToApk.isTv()) {
document.documentElement.classList.add('tv');
}24Login com Facebook, analytics e RevenueCat#
O login com Facebook é compatível. Na etapa Integrate, informe seu Facebook App ID e Client Token; o login abre em uma aba do navegador e volta ao app. Chame AppMint.facebook.login(['public_profile', 'email']) e envie o token ao seu servidor. AppMint.analytics (Firebase) e AppMint.facebook.logEvent (Meta App Events) só registram depois que o usuário concordou. AppMint.revenuecat cuida de assinaturas com a sua chave pública do Android.
const r = await AppMint.facebook.login(['public_profile', 'email']);
if (r.ok) await fetch('/api/login/facebook', { method: 'POST', body: JSON.stringify({ token: r.accessToken }) });
AppMint.analytics.log('share', { method: 'whatsapp' });
const offerings = await AppMint.revenuecat.getOfferings();25Texto de fotos, PDFs e widgets#
AppMint.textRecognition.recognize(image, { script }) lê texto em latim, devanágari, chinês, japonês ou coreano. AppMint.pdf.fromPage() e fromHtml(html, options) geram um PDF de verdade, sem caixa de impressão. AppMint.widget.set(card) preenche o widget da tela inicial.
const r = await AppMint.textRecognition.recognize(file, { script: 'devanagari' });
const pdf = await AppMint.pdf.fromHtml('<h1>Invoice</h1>', { fileName: 'invoice', save: 'share' });
await AppMint.widget.set({ title: 'Today', value: '3 events', route: '/calendar' });26Dados de saúde e beacons Bluetooth#
AppMint.health lê e grava passos, frequência cardíaca, sono, peso e exercícios pelo Health Connect; marque Health data e os tipos na etapa Access. AppMint.beacons.startScan() encontra beacons iBeacon e Eddystone e envia eventos appmint:beacon.
const a = await AppMint.health.isAvailable();
if (a.available) {
await AppMint.health.requestPermissions({ read: ['steps'] });
const steps = await AppMint.health.read('steps', { start: new Date(Date.now() - 864e5), end: new Date(), aggregate: true });
}
window.addEventListener('appmint:beacon', (e) => console.log(e.detail));
await AppMint.beacons.startScan();27Plugins prontos: SQLite, notificações, push, scanner, contatos#
Nas páginas que o próprio app serve, window.Capacitor.Plugins contém CapacitorSQLite (um banco de dados privado), LocalNotifications, PushNotifications, CapacitorBarcodeScanner e Contacts. Confira primeiro se o plugin existe: um app no modo Website só tem window.Capacitor nas páginas do próprio site, nunca em outros domínios.
const P = window.Capacitor && window.Capacitor.Plugins;
if (P && P.CapacitorBarcodeScanner) {
const s = await P.CapacitorBarcodeScanner.scanBarcode({ hint: 17 });
console.log(s.ScanResult);
}
if (P && P.LocalNotifications) await P.LocalNotifications.requestPermissions();