""" Android text.util 包 — Linkify, Rfc822Token, Rfc822Tokenizer, Selection, LinkSpec 方法签名完全对齐 Android SDK """ from __future__ import annotations import re from typing import Any, Dict, List, Optional, Set, Tuple from .._core import Stub, AndroidOnlyError from ..java.lang import RuntimeException IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) __all__ = [ "Linkify", "Rfc822Token", "Rfc822Tokenizer", "Selection", "LinkSpec", ] # ═══════════════════════════════════════════════════════════════ # Linkify # ═══════════════════════════════════════════════════════════════ class Linkify(Stub): """Linkify — 自动链接工具 (使用正则表达式检测 URL/邮件/电话)""" ALL = 0xFFFFFFFF PHONE_NUMBERS = 0x1 WEB_URLS = 0x2 EMAIL_ADDRESSES = 0x4 MAP_ADDRESSES = 0x8 _WEB_URL_PATTERN = re.compile( r'(https?://|www\.)[a-zA-Z0-9\-\.]+\.\w{2,}(/\S*)?' ) _EMAIL_PATTERN = re.compile( r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}' ) _PHONE_PATTERN = re.compile( r'(\+?\d{1,3}[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}' ) def __init__(self): super().__init__() @staticmethod def addLinks(text: Any, mask: int = ALL) -> bool: """向文本中添加链接""" if hasattr(text, '_links'): text._links = [] s = str(text) if mask & Linkify.WEB_URLS: for m in Linkify._WEB_URL_PATTERN.finditer(s): spec = LinkSpec() spec.url = m.group() spec.start = m.start() spec.end = m.end() text._links.append(spec) if mask & Linkify.EMAIL_ADDRESSES: for m in Linkify._EMAIL_PATTERN.finditer(s): spec = LinkSpec() spec.url = f"mailto:{m.group()}" spec.start = m.start() spec.end = m.end() text._links.append(spec) if mask & Linkify.PHONE_NUMBERS: for m in Linkify._PHONE_PATTERN.finditer(s): spec = LinkSpec() spec.url = f"tel:{m.group()}" spec.start = m.start() spec.end = m.end() text._links.append(spec) return len(text._links) > 0 return False @staticmethod def addLinks_ext(text: Any, pattern: Any, scheme: str, linkMatchFilter: Any = None, urlFilter: Any = None) -> bool: """使用自定义模式添加链接""" if hasattr(text, '_links'): text._links = [] s = str(text) if isinstance(pattern, str): compiled = re.compile(pattern) else: compiled = pattern for m in compiled.finditer(s): spec = LinkSpec() spec.url = f"{scheme}:{m.group()}" spec.start = m.start() spec.end = m.end() text._links.append(spec) return len(text._links) > 0 return False @staticmethod def removeLinks(text: Any) -> None: """移除文本中的链接""" if hasattr(text, '_links'): text._links = [] @staticmethod def applyLinks(text: Any, mask: int) -> bool: """应用链接""" return Linkify.addLinks(text, mask) def toString(self) -> str: return f"Linkify()" # ═══════════════════════════════════════════════════════════════ # Rfc822Token # ═══════════════════════════════════════════════════════════════ class Rfc822Token(Stub): """Rfc822Token — RFC 822 电子邮件令牌""" def __init__(self, name: str = "", address: str = "", comment: str = ""): super().__init__() self._name = name self._address = address self._comment = comment def getName(self) -> str: """获取名称""" return self._name def getAddress(self) -> str: """获取地址""" return self._address def getComment(self) -> str: """获取注释""" return self._comment def setAddress(self, address: str) -> None: """设置地址""" self._address = address def setComment(self, comment: str) -> None: """设置注释""" self._comment = comment def setName(self, name: str) -> None: """设置名称""" self._name = name def toString(self) -> str: """返回完整的 RFC 822 字符串""" parts = [] if self._name: parts.append(f"{self._name} <{self._address}>") else: parts.append(self._address) if self._comment: parts.append(f"({self._comment})") return " ".join(parts) def equals(self, other: Any) -> bool: if not isinstance(other, Rfc822Token): return False return (self._name == other._name and self._address == other._address and self._comment == other._comment) def hashCode(self) -> int: return hash((self._name, self._address, self._comment)) & 0x7FFFFFFF # ═══════════════════════════════════════════════════════════════ # Rfc822Tokenizer # ═══════════════════════════════════════════════════════════════ class Rfc822Tokenizer(Stub): """Rfc822Tokenizer — RFC 822 分词器""" def __init__(self): super().__init__() @staticmethod def tokenize(text: str) -> List[Rfc822Token]: """分词""" tokens = [] pattern = re.compile(r'([^<,]+)?<([^>]+)>\s*\(([^)]*)\)|([^<,]+)?<([^>]+)>|([^@,\s]+@[^,\s]+)') for m in pattern.finditer(text): if m.group(1) or m.group(2): name = (m.group(1) or "").strip() addr = (m.group(2) or m.group(5) or "").strip() comment = (m.group(3) or "").strip() tokens.append(Rfc822Token(name, addr, comment)) elif m.group(6): tokens.append(Rfc822Token("", m.group(6).strip(), "")) return tokens @staticmethod def getTokenStart(text: str, cursor: int) -> int: """获取令牌起始位置""" return cursor @staticmethod def getTokenEnd(text: str, cursor: int) -> int: """获取令牌结束位置""" return cursor @staticmethod def advance(text: str, cursor: int) -> int: """前进光标""" return cursor + 1 # ═══════════════════════════════════════════════════════════════ # Selection # ═══════════════════════════════════════════════════════════════ class Selection(Stub): """Selection — 文本选择工具""" def __init__(self): super().__init__() @staticmethod def setSelection(content: Any, start: int, stop: Optional[int] = None) -> None: """设置选择范围""" if hasattr(content, '_selection_start'): content._selection_start = start content._selection_end = stop if stop is not None else start if hasattr(content, '_selection'): content._selection = (start, stop if stop is not None else start) @staticmethod def extendSelection(content: Any, index: int) -> None: """扩展选择范围""" if hasattr(content, '_selection_end'): content._selection_end = index @staticmethod def extendUp(content: Any, view: Any) -> bool: """向上扩展选择""" return False @staticmethod def extendDown(content: Any, view: Any) -> bool: """向下扩展选择""" return False @staticmethod def extendLeft(content: Any, view: Any) -> bool: """向左扩展选择""" return False @staticmethod def extendRight(content: Any, view: Any) -> bool: """向右扩展选择""" return False @staticmethod def moveUp(content: Any, view: Any) -> bool: """向上移动选择""" return False @staticmethod def moveDown(content: Any, view: Any) -> bool: """向下移动选择""" return False @staticmethod def moveLeft(content: Any, view: Any) -> bool: """向左移动选择""" return False @staticmethod def moveRight(content: Any, view: Any) -> bool: """向右移动选择""" return False # ═══════════════════════════════════════════════════════════════ # LinkSpec # ═══════════════════════════════════════════════════════════════ class LinkSpec(Stub): """LinkSpec — 链接规范""" def __init__(self): super().__init__() self.url: str = "" self.start: int = 0 self.end: int = 0 self.frameworkAddedSpan: bool = False def toString(self) -> str: return f"LinkSpec(url={self.url}, start={self.start}, end={self.end})"