API del bridge JavaScript
Plugin Capacitor
I plugin Capacitor che l'app include. Una pagina scritta a mano li raggiunge come window.Capacitor.Plugins.<Name>; un'app creata con l'AI usa il wrapper @/lib/appmintNative indicato in ogni esempio.
Capacitor.ActionSheet Capacitor#
Capacitor.Plugins.ActionSheet — showActions({ title, message, options })
Esempio
Mostra un foglio nativo dal basso con un elenco di scelte e ti dice quale è stata toccata (@capacitor/action-sheet 8.1.1).
Restituisce: Promise { index, canceled } - index è l'opzione toccata; quando il foglio viene chiuso canceled è true e index è fuori dall'elenco (-1). Richiede: niente da attivare: ogni app include il plugin. window.Capacitor esiste nelle pagine servite dall'app stessa (app HTML, ZIP e create con l'AI) e, in un'app in modalità Sito web, sul tuo sito (il dominio e i sottodomini), mai su altri siti aperti dall'app. Verifica sempre che sia disponibile.
Ogni opzione è { title, style? } con style 'DEFAULT', 'DESTRUCTIVE' o 'CANCEL'.
var ActionSheet = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.ActionSheet;
async function photoMenu() {
if (!ActionSheet) return;
var opts = [
{ title: 'Share', style: 'DEFAULT' },
{ title: 'Edit', style: 'DEFAULT' },
{ title: 'Delete', style: 'DESTRUCTIVE' }
];
var r = await ActionSheet.showActions({ title: 'Photo', message: 'Choose an action', options: opts });
if (r.canceled || r.index < 0 || r.index >= opts.length) return; // dismissed
if (opts[r.index].title === 'Delete') deletePhoto();
}In un'app creata con l'AI il wrapper con la forma di Capacitor restituisce { index: -1 } alla chiusura; la forma breve actionSheet() restituisce null:
import { ActionSheet, actionSheet } from '@/lib/appmintNative';
const { index } = await ActionSheet.showActions({
title: 'Photo',
options: [{ title: 'Share' }, { title: 'Delete', destructive: true }],
});
if (index === 1) deletePhoto();
const i = await actionSheet('Sort by', [{ title: 'Name' }, { title: 'Date' }]); // number | nullConferma di eliminazione che funziona anche in un normale browser:
async function confirmDelete(name) {
const AS = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.ActionSheet;
if (!AS) return window.confirm('Delete ' + name + '?');
const r = await AS.showActions({
title: 'Delete ' + name + '?',
options: [{ title: 'Delete', style: 'DESTRUCTIVE' }, { title: 'Keep it', style: 'CANCEL' }]
});
return !r.canceled && r.index === 0;
}Note: Controlla sempre il limite dell'indice: non trattare mai un indice qualsiasi come una scelta reale. La forma delle opzioni del wrapper è { title, destructive? }; mappa destructive sullo stile 'DESTRUCTIVE' del plugin.
Capacitor.App Capacitor#
Capacitor.Plugins.App — getInfo(), getState(), exitApp(), minimizeApp(), addListener(...)
Esempio
Identità e ciclo di vita dell'app: nome, id del pacchetto, versione, se l'app è in primo piano ed eventi di pausa/ripresa (@capacitor/app 8.1.0).
Restituisce: getInfo() → { name, id, build, version }; getState() → { isActive }; getLaunchUrl() → { url } o niente; getAppLanguage() → { value }; exitApp() / minimizeApp() → void. addListener() → una Promise di un handle con remove(), come nell'API di Capacitor - usa await oppure concatena .then(); il valore restituito ha anche remove() in sé. Richiede: niente da attivare - ogni app ospita il plugin. window.Capacitor esiste nelle pagine che l'app serve da sola (app HTML, ZIP e create con l'AI) e, in un'app in modalità Sito web, sul tuo sito (il suo dominio e sottodomini) - mai su altri siti che l'app apre. Rileva la disponibilità .
var App = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.App;
if (App) {
App.getInfo().then(function (i) {
document.getElementById('about').textContent = i.name + ' ' + i.version + ' (' + i.build + ')';
});
}Pausa e ripresa - salva una bozza quando l'utente se ne va, aggiorna quando torna:
if (App) {
App.addListener('appStateChange', function (s) {
if (!s.isActive) saveDraft(); // went to the background
else refreshInbox(); // back in front
});
App.addListener('pause', function () { console.log('paused'); });
App.addListener('resume', function () { console.log('resumed'); });
}In un'app creata con l'AI:
import { App } from '@/lib/appmintNative';
const info = await App.getInfo(); // { name, id, build, version }
App.exitApp(); // closes the app on AndroidNote: L'evento backButton non viene mai emesso: l'app mantiene il pulsante Indietro (il plugin gira con disableBackButtonHandler). Gestisci Indietro con AppMint.setBackHandler(fn) o il wrapper onBackGesture. Per i deep link usa WebToApk.getLaunchUrl() / l'evento appmint:deep-link (o launchUrl() / onDeepLink() nelle app create con l'AI). minimizeApp() è solo per Android. Non fare mai di un pulsante Esci l'unico modo per andare avanti.
Capacitor.AppLauncher Capacitor#
Capacitor.Plugins.AppLauncher — canOpenUrl({ url }), openUrl({ url })
Esempio
Apre un'altra app tramite link (whatsapp://send?text=…, tel:, geo:, un link https) e chiede se ce n'è una in grado di gestirlo (@capacitor/app-launcher 8.0.1).
Restituisce: canOpenUrl() → Promise { value: boolean }. openUrl() → Promise { completed: boolean } (false quando nessuna app ha gestito il link). Richiede: niente da attivare: ogni app include il plugin. window.Capacitor esiste nelle pagine servite dall'app stessa (app HTML, ZIP e create con l'AI) e, in un'app in modalità Sito web, sul tuo sito (il dominio e i sottodomini), mai su altri siti aperti dall'app. Verifica sempre che sia disponibile.
var AL = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.AppLauncher;
async function chatOnWhatsApp(text) {
if (!AL) return;
var url = 'whatsapp://send?text=' + encodeURIComponent(text);
var can = await AL.canOpenUrl({ url: url });
if (!can.value) { alert('WhatsApp is not installed'); return; }
var r = await AL.openUrl({ url: url });
if (!r.completed) alert('Could not open WhatsApp');
}In un'app creata con l'AI:
import { appLauncher } from '@/lib/appmintNative';
if (await appLauncher.canOpen('tel:+15551234567')) await appLauncher.open('tel:+15551234567');Apri un link di una mappa senza chiedere prima:
async function openMap(query) {
const AL = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.AppLauncher;
if (!AL) { window.open('https://maps.google.com/?q=' + encodeURIComponent(query)); return; }
const r = await AL.openUrl({ url: 'geo:0,0?q=' + encodeURIComponent(query) });
if (!r.completed) alert('No maps app could open this');
}Note: Su Android 11+ un'app può "vedere" solo le altre app che dichiara. Le app generate dichiarano i link https, geo, google.navigation, tel, mailto, sms, whatsapp, intent e upi, quindi canOpenUrl risponde false per altri schemi (per esempio spotify:) anche quando l'app è installata. openUrl funziona comunque con essi: chiamalo e controlla completed. Su Android canOpenUrl accetta anche il nome di un pacchetto ('com.whatsapp'), con lo stesso limite di visibilità .
Capacitor.AppUpdate Capacitor#
Capacitor.Plugins.AppUpdate — getAppUpdateInfo(), performImmediateUpdate(), startFlexibleUpdate(), completeFlexibleUpdate(), openAppStore()
Esempio
Controlla su Google Play se esiste una versione piĂą recente dell'app e avvia l'aggiornamento in-app di Play. Incluso @capawesome/capacitor-app-update (template/plugins/app-update).
Restituisce: getAppUpdateInfo() → { currentVersionName, currentVersionCode, availableVersionCode, updateAvailability, updatePriority, immediateUpdateAllowed, flexibleUpdateAllowed, clientVersionStalenessDays?, installStatus } (i codici di versione sono stringhe; updateAvailability 2 = è disponibile un aggiornamento). performImmediateUpdate() / startFlexibleUpdate() → { code }: 0 OK, 1 annullato, 2 non riuscito, 3 non disponibile, 4 non consentito, 5 chiama prima getAppUpdateInfo(). openAppStore() → void. Richiede: niente da attivare; l'app deve essere installata da Google Play. window.Capacitor esiste nelle pagine servite dall'app stessa (app HTML, ZIP e create con l'AI) e, in un'app in modalità Sito web, sul tuo sito (il dominio e i sottodomini), mai su altri siti aperti dall'app. Verifica sempre che sia disponibile.
var AU = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.AppUpdate;
async function checkForUpdate() {
if (!AU) return;
try {
var info = await AU.getAppUpdateInfo(); // must come first
if (info.updateAvailability !== 2) return; // up to date
if (info.immediateUpdateAllowed) {
var r = await AU.performImmediateUpdate(); // Play's full-screen update
if (r.code === 1) console.log('user said not now');
} else {
await AU.openAppStore(); // the Play listing
}
} catch (e) {
console.log('Play update check failed:', e.message); // e.g. no Play services
}
}Aggiornamento flessibile - scarica in background, poi riavvia quando l'utente è d'accordo:
async function flexibleUpdate() {
if (!AU) return;
var info = await AU.getAppUpdateInfo();
if (info.updateAvailability !== 2 || !info.flexibleUpdateAllowed) return;
await AU.addListener('onFlexibleUpdateStateChange', function (s) {
if (s.installStatus === 2) console.log(s.bytesDownloaded + ' / ' + s.totalBytesToDownload);
if (s.installStatus === 11) showRestartButton(); // 11 = DOWNLOADED
});
await AU.startFlexibleUpdate();
}
function restartNow() { if (AU) AU.completeFlexibleUpdate(); }In un'app creata con l'AI:
import { appUpdate } from '@/lib/appmintNative';
const info = await appUpdate.check(); // null on the web / without Play
if (info?.inPlace) await appUpdate.start(); // Android: immediate updateNote: Usa updateAvailability === 2 come unico test di "aggiornamento disponibile". Il check() del wrapper confronta currentVersionName con availableVersionCode, che su Android non sono mai uguali, quindi il suo updateAvailable non è affidabile: testa invece inPlace. start() restituisce true anche quando l'utente annulla.
Capacitor.AppwrightDocScanner Capacitor#
Capacitor.Plugins.AppwrightDocScanner — scan({ pageLimit, pdf, quality }), isSupported()
Esempio
Apre lo scanner di documenti Google ML Kit: l'utente fotografa una o piĂą pagine (o le importa), i bordi vengono individuati e raddrizzati e le pagine tornano come immagini JPEG. Plugin di Appwright (AppwrightDocScannerPlugin.kt).
Restituisce: scan() → Promise che si RISOLVE sempre: { ok: true, pages: string[] (JPEG data URLs), pdfDataUrl?, pdfPath?, pdfError? } in caso di successo, { ok: false, cancelled: true } quando l'utente esce, { ok: false, error } quando lo scanner non riesce ad avviarsi. isSupported() → { value: boolean } (Google Play services presente). Richiede: niente da attivare e nessuna autorizzazione per la fotocamera (lo scanner gira dentro Play services, che scarica il suo modello al primo utilizzo).
var Scanner = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.AppwrightDocScanner;
async function scanReceipt() {
if (!Scanner) { alert('Scanning is not available here'); return; }
var s = await Scanner.isSupported();
if (!s.value) { alert('This phone has no Google Play services'); return; }
var r = await Scanner.scan({ pageLimit: 1, pdf: false, quality: 0.8 });
if (r.cancelled) return;
if (!r.ok) { alert('Scanner error: ' + r.error); return; }
document.getElementById('preview').src = r.pages[0];
}pageLimit 0 = nessun limite. quality è 0,1-1 per il JPEG. Con pdf: true lo scanner scrive anche un PDF: pdfDataUrl è il PDF come URL data:application/pdf;base64,… che la pagina può scaricare, caricare o mostrare, e pdfPath è la sua copia nella cartella cache dell'app. Se il PDF non ha potuto essere letto, le pagine vengono comunque restituite e pdfError ne spiega il motivo.
In un'app creata con l'AI (null in caso di annullamento, errore o sul web):
import { docScanner, can } from '@/lib/appmintNative';
if (can('docScanner') !== 'none') {
const res = await docScanner.scan({ pageLimit: 5, pdf: true });
if (res) setPages(res.pages); // res.pdfPath when pdf: true
}Scansiona piĂą pagine e offri il PDF:
async function scanContract() {
const Scanner = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.AppwrightDocScanner;
if (!Scanner) return;
const r = await Scanner.scan({ pageLimit: 0, pdf: true, quality: 0.9 });
if (!r.ok) { if (!r.cancelled) alert('Scanner error: ' + r.error); return; }
if (r.pdfDataUrl) {
const a = document.createElement('a');
a.href = r.pdfDataUrl; a.download = 'contract.pdf'; a.click();
} else if (r.pdfError) console.warn('PDF not made:', r.pdfError);
console.log(r.pages.length + ' pages scanned');
}Note: Le pagine sono data URL completi e possono essere grandi: riducile prima di salvarne molte in localStorage. Usa pdfDataUrl, non pdfPath: il percorso di un file del dispositivo non è un URL che la pagina può caricare. Una pagina che non può essere letta viene saltata; le altre vengono comunque restituite.
Capacitor.AppwrightGallery Capacitor#
Capacitor.Plugins.AppwrightGallery — save({ source, fileName, album, kind })
Esempio
Salva una foto o un video nella galleria del telefono (Pictures/<album> o Movies/<album>). Plugin dell'app (AppwrightGalleryPlugin.kt).
Restituisce: Promise di { uri } dell'elemento salvato. Viene rifiutata con un code: bad-source, download-failed, permission-denied (Android 7-9, archiviazione rifiutata) o failed. Richiede: niente da attivare; nessuna autorizzazione su Android 10+ (Android 7-9 chiede l'archiviazione la prima volta).
const Gallery = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.AppwrightGallery;
async function saveCard(canvas) {
const dataUrl = canvas.toDataURL('image/png');
if (!Gallery) { const a = document.createElement('a'); a.href = dataUrl; a.download = 'card.png'; a.click(); return; }
try {
await Gallery.save({ source: dataUrl, fileName: 'birthday-card.png', album: 'Cards' });
toast('Saved to your gallery');
} catch (e) {
toast('Could not save: ' + e.message);
}
}Salva un video dal tuo server nel suo album, con un messaggio per ogni codice di errore:
const Gallery = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.AppwrightGallery;
async function saveClip(url) {
if (!Gallery) { window.open(url, '_blank'); return; }
try {
await Gallery.save({ source: url, fileName: 'highlight.mp4', album: 'Highlights', kind: 'video' });
toast('Video saved to Movies/Highlights');
} catch (e) {
if (e.code === 'download-failed') toast('Could not download the video. Check your connection.');
else if (e.code === 'permission-denied') toast('Allow storage access to save videos.');
else toast('Could not save: ' + e.message);
}
}Salva una foto modificata dall'utente come Blob (per esempio da canvas.toBlob) trasformandola prima in un URL data::
const Gallery = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.AppwrightGallery;
function blobToDataUrl(blob) {
return new Promise((resolve, reject) => {
const r = new FileReader();
r.onload = () => resolve(r.result);
r.onerror = () => reject(r.error);
r.readAsDataURL(blob);
});
}
async function saveEditedPhoto(canvas) {
if (!Gallery) return;
const blob = await new Promise((res) => canvas.toBlob(res, 'image/jpeg', 0.92));
const { uri } = await Gallery.save({ source: await blobToDataUrl(blob), fileName: 'edit-' + Date.now() + '.jpg', album: 'My Edits' });
console.log('saved as', uri);
}Note: source è un URL data:, un URL http(s) o un URI content:///file://; kind ('image'/'video') viene letto dal tipo MIME quando omesso. Le app create con l'AI usano saveToGallery() da @/lib/appmintNative.
Capacitor.AppwrightHaptics Capacitor#
Capacitor.Plugins.AppwrightHaptics
Esempio
Il plugin di AppMint per schemi di vibrazione ricchi: un elenco di battiti, ciascuno con il proprio istante di inizio, durata e intensitĂ .
Restituisce: play({ events }) restituisce { ok: true } o { ok: false, error } ('unsupported' = nessun vibratore, 'empty' = nessun evento, oppure il messaggio del sistema). Non viene mai rifiutata. isSupported() restituisce { value: true/false }. Richiede: niente da attivare: ogni app dichiara l'autorizzazione per la vibrazione, che Vibrazione sia spuntata o meno nel Passaggio 4 (Accesso). window.Capacitor esiste nelle pagine servite dall'app stessa (app HTML, ZIP e create con l'AI) e, in un'app in modalitĂ Sito web, sul tuo sito (il dominio e i sottodomini), mai su altri siti aperti dall'app. Verifica sempre che sia disponibile.
Ogni evento è { at, duration, intensity, sharpness }: at = inizio in ms, duration = durata in ms (sotto 20 diventa un tocco da 30 ms), intensity = 0-1 (predefinito 0,8). sharpness è accettato ma ignorato su Android.
var AH = window.Capacitor && window.Capacitor.Plugins ? window.Capacitor.Plugins.AppwrightHaptics : null;
async function heartbeat() {
if (!AH) return false;
var r = await AH.play({
events: [
{ at: 0, intensity: 0.8 },
{ at: 140, intensity: 0.5 },
{ at: 700, intensity: 0.8 },
{ at: 840, intensity: 0.5 }
]
});
if (!r.ok) console.log('pattern not played:', r.error);
return r.ok;
}
document.getElementById('like').addEventListener('click', heartbeat);Nascondi il pulsante sui telefoni senza vibratore:
(async function () {
var btn = document.getElementById('feel-it');
var ok = false;
if (AH) { try { ok = (await AH.isSupported()).value; } catch (e) {} }
btn.hidden = !ok;
})();Riproduci un doppio tocco rapido dopo un'azione riuscita e segnala quando non può essere riprodotto:
async function successBuzz() {
const AH = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.AppwrightHaptics;
if (!AH) return;
const r = await AH.play({ events: [{ at: 0, duration: 40, intensity: 1 }, { at: 120, duration: 40, intensity: 0.6 }] });
if (!r.ok && r.error === 'unsupported') console.log('this phone has no vibrator');
}Note: L'intensitĂ funziona solo sui telefoni con controllo dell'ampiezza; gli altri telefoni mantengono la sequenza a un'intensitĂ fissa. Se il sistema rifiuta la vibrazione, play risponde { ok: false, error: '<the system message>' }. Nelle app create con l'AI usa hapticPlay da @/lib/appmintNative, che ha anche schemi con nome.
Capacitor.AppwrightLiveProgress Capacitor#
Capacitor.Plugins.AppwrightLiveProgress — start / update / end
Esempio
Il plugin Android di AppMint (il suo nome JS è AppwrightLiveProgress) per una notifica di stato fissa, silenziosa e aggiornata in tempo reale (barra di avanzamento, indicatore di attività o cronometro in corsa) che apre una route al tocco.
Restituisce: start(opts) / update(opts) → Promise<{ ok: true, id }>, oppure { ok: false, error } senza nulla pubblicato - 'permission' quando le notifiche non sono consentite (Android 13+: non ancora concesse, o disattivate per l'app), 'channel_disabled' quando l'utente ha disattivato la categoria "Live status"; end({ id }) → Promise<void>. Richiede: l'app Android e l'autorizzazione per le notifiche (Android 13+). Non disponibile sul web.
In un'app creata con l'AI usa il wrapper liveProgress da @/lib/appmintNative:
import { liveProgress } from '@/lib/appmintNative';
await liveProgress.start({ id: 'order', title: 'Order #42', text: 'Being prepared', indeterminate: true });
await liveProgress.update({ id: 'order', title: 'Order #42', text: 'On the way', progress: 0.6, route: '/orders/42' });
await liveProgress.end('order');In una pagina scritta a mano:
async function showDelivery(step, total) {
const P = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.AppwrightLiveProgress;
if (!P) return; // not in the Android app
if (window.Notification && Notification.permission !== 'granted') {
if (await Notification.requestPermission() !== 'granted') return;
}
const r = await P.update({
id: 'delivery',
title: 'Your delivery',
text: 'Stop ' + step + ' of ' + total,
progress: step / total, // 0–1; or indeterminate: true
route: '/track' // opened when the card is tapped
});
if (!r.ok) console.warn('live card not posted:', r.error);
}
async function deliveryDone() {
const P = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.AppwrightLiveProgress;
if (P) await P.end({ id: 'delivery' });
}Un timer per l'allenamento: startedAt fa contare la scheda da sola, quindi viene pubblicata una volta e terminata quando l'allenamento si ferma:
const Live = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.AppwrightLiveProgress;
let workoutStart = 0;
async function startWorkout(name) {
workoutStart = Date.now();
if (!Live) return;
const r = await Live.start({ id: 'workout', title: name, text: 'Workout in progress', startedAt: workoutStart, route: '/workout' });
if (!r.ok && r.error === 'channel_disabled') {
showHint('Turn on "Live status" in this app\'s notification settings to see the timer.');
}
}
async function stopWorkout() {
if (Live) await Live.end({ id: 'workout' });
saveWorkout({ durationMs: Date.now() - workoutStart });
}Opzioni: id (predefinito 'live'), title, text, progress (0-1, omettilo per nessuna barra), indeterminate, startedAt (epoch ms, mostra un cronometro in corsa), route.
Note: start e update fanno la stessa cosa (pubblicano o sostituiscono la scheda con quell'id). La scheda non può essere rimossa con uno swipe: chiama sempre end. Chiedi prima l'autorizzazione: su Android 13+ senza di essa start risponde error: 'permission' e non pubblica nulla.
Capacitor.AppwrightSystemPalette Capacitor#
Capacitor.Plugins.AppwrightSystemPalette — get()
Esempio
Restituisce i colori Material You del telefono (la palette che Android 12+ costruisce dallo sfondo), così l'app può abbinarsi alle app di sistema. Plugin di AppMint (AppwrightSystemPalettePlugin.kt).
Restituisce: Promise { available: false } sotto Android 12, altrimenti { available: true, accent1, accent2, accent3, neutral1, neutral2 } - ciascuno un oggetto da tono a colore esadecimale, con toni '0', '10', '50', '100' … '900', '1000' (ad es. accent1['600'] === '#3F5F90'). Richiede: niente da attivare: ogni app include il plugin. window.Capacitor esiste nelle pagine servite dall'app stessa (app HTML, ZIP e create con l'AI) e, in un'app in modalità Sito web, sul tuo sito (il dominio e i sottodomini), mai su altri siti aperti dall'app. Verifica sempre che sia disponibile.
var Palette = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.AppwrightSystemPalette;
async function useWallpaperColours() {
if (!Palette) return;
var p = await Palette.get();
if (!p.available) return; // keep the app's own colours
var root = document.documentElement.style;
root.setProperty('--accent', p.accent1['600']);
root.setProperty('--surface', p.neutral1['50']);
}
useWallpaperColours();In un'app creata con l'AI:
import { systemPalette } from '@/lib/appmintNative';
const p = await systemPalette();
if (p.available && p.accent1) document.documentElement.style.setProperty('--accent', p.accent1['600']);Scegli toni chiari o scuri in base al tema di sistema e rileggi la palette quando l'utente torna dopo aver cambiato lo sfondo:
var Palette = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.AppwrightSystemPalette;
var darkQuery = window.matchMedia('(prefers-color-scheme: dark)');
async function applyPalette() {
if (!Palette) return;
var p = await Palette.get();
if (!p.available) return;
var dark = darkQuery.matches;
var s = document.documentElement.style;
s.setProperty('--accent', p.accent1[dark ? '200' : '600']);
s.setProperty('--on-accent', p.accent1[dark ? '800' : '0']);
s.setProperty('--bg', p.neutral1[dark ? '900' : '10']);
s.setProperty('--text', p.neutral1[dark ? '100' : '900']);
}
darkQuery.addEventListener('change', applyPalette);
document.addEventListener('visibilitychange', function () {
if (document.visibilityState === 'visible') applyPalette();
});
applyPalette();Note: Controlla il contrasto del testo prima di applicare i colori della palette: un tono dello sfondo non garantisce di leggersi bene sul tuo sfondo. I browser non espongono i colori dello sfondo: sul web il wrapper risponde { available: false }. I colori cambiano quando l'utente cambia sfondo; chiama di nuovo get() alla ripresa.
Capacitor.AppwrightWidget Capacitor#
Capacitor.Plugins.AppwrightWidget — set(card), clear(), placedCount(), requestPin()
Esempio
Riempie il widget della schermata Home dell'app: una scheda con un titolo, un valore grande, fino a dodici righe, i tuoi colori e una route al tocco - la stessa scheda che accetta AppMint.widget.set(), con tutte le chiavi. La scheda viene salvata sul telefono e mostrata anche dopo un riavvio. Plugin di AppMint (AppwrightWidgetPlugin.kt).
Restituisce: set({ title?, value?, caption?, lines?, route?, bg?, ink?, accent?, textSize?, align?, wrap?, … }) → Promise { ok: true, placed }, rifiutata con il motivo per una scheda non valida (placed = widget attualmente sulla schermata Home). clear() → void. placedCount() → { value }. requestPin() → { value: boolean } (true quando si è aperto il foglio "aggiungi widget" del launcher). Richiede: niente da attivare: ogni app dichiara il widget. window.Capacitor esiste nelle pagine servite dall'app stessa (app HTML, ZIP e create con l'AI) e, in un'app in modalità Sito web, sul tuo sito (il dominio e i sottodomini), mai su altri siti aperti dall'app. Verifica sempre che sia disponibile.
var Widget = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.AppwrightWidget;
async function updateWidget(tasks) {
if (!Widget) return;
await Widget.set({
title: 'Today',
value: tasks.length + ' tasks',
lines: tasks.slice(0, 6).map(function (t) { return t.title; }),
route: '/today', // opened when the widget is tapped
bg: '#0B0F1A', ink: '#FFFFFF', accent: '#22D3EE'
});
}Offri di aggiungerlo (solo quando non ne è ancora stato aggiunto uno):
async function offerWidget() {
if (!Widget) return;
var n = (await Widget.placedCount()).value;
if (n > 0) return;
var r = await Widget.requestPin();
if (!r.value) alert('Long-press the home screen, tap Widgets and pick this app.');
}In un'app creata con l'AI:
import { widget } from '@/lib/appmintNative';
await widget.set({ title: 'Today', value: '3 tasks', lines: ['Gym', 'Call mum'], route: '/today' });
if ((await widget.placedCount()) === 0) await widget.requestPin();Note: I colori sono #RRGGBB, #AARRGGBB (alfa per primo) o rgba(); quelli vuoti usano una scheda scura con un valore ciano. Vedi AppMint.widget per righe con stile, caption, textSize, align, wrap e font (il tuo file .ttf/.otf, indicato come percorso nella tua app). Il tocco apre l'app su route come deep link (leggilo con WebToApk.getLaunchUrl() o l'evento appmint:deep-link). Prima di qualsiasi set() il widget mostra "Nothing to show yet"; clear() lo riporta a quello stato. In un browser can('widget') è 'none'.
Capacitor.Badge Capacitor#
Capacitor.Plugins.Badge — the launcher-icon count
Esempio
Il plugin Badge (capawesome) che imposta il numero sull'icona dell'app nel launcher.
Restituisce: Promise: set({ count }), clear(), increase(), decrease() → void; get() → { count }; isSupported() → { isSupported }. Il plugin Android non ha metodi per le autorizzazioni. Richiede: niente su Android. Non disponibile sul web.
In un'app creata con l'AI usa il wrapper badge da @/lib/appmintNative:
import { badge } from '@/lib/appmintNative';
await badge.set(3);
await badge.clear();In una pagina scritta a mano:
async function setUnread(count) {
const B = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Badge;
if (!B) return false; // not inside the app
try {
const s = await B.isSupported();
if (!s.isSupported) return false; // this launcher shows no badges
if (count > 0) await B.set({ count: count });
else await B.clear();
return true;
} catch (e) {
return false;
}
}
window.addEventListener('appmint:push', function () {
const inbox = JSON.parse(window.WebToApk ? WebToApk.getPushInbox() : '[]');
setUnread(inbox.filter(function (m) { return !m.read; }).length);
});Conta in aumento e in diminuzione man mano che gli elementi arrivano e vengono letti:
const B = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Badge;
async function onNewMessage() { if (B) await B.increase(); }
async function onMessageRead() { if (B) await B.decrease(); }
async function showCurrent() {
if (!B) return;
const { count } = await B.get();
console.log('badge shows', count);
}Note: su Android l'aspetto dipende dal launcher (un numero, un punto o niente). navigator.setAppBadge(n) nell'app scrive sullo stesso badge (esiste solo nei launcher che possono mostrare un conteggio).
Capacitor.BarcodeScanner Capacitor#
Capacitor.Plugins.BarcodeScanner — scan, startScan, readBarcodesFromImage, isSupported
Esempio
Scansione di QR e codici a barre con l'API @capacitor-mlkit/barcode-scanning - il plugin dell'app (BarcodeScannerPlugin.kt) sopra lo stesso scanner di codici Google di AppMint.scanCode(). Funzionano anche le vecchie chiamate @capacitor-community/barcode-scanner (startScan() che restituisce { hasContent, content }, checkPermission, prepare, hideBackground), che condividono questo nome.
Restituisce: scan({ formats?, autoZoom? }) → { barcodes: [{ rawValue, displayValue, format, valueType, bytes?, cornerPoints?, urlBookmark?, wifi?, email?, phone?, sms?, geoPoint?, contactInfo?, calendarEvent?, driverLicense? }] } (rifiuta con "scan canceled." quando l'utente esce); readBarcodesFromImage({ path, formats? }) → { barcodes }; isSupported() → { supported }; isGoogleBarcodeScannerModuleAvailable() → { available }; installGoogleBarcodeScannerModule() (avanzamento come googleBarcodeScannerModuleInstallProgress); checkPermissions() / requestPermissions() → { camera }; openSettings(). format è 'QR_CODE', 'EAN_13', 'CODE_128'… Richiede: niente da attivare e nessuna autorizzazione per la fotocamera: lo scanner di Google disegna la propria schermata (Play services la scarica al primo utilizzo). window.Capacitor esiste nelle pagine servite dall'app stessa (app HTML, ZIP e create con l'AI) e, in un'app in modalità Sito web, sul tuo sito (il dominio e i sottodomini), mai su altri siti aperti dall'app. Verifica sempre che sia disponibile.
const Scanner = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.BarcodeScanner;
scanBtn.onclick = async () => {
if (!Scanner) return;
try {
const { barcodes } = await Scanner.scan({ formats: ['QR_CODE'] });
const code = barcodes[0];
if (code.valueType === 'URL') location.href = code.urlBookmark.url;
else resultBox.textContent = code.displayValue;
} catch (e) {
if (e.message !== 'scan canceled.') alert(e.message);
}
};Ricerca del prezzo da un codice a barre di prodotto, con lo stile a listener (startScan + barcodesScanned) che le app ML Kit giĂ usano:
const Scanner = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.BarcodeScanner;
async function scanProduct() {
const listener = await Scanner.addListener('barcodesScanned', async ({ barcodes }) => {
await listener.remove();
await Scanner.stopScan();
const ean = barcodes[0].rawValue;
const res = await fetch('https://api.example.com/products/' + encodeURIComponent(ean));
priceBox.textContent = res.ok ? (await res.json()).price : 'Not found';
});
await Scanner.startScan({ formats: ['EAN_13', 'EAN_8', 'UPC_A', 'UPC_E'] });
}Leggi un codice QR da una foto che l'utente ha giĂ :
const Scanner = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.BarcodeScanner;
const Camera = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Camera;
async function qrFromGallery() {
const photo = await Camera.getPhoto({ source: 'PHOTOS', resultType: 'uri', quality: 90 });
const { barcodes } = await Scanner.readBarcodesFromImage({ path: photo.path, formats: ['QR_CODE'] });
return barcodes.length ? barcodes[0].rawValue : null;
}Note: startScan() originale mostra un'anteprima dal vivo della fotocamera dietro una pagina trasparente e continua a scansionare finché non chiami stopScan(); qui apre la schermata dello scanner, il primo codice fa scattare barcodeScanned / barcodesScanned e la sessione termina (la chiamata si risolve poi con { hasContent, content, format } per il codice della community; uno scanner chiuso si risolve con { hasContent: false }). Torcia e zoom appartengono a quell'anteprima, quindi isTorchAvailable() è false e i setter di torcia/zoom vengono rifiutati - la schermata dello scanner ha il suo pulsante della torcia. checkPermissions() risponde granted quando l'app non ha alcuna autorizzazione per la fotocamera (lo scanner non ne richiede); un'app che usa anche la fotocamera riceve il suo stato reale. Le app create con l'AI usano scanCode() da @/lib/appmintNative.
Capacitor.Browser Capacitor#
Capacitor.Plugins.Browser — open({ url, toolbarColor }), close(), addListener('browserFinished')
Esempio
Apre una pagina web in una scheda del browser integrato (Chrome Custom Tabs), così l'utente legge termini, documentazione o una pagina di accesso e torna indietro con un solo gesto (@capacitor/browser 8.0.3).
Restituisce: open() / close() → Promise void. Eventi: browserFinished (l'utente ha chiuso la scheda) e browserPageLoaded. Richiede: niente: ogni app include il plugin (deve essere installato un browser che supporti le Custom Tabs, come Chrome). window.Capacitor esiste nelle pagine servite dall'app stessa (app HTML, ZIP e create con l'AI) e, in un'app in modalità Sito web, sul tuo sito (il dominio e i sottodomini), mai su altri siti aperti dall'app. Verifica sempre che sia disponibile.
var Browser = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Browser;
async function openTerms() {
if (!Browser) return;
var h = await Browser.addListener('browserFinished', function () {
console.log('user closed the terms');
h.remove();
});
await Browser.open({ url: 'https://example.com/terms', toolbarColor: '#0B0F1A' });
}In un'app creata con l'AI (una nuova scheda sul web):
import { browser } from '@/lib/appmintNative';
const off = browser.onClosed(() => refreshAccount());
await browser.open('https://example.com/terms', { toolbarColor: '#0B0F1A' });
// call off() when you no longer careApri un link e chiudi la scheda quando un ritorno da un accesso raggiunge la pagina:
var Browser = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Browser;
async function openHelp() {
if (!Browser) { window.open('https://example.com/help', '_blank'); return; }
try {
await Browser.open({ url: 'https://example.com/help' });
} catch (e) {
alert('Could not open the page: ' + e.message);
}
}
window.addEventListener('appmint:deep-link', function () {
if (Browser) Browser.close().catch(function () {});
});Note: url non deve essere vuoto. toolbarColor è un colore esadecimale; uno non valido viene ignorato. In questa versione del plugin close() funziona anche su Android (termina l'activity che ospita la scheda): utile dopo che un link di accesso torna all'app. La scheda è un browser separato: non condivide i cookie dell'app né localStorage.
Capacitor.Calendar Capacitor#
Capacitor.Plugins.Calendar — createEvent, findEvents, deleteEvent, listCalendars, openCalendar
Esempio
Aggiunge, trova ed elimina eventi nel calendario del telefono (Android CalendarContract) e apre l'app del calendario a una data. Incluso @capacitor/calendar (template/plugins/calendar).
Restituisce: createEvent() → { id }; findEvents() → { events: [{ id, title, location?, notes?, startDate, endDate, isAllDay, calendarId, calendarName?, attendees? }] }; listCalendars() → { calendars: [{ id, name, displayName?, isPrimary }] }; deleteEvent(), openCalendar() → void. Le date sono in millisecondi epoch. Richiede: spunta Calendario nel Passaggio 4 (Accesso) quando crei l'app. Per le app HTML, ZIP e create con l'AI la build aggiunge da sola anche l'autorizzazione per il calendario quando i tuoi file contengono una chiamata Calendar.<method>( scritta esattamente così (come sotto) oppure calendar.createEvent( del wrapper. Il plugin chiede all'utente l'accesso al calendario al primo utilizzo. window.Capacitor esiste nelle pagine servite dall'app stessa (app HTML, ZIP e create con l'AI) e, in un'app in modalità Sito web, sul tuo sito (il dominio e i sottodomini), mai su altri siti aperti dall'app. Verifica sempre che sia disponibile.
var Calendar = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Calendar;
async function addDentist() {
if (!Calendar) return;
var start = new Date('2026-10-02T10:00:00').getTime();
try {
var r = await Calendar.createEvent({
title: 'Dentist', startDate: start, endDate: start + 60 * 60 * 1000,
location: 'Main St 5', notes: 'Bring the card', isAllDay: false,
firstReminderMinutes: 30
});
console.log('event id', r.id);
} catch (e) {
// e.code: OS-PLUG-CLDR-0020 permission denied, -0001 bad arguments
alert('Could not add the event: ' + e.message);
}
}Leggi questa settimana, eliminane uno, apri il calendario:
async function thisWeek() {
if (!Calendar) return;
var now = Date.now();
var found = await Calendar.findEvents({ startDate: now, endDate: now + 7 * 864e5 });
found.events.forEach(function (ev) { console.log(ev.title, new Date(ev.startDate)); });
if (found.events.length) await Calendar.deleteEvent({ id: found.events[0].id });
await Calendar.openCalendar({ date: now });
}In un'app creata con l'AI (sul web createEvent scarica un file .ics; la lettura restituisce []):
import { calendar } from '@/lib/appmintNative';
if ((await calendar.requestPermission()) === 'granted') {
const id = await calendar.createEvent({ title: 'Dentist', start: new Date('2026-10-02T10:00'), end: new Date('2026-10-02T11:00') });
const week = await calendar.listEvents({ from: Date.now(), to: Date.now() + 7 * 864e5 });
if (id) await calendar.deleteEvent(id);
await calendar.openCalendar(new Date());
}Note: Senza l'autorizzazione per il calendario nell'app non compare alcuna finestra e ogni chiamata sugli eventi viene rifiutata (autorizzazione mancante); un utente che dice no ottiene OS-PLUG-CLDR-0020. recurrence: { frequency: 'daily' | 'weekly' | 'monthly' | 'yearly', interval?, count?, endDate? } crea un evento ricorrente. createEventInteractively({ title, startDate, endDate }) apre invece l'editor dell'app del calendario: restituisce { id } dell'evento salvato e viene rifiutata con OS-PLUG-CLDR-0006 quando l'utente chiude l'editor senza salvare (serve l'accesso in lettura oltre che in scrittura per distinguere i due casi). deleteEvent({ id, fromDate }) termina un evento ricorrente prima di fromDate e mantiene le occorrenze precedenti. Il wrapper restituisce null / [] / false invece di generare un errore.
Capacitor.Camera Capacitor#
Capacitor.Plugins.Camera
Esempio
Il plugin ufficiale Capacitor Camera, incluso nell'app: scatta una foto con la fotocamera o scegli foto dalla galleria.
Restituisce: Promise. getPhoto(opts) → { dataUrl | base64String | webPath, format, saved } (il campo dipende da resultType), e viene RIFIUTATA quando l'utente annulla; pickImages(opts) → { photos: [{ webPath, format }] }; checkPermissions() / requestPermissions({ permissions }) → { camera, photos }. Richiede: spunta Fotocamera nel Passaggio 4 (Accesso). Per le app HTML, ZIP e create con l'AI la build aggiunge da sola anche l'autorizzazione per la fotocamera quando i tuoi file contengono Camera.getPhoto( scritto esattamente così (come sotto), getUserMedia con video, oppure <input capture>. window.Capacitor esiste nelle pagine servite dall'app stessa (app HTML, ZIP e create con l'AI) e, in un'app in modalità Sito web, sul tuo sito (il dominio e i sottodomini), mai su altri siti aperti dall'app. Verifica sempre che sia disponibile.
Scatta una foto e mostrala.
async function takePhoto() {
var Camera = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Camera;
if (!Camera) { showMessage('Open the app to use the camera.'); return; }
try {
var perm = await Camera.requestPermissions({ permissions: ['camera'] });
if (perm.camera !== 'granted') { showMessage('Camera permission was refused.'); return; }
var photo = await Camera.getPhoto({
source: 'CAMERA', // 'CAMERA' | 'PHOTOS' | 'PROMPT'
resultType: 'dataUrl', // 'dataUrl' | 'base64' | 'uri'
quality: 80,
direction: 'REAR', // 'REAR' | 'FRONT'
correctOrientation: true,
saveToGallery: false
});
document.getElementById('photo').src = photo.dataUrl;
} catch (err) {
// The user closed the camera (or no camera app). Nothing to do.
}
}Scegli fino a 5 foto dalla galleria.
async function choosePhotos() {
var Camera = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Camera;
if (!Camera) return;
try {
var result = await Camera.pickImages({ quality: 80, limit: 5 });
result.photos.forEach(function (p) {
var img = document.createElement('img');
img.src = p.webPath; // a URL the page can load
document.getElementById('gallery').appendChild(img);
});
} catch (err) { /* cancelled */ }
}In un'app creata con l'AI usa il wrapper, che funziona anche sul web (e restituisce null invece di generare un errore quando si annulla):
import { camera } from '@/lib/appmintNative';
const photo = await camera.take({ source: 'camera' });Note: pickImages non richiede alcuna autorizzazione per la fotocamera. Se l'autorizzazione per la fotocamera non è nell'app (la casella era disattivata e la build non ha trovato alcuna chiamata alla fotocamera), l'autorizzazione viene rifiutata senza alcuna richiesta. Anche un <input type="file" accept="image/*" capture> apre la fotocamera. La fotocamera è sensibile: dichiarala nel modulo Sicurezza dei dati di Play.
Capacitor.CapacitorBarcodeScanner Capacitor#
Capacitor.Plugins.CapacitorBarcodeScanner — scanBarcode({ hint })
Esempio
Scansione di QR / codici a barre in una sola chiamata con l'API ufficiale @capacitor/barcode-scanner - il plugin dell'app (CapacitorBarcodeScannerPlugin.kt) sopra lo stesso scanner di codici Google di AppMint.scanCode(). Il codice scritto per il pacchetto npm (CapacitorBarcodeScanner.scanBarcode({ hint: CapacitorBarcodeScannerTypeHint.ALL })) funziona senza modifiche.
Restituisce: scanBarcode({ hint, scanInstructions?, cameraDirection?, scanOrientation? }) → { ScanResult, format }. hint / format sono i numeri del pacchetto: 0 QR_CODE, 1 AZTEC, 2 CODABAR, 3 CODE_39, 4 CODE_93, 5 CODE_128, 6 DATA_MATRIX, 8 ITF, 9 EAN_13, 10 EAN_8, 11 PDF_417, 14 UPC_A, 15 UPC_E, 17 ALL. Viene rifiutata con il codice OS-PLUG-BARC-0006 quando l'utente chiude lo scanner. Richiede: niente da attivare e nessuna autorizzazione per la fotocamera: lo scanner di Google disegna la propria schermata (Play services la scarica al primo utilizzo). window.Capacitor esiste nelle pagine servite dall'app stessa (app HTML, ZIP e create con l'AI) e, in un'app in modalità Sito web, sul tuo sito (il dominio e i sottodomini), mai su altri siti aperti dall'app. Verifica sempre che sia disponibile.
const Scanner = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.CapacitorBarcodeScanner;
ticketBtn.onclick = async () => {
if (!Scanner) return;
try {
const { ScanResult } = await Scanner.scanBarcode({ hint: 0 }); // QR codes only
const ok = await fetch('/api/tickets/check?code=' + encodeURIComponent(ScanResult)).then((r) => r.ok);
statusBox.textContent = ok ? 'Valid ticket' : 'Unknown ticket';
} catch (e) {
if (e.code !== 'OS-PLUG-BARC-0006') alert(e.message);
}
};Accetta qualsiasi codice e dì all'utente di che tipo era:
const Scanner = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.CapacitorBarcodeScanner;
const NAMES = { 0: 'QR code', 5: 'Code 128', 9: 'EAN-13', 10: 'EAN-8', 14: 'UPC-A', 15: 'UPC-E' };
async function scanAnything() {
const { ScanResult, format } = await Scanner.scanBarcode({ hint: 17 });
return (NAMES[format] || 'Barcode') + ': ' + ScanResult;
}Gestisci un rifiuto che non sia l'utente che chiude lo scanner:
const Scanner = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.CapacitorBarcodeScanner;
async function scanProduct() {
if (!Scanner) { alert('Scanning works only inside the app'); return null; }
try {
const { ScanResult } = await Scanner.scanBarcode({ hint: 9 }); // EAN-13
return ScanResult;
} catch (e) {
if (e.code === 'OS-PLUG-BARC-0006') return null; // user closed the scanner
alert('Scan failed: ' + e.message);
return null;
}
}Note: scanInstructions, scanButton, cameraDirection e scanOrientation stilizzano la schermata della fotocamera originale e non hanno effetto sullo scanner di Google (sempre la fotocamera posteriore). MAXICODE, RSS_14, RSS_EXPANDED e UPC_EAN_EXTENSION non sono supportati come hint (rifiuta con OS-PLUG-BARC-0008); 17 (ALL) scansiona ogni tipo supportato. Le app create con l'AI usano scanCode() da @/lib/appmintNative.
Capacitor.CapacitorCookies Capacitor#
Capacitor.Plugins.CapacitorCookies — getCookies / setCookie / deleteCookie / clearCookies / clearAllCookies
Esempio
Legge e scrive i cookie dell'app, compresi quelli di un altro host con cui la tua pagina comunica (di Capacitor core CapacitorCookies).
Restituisce: getCookies({ url? }) → { [name]: value } - con un url, i cookie che quell'URL (host e percorso) invierebbe; senza, il document.cookie della pagina stessa. Rifiuta un url che non sia http(s). setCookie({ key, value, url?, path?, expires? }), deleteCookie({ key, url? }), clearCookies({ url? }), clearAllCookies() → Promise void; setCookie / deleteCookie vengono rifiutati senza una key stringa (e value) e allora non scrivono nulla. Richiede: niente da attivare - ogni app ospita il plugin. window.Capacitor esiste nelle pagine che l'app serve da sola (app HTML, ZIP e create con l'AI) e, in un'app in modalità Sito web, sul tuo sito (il suo dominio e sottodomini) - mai su altri siti che l'app apre. Rileva la disponibilità .
var Cookies = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.CapacitorCookies;
// Read the session cookie your API host set — not the page's own cookies.
async function apiSession() {
if (!Cookies) return null; // not inside the app
var jar = await Cookies.getCookies({ url: 'https://api.example.com/' });
return jar.session || null;
}
// Remember a choice for the API host, for 30 days, on one path only.
async function rememberRegion(region) {
if (!Cookies) return;
var expires = new Date(Date.now() + 30 * 864e5).toUTCString();
await Cookies.setCookie({ url: 'https://api.example.com', path: '/v2', key: 'region', value: region, expires: expires });
}Esci: elimina i cookie di quell'host, oppure tutti i cookie che l'app conserva.
async function signOut(everything) {
if (!Cookies) return;
if (everything) await Cookies.clearAllCookies();
else await Cookies.clearCookies({ url: 'https://api.example.com' });
}Note: Un cookie impostato con un path viene restituito solo per un url sotto quel percorso. I valori vengono decodificati dall'URL quando letti. I cookie sono condivisi da ogni pagina che l'app mostra, quindi non tenerci mai segreti che una pagina esterna potrebbe leggere: vanno in Preferences o nell'archivio sicuro dell'app.
Capacitor.CapacitorSQLite Capacitor#
Capacitor.Plugins.CapacitorSQLite — createConnection, open, execute, query, run, executeSet, close
Esempio
Un vero database SQLite sul telefono, con l'API @capacitor-community/sqlite. Versione inclusa di @capacitor-community/sqlite (template/plugins/sqlite), che gira sull'SQLite di Android. Il codice scritto per il pacchetto npm (new SQLiteConnection(CapacitorSQLite)) funziona senza modifiche; una pagina scritta a mano chiama direttamente l'oggetto plugin, come sotto.
Restituisce: createConnection({ database, version, encrypted: false, mode: 'no-encryption', readonly: false }), open({ database }), close({ database }), closeConnection({ database }) → void; execute({ database, statements }) → { changes: { changes } }; run({ database, statement, values }) → { changes: { changes, lastId } }; query({ database, statement, values }) → { values: [rows] }; executeSet({ database, set: [{ statement, values }] }) → { changes }; isDBExists({ database }) / isDBOpen({ database }) → { result }; beginTransaction, commitTransaction, rollbackTransaction, isTransactionActive({ database }); deleteDatabase({ database }); exportToJson / importFromJson per i backup. Richiede: niente da attivare e nessuna autorizzazione. Il database risiede nell'archivio privato dell'app e sopravvive agli aggiornamenti. window.Capacitor esiste nelle pagine servite dall'app stessa (app HTML, ZIP e create con l'AI) e, in un'app in modalità Sito web, sul tuo sito (il dominio e i sottodomini), mai su altri siti aperti dall'app. Verifica sempre che sia disponibile.
const DB = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.CapacitorSQLite;
const database = 'notes';
async function openNotes() {
if (!DB) return false;
await DB.createConnection({ database, version: 1, encrypted: false, mode: 'no-encryption', readonly: false });
await DB.open({ database, readonly: false });
await DB.execute({ database, statements:
'CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY AUTOINCREMENT, text TEXT NOT NULL, at INTEGER);' });
return true;
}
async function addNote(text) {
const r = await DB.run({ database, statement: 'INSERT INTO notes (text, at) VALUES (?, ?);', values: [text, Date.now()] });
return r.changes.lastId;
}
async function listNotes() {
const r = await DB.query({ database, statement: 'SELECT id, text, at FROM notes ORDER BY at DESC;', values: [] });
return r.values || [];
}PiĂą scritture che devono riuscire tutte o nessuna - una transazione con executeSet:
const DB = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.CapacitorSQLite;
async function moveMoney(database, fromId, toId, amount) {
await DB.executeSet({ database, transaction: true, set: [
{ statement: 'UPDATE accounts SET balance = balance - ? WHERE id = ?;', values: [amount, fromId] },
{ statement: 'UPDATE accounts SET balance = balance + ? WHERE id = ?;', values: [amount, toId] },
{ statement: 'INSERT INTO ledger (from_id, to_id, amount, at) VALUES (?, ?, ?, ?);', values: [fromId, toId, amount, Date.now()] },
] });
}Apri il database una sola volta per avvio dell'app, anche quando due schermate lo chiedono nello stesso momento:
const DB = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.CapacitorSQLite;
let ready = null;
function db(database) {
if (!ready) ready = (async () => {
try {
await DB.createConnection({ database, version: 1, encrypted: false, mode: 'no-encryption', readonly: false });
} catch (e) {
if (!/already exists/i.test(e.message)) throw e; // a page reload keeps the native connection
}
if (!(await DB.isDBOpen({ database, readonly: false })).result) await DB.open({ database, readonly: false });
return database;
})();
return ready;
}Note: L'app è creata senza SQLCipher, quindi i database non sono cifrati: encrypted: true, setEncryptionSecret, changeEncryptionSecret, isSecretStored e le altre chiamate di cifratura vengono rifiutate con "Encryption is not available in this app". isInConfigEncryption() risponde false. Tutto il resto (query, valori associati, transazioni, aggiornamenti versionati con addUpgradeStatement, importazione/esportazione JSON) è codice del plugin. I rifiuti riportano il testo dell'errore di SQLite.
Capacitor.Clipboard Capacitor#
Capacitor.Plugins.Clipboard — write(options), read()
Esempio
Copia testo (o un link) negli appunti di sistema e lo rilegge (@capacitor/clipboard 8.0.1).
Restituisce: write({ string?, url?, image?, label? }) → Promise void. read() → Promise { value, type } (type è 'text/plain' per il testo). Richiede: niente da attivare - ogni app ospita il plugin. window.Capacitor esiste nelle pagine che l'app serve da sola (app HTML, ZIP e create con l'AI) e, in un'app in modalità Sito web, sul tuo sito (il suo dominio e sottodomini) - mai su altri siti che l'app apre. Rileva la disponibilità .
async function copyCode(code) {
var Clipboard = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Clipboard;
if (!Clipboard) { alert('Copy is not available here'); return; }
await Clipboard.write({ string: code, label: 'Invite code' });
}
async function pasteCode(input) {
var Clipboard = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Clipboard;
if (!Clipboard) return;
try {
var r = await Clipboard.read();
input.value = r.value;
} catch (e) {
// An EMPTY clipboard is a rejection: "There is no data on the clipboard".
if (/no data/i.test(e.message)) input.value = '';
else alert('Could not read the clipboard: ' + e.message);
}
}In un'app creata con l'AI il wrapper con la forma di Capacitor trasforma gli appunti vuoti in '':
import { Clipboard } from '@/lib/appmintNative';
await Clipboard.write({ string: 'ABC-123' });
const { value } = await Clipboard.read(); // '' when the clipboard is emptyCopia un link e confermalo con un tocco di feedback:
async function copyLink(url) {
var P = window.Capacitor && window.Capacitor.Plugins;
if (!P || !P.Clipboard) { alert('Copy is not available here'); return; }
await P.Clipboard.write({ url: url });
if (P.Haptics) P.Haptics.impact({ style: 'LIGHT' });
copiedToast.hidden = false;
}Note: write richiede uno tra string, url o image (un data URL), altrimenti viene rifiutato con "No data provided". Android 12+ mostra un proprio avviso "incollato da…" alla lettura: è il sistema operativo, non l'app. Il write del wrapper invia solo string || url.
Capacitor.Contacts Capacitor#
Capacitor.Plugins.Contacts — getContacts, getContact, pickContact, createContact, deleteContact
Esempio
La rubrica del telefono con l'API @capacitor-community/contacts. Versione inclusa di @capacitor-community/contacts (template/plugins/contacts). Il codice scritto per il pacchetto npm funziona senza modifiche.
Restituisce: checkPermissions() / requestPermissions() → { contacts, contactsWrite } (ogni 'granted' | 'denied' | 'prompt'; contacts è la lettura, contactsWrite è l'aggiunta e l'eliminazione); getContacts({ projection }) → { contacts: [ContactPayload] }; getContact({ contactId, projection }) e pickContact({ projection }) → { contact }; createContact({ contact }) → { contactId }; deleteContact({ contactId }) → void. projection sceglie i campi: { name, phones, emails, organization, birthday, note, urls, postalAddresses, image } (ognuno true). Richiede: spunta Contatti nel Passaggio 4 (Accesso) per leggere, e anche Modifica contatti per aggiungere o eliminare. Le build HTML, ZIP e AI-Studio le spuntano da sole quando il codice chiama Contacts.getContacts( / getContact( / pickContact( (lettura) o Contacts.createContact( / deleteContact( (entrambe). Il plugin chiede all'utente al primo utilizzo; senza la casella spuntata la chiamata viene rifiutata con "Permission is required to access contacts." - e in un'app senza Contatti, SMS o Registro chiamate spuntati il plugin non è presente affatto (Capacitor rifiuta con "not implemented"). window.Capacitor esiste nelle pagine servite dall'app stessa (app HTML, ZIP e create con l'AI) e, in un'app in modalità Sito web, sul tuo sito (il dominio e i sottodomini), mai su altri siti aperti dall'app. Verifica sempre che sia disponibile.
const Contacts = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Contacts;
async function friendsWithPhones() {
if (!Contacts) return [];
const { contacts } = await Contacts.requestPermissions();
if (contacts !== 'granted') return [];
const res = await Contacts.getContacts({ projection: { name: true, phones: true } });
return res.contacts
.filter((c) => c.phones && c.phones.length)
.map((c) => ({ id: c.contactId, name: (c.name && c.name.display) || '', phone: c.phones[0].number }));
}Lascia che l'utente scelga una persona dal selettore di sistema:
const Contacts = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Contacts;
inviteBtn.onclick = async () => {
if (!Contacts) return;
try {
const { contact } = await Contacts.pickContact({ projection: { name: true, phones: true, emails: true } });
inviteField.value = (contact.emails && contact.emails[0] && contact.emails[0].address) || '';
} catch (e) {
// The user closed the picker, or refused access.
}
};Salva un biglietto da visita dall'app nella rubrica:
const Contacts = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Contacts;
async function saveCard(card) {
const perm = await Contacts.requestPermissions();
if (perm.contactsWrite !== 'granted') return null;
const { contactId } = await Contacts.createContact({ contact: {
name: { given: card.first, family: card.last },
organization: { company: card.company, jobTitle: card.title },
phones: [{ type: 'work', label: 'work', number: card.phone }],
emails: [{ type: 'work', label: 'work', address: card.email }],
} });
return contactId;
}Note: pickContact() viene rifiutato con codice CANCELLED quando l'utente esce dal selettore. Android compila contactId, name, phones, emails, organization, birthday, note, urls, postalAddresses e image solo quando la proiezione li richiede - chiedi solo ciò che mostri. Leggere i contatti è un'autorizzazione sensibile su Google Play: descrivi il motivo nel modulo sulla sicurezza dei dati della scheda. Le app create con l'AI usano listContacts(), pickContact() e addContact() da @/lib/appmintNative.
Capacitor.Device Capacitor#
Capacitor.Plugins.Device
Esempio
Il plugin ufficiale Capacitor Device, incluso nell'app: modello del telefono, versione del sistema operativo, batteria, un ID stabile e la lingua.
Restituisce: Promise. getInfo() → { model, platform, operatingSystem, osVersion, androidSDKVersion, manufacturer, isVirtual, webViewVersion, … }; getId() → { identifier }; getBatteryInfo() → { batteryLevel (0–1), isCharging }; getLanguageCode() → { value }. Richiede: niente da attivare. window.Capacitor esiste nelle pagine servite dall'app stessa (app HTML, ZIP e create con l'AI) e, in un'app in modalità Sito web, sul tuo sito (il dominio e i sottodomini), mai su altri siti aperti dall'app. Verifica sempre che sia disponibile.
Mostra il telefono e la batteria.
async function showDevice() {
var cap = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Device;
if (!cap) { document.getElementById('out').textContent = navigator.userAgent; return; }
try {
var info = await cap.getInfo();
var battery = await cap.getBatteryInfo();
document.getElementById('out').textContent =
info.manufacturer + ' ' + info.model + ' - ' + info.operatingSystem + ' ' + info.osVersion +
' - battery ' + Math.round(battery.batteryLevel * 100) + '%' + (battery.isCharging ? ' (charging)' : '');
} catch (err) {
document.getElementById('out').textContent = 'Could not read device info';
}
}Un ID stabile e la lingua.
async function helloServer() {
var cap = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Device;
if (!cap) return;
var id = (await cap.getId()).identifier;
var lang = (await cap.getLanguageCode()).value; // e.g. "en"
await fetch('https://api.example.com/hello?lang=' + encodeURIComponent(lang), {
method: 'POST', body: JSON.stringify({ device: id })
});
}In un'app creata con l'AI usa il wrapper, che funziona anche sul web:
import { Device } from '@/lib/appmintNative';
const info = await Device.getInfo();Note: Per l'ID della build Android, la patch di sicurezza, lo schermo, la memoria e la rete, WebToApk.getDeviceInfo() dell'app fornisce piĂą dettagli. Gli id dei plugin non sono id hardware; possono cambiare dopo un ripristino di fabbrica.
Capacitor.Dialog Capacitor#
Capacitor.Plugins.Dialog — alert, confirm, prompt
Esempio
Mostra una vera finestra di dialogo di sistema Android: un messaggio, una domanda sì/no, o una domanda di testo su una riga (@capacitor/dialog 8.0.1).
Restituisce: alert({ title?, message, buttonTitle? }) → Promise void. confirm({ title?, message, okButtonTitle?, cancelButtonTitle? }) → { value: boolean }. prompt({ title?, message, okButtonTitle?, cancelButtonTitle?, inputPlaceholder?, inputText? }) → { value: string, cancelled: boolean }. Richiede: niente da attivare: ogni app include il plugin. window.Capacitor esiste nelle pagine servite dall'app stessa (app HTML, ZIP e create con l'AI) e, in un'app in modalità Sito web, sul tuo sito (il dominio e i sottodomini), mai su altri siti aperti dall'app. Verifica sempre che sia disponibile.
var Dialog = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Dialog;
async function deleteNote(id) {
if (!Dialog) return;
var r = await Dialog.confirm({
title: 'Delete note?',
message: 'This cannot be undone.',
okButtonTitle: 'Delete',
cancelButtonTitle: 'Keep'
});
if (r.value) removeNote(id);
}Avviso e richiesta:
async function renameFlow() {
if (!Dialog) return;
await Dialog.alert({ title: 'Saved', message: 'Your changes are stored.', buttonTitle: 'OK' });
var p = await Dialog.prompt({ title: 'Rename', message: 'New name', inputText: 'Groceries', inputPlaceholder: 'List name' });
if (!p.cancelled && p.value.trim()) renameList(p.value.trim());
}In un'app creata con l'AI il wrapper non rifiuta mai: la chiusura è { value: false } / { cancelled: true }, e sul web usa le finestre di dialogo del browser:
import { Dialog } from '@/lib/appmintNative';
const { value } = await Dialog.confirm({ title: 'Delete note?', message: 'This cannot be undone.' });
if (value) removeNote(id);Note: message è obbligatorio: senza di esso il plugin rifiuta con "Please provide a message for the dialog". Le finestre di dialogo sono modali; usale solo per decisioni che devono bloccare.
Capacitor.Filesystem Capacitor#
Capacitor.Plugins.Filesystem
Esempio
Il plugin Capacitor Filesystem standard: leggi e scrivi file nell'archivio PROPRIO dell'app (non in una cartella scelta dall'utente - per quella usa WebToApkFS).
Restituisce: ogni metodo restituisce una Promise e viene rifiutato in caso di errore (per esempio leggere un file che non esiste). readFile → { data }, writeFile → { uri }, readdir → { files: [{ name, type, size, mtime, uri }] }, mkdir / deleteFile → niente. Richiede: niente da attivare; 'DATA' e 'CACHE' non richiedono autorizzazioni.
Una pagina non ha import di Directory / Encoding, quindi passa le semplici stringhe 'DATA' (file privati dell'app, conservati fino alla disinstallazione) o 'CACHE' (il sistema può svuotarla), e 'utf8' per il testo:
async function saveAndReadBack() {
var Fs = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Filesystem;
if (!Fs) { console.log('Filesystem plugin not available (not inside the app)'); return; }
try {
await Fs.mkdir({ path: 'notes', directory: 'DATA', recursive: true });
await Fs.writeFile({ path: 'notes/today.txt', data: 'Hello', directory: 'DATA', encoding: 'utf8', recursive: true });
var r = await Fs.readFile({ path: 'notes/today.txt', directory: 'DATA', encoding: 'utf8' });
console.log('text:', r.data);
var dir = await Fs.readdir({ path: 'notes', directory: 'DATA' });
dir.files.forEach(function (f) { console.log(f.type, f.name, f.size); }); // type: 'file' | 'directory'
await Fs.deleteFile({ path: 'notes/today.txt', directory: 'DATA' });
} catch (e) {
console.log('File error:', e.message); // e.g. the file does not exist
}
}Ometti encoding per scrivere o leggere dati binari come base64:
var Fs = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Filesystem;
var base64Data = btoa('raw bytes here'); // any base64 string, e.g. from a canvas or FileReader
if (Fs) {
Fs.writeFile({ path: 'blobs/data.bin', data: base64Data, directory: 'CACHE', recursive: true })
.then(function (w) { console.log('written to', w.uri); })
.catch(function (e) { console.log('write failed:', e.message); });
}Nelle app create con l'AI, il wrapper @/lib/appmintNative che usa questo plugin è fileTransfer (prepara download e caricamenti in 'CACHE'):
import { fileTransfer } from '@/lib/appmintNative';
const r = await fileTransfer.download('https://example.com/report.pdf', 'report.pdf', {
onProgress: (p) => console.log(Math.round(p.fraction * 100) + '%'),
});
if (r.path) console.log('saved in the app at', r.path); // on the web the browser saves it insteadNote: window.Capacitor esiste solo dentro l'app - rileva sempre la disponibilità . I file in 'DATA' sono privati dell'app e vengono eliminati alla disinstallazione; per mettere i file dove l'utente può vederli, usa WebToApkFS (una cartella concessa dall'utente) o un download.
Capacitor.FileTransfer Capacitor#
Capacitor.Plugins.FileTransfer
Esempio
Il plugin ufficiale Capacitor File Transfer: scarica un URL in un file sul telefono, o carica un file dal telefono, in modo nativo, con eventi di avanzamento.
Restituisce: downloadFile({url, path, headers?, progress?}) → Promise<{path}>; uploadFile({url, path, fileKey?, mimeType?, method?, headers?, progress?}) → Promise<{bytesSent, responseCode, response, headers}>. L'avanzamento arriva come eventi del listener 'progress' {type, url, bytes, contentLength, lengthComputable}. Gli errori vengono rifiutati con {message, code}. Richiede: niente da attivare. window.Capacitor esiste nelle pagine servite dall'app stessa e, in un'app in modalità Sito web, sul tuo sito (il dominio e i sottodomini), mai su altri siti. Verifica sempre che sia disponibile.
Scarica nella cache dell'app con l'avanzamento:
async function download(url, name, onPct) {
const P = window.Capacitor && window.Capacitor.Plugins;
if (!P || !P.FileTransfer || !P.Filesystem) return null; // not in the app
const target = await P.Filesystem.getUri({ path: name, directory: 'CACHE' });
const listener = await P.FileTransfer.addListener('progress', function (p) {
if (p.url === url && p.lengthComputable) onPct(Math.round(p.bytes / p.contentLength * 100));
});
try {
const r = await P.FileTransfer.downloadFile({ url: url, path: target.uri, progress: true });
return r.path;
} catch (e) {
alert('Download failed: ' + e.message);
return null;
} finally {
listener.remove();
}
}Carica un file che è già sul telefono:
const P = window.Capacitor && window.Capacitor.Plugins;
if (P && P.FileTransfer) {
P.FileTransfer.uploadFile({
url: 'https://example.com/api/upload', path: localFileUri,
fileKey: 'file', mimeType: 'image/jpeg', headers: { Authorization: 'Bearer ' + token }
}).then(function (r) { console.log('server said', r.responseCode, r.response); })
.catch(function (e) { alert('Upload failed: ' + e.message); });
}Segnala un trasferimento non riuscito con il suo codice:
async function safeDownload(url, target) {
const P = window.Capacitor && window.Capacitor.Plugins;
if (!P || !P.FileTransfer) { alert('Downloads need the app'); return null; }
try {
const r = await P.FileTransfer.downloadFile({ url: url, path: target });
return r.path;
} catch (e) {
console.log('transfer failed', e.code, e.message);
alert('Could not download the file.');
return null;
}
}Note: Il percorso deve essere un percorso di file o un URI file://, non un Blob: scrivi prima un Blob nella cache con Filesystem.writeFile. Su Android 10 e precedenti, un percorso nell'archiviazione pubblica chiede l'autorizzazione per l'archiviazione. Nelle app create con l'AI preferisci fileTransfer da @/lib/appmintNative, che fa tutto questo e funziona anche sul web.
Capacitor.FileViewer Capacitor#
Capacitor.Plugins.FileViewer
Esempio
Il plugin ufficiale Capacitor File Viewer: apre un documento o un file multimediale nell'app di visualizzazione del telefono, da un indirizzo web, da un percorso di file locale o da un file incluso nell'app.
Restituisce: ogni metodo restituisce una Promise<void> che viene rifiutata con {message, code} quando nulla può aprire il file. Richiede: niente da attivare. window.Capacitor esiste nelle pagine servite dall'app stessa e, in un'app in modalità Sito web, sul tuo sito (il dominio e i sottodomini), mai su altri siti. Verifica sempre che sia disponibile.
async function openPdf(url) {
const FV = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.FileViewer;
if (!FV) { window.open(url, '_blank'); return; } // a normal browser: open a tab
try {
await FV.openDocumentFromUrl({ url: url });
} catch (e) {
alert('No app on this phone can open this file.');
}
}Un file sul telefono, per esempio uno scritto con Capacitor.Plugins.Filesystem:
async function openSaved(name) {
const P = window.Capacitor && window.Capacitor.Plugins;
if (!P || !P.FileViewer || !P.Filesystem) return;
const f = await P.Filesystem.getUri({ path: name, directory: 'CACHE' });
try {
await P.FileViewer.openDocumentFromLocalPath({ path: f.uri.replace(/^file:\/\//, '') });
} catch (e) {
alert('Cannot open ' + name + ': ' + e.message);
}
}openDocumentFromResources({path}) apre un file incluso nella cartella assets dell'APK. Gli stessi tre metodi esistono anche come previewMediaContentFromUrl, previewMediaContentFromLocalPath e previewMediaContentFromResources per video e audio.
Scarica un file e aprilo quando arriva:
async function downloadAndOpen(url, name) {
const P = window.Capacitor && window.Capacitor.Plugins;
if (!P || !P.FileTransfer || !P.Filesystem || !P.FileViewer) return;
const f = await P.Filesystem.getUri({ path: name, directory: 'CACHE' });
const r = await P.FileTransfer.downloadFile({ url: url, path: f.uri });
await P.FileViewer.openDocumentFromLocalPath({ path: r.path.replace(/^file:\/\//, '') });
}Note: Nelle app create con l'AI preferisci fileViewer.open(target) da @/lib/appmintNative, che sceglie il metodo giusto e funziona anche sul web. Un percorso locale è un semplice percorso (senza file://).
Capacitor.Geolocation Capacitor#
Capacitor.Plugins.Geolocation
Esempio
Il plugin ufficiale Capacitor Geolocation, incluso nell'app: la posizione del telefono una volta, o aggiornamenti continui.
Restituisce: Promise. getCurrentPosition(opts) → { timestamp, coords: { latitude, longitude, accuracy, altitude, speed, heading, … } }; watchPosition(opts, callback) → l'id di osservazione (una stringa); clearWatch({ id }); checkPermissions() / requestPermissions() → { location, coarseLocation } ('granted' | 'denied' | 'prompt' | …). Richiede: GPS e/o Posizione nel Passaggio 4 (Accesso) - per le app HTML, ZIP e create con l'AI la build le attiva anche da sola quando i tuoi file chiamano getCurrentPosition o watchPosition. window.Capacitor esiste nelle pagine servite dall'app stessa (app HTML, ZIP e create con l'AI) e, in un'app in modalità Sito web, sul tuo sito (il dominio e i sottodomini), mai su altri siti aperti dall'app. Verifica sempre che sia disponibile.
Posizione una volta, chiedendo prima.
async function whereAmI() {
var geo = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Geolocation;
if (!geo) { showMessage('Open the app to use location.'); return; }
try {
var perm = await geo.checkPermissions();
if (perm.location !== 'granted' && perm.coarseLocation !== 'granted') {
perm = await geo.requestPermissions();
if (perm.location !== 'granted' && perm.coarseLocation !== 'granted') {
showMessage('Location is off for this app.');
return;
}
}
var pos = await geo.getCurrentPosition({ enableHighAccuracy: true, timeout: 15000 });
showMessage(pos.coords.latitude.toFixed(5) + ', ' + pos.coords.longitude.toFixed(5));
} catch (err) {
showMessage('No location: ' + err.message); // location off in settings, timeout, …
}
}Aggiornamenti continui. Il callback riceve (position, error).
var watchId = null;
async function startTracking() {
var geo = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Geolocation;
if (!geo) return;
watchId = await geo.watchPosition({ enableHighAccuracy: true }, function (pos, err) {
if (err || !pos) return;
addPointToMap(pos.coords.latitude, pos.coords.longitude);
});
}
async function stopTracking() {
var geo = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Geolocation;
if (geo && watchId) { await geo.clearWatch({ id: watchId }); watchId = null; }
}In un'app creata con l'AI usa il wrapper, che funziona anche sul web:
import { Geolocation } from '@/lib/appmintNative';
const pos = await Geolocation.getCurrentPosition({ enableHighAccuracy: true });Note: Anche il semplice navigator.geolocation funziona nell'app. Se gli interruttori della posizione erano disattivati al momento della creazione, l'autorizzazione viene rifiutata senza alcuna richiesta. Su Android 12+ l'utente può consentire solo la posizione approssimativa (coarseLocation concessa). La posizione è un dato personale: dichiaralo nel modulo Sicurezza dei dati di Play.
Capacitor.Haptics Capacitor#
Capacitor.Plugins.Haptics
Esempio
Il plugin ufficiale Capacitor Haptics (@capacitor/haptics), incluso nell'app: tocchi leggeri/medi/forti, feedback di successo/avviso/errore, tick di selezione e semplice vibrazione.
Restituisce: ogni metodo restituisce una Promise che si risolve senza valore. Richiede: niente da attivare - ogni app dichiara l'autorizzazione per la vibrazione, che Vibrazione sia selezionata o meno nel passaggio 4 (Accesso). window.Capacitor esiste nelle pagine che l'app serve da sola (app HTML, ZIP e create con l'AI) e, in un'app in modalitĂ Sito web, sul tuo sito (il suo dominio e sottodomini) - mai su altri siti che l'app apre. Rileva la disponibilitĂ .
function haptics() {
return window.Capacitor && window.Capacitor.Plugins ? window.Capacitor.Plugins.Haptics : null;
}
async function tap(style) { // 'LIGHT' | 'MEDIUM' | 'HEAVY'
var h = haptics();
if (!h) return;
try { await h.impact({ style: style }); } catch (e) { console.log('haptics failed', e); }
}
async function result(type) { // 'SUCCESS' | 'WARNING' | 'ERROR'
var h = haptics();
if (h) { try { await h.notification({ type: type }); } catch (e) {} }
}
document.getElementById('add').addEventListener('click', function () { tap('LIGHT'); });
document.getElementById('pay').addEventListener('click', function () { result('SUCCESS'); });Tick di selezione per un selettore o un cursore, e una semplice vibrazione:
var h = window.Capacitor && window.Capacitor.Plugins ? window.Capacitor.Plugins.Haptics : null;
var wheel = document.getElementById('wheel');
wheel.addEventListener('pointerdown', function () { if (h) h.selectionStart(); });
wheel.addEventListener('change', function () { if (h) h.selectionChanged(); });
wheel.addEventListener('pointerup', function () { if (h) h.selectionEnd(); });
document.getElementById('buzz').addEventListener('click', function () {
if (h) h.vibrate({ duration: 300 }); // milliseconds
});Fai vibrare in caso di errore e abbinalo a un messaggio visibile:
var h = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Haptics;
function rejectInput(field) {
field.classList.add('invalid');
if (h) h.notification({ type: 'ERROR' }).catch(function () {});
}Note: Le stringhe sono in maiuscolo ('HEAVY', non 'heavy'). Nelle app create con l'AI, import { Haptics } from '@/lib/appmintNative' fornisce gli stessi nomi. Per ritmi personalizzati con intensitĂ per ogni battito, usa Capacitor.Plugins.AppwrightHaptics.
Capacitor.InAppReview Capacitor#
Capacitor.Plugins.InAppReview — requestReview()
Esempio
Chiede a Google Play di mostrare il suo riquadro "valuta questa app" dentro l'app. Incluso @capacitor-community/in-app-review (template/plugins/in-app-review).
Restituisce: Promise void - si risolve quando il flusso di Play termina, e Play non dice mai se il riquadro è stato mostrato o cosa ha fatto l'utente. Viene rifiutata quando Play non ha potuto avviare il flusso. Richiede: niente da attivare; l'app deve essere installata da Google Play perché il riquadro compaia. window.Capacitor esiste nelle pagine servite dall'app stessa (app HTML, ZIP e create con l'AI) e, in un'app in modalità Sito web, sul tuo sito (il dominio e i sottodomini), mai su altri siti aperti dall'app. Verifica sempre che sia disponibile.
var Review = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.InAppReview;
async function afterThirdOrder() {
if (!Review) return;
try {
await Review.requestReview();
} catch (e) {
console.log('review flow not available:', e.message);
}
// continue the app flow either way — you cannot know if the user rated
}In un'app creata con l'AI:
import { requestReview } from '@/lib/appmintNative';
// after a success moment, not on launch
const asked = await requestReview(); // false where nothing could be asked (web)Chiedi una sola volta, dopo un momento di successo, e ricorda di aver chiesto:
var Review = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.InAppReview;
async function maybeAskForReview() {
if (!Review || localStorage.getItem('review_asked')) return;
localStorage.setItem('review_asked', '1');
try { await Review.requestReview(); } catch (e) { console.log('review not shown:', e.message); }
}Note: Play limita la frequenza con cui il riquadro compare e non lo mostra a ogni chiamata: non legare mai un premio al riquadro e non chiedere mai da un pulsante "Valutaci" aspettandoti che si apra (per quello apri la scheda dello store). Chiedi dopo un momento di successo, non all'avvio. Un'installazione non ufficiale o di debug di solito non mostra nulla.
Capacitor.KeepAwake Capacitor#
Capacitor.Plugins.KeepAwake — keepAwake(), allowSleep(), isKeptAwake(), isSupported()
Esempio
Impedisce allo schermo di oscurarsi e bloccarsi mentre qualcosa deve restare visibile - una ricetta, un percorso, un codice QR, un timer. Incluso @capacitor-community/keep-awake (template/plugins/keep-awake).
Restituisce: keepAwake() / allowSleep() → Promise void. isKeptAwake() → { isKeptAwake: boolean }. isSupported() → { isSupported: true }. Richiede: niente: ogni app include il plugin; nessuna autorizzazione. window.Capacitor esiste nelle pagine servite dall'app stessa (app HTML, ZIP e create con l'AI) e, in un'app in modalità Sito web, sul tuo sito (il dominio e i sottodomini), mai su altri siti aperti dall'app. Verifica sempre che sia disponibile.
var KA = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.KeepAwake;
async function startCooking() {
if (KA) await KA.keepAwake();
showSteps();
}
async function stopCooking() {
if (KA) await KA.allowSleep(); // always release when the view closes
}
if (KA) KA.isKeptAwake().then(function (r) { console.log('awake?', r.isKeptAwake); });In un'app creata con l'AI (la Screen Wake Lock API sul web):
import { keepAwake } from '@/lib/appmintNative';
useEffect(() => {
void keepAwake(true);
return () => { void keepAwake(false); };
}, []);Tieni lo schermo acceso solo mentre un timer è in corso:
var KA = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.KeepAwake;
async function runTimer(seconds) {
if (KA) await KA.keepAwake();
await new Promise(function (done) { setTimeout(done, seconds * 1000); });
if (KA) await KA.allowSleep();
alert('Time is up');
}Note: Il flag appartiene alla finestra dell'app: funziona solo mentre l'app è in primo piano e resta attivo fino a allowSleep() - rilascialo a ogni uscita dalla schermata. keepAwake() restituisce false quando nulla ha potuto mantenere lo schermo acceso.
Capacitor.Keyboard Capacitor#
Capacitor.Plugins.Keyboard — show(), hide(), addListener('keyboardDidShow' | ...)
Esempio
Apre o chiude la tastiera a schermo e dice alla pagina quando compare e quanto è alta (@capacitor/keyboard 8.0.5).
Restituisce: show() / hide() → Promise void. addListener(event, fn) → una Promise con un handle che ha remove(), come nell'API di Capacitor - usa await o concatena .then(); il valore restituito ha anche remove() stesso; gli eventi di visualizzazione passano { keyboardHeight } in pixel CSS. Eventi: keyboardWillShow, keyboardDidShow, keyboardWillHide, keyboardDidHide. Richiede: niente da attivare: ogni app include il plugin. window.Capacitor esiste nelle pagine servite dall'app stessa (app HTML, ZIP e create con l'AI) e, in un'app in modalità Sito web, sul tuo sito (il dominio e i sottodomini), mai su altri siti aperti dall'app. Verifica sempre che sia disponibile.
var Keyboard = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Keyboard;
if (Keyboard) {
Keyboard.addListener('keyboardDidShow', function (info) {
console.log('keyboard height', info.keyboardHeight);
document.body.classList.add('typing'); // e.g. hide the bottom tab bar
});
Keyboard.addListener('keyboardDidHide', function () {
document.body.classList.remove('typing');
});
}
function onSend() {
if (Keyboard) Keyboard.hide().catch(function () {}); // rejects if no field is focused
}In un'app creata con l'AI usa onKeyboard() per il layout - riporta solo la parte di tastiera che la pagina ridimensionata non libera giĂ - e hideKeyboard() per chiuderla:
import { onKeyboard, hideKeyboard, Keyboard } from '@/lib/appmintNative';
const off = onKeyboard((px) => { composer.style.paddingBottom = `${px}px`; }); // call off() on unmount
hideKeyboard();
const h = Keyboard.addListener('keyboardWillShow', ({ keyboardHeight }) => console.log(keyboardHeight));
h.remove();Mostra la tastiera per un campo dopo il tocco su un pulsante:
var Keyboard = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Keyboard;
searchBtn.addEventListener('click', function () {
searchInput.focus();
if (Keyboard) Keyboard.show().catch(function () {});
});Note: L'app ridimensiona già la pagina per la tastiera, quindi NON aggiungere l'intero keyboardHeight come padding: una barra in basso è già sopra i tasti. setAccessoryBarVisible, setStyle, setResizeMode e setScroll non sono implementati su Android: non chiamarli. hide() viene rifiutato con "Can't close keyboard, not currently focused" quando nulla ha il focus.
Capacitor.LocalNotifications Capacitor#
Capacitor.Plugins.LocalNotifications — schedule, cancel, getPending, requestPermissions, events
Esempio
Notifiche che l'app pubblica da sola - subito, o a un'ora stabilita - con l'API ufficiale @capacitor/local-notifications (com.capacitorjs:local-notifications). Il codice scritto per il pacchetto npm funziona senza modifiche.
Restituisce: requestPermissions() / checkPermissions() → { display: 'granted' | 'denied' | 'prompt' }; schedule({ notifications: [{ id, title, body, schedule?: { at | on | every }, extra?, actionTypeId?, channelId? }] }) → { notifications: [{ id }] }; getPending() → { notifications }; cancel({ notifications: [{ id }] }); registerActionTypes({ types }); createChannel / listChannels / deleteChannel; checkExactNotificationSetting() → { exact_alarm }. Eventi: localNotificationReceived, localNotificationActionPerformed (actionId è 'tap' per un tocco sulla notifica stessa). Richiede: spunta Notifiche nel Passaggio 4 (Accesso): senza di essa il plugin non è nell'app e ogni chiamata viene rifiutata con "not implemented". Per le notifiche programmate spunta anche ⏰ Promemoria (orario esatto e sopravvivenza al riavvio). Le build HTML, ZIP e AI-Studio spuntano entrambe da sole quando il codice importa @capacitor/local-notifications e chiama LocalNotifications.schedule(. window.Capacitor esiste nelle pagine servite dall'app stessa (app HTML, ZIP e create con l'AI) e, in un'app in modalità Sito web, sul tuo sito (il dominio e i sottodomini), mai su altri siti aperti dall'app. Verifica sempre che sia disponibile.
const LN = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.LocalNotifications;
async function remindIn(minutes, text) {
if (!LN) return false;
const { display } = await LN.requestPermissions();
if (display !== 'granted') return false;
await LN.schedule({ notifications: [{
id: Date.now() % 2147483647,
title: 'Reminder',
body: text,
schedule: { at: new Date(Date.now() + minutes * 60000), allowWhileIdle: true },
extra: { screen: 'tasks' },
}] });
return true;
}Apri la schermata giusta quando l'utente tocca una notifica - anche quando il tocco ha avviato l'app, poiché l'evento attende il listener:
const LN = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.LocalNotifications;
if (LN) {
LN.addListener('localNotificationActionPerformed', ({ actionId, notification }) => {
if (actionId === 'tap' && notification.extra && notification.extra.screen) {
location.hash = '#/' + notification.extra.screen;
}
});
}Un promemoria giornaliero per un'abitudine con i pulsanti Fatto / Posticipa:
const LN = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.LocalNotifications;
async function dailyWaterReminder() {
await LN.registerActionTypes({ types: [{ id: 'water', actions: [
{ id: 'done', title: 'Done' },
{ id: 'snooze', title: 'In 30 min' },
] }] });
await LN.schedule({ notifications: [{
id: 42, title: 'Drink water', body: 'Time for a glass of water.',
actionTypeId: 'water', schedule: { on: { hour: 10, minute: 0 }, repeats: true },
}] });
LN.addListener('localNotificationActionPerformed', ({ actionId }) => {
if (actionId === 'snooze') {
LN.schedule({ notifications: [{ id: 43, title: 'Drink water', body: 'Snoozed reminder.',
schedule: { at: new Date(Date.now() + 30 * 60000) } }] });
}
});
}Note: Su Android 12+ una notifica programmata richiede l'autorizzazione per gli allarmi esatti: il primo schedule() può aprire la pagina di sistema "Sveglie e promemoria"; se l'utente la lascia disattivata, la notifica arriva comunque, un po' più tardi (il risultato contiene un warning). Le notifiche del plugin usano il proprio canale "Default", separato dalle altre notifiche dell'app. Un sound dai file web non è supportato (Android riproduce il suono predefinito del canale). I tocchi raggiungono la pagina attraverso la schermata di sblocco quando l'app ha un PIN. Le app create con l'AI usano notify() / LocalNotifications.schedule da @/lib/appmintNative, che pubblica tramite lo stack di notifiche dell'app.
Capacitor.Network Capacitor#
Capacitor.Plugins.Network — getStatus(), addListener('networkStatusChange')
Esempio
Dice alla pagina se il telefono è online e con quale tipo di connessione, e quando questo cambia (@capacitor/network 8.0.1).
Restituisce: getStatus() → Promise { connected: boolean, connectionType: 'wifi' | 'cellular' | 'none' | 'unknown' }. L'evento networkStatusChange passa la stessa forma. Richiede: niente da attivare: ogni app include il plugin. window.Capacitor esiste nelle pagine servite dall'app stessa (app HTML, ZIP e create con l'AI) e, in un'app in modalità Sito web, sul tuo sito (il dominio e i sottodomini), mai su altri siti aperti dall'app. Verifica sempre che sia disponibile.
var Network = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Network;
function showBanner(s) {
document.getElementById('offline').hidden = s.connected;
}
if (Network) {
Network.getStatus().then(showBanner);
Network.addListener('networkStatusChange', function (s) {
showBanner(s);
if (s.connected) retryQueuedWrites();
if (s.connectionType === 'cellular') console.log('on mobile data');
});
}In un'app creata con l'AI:
import { Network, networkStatus, onNetworkChange } from '@/lib/appmintNative';
const { connected, connectionType } = await Network.getStatus();
const now = networkStatus(); // sync: { connected, type }
const off = onNetworkChange((s) => setOnline(s.connected)); // call off() on unmountMetti in pausa un caricamento grande quando si usano i dati mobili:
var Network = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Network;
async function canUploadVideo() {
if (!Network) return navigator.onLine;
var s = await Network.getStatus();
if (!s.connected) return false;
return s.connectionType === 'wifi' || confirm('Upload over mobile data?');
}Note: connected significa che il telefono ha una rete, non che il tuo server risponde: gestisci comunque le richieste non riuscite. Network.getStatus() e networkStatus() del wrapper leggono navigator.onLine / navigator.connection, non il plugin, quindi il loro tipo può essere 'unknown' o una classe di velocità come '4g'; onNetworkChange() usa invece l'evento del plugin nell'app.
Capacitor.Preferences Capacitor#
Capacitor.Plugins.Preferences — get / set / remove / keys / clear
Esempio
Un piccolo archivio chiave-valore nativo (Android SharedPreferences) per stringhe (@capacitor/preferences 8.0.1).
Restituisce: set({ key, value }), remove({ key }), clear() → Promise void; get({ key }) → { value } (null quando la chiave manca); keys() → { keys: string[] }. Richiede: niente da attivare: ogni app include il plugin. window.Capacitor esiste nelle pagine servite dall'app stessa (app HTML, ZIP e create con l'AI) e, in un'app in modalità Sito web, sul tuo sito (il dominio e i sottodomini), mai su altri siti aperti dall'app. Verifica sempre che sia disponibile.
var Prefs = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Preferences;
async function saveTheme(name) {
if (!Prefs) return false; // not inside the app
await Prefs.set({ key: 'theme', value: name });
return true;
}
async function loadTheme() {
if (!Prefs) return null;
var r = await Prefs.get({ key: 'theme' });
return r.value; // null if never saved
}Gli oggetti devono prima essere trasformati in stringa:
async function saveUser() {
if (!Prefs) return;
await Prefs.set({ key: 'user', value: JSON.stringify({ id: 7, name: 'Asha' }) });
var u = JSON.parse((await Prefs.get({ key: 'user' })).value || 'null');
var all = (await Prefs.keys()).keys;
await Prefs.remove({ key: 'user' });
}Elenca e cancella le chiavi salvate da una schermata delle impostazioni:
async function resetSettings() {
if (!Prefs) return;
var k = (await Prefs.keys()).keys;
console.log('clearing', k.length, 'saved values');
await Prefs.clear();
}Note: @/lib/appmintNative NON ha un wrapper Preferences, di proposito: localStorage è già reale e persistente nell'app, e un secondo archivio dividerebbe i dati di un'app in due posti. Le app create con l'AI dovrebbero continuare a usare localStorage (o il data runtime). Usa Preferences in una pagina scritta a mano solo se vuoi tutte le tue impostazioni lì, e scegli un solo archivio per ogni valore. configure({ group }) cambia il nome dell'archivio (predefinito CapacitorStorage).
Capacitor.PrivacyScreen Capacitor#
Capacitor.Plugins.PrivacyScreen — enable(), disable(), isEnabled()
Esempio
Il plugin ufficiale @capacitor/privacy-screen: blocca gli screenshot e la registrazione dello schermo e nasconde il contenuto dell'app nella vista delle app recenti. Incluso, così i progetti Capacitor importati che lo chiamano funzionano.
Restituisce: enable({android}) → {success}; disable() → {success}; isEnabled() → {enabled}. Richiede: niente da attivare.
const Privacy = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.PrivacyScreen;
async function openVault() {
if (Privacy) await Privacy.enable({ android: { privacyModeOnActivityHidden: 'dim' } });
showVault();
}
async function closeVault() {
if (Privacy) await Privacy.disable();
}Controlla lo stato attuale prima di cambiarlo:
async function toggleProtection() {
if (!Privacy) return;
var s = await Privacy.isEnabled();
if (s.enabled) await Privacy.disable();
else await Privacy.enable();
}Proteggi lo schermo solo mentre è aperta una vista sensibile:
async function showCardNumber() {
if (Privacy) await Privacy.enable({ android: { privacyModeOnActivityHidden: 'splash' } });
cardBox.hidden = false;
}
async function hideCardNumber() {
cardBox.hidden = true;
if (Privacy) await Privacy.disable();
}Note: privacyModeOnActivityHidden: 'none', 'dim' o 'splash'. Il codice scritto per l'app stessa dovrebbe usare WebToApk.setSecureScreen(true) / privacyScreen.enable() da @/lib/appmintNative, che segue la pagina (rilasciato quando l'app la lascia); se li mischi, vince l'ultima chiamata.
Capacitor.PushNotifications Capacitor#
Capacitor.Plugins.PushNotifications — register, requestPermissions, registration, pushNotificationReceived
Esempio
Notifiche push dal TUO server tramite il TUO progetto Firebase, con l'API ufficiale @capacitor/push-notifications (com.capacitorjs:push-notifications, registrata da PushNotificationsPlugin.kt dell'app). Il codice scritto per il pacchetto npm funziona senza modifiche: register(), poi invia al token ricevuto dall'evento registration con il Firebase Admin SDK o l'API FCM HTTP v1.
Restituisce: requestPermissions() / checkPermissions() → { receive: 'granted' | 'denied' | 'prompt' }; register() / unregister() → void (il token arriva con l'evento registration { value }, un errore con registrationError { error }); getDeliveredNotifications() → { notifications }; removeDeliveredNotifications({ notifications }), removeAllDeliveredNotifications(); createChannel({ id, name, importance? }), listChannels(), deleteChannel({ id }). Eventi: pushNotificationReceived (un messaggio mentre l'app è aperta: { id, title?, body?, data }) e pushNotificationActionPerformed ({ actionId: 'tap', notification: { id, data } }). Richiede: crea l'app con AppMint Push attivo e il tuo google-services.json caricato (passaggio Notifiche): è il progetto Firebase a cui appartiene il token. Senza di esso register() viene rifiutato con il codice PUSH_NOT_CONFIGURED ("Push is not set up in this app"). Le build HTML, ZIP e AI-Studio spuntano Notifiche da sole quando il codice chiama PushNotifications.register( / requestPermissions( / addListener(. window.Capacitor esiste nelle pagine servite dall'app stessa (app HTML, ZIP e create con l'AI) e, in un'app in modalità Sito web, sul tuo sito (il dominio e i sottodomini), mai su altri siti aperti dall'app. Verifica sempre che sia disponibile.
const Push = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.PushNotifications;
async function enablePush(userId) {
if (!Push) return;
await Push.addListener('registration', ({ value }) => {
// Your server stores the token and sends to it later.
fetch('https://api.example.com/push-tokens', {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userId, token: value, platform: 'android' }),
});
});
await Push.addListener('registrationError', ({ error }) => console.warn('push:', error));
const { receive } = await Push.requestPermissions();
if (receive !== 'granted') return;
try { await Push.register(); }
catch (e) { if (e.code === 'PUSH_NOT_CONFIGURED') alert('Push is not set up in this build.'); }
}Reagisci a un messaggio mentre l'app è aperta e al tocco su uno arrivato mentre era chiusa (l'evento del tocco attende il tuo listener, quindi un avvio a freddo non va perso):
const Push = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.PushNotifications;
if (Push) {
Push.addListener('pushNotificationReceived', (msg) => {
alert((msg.title || 'New message') + (msg.body ? ': ' + msg.body : ''));
});
Push.addListener('pushNotificationActionPerformed', ({ notification }) => {
const orderId = notification.data && notification.data.orderId;
if (orderId) location.hash = '#/orders/' + orderId;
});
}Un canale per avvisi importanti (Android 8+), usato dai messaggi che lo nominano in android.notification.channel_id:
const Push = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.PushNotifications;
async function setupChannels() {
if (!Push) return;
await Push.createChannel({ id: 'orders', name: 'Order updates', description: 'Your order status', importance: 4, vibration: true });
}Note: un messaggio inviato con un blocco notification viene mostrato da Android stesso mentre l'app è in secondo piano; il tocco apre l'app (tramite la schermata di sblocco quando l'app ha un PIN) e fa scattare pushNotificationActionPerformed. Quando l'app è aperta, i messaggi arrivano come pushNotificationReceived e non vengono disegnati nell'area notifiche. I messaggi solo dati raggiungono la pagina solo mentre è aperta. I messaggi AppMint Push continuano a funzionare in parallelo - non raggiungono mai questo plugin. In un'app creata con OneSignal invece di AppMint Push, i messaggi sono consegnati da OneSignal e non raggiungono mai questo plugin. Le app create con l'AI ricevono i push del creatore tramite pushInbox() da @/lib/appmintNative.
Capacitor.ScreenBrightness Capacitor#
Capacitor.Plugins.ScreenBrightness — getBrightness(), setBrightness({ brightness })
Esempio
Imposta la luminositĂ dello schermo della finestra dell'app: piena luminositĂ per una carta d'imbarco o un codice QR, poi di nuovo normale. Incluso @capacitor-community/screen-brightness (template/plugins/screen-brightness).
Restituisce: getBrightness() → Promise { brightness } (0-1, oppure -1 quando l'app non l'ha sovrascritta e lo schermo segue il sistema). setBrightness({ brightness }) → Promise void. Richiede: niente: ogni app include il plugin; nessuna autorizzazione (cambia solo la finestra di questa app, non l'impostazione di sistema). window.Capacitor esiste nelle pagine servite dall'app stessa (app HTML, ZIP e create con l'AI) e, in un'app in modalità Sito web, sul tuo sito (il dominio e i sottodomini), mai su altri siti aperti dall'app. Verifica sempre che sia disponibile.
var SB = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.ScreenBrightness;
async function showTicket() {
if (SB) await SB.setBrightness({ brightness: 1 }); // full brightness
document.getElementById('qr').hidden = false;
}
async function hideTicket() {
if (SB) await SB.setBrightness({ brightness: -1 }); // -1 = follow the system again
document.getElementById('qr').hidden = true;
}In un'app creata con l'AI:
import { brightness } from '@/lib/appmintNative';
await brightness.set(1); // clamped to 0–1; false where not available (web)
// ... on leaving the screen
await brightness.restore();
const level = await brightness.get(); // -1 = following the system / not availableLeggi prima il livello attuale e ripristina esattamente quello:
var SB = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.ScreenBrightness;
var before = -1;
async function boost() {
if (!SB) return;
before = (await SB.getBrightness()).brightness;
await SB.setBrightness({ brightness: 1 });
}
async function undo() {
if (SB) await SB.setBrightness({ brightness: before });
}Note: Passa sempre un numero: setBrightness({}) non ha alcun valore da applicare. Ripristina con -1 quando la vista si chiude; l'override termina anche quando l'app si chiude. Una pagina web non può controllare la retroilluminazione, quindi can('brightness') è 'none' in un browser.
Capacitor.ScreenOrientation Capacitor#
Capacitor.Plugins.ScreenOrientation — orientation(), lock({ orientation }), unlock()
Esempio
Legge l'orientamento dello schermo, lo blocca per una vista (un gioco, un grafico, un riquadro per la firma) e lo sblocca (@capacitor/screen-orientation 8.0.1).
Restituisce: orientation() → Promise { type } (ad es. 'portrait-primary'). lock() / unlock() → Promise void. L'evento screenOrientationChange passa { type }. Richiede: niente da attivare: ogni app include il plugin. window.Capacitor esiste nelle pagine servite dall'app stessa (app HTML, ZIP e create con l'AI) e, in un'app in modalità Sito web, sul tuo sito (il dominio e i sottodomini), mai su altri siti aperti dall'app. Verifica sempre che sia disponibile.
orientation valori: 'portrait', 'landscape', 'portrait-primary', 'portrait-secondary', 'landscape-primary', 'landscape-secondary', 'natural', 'any'.
var SO = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.ScreenOrientation;
async function openChart() {
if (SO) await SO.lock({ orientation: 'landscape' });
showChart();
}
async function closeChart() {
if (SO) await SO.unlock(); // always release when leaving the view
hideChart();
}
if (SO) {
SO.orientation().then(function (o) { console.log('now', o.type); });
SO.addListener('screenOrientationChange', function (o) { console.log('rotated to', o.type); });
}In un'app creata con l'AI:
import { lockOrientation, ScreenOrientation } from '@/lib/appmintNative';
const ok = await lockOrientation('landscape'); // false when refused (always on desktop)
await lockOrientation('any'); // release
await ScreenOrientation.lock({ orientation: 'portrait' });
await ScreenOrientation.unlock();Gestisci un blocco rifiutato e dillo all'utente:
var SO = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.ScreenOrientation;
var Toast = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Toast;
async function tryLandscape() {
if (!SO) { document.getElementById('rotate-hint').hidden = false; return; } // ask the user to rotate by hand
try {
await SO.lock({ orientation: 'landscape' });
} catch (e) {
if (Toast) Toast.show({ text: 'Could not rotate the screen', duration: 'short' });
}
}Note: I wrapper conoscono solo portrait / landscape / any: ScreenOrientation.lock({ orientation: 'landscape-secondary' }) blocca su un semplice landscape. Dai all'utente un modo per uscire da una vista bloccata.
Capacitor.ScreenReader Capacitor#
Capacitor.Plugins.ScreenReader — isEnabled(), speak({ value, language }), addListener('stateChange')
Esempio
Dice alla pagina se TalkBack è attivo e legge un annuncio (@capacitor/screen-reader 8.0.1).
Restituisce: isEnabled() → Promise { value: boolean }. speak() → Promise void. L'evento stateChange passa { value: boolean } quando TalkBack viene attivato o disattivato. Richiede: niente da attivare: ogni app include il plugin. window.Capacitor esiste nelle pagine servite dall'app stessa (app HTML, ZIP e create con l'AI) e, in un'app in modalità Sito web, sul tuo sito (il dominio e i sottodomini), mai su altri siti aperti dall'app. Verifica sempre che sia disponibile.
var SR = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.ScreenReader;
function setCalmMode(on) {
document.documentElement.classList.toggle('no-motion', on); // shorten animations
}
if (SR) {
SR.isEnabled().then(function (r) { setCalmMode(r.value); });
SR.addListener('stateChange', function (s) { setCalmMode(s.value); });
}
async function announceSaved() {
if (SR) await SR.speak({ value: 'Order placed', language: 'en' });
}In un'app creata con l'AI (speak usa la sintesi vocale sul web; isEnabled() è false lì):
import { screenReader } from '@/lib/appmintNative';
const on = await screenReader.isEnabled();
const off = screenReader.onChange((enabled) => setCalmMode(enabled));
await screenReader.speak('Order placed', 'en');Verifica la disponibilitĂ e mantieni la pagina utilizzabile senza il plugin:
var SR = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.ScreenReader;
async function say(message) {
if (SR) { await SR.speak({ value: message }); return; }
document.getElementById('live-region').textContent = message; // an aria-live="polite" element
}Note: language è 'en' per impostazione predefinita. Per la maggior parte degli annunci preferisci un normale markup accessibile (etichette, regioni aria-live); usa speak per cambi di stato brevi e importanti. Non esiste un'API web per rilevare uno screen reader, quindi can('screenReader') è 'none' in un browser.
Capacitor.SpeechRecognition Capacitor#
Capacitor.Plugins.SpeechRecognition
Esempio
Trasforma la voce dell'utente in testo con il riconoscimento vocale di Android (il plugin della community @capacitor-community/speech-recognition, incluso nell'app). Android WebView non ha un proprio riconoscimento Web Speech, quindi questo è il modo per fare la dettatura.
Restituisce: Promise. available() → { available }. checkPermissions() / requestPermissions() → { speechRecognition: 'granted' | 'denied' | 'prompt' }. start({ partialResults: false }) → { status: 'success', matches: [...] } dopo che l'utente smette di parlare; viene rifiutata in caso di errori come nessun parlato o nessuna corrispondenza. Richiede: spunta Microfono nel Passaggio 4 (Accesso) quando crei l'app (per le app HTML, ZIP e create con l'AI la build aggiunge da sola anche l'autorizzazione per il microfono quando i tuoi file menzionano SpeechRecognition), e l'utente deve consentire il microfono. window.Capacitor esiste nelle pagine servite dall'app stessa (app HTML, ZIP e create con l'AI) e, in un'app in modalità Sito web, sul tuo sito (il dominio e i sottodomini), mai su altri siti aperti dall'app. Verifica sempre che sia disponibile.
Dettatura singola (nello stesso modo in cui @/lib/appmintNative speech la usa):
var SR = window.Capacitor && window.Capacitor.Plugins ? window.Capacitor.Plugins.SpeechRecognition : null;
async function dictate() {
var out = document.getElementById('text');
if (!SR || !(await SR.available()).available) { out.textContent = 'Voice input is not available on this phone.'; return; }
var p = (await SR.checkPermissions()).speechRecognition;
if (p !== 'granted') p = (await SR.requestPermissions()).speechRecognition;
if (p !== 'granted') { out.textContent = 'Microphone permission was not given.'; return; }
try {
var r = await SR.start({ language: 'en-US', maxResults: 3, partialResults: false, popup: false });
out.textContent = r.matches && r.matches.length ? r.matches[0] : '';
} catch (e) {
out.textContent = 'Did not catch that. Try again.';
}
}
document.getElementById('mic').addEventListener('click', dictate);Testo in tempo reale mentre l'utente parla (partialResults: true) e lo stato di ascolto:
// Add the listeners ONCE, when the page starts.
if (SR) {
SR.addListener('partialResults', function (d) {
// Words as they come, and then the final text, all arrive here.
if (d.matches && d.matches.length) document.getElementById('text').textContent = d.matches[0];
});
SR.addListener('listeningState', function (d) {
// 'started' = the user began speaking, 'stopped' = the user stopped speaking
document.body.classList.toggle('listening', d.status === 'started');
});
}
document.getElementById('mic-live').addEventListener('click', async function () {
if (!SR) return;
try {
await SR.start({ language: 'en-US', partialResults: true, popup: false }); // resolves at once, no matches
} catch (e) {
document.getElementById('text').textContent = 'Could not start: ' + e.message;
}
});
document.getElementById('stop').addEventListener('click', function () { if (SR) SR.stop(); });Mostra il pulsante del microfono solo quando il dettato può funzionare, e smetti di ascoltare quando la pagina è nascosta:
var SR = window.Capacitor && window.Capacitor.Plugins ? window.Capacitor.Plugins.SpeechRecognition : null;
var mic = document.getElementById('mic');
mic.hidden = true;
if (SR) {
SR.available().then(function (r) { mic.hidden = !r.available; });
}
document.addEventListener('visibilitychange', function () {
if (document.hidden && SR) SR.stop();
});Note:
- In modalitĂ live (
partialResults: true), il testo finale arriva dopo lo stato'stopped', quindi non rimuovere il listenerpartialResultssu'stopped'. Un errore dopo l'avvio (nessun parlato, nessuna corrispondenza) non viene segnalato in questa modalità . languageè un tag come'en-US'o'ta-IN'(predefinito: la lingua del telefono).popup: truemostra invece la finestra di ascolto di Google. Esistono anchegetSupportedLanguages()→{ languages }eisListening()→{ listening }.start()viene rifiutata con'Missing permission'se non hai chiesto prima. L'ascolto si ferma da solo dopo una pausa nel parlato.
Capacitor.SplashScreen Capacitor#
Capacitor.Plugins.SplashScreen — show(), hide()
Esempio
L'API @capacitor/splash-screen sulla schermata di avvio personalizzata dell'app, così i progetti Capacitor / AI Studio importati che chiamano SplashScreen.hide() quando la prima schermata è pronta funzionano nell'app Android.
Restituisce: hide() → Promise (la schermata di avvio scompare una volta trascorso il tempo minimo impostato dal creatore); show({showDuration, autoHide}) → Promise (la schermata di avvio torna sopra la pagina e, con autoHide - il valore predefinito - scompare dopo showDuration ms, predefinito 3000). Richiede: niente da attivare.
const Splash = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.SplashScreen;
async function boot() {
await loadFirstScreen();
if (Splash) await Splash.hide(); // otherwise the shell hides it at first paint
}
boot();Copri un cambio account lento con la schermata di avvio, mantenuta finché i nuovi dati non sono a schermo:
const Splash = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.SplashScreen;
async function switchAccount(accountId) {
if (Splash) await Splash.show({ autoHide: false }); // stays until hide()
try {
await loadAccount(accountId);
renderHome();
} finally {
if (Splash) await Splash.hide();
}
}Mostra per un momento la schermata di avvio personalizzata dopo la disconnessione; scompare da sola dopo showDuration:
const Splash = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.SplashScreen;
async function signOut() {
await clearSession();
showSignInScreen();
if (Splash) {
try {
await Splash.show({ showDuration: 1200 });
} catch (e) {
console.warn('splash not shown:', e.code);
}
}
}Note: senza una chiamata hide() la schermata di avvio scompare comunque al primo rendering, come in ogni app. Le app create con l'AI usano splash.hold() / splash.ready() da @/lib/appmintNative.
Capacitor.StatusBar Capacitor#
Capacitor.Plugins.StatusBar — setStyle, setBackgroundColor, show, hide, getInfo
Esempio
Cambia lo stile delle icone, il colore e la visibilitĂ della barra di stato (@capacitor/status-bar 8.0.2).
Restituisce: ogni metodo → Promise void, tranne getInfo() → { visible, style, color, overlays, height }. Richiede: niente da attivare: ogni app include il plugin. window.Capacitor esiste nelle pagine servite dall'app stessa (app HTML, ZIP e create con l'AI) e, in un'app in modalità Sito web, sul tuo sito (il dominio e i sottodomini), mai su altri siti aperti dall'app. Verifica sempre che sia disponibile.
style indica lo SFONDO a cui si adatta: 'DARK' = icone chiare (per una barra scura), 'LIGHT' = icone scure, 'DEFAULT' = segue il sistema.
var StatusBar = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.StatusBar;
if (StatusBar) {
StatusBar.setStyle({ style: 'DARK' }); // light icons
StatusBar.hide(); // e.g. while a video plays
// later
StatusBar.show();
}Colora la barra con il bridge dell'app, non con il plugin. Su Android 15+ il setBackgroundColor del plugin non fa nulla (l'edge-to-edge lo ignora). L'app dipinge da sé la striscia e riceve lo stile delle icone nella stessa chiamata:
if (window.WebToApk && window.WebToApk.setStatusBarColor) {
window.WebToApk.setStatusBarColor('#0B0F1A', 'light'); // dark bar, light icons
} else if (StatusBar) {
StatusBar.setBackgroundColor({ color: '#0B0F1A' }); // hex only, e.g. #RRGGBB
}In un'app creata con l'AI usa setStatusBar(color, contentStyle); preferisce il bridge dell'app e imposta colore e icone in una sola chiamata:
import { setStatusBar, StatusBar } from '@/lib/appmintNative';
setStatusBar('#0B0F1A', 'light'); // dark bar, light icons
await StatusBar.setBackgroundColor({ color: '#FFFFFF' }); // colour only; icons picked from luminanceNote: setBackgroundColor rifiuta un colore che non sia una stringa esadecimale. Nelle build a schermo intero / kiosk le barre sono nascoste dall'app. Nell'app Android il solo StatusBar.setStyle() del wrapper non cambia nulla (il bridge ha bisogno di un colore da dipingere): passa lo stile a setStatusBar() insieme a un colore.
Capacitor.TextToSpeech Capacitor#
Capacitor.Plugins.TextToSpeech — speak(), stop(), getSupportedLanguages(), getSupportedVoices()
Esempio
Il plugin della community @capacitor-community/text-to-speech sul motore di sintesi vocale del telefono. Incluso così i progetti Capacitor importati che lo chiamano funzionano.
Restituisce: speak({text, lang, rate, pitch, volume, voice}) → Promise che si risolve quando la frase è stata letta; stop(); getSupportedLanguages() → {languages}; getSupportedVoices() → {voices}; isLanguageSupported({lang}) → {supported}. Richiede: niente da attivare.
const Tts = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.TextToSpeech;
async function readAloud(text) {
if (!Tts) { speechSynthesis.speak(new SpeechSynthesisUtterance(text)); return; }
await Tts.speak({ text: text, lang: 'en-US', rate: 1.0, pitch: 1.0, volume: 1.0 });
}
document.getElementById('stop').onclick = () => Tts && Tts.stop();Controlla la lingua prima di parlare e mostra un messaggio quando non è installata:
const Tts = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.TextToSpeech;
async function speakIn(lang, text) {
if (!Tts) return false;
const { supported } = await Tts.isLanguageSupported({ lang: lang });
if (!supported) { showMessage('This phone has no ' + lang + ' voice installed.'); return false; }
await Tts.speak({ text: text, lang: lang });
return true;
}Leggi un paragrafo ad alta voce con un pulsante di stop e gestisci un errore di lettura:
const Tts = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.TextToSpeech;
async function readParagraph(el, btn) {
if (!Tts) return;
btn.textContent = 'Stop';
btn.onclick = () => Tts.stop();
try {
await Tts.speak({ text: el.textContent, lang: 'en-US', rate: 0.9 }); // resolves when finished
} catch (e) {
console.warn('could not speak:', e.message);
} finally {
btn.textContent = 'Listen';
btn.onclick = () => readParagraph(el, btn);
}
}Note: window.speechSynthesis funziona anche nell'app (il motore dell'app stessa) ed è quello che le app create con l'AI usano tramite speak(); questo plugin è il percorso con la forma di Capacitor.
Capacitor.TextZoom Capacitor#
Capacitor.Plugins.TextZoom — get(), getPreferred(), set({ value })
Esempio
Legge e cambia la dimensione del testo della pagina dell'app, per un controllo "dimensione del testo" nell'app (@capacitor/text-zoom 8.0.1).
Restituisce: get() → Promise { value } (zoom attuale, 1 = 100%). getPreferred() → { value } (la dimensione scelta dall'utente nelle impostazioni di Android). set({ value }) → Promise void. Richiede: niente da attivare: ogni app include il plugin. window.Capacitor esiste nelle pagine servite dall'app stessa (app HTML, ZIP e create con l'AI) e, in un'app in modalità Sito web, sul tuo sito (il dominio e i sottodomini), mai su altri siti aperti dall'app. Verifica sempre che sia disponibile.
var TZ = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.TextZoom;
async function biggerText() {
if (!TZ) return;
var now = (await TZ.get()).value;
await TZ.set({ value: Math.min(2, now + 0.1) });
}
async function resetText() {
if (!TZ) return;
var pref = (await TZ.getPreferred()).value; // e.g. 1.15 when the phone uses large text
await TZ.set({ value: pref });
}In un'app creata con l'AI (sul web scala invece la dimensione del carattere di base):
import { textZoom } from '@/lib/appmintNative';
const size = await textZoom.get();
await textZoom.set(size + 0.1); // clamped to 0.5–3
await textZoom.set(await textZoom.getPreferred()); // back to the system sizeUn cursore per la dimensione del testo che ricorda la scelta:
var TZ = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.TextZoom;
var slider = document.getElementById('text-size'); // <input type="range" min="0.8" max="1.6" step="0.1">
if (TZ) {
var saved = parseFloat(localStorage.getItem('textZoom'));
if (saved) TZ.set({ value: saved });
slider.addEventListener('change', function () {
TZ.set({ value: parseFloat(slider.value) });
localStorage.setItem('textZoom', slider.value);
});
} else {
slider.hidden = true;
}Note: L'app segue giĂ la dimensione del testo di sistema; questo serve per un controllo aggiuntivo nell'app. Prova il tuo layout con dimensioni grandi: le altezze fisse sui controlli di testo taglieranno il contenuto.
Capacitor.Toast Capacitor#
Capacitor.Plugins.Toast — show({ text, duration, position })
Esempio
Mostra un breve toast di sistema Android come "Saved" o "Copied" (@capacitor/toast 8.0.1).
Restituisce: Promise void. Richiede: niente da attivare: ogni app include il plugin. window.Capacitor esiste nelle pagine servite dall'app stessa (app HTML, ZIP e create con l'AI) e, in un'app in modalitĂ Sito web, sul tuo sito (il dominio e i sottodomini), mai su altri siti aperti dall'app. Verifica sempre che sia disponibile.
var Toast = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Toast;
function saved() {
if (Toast) Toast.show({ text: 'Saved', duration: 'short', position: 'bottom' });
}duration è 'short' (circa 2 s) o 'long' (circa 3,5 s). position è 'top', 'center' o 'bottom'.
In un'app creata con l'AI usa toast() oppure Toast con la forma di Capacitor; sul web disegnano uno snackbar nei colori dell'app:
import { Toast, toast } from '@/lib/appmintNative';
await Toast.show({ text: 'Added to list', duration: 'long' });
toast('Copied'); // short form, same resultVerifica la disponibilitĂ e mostra un messaggio nella pagina quando il plugin manca:
function notify(message) {
var Toast = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Toast;
if (Toast) { Toast.show({ text: message, duration: 'short' }); return; }
var el = document.getElementById('snackbar'); // a plain in-page message
el.textContent = message;
el.hidden = false;
setTimeout(function () { el.hidden = true; }, 2000);
}Note: text è obbligatorio («Must provide text»). Il sistema disegna il toast nello stile del produttore del telefono, e le versioni più recenti di Android ignorano position per i toast di testo. Un toast è facile da perdere - non usarlo mai per un errore su cui l'utente deve intervenire; per quello usa Dialog.alert.
Capacitor.Torch Capacitor#
Capacitor.Plugins.Torch — enable(), disable(), toggle(), isEnabled(), isAvailable()
Esempio
La torcia del telefono, con l'API @capawesome/capacitor-torch. Plugin dell'app (TorchPlugin.kt).
Restituisce: enable() / disable() / toggle() → Promise (rifiuta con CAMERA_IN_USE mentre lo stream della fotocamera dell'app è aperto, con UNAVAILABLE senza flash); isEnabled() → {enabled}; isAvailable() → {available}. Richiede: niente da attivare e nessuna autorizzazione.
const Torch = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Torch;
async function setupLightButton() {
if (!Torch || !(await Torch.isAvailable()).available) { lightBtn.hidden = true; return; }
lightBtn.onclick = async () => {
try {
await Torch.toggle();
lightBtn.classList.toggle('on', (await Torch.isEnabled()).enabled);
} catch (e) {
alert(e.message);
}
};
}Fai lampeggiare un segnale SOS da un pulsante e spegni la luce se la fotocamera si prende il flash a metĂ :
const Torch = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Torch;
const wait = (ms) => new Promise((r) => setTimeout(r, ms));
sosBtn.onclick = async () => {
if (!Torch || !(await Torch.isAvailable()).available) return;
const flashes = [200, 200, 200, 600, 600, 600, 200, 200, 200]; // S O S, in ms
try {
for (const ms of flashes) {
await Torch.enable(); await wait(ms);
await Torch.disable(); await wait(200);
}
} catch (e) {
await Torch.disable().catch(() => {});
alert(e.code === 'CAMERA_IN_USE' ? 'Close the camera first.' : e.message);
}
};Tieni un pulsante della luce allineato al sistema, poiché l'utente può accendere la torcia dal riquadro delle impostazioni rapide mentre l'app è in background:
const Torch = window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Torch;
async function syncLightButton() {
if (!Torch) return;
const { enabled } = await Torch.isEnabled();
lightBtn.classList.toggle('on', enabled);
lightBtn.setAttribute('aria-pressed', String(enabled));
}
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') syncLightButton();
});
syncLightButton();Note: la luce segue lo stato del sistema (spegnerla dal riquadro delle impostazioni rapide viene letto come spento) e viene spenta quando l'app si chiude. Mentre uno stream getUserMedia è aperto, accendila tramite la traccia: track.applyConstraints({ advanced: [{ torch: true }] }). Le app create con l'AI usano torch da @/lib/appmintNative.