API پل جاوااسکریپت
NFC
خواندن تگ از طریق window.AndroidNFC و سمت نوشتن پشت polyfill مربوط به NDEFReader.
NDEFReader استاندارد وب#
new NDEFReader() — Web NFC
نمونه
Web NFC استاندارد: new NDEFReader() تگهای NFC را در برنامهی نصبشده میخواند، مینویسد و قفل میکند، با همان کدی که Chrome در اندروید میپذیرد.
مقدار بازگشتی: Promiseها و رویدادهای reading / readingerror، مثل استاندارد وب. رکوردها NDEFRecord هستند: recordType ('text'، 'url'، 'absolute-url'، 'mime'، 'smart-poster'، 'empty'، 'unknown' یا یک نوع خارجی مثل 'example.com:thing')، mediaType (رکوردهای mime)، id، encoding و lang (رکوردهای متنی) و data بهصورت DataView. پیشنیاز: هنگام ساخت Web NFC Support را در مرحلهی 4 (دسترسی) روشن کنید. بدون آن NDEFReader وجود ندارد. درخواست مجوز در زمان اجرا نیست؛ navigator.permissions.query({name:'nfc'}) پاسخ میدهد granted.
تگها را بخوانید. data را با TextDecoder رمزگشایی کنید، دقیقاً مثل 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
});متن یا لینک بنویسید. Promise پس از ضربهی کاربر روی تگ حل میشود:
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
}
}یک تگ را برای همیشه قفل کنید. makeReadOnly() قابل بازگشت نیست - ابتدا بپرسید:
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); }
}نکتهها: انواع رکورد برای write: text (رشته، یا بایت با encoding)، url و absolute-url (رشته)، mime (بایت + mediaType)، smart-poster ({records: [...]})، یک نوع خارجی 'domain:thing' (بایت یا {records: [...]})، unknown (بایت) و empty. یک رشتهی ساده رکورد متنی مینویسد؛ بایت ساده یک رکورد mime به نام application/octet-stream مینویسد. فقط خوانندههایی که scan() را فراخوانی کردهاند رویداد reading میگیرند؛ AbortController.abort() روی signal آن خواننده را متوقف میکند (در استاندارد stop() وجود ندارد). readingerror برای تگی که نمیتواند دادهی NFC نگه دارد اجرا میشود. گوشی هر بار منتظر فقط یک عملیات تگ میماند: یک write() یا makeReadOnly() جدید در حین انتظار قبلی، آن را جایگزین میکند و فراخوانی جایگزینشده با AbortError رد میشود (Chrome آن را در صف میگذارد). یک signal روی write() / makeReadOnly() انتظار را لغو میکند. خطاها: NotSupportedError (NFC نیست، یا تگ نمیتواند NDEF نگه دارد، یا خیلی کوچک است)، NotReadableError (NFC خاموش است)، NotAllowedError (تگ فقطخواندنی، یا overwrite: false روی تگی که رکورد دارد)، NetworkError (تگ دور شد)، TypeError (رکوردی که استاندارد اجازه نمیدهد). NDEFReader در صفحهی اصلی وجود دارد، نه درون iframeها.
__nfcCancelWrite پل#
window.WebToApk.__nfcCancelWrite()
نوشتن/قفل در حال انتظار را لغو میکند (polyfill وقتی `signal` عملیات لغو شود آن را فراخوانی میکند).
نمونه
نوشتن یا قفل NFC در صف را حذف میکند تا ضربهی بعدی تگ بهطور عادی خوانده شود. صفحهها از طریق استاندارد وب لغو میکنند - یک AbortController signal - و polyfill مربوط به NDEFReader این را برایشان فراخوانی میکند.
مقدار بازگشتی: چیزی نیست. پیشنیاز: گزینهی Web NFC Support در مرحلهی 4 (دسترسی) هنگام ساخت روشن باشد.
راه استاندارد که برنامه رعایت میکند - write() در حال انتظار با این خطا رد میشود 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();
});اگر تگی بهموقع نرسید خودش منصرف شود؛ تایمری که signal را لغو کند همینطور لغو میکند:
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);
}
}وقتی کاربر صفحه را ترک میکند، قفل (یا نوشتن) در حال انتظار را لغو کنید تا ضربهی بعدی تگ را تغییر ندهد:
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; });
});نکتهها: خودتان WebToApk.__nfcCancelWrite() را فراخوانی نکنید: اگر مستقیم فراخوانی شود انتظار بومی را حذف میکند اما نمیتواند Promise صفحه را تمام کند. signal هر دو کار را انجام میدهد. لغو کردن، یک عملیات در صف را هم حذف میکند makeReadOnly().
__nfcMakeReadOnly پل#
window.WebToApk.__nfcMakeReadOnly(): String
یک قفل دائمی (`NDEFReader.makeReadOnly()`) را برای ضربهی بعدی روی تگ در صف میگذارد؛ شناسهی عملیات آن را برمیگرداند و اگر NFC در این برنامه خاموش باشد، "" برمیگرداند.
نمونه
زیرساخت داخلی پشت NDEFReader.makeReadOnly(). صفحهها از new NDEFReader().makeReadOnly() استفاده میکنند؛ هرگز مستقیم آن را فراخوانی نکنید.
مقدار بازگشتی: (از طریق makeReadOnly) یک Promise که پس از ضربهی کاربر روی تگ و قفل شدن آن حل میشود، یا با یک DOMException رد میشود. پیشنیاز: گزینهی Web NFC Support در مرحلهی 4 (دسترسی) هنگام ساخت روشن باشد.
قفل کردن دائمی است. ابتدا تگ را بنویسید، بعد قفلش کنید و پیش از هر دو از کاربر بپرسید:
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);
}
});یک تگ را که قبلاً نوشته شده قفل کنید و اگر ظرف 20 ثانیه تگی نرسید منصرف شوید:
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);
}
}ابتدا یک تگ را بخوانید و فقط وقتی لینک مورد انتظار را دارد، پیشنهاد قفل کردنش را بدهید:
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); }
};
}نکتهها: هر مرحله به ضربهی جداگانه نیاز دارد. فراخوانی یک شناسهی عملیات برمیگرداند و نتیجه از طریق window.__appmintNfcDone میرسد. رد شدنها: NotSupportedError «Tag does not support NDEF»، NotAllowedError «This tag cannot be made read-only» (بسیاری از تگها قفل نمیشوند)، NetworkError وقتی تگ دور میشود. شروع یک write() در حالی که قفلی منتظر است، قفل را جایگزین میکند و آن قفل با AbortError رد میشود. یک signal که به makeReadOnly({ signal }) داده شود، انتظار را لغو میکند.
__nfcState پل#
window.WebToApk.__nfcState(): String
"on"، "off" (NFC در تنظیمات گوشی خاموش شده) یا "none" (NFC نیست یا در این برنامه فعال نشده) - چیزی که `NDEFReader.scan()` ابتدا بررسی میکند.
نمونه
بررسی داخلی پشت NDEFReader.scan(): اینکه این برنامه الان میتواند از NFC استفاده کند یا نه. صفحهها scan() را فراخوانی میکنند و بهجای آن، رد شدنش را میخوانند.
مقدار بازگشتی: 'on'، 'off' (NFC در تنظیمات گوشی خاموش است) یا 'none' (گوشی NFC ندارد یا برنامه بدون Web NFC Support ساخته شده است). پیشنیاز: گزینهی Web NFC Support در مرحلهی 4 (دسترسی) هنگام ساخت روشن باشد.
راه استاندارد - scan() با همین پاسخ رد میشود:
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);
}
}یک بار هنگام باز شدن صفحه بررسی کنید تا بین پنل NFC، یک راهنمای «NFC را روشن کنید» و پنهان کردن کامل NFC یکی را انتخاب کنید. اگر اسکن بررسی را فوراً لغو کنید، چیزی در حال اجرا نمیماند:
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
});تگها را فقط وقتی یک صفحه باز است بخوانید و تگ خالی یا ناخوانا را از خاموش بودن NFC تشخیص دهید:
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; }
}نکتهها: زیرساخت polyfill است؛ از scan() استفاده کنید. window.AndroidNFC.startScan() همین پرسش را بهصورت بولی پاسخ میدهد و وقتی NFC خاموش است پیام «لطفاً NFC را روشن کنید» نشان میدهد.
__nfcWrite پل#
window.WebToApk.__nfcWrite(messageJson: String): String
[messageJson] (`{records:[…], overwrite}`، ساختهشده توسط `NDEFReader.write()`) را برای ضربهی بعدی روی تگ در صف میگذارد و شناسهی عملیات آن را برمیگرداند؛ اگر NFC در این برنامه فعال نباشد "" برمیگرداند.
نمونه
زیرساخت داخلی پشت NDEFReader.write(message). صفحهها از new NDEFReader().write(...) استفاده میکنند؛ هرگز مستقیم آن را فراخوانی نکنید.
مقدار بازگشتی: (از طریق write) یک Promise که پس از ضربهی کاربر روی تگ و نوشته شدن آن حل میشود، یا با یک DOMException رد میشود. پیشنیاز: گزینهی Web NFC Support در مرحلهی 4 (دسترسی) هنگام ساخت روشن باشد.
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);
}
});فقط روی تگ خالی بنویسید تا تگی که داده دارد بهاشتباه جایگزین نشود:
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);
}
}یک smart poster، یعنی لینک همراه عنوانش، را بهصورت یک رکورد بنویسید:
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);
}
});نکتهها: polyfill رکوردها را بررسی میکند، آنها را به {records:[{recordType, id, data(base64), encoding, lang, mediaType, records?}], overwrite} تبدیل میکند و رشتهی JSON را به اینجا میدهد. فراخوانی یک شناسهی عملیات برمیگرداند؛ نتیجه از طریق window.__appmintNfcDone(id, errorName, message) به صفحه میرسد، پس پاسخ یک نوشتن جایگزینشده هرگز نوشتن جدیدتر را تمام نمیکند. تگهای فرمتنشده ابتدا فرمت میشوند. رد شدنها: NotAllowedError («Tag is read-only» یا تگی که رکورد دارد وقتی overwrite برابر false است)، NotSupportedError («Tag does not support NDEF»، «Tag too small (N bytes available)»)، NetworkError (تگ دور شد)، SyntaxError (رکوردی که قالب تگ نمیتواند نگه دارد). برنامه پیامهای کوتاه خودش، «Ready to write» و «NFC tag written»، را هم نشان میدهد.
startScan پل#
window.AndroidNFC.startScan(): Boolean
بررسی قدیمی در دسترس بودن، فقط وقتی سازنده NFC را فعال کرده باشد وجود دارد. کار واقعی NFC از طریق polyfill استاندارد NDEFReader (window.WebToApk.__nfc*) انجام میشود که صفحه باید از آن استفاده کند؛ این فقط گزارش میدهد که آداپتور روشن است یا نه.
با گروهش، Web NFC، توضیح داده شده است، نه جداگانه.
نمونه
به شما میگوید NFC گوشی روشن است یا نه. در window.AndroidNFC است، نه در window.WebToApk، و تگ را نمیخواند - خواندن با این انجام میشود NDEFReader.
مقدار بازگشتی: true (همزمان) وقتی گوشی NFC دارد و روشن است؛ false وقتی گوشی NFC ندارد یا NFC خاموش است (در این حالت برنامه یک پیام کوتاه «لطفاً NFC را روشن کنید» هم نشان میدهد). پیشنیاز: گزینهی Web NFC Support در مرحلهی 4 (دسترسی) هنگام ساخت روشن باشد؛ بدون آن window.AndroidNFC وجود ندارد.
پیش از نشان دادن دکمهی 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.');
});درست پیش از نوشتن بررسی کنید: وقتی NFC خاموش است نوشتن منتظر ضربهای میماند که هرگز نمیآید، در حالی که startScan() فوراً پاسخ میدهد و پیام «لطفاً NFC را روشن کنید» برنامه را نشان میدهد:
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);
}
});وقتی کاربر برمیگردد و «روشنش کردم» را میزند، دوباره بررسی کنید:
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.');
}
});نکتهها: window.AndroidNFC از همان اولین اسکریپت صفحه وجود دارد. بعد از اینکه کاربر NFC را در تنظیمات روشن کرد و برگشت، خواندن تگ خودکار شروع میشود.