API de JavaScript
Las apps creadas con Free HTML to APK inyectan un puente nativo como window.FreeApp. Llámalo desde JavaScript simple, desde React o Vue, o desde un motor de juegos. Tu página sigue funcionando en un navegador normal si proteges las llamadas.
Primeros pasos
El puente puede llegar un instante después de que se ejecuten tus scripts, así que haz la configuración nativa en el evento freeappready y también de inmediato si window.FreeApp ya existe. Ofrece siempre una alternativa para el navegador.
function setup() { FreeApp.statusBar('#E2552A'); FreeApp.toast('Hello from Android'); } if (window.FreeApp) setup(); else window.addEventListener('freeappready', setup, { once: true }); document.querySelector('#buzz').onclick = () => window.FreeApp ? FreeApp.vibrate(40) : console.log('Not in the app');
Las llamadas que necesitan un permiso (cámara, micrófono, ubicación, notificaciones) se lo piden al usuario la primera vez. Activa el permiso correspondiente en el paso Permisos del creador; las plantillas activan lo que usa su código.
Respuesta e interfaz
FreeApp.brightness(level: number): voidDe 0.0 a 1.0 para esta ventana; un valor negativo restaura el brillo del sistema.
FreeApp.fullscreen(on: boolean): voidOculta las barras del sistema (inmersivo).
FreeApp.keepAwake(on: boolean): voidMantener la pantalla encendida.
FreeApp.notify(title: string, body?: string): voidNotificación local instantánea (pide permiso en Android 13+).
FreeApp.orientation(value: 'portrait' | 'landscape' | 'sensor_portrait' | 'sensor_landscape' | 'sensor' | 'unspecified'): voidFreeApp.print(): voidImprime o guarda la página como PDF.
FreeApp.scheduleNotification(title: string, body: string, delayMs: number, opts?: {id?: number, url?: string, repeatMs?: number, exact?: boolean}): numberDevuelve el id.
FreeApp.speak(text: string, lang?: string): voidTexto a voz nativo, con idioma como 'en-US'.
FreeApp.statusBar(hexColor: string): voidPor ejemplo, '#2563EB'.
FreeApp.stopSpeaking(): voidFreeApp.toast(message: string): voidToast nativo breve.
FreeApp.vibrate(ms?: number | number[]): voidUna vibración única (50 ms por defecto) o un patrón [apagado, encendido, apagado, encendido…].
Cámara y escaneo
FreeApp.camera.capture(opts?: {maxSize?: number, quality?: number, mirror?: boolean}): Promise<{dataUrl, facing}>Foto fija de la vista previa en ejecución.
FreeApp.camera.start(opts?: {facing?: 'back' | 'front', element?: string | Element /* place the preview over this element */, behind?: boolean /* draw under a transparent page so HTML controls float on top */}): Promise<{facing}>FreeApp.camera.switch(): Promise<{facing}>; FreeApp.camera.torch(on: boolean): boolean; FreeApp.camera.stop(): void; FreeApp.camera.isOpen(): booleanFreeApp.compressImage(dataUrl: string, opts?: {maxSize?: number, quality?: number}): Promise<{dataUrl, width, height, bytes}>Nativo, rápido en teléfonos de gama baja.
FreeApp.saveToGallery(dataUrl: string, fileName?: string): Promise<{uri}>Guarda una imagen en Fotos.
FreeApp.scanCode(opts?: {formats?: string[] /* e.g. ['QR_CODE','EAN_13','CODE_128'] */, facing?: 'back' | 'front'}): Promise<{text, format}>Escáner nativo de QR / códigos de barras a pantalla completa con linterna; se rechaza con 'cancelled'.
FreeApp.torch(on: boolean): booleanLinterna; devuelve false si no hay flash.
Ubicación y sensores
FreeApp.address(lat: number, lng: number): Promise<{address, city, country, countryCode, postalCode, street}>FreeApp.compass(cb: (d: {heading: number /* degrees */, pitch: number, roll: number}) => void): (() => void) | nullDevuelve una función para detener.
FreeApp.location(opts?: {maxAge?: number}): Promise<{lat, lng, accuracy, altitude, speed, provider, time, fromLastKnown}>FreeApp.maps(query: string): voidAbre un lugar o "lat,lng" en la app de mapas.
FreeApp.sensors.start(type: 'accelerometer' | 'gyroscope' | 'magnetometer' | 'light' | 'proximity' | 'pressure' | 'gravity' | 'linear_acceleration' | 'rotation' | 'humidity' | 'temperature' | 'steps' | 'step_detector', cb: (d: {type, values: number[], x, y, z, accuracy, t}) => void, intervalMs?: number): (() => void) | nullDevuelve una función para detener, o null si no está disponible.
FreeApp.sensors.stop(type: string): voidFreeApp.watchLocation(cb: (p) => void, opts?: {interval?: number, distance?: number}): () => voidDevuelve una función para detener.
Dispositivo y app
FreeApp.app(): {packageName, versionName, versionCode, name, firstInstall}FreeApp.battery(): {level: number /* 0-100 */, charging: boolean}FreeApp.biometric(title: string, subtitle?: string): Promise<boolean>Desbloqueo con huella o rostro.
FreeApp.device(): {manufacturer, model, brand, androidVersion, sdkInt, language, screenWidth, screenHeight, density, isTablet, darkMode}FreeApp.launchUrl(): string | nullEl enlace profundo que abrió la app.
FreeApp.network(): 'wifi' | 'cellular' | 'ethernet' | 'vpn' | 'other' | 'none' | 'unknown'FreeApp.on('deeplink', (d: {url}) => void): () => voidEnlaces abiertos mientras la app está en ejecución.
FreeApp.online(): booleanArchivos, compartir y almacenamiento
FreeApp.copy(text: string): voidCopiar al portapapeles.
FreeApp.open(url: string): voidAbrir en el navegador o app externos.
FreeApp.paste(): stringLee el texto del portapapeles.
FreeApp.pickFile(opts?: {accept?: string[], multiple?: boolean}): Promise<{name, mime, size, dataUrl}>FreeApp.saveFile(name: string, mime: string, data: string /* base64 or data: URL */ | Blob): Promise<boolean>Guarda en Descargas; FreeApp.on('saved', d => …) entrega {name, ok, location}.
FreeApp.store.get(key: string): any | nullFreeApp.store.set(key: string, value: any): voidAlmacenamiento nativo persistente de clave/valor (JSON).
NFC
FreeApp.nfc.read(): Promise<{tagId, techs: string[], writable, maxSize, records: Array<{type: "text" | "uri" | "mime" | "unknown", value, mime?, lang?}>}>Espera una etiqueta; FreeApp.nfc.stop() cancela.
FreeApp.nfc.status(): 'ready' | 'disabled' | 'unsupported' | 'not_enabled_in_app'FreeApp.nfc.write(records: string | Array<{type: 'text' | 'uri' | 'mime', value: string, mime?: string}>): Promise<{written}>Permisos
FreeApp.hasPermission(name: string): booleanFreeApp.openSettings(): voidAbre la página de ajustes del sistema de esta app.
FreeApp.permissionStatus(name: 'camera' | 'microphone' | 'location' | 'notifications' | 'activity'): 'granted' | 'prompt' | 'blocked' | 'unavailable'FreeApp.requestPermission(name: 'camera' | 'microphone' | 'location' | 'notifications'): Promise<boolean>FreeApp.requestPermissions(names: string[]): Promise<{[name]: boolean}>Anuncios
Disponible cuando AdMob está activado en el creador. En modo de prueba se muestran los anuncios de prueba de Google.
FreeApp.ads.loadBanner(unit?: string, position?: 'top' | 'bottom'): voidCarga y muestra un banner. Sin argumentos usa la unidad y la posición definidas en el creador.
FreeApp.ads.showBanner(position?) / hideBanner() / isBannerLoaded()Muestra, oculta o comprueba el banner.
FreeApp.ads.loadInterstitial(unit?) / showInterstitial() / isInterstitialLoaded()Anuncios a pantalla completa entre pantallas. Se aplica un intervalo mínimo entre anuncios a pantalla completa.
FreeApp.ads.loadRewarded(unit?) / isRewardedLoaded()Precarga un anuncio con recompensa.
FreeApp.ads.showRewarded(): Promise<{ok, amount, type}>Muestra un anuncio con recompensa; se resuelve cuando el usuario obtuvo la recompensa y se rechaza si lo cerró antes de tiempo.
FreeApp.ads.privacyOptionsRequired(): boolean / showPrivacyOptions()Permite a los usuarios cambiar sus opciones de consentimiento (obligatorio en algunas regiones).
FreeApp.ads.adapterStatus(): {[network]: {installed, configured, ready, description, latencyMs}}Con mediación: qué redes asociadas están dentro de la app y cuáles ha configurado tu cuenta de AdMob.
FreeApp.ads.openAdInspector(): voidAbre el Ad Inspector de Google en dispositivos de prueba.
FreeApp.ads.on(cb: (event) => void): () => voidEventos de anuncios como initialized, bannerLoaded, interstitialDismissed o rewardedFailed. Devuelve una función para cancelar la suscripción.
Con una compra de Quitar anuncios, los anuncios de banner y a pantalla completa se detienen y se dispara el evento adFree. Los anuncios con recompensa siguen funcionando, porque la gente elige verlos.
Compras dentro de la app
Disponible cuando Compras está activado en el creador. Vende productos de compra única, consumibles como monedas y suscripciones a través de tu propia cuenta de Google Play. Primero crea los productos en Play Console, con los mismos ID. Las compras funcionan en una copia instalada desde Google Play, así que prueba con una pista de prueba y un tester de licencias.
const billing = window.FreeApp && FreeApp.billing; if (billing && billing.isOwned('pro_unlock')) unlockPro(); // Unlock in the purchase event: it also arrives for purchases finished while the app was closed. billing && billing.on(e => { if (e.event === 'purchase' && e.id === 'pro_unlock') unlockPro(); }); buyButton.onclick = async () => { try { await billing.buy('pro_unlock'); } catch (err) { if (err.code === 'pending') show('Payment pending'); else if (err.code !== 'cancelled') show('Purchase failed'); } };
FreeApp.billing.products(ids: string[]): Promise<Product[]>Los datos en vivo de Google Play sobre tus productos: título, precio local, moneda y, para las suscripciones, el periodo de facturación y las ofertas. Los ID que Play no conoce se omiten.
FreeApp.billing.buy(id: string, opts?: {offer?: string}): Promise<Purchase>Abre la pantalla de pago de Google Play. Para una suscripción, offer elige un plan base o un ID de oferta. Se rechaza con un error cuyo código es cancelled, pending, not_found, unavailable, busy, network o developer_error.
FreeApp.billing.owned(): string[] / isOwned(id): booleanLo que el usuario tiene, guardado en el teléfono, así que responde al instante y sin conexión. Úsalo al iniciar para desbloquear las funciones de pago.
FreeApp.billing.restore(): Promise<{purchases, owned}>Vuelve a pedir a Google Play todo lo que el usuario tiene, para un botón Restaurar compras.
FreeApp.billing.consume(idOrToken: string): PromiseConsume un producto de compra única para que se pueda volver a comprar. Los productos marcados como consumibles en el creador se consumen automáticamente.
FreeApp.billing.isAdFree(): booleanTrue cuando el usuario tiene uno de los productos de Quitar anuncios configurados en el creador. Entonces la app deja de mostrar por sí sola los anuncios de banner y a pantalla completa.
FreeApp.billing.manageSubscriptions(id?: string): voidAbre la pantalla de suscripciones de Google Play, donde los usuarios cancelan o cambian su plan.
FreeApp.billing.on(cb: (event) => void): () => voidEventos de compra: purchase (desbloquea aquí), pending, owned (cambió la lista de lo que tiene el usuario: una compra, un reembolso o una suscripción vencida), failed y unavailable. Devuelve una función para cancelar la suscripción.
Capa de compatibilidad
Las páginas escritas para otras herramientas de HTML a APK que llaman a window.AndroidAPI siguen funcionando: se admiten los mismos nombres de métodos, IDs de callback y nombres de eventos, y las llamadas de anuncios son inofensivas y no hacen nada en apps sin anuncios.