Бесплатно HTML в APK
Разработчикам

JavaScript API

Приложения, созданные в Free HTML to APK, добавляют встроенный мост как window.FreeApp. Вызывайте его из обычного JavaScript, из React или Vue, а также из игрового движка. Ваша страница продолжит работать и в обычном браузере, если вы защитите вызовы проверкой.

Начало работы

Мост может появиться чуть позже запуска ваших скриптов, поэтому выполняйте настройку в событии freeappready и сразу же, если window.FreeApp уже существует. Всегда предусматривайте запасной вариант для браузера.

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');

Вызовы, требующие разрешения (камера, микрофон, местоположение, уведомления), запрашивают его у пользователя при первом использовании. Включите нужное разрешение на шаге Разрешения в конструкторе; шаблоны включают то, что использует их код.

Обратная связь и интерфейс

FreeApp.brightness(level: number): void

От 0.0 до 1.0 для этого окна, отрицательное значение возвращает системную яркость.

FreeApp.fullscreen(on: boolean): void

Скрыть системные панели (полноэкранный режим).

FreeApp.keepAwake(on: boolean): void

Не выключать экран.

FreeApp.notify(title: string, body?: string): void

Мгновенное локальное уведомление (на Android 13+ запрашивает разрешение).

FreeApp.orientation(value: 'portrait' | 'landscape' | 'sensor_portrait' | 'sensor_landscape' | 'sensor' | 'unspecified'): void
FreeApp.print(): void

Печать страницы или сохранение в PDF.

FreeApp.scheduleNotification(title: string, body: string, delayMs: number, opts?: {id?: number, url?: string, repeatMs?: number, exact?: boolean}): number

Возвращает id.

FreeApp.speak(text: string, lang?: string): void

Встроенный синтез речи, язык вида 'en-US'.

FreeApp.statusBar(hexColor: string): void

Например, '#2563EB'.

FreeApp.stopSpeaking(): void
FreeApp.toast(message: string): void

Короткое встроенное всплывающее сообщение.

FreeApp.vibrate(ms?: number | number[]): void

Одиночная вибрация (по умолчанию 50 мс) или шаблон [пауза, вибрация, пауза, вибрация…].

Камера и сканирование

FreeApp.camera.capture(opts?: {maxSize?: number, quality?: number, mirror?: boolean}): Promise<{dataUrl, facing}>

Снимок из работающего предпросмотра.

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(): boolean
FreeApp.compressImage(dataUrl: string, opts?: {maxSize?: number, quality?: number}): Promise<{dataUrl, width, height, bytes}>

Встроенный, быстро работает на слабых телефонах.

FreeApp.saveToGallery(dataUrl: string, fileName?: string): Promise<{uri}>

Сохраняет изображение в «Фото».

FreeApp.scanCode(opts?: {formats?: string[] /* e.g. ['QR_CODE','EAN_13','CODE_128'] */, facing?: 'back' | 'front'}): Promise<{text, format}>

Полноэкранный сканер QR-кодов и штрихкодов с подсветкой; отклоняется с 'cancelled'.

FreeApp.torch(on: boolean): boolean

Фонарик, возвращает false, если вспышки нет.

Местоположение и датчики

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) | null

Возвращает функцию остановки.

FreeApp.location(opts?: {maxAge?: number}): Promise<{lat, lng, accuracy, altitude, speed, provider, time, fromLastKnown}>
FreeApp.maps(query: string): void

Открывает место или «lat,lng» в приложении карт.

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) | null

Возвращает функцию остановки или null, если недоступно.

FreeApp.sensors.stop(type: string): void
FreeApp.watchLocation(cb: (p) => void, opts?: {interval?: number, distance?: number}): () => void

Возвращает функцию остановки.

Устройство и приложение

FreeApp.app(): {packageName, versionName, versionCode, name, firstInstall}
FreeApp.battery(): {level: number /* 0-100 */, charging: boolean}
FreeApp.biometric(title: string, subtitle?: string): Promise<boolean>

Разблокировка по отпечатку пальца или лицу.

FreeApp.device(): {manufacturer, model, brand, androidVersion, sdkInt, language, screenWidth, screenHeight, density, isTablet, darkMode}
FreeApp.launchUrl(): string | null

Глубокая ссылка, которой было открыто приложение.

FreeApp.network(): 'wifi' | 'cellular' | 'ethernet' | 'vpn' | 'other' | 'none' | 'unknown'
FreeApp.on('deeplink', (d: {url}) => void): () => void

Ссылки, открытые во время работы приложения.

FreeApp.online(): boolean

Файлы, отправка и хранилище

FreeApp.copy(text: string): void

Копирование в буфер обмена.

FreeApp.open(url: string): void

Открыть во внешнем браузере или приложении.

FreeApp.paste(): string

Прочитать текст из буфера обмена.

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>

Сохраняет в «Загрузки»; FreeApp.on('saved', d => …) возвращает {name, ok, location}.

FreeApp.share(text: string, title?: string): void

Системное окно Android для отправки.

FreeApp.shareFile(fileName: string, mimeType: string, base64OrDataUrl: string): boolean

Отправить настоящий файл.

FreeApp.store.get(key: string): any | null
FreeApp.store.set(key: string, value: any): void

Постоянное встроенное хранилище «ключ — значение» (JSON).

NFC

FreeApp.nfc.read(): Promise<{tagId, techs: string[], writable, maxSize, records: Array<{type: "text" | "uri" | "mime" | "unknown", value, mime?, lang?}>}>

Ждёт метку; FreeApp.nfc.stop() отменяет ожидание.

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}>

Разрешения

FreeApp.hasPermission(name: string): boolean
FreeApp.openSettings(): void

Открывает страницу системных настроек этого приложения.

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}>

Реклама

Доступно, если в конструкторе включён AdMob. В тестовом режиме показывается тестовая реклама Google.

FreeApp.ads.loadBanner(unit?: string, position?: 'top' | 'bottom'): void

Загружает и показывает баннер. Без аргументов используются блок и позиция, заданные в конструкторе.

FreeApp.ads.showBanner(position?) / hideBanner() / isBannerLoaded()

Показать, скрыть или проверить баннер.

FreeApp.ads.loadInterstitial(unit?) / showInterstitial() / isInterstitialLoaded()

Полноэкранная реклама между экранами. Между показами полноэкранной рекламы соблюдается минимальный интервал.

FreeApp.ads.loadRewarded(unit?) / isRewardedLoaded()

Заранее загрузить рекламу с вознаграждением.

FreeApp.ads.showRewarded(): Promise<{ok, amount, type}>

Показывает рекламу с вознаграждением; выполняется, когда пользователь получил награду, и отклоняется, если он закрыл рекламу раньше.

FreeApp.ads.privacyOptionsRequired(): boolean / showPrivacyOptions()

Позволяет пользователям изменить свой выбор по согласию (в некоторых регионах это обязательно).

FreeApp.ads.adapterStatus(): {[network]: {installed, configured, ready, description, latencyMs}}

При медиации: какие партнёрские сети есть в приложении и какие настроены в вашем аккаунте AdMob.

FreeApp.ads.openAdInspector(): void

Открывает Ad Inspector от Google на тестовых устройствах.

FreeApp.ads.on(cb: (event) => void): () => void

События рекламы, такие как initialized, bannerLoaded, interstitialDismissed или rewardedFailed. Возвращает функцию отписки.

После покупки «Убрать рекламу» баннерная и полноэкранная реклама отключается и срабатывает событие adFree. Реклама с вознаграждением продолжает работать, потому что люди сами решают её смотреть.

Покупки в приложении

Доступно, когда в конструкторе включены «Покупки». Продавайте разовые товары, расходуемые товары вроде монет и подписки через собственный аккаунт Google Play. Сначала создайте товары в Play Console с теми же идентификаторами. Покупки работают в копии, установленной из Google Play, поэтому проверяйте через тестовый трек и тестировщика лицензий.

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[]>

Актуальные данные Google Play о ваших товарах: название, местная цена, валюта, а для подписок — период оплаты и предложения. Идентификаторы, которых Play не знает, пропускаются.

FreeApp.billing.buy(id: string, opts?: {offer?: string}): Promise<Purchase>

Открывает окно оплаты Google Play. Для подписки offer выбирает базовый план или идентификатор предложения. Отклоняется с ошибкой с кодом cancelled, pending, not_found, unavailable, busy, network или developer_error.

FreeApp.billing.owned(): string[] / isOwned(id): boolean

Чем владеет пользователь, сохранённое на телефоне, поэтому ответ приходит сразу и без интернета. Используйте при запуске, чтобы открыть платные функции.

FreeApp.billing.restore(): Promise<{purchases, owned}>

Заново запрашивает у Google Play всё, чем владеет пользователь, — для кнопки «Восстановить покупки».

FreeApp.billing.consume(idOrToken: string): Promise

Расходует разовый товар, чтобы его можно было купить снова. Товары, отмеченные в конструкторе как расходуемые, расходуются автоматически.

FreeApp.billing.isAdFree(): boolean

True, если у пользователя есть один из товаров «Убрать рекламу», заданных в конструкторе. Тогда приложение само отключает баннерную и полноэкранную рекламу.

FreeApp.billing.manageSubscriptions(id?: string): void

Открывает экран подписок Google Play, где пользователи отменяют или меняют тариф.

FreeApp.billing.on(cb: (event) => void): () => void

События покупок: purchase (открывайте доступ здесь), pending, owned (изменился список купленного: покупка, возврат или истёкшая подписка), failed и unavailable. Возвращает функцию отписки.

Слой совместимости

Страницы, написанные для других инструментов HTML-to-APK, которые вызывают window.AndroidAPI, продолжают работать: поддерживаются те же названия методов, ID обратных вызовов и названия событий, а вызовы рекламы безопасно ничего не делают в приложениях без рекламы.