390 lines
13 KiB
Python
390 lines
13 KiB
Python
"""
|
|
Android NFC 组件 — NfcAdapter, NfcManager, Tag, NdefMessage 等
|
|
方法签名完全对齐 Android SDK
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
from typing import Any, Dict, List, Optional, Set, Tuple, Union
|
|
|
|
from .._core import (
|
|
Stub, AndroidOnlyError, android_context,
|
|
resolve_android_path, to_android_path,
|
|
_sdcard_root, _app_data_dir, _package_name,
|
|
)
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# NdefRecord
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class NdefRecord(Stub):
|
|
"""NdefRecord — NDEF 记录"""
|
|
|
|
TNF_ABSOLUTE_URI = 3
|
|
TNF_EMPTY = 0
|
|
TNF_EXTERNAL_TYPE = 4
|
|
TNF_MIME_MEDIA = 2
|
|
TNF_UNCHANGED = 6
|
|
TNF_UNKNOWN = 5
|
|
TNF_WELL_KNOWN = 1
|
|
|
|
RTD_URI = "U"
|
|
RTD_TEXT = "T"
|
|
RTD_SMART_POSTER = "Sp"
|
|
RTD_ALTERNATIVE_CARRIER = "ac"
|
|
RTD_HANDOVER_CARRIER = "Hc"
|
|
RTD_HANDOVER_REQUEST = "Hr"
|
|
RTD_HANDOVER_SELECT = "Hs"
|
|
|
|
def __init__(self, tnf: int = TNF_EMPTY, type_: bytes = b"",
|
|
id_: bytes = b"", payload: bytes = b""):
|
|
super().__init__()
|
|
self._tnf = tnf
|
|
self._type = type_
|
|
self._id = id_
|
|
self._payload = payload
|
|
|
|
@staticmethod
|
|
def createUri(uri_spec: str) -> "NdefRecord":
|
|
return NdefRecord(NdefRecord.TNF_ABSOLUTE_URI,
|
|
type_=uri_spec.encode("utf-8"),
|
|
payload=uri_spec.encode("utf-8"))
|
|
|
|
@staticmethod
|
|
def createMime(mime_type: str, data: bytes) -> "NdefRecord":
|
|
return NdefRecord(NdefRecord.TNF_MIME_MEDIA,
|
|
type_=mime_type.encode("utf-8"),
|
|
payload=data)
|
|
|
|
@staticmethod
|
|
def createExternal(domain: str, type_: str, data: bytes) -> "NdefRecord":
|
|
full_type = f"{domain}:{type_}"
|
|
return NdefRecord(NdefRecord.TNF_EXTERNAL_TYPE,
|
|
type_=full_type.encode("utf-8"),
|
|
payload=data)
|
|
|
|
@staticmethod
|
|
def createApplicationRecord(package_name: str) -> "NdefRecord":
|
|
return NdefRecord(NdefRecord.TNF_EXTERNAL_TYPE,
|
|
type_=package_name.encode("utf-8"),
|
|
payload=package_name.encode("utf-8"))
|
|
|
|
def getTnf(self) -> int:
|
|
return self._tnf
|
|
|
|
def getType(self) -> bytes:
|
|
return self._type
|
|
|
|
def getId(self) -> bytes:
|
|
return self._id
|
|
|
|
def getPayload(self) -> bytes:
|
|
return self._payload
|
|
|
|
def toByteArray(self) -> bytes:
|
|
import struct
|
|
tnf_byte = struct.pack("B", self._tnf & 0x07)
|
|
type_len = len(self._type)
|
|
id_len = len(self._id)
|
|
payload_len = len(self._payload)
|
|
header = struct.pack("BB", type_len, id_len)
|
|
return tnf_byte + header + struct.pack(">I", payload_len) + \
|
|
self._type + self._id + self._payload
|
|
|
|
def toString(self) -> str:
|
|
return f"NdefRecord(tnf={self._tnf}, type={self._type})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# NdefMessage
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class NdefMessage(Stub):
|
|
"""NdefMessage — NDEF 消息"""
|
|
|
|
def __init__(self, records: Union[List[NdefRecord], bytes, Any]):
|
|
super().__init__()
|
|
if isinstance(records, list):
|
|
self._records = list(records)
|
|
elif isinstance(records, bytes):
|
|
self._records = []
|
|
else:
|
|
self._records = []
|
|
|
|
def getRecords(self) -> List[NdefRecord]:
|
|
return list(self._records)
|
|
|
|
def getByteArrayLength(self) -> int:
|
|
return sum(len(r.toByteArray()) for r in self._records)
|
|
|
|
def toByteArray(self) -> bytes:
|
|
data = b""
|
|
for r in self._records:
|
|
data += r.toByteArray()
|
|
return data
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# Tag
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class Tag(Stub):
|
|
"""Tag — NFC 标签"""
|
|
|
|
def __init__(self, id_: bytes = b"", tech_list: Optional[List[str]] = None):
|
|
super().__init__()
|
|
self._id = id_
|
|
self._tech_list = tech_list or []
|
|
|
|
def getId(self) -> bytes:
|
|
return self._id
|
|
|
|
def getTechList(self) -> List[str]:
|
|
return list(self._tech_list)
|
|
|
|
def getTechListParcelable(self) -> Any:
|
|
return TechListParcelable(self._tech_list)
|
|
|
|
def toString(self) -> str:
|
|
return f"Tag(id={self._id.hex()}, techs={self._tech_list})"
|
|
|
|
|
|
class TechListParcelable(Stub):
|
|
"""TechListParcelable — 技术列表包裹"""
|
|
|
|
def __init__(self, tech_list: List[str]):
|
|
super().__init__()
|
|
self._tech_list = list(tech_list)
|
|
|
|
def getTechList(self) -> List[str]:
|
|
return list(self._tech_list)
|
|
|
|
def toString(self) -> str:
|
|
return f"TechListParcelable(techs={self._tech_list})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# NfcAdapter callback classes
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class NfcAdapterCreateBeamUrisCallback(Stub):
|
|
def createBeamUris(self, event: "NfcEvent") -> List[Any]:
|
|
return []
|
|
|
|
|
|
class NfcAdapterOnNdefPushCompleteCallback(Stub):
|
|
def onNdefPushComplete(self, event: "NfcEvent"):
|
|
pass
|
|
|
|
|
|
class NdefMessageCallback(Stub):
|
|
def createNdefMessage(self, event: "NfcEvent") -> Optional[NdefMessage]:
|
|
return None
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# NfcEvent
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class NfcEvent(Stub):
|
|
"""NfcEvent — NFC 事件"""
|
|
|
|
def __init__(self, nfc_adapter: Any = None):
|
|
super().__init__()
|
|
self.nfcAdapter = nfc_adapter
|
|
|
|
def getNfcAdapter(self) -> Any:
|
|
return self.nfcAdapter
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# NfcAntennaInfo / ApduServiceInfo / CardEmulation / NfcFCardEmulation
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class NfcAntennaInfo(Stub):
|
|
"""NfcAntennaInfo — NFC 天线信息"""
|
|
|
|
def __init__(self, x: float = 0.0, y: float = 0.0):
|
|
super().__init__()
|
|
self._x = x
|
|
self._y = y
|
|
|
|
def getX(self) -> float:
|
|
return self._x
|
|
|
|
def getY(self) -> float:
|
|
return self._y
|
|
|
|
|
|
class ApduServiceInfo(Stub):
|
|
"""ApduServiceInfo — APDU 服务信息"""
|
|
|
|
def __init__(self, description: str = ""):
|
|
super().__init__()
|
|
self._description = description
|
|
|
|
def getDescription(self) -> str:
|
|
return self._description
|
|
|
|
|
|
class CardEmulation(Stub):
|
|
"""CardEmulation — 卡模拟"""
|
|
|
|
CATEGORY_PAYMENT = "payment"
|
|
CATEGORY_OTHER = "other"
|
|
|
|
SELECTION_TYPE_ASK_IF_CONFLICT = 1
|
|
SELECTION_TYPE_PREFER_DEFAULT = 2
|
|
SELECTION_TYPE_USER_PROMPTED = 0
|
|
|
|
|
|
class NfcFCardEmulation(Stub):
|
|
"""NfcFCardEmulation — NFC-F 卡模拟"""
|
|
|
|
EXTRA_NFC_F_CARD_EMULATION_PARAMS = "android.nfc.extra.EXTRA_NFC_F_CARD_EMULATION_PARAMS"
|
|
|
|
@staticmethod
|
|
def getInstance(nfc_adapter: Any) -> "NfcFCardEmulation":
|
|
return NfcFCardEmulation()
|
|
|
|
def enableService(self, activity: Any, service: Any) -> bool:
|
|
return True
|
|
|
|
def disableService(self, activity: Any, service: Any) -> bool:
|
|
return True
|
|
|
|
def setSystemCodeForService(self, activity: Any, service: Any, system_code: str) -> bool:
|
|
return True
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# NfcManager
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class NfcManager(Stub):
|
|
"""NfcManager — NFC 管理器"""
|
|
|
|
def __init__(self, context: Any = None):
|
|
super().__init__()
|
|
self._context = context
|
|
self._default_adapter: Optional["NfcAdapter"] = None
|
|
|
|
def getDefaultAdapter(self) -> Optional["NfcAdapter"]:
|
|
if self._default_adapter is None:
|
|
self._default_adapter = NfcAdapter()
|
|
return self._default_adapter
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# NfcAdapter
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class NfcAdapter(Stub):
|
|
"""NfcAdapter — NFC 适配器"""
|
|
|
|
ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED"
|
|
ACTION_TECH_DISCOVERED = "android.nfc.action.TECH_DISCOVERED"
|
|
ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED"
|
|
|
|
FLAG_READER_NFC_A = 1
|
|
FLAG_READER_NFC_B = 2
|
|
FLAG_READER_NFC_F = 4
|
|
FLAG_READER_NFC_V = 8
|
|
FLAG_READER_NFC_BARCODE = 16
|
|
FLAG_READER_SKIP_NDEF_CHECK = 128
|
|
FLAG_READER_NO_PLATFORM_SOUNDS = 256
|
|
|
|
FLAG_NDEF_PUSH_NO_CONFIRM = 1
|
|
|
|
_default_adapter: Optional["NfcAdapter"] = None
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._enabled = True
|
|
self._ndef_push_message: Optional[NdefMessage] = None
|
|
self._ndef_push_callback: Optional[NdefMessageCallback] = None
|
|
self._push_complete_callback: Optional[NfcAdapterOnNdefPushCompleteCallback] = None
|
|
self._beam_uri_callback: Optional[NfcAdapterCreateBeamUrisCallback] = None
|
|
|
|
@staticmethod
|
|
def getDefaultAdapter(context: Any = None) -> Optional["NfcAdapter"]:
|
|
if NfcAdapter._default_adapter is None:
|
|
NfcAdapter._default_adapter = NfcAdapter()
|
|
return NfcAdapter._default_adapter
|
|
|
|
def isEnabled(self) -> bool:
|
|
return self._enabled
|
|
|
|
def enable(self) -> bool:
|
|
self._enabled = True
|
|
return True
|
|
|
|
def disable(self) -> bool:
|
|
self._enabled = False
|
|
return True
|
|
|
|
def setNdefPushMessage(self, message: NdefMessage, activity: Any,
|
|
vararg_activities: Any = None):
|
|
self._ndef_push_message = message
|
|
|
|
def setNdefPushMessageCallback(self, callback: NdefMessageCallback, activity: Any,
|
|
vararg_activities: Any = None):
|
|
self._ndef_push_callback = callback
|
|
|
|
def setOnNdefPushCompleteCallback(self, callback: NfcAdapterOnNdefPushCompleteCallback,
|
|
activity: Any, vararg_activities: Any = None):
|
|
self._push_complete_callback = callback
|
|
|
|
def setBeamUrisCallback(self, callback: NfcAdapterCreateBeamUrisCallback, activity: Any):
|
|
self._beam_uri_callback = callback
|
|
|
|
def enableForegroundDispatch(self, activity: Any, intent: Any,
|
|
filters: Any, tech_lists: Any):
|
|
pass
|
|
|
|
def disableForegroundDispatch(self, activity: Any):
|
|
pass
|
|
|
|
def enableReaderMode(self, activity: Any, callback: Any, flags: int, extras: Any = None):
|
|
pass
|
|
|
|
def disableReaderMode(self, activity: Any):
|
|
pass
|
|
|
|
def invokeBeam(self, activity: Any) -> bool:
|
|
return True
|
|
|
|
def isSecureNfcSupported(self) -> bool:
|
|
return False
|
|
|
|
def isSecureNfcEnabled(self) -> bool:
|
|
return False
|
|
|
|
def enableSecureNfc(self, enable: bool) -> bool:
|
|
return True
|
|
|
|
def toString(self) -> str:
|
|
return f"NfcAdapter(enabled={self._enabled})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# __all__
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
__all__ = [
|
|
"NfcAdapter",
|
|
"NfcManager",
|
|
"Tag",
|
|
"NdefMessage",
|
|
"NdefRecord",
|
|
"NfcEvent",
|
|
"NfcAdapterCreateBeamUrisCallback",
|
|
"NfcAdapterOnNdefPushCompleteCallback",
|
|
"NdefMessageCallback",
|
|
"TechListParcelable",
|
|
"NfcAntennaInfo",
|
|
"ApduServiceInfo",
|
|
"CardEmulation",
|
|
"NfcFCardEmulation",
|
|
] |