mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-05-11 12:37:34 +02:00
e1d06b93b7
* Drop eslint config love * More eqeqeq * Even more
276 lines
5.9 KiB
TypeScript
276 lines
5.9 KiB
TypeScript
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
import { invoke } from "@tauri-apps/api/core";
|
|
|
|
export const RTD_TEXT = [0x54]; // "T"
|
|
export const RTD_URI = [0x55]; // "U"
|
|
|
|
export interface UriFilter {
|
|
scheme?: string;
|
|
host?: string;
|
|
pathPrefix?: string;
|
|
}
|
|
|
|
export enum TechKind {
|
|
IsoDep,
|
|
MifareClassic,
|
|
MifareUltralight,
|
|
Ndef,
|
|
NdefFormatable,
|
|
NfcA,
|
|
NfcB,
|
|
NfcBarcode,
|
|
NfcF,
|
|
NfcV,
|
|
}
|
|
|
|
export type ScanKind =
|
|
| {
|
|
type: "tag";
|
|
uri?: UriFilter;
|
|
mimeType?: string;
|
|
}
|
|
| {
|
|
type: "ndef";
|
|
uri?: UriFilter;
|
|
mimeType?: string;
|
|
/**
|
|
* Each of the tech-lists is considered independently and the activity is considered a match if
|
|
* any single tech-list matches the tag that was discovered.
|
|
* This provides AND and OR semantics for filtering desired techs.
|
|
*
|
|
* See <https://developer.android.com/reference/android/nfc/NfcAdapter#ACTION_TECH_DISCOVERED> for more information.
|
|
*
|
|
* Examples
|
|
*
|
|
* ```ts
|
|
* import type { TechKind } from "@tauri-apps/plugin-nfc"
|
|
*
|
|
* const techLists = [
|
|
* // capture anything using NfcF
|
|
* [TechKind.NfcF],
|
|
* // capture all MIFARE Classics with NDEF payloads
|
|
* [TechKind.NfcA, TechKind.MifareClassic, TechKind.Ndef]
|
|
* ]
|
|
* ```
|
|
*/
|
|
techLists?: TechKind[][];
|
|
};
|
|
|
|
export interface ScanOptions {
|
|
keepSessionAlive?: boolean;
|
|
/** Message displayed in the UI. iOS only. */
|
|
message?: string;
|
|
/** Message displayed in the UI when the message has been read. iOS only. */
|
|
successMessage?: string;
|
|
}
|
|
|
|
export interface WriteOptions {
|
|
kind?: ScanKind;
|
|
/** Message displayed in the UI when reading the tag. iOS only. */
|
|
message?: string;
|
|
/** Message displayed in the UI when the tag has been read. iOS only. */
|
|
successfulReadMessage?: string;
|
|
/** Message displayed in the UI when the message has been written. iOS only. */
|
|
successMessage?: string;
|
|
}
|
|
|
|
export enum NFCTypeNameFormat {
|
|
Empty = 0,
|
|
NfcWellKnown = 1,
|
|
Media = 2,
|
|
AbsoluteURI = 3,
|
|
NfcExternal = 4,
|
|
Unknown = 5,
|
|
Unchanged = 6,
|
|
}
|
|
|
|
export interface TagRecord {
|
|
tnf: NFCTypeNameFormat;
|
|
kind: number[];
|
|
id: number[];
|
|
payload: number[];
|
|
}
|
|
|
|
export interface Tag {
|
|
id: number[];
|
|
kind: string[];
|
|
records: TagRecord[];
|
|
}
|
|
|
|
export interface NFCRecord {
|
|
format: NFCTypeNameFormat;
|
|
kind: number[];
|
|
id: number[];
|
|
payload: number[];
|
|
}
|
|
|
|
export function record(
|
|
format: NFCTypeNameFormat,
|
|
kind: string | number[],
|
|
id: string | number[],
|
|
payload: string | number[],
|
|
): NFCRecord {
|
|
return {
|
|
format,
|
|
kind:
|
|
typeof kind === "string"
|
|
? Array.from(new TextEncoder().encode(kind))
|
|
: kind,
|
|
id: typeof id === "string" ? Array.from(new TextEncoder().encode(id)) : id,
|
|
payload:
|
|
typeof payload === "string"
|
|
? Array.from(new TextEncoder().encode(payload))
|
|
: payload,
|
|
};
|
|
}
|
|
|
|
export function textRecord(
|
|
text: string,
|
|
id?: string | number[],
|
|
language: string = "en",
|
|
): NFCRecord {
|
|
const payload = Array.from(new TextEncoder().encode(language + text));
|
|
payload.unshift(language.length);
|
|
return record(NFCTypeNameFormat.NfcWellKnown, RTD_TEXT, id ?? [], payload);
|
|
}
|
|
|
|
const protocols = [
|
|
"",
|
|
"http://www.",
|
|
"https://www.",
|
|
"http://",
|
|
"https://",
|
|
"tel:",
|
|
"mailto:",
|
|
"ftp://anonymous:anonymous@",
|
|
"ftp://ftp.",
|
|
"ftps://",
|
|
"sftp://",
|
|
"smb://",
|
|
"nfs://",
|
|
"ftp://",
|
|
"dav://",
|
|
"news:",
|
|
"telnet://",
|
|
"imap:",
|
|
"rtsp://",
|
|
"urn:",
|
|
"pop:",
|
|
"sip:",
|
|
"sips:",
|
|
"tftp:",
|
|
"btspp://",
|
|
"btl2cap://",
|
|
"btgoep://",
|
|
"tcpobex://",
|
|
"irdaobex://",
|
|
"file://",
|
|
"urn:epc:id:",
|
|
"urn:epc:tag:",
|
|
"urn:epc:pat:",
|
|
"urn:epc:raw:",
|
|
"urn:epc:",
|
|
"urn:nfc:",
|
|
];
|
|
|
|
function encodeURI(uri: string): number[] {
|
|
let prefix = "";
|
|
|
|
protocols.slice(1).forEach(function (protocol) {
|
|
if (
|
|
(prefix.length === 0 || prefix === "urn:") &&
|
|
uri.indexOf(protocol) === 0
|
|
) {
|
|
prefix = protocol;
|
|
}
|
|
});
|
|
|
|
if (prefix.length === 0) {
|
|
prefix = "";
|
|
}
|
|
|
|
const encoded = Array.from(
|
|
new TextEncoder().encode(uri.slice(prefix.length)),
|
|
);
|
|
const protocolCode = protocols.indexOf(prefix);
|
|
// prepend protocol code
|
|
encoded.unshift(protocolCode);
|
|
|
|
return encoded;
|
|
}
|
|
|
|
export function uriRecord(uri: string, id?: string | number[]): NFCRecord {
|
|
return record(
|
|
NFCTypeNameFormat.NfcWellKnown,
|
|
RTD_URI,
|
|
id ?? [],
|
|
encodeURI(uri),
|
|
);
|
|
}
|
|
|
|
function mapScanKind(kind: ScanKind): Record<string, unknown> {
|
|
const { type: scanKind, ...kindOptions } = kind;
|
|
return { [scanKind]: kindOptions };
|
|
}
|
|
|
|
/**
|
|
* Scans an NFC tag.
|
|
*
|
|
* ```javascript
|
|
* import { scan } from "@tauri-apps/plugin-nfc";
|
|
* await scan({ type: "tag" });
|
|
* ```
|
|
*
|
|
* See <https://developer.android.com/develop/connectivity/nfc/nfc#ndef> for more information.
|
|
*
|
|
* @param kind
|
|
* @param options
|
|
* @returns
|
|
*/
|
|
export async function scan(
|
|
kind: ScanKind,
|
|
options?: ScanOptions,
|
|
): Promise<Tag> {
|
|
return await invoke("plugin:nfc|scan", {
|
|
kind: mapScanKind(kind),
|
|
...options,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Write to an NFC tag.
|
|
*
|
|
* ```javascript
|
|
* import { uriRecord, write } from "@tauri-apps/plugin-nfc";
|
|
* await write([uriRecord("https://tauri.app")], { kind: { type: "ndef" } });
|
|
* ```
|
|
*
|
|
* If you did not previously call {@link scan} with {@link ScanOptions.keepSessionAlive} set to true,
|
|
* it will first scan the tag then write to it.
|
|
*
|
|
* @param records
|
|
* @param options
|
|
* @returns
|
|
*/
|
|
export async function write(
|
|
records: NFCRecord[],
|
|
options?: WriteOptions,
|
|
): Promise<void> {
|
|
const { kind, ...opts } = options ?? {};
|
|
if (kind) {
|
|
// @ts-expect-error map the property
|
|
opts.kind = mapScanKind(kind);
|
|
}
|
|
await invoke("plugin:nfc|write", {
|
|
records,
|
|
...opts,
|
|
});
|
|
}
|
|
|
|
export async function isAvailable(): Promise<boolean> {
|
|
return await invoke("plugin:nfc|is_available");
|
|
}
|