API del bridge JavaScript
NFC
Lettura di un tag tramite window.AndroidNFC e il lato di scrittura dietro il polyfill NDEFReader.
NDEFReader standard web#
new NDEFReader() — Web NFC
Esempio
Web NFC standard: new NDEFReader() legge, scrive e blocca i tag NFC nell'app installata, con lo stesso codice che accetta Chrome su Android.
Restituisce: Promise ed eventi reading / readingerror, come nello standard web. I record sono NDEFRecord: recordType ('text', 'url', 'absolute-url', 'mime', 'smart-poster', 'empty', 'unknown' o un tipo esterno come 'example.com:thing'), mediaType (record mime), id, encoding e lang (record di testo) e data come DataView. Richiede: attiva Web NFC Support nel Passaggio 4 (Accesso) quando crei l'app. Senza, NDEFReader non esiste. Nessuna richiesta di autorizzazione a runtime; navigator.permissions.query({name:'nfc'}) risponde granted.
Leggi i tag. Decodifica data con TextDecoder, esattamente come in Chrome:
function describe(record) {
if (record.recordType === 'text') return new TextDecoder(record.encoding).decode(record.data);
if (record.recordType === 'url' || record.recordType === 'absolute-url') return new TextDecoder().decode(record.data);
if (record.recordType === 'mime') return record.mediaType + ' (' + record.data.byteLength + ' bytes)';
return record.recordType;
}
var scanStop = null;
document.getElementById('read').addEventListener('click', async function () {
if (!('NDEFReader' in window)) { showStatus('NFC is not available in this app.'); return; }
var reader = new NDEFReader();
scanStop = new AbortController();
reader.onreading = function (e) {
showStatus('Tag ' + e.serialNumber + ': ' + e.message.records.map(describe).join(' | '));
};
reader.onreadingerror = function () { showStatus('That tag holds no NFC data this phone can read.'); };
try {
await reader.scan({ signal: scanStop.signal });
showStatus('Hold a tag to the back of the phone.');
} catch (err) {
showStatus('NFC error: ' + err.name + ' ' + err.message); // NotReadableError = NFC switched off
}
});
document.getElementById('stop').addEventListener('click', function () {
if (scanStop) scanStop.abort(); // the standard way to stop scanning
});Scrivi un testo o un link. La Promise si risolve dopo che l'utente tocca un tag:
async function writeTag(kind, value) {
if (!('NDEFReader' in window)) { showStatus('NFC is not available in this app.'); return; }
var reader = new NDEFReader();
showStatus('Tap a tag to write it...');
try {
if (kind === 'text') await reader.write(value); // same as { records: [{ recordType: 'text', data: value }] }
else await reader.write({ records: [{ recordType: 'url', data: value }] }, { overwrite: false });
showStatus('Tag written.');
} catch (err) {
showStatus('Write failed: ' + err.name + ' ' + err.message); // NotAllowedError: read-only, or overwrite:false on a used tag
}
}Blocca un tag per sempre. makeReadOnly() non si può annullare: chiedi prima:
async function lockTag() {
if (!('NDEFReader' in window)) return;
if (!confirm('Lock the next tag you tap? It can never be changed again.')) return;
try { await new NDEFReader().makeReadOnly(); showStatus('Tag locked.'); }
catch (err) { showStatus('Lock failed: ' + err.message); }
}Note: Tipi di record per write: text (stringa, oppure byte con encoding), url e absolute-url (stringa), mime (byte + mediaType), smart-poster ({records: [...]}), un tipo esterno 'domain:thing' (byte o {records: [...]}), unknown (byte) e empty. Una semplice stringa scrive un record di testo; dei semplici byte scrivono un record mime application/octet-stream. Solo i lettori che hanno chiamato scan() ricevono gli eventi reading; AbortController.abort() sul signal ferma quel lettore (nello standard non esiste stop()). readingerror scatta per un tag che non può contenere dati NFC. Il telefono attende UNA sola operazione su tag alla volta: un nuovo write() o makeReadOnly() mentre uno è in attesa lo sostituisce, e la chiamata sostituita viene rifiutata con AbortError (Chrome la metterebbe in coda). Un signal su write() / makeReadOnly() annulla l'attesa. Errori: NotSupportedError (nessun NFC, oppure il tag non può contenere NDEF o è troppo piccolo), NotReadableError (NFC disattivato), NotAllowedError (tag in sola lettura, oppure overwrite: false su un tag con record), NetworkError (il tag si è allontanato), TypeError (un record che lo standard non permette). NDEFReader esiste nella pagina principale, non negli iframe.
__nfcCancelWrite bridge#
window.WebToApk.__nfcCancelWrite()
Annulla la scrittura/il blocco in attesa (il polyfill la chiama quando il `signal` dell'operazione viene interrotto).
Esempio
Elimina una scrittura o un blocco NFC in coda così il prossimo tocco su un tag viene letto normalmente. Le pagine annullano tramite lo standard web, un AbortController signal, e il polyfill NDEFReader la chiama per loro.
Restituisce: niente. Richiede: Web NFC Support attivato nel Passaggio 4 (Accesso) quando crei l'app.
Il modo standard, che l'app rispetta: il write() in attesa viene rifiutato con AbortError:
var controller = null;
async function writeWithCancel(text) {
if (!('NDEFReader' in window)) { showStatus('NFC is not available in this app.'); return; }
controller = new AbortController();
showStatus('Tap a tag to write it... (or press Cancel)');
try {
await new NDEFReader().write(text, { signal: controller.signal });
showStatus('Tag written.');
} catch (err) {
showStatus(err.name === 'AbortError' ? 'Cancelled.' : 'Write failed: ' + err.message);
}
}
document.getElementById('cancel').addEventListener('click', function () {
if (controller) controller.abort();
});Rinuncia da solo quando nessun tag arriva in tempo; un timer che interrompe il signal annulla allo stesso modo:
async function writeWithTimeout(url, ms) {
if (!('NDEFReader' in window)) { showStatus('NFC is not available in this app.'); return; }
var controller = new AbortController();
var timer = setTimeout(function () { controller.abort(); }, ms);
showStatus('Tap a tag within ' + Math.round(ms / 1000) + ' seconds...');
try {
await new NDEFReader().write({ records: [{ recordType: 'url', data: url }] }, { signal: controller.signal });
showStatus('Tag written.');
} catch (err) {
showStatus(err.name === 'AbortError' ? 'Timed out. Tap Write to try again.' : 'Write failed: ' + err.message);
} finally {
clearTimeout(timer);
}
}Annulla un blocco (o una scrittura) in attesa quando l'utente lascia la schermata, così un tocco successivo non modifica un tag:
var pendingTagOp = null;
window.addEventListener('hashchange', function () {
if (pendingTagOp) { pendingTagOp.abort(); pendingTagOp = null; }
});
document.getElementById('lock').addEventListener('click', function () {
if (!('NDEFReader' in window)) { showStatus('NFC is not available in this app.'); return; }
if (pendingTagOp) pendingTagOp.abort();
var controller = pendingTagOp = new AbortController();
showStatus('Tap the tag to lock it...');
new NDEFReader().makeReadOnly({ signal: controller.signal })
.then(function () { showStatus('Tag locked.'); })
.catch(function (err) { if (err.name !== 'AbortError') showStatus('Lock failed: ' + err.message); })
.finally(function () { if (pendingTagOp === controller) pendingTagOp = null; });
});Note: Non chiamare WebToApk.__nfcCancelWrite() da solo: chiamato direttamente elimina l'attesa nativa ma non può chiudere la Promise della pagina. Il signal fa entrambe le cose. Annullare elimina anche un'operazione in coda makeReadOnly().
__nfcMakeReadOnly bridge#
window.WebToApk.__nfcMakeReadOnly(): String
Mette in coda un blocco permanente (`NDEFReader.makeReadOnly()`) per il prossimo tocco su un tag; restituisce il suo id operazione, "" quando l'NFC è disattivato in questa app.
Esempio
Trasporto interno dietro NDEFReader.makeReadOnly(). Le pagine usano new NDEFReader().makeReadOnly(); non chiamarlo mai direttamente.
Restituisce: (tramite makeReadOnly) una Promise che si risolve dopo che l'utente tocca un tag e questo viene bloccato, oppure viene rifiutata con una DOMException. Richiede: Web NFC Support attivato nel Passaggio 4 (Accesso) quando crei l'app.
Il blocco è permanente. Scrivi prima il tag, poi bloccalo, e chiedi all'utente prima di entrambi:
document.getElementById('publish-tag').addEventListener('click', async function () {
if (!('NDEFReader' in window)) { showStatus('NFC is not available in this app.'); return; }
if (!confirm('Write and lock this tag? A locked tag can never be changed.')) return;
var reader = new NDEFReader();
try {
showStatus('Tap the tag to write it...');
await reader.write({ records: [{ recordType: 'url', data: 'https://example.com/item/42' }] });
showStatus('Now tap the same tag again to lock it...');
await reader.makeReadOnly();
showStatus('Tag written and locked.');
} catch (err) {
showStatus('Failed: ' + err.message);
}
});Blocca un tag già scritto e rinuncia se nessun tag arriva entro 20 secondi:
async function lockTag() {
if (!('NDEFReader' in window)) { showStatus('NFC is not available in this app.'); return; }
if (!confirm('Lock this tag for good? It can never be changed again.')) return;
var controller = new AbortController();
var timer = setTimeout(function () { controller.abort(); }, 20000);
showStatus('Tap the tag to lock it...');
try {
await new NDEFReader().makeReadOnly({ signal: controller.signal });
showStatus('Tag locked.');
} catch (err) {
if (err.name === 'AbortError') showStatus('No tag was tapped. Try again when ready.');
else if (err.name === 'NotAllowedError') showStatus('This kind of tag cannot be locked.');
else showStatus('Lock failed: ' + err.message);
} finally {
clearTimeout(timer);
}
}Leggi prima un tag e proponi di bloccarlo solo se contiene il link previsto:
async function checkThenLock(expectedUrl) {
if (!('NDEFReader' in window)) { showStatus('NFC is not available in this app.'); return; }
var reader = new NDEFReader();
var scanning = new AbortController();
var lockBtn = document.getElementById('lock-btn');
reader.onreading = function (e) {
var rec = e.message.records.find(function (r) { return r.recordType === 'url'; });
var url = rec ? new TextDecoder().decode(rec.data) : null;
showStatus(url ? 'Tag points to ' + url : 'This tag holds no link.');
lockBtn.disabled = url !== expectedUrl;
};
await reader.scan({ signal: scanning.signal });
showStatus('Hold the tag to the phone to check it.');
lockBtn.onclick = async function () {
if (!confirm('Lock this tag? It can never be changed again.')) return;
scanning.abort();
showStatus('Tap the tag again to lock it...');
try { await reader.makeReadOnly(); showStatus('Tag locked.'); }
catch (err) { showStatus('Lock failed: ' + err.message); }
};
}Note: Ogni passaggio richiede un tocco a sé. La chiamata restituisce un id operazione e il risultato arriva tramite window.__appmintNfcDone. Rifiuti: NotSupportedError "Tag does not support NDEF", NotAllowedError "This tag cannot be made read-only" (molti tag non possono essere bloccati), NetworkError quando il tag si allontana. Avviare un write() mentre un blocco è in attesa sostituisce il blocco, che poi viene rifiutato con AbortError. Un signal passato a makeReadOnly({ signal }) annulla l'attesa.
__nfcState bridge#
window.WebToApk.__nfcState(): String
"on", "off" (NFC disattivato nelle impostazioni del telefono) o "none" (nessun NFC, oppure non abilitato in questa app): è ciò che `NDEFReader.scan()` controlla per prima cosa.
Esempio
Controllo interno dietro NDEFReader.scan(): se questa app può usare l'NFC in questo momento. Le pagine chiamano scan() e leggono il suo rifiuto.
Restituisce: 'on', 'off' (l'NFC è disattivato nelle impostazioni del telefono) o 'none' (il telefono non ha l'NFC, oppure l'app è stata creata senza Web NFC Support). Richiede: Web NFC Support attivato nel Passaggio 4 (Accesso) quando crei l'app.
Il modo standard: scan() viene rifiutato con la stessa risposta:
async function startReading() {
if (!('NDEFReader' in window)) { showStatus('NFC is not available in this app.'); return; }
try {
await new NDEFReader().scan();
showStatus('Hold a tag to the back of the phone.');
} catch (err) {
if (err.name === 'NotReadableError') showStatus('Turn on NFC in the phone settings, then try again.');
else if (err.name === 'NotSupportedError') showStatus('This phone has no NFC.');
else showStatus('NFC error: ' + err.message);
}
}Controlla una volta all'apertura della schermata, per scegliere tra il pannello NFC, un suggerimento "attiva l'NFC" e nascondere del tutto l'NFC. Interrompere subito la scansione di prova non lascia nulla in esecuzione:
async function nfcStatus() {
if (!('NDEFReader' in window)) return 'none';
var probe = new AbortController();
try {
await new NDEFReader().scan({ signal: probe.signal });
return 'on';
} catch (err) {
return err.name === 'NotReadableError' ? 'off' : 'none';
} finally {
probe.abort();
}
}
async function refreshNfcUi() {
var state = await nfcStatus();
document.getElementById('nfc-panel').hidden = state !== 'on';
document.getElementById('nfc-off-hint').hidden = state !== 'off';
}
refreshNfcUi();
document.addEventListener('visibilitychange', function () {
if (document.visibilityState === 'visible') refreshNfcUi(); // back from the phone settings
});Leggi i tag solo mentre una schermata è aperta e distingui un tag vuoto o illeggibile dall'NFC disattivato:
var tagScreen = null;
async function openTagScreen() {
if (!('NDEFReader' in window)) { showStatus('NFC is not available in this app.'); return; }
tagScreen = new AbortController();
var reader = new NDEFReader();
reader.onreading = function (e) {
var n = e.message.records.length;
showStatus(n ? 'Tag ' + e.serialNumber + ' holds ' + n + ' record(s).' : 'This tag is empty.');
};
reader.onreadingerror = function () { showStatus('This tag cannot be read. Try another one.'); };
try {
await reader.scan({ signal: tagScreen.signal });
showStatus('Hold a tag to the back of the phone.');
} catch (err) {
showStatus(err.name === 'NotReadableError' ? 'NFC is off. Turn it on in the phone settings.' : 'NFC error: ' + err.message);
}
}
function closeTagScreen() {
if (tagScreen) { tagScreen.abort(); tagScreen = null; }
}Note: Infrastruttura per il polyfill; usa scan(). window.AndroidNFC.startScan() risponde alla stessa domanda come booleano e mostra il messaggio "Attiva l'NFC" quando l'NFC è disattivato.
__nfcWrite bridge#
window.WebToApk.__nfcWrite(messageJson: String): String
Mette in coda [messageJson] (`{records:[…], overwrite}`, creato da `NDEFReader.write()`) per il prossimo tocco su un tag e restituisce il suo id operazione; "" quando l'NFC non è abilitato in questa app.
Esempio
Trasporto interno dietro NDEFReader.write(message). Le pagine usano new NDEFReader().write(...); non chiamarlo mai direttamente.
Restituisce: (tramite write) una Promise che si risolve dopo che l'utente tocca un tag e questo viene scritto, oppure viene rifiutata con una DOMException. Richiede: Web NFC Support attivato nel Passaggio 4 (Accesso) quando crei l'app.
document.getElementById('write-table-tag').addEventListener('click', async function () {
if (!('NDEFReader' in window)) { showStatus('NFC is not available in this app.'); return; }
showStatus('Tap a tag to write it...');
try {
await new NDEFReader().write({
records: [
{ recordType: 'url', data: 'https://example.com/menu' },
{ recordType: 'text', data: 'Table 12', lang: 'en' },
{ recordType: 'mime', mediaType: 'application/json',
data: new TextEncoder().encode(JSON.stringify({ table: 12 })) }
]
});
showStatus('Tag written.');
} catch (err) {
showStatus('Write failed: ' + err.message);
}
});Scrivi solo su un tag vuoto, così un tag che contiene già dati non viene mai sostituito per errore:
async function writeToBlankTag(url) {
if (!('NDEFReader' in window)) { showStatus('NFC is not available in this app.'); return; }
showStatus('Tap a blank tag...');
try {
await new NDEFReader().write(
{ records: [{ recordType: 'url', data: url }] },
{ overwrite: false }
);
showStatus('Tag written.');
} catch (err) {
if (err.name === 'NotAllowedError') showStatus('That tag already holds data or is locked. Use a blank tag.');
else if (err.name === 'NetworkError') showStatus('Hold the tag still until it is written.');
else showStatus('Write failed: ' + err.message);
}
}Scrivi uno smart poster, un link insieme al suo titolo, come un unico record:
document.getElementById('write-poster').addEventListener('click', async function () {
if (!('NDEFReader' in window)) { showStatus('NFC is not available in this app.'); return; }
showStatus('Tap a tag to write the poster...');
try {
await new NDEFReader().write({
records: [{
recordType: 'smart-poster',
data: { records: [
{ recordType: 'url', data: 'https://example.com/events/spring-fair' },
{ recordType: 'text', data: 'Spring Fair tickets', lang: 'en' }
] }
}]
});
showStatus('Poster tag written.');
} catch (err) {
showStatus(err.name === 'NotSupportedError' ? 'This tag is too small or cannot hold NFC data.' : 'Write failed: ' + err.message);
}
});Note: Il polyfill controlla i record, li trasforma in {records:[{recordType, id, data(base64), encoding, lang, mediaType, records?}], overwrite} e passa qui la stringa JSON. La chiamata restituisce un id operazione; il risultato torna alla pagina tramite window.__appmintNfcDone(id, errorName, message), così la risposta a una scrittura sostituita non chiude mai una più recente. I tag non formattati vengono prima formattati. Rifiuti: NotAllowedError ("Tag is read-only", oppure un tag con record quando overwrite è false), NotSupportedError ("Tag does not support NDEF", "Tag too small (N bytes available)"), NetworkError (il tag si è allontanato), SyntaxError (un record che il formato del tag non può contenere). L'app mostra anche i suoi brevi messaggi "Ready to write" e "NFC tag written".
startScan bridge#
window.AndroidNFC.startScan(): Boolean
Controllo di disponibilità storico, presente solo quando chi crea l'app ha attivato l'NFC. Il vero lavoro con l'NFC passa dal polyfill standard NDEFReader (window.WebToApk.__nfc*), che è ciò che una pagina dovrebbe usare; questo segnala soltanto se l'adattatore è attivo.
Descritto dal suo gruppo, Web NFC, anziché da solo.
Esempio
Ti dice se l'NFC del telefono è attivo. Si trova su window.AndroidNFC, non su window.WebToApk, e non legge un tag: la lettura si fa con NDEFReader.
Restituisce: true (in modo sincrono) quando il telefono ha l'NFC ed è attivo; false quando il telefono non ha l'NFC, oppure l'NFC è disattivato (in quel caso l'app mostra anche un breve messaggio "Attiva l'NFC"). Richiede: Web NFC Support attivato nel Passaggio 4 (Accesso) quando crei l'app; senza, window.AndroidNFC non esiste.
Controlla prima di mostrare un pulsante NFC:
function nfcReady() {
if (!window.AndroidNFC || typeof window.AndroidNFC.startScan !== 'function') return false;
return window.AndroidNFC.startScan();
}
document.getElementById('scan-tag').addEventListener('click', async function () {
if (!nfcReady()) { showStatus('Turn on NFC in the phone settings, then try again.'); return; }
var reader = new NDEFReader();
reader.addEventListener('reading', function (e) { showStatus('Tag ' + e.serialNumber); });
await reader.scan();
showStatus('Hold a tag to the back of the phone.');
});Controlla subito prima di una scrittura: con l'NFC disattivato la scrittura aspetterebbe un tocco che non arriva mai, mentre startScan() risponde subito e mostra il messaggio "Attiva l'NFC" dell'app:
document.getElementById('write-pass').addEventListener('click', async function () {
if (!window.AndroidNFC || typeof window.AndroidNFC.startScan !== 'function') {
showStatus('NFC is not available in this app.');
return;
}
if (!window.AndroidNFC.startScan()) { showStatus('NFC is off, or this phone has no NFC.'); return; }
showStatus('Tap a tag to write it...');
try {
await new NDEFReader().write('Visitor pass #1042');
showStatus('Tag written.');
} catch (err) {
showStatus('Write failed: ' + err.message);
}
});Ricontrolla quando l'utente torna e tocca "L'ho attivato":
var retry = document.getElementById('nfc-retry');
retry.addEventListener('click', function () {
if (!window.AndroidNFC || typeof window.AndroidNFC.startScan !== 'function') {
showStatus('NFC is not available in this app.');
return;
}
if (window.AndroidNFC.startScan()) {
retry.hidden = true;
document.getElementById('nfc-panel').hidden = false;
startReading(); // your own NDEFReader.scan() code
} else {
showStatus('NFC is still off, or this phone has no NFC.');
}
});Note: window.AndroidNFC esiste fin dal primo script della pagina. Dopo che l'utente attiva l'NFC nelle impostazioni e torna indietro, la lettura dei tag riparte da sola.