Empieza aquí
Cómo escribir HTML para tu app
Lo que el entorno de ejecución de la app expone a tu página (contactos, SMS, notificaciones, información del dispositivo, biometría) y las reglas para llamarlo correctamente
1Regla 1: comprueba siempre primero#
window.WebToApk solo existe dentro de tu app compilada. En un navegador no existe.
Por eso comprueba siempre antes de llamar. Si no lo haces, tu página falla en todas partes excepto dentro de la app.
Algunas cosas no necesitan comprobación alguna: navigator.vibrate(), speechSynthesis y Notification ya funcionan; la app los conecta por ti. Solo usa el código web normal.
// GOOD — safe everywhere
if (window.WebToApk) {
WebToApk.playClick();
}
// BAD — this breaks your page in a browser
WebToApk.playClick();2Regla 2: algunas respuestas llegan más tarde#
Leer contactos, SMS o el registro de llamadas lleva tiempo. Por eso no responden de inmediato. Si lo hicieran, tu app se congelaría.
Tú le das un nombre a la llamada (un id). La respuesta llega más tarde como un evento. Tú escuchas ese evento.
Nombres de 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);3Regla 3: dos interruptores deben estar ACTIVADOS#
Una función del teléfono necesita DOS cosas:
- Lo activaste en el paso 4 (Acceso) al compilar la app. Si no, recibes el error 'not_enabled' y el usuario no ve nada.
- El usuario dijo Sí en el teléfono. Si dijo No, recibes el error 'permission_denied'.
Maneja siempre ambos errores para que el usuario sepa qué pasó.
Forma más fácil: pickContact() y composeSms() NO necesitan ningún permiso. Úsalos cuando sirvan: hacen el mismo trabajo con menos complicaciones.
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);
}
});4Notificaciones con imágenes y botones#
notify() recibe una lista de opciones. Puedes añadir una imagen grande, un icono, botones y una barra de progreso.
channel puede ser: urgent, default, quiet u ongoing.
Usa ongoing:true para una notificación que el usuario no pueda descartar deslizando. Ojo: aun así puede quitarla desde los ajustes de Android. Ninguna app puede crear una imposible de quitar.
Usa tag para darle un nombre. Si vuelves a enviar con el mismo tag, reemplaza la anterior en lugar de añadir una nueva.
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();
});5Información del teléfono y huella digital#
getDeviceInfo() no necesita permiso. Te da la versión de Android, el ID de versión (buildId), el modelo del teléfono, el tamaño de pantalla, el nivel de batería y más.
Sobre el ID del dispositivo: NO hay IMEI ni número de serie. Android lo bloqueó para todas las apps desde Android 10. Usa getInstallId() en su lugar: un ID fijo para esta instalación que se mantiene igual tras las actualizaciones.
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
}));6Haz que tu página llene la pantalla#
Cuando activas Pantalla completa, la app oculta las barras de Android por ti. Pero añade este CSS para que tu contenido no quede oculto bajo la muesca del teléfono.
Importante: Pantalla completa oculta las barras de ANDROID (reloj, batería, botones de navegación). La barra de color propia de tu app en la parte superior es otro interruptor llamado «Mostrar barra superior». Si aún ves una barra después de activar Pantalla completa, esa es la que debes desactivar.
Además: prueba tu app rotada. La muesca pasa al lateral en 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 */
}7Las descargas conservan el nombre de archivo que elegiste#
Tu código de descarga normal ya funciona. Cuando tu página guarda un archivo (un informe en PDF, una exportación CSV, una copia de seguridad), la app lo captura y abre la hoja «Guardar como…» de Android con TU nombre de archivo ya escrito.
Esto cubre todas las formas habituales de hacerlo: <a download="...">, a.click() desde código, html2pdf / jsPDF, FileSaver.js saveAs() y window.open() sobre una URL blob. Un objeto File trae su propio nombre.
Si prefieres pedirlo directamente en lugar de construir una etiqueta <a>, usa AppMint.downloadFile().
Algo que debes saber: el nombre vive solo en el JavaScript de tu página; Android nunca lo ve por sí solo. Por eso indica siempre download= (o pasa un nombre), o el archivo se guardará 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');8Haz que el botón Atrás haga lo que TU app espera#
Si tu app cambia de pantalla mostrando y ocultando elementos (sin cambios de URL ni history.pushState), el botón Atrás de Android no puede ver esas pantallas: su historial está vacío, así que Atrás cierra la app desde cualquier lugar.
Registra un manejador de Atrás y decide tú mismo: devuelve true cuando manejaste la pulsación (cerraste un menú, volviste una pantalla) y false para que se ejecute el comportamiento normal: retroceder en el historial de la página, luego la confirmación de salida y después salir.
Las apps que usan history.pushState en cada pantalla no necesitan esto: Atrás ya recorre su historial. Y no te preocupes por congelar la app: si tu manejador se cuelga, el botón Atrás del teléfono sigue funcionando de forma nativa tras un 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();
});9Recibe lo que la gente comparte con tu app: enlaces, fotos, videos, archivos#
Activa «Recibir contenido compartido desde otras apps» en el asistente de compilación y marca lo que acepta tu app: texto y enlaces, archivos de datos (JSON, CSV, TXT, XML, Markdown, GPX, TCX, FIT), fotos, videos, audio, documentos (PDF, Word, Excel, PowerPoint). Tu app aparecerá entonces en el menú Compartir de todas las demás apps para esos tipos exactos: una app de notas para texto, un reproductor para videos.
Un enlace o texto (compartir un video de YouTube desde la app de YouTube envía su enlace) se lee con WebToApk.getSharedText() y se anuncia como appmint:shared. Un archivo se pide con AppMint.getOpenedFile(): pídelo cuando estés listo, el archivo espera. Esto importa para las apps de React, Vue y Angular: terminan de iniciarse DESPUÉS de que carga la página, así que una app que solo escuchaba el evento se perdía el archivo y mostraba solo su pantalla de inicio.
f.kind te dice qué llegó: image, video, audio, document o data. Las fotos, los videos y el audio llegan como f.url, una dirección reproducible y con búsqueda que pones directamente en <video src>, <img src> o <audio src>, para que un clip de 300 MB nunca tenga que caber en memoria; f.getFile() obtiene el File cuando realmente necesitas los bytes. Los archivos de datos y los documentos también llegan con f.file, además de f.text para formatos de texto y f.base64 para binarios pequeños como atajos. Una foto compartida con un pie de foto entrega también el pie como texto compartido.
// 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 estándar#
Bandas de frecuencia cardíaca, sensores de cadencia y velocidad, medidores de potencia, rodillos inteligentes, básculas: cualquier cosa que use Bluetooth LE.
Usa navigator.bluetooth, exactamente el mismo código que escribirías para Chrome en un escritorio. AppMint lo proporciona dentro de la app; un WebView de Android normal no lo tiene, por eso este código no hace nada en otros creadores de apps.
El selector de dispositivos lo dibuja la app, igual que el del navegador: tu página solo puede acceder al dispositivo que eligió el usuario.
Activa el permiso de Bluetooth en el paso 4 (Acceso). Si tu código menciona navigator.bluetooth, AppMint lo activa por ti cuando eliges tu ZIP.
No disponible: getDevices(), watchAdvertisements() y requestLEScan(): se rechazan con NotSupportedError, así que comprueba antes de usarlos.
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());11Subida de fotos: el selector de fotos de Android, sin permisos#
Usa un input de archivo normal. No necesitas añadir nada más.
En la app, <input type="file" accept="image/*"> abre el selector de fotos de Android: las fotos del usuario en una cuadrícula, y tu página recibe solo las que elija. Sin permiso de almacenamiento, sin ventana de «¿Permitir acceso a las fotos?». También funciona en teléfonos antiguos: Google Play services instala allí el selector.
- accept="image/*": solo fotos
- accept="video/*": solo videos
- accept="image/*,video/*": ambos
- añade multiple: el usuario puede elegir varias
- añade capture="environment": abre la cámara en su lugar (requiere Cámara marcada en el paso 4 (Acceso))
Si mezclas otros archivos (accept="image/*,.pdf"), se abre el selector de archivos normal, para que también se pueda elegir el PDF.
<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>12Escanea un código QR o de barras con una línea#
AppMint.scanCode() abre la pantalla del escáner de Google. El usuario apunta la cámara y recibes el código de vuelta.
Sin permiso de cámara. Sin elemento video. Nada que dibujar. El escáner hace zoom por sí solo con los códigos pequeños. La primera vez en un teléfono, Google Play services descarga el escáner (unos segundos).
La respuesta incluye value (el texto del código) y format (qr_code, ean_13, …). Para los códigos QR comunes también recibes las partes ya separadas: url, wifi (ssid, password), phone, email, sms, geo, contact.
Si ok es false, code te dice por qué: cancelled (el usuario lo cerró), unavailable (no hay Google Play services, o falló la descarga), busy (ya hay un escaneo abierto).
AppMint.scanCode solo existe dentro de la app: comprueba primero que esté disponible.
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: inicia sesión con huella o rostro, sin contraseña#
Si tu sitio web ya tiene passkeys, ahora también funcionan dentro de tu app. Un WebView de Android normal no puede usar passkeys; AppMint conecta navigator.credentials con el administrador de passkeys de Android, así que bibliotecas como SimpleWebAuthn, Hanko, Corbado o Auth0 funcionan sin cambios.
Necesitas un servidor que genere las opciones de la passkey y verifique la respuesta (por ejemplo, SimpleWebAuthn en tu backend). Una passkey no se puede verificar dentro de la página.
Dos cosas que configurar una sola vez:
- Tu sitio web debe indicar que confía en la app. Pon el archivo de
AppMint.passkey.assetLinks() en https://YOUR-DOMAIN/.well-known/assetlinks.json. Toma el archivo de la app instalada DESDE Google Play: Play firma tu app con su propia clave, así que el archivo de un APK de prueba no coincidirá para los usuarios de Play. El dominio debe ser exactamente el rpID de tu servidor (www y sin www son distintos).
- Tu servidor debe aceptar el origen de la app. La respuesta de la app indica el origen 'android:apk-key-hash:…' en lugar de https://…: obtén ese valor con
AppMint.passkey.origin() y añádelo a los orígenes permitidos de tu servidor.
Errores: NotAllowedError = el usuario canceló o no tiene passkey en este teléfono. InvalidStateError = esta passkey ya existe. SecurityError = assetlinks.json no incluye la app.
Las passkeys requieren Android 9 o superior con Google Play services; AppMint.passkey.isAvailable() te lo indica. El autocompletado de passkeys en un campo de texto (mediation: 'conditional') no está disponible en apps; inicia el acceso desde un botón.
// 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 de pantalla de inicio: muestra tus datos en la pantalla de inicio#
Tu app tiene un widget de pantalla de inicio. El usuario lo añade desde la lista de widgets del teléfono (mantén pulsada la pantalla de inicio y luego Widgets), o tú abres la hoja de añadir con AppMint.widget.requestPin().
El widget se queda vacío («Nada que mostrar todavía») hasta que tu página lo rellena. Es una pequeña tarjeta de Android, no una página web, así que no puede mostrar tu HTML. Le das un título, un valor grande, hasta seis líneas cortas y colores.
Llama a AppMint.widget.set() cuando se abra la app y de nuevo cada vez que cambien los datos que muestra. El widget conserva la última tarjeta, incluso después de reiniciar. route es adonde lleva a tu app un toque en el widget.
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();
}15Protección contra copia y menú de pulsación larga#
Dos interruptores en el paso 3 (Integrar), bajo Opciones de pantalla. Ambos están desactivados hasta que los actives.
- Menú de pulsación larga en enlaces e imágenes: mantén pulsado un enlace o una imagen para abrirlo, copiarlo, compartirlo o guardarlo. Desactivado de forma predeterminada, así que una pulsación larga en un enlace no hace nada. Una página aún puede rechazar el menú en un elemento cancelando el evento contextmenu.
- Protección contra copia: nadie puede seleccionar, copiar ni cortar el texto de tu app. Escribir en formularios sigue funcionando. También mantiene desactivado el menú de pulsación larga.
Tu página puede saberlo: AppMint.copyProtection es true cuando la protección está activada, y no existe cuando está desactivada (y en un navegador). Es de solo lectura. Oculta tus propios botones de Copiar cuando esté activada, porque no copiarían 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} ejemplos listos para usar: ábrelos aquí#
Código funcional que puedes copiar: enviar un SMS, leer contactos, mostrar una notificación con imagen, bloqueo por huella, información del teléfono, guardar archivos, sensores Bluetooth y más. Cada uno es breve y está explicado con palabras sencillas.
Se abre dentro de AppMint. Puedes buscar en él, y cada bloque de código tiene un botón Copiar.
La misma pantalla también tiene «Todos los métodos»: las {methodCount} cosas que tu app puede llamar, y cada una viene con un ejemplo funcional que puedes copiar. Esa lista se construye desde el propio entorno de ejecución de la app, así que siempre coincide exactamente con lo que tu app puede hacer.
17Guarda los ejemplos en tu teléfono o computadora#
Guarda tres archivos en Downloads/AppMint/Guides:
- Ejemplos: los {exampleCount} ejemplos de arriba
- Lista de métodos: los {methodCount} métodos
- index.html: una página de prueba lista
La página de prueba tiene botones para información del dispositivo, vibración, notificaciones y el selector de contactos. Compílala como app para ver todo funcionando y luego conviértela en tu propia app. También se abre sin problemas en un navegador normal: nada falla, solo indica que no está dentro de la app.
En la app, el botón de este paso los guarda en tu teléfono. Aquí son descargas directas.
AppMint-examples.md AppMint-all-methods.md index.html18Despertador que suena sobre la pantalla de bloqueo#
Marca Despertador en el paso Acceso. scheduleNotificationEx con alarm: true suena con tu sonido hasta que el usuario la descarta o la pospone, incluso con la app cerrada. La repetición puede ser diaria, semanal, entre semana, mensual o anual, o elegir días de la semana.
Antes de programar, consulta getAlarmSupport(). Responde 'ready', 'not_enabled', 'needs_exact_alarm' o 'needs_notification_permission'. Luego stopAlarm(id), snoozeAlarm(id, minutes) y isAlarmRinging(id) controlan una alarma que está sonando.
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);19Volumen del sistema#
getVolume(stream) lee el volumen de medios, alarma, timbre, notificaciones o llamadas como JSON. setVolume(stream, percent, showUi) lo cambia. watchVolume(true) envía un evento appmint:volume en cada cambio. Silenciar el volumen del timbre requiere acceso a No molestar: 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);
}20Servicio en segundo plano, ubicación en segundo plano, geovallas y tareas#
startBackgroundService mantiene tu página, el audio y los temporizadores en marcha mientras el usuario no está, con una notificación. requestBackgroundLocation pide «Permitir todo el tiempo». addGeofence dispara entrada, salida o permanencia con la app cerrada. AppMint.background.register ejecuta el código de tu propia página cada 15 minutos o más. Todos se reinician tras reiniciar el teléfono.
Esto necesita su interruptor en el asistente (paso Acceso); sin él, una llamada 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 });
}21Informes de fallos#
Marca Informes de fallos en el paso Integrar. Los errores no capturados se registran por ti. logError(message, stack) registra uno que capturaste, setCrashUserId(id) etiqueta los informes con tu propio ID de usuario (nunca un correo) y getLastCrash() devuelve el fallo más reciente en JSON, o una cadena vacía. Lee los informes en Actualizar la app, luego tu app y después Informes de fallos.
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
}22Integridad del dispositivo y Play Integrity#
Marca Comprobaciones de integridad del dispositivo en el paso Integrar. getDeviceIntegrity() devuelve en JSON indicios de root, emulador y depurador; es una advertencia, no una prueba. Para un veredicto fiable, llama a requestIntegrityToken(nonce, '') y envía el token del evento appmint:integrity a TU servidor, que pide el veredicto a 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, '');23Iconos alternativos de la app y TV#
Añade hasta 4 iconos alternativos en el paso Básico. setAppIcon(name) cambia el icono del launcher ('default' vuelve al original), getAppIcon() indica el actual y getAvailableIcons() los lista. isTv() te dice que la app está en un televisor, para mostrar un diseño apto para control remoto.
if (window.WebToApk && WebToApk.setAppIcon) WebToApk.setAppIcon('dark');
if (window.WebToApk && WebToApk.isTv && WebToApk.isTv()) {
document.documentElement.classList.add('tv');
}24Inicio de sesión con Facebook, analítica y RevenueCat#
Se admite el inicio de sesión con Facebook. En el paso Integrar introduce tu Facebook App ID y Client Token; el inicio de sesión se abre en una pestaña del navegador y vuelve a la app. Llama a AppMint.facebook.login(['public_profile', 'email']) y envía el token a tu servidor. AppMint.analytics (Firebase) y AppMint.facebook.logEvent (Meta App Events) registran solo después de que el usuario haya dado su consentimiento. AppMint.revenuecat gestiona las suscripciones con tu clave pública de 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, PDF y widgets#
AppMint.textRecognition.recognize(image, { script }) lee texto en escritura latina, devanagari, china, japonesa o coreana. AppMint.pdf.fromPage() y fromHtml(html, options) crean un PDF real sin cuadro de impresión. AppMint.widget.set(card) rellena el widget de la pantalla de inicio.
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' });26Datos de salud y balizas Bluetooth#
AppMint.health lee y escribe pasos, frecuencia cardíaca, sueño, peso y ejercicio mediante Health Connect; marca Datos de salud y los tipos en el paso Acceso. AppMint.beacons.startScan() encuentra balizas iBeacon y Eddystone y envía 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 listos para usar: SQLite, notificaciones, push, escáner, contactos#
En las páginas que la app sirve por sí misma, window.Capacitor.Plugins contiene CapacitorSQLite (una base de datos privada), LocalNotifications, PushNotifications, CapacitorBarcodeScanner y Contacts. Comprueba primero que el plugin exista: una app en modo Sitio web tiene window.Capacitor solo en las páginas de su propio sitio, nunca en otros dominios.
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();