Create Free APK

Reference

JavaScript bridge API

Every native method a page inside a generated app can call - 138 of them, across 22 families. Extracted from the app runtime itself, so this list is exactly what ships.

AppMintAppwrightOne bridge, identical in both builders.

I am using

Check before you call. The bridge exists only inside a generated app. In a browser - including while you are developing - window.WebToApk is undefined, so guard every call and give the page something sensible to do without it.

if (window.WebToApk && window.WebToApk.isOwned('pro_unlock')) unlockPro();
else showWebCheckout();

Anything asynchronous answers with a DOM event rather than a return value - window.addEventListener('appmint:…', …). The method's description names the event it fires. Start with writing HTML for your app and the Web APIs bridge, which cover the conventions a method list cannot.

Purchases and entitlements

Selling from your own page, and asking what this buyer already owns. See the in-app purchases guide for the code around these.

isPremium#

window.WebToApk.isPremium(): Boolean

True when the user owns "Remove Ads" (Play Billing entitlement, auto-restored on every launch). Matches the reason 'ad_free' in 'appmint:ad-unavailable'.

startRemoveAdsPurchase#

window.WebToApk.startRemoveAdsPurchase()

Launch the Google Play "Remove Ads" purchase flow from the page (user-initiated, e.g. the app's own "Remove ads" button). Without this the ONLY entry point was the native side-menu item - unreachable in apps built without the side menu. Outcome reaches the page via 'appmint:premium' {premium:true}; already-owned re-notifies immediately; flow errors surface as a native toast.

isOwned#

window.WebToApk.isOwned(productId: String): Boolean

Instant on-device ownership check (one-time product owned OR sub active).

getOwnedProducts#

window.WebToApk.getOwnedProducts(): String

JSON array of every owned product ID - for gating whole screens at startup.

getProducts#

window.WebToApk.getProducts(requestId: String, productIdsJson: String)

Asks Play for live details of the given product IDs (JSON array of strings). Result arrives as 'appmint:products' {requestId, products:[{productId, type, title, description, price, currency, period?, owned}]}. IDs Play doesn't know are absent from the result - the page hides those items.

purchase#

window.WebToApk.purchase(productId: String)

Opens Google's purchase sheet for [productId] (one-time product or subscription - resolved automatically). Grant ONLY on 'appmint:purchase' {productId, owned:true}; failures fire 'appmint:purchase-failed' {reason}. Already-owned re-fires the purchase event so the page can idempotently unlock.

Ads

Showing an interstitial or a rewarded ad on your own cue, and telling the shell when a navigation starts and settles so it does not interrupt one.

isRewardedAdReady#

window.WebToApk.isRewardedAdReady(): Boolean

True when a rewarded ad is loaded and can be shown right now. Let the page enable/disable its "Watch ad" button off this (poll or call before showing).

isInterstitialAdReady#

window.WebToApk.isInterstitialAdReady(): Boolean

True when an interstitial is loaded AND past its cooldown (matches exactly what showInterstitialAd() will accept - no "ready" that then refuses to show).

showRewardedAd#

window.WebToApk.showRewardedAd()

Show a rewarded ad NOW (user-initiated - the only policy-compliant way to show rewarded). Pair it with trigger mode "On Demand" in the wizard to get rewarded ads with NO automatic app-open/navigation ads at all. The reward is delivered back to the page via deliverRewardToPage(): window.addEventListener('appmint:reward', e => { /* e.detail = {type,amount} */ }) When the ad closes, 'appmint:ad-closed' fires with {adType:'rewarded', earned} - earned is false if the user backed out early, so grant nothing in that case. Not showable right now → skipped + 'appmint:ad-unavailable' fired with the reason.

showInterstitialAd#

window.WebToApk.showInterstitialAd()

Show an interstitial NOW (e.g. between levels / on a deliberate action). Not showable right now → skipped + 'appmint:ad-unavailable' with the reason.

notifyUserAction#

window.WebToApk.notifyUserAction()

Internal (called by the injected tap counter, not creator code): one meaningful user interaction happened in the page. Counts toward the "interstitial every N actions" cadence ONLY while no URL-based navigation has ever been observed - single-HTML apps that switch screens via DOM show/hide never change the URL, so without this their navigation counter stayed at 0 forever and configured ads never showed (TicTrek complaint class, 2026-07-26). URL/SPA apps are unaffected: their first real navigation permanently switches counting back to URL signals.

Notifications on the device

Posting and scheduling without a server, the POST_NOTIFICATIONS grant, exact alarms, and custom sounds.

showNotification#

window.WebToApk.showNotification(title: String, body: String, icon: String, tag: String): Boolean

The ORIGINAL four-argument form, kept verbatim as an adapter so every app already in the wild and every guide snippet keeps working. It now forwards to the rich builder - which means the `icon` argument, silently discarded by the old implementation, finally does something.

notify#

window.WebToApk.notify(optionsJson: String): Boolean

Rich notification. See NotificationBridge.show for the full option list: channel (urgent|default|quiet|ongoing), image, largeIcon, bigText, ongoing, actions, progress, sound, silent, group, color. Image fetching and decoding happen off the main thread, so a remote image can never block the page.

getNotificationPermission#

window.WebToApk.getNotificationPermission(): String

The CURRENT notification permission, read live from the OS. The polyfill used to hardcode `Notification.permission = 'default'` on every page load, so the overwhelmingly common page pattern if (Notification.permission === 'granted') new Notification(...) never fired - not even for a user who had already granted it, and not on Android 12 and below where notifications need no runtime grant at all.

requestNotificationPermission#

window.WebToApk.requestNotificationPermission(): String

Reading and requesting the POST_NOTIFICATIONS grant, and managing notifications the page already scheduled. Posting and scheduling are documented on their own methods.

Described by its group, Notification permission, cancelling and listing, rather than on its own.

scheduleNotification#

window.WebToApk.scheduleNotification(id: String, title: String, body: String, triggerAtMillis: String, repeat: String): Boolean

===== Tier-2: SCHEDULED local notifications (reminders) ===== Fire a notification at a future time even when the app is closed - backed by AlarmManager + persistence so it survives reboot. Dormant unless called. JS contract (all args strings; triggerAtMillis = epoch millis): window.WebToApk.scheduleNotification(id, title, body, triggerAtMillis, repeat) repeat ∈ "none" | "minutely" | "hourly" | "daily" | "weekly" window.WebToApk.cancelNotification(id) window.WebToApk.cancelAllNotifications() window.WebToApk.getScheduledNotifications() -> JSON array string

cancelNotification#

window.WebToApk.cancelNotification(id: String): Boolean

Reading and requesting the POST_NOTIFICATIONS grant, and managing notifications the page already scheduled. Posting and scheduling are documented on their own methods.

Described by its group, Notification permission, cancelling and listing, rather than on its own.

cancelAllNotifications#

window.WebToApk.cancelAllNotifications(): Boolean

Reading and requesting the POST_NOTIFICATIONS grant, and managing notifications the page already scheduled. Posting and scheduling are documented on their own methods.

Described by its group, Notification permission, cancelling and listing, rather than on its own.

getScheduledNotifications#

window.WebToApk.getScheduledNotifications(): String

Reading and requesting the POST_NOTIFICATIONS grant, and managing notifications the page already scheduled. Posting and scheduling are documented on their own methods.

Described by its group, Notification permission, cancelling and listing, rather than on its own.

scheduleNotificationEx#

window.WebToApk.scheduleNotificationEx(id: String, optionsJson: String): Boolean

The full form of [scheduleNotification]: everything `notify()` accepts, fired at a future time with the app closed. WebToApk.scheduleNotificationEx('dose-1', JSON.stringify({ at: Date.now() + 3600000, // epoch millis (alias: triggerAt) repeat: 'daily', // none|minutely|hourly|daily|weekly title: 'Time for your dose', body: 'Vitamin D - 1 tablet', sound: '/audio/chime.mp3', // a file in YOUR bundle; 'silent' for none openApp: true // open the app itself, over the lock screen })); A bundled sound is copied into place during this call, so a wrong file name is reported now (false) rather than turning into the default sound days later.

getAlarmPrecision#

window.WebToApk.getAlarmPrecision(): String

"exact" - reminders fire on the minute. "inexact" - the user (or the OEM) turned exact alarms off for this app, so Doze may batch them by minutes. Ask [requestExactAlarms] to fix it. A page that must be on time should check this instead of assuming.

requestExactAlarms#

window.WebToApk.requestExactAlarms(): Boolean

Opens the system screen where exact alarms are granted. Android 12+.

canOpenAppAtTime#

window.WebToApk.canOpenAppAtTime(): String

Whether a scheduled notification may put the APP ITSELF on screen (`openApp`). "granted" - a full-screen alert will show, over the lock screen. "denied" - Android 14+ withheld it; call requestOpenAppPermission(). There is no third answer where the app silently launches itself in the background: Android has blocked background activity starts since Android 10, and a full-screen intent is the whole of what remains.

requestOpenAppPermission#

window.WebToApk.requestOpenAppPermission(): Boolean

Opens the system screen where the full-screen alert is granted. Android 14+.

prepareNotificationSound#

window.WebToApk.prepareNotificationSound(spec: String): Boolean

Copies a bundled audio file into place ahead of time and reports whether it is usable as a notification sound. Optional - `notify()` and `scheduleNotificationEx()` do it themselves - but it lets a settings screen verify a sound the moment the user picks it.

clearNotificationSounds#

window.WebToApk.clearNotificationSounds(): Int

Drops the copied sound files. They are re-created on next use.

The push inbox

Messages the creator sent from a dashboard, kept on the device so the page can show a history rather than only a tray pop-up.

getPushInbox#

window.WebToApk.getPushInbox(): String

Appwright Push inbox (creator-sent messages, newest first, max 50): JSON array of {id, title, body, imageUrl, link, receivedAt, read}. Always the full current truth - an app that mounts late loses nothing by having missed the live `appmint:push` events.

markPushRead#

window.WebToApk.markPushRead(id: String): Boolean

Marks one push message read; pass "" to mark every message read.

dismissPushMessage#

window.WebToApk.dismissPushMessage(id: String): Boolean

Dismisses ONE message from the inbox - the per-row swipe-away an inbox screen needs (clearPushInbox is all-or-nothing). REVERSIBLE by design: the entry is hidden, not destroyed, so the UI can offer "Undo" for a few seconds after the swipe. Local only: the tray notification and the creator's history are untouched.

restorePushMessage#

window.WebToApk.restorePushMessage(id: String): Boolean

Undo a dismiss: puts the message back in its place in the inbox.

clearPushInbox#

window.WebToApk.clearPushInbox(): Boolean

Empties the push inbox (the tray and server history are untouched).

Sign-in

A real Google account chooser, native rather than a web redirect.

signInWithGoogle#

window.WebToApk.signInWithGoogle(serverClientId: String, callbackId: String)

===== Native Google Sign-In via Credential Manager ===== Bypasses Google's Android WebView OAuth block (disallowed_useragent error) by running the sign-in flow in native Android and bridging just the resulting Google ID token back to JS, where the Firebase Web SDK consumes it via GoogleAuthProvider.credential(idToken) + signInWithCredential(...). JS contract: window.WebToApk.signInWithGoogle(serverClientId, callbackId) -> on completion, window.__webToApkAuth[callbackId](resultJsonString) is called resultJsonString = '{"ok":true,"idToken":"...","email":"...","displayName":"..."}' or '{"ok":false,"error":"<message>"}'

signOutGoogle#

window.WebToApk.signOutGoogle(callbackId: String)

Clears the saved Credential Manager state so the next signInWithGoogle call shows the account picker again (rather than silently re-selecting the last account). Generated apps should call this on user-initiated sign-out, in addition to firebase.auth().signOut().

Biometrics and the keystore

Fingerprint and face unlock, and hardware-backed keys the page can encrypt with but never read.

generateKey#

window.WebToApk.generateKey(alias: String, optionsJson: String): String

Creates (or replaces) an AES-256-GCM key called [alias]. @param optionsJson `{"requireAuth":bool, "authValiditySeconds":int, "strongBox":bool}`. `requireAuth` binds the key to the device lock; `strongBox` fails outright on devices without a secure element rather than handing back a weaker key than was asked for. @return JSON `{ok, alias, hardwareBacked, error}`.

hasKey#

window.WebToApk.hasKey(alias: String): String

True when a key called [alias] exists in this app's keystore.

listKeys#

window.WebToApk.listKeys(): String

JSON array of every key alias this page has created.

encryptWithKey#

window.WebToApk.encryptWithKey(alias: String, base64Plaintext: String): String

Encrypts base64 [base64Plaintext] with [alias]. Each call generates its own IV, so a large file is encrypted chunk by chunk - chunks are independent and may be decrypted in any order. @return JSON `{ok, base64, error}` where `base64` is `IV || ciphertext || tag`.

decryptWithKey#

window.WebToApk.decryptWithKey(alias: String, base64Payload: String): String

Reverses [encryptWithKey]. Fails with `error:"no-such-key"` once the key has been deleted - the payload is then unrecoverable by design. @return JSON `{ok, base64, error}`.

deleteKey#

window.WebToApk.deleteKey(alias: String): String

Destroys [alias] permanently. Everything encrypted with it becomes undecryptable - including by this app, and by anyone holding the device.

isKeyHardwareBacked#

window.WebToApk.isKeyHardwareBacked(alias: String): String

True when [alias] lives in the TEE or a secure element rather than in software. Worth checking before promising a user that destroying a key is irreversible.

isKeystoreEnabled#

window.WebToApk.isKeystoreEnabled(): String

Whether the creator enabled the keystore bridge for this app.

isBiometricAvailable#

window.WebToApk.isBiometricAvailable(): String

WebToApk.isBiometricAvailable() -> "available" | "no_hardware" | "not_enrolled" | "unavailable" WebToApk.authenticateBiometric(id, title, subtitle) Result is delivered to the same __webToApkAuth callback table the Google Sign-In bridge uses: window.__webToApkAuth = window.__webToApkAuth || {}; window.__webToApkAuth['unlock'] = function (json) { var r = JSON.parse(json); // {ok:true} | {ok:false,error:"..."} if (r.ok) showApp(); }; WebToApk.authenticateBiometric('unlock', 'Unlock', 'Use your fingerprint');

Described by its group, Fingerprint / biometric bridge, rather than on its own.

authenticateBiometric#

window.WebToApk.authenticateBiometric(callbackId: String, title: String, subtitle: String)

WebToApk.isBiometricAvailable() -> "available" | "no_hardware" | "not_enrolled" | "unavailable" WebToApk.authenticateBiometric(id, title, subtitle) Result is delivered to the same __webToApkAuth callback table the Google Sign-In bridge uses: window.__webToApkAuth = window.__webToApkAuth || {}; window.__webToApkAuth['unlock'] = function (json) { var r = JSON.parse(json); // {ok:true} | {ok:false,error:"..."} if (r.ok) showApp(); }; WebToApk.authenticateBiometric('unlock', 'Unlock', 'Use your fingerprint');

Described by its group, Fingerprint / biometric bridge, rather than on its own.

authenticateBiometricEx#

window.WebToApk.authenticateBiometricEx(callbackId: String, optionsJson: String)

The full form. [optionsJson] accepts: title, subtitle, description, negativeButtonText, allowDeviceCredential (PIN/pattern/password fallback), strong (require BIOMETRIC_STRONG - use for anything payment-shaped). Errors are distinct codes, not one boolean: no_hardware | none_enrolled | hw_unavailable | lockout | lockout_permanent | user_cancel | permission_missing.

Files: a folder the user granted

A directory chosen once, readable and writable afterwards, with permission that survives reboots.

requestFolderAccess#

window.WebToApk.requestFolderAccess(requestId: String)

Play-safe folder access using ACTION_OPEN_DOCUMENT_TREE with persistable URI permissions. JavaScript usage: window.WebToApk.requestFolderAccess('myRequestId') window.WebToApk.hasFolderAccess() // "true" / "false" window.WebToApk.getFolderAccessUri() // "content://..." or "" window.WebToApk.clearFolderAccess() // releases persisted access Callback from Android → JS: window.WebToApkOnFolderAccessResult(success, folderUri, requestId)

Described by its group, Native Folder Access (SAF) Bridge, rather than on its own.

hasFolderAccess#

window.WebToApk.hasFolderAccess(): String

Play-safe folder access using ACTION_OPEN_DOCUMENT_TREE with persistable URI permissions. JavaScript usage: window.WebToApk.requestFolderAccess('myRequestId') window.WebToApk.hasFolderAccess() // "true" / "false" window.WebToApk.getFolderAccessUri() // "content://..." or "" window.WebToApk.clearFolderAccess() // releases persisted access Callback from Android → JS: window.WebToApkOnFolderAccessResult(success, folderUri, requestId)

Described by its group, Native Folder Access (SAF) Bridge, rather than on its own.

isFolderAccessEnabled#

window.WebToApk.isFolderAccessEnabled(): String

Play-safe folder access using ACTION_OPEN_DOCUMENT_TREE with persistable URI permissions. JavaScript usage: window.WebToApk.requestFolderAccess('myRequestId') window.WebToApk.hasFolderAccess() // "true" / "false" window.WebToApk.getFolderAccessUri() // "content://..." or "" window.WebToApk.clearFolderAccess() // releases persisted access Callback from Android → JS: window.WebToApkOnFolderAccessResult(success, folderUri, requestId)

Described by its group, Native Folder Access (SAF) Bridge, rather than on its own.

getFolderAccessUri#

window.WebToApk.getFolderAccessUri(): String

Play-safe folder access using ACTION_OPEN_DOCUMENT_TREE with persistable URI permissions. JavaScript usage: window.WebToApk.requestFolderAccess('myRequestId') window.WebToApk.hasFolderAccess() // "true" / "false" window.WebToApk.getFolderAccessUri() // "content://..." or "" window.WebToApk.clearFolderAccess() // releases persisted access Callback from Android → JS: window.WebToApkOnFolderAccessResult(success, folderUri, requestId)

Described by its group, Native Folder Access (SAF) Bridge, rather than on its own.

clearFolderAccess#

window.WebToApk.clearFolderAccess()

Play-safe folder access using ACTION_OPEN_DOCUMENT_TREE with persistable URI permissions. JavaScript usage: window.WebToApk.requestFolderAccess('myRequestId') window.WebToApk.hasFolderAccess() // "true" / "false" window.WebToApk.getFolderAccessUri() // "content://..." or "" window.WebToApk.clearFolderAccess() // releases persisted access Callback from Android → JS: window.WebToApkOnFolderAccessResult(success, folderUri, requestId)

Described by its group, Native Folder Access (SAF) Bridge, rather than on its own.

listFolderEntries#

window.WebToApk.listFolderEntries(relativePath: String): String

Play-safe folder access using ACTION_OPEN_DOCUMENT_TREE with persistable URI permissions. JavaScript usage: window.WebToApk.requestFolderAccess('myRequestId') window.WebToApk.hasFolderAccess() // "true" / "false" window.WebToApk.getFolderAccessUri() // "content://..." or "" window.WebToApk.clearFolderAccess() // releases persisted access Callback from Android → JS: window.WebToApkOnFolderAccessResult(success, folderUri, requestId)

Described by its group, Native Folder Access (SAF) Bridge, rather than on its own.

readTextFile#

window.WebToApk.readTextFile(relativePath: String): String

Play-safe folder access using ACTION_OPEN_DOCUMENT_TREE with persistable URI permissions. JavaScript usage: window.WebToApk.requestFolderAccess('myRequestId') window.WebToApk.hasFolderAccess() // "true" / "false" window.WebToApk.getFolderAccessUri() // "content://..." or "" window.WebToApk.clearFolderAccess() // releases persisted access Callback from Android → JS: window.WebToApkOnFolderAccessResult(success, folderUri, requestId)

Described by its group, Native Folder Access (SAF) Bridge, rather than on its own.

writeTextFile#

window.WebToApk.writeTextFile(relativePath: String, content: String): String

Play-safe folder access using ACTION_OPEN_DOCUMENT_TREE with persistable URI permissions. JavaScript usage: window.WebToApk.requestFolderAccess('myRequestId') window.WebToApk.hasFolderAccess() // "true" / "false" window.WebToApk.getFolderAccessUri() // "content://..." or "" window.WebToApk.clearFolderAccess() // releases persisted access Callback from Android → JS: window.WebToApkOnFolderAccessResult(success, folderUri, requestId)

Described by its group, Native Folder Access (SAF) Bridge, rather than on its own.

mkdir#

window.WebToApk.mkdir(relativePath: String): String

Play-safe folder access using ACTION_OPEN_DOCUMENT_TREE with persistable URI permissions. JavaScript usage: window.WebToApk.requestFolderAccess('myRequestId') window.WebToApk.hasFolderAccess() // "true" / "false" window.WebToApk.getFolderAccessUri() // "content://..." or "" window.WebToApk.clearFolderAccess() // releases persisted access Callback from Android → JS: window.WebToApkOnFolderAccessResult(success, folderUri, requestId)

Described by its group, Native Folder Access (SAF) Bridge, rather than on its own.

renameEntry#

window.WebToApk.renameEntry(relativePath: String, newName: String): String

Play-safe folder access using ACTION_OPEN_DOCUMENT_TREE with persistable URI permissions. JavaScript usage: window.WebToApk.requestFolderAccess('myRequestId') window.WebToApk.hasFolderAccess() // "true" / "false" window.WebToApk.getFolderAccessUri() // "content://..." or "" window.WebToApk.clearFolderAccess() // releases persisted access Callback from Android → JS: window.WebToApkOnFolderAccessResult(success, folderUri, requestId)

Described by its group, Native Folder Access (SAF) Bridge, rather than on its own.

deleteEntry#

window.WebToApk.deleteEntry(relativePath: String, recursive: String): String

Play-safe folder access using ACTION_OPEN_DOCUMENT_TREE with persistable URI permissions. JavaScript usage: window.WebToApk.requestFolderAccess('myRequestId') window.WebToApk.hasFolderAccess() // "true" / "false" window.WebToApk.getFolderAccessUri() // "content://..." or "" window.WebToApk.clearFolderAccess() // releases persisted access Callback from Android → JS: window.WebToApkOnFolderAccessResult(success, folderUri, requestId)

Described by its group, Native Folder Access (SAF) Bridge, rather than on its own.

Files: one file at a time

The system picker and chunked binary read/write, for files too large to hold in a string.

pickFile#

window.WebToApk.pickFile(requestId: String, mimeFilter: String)

Opens the system single-file picker (ACTION_OPEN_DOCUMENT). Unlike [requestFolderAccess] this grants access to exactly one file, which is what an editor or a shredder should ask for; the read/write grant is persisted so the URI keeps working after the process is killed. @param mimeFilter e.g. "image/\*", "application/pdf", or "" for any file. (The star is backslash-escaped only because Kotlin block comments nest.)

readFileBase64#

window.WebToApk.readFileBase64(pathOrUri: String, offset: Long, length: Long): String

Reads up to [length] raw bytes starting at [offset] and returns them base64. @param pathOrUri a `content://` URI from [pickFile], or a path relative to the folder granted by [requestFolderAccess]. @param length bytes to read; clamped to 4 MB per call. 0 or negative means "as much as one call allows". @return JSON `{ok, base64, bytesRead, offset, size, eof, error}`. `eof` tells a chunking loop when to stop without having to compare against `size` itself.

writeFileBase64#

window.WebToApk.writeFileBase64(pathOrUri: String, base64: String, offset: Long, mode: String): String

Writes raw bytes and confirms how many landed. @param mode `"patch"` writes at [offset] leaving the rest of the file intact (what a multi-pass overwrite needs), `"truncate"` replaces the file, `"append"` ignores [offset] and writes at the end. @return JSON `{ok, bytesWritten, offset, size, error}` - `size` is re-read from the file after an fsync, so a caller can verify the write for real rather than trust a boolean.

statFile#

window.WebToApk.statFile(pathOrUri: String): String

File metadata without reading any of it. @return JSON `{ok, name, size, mime, lastModified, uri, isDirectory, canWrite}`.

releaseFileAccess#

window.WebToApk.releaseFileAccess(uriString: String): String

Releases a persisted single-file grant taken by [pickFile]. Android caps how many URI grants an app may persist, so an app that picks many files should release the ones it is done with.

getMaxIoChunkSize#

window.WebToApk.getMaxIoChunkSize(): Long

The per-call byte cap for [readFileBase64] / [writeFileBase64], for chunk maths.

Files: opened with this app

Collecting what another app handed over through "Open with", in chunks the page pulls.

openedFilesPending#

window.WebToApk.openedFilesPending(): String

The page pulls, rather than being pushed at: a file opened on cold start is parked until it is asked for, so an SPA that mounts late still gets it. Bytes come across in MAX_IO_CHUNK slices - nothing is held whole in memory, which is what lets a 40 MB GPX through where the old 5 MB inline cap refused.

Described by its group, "Open with this app" file collection, rather than on its own.

openedFileChunk#

window.WebToApk.openedFileChunk(token: String, offset: String, len: Int): String

The page pulls, rather than being pushed at: a file opened on cold start is parked until it is asked for, so an SPA that mounts late still gets it. Bytes come across in MAX_IO_CHUNK slices - nothing is held whole in memory, which is what lets a 40 MB GPX through where the old 5 MB inline cap refused.

Described by its group, "Open with this app" file collection, rather than on its own.

openedFileCollected#

window.WebToApk.openedFileCollected(token: String)

The page pulls, rather than being pushed at: a file opened on cold start is parked until it is asked for, so an SPA that mounts late still gets it. Bytes come across in MAX_IO_CHUNK slices - nothing is held whole in memory, which is what lets a 40 MB GPX through where the old 5 MB inline cap refused.

Described by its group, "Open with this app" file collection, rather than on its own.

openedFileRelease#

window.WebToApk.openedFileRelease(token: String)

The page pulls, rather than being pushed at: a file opened on cold start is parked until it is asked for, so an SPA that mounts late still gets it. Bytes come across in MAX_IO_CHUNK slices - nothing is held whole in memory, which is what lets a 40 MB GPX through where the old 5 MB inline cap refused.

Described by its group, "Open with this app" file collection, rather than on its own.

openedFileMaxChunk#

window.WebToApk.openedFileMaxChunk(): Int

The page pulls, rather than being pushed at: a file opened on cold start is parked until it is asked for, so an SPA that mounts late still gets it. Bytes come across in MAX_IO_CHUNK slices - nothing is held whole in memory, which is what lets a 40 MB GPX through where the old 5 MB inline cap refused.

Described by its group, "Open with this app" file collection, rather than on its own.

Media and casting

The lock-screen media notification your page drives, and handing a video to a TV.

castMedia#

window.WebToApk.castMedia(mediaUrl: String)

Offers [mediaUrl] to whatever on this phone can play it on another screen - Chromecast, a Smart TV app, a local player - through the system chooser. This is a hand-off, not a cast session: the app does not stay in control of playback, and there is no callback. A page that needs a real session should use the Cast SDK in JavaScript instead.

setMediaMetadata#

window.WebToApk.setMediaMetadata(title: String, artist: String, artworkUrl: String)

Lets web content drive the Android media-style notification and receive transport control callbacks (play/pause/next/prev) from the lock screen and notification shade. Usage from JavaScript: window.WebToApk.setMediaMetadata('Song Title', 'Artist Name', 'https://…/art.jpg') window.WebToApk.setPlaybackState(true, 30000, 240000) // playing, pos ms, dur ms window.WebToApk.clearMediaNotification() Callback from Android → JS: window.WebToApkOnMediaAction('play'|'pause'|'next'|'prev'|'stop')

Described by its group, Media Notification Bridge, rather than on its own.

setPlaybackState#

window.WebToApk.setPlaybackState(isPlaying: Boolean, positionMs: Long, durationMs: Long)

Lets web content drive the Android media-style notification and receive transport control callbacks (play/pause/next/prev) from the lock screen and notification shade. Usage from JavaScript: window.WebToApk.setMediaMetadata('Song Title', 'Artist Name', 'https://…/art.jpg') window.WebToApk.setPlaybackState(true, 30000, 240000) // playing, pos ms, dur ms window.WebToApk.clearMediaNotification() Callback from Android → JS: window.WebToApkOnMediaAction('play'|'pause'|'next'|'prev'|'stop')

Described by its group, Media Notification Bridge, rather than on its own.

clearMediaNotification#

window.WebToApk.clearMediaNotification()

Lets web content drive the Android media-style notification and receive transport control callbacks (play/pause/next/prev) from the lock screen and notification shade. Usage from JavaScript: window.WebToApk.setMediaMetadata('Song Title', 'Artist Name', 'https://…/art.jpg') window.WebToApk.setPlaybackState(true, 30000, 240000) // playing, pos ms, dur ms window.WebToApk.clearMediaNotification() Callback from Android → JS: window.WebToApkOnMediaAction('play'|'pause'|'next'|'prev'|'stop')

Described by its group, Media Notification Bridge, rather than on its own.

The window

Fullscreen, picture-in-picture, the system bar colours, screenshot blocking, the splash screen and closing a window.

splashHold#

window.WebToApk.splashHold()

Keeps the branded launch splash up until [splashReady] is called. By default the splash hides as soon as the body has laid-out children, which for an app that paints a skeleton and then fetches its first screen means "splash, then a skeleton". An app that would rather reveal a finished first screen calls this at import and [splashReady] once that screen is on. Capped at the same 6s backstop as the paint poll, so a page that never reports ready still opens.

splashReady#

window.WebToApk.splashReady()

The first screen is on - hide the launch splash now (subject to the creator's minimum splash duration). Idempotent; harmless when nothing was held.

enterPip#

window.WebToApk.enterPip(): Boolean

Shrinks the whole app into a floating picture-in-picture window right now - a video or a call the user wants to keep watching while they leave. Android 8+ and only when the build enabled PiP (the manifest flag it needs); returns whether the window opened. Leaving the app with PiP enabled does this by itself; this is the explicit "minimise" button.

closeWindow#

window.WebToApk.closeWindow()

The Android share sheet, the system clipboard, and closing a window the page opened. These are the plain-web equivalents (navigator.share, navigator.clipboard, window.close) answered natively so they behave the same inside the app.

Described by its group, Share, clipboard and window, rather than on its own.

setFullscreen#

window.WebToApk.setFullscreen(enabled: Boolean)

Lets the page hide the status bar (clock/battery) and navigation bar itself - e.g. a game entering its play screen: WebToApk.setFullscreen(true) // hide the system bars WebToApk.setFullscreen(false) // bring them back WebToApk.toggleFullscreen() WebToApk.isFullscreen() The choice is remembered across launches. It is a no-op in apps the creator already locked to fullscreen/kiosk - there is nothing to restore to there. The side-menu "Fullscreen" item is the equivalent for end users and is gated by the creator's enableFullscreenToggle option; this bridge is always available to the app's own page.

Described by its group, Fullscreen bridge, rather than on its own.

toggleFullscreen#

window.WebToApk.toggleFullscreen()

Lets the page hide the status bar (clock/battery) and navigation bar itself - e.g. a game entering its play screen: WebToApk.setFullscreen(true) // hide the system bars WebToApk.setFullscreen(false) // bring them back WebToApk.toggleFullscreen() WebToApk.isFullscreen() The choice is remembered across launches. It is a no-op in apps the creator already locked to fullscreen/kiosk - there is nothing to restore to there. The side-menu "Fullscreen" item is the equivalent for end users and is gated by the creator's enableFullscreenToggle option; this bridge is always available to the app's own page.

Described by its group, Fullscreen bridge, rather than on its own.

isFullscreen#

window.WebToApk.isFullscreen(): Boolean

Lets the page hide the status bar (clock/battery) and navigation bar itself - e.g. a game entering its play screen: WebToApk.setFullscreen(true) // hide the system bars WebToApk.setFullscreen(false) // bring them back WebToApk.toggleFullscreen() WebToApk.isFullscreen() The choice is remembered across launches. It is a no-op in apps the creator already locked to fullscreen/kiosk - there is nothing to restore to there. The side-menu "Fullscreen" item is the equivalent for end users and is gated by the creator's enableFullscreenToggle option; this bridge is always available to the app's own page.

Described by its group, Fullscreen bridge, rather than on its own.

setStatusBarColor#

window.WebToApk.setStatusBarColor(color: String, style: String): Boolean

WebToApk.setStatusBarColor('#0B0F1A', 'light') // light icons on a dark bar WebToApk.setStatusBarColor('#FFFFFF', 'dark') // dark icons on a light bar WebToApk.setStatusBarColor('#0B0F1A', '') // icons chosen from luminance Why the shell and not Capacitor's StatusBar plugin: on API 35+ the plugin's setBackgroundColor is a no-op (it writes window.statusBarColor, which enforced edge-to-edge ignores), so appmintNative's setStatusBar() reported 'native' and changed nothing on every Android 15 device. The shell owns the strip - see systemBarCanvas - so it is the only party that can actually paint it there. Returns false for an unparseable colour; hidden bars (fullscreen/kiosk) accept the colour for when they reappear.

Described by its group, System bar colour, rather than on its own.

setSecureScreen#

window.WebToApk.setSecureScreen(enabled: Boolean): Boolean

Sets protection for the CURRENT page and returns the state now in force. The window flag itself is applied on the UI thread, but the state this returns is already committed, so a page can call this and immediately reveal its sensitive content without waiting for a callback.

isSecureScreen#

window.WebToApk.isSecureScreen(): Boolean

WebToApk.setSecureScreen(true) // block screenshots on THIS page WebToApk.setSecureScreen(false) // release it early WebToApk.isSecureScreen() // -> boolean Protection belongs to the page that asked for it. It is released the moment the app navigates anywhere else - a new document, a pushState route, a #hash route - so a protected screen cannot leave the rest of the app locked down, and a page that wants protection back after a reload simply asks again. What it does: screenshots, screen recording, casting and the recents-screen thumbnail all stop while it is on. What it cannot do: stop a camera pointed at the screen. Nothing on Android can, and no bridge here will pretend otherwise.

Described by its group, Screen-capture protection, rather than on its own.

Share, clipboard and print

The Android share sheet, the system clipboard, text another app shared into yours, and the print dialogue.

print#

window.WebToApk.print()

Hands the page as it currently stands to Android's print dialogue, which can print on paper or save a PDF. Nothing to configure - the WebView renders the document.

Described by its group, Printing, rather than on its own.

shareNative#

window.WebToApk.shareNative(title: String, text: String, url: String)

The Android share sheet, the system clipboard, and closing a window the page opened. These are the plain-web equivalents (navigator.share, navigator.clipboard, window.close) answered natively so they behave the same inside the app.

Described by its group, Share, clipboard and window, rather than on its own.

getSharedText#

window.WebToApk.getSharedText(): String

Text or a link another app shared INTO this one ("Share to <app>"), as JSON `{text, subject, url, receivedAt}` or "" when nothing was shared. Kept until read, so an app that mounts after the share still sees it; a share arriving while the app runs also fires `appmint:shared`. Requires the build's "Open with this app" option, which registers the share-sheet entry.

clearSharedText#

window.WebToApk.clearSharedText()

Clears the kept share once the page has consumed it.

copyToClipboard#

window.WebToApk.copyToClipboard(text: String): Boolean

The Android share sheet, the system clipboard, and closing a window the page opened. These are the plain-web equivalents (navigator.share, navigator.clipboard, window.close) answered natively so they behave the same inside the app.

Described by its group, Share, clipboard and window, rather than on its own.

shareFiles#

window.WebToApk.shareFiles(filesJson: String, title: String, text: String): Boolean

TRUE native file share for navigator.share({files:[...]}). Android WebView has no Web Share API, and text/plain ACTION_SEND can't carry a file - so before this a shared image was merely SAVED, not shared. filesJson is [{name, mimeType, base64}, ...]; we write each to cache, expose it via the app's FileProvider, and fire ACTION_SEND / ACTION_SEND_MULTIPLE so the real Android share sheet (WhatsApp, Gmail, …) opens. Returns true if the sheet opened.

readClipboard#

window.WebToApk.readClipboard(): String

The Android share sheet, the system clipboard, and closing a window the page opened. These are the plain-web equivalents (navigator.share, navigator.clipboard, window.close) answered natively so they behave the same inside the app.

Described by its group, Share, clipboard and window, rather than on its own.

Contacts

Reading, searching, picking and writing - the picker needs no permission at all.

listContacts#

window.WebToApk.listContacts(requestId: String, limit: Int, offset: Int)

All async: cursor reads can be thousands of rows, and a blocking @JavascriptInterface return would run that on the JS thread - an ANR. Results arrive as `appmint:contacts` / window.onAppMintContacts({requestId,...}). requestId: pass a STRING ('pick-' + Date.now()). A raw NUMBER also works - the bridge coerces it through a double ("1.787491234567E12"), and every reply canonicalizes it back (BridgeRequestId), so `res.requestId === Date.now()` strict-compares true either way. WebToApk.listContacts(id, limit, offset) WebToApk.searchContacts(id, query, limit) WebToApk.getContact(id, contactId) WebToApk.pickContact(id) <- NO permission needed (system picker) WebToApk.addContact(id, json) <- needs the Contacts-write permission

Described by its group, Contacts, rather than on its own.

searchContacts#

window.WebToApk.searchContacts(requestId: String, query: String, limit: Int)

All async: cursor reads can be thousands of rows, and a blocking @JavascriptInterface return would run that on the JS thread - an ANR. Results arrive as `appmint:contacts` / window.onAppMintContacts({requestId,...}). requestId: pass a STRING ('pick-' + Date.now()). A raw NUMBER also works - the bridge coerces it through a double ("1.787491234567E12"), and every reply canonicalizes it back (BridgeRequestId), so `res.requestId === Date.now()` strict-compares true either way. WebToApk.listContacts(id, limit, offset) WebToApk.searchContacts(id, query, limit) WebToApk.getContact(id, contactId) WebToApk.pickContact(id) <- NO permission needed (system picker) WebToApk.addContact(id, json) <- needs the Contacts-write permission

Described by its group, Contacts, rather than on its own.

getContact#

window.WebToApk.getContact(requestId: String, contactId: String)

All async: cursor reads can be thousands of rows, and a blocking @JavascriptInterface return would run that on the JS thread - an ANR. Results arrive as `appmint:contacts` / window.onAppMintContacts({requestId,...}). requestId: pass a STRING ('pick-' + Date.now()). A raw NUMBER also works - the bridge coerces it through a double ("1.787491234567E12"), and every reply canonicalizes it back (BridgeRequestId), so `res.requestId === Date.now()` strict-compares true either way. WebToApk.listContacts(id, limit, offset) WebToApk.searchContacts(id, query, limit) WebToApk.getContact(id, contactId) WebToApk.pickContact(id) <- NO permission needed (system picker) WebToApk.addContact(id, json) <- needs the Contacts-write permission

Described by its group, Contacts, rather than on its own.

pickContact#

window.WebToApk.pickContact(requestId: String)

System contact picker - needs NO permission, and is the right choice whenever the page just wants the user to choose one person. Picks from the PHONE table, not the contact table: the picker grants read access only to the row it returns, so picking a phone row is what makes the number readable without READ_CONTACTS. Picking a bare contact would return a row with a name and no number, and reading the number from it would need the permission this method exists to avoid.

addContact#

window.WebToApk.addContact(requestId: String, contactJson: String)

The system picker (no permission needed) and writing a new contact (WRITE_CONTACTS). The read side - listing and searching - is under the Contacts group above.

Described by its group, Contacts: pick and add, rather than on its own.

Phone, SMS and the call log

Placing a call, sending or drafting a message, and reading history. Every one of these is permission-gated and Play asks about them.

makePhoneCall#

window.WebToApk.makePhoneCall(phoneNumber: String)

Places a call directly when the creator enabled the Phone permission, otherwise opens the dialer with the number prefilled (which needs no permission). A plain `tel:` link in your HTML also works and is often enough.

listCallLog#

window.WebToApk.listCallLog(requestId: String, limit: Int, offset: Int, sinceMillis: String)

WebToApk.listCallLog(id, limit, offset, sinceMillis) Delivered as `appmint:calllog` / window.onAppMintCallLog.

Described by its group, Call log, rather than on its own.

composeSms#

window.WebToApk.composeSms(phoneNumber: String, message: String): Boolean

Opens the user's SMS app with the message prefilled. Needs no permission and cannot be rejected by Play - the right default for anything non-automated.

sendSms#

window.WebToApk.sendSms(

WebToApk.composeSms(number, message) <- NO permission, opens the SMS app WebToApk.sendSms(id, number, message, wantDelivery) WebToApk.listSms(id, "inbox"|"sent"|"draft", limit, offset) Send results: `appmint:sms` / window.onAppMintSms {requestId, status:"sent"|"delivered"|"failed", ok, reason?} Inbound messages: `appmint:sms-received` / window.onAppMintSmsReceived.

Described by its group, SMS, rather than on its own.

listSms#

window.WebToApk.listSms(requestId: String, box: String, limit: Int, offset: Int)

WebToApk.composeSms(number, message) <- NO permission, opens the SMS app WebToApk.sendSms(id, number, message, wantDelivery) WebToApk.listSms(id, "inbox"|"sent"|"draft", limit, offset) Send results: `appmint:sms` / window.onAppMintSms {requestId, status:"sent"|"delivered"|"failed", ok, reason?} Inbound messages: `appmint:sms-received` / window.onAppMintSmsReceived.

Described by its group, SMS, rather than on its own.

Vibration and tap sound

Haptic feedback and the app's own click sound, fired on your semantic events rather than every stray tap.

vibrate#

window.WebToApk.vibrate(durationMs: Long)

Simple haptics for the page (navigator.vibrate and a pattern form). Both are ignored unless the build enabled the Vibrate permission.

Described by its group, Vibration, rather than on its own.

cancelVibrate#

window.WebToApk.cancelVibrate()

Simple haptics for the page (navigator.vibrate and a pattern form). Both are ignored unless the build enabled the Vibrate permission.

Described by its group, Vibration, rather than on its own.

vibratePattern#

window.WebToApk.vibratePattern(patternJson: String)

Vibrates an on/off pattern, e.g. "[100,50,100]" = buzz 100ms, pause 50, buzz 100. Backs navigator.vibrate(array); prefer the standard API. No-op unless the creator enabled the Vibrate permission.

playClick#

window.WebToApk.playClick()

Plays the app's tap sound on demand, so a page can fire it on its OWN semantic events (a button press) instead of on every stray tap - which is what a native app actually does, and the reason clickSoundMode can be "off" while this still works. Honours the creator's mode; no-op when tap sound is off.

Text to speech

The device voices, spoken from the page.

ttsSpeak#

window.WebToApk.ttsSpeak(text: String, lang: String, rate: Double, pitch: Double, utteranceId: String)

Backs the window.speechSynthesis polyfill, which is installed at document start in every frame - pages should use the STANDARD speechSynthesis API and these will be called for them. Android WebView ships no working speechSynthesis of its own, which is why the reroute exists.

Described by its group, Text-to-speech, rather than on its own.

ttsCancel#

window.WebToApk.ttsCancel()

Backs the window.speechSynthesis polyfill, which is installed at document start in every frame - pages should use the STANDARD speechSynthesis API and these will be called for them. Android WebView ships no working speechSynthesis of its own, which is why the reroute exists.

Described by its group, Text-to-speech, rather than on its own.

ttsIsSpeaking#

window.WebToApk.ttsIsSpeaking(): Boolean

Backs the window.speechSynthesis polyfill, which is installed at document start in every frame - pages should use the STANDARD speechSynthesis API and these will be called for them. Android WebView ships no working speechSynthesis of its own, which is why the reroute exists.

Described by its group, Text-to-speech, rather than on its own.

ttsWarmUp#

window.WebToApk.ttsWarmUp()

Backs the window.speechSynthesis polyfill, which is installed at document start in every frame - pages should use the STANDARD speechSynthesis API and these will be called for them. Android WebView ships no working speechSynthesis of its own, which is why the reroute exists.

Described by its group, Text-to-speech, rather than on its own.

ttsGetVoices#

window.WebToApk.ttsGetVoices(): String

JSON array of installed voices: [{name, lang, default}]. Empty until the engine finishes init - the page gets 'voiceschanged' then and re-queries, exactly like Chrome's async getVoices() contract.

BLE fitness sensors

Heart rate, speed, cadence, power and battery from standard Bluetooth Low Energy profiles.

bleStartScan#

window.WebToApk.bleStartScan()

Runtime for the existing Bluetooth permission toggle (permBluetooth) - the permissions were declared while nothing in the runtime touched a sensor, the exact declared-but-dead pattern SmsBridge documents. Results stream as `appmint:ble` events / window.onAppMintBle({kind, ...}).

Described by its group, BLE fitness sensors (heart rate / speed / cadence / power / battery), rather than on its own.

bleStopScan#

window.WebToApk.bleStopScan()

Runtime for the existing Bluetooth permission toggle (permBluetooth) - the permissions were declared while nothing in the runtime touched a sensor, the exact declared-but-dead pattern SmsBridge documents. Results stream as `appmint:ble` events / window.onAppMintBle({kind, ...}).

Described by its group, BLE fitness sensors (heart rate / speed / cadence / power / battery), rather than on its own.

bleConnect#

window.WebToApk.bleConnect(address: String)

Runtime for the existing Bluetooth permission toggle (permBluetooth) - the permissions were declared while nothing in the runtime touched a sensor, the exact declared-but-dead pattern SmsBridge documents. Results stream as `appmint:ble` events / window.onAppMintBle({kind, ...}).

Described by its group, BLE fitness sensors (heart rate / speed / cadence / power / battery), rather than on its own.

bleDisconnect#

window.WebToApk.bleDisconnect()

Runtime for the existing Bluetooth permission toggle (permBluetooth) - the permissions were declared while nothing in the runtime touched a sensor, the exact declared-but-dead pattern SmsBridge documents. Results stream as `appmint:ble` events / window.onAppMintBle({kind, ...}).

Described by its group, BLE fitness sensors (heart rate / speed / cadence / power / battery), rather than on its own.

bleSetWheelCircumference#

window.WebToApk.bleSetWheelCircumference(mm: Int)

Runtime for the existing Bluetooth permission toggle (permBluetooth) - the permissions were declared while nothing in the runtime touched a sensor, the exact declared-but-dead pattern SmsBridge documents. Results stream as `appmint:ble` events / window.onAppMintBle({kind, ...}).

Described by its group, BLE fitness sensors (heart rate / speed / cadence / power / battery), rather than on its own.

Web Bluetooth

The navigator.bluetooth shape, answered natively so a page written for Chrome works unchanged.

wbIsAvailable#

window.WebToApk.wbIsAvailable(): Boolean

Raw transport for WebBluetoothPolyfill. A page never calls these directly - it calls navigator.bluetooth, exactly as it would in a browser.

Described by its group, Web Bluetooth (navigator.bluetooth), rather than on its own.

wbRequestDevice#

window.WebToApk.wbRequestDevice(requestId: String, optionsJson: String)

Raw transport for WebBluetoothPolyfill. A page never calls these directly - it calls navigator.bluetooth, exactly as it would in a browser.

Described by its group, Web Bluetooth (navigator.bluetooth), rather than on its own.

wbConnect#

window.WebToApk.wbConnect(requestId: String, deviceId: String)

Raw transport for WebBluetoothPolyfill. A page never calls these directly - it calls navigator.bluetooth, exactly as it would in a browser.

Described by its group, Web Bluetooth (navigator.bluetooth), rather than on its own.

wbDisconnect#

window.WebToApk.wbDisconnect(deviceId: String)

Raw transport for WebBluetoothPolyfill. A page never calls these directly - it calls navigator.bluetooth, exactly as it would in a browser.

Described by its group, Web Bluetooth (navigator.bluetooth), rather than on its own.

wbGetCharacteristics#

window.WebToApk.wbGetCharacteristics(requestId: String, deviceId: String, service: String)

Raw transport for WebBluetoothPolyfill. A page never calls these directly - it calls navigator.bluetooth, exactly as it would in a browser.

Described by its group, Web Bluetooth (navigator.bluetooth), rather than on its own.

wbRead#

window.WebToApk.wbRead(requestId: String, deviceId: String, service: String, characteristic: String)

Raw transport for WebBluetoothPolyfill. A page never calls these directly - it calls navigator.bluetooth, exactly as it would in a browser.

Described by its group, Web Bluetooth (navigator.bluetooth), rather than on its own.

wbWrite#

window.WebToApk.wbWrite(

Raw transport for WebBluetoothPolyfill. A page never calls these directly - it calls navigator.bluetooth, exactly as it would in a browser.

Described by its group, Web Bluetooth (navigator.bluetooth), rather than on its own.

wbNotify#

window.WebToApk.wbNotify(

Raw transport for WebBluetoothPolyfill. A page never calls these directly - it calls navigator.bluetooth, exactly as it would in a browser.

Described by its group, Web Bluetooth (navigator.bluetooth), rather than on its own.

NFC

Reading a tag through window.AndroidNFC, and the write side behind the NDEFReader polyfill.

__nfcWrite#

window.WebToApk.__nfcWrite(messageJson: String)

Called from the JS NDEFReader.write() polyfill. Stores the message JSON and waits for the next NFC tag tap.

Described by its group, NFC Write Bridge, rather than on its own.

__nfcMakeReadOnly#

window.WebToApk.__nfcMakeReadOnly()

Called from the JS NDEFReader.makeReadOnly() polyfill.

__nfcCancelWrite#

window.WebToApk.__nfcCancelWrite()

Cancel any pending NFC write/lock operation.

startScan#

window.AndroidNFC.startScan(): Boolean

Legacy availability probe, present only when the creator enabled NFC. Real NFC work goes through the standard NDEFReader polyfill (window.WebToApk.__nfc*), which is what a page should use; this only reports whether the adapter is on.

Described by its group, Web NFC, rather than on its own.

Background jobs

Work that keeps going after the app is closed: a durable auto-save, a periodic sync.

workEnqueueOnline#

window.WebToApk.workEnqueueOnline(id: String, url: String, method: String, headersJson: String, payload: String)

No permission needed; results echo as `appmint:work` / window.onAppMintWork.

Described by its group, Background jobs (WorkManager): durable auto-save + periodic native sync, rather than on its own.

workSchedulePeriodic#

window.WebToApk.workSchedulePeriodic(id: String, url: String, method: String, headersJson: String, payload: String, intervalMinutes: Int)

No permission needed; results echo as `appmint:work` / window.onAppMintWork.

Described by its group, Background jobs (WorkManager): durable auto-save + periodic native sync, rather than on its own.

workCancel#

window.WebToApk.workCancel(id: String)

No permission needed; results echo as `appmint:work` / window.onAppMintWork.

Described by its group, Background jobs (WorkManager): durable auto-save + periodic native sync, rather than on its own.

workList#

window.WebToApk.workList(requestId: String)

No permission needed; results echo as `appmint:work` / window.onAppMintWork.

Described by its group, Background jobs (WorkManager): durable auto-save + periodic native sync, rather than on its own.

Device and app information

What phone this is, which build, and a stable per-install identifier.

getAppName#

window.WebToApk.getAppName(): String

The app name the creator chose in the wizard - what the launcher shows.

getAppVersion#

window.WebToApk.getAppVersion(): String

The versionName from the installed package, e.g. "1.4.2" - the string a creator sets when building, not the versionCode. Falls back to "1.0" if the package cannot be read, so a page can print it without a null check.

getDeviceInfo#

window.WebToApk.getDeviceInfo(): String

WebToApk.getDeviceInfo() -> JSON string, NO permission required WebToApk.getPhoneIdentity() -> carrier/SIM, needs the Phone permission WebToApk.getInstallId() -> stable per-install UUID

Described by its group, Device / app information, rather than on its own.

getPhoneIdentity#

window.WebToApk.getPhoneIdentity(): String

WebToApk.getDeviceInfo() -> JSON string, NO permission required WebToApk.getPhoneIdentity() -> carrier/SIM, needs the Phone permission WebToApk.getInstallId() -> stable per-install UUID

Described by its group, Device / app information, rather than on its own.

getInstallId#

window.WebToApk.getInstallId(): String

WebToApk.getDeviceInfo() -> JSON string, NO permission required WebToApk.getPhoneIdentity() -> carrier/SIM, needs the Phone permission WebToApk.getInstallId() -> stable per-install UUID

Described by its group, Device / app information, rather than on its own.