- android/ 167个模块 (app/content/os/view/widget/graphics/media/hardware/net/telephony/provider等) - java/ 35个模块 (lang/io/util/net/nio/security/crypto/sql/beans/rmi/awt等) - javax/ 4个模块 (net/crypto/security/sql) - org/ 5个模块 (json/w3c_dom/xml_sax/xml_pull/apache_http) - dalvik/ 1个模块 (system) - ~3053个类/接口,方法签名完全对齐Android SDK - 桌面跨平台运行 (Windows/macOS/Linux) - 修复所有导入错误和Python关键字冲突
297 lines
10 KiB
Python
297 lines
10 KiB
Python
"""
|
|
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})" |