feat: Android-pyApi v1.0 - 全面翻译 Android SDK API 为 Python

52 个模块覆盖 Android + Java + org.json 标准库
This commit is contained in:
shuiyu1123 committed 2026-06-24 11:16:16 +00:00
commit bfe27fdc08
104 files changed
+58750

No files matched your search

+258
View File
@@ -0,0 +1,258 @@
"""
Android-pyApi — Android API 全面 Python 翻译
==============================================
方法签名完全对齐 Android SDK,跨平台运行于 Windows/macOS/Linux。
用法:
from android_pyapi import Intent, Context, Uri, Bitmap, View
from android_pyapi.java.io import File, FileInputStream
from android_pyapi.java.util import ArrayList, HashMap
from android_pyapi.org.json import JSONObject, JSONArray
设计原则:
1. 方法名、参数名、返回类型完全对齐 Android SDK
2. 能桌面实现的 → 真实实现 (基于 PIL/sqlite3/socket 等)
3. 无法桌面实现的 → 返回合理默认值或抛出 AndroidOnlyError
4. 所有常量值与 Android SDK 一致
"""
from ._core import (
AndroidOnlyError,
Stub,
android_context,
get_context,
_sdcard_root,
_app_data_dir,
_package_name,
)
# android.* 包
from .android.app import *
from .android.content import *
from .android.content_pm import *
from .android.os import *
from .android.view import *
from .android.view_accessibility import *
from .android.view_animation import *
from .android.view_inputmethod import *
from .android.widget import *
from .android.graphics import *
from .android.graphics_drawable import *
from .android.graphics_pdf import *
from .android.net import *
from .android.net_wifi import *
from .android.media import *
from .android.hardware import *
from .android.hardware_camera2 import *
from .android.hardware_biometrics import *
from .android.location import *
from .android.database import *
from .android.database_sqlite import *
from .android.provider import *
from .android.telephony import *
from .android.bluetooth import *
from .android.bluetooth_le import *
from .android.text import *
from .android.util import *
from .android.preference import *
from .android.webkit import *
from .android.nfc import *
from .android.speech_tts import *
from .android.inputmethod import *
# java.* 包
from .java.io import *
from .java.net import *
from .java.util import *
from .java.util_regex import *
from .java.util_zip import *
from .java.util_concurrent import *
from .java.lang import *
from .java.math import *
from .java.text import *
from .java.time import *
from .java.nio import *
from .java.nio_file import *
from .java.security import *
from .java.crypto import *
# org.* 包
from .org.json import *
# Base64 (标准 Android API)
import base64 as _base64
class Base64:
DEFAULT = 0
NO_PADDING = 1
NO_WRAP = 2
CRLF = 4
URL_SAFE = 8
NO_CLOSE = 16
@staticmethod
def encodeToString(input_data, flags=0):
if isinstance(input_data, str):
input_data = input_data.encode('utf-8')
return _base64.b64encode(input_data).decode('ascii')
@staticmethod
def decode(str_val, flags=0):
return _base64.b64decode(str_val)
@staticmethod
def encode(input_data, flags=0):
if isinstance(input_data, str):
input_data = input_data.encode('utf-8')
return _base64.b64encode(input_data)
# Scanner (java.util)
class Scanner:
def __init__(self, source):
if isinstance(source, str):
self._source = source
else:
self._source = str(source)
self._pos = 0
def next(self):
while self._pos < len(self._source) and self._source[self._pos].isspace():
self._pos += 1
start = self._pos
while self._pos < len(self._source) and not self._source[self._pos].isspace():
self._pos += 1
if start == self._pos:
raise Exception("NoSuchElementException")
return self._source[start:self._pos]
def hasNext(self):
pos = self._pos
while pos < len(self._source) and self._source[pos].isspace():
pos += 1
return pos < len(self._source)
def nextInt(self):
return int(self.next())
def close(self):
pass
# Timer (java.util)
class Timer:
def __init__(self, name=None, daemon=False):
import threading
self._thread = None
self._tasks = []
def schedule(self, task, delay=None, period=None):
import threading
if delay is None:
self._run_once(task)
elif period is None:
self._run_delayed(task, delay)
else:
self._run_periodic(task, delay, period)
def _run_once(self, task):
t = threading.Thread(target=task.run)
t.daemon = True
self._tasks.append(t)
t.start()
def _run_delayed(self, task, delay_ms):
import threading
def _run():
import time
time.sleep(delay_ms / 1000.0)
task.run()
t = threading.Thread(target=_run)
t.daemon = True
self._tasks.append(t)
t.start()
def _run_periodic(self, task, delay_ms, period_ms):
import threading
def _run():
import time
time.sleep(delay_ms / 1000.0)
while True:
task.run()
time.sleep(period_ms / 1000.0)
t = threading.Thread(target=_run)
t.daemon = True
self._tasks.append(t)
t.start()
def cancel(self):
pass
def purge(self):
return 0
class TimerTask:
def __init__(self):
self._cancelled = False
def run(self):
pass
def cancel(self):
self._cancelled = True
def scheduledExecutionTime(self):
return 0
__version__ = "1.0.0"
__all__ = [
# android
"Intent", "Context", "Uri", "ContentValues", "ContentResolver",
"BroadcastReceiver", "IntentFilter", "SharedPreferences",
"PackageManager", "PackageInfo", "ActivityManager",
"Build", "Environment", "PowerManager", "BatteryManager",
"Bundle", "Handler", "Looper", "Message",
"View", "ViewGroup", "TextView", "EditText", "Button",
"ImageView", "ScrollView", "ListView", "Spinner",
"CheckBox", "Switch", "ProgressBar", "Toast",
"Bitmap", "BitmapFactory", "Canvas", "Color", "Paint",
"Rect", "RectF", "Matrix", "Typeface",
"PdfRenderer", "Drawable", "BitmapDrawable",
"ConnectivityManager", "NetworkInfo", "WifiManager",
"MediaPlayer", "AudioManager", "MediaRecorder",
"SensorManager", "CameraManager", "BiometricPrompt",
"LocationManager", "Location", "Geocoder",
"SQLiteDatabase", "SQLiteOpenHelper", "Cursor",
"Settings", "MediaStore", "ContactsContract", "CalendarContract",
"TelephonyManager", "SmsManager",
"BluetoothAdapter", "BluetoothDevice",
"TextUtils", "Html", "SpannableString",
"Log", "Base64", "TypedValue",
"AccessibilityNodeInfo", "AccessibilityService",
"Animation", "AlphaAnimation", "TranslateAnimation",
"InputMethodManager",
"WebView",
"TextToSpeech",
# java
"File", "FileInputStream", "FileOutputStream",
"BufferedReader", "BufferedWriter",
"InputStreamReader", "OutputStreamWriter",
"URL", "HttpURLConnection", "Socket", "ServerSocket",
"ArrayList", "HashMap", "HashSet", "LinkedList",
"Collections", "Arrays", "Calendar", "Date", "Random",
"UUID", "Scanner", "Timer", "TimerTask",
"Pattern", "Matcher",
"ZipFile", "GZIPInputStream", "GZIPOutputStream",
"ExecutorService", "Executors", "Future", "Callable",
"String", "StringBuilder", "StringBuffer",
"Integer", "Math", "System", "Thread",
"BigInteger", "BigDecimal",
"SimpleDateFormat", "DecimalFormat",
"LocalDateTime", "LocalDate", "LocalTime", "Instant", "Duration", "Period",
"ChronoUnit", "ChronoField", "DayOfWeek", "Month", "ZoneId", "ZoneOffset",
"DateTimeFormatter",
"ByteBuffer", "Files", "Path", "Paths",
"MessageDigest", "SecureRandom",
"Cipher", "Mac", "KeyGenerator",
# org
"JSONObject", "JSONArray", "JSONException",
]
Binary file not shown.
Binary file not shown.
+186
View File
@@ -0,0 +1,186 @@
"""
Android-pyApi 核心基础设施
"""
import os
import sys
import platform
import tempfile
import json
import hashlib
import random
import time
from typing import Any, Dict, List, Optional, Callable
class AndroidOnlyError(Exception):
"""当调用仅 Android 可用的 API 时抛出"""
pass
class Stub:
"""
通用 Android API 存根基类。
子类可以覆盖具体方法实现真实逻辑;
未覆盖的方法调用会返回合理默认值。
"""
_android_only = False
def __init__(self, *args, **kwargs):
pass
def __getattr__(self, name: str):
if name.startswith("_"):
raise AttributeError(name)
def _stub_method(*args, **kwargs):
if self._android_only:
raise AndroidOnlyError(
f"{self.__class__.__name__}.{name}() 仅在 Android 设备上可用"
)
return None
return _stub_method
def toString(self) -> str:
return f"{self.__class__.__name__}@{hex(id(self))}"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return hash(self) & 0x7FFFFFFF
def getClass(self) -> type:
return self.__class__
# ═══════════════════════════════════════════════════════════════
# 全局路径和环境
# ═══════════════════════════════════════════════════════════════
_package_name = "com.ai.assistance.operit"
_sdcard_root = os.path.join(os.path.expanduser("~"), "Android-compat", "sdcard")
_app_data_dir = os.path.join(os.path.expanduser("~"), "Android-compat", "app-data")
def _ensure_dirs():
dirs = [
_sdcard_root,
os.path.join(_sdcard_root, "Download"),
os.path.join(_sdcard_root, "DCIM"),
os.path.join(_sdcard_root, "DCIM", "Screenshots"),
os.path.join(_sdcard_root, "Documents"),
os.path.join(_sdcard_root, "Music"),
os.path.join(_sdcard_root, "Movies"),
os.path.join(_sdcard_root, "Pictures"),
os.path.join(_sdcard_root, "Android", "data"),
os.path.join(_sdcard_root, "Android", "data", _package_name, "files"),
os.path.join(_sdcard_root, "Android", "data", _package_name, "cache"),
_app_data_dir,
os.path.join(_app_data_dir, "files"),
os.path.join(_app_data_dir, "cache"),
os.path.join(_app_data_dir, "shared_prefs"),
]
for d in dirs:
os.makedirs(d, exist_ok=True)
_ensure_dirs()
def resolve_android_path(path: str) -> str:
"""将 Android 路径解析为桌面路径"""
if path.startswith("/sdcard"):
return os.path.join(_sdcard_root, path[7:].lstrip("/"))
if path.startswith("/data/data/"):
return os.path.join(_app_data_dir, path[11:].lstrip("/"))
if path.startswith("content://"):
# 简单解析 content URI
from urllib.parse import urlparse
parsed = urlparse(path)
return parsed.path or path
if path.startswith("file://"):
from urllib.parse import unquote
return unquote(path[7:])
return path
def to_android_path(desktop_path: str) -> str:
"""将桌面路径映射回 Android 路径"""
if desktop_path.startswith(_sdcard_root):
rel = os.path.relpath(desktop_path, _sdcard_root)
return f"/sdcard/{rel}"
if desktop_path.startswith(_app_data_dir):
rel = os.path.relpath(desktop_path, _app_data_dir)
return f"/data/data/{_package_name}/{rel}"
return desktop_path
# ═══════════════════════════════════════════════════════════════
# 全局 Context 单例
# ═══════════════════════════════════════════════════════════════
class _GlobalContext:
"""全局应用上下文占位符,在 android.content 模块中被替换为真实 Context"""
@property
def packageName(self):
return _package_name
def getPackageName(self):
return _package_name
def getFilesDir(self):
return os.path.join(_app_data_dir, "files")
def getCacheDir(self):
return os.path.join(_app_data_dir, "cache")
def getExternalFilesDir(self, type_=None):
base = os.path.join(_sdcard_root, "Android", "data", _package_name, "files")
if type_:
base = os.path.join(base, type_)
os.makedirs(base, exist_ok=True)
return base
def getExternalStorageDirectory(self):
return _sdcard_root
def getSystemService(self, name):
return None # 由具体模块填充
android_context = _GlobalContext()
def get_context():
"""获取全局 Android Context"""
return android_context
# ═══════════════════════════════════════════════════════════════
# 可选依赖检测
# ═══════════════════════════════════════════════════════════════
def _check_optional(name: str) -> bool:
"""检查可选依赖是否可用"""
try:
__import__(name)
return True
except ImportError:
return False
HAS_PIL = _check_optional("PIL")
HAS_REQUESTS = _check_optional("requests")
HAS_HTTPX = _check_optional("httpx")
HAS_PYAUTOGUI = _check_optional("pyautogui")
HAS_SCREENINFO = _check_optional("screeninfo")
HAS_PSUTIL = _check_optional("psutil")
HAS_PYMUPDF = _check_optional("fitz")
HAS_PDF2IMAGE = _check_optional("pdf2image")
HAS_CRYPTOGRAPHY = _check_optional("cryptography")
HAS_PYPERCLIP = _check_optional("pyperclip")
HAS_JPYPE = _check_optional("jpype")
View File
Whitespace-only changes.
Binary file not shown.
File diff suppressed because it is too large. Load diff
+791
View File
@@ -0,0 +1,791 @@
"""
Android Bluetooth 组件 — BluetoothAdapter, BluetoothDevice, BluetoothGatt 等
方法签名完全对齐 Android SDK
"""
import os
import sys
import uuid
from typing import Any, Callable, 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,
)
# ═══════════════════════════════════════════════════════════════
# BluetoothProfile
# ═══════════════════════════════════════════════════════════════
class BluetoothProfile(Stub):
"""BluetoothProfile — 蓝牙配置档案"""
A2DP = 2
HEALTH = 3
HEADSET = 1
GATT = 7
GATT_SERVER = 8
A2DP_SINK = 11
AVRC = 12
HEARING_AID = 21
LE_AUDIO = 22
LE_AUDIO_BROADCAST = 26
STATE_DISCONNECTED = 0
STATE_CONNECTING = 1
STATE_CONNECTED = 2
STATE_DISCONNECTING = 3
def getConnectedDevices(self) -> List[Any]:
return []
def getDevicesMatchingConnectionState(self, state: int) -> List[Any]:
return []
def getConnectionState(self, device: Any) -> int:
return self.STATE_DISCONNECTED
class BluetoothProfileServiceListener(Stub):
"""BluetoothProfile.ServiceListener"""
def onServiceConnected(self, profile_id: int, proxy: Any):
pass
def onServiceDisconnected(self, profile_id: int):
pass
# ═══════════════════════════════════════════════════════════════
# BluetoothClass
# ═══════════════════════════════════════════════════════════════
class BluetoothClass(Stub):
"""BluetoothClass — 蓝牙设备类别"""
class Device(Stub):
COMPUTER_DESKTOP = 0x100
COMPUTER_LAPTOP = 0x10C
PHONE_SMART = 0x200
PHONE_UNCATEGORIZED = 0x204
AUDIO_VIDEO_WEARABLE_HEADSET = 0x404
AUDIO_VIDEO_HANDSFREE = 0x408
class Major(Stub):
MISC = 0x0000
COMPUTER = 0x0100
PHONE = 0x0200
NETWORKING = 0x0300
AUDIO_VIDEO = 0x0400
PERIPHERAL = 0x0500
IMAGING = 0x0600
WEARABLE = 0x0700
TOY = 0x0800
HEALTH = 0x0900
UNCATEGORIZED = 0x1F00
def __init__(self):
super().__init__()
self._device_class = 0
self._major_class = 0
def getDeviceClass(self) -> int:
return self._device_class
def getMajorDeviceClass(self) -> int:
return self._major_class
def hasService(self, service: int) -> bool:
return False
def toString(self) -> str:
return f"BluetoothClass(device=0x{self._device_class:04X})"
# ═══════════════════════════════════════════════════════════════
# BluetoothSocket
# ═══════════════════════════════════════════════════════════════
class BluetoothSocket(Stub):
"""BluetoothSocket — 蓝牙套接字"""
TYPE_RFCOMM = 1
TYPE_L2CAP = 3
TYPE_SCO = 2
def __init__(self, type_: int = TYPE_RFCOMM):
super().__init__()
self._type = type_
self._closed = False
self._connected = False
self._device: Optional["BluetoothDevice"] = None
def connect(self):
self._connected = True
def bind(self):
pass
def getInputStream(self) -> Any:
return None
def getOutputStream(self) -> Any:
return None
def close(self):
self._closed = True
self._connected = False
def getRemoteDevice(self) -> Optional["BluetoothDevice"]:
return self._device
def getMaxReceivePacketSize(self) -> int:
return 0
def isConnected(self) -> bool:
return self._connected
def isClosed(self) -> bool:
return self._closed
class BluetoothServerSocket(Stub):
"""BluetoothServerSocket — 蓝牙服务端套接字"""
def __init__(self):
super().__init__()
self._closed = False
def accept(self, timeout: int = 0) -> Optional[BluetoothSocket]:
return None
def close(self):
self._closed = True
# ═══════════════════════════════════════════════════════════════
# BluetoothDevice
# ═══════════════════════════════════════════════════════════════
class BluetoothDevice(Stub):
"""BluetoothDevice — 蓝牙设备"""
BOND_NONE = 10
BOND_BONDING = 11
BOND_BONDED = 12
DEVICE_TYPE_DUAL = 3
DEVICE_TYPE_LE = 2
DEVICE_TYPE_BREDR = 1
DEVICE_TYPE_UNKNOWN = 0
ACTION_FOUND = "android.bluetooth.device.action.FOUND"
ACTION_NAME_CHANGED = "android.bluetooth.device.action.NAME_CHANGED"
ACTION_BOND_STATE_CHANGED = "android.bluetooth.device.action.BOND_STATE_CHANGED"
EXTRA_DEVICE = "android.bluetooth.device.extra.DEVICE"
EXTRA_NAME = "android.bluetooth.device.extra.NAME"
EXTRA_RSSI = "android.bluetooth.device.extra.RSSI"
EXTRA_BOND_STATE = "android.bluetooth.device.extra.BOND_STATE"
EXTRA_PREVIOUS_BOND_STATE = "android.bluetooth.device.extra.PREVIOUS_BOND_STATE"
def __init__(self, address: str = "00:00:00:00:00:00"):
super().__init__()
self._address = address
self._name = "Unknown"
self._bond_state = self.BOND_NONE
self._device_type = self.DEVICE_TYPE_UNKNOWN
def getAddress(self) -> str:
return self._address
def getName(self) -> str:
return self._name
def setName(self, name: str):
self._name = name
def getBondState(self) -> int:
return self._bond_state
def setBondState(self, state: int):
self._bond_state = state
def getType(self) -> int:
return self._device_type
def untergehende(self) -> bool:
return self._bond_state == self.BOND_BONDED
def createRfcommSocketToServiceRecord(self, uuid_str: str) -> BluetoothSocket:
sock = BluetoothSocket(BluetoothSocket.TYPE_RFCOMM)
sock._device = self
return sock
def createInsecureRfcommSocketToServiceRecord(self, uuid_str: str) -> BluetoothSocket:
sock = BluetoothSocket(BluetoothSocket.TYPE_RFCOMM)
sock._device = self
return sock
def createL2capChannel(self, psm: int) -> BluetoothSocket:
sock = BluetoothSocket(BluetoothSocket.TYPE_L2CAP)
sock._device = self
return sock
def createInsecureL2capChannel(self, psm: int) -> BluetoothSocket:
sock = BluetoothSocket(BluetoothSocket.TYPE_L2CAP)
sock._device = self
return sock
def fetchUuidsWithSdp(self) -> bool:
return False
def uuidEquals(self, uuid_str: str) -> bool:
return False
def setPairingConfirmation(self, accept: bool):
pass
def setPin(self, pin: bytes) -> bool:
return True
def toString(self) -> str:
return f"BluetoothDevice(address={self._address}, name={self._name})"
# ═══════════════════════════════════════════════════════════════
# BluetoothGattDescriptor
# ═══════════════════════════════════════════════════════════════
class BluetoothGattDescriptor(Stub):
"""BluetoothGattDescriptor — GATT 描述符"""
ENABLE_NOTIFICATION_VALUE = bytes([0x01, 0x00])
ENABLE_INDICATION_VALUE = bytes([0x02, 0x00])
DISABLE_NOTIFICATION_VALUE = bytes([0x00, 0x00])
def __init__(self, uuid_str: str, permissions: int = 0):
super().__init__()
self._uuid = uuid_str
self._value: Optional[bytes] = None
self._permissions = permissions
self._characteristic: Optional["BluetoothGattCharacteristic"] = None
def getUuid(self) -> str:
return self._uuid
def getValue(self) -> Optional[bytes]:
return self._value
def setValue(self, value: bytes):
self._value = value
def getCharacteristic(self) -> Optional["BluetoothGattCharacteristic"]:
return self._characteristic
def getPermissions(self) -> int:
return self._permissions
# ═══════════════════════════════════════════════════════════════
# BluetoothGattCharacteristic
# ═══════════════════════════════════════════════════════════════
class BluetoothGattCharacteristic(Stub):
"""BluetoothGattCharacteristic — GATT 特征"""
PROPERTY_BROADCAST = 1
PROPERTY_READ = 2
PROPERTY_WRITE_NO_RESPONSE = 4
PROPERTY_WRITE = 8
PROPERTY_NOTIFY = 16
PROPERTY_INDICATE = 32
PROPERTY_SIGNED_WRITE = 64
PROPERTY_EXTENDED_PROPS = 128
WRITE_TYPE_NO_RESPONSE = 1
WRITE_TYPE_DEFAULT = 2
WRITE_TYPE_SIGNED = 4
PERMISSION_READ = 1
PERMISSION_READ_ENCRYPTED = 2
PERMISSION_READ_ENCRYPTED_MITM = 4
PERMISSION_WRITE = 16
PERMISSION_WRITE_ENCRYPTED = 32
PERMISSION_WRITE_ENCRYPTED_MITM = 64
FORMAT_FLOAT = 0x20
FORMAT_SFLOAT = 0x10
FORMAT_SINT16 = 0x02
FORMAT_SINT32 = 0x04
FORMAT_SINT8 = 0x01
FORMAT_UINT16 = 0x12
FORMAT_UINT32 = 0x14
FORMAT_UINT8 = 0x11
def __init__(self, uuid_str: str, properties: int = 0, permissions: int = 0):
super().__init__()
self._uuid = uuid_str
self._properties = properties
self._permissions = permissions
self._write_type = self.WRITE_TYPE_DEFAULT
self._value: Optional[bytes] = None
self._service: Optional["BluetoothGattService"] = None
self._descriptors: List[BluetoothGattDescriptor] = []
def getUuid(self) -> str:
return self._uuid
def getService(self) -> Optional["BluetoothGattService"]:
return self._service
def getPermissions(self) -> int:
return self._permissions
def getProperties(self) -> int:
return self._properties
def getWriteType(self) -> int:
return self._write_type
def setWriteType(self, write_type: int):
self._write_type = write_type
def getValue(self) -> Optional[bytes]:
return self._value
def setValue(self, value: bytes):
self._value = value
def getDescriptors(self) -> List[BluetoothGattDescriptor]:
return list(self._descriptors)
def getDescriptor(self, uuid_str: str) -> Optional[BluetoothGattDescriptor]:
for d in self._descriptors:
if d.getUuid() == uuid_str:
return d
return None
def addDescriptor(self, descriptor: BluetoothGattDescriptor):
self._descriptors.append(descriptor)
descriptor._characteristic = self
def getIntValue(self, format_type: int, offset: int) -> Optional[int]:
if self._value is None:
return None
import struct
try:
fmt_map = {
self.FORMAT_UINT8: ("B", 1), self.FORMAT_SINT8: ("b", 1),
self.FORMAT_UINT16: ("H", 2), self.FORMAT_SINT16: ("h", 2),
self.FORMAT_UINT32: ("I", 4), self.FORMAT_SINT32: ("i", 4),
self.FORMAT_FLOAT: ("f", 4), self.FORMAT_SFLOAT: ("e", 2),
}
fmt, size = fmt_map.get(format_type, ("B", 1))
if offset + size > len(self._value):
return None
val = struct.unpack(fmt, self._value[offset:offset + size])[0]
return int(val)
except Exception:
return None
# ═══════════════════════════════════════════════════════════════
# BluetoothGattService
# ═══════════════════════════════════════════════════════════════
class BluetoothGattService(Stub):
"""BluetoothGattService — GATT 服务"""
SERVICE_TYPE_PRIMARY = 0
SERVICE_TYPE_SECONDARY = 1
def __init__(self, uuid_str: str, service_type: int = SERVICE_TYPE_PRIMARY):
super().__init__()
self._uuid = uuid_str
self._type = service_type
self._characteristics: List[BluetoothGattCharacteristic] = []
self._included_services: List["BluetoothGattService"] = []
self._device: Optional[BluetoothDevice] = None
def getUuid(self) -> str:
return self._uuid
def getType(self) -> int:
return self._type
def getCharacteristics(self) -> List[BluetoothGattCharacteristic]:
return list(self._characteristics)
def getCharacteristic(self, uuid_str: str) -> Optional[BluetoothGattCharacteristic]:
for c in self._characteristics:
if c.getUuid() == uuid_str:
return c
return None
def addCharacteristic(self, characteristic: BluetoothGattCharacteristic):
self._characteristics.append(characteristic)
characteristic._service = self
def getIncludedServices(self) -> List["BluetoothGattService"]:
return list(self._included_services)
def addIncludedService(self, service: "BluetoothGattService"):
self._included_services.append(service)
def getDevice(self) -> Optional[BluetoothDevice]:
return self._device
# ═══════════════════════════════════════════════════════════════
# BluetoothGattCallback
# ═══════════════════════════════════════════════════════════════
class BluetoothGattCallback(Stub):
"""BluetoothGattCallback — GATT 回调"""
def onConnectionStateChange(self, gatt: "BluetoothGatt", status: int, new_state: int):
pass
def onServicesDiscovered(self, gatt: "BluetoothGatt", status: int):
pass
def onCharacteristicRead(self, gatt: "BluetoothGatt",
characteristic: BluetoothGattCharacteristic, value: bytes, status: int):
pass
def onCharacteristicWrite(self, gatt: "BluetoothGatt",
characteristic: BluetoothGattCharacteristic, status: int):
pass
def onDescriptorRead(self, gatt: "BluetoothGatt",
descriptor: BluetoothGattDescriptor, status: int):
pass
def onDescriptorWrite(self, gatt: "BluetoothGatt",
descriptor: BluetoothGattDescriptor, status: int):
pass
def onCharacteristicChanged(self, gatt: "BluetoothGatt",
characteristic: BluetoothGattCharacteristic, value: bytes):
pass
def onReadRemoteRssy(self, gatt: "BluetoothGatt", rssi: int, status: int):
pass
def onMtuChanged(self, gatt: "BluetoothGatt", mtu: int, status: int):
pass
def onPhyUpdate(self, gatt: "BluetoothGatt", tx_phy: int, rx_phy: int, status: int):
pass
def onPhyRead(self, gatt: "BluetoothGatt", tx_phy: int, rx_phy: int, status: int):
pass
def onServiceChanged(self, gatt: "BluetoothGatt"):
pass
def onNotificationSent(self, gatt: "BluetoothGatt", status: int):
pass
def onConnectionUpdated(self, gatt: "BluetoothGatt", interval: int,
latency: int, timeout: int, status: int):
pass
# ═══════════════════════════════════════════════════════════════
# BluetoothGatt
# ═══════════════════════════════════════════════════════════════
class BluetoothGatt(Stub):
"""BluetoothGatt — GATT 客户端"""
CONNECTION_STATE_DISCONNECTED = 0
CONNECTION_STATE_CONNECTING = 1
CONNECTION_STATE_CONNECTED = 2
CONNECTION_STATE_DISCONNECTING = 3
GATT_SUCCESS = 0
GATT_FAILURE = 1
GATT_CONNECTION_CONGESTED = 143
GATT_INSUFFICIENT_AUTHENTICATION = 5
GATT_INSUFFICIENT_ENCRYPTION = 15
GATT_INSUFFICIENT_KEY_SIZE = 12
GATT_INVALID_ATTRIBUTE_LENGTH = 13
GATT_REQUEST_NOT_SUPPORTED = 6
GATT_WRITE_NOT_PERMITTED = 3
GATT_READ_NOT_PERMITTED = 2
GATT_WRITE_NO_RESPONSE = 1
GATT_WRITE_DEFAULT = 0
def __init__(self, device: BluetoothDevice):
super().__init__()
self._device = device
self._services: List[BluetoothGattService] = []
self._connected = False
self._mtu = 23
def connect(self) -> bool:
self._connected = True
return True
def disconnect(self):
self._connected = False
def close(self):
self._connected = False
self._services.clear()
def discoverServices(self) -> bool:
return True
def getServices(self) -> List[BluetoothGattService]:
return list(self._services)
def getService(self, uuid_str: str) -> Optional[BluetoothGattService]:
for s in self._services:
if s.getUuid() == uuid_str:
return s
return None
def getConnectedDevices(self) -> List[BluetoothDevice]:
return [self._device] if self._connected else []
def getDevicesMatchingConnectionState(self, state: int) -> List[BluetoothDevice]:
if state == self.CONNECTION_STATE_CONNECTED and self._connected:
return [self._device]
return []
def readCharacteristic(self, characteristic: BluetoothGattCharacteristic) -> bool:
return True
def writeCharacteristic(self, characteristic: BluetoothGattCharacteristic) -> bool:
return True
def readDescriptor(self, descriptor: BluetoothGattDescriptor) -> bool:
return True
def writeDescriptor(self, descriptor: BluetoothGattDescriptor) -> bool:
return True
def setCharacteristicNotification(self, characteristic: BluetoothGattCharacteristic,
enable: bool) -> bool:
return True
def readRemoteRssy(self) -> bool:
return True
def requestMtu(self, mtu: int) -> bool:
self._mtu = mtu
return True
def getMtu(self) -> int:
return self._mtu
def requestConnectionPriority(self, connection_priority: int) -> bool:
return True
def getDevice(self) -> BluetoothDevice:
return self._device
# ═══════════════════════════════════════════════════════════════
# BluetoothManager
# ═══════════════════════════════════════════════════════════════
class BluetoothManager(Stub):
"""BluetoothManager — 蓝牙管理器"""
def __init__(self, context: Any = None):
super().__init__()
self._context = context
def getAdapter(self) -> Optional["BluetoothAdapter"]:
return BluetoothAdapter.getDefaultAdapter()
# ═══════════════════════════════════════════════════════════════
# BluetoothAdapter
# ═══════════════════════════════════════════════════════════════
class BluetoothAdapter(Stub):
"""BluetoothAdapter — 蓝牙适配器"""
STATE_OFF = 10
STATE_TURNING_ON = 11
STATE_ON = 12
STATE_TURNING_OFF = 13
ACTION_REQUEST_ENABLE = "android.bluetooth.adapter.action.REQUEST_ENABLE"
ACTION_REQUEST_DISCOVERABLE = "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE"
ACTION_STATE_CHANGED = "android.bluetooth.adapter.action.STATE_CHANGED"
ACTION_DISCOVERY_STARTED = "android.bluetooth.adapter.action.DISCOVERY_STARTED"
ACTION_DISCOVERY_FINISHED = "android.bluetooth.adapter.action.DISCOVERY_FINISHED"
SCAN_MODE_NONE = 20
SCAN_MODE_CONNECTABLE = 21
SCAN_MODE_CONNECTABLE_DISCOVERABLE = 23
_default_adapter: Optional["BluetoothAdapter"] = None
def __init__(self):
super().__init__()
self._enabled = True
self._state = self.STATE_ON
self._address = "00:1A:2B:3C:4D:5E"
self._name = "MockBluetooth"
self._discovering = False
self._bonded_devices: List[BluetoothDevice] = []
@staticmethod
def getDefaultAdapter() -> Optional["BluetoothAdapter"]:
if BluetoothAdapter._default_adapter is None:
BluetoothAdapter._default_adapter = BluetoothAdapter()
return BluetoothAdapter._default_adapter
def isEnabled(self) -> bool:
return self._enabled
def enable(self) -> bool:
self._enabled = True
self._state = self.STATE_ON
return True
def disable(self) -> bool:
self._enabled = False
self._state = self.STATE_OFF
return True
def getAddress(self) -> str:
return self._address
def getName(self) -> str:
return self._name
def setName(self, name: str) -> bool:
self._name = name
return True
def getBondedDevices(self) -> Set[BluetoothDevice]:
return set(self._bonded_devices)
def startDiscovery(self) -> bool:
self._discovering = True
return True
def cancelDiscovery(self) -> bool:
self._discovering = False
return True
def isDiscovering(self) -> bool:
return self._discovering
@staticmethod
def checkBluetoothAddress(address: str) -> bool:
import re
return bool(re.match(r"^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$", address))
def getProfileProxy(self, context: Any, listener: BluetoothProfileServiceListener,
profile_id: int) -> bool:
return True
def closeProfileProxy(self, profile_id: int, proxy: Any):
pass
def getState(self) -> int:
return self._state
def listenUsingRfcommWithServiceRecord(self, name: str, uuid_str: str) -> BluetoothServerSocket:
return BluetoothServerSocket()
def listenUsingInsecureRfcommWithServiceRecord(self, name: str,
uuid_str: str) -> BluetoothServerSocket:
return BluetoothServerSocket()
def getRemoteDevice(self, address: str) -> Optional[BluetoothDevice]:
if not BluetoothAdapter.checkBluetoothAddress(address):
return None
return BluetoothDevice(address)
def getLeScanner(self) -> Any:
from .bluetooth_le import BluetoothLeScanner
return BluetoothLeScanner()
def toString(self) -> str:
return f"BluetoothAdapter(name={self._name})"
# ═══════════════════════════════════════════════════════════════
# Additional profile stubs
# ═══════════════════════════════════════════════════════════════
class BluetoothHeadset(Stub):
pass
class BluetoothA2dp(Stub):
pass
class BluetoothHealth(Stub):
pass
class BluetoothHealthCallback(Stub):
pass
class BluetoothHealthAppConfiguration(Stub):
pass
class BluetoothCodecConfig(Stub):
pass
class BluetoothLeAudio(Stub):
pass
class BluetoothLeBroadcast(Stub):
pass
class BluetoothLeBroadcastMetadata(Stub):
pass
class BluetoothLeBroadcastSubgroup(Stub):
pass
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"BluetoothAdapter",
"BluetoothDevice",
"BluetoothServerSocket",
"BluetoothSocket",
"BluetoothClass",
"BluetoothGatt",
"BluetoothGattCallback",
"BluetoothGattCharacteristic",
"BluetoothGattDescriptor",
"BluetoothGattService",
"BluetoothHeadset",
"BluetoothA2dp",
"BluetoothHealth",
"BluetoothHealthCallback",
"BluetoothHealthAppConfiguration",
"BluetoothProfile",
"BluetoothProfileServiceListener",
"BluetoothManager",
"BluetoothCodecConfig",
"BluetoothLeAudio",
"BluetoothLeBroadcast",
"BluetoothLeBroadcastMetadata",
"BluetoothLeBroadcastSubgroup",
]
+512
View File
@@ -0,0 +1,512 @@
"""
Android BLE 组件 — BluetoothLeScanner, ScanCallback, ScanFilter 等
方法签名完全对齐 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,
)
# ═══════════════════════════════════════════════════════════════
# ScanRecord
# ═══════════════════════════════════════════════════════════════
class ScanRecord(Stub):
"""ScanRecord — 扫描记录"""
def __init__(self):
super().__init__()
self._service_uuids: List[str] = []
self._service_solicitation_uuids: List[str] = []
self._service_data: Dict[str, bytes] = {}
self._manufacturer_specific_data: Dict[int, bytes] = {}
self._tx_power_level: int = -127
self._device_name: str = ""
self._advertise_flags: int = 0
self._bytes_data: bytes = b""
def getServiceUuids(self) -> Optional[List[str]]:
return list(self._service_uuids) if self._service_uuids else None
def getServiceSolicitationUuids(self) -> Optional[List[str]]:
return list(self._service_solicitation_uuids) if self._service_solicitation_uuids else None
def getServiceData(self) -> Optional[Dict[str, bytes]]:
return dict(self._service_data) if self._service_data else None
def getServiceDataForService(self, uuid_str: str) -> Optional[bytes]:
return self._service_data.get(uuid_str)
def getManufacturerSpecificData(self) -> Optional[Dict[int, bytes]]:
return dict(self._manufacturer_specific_data) if self._manufacturer_specific_data else None
def getTxPowerLevel(self) -> int:
return self._tx_power_level
def getDeviceName(self) -> Optional[str]:
return self._device_name or None
def getAdvertiseFlags(self) -> int:
return self._advertise_flags
def getBytes(self) -> bytes:
return self._bytes_data
# ═══════════════════════════════════════════════════════════════
# ScanFilter
# ═══════════════════════════════════════════════════════════════
class ScanFilter(Stub):
"""ScanFilter — 扫描过滤器"""
class Builder(Stub):
def __init__(self):
super().__init__()
self._device_name: Optional[str] = None
self._device_address: Optional[str] = None
self._service_uuid: Optional[str] = None
self._service_data: Optional[bytes] = None
self._manufacturer_id: int = -1
self._manufacturer_data: Optional[bytes] = None
def setDeviceName(self, name: str) -> "ScanFilter.Builder":
self._device_name = name
return self
def setDeviceAddress(self, address: str) -> "ScanFilter.Builder":
self._device_address = address
return self
def setServiceUuid(self, uuid_str: str) -> "ScanFilter.Builder":
self._service_uuid = uuid_str
return self
def setServiceData(self, data: bytes) -> "ScanFilter.Builder":
self._service_data = data
return self
def setManufacturerData(self, manufacturer_id: int, data: bytes) -> "ScanFilter.Builder":
self._manufacturer_id = manufacturer_id
self._manufacturer_data = data
return self
def build(self) -> "ScanFilter":
return ScanFilter(
device_name=self._device_name,
device_address=self._device_address,
service_uuid=self._service_uuid,
service_data=self._service_data,
manufacturer_id=self._manufacturer_id,
manufacturer_data=self._manufacturer_data,
)
def __init__(self, device_name: Optional[str] = None,
device_address: Optional[str] = None,
service_uuid: Optional[str] = None,
service_data: Optional[bytes] = None,
manufacturer_id: int = -1,
manufacturer_data: Optional[bytes] = None):
super().__init__()
self._device_name = device_name
self._device_address = device_address
self._service_uuid = service_uuid
self._service_data = service_data
self._manufacturer_id = manufacturer_id
self._manufacturer_data = manufacturer_data
class ScanFilterBuilder(ScanFilter.Builder):
"""ScanFilter.Builder 别名"""
pass
# ═══════════════════════════════════════════════════════════════
# ScanSettings
# ═══════════════════════════════════════════════════════════════
class ScanSettings(Stub):
"""ScanSettings — 扫描设置"""
SCAN_MODE_OPPORTUNISTIC = -1
SCAN_MODE_LOW_POWER = 0
SCAN_MODE_BALANCED = 1
SCAN_MODE_LOW_LATENCY = 2
CALLBACK_TYPE_ALL_MATCHES = 1
CALLBACK_TYPE_FIRST_MATCH = 2
CALLBACK_TYPE_MATCH_LOST = 4
MATCH_NUM_FEW_CFG = 2
MATCH_NUM_MAX_CFG = 1
MATCH_MODE_AGGRESSIVE = 0
MATCH_MODE_STICKY = 1
class Builder(Stub):
def __init__(self):
super().__init__()
self._scan_mode = ScanSettings.SCAN_MODE_LOW_POWER
self._callback_type = ScanSettings.CALLBACK_TYPE_ALL_MATCHES
self._legacy = True
self._phy = 1
def setScanMode(self, scan_mode: int) -> "ScanSettings.Builder":
self._scan_mode = scan_mode
return self
def setCallbackType(self, callback_type: int) -> "ScanSettings.Builder":
self._callback_type = callback_type
return self
def setLegacy(self, legacy: bool) -> "ScanSettings.Builder":
self._legacy = legacy
return self
def setPhy(self, phy: int) -> "ScanSettings.Builder":
self._phy = phy
return self
def build(self) -> "ScanSettings":
return ScanSettings(
scan_mode=self._scan_mode,
callback_type=self._callback_type,
legacy=self._legacy,
phy=self._phy,
)
def __init__(self, scan_mode: int = SCAN_MODE_LOW_POWER,
callback_type: int = CALLBACK_TYPE_ALL_MATCHES,
legacy: bool = True, phy: int = 1):
super().__init__()
self._scan_mode = scan_mode
self._callback_type = callback_type
self._legacy = legacy
self._phy = phy
def getScanMode(self) -> int:
return self._scan_mode
def getCallbackType(self) -> int:
return self._callback_type
def getLegacy(self) -> bool:
return self._legacy
def getPhy(self) -> int:
return self._phy
# ═══════════════════════════════════════════════════════════════
# ScanResult
# ═══════════════════════════════════════════════════════════════
class ScanResult(Stub):
"""ScanResult — BLE 扫描结果"""
DATA_COMPLETE = 0
DATA_TRUNCATED = 2
PHY_LE_1M = 1
PHY_LE_2M = 2
PHY_LE_CODED = 3
PERIODIC_INTERVAL_NOT_PRESENT = 0
def __init__(self, device: Any = None, scan_record: Optional[ScanRecord] = None,
rssi: int = -127, timestamp_nanos: int = 0):
super().__init__()
self._device = device
self._scan_record = scan_record or ScanRecord()
self._rssi = rssi
self._timestamp_nanos = timestamp_nanos
self._primary_phy = self.PHY_LE_1M
self._secondary_phy = 0
self._advertising_sid = -1
self._tx_power = -127
self._periodic_interval = self.PERIODIC_INTERVAL_NOT_PRESENT
self._data_status = self.DATA_COMPLETE
def getDevice(self) -> Any:
return self._device
def getScanRecord(self) -> Optional[ScanRecord]:
return self._scan_record
def getRssi(self) -> int:
return self._rssi
def getTimestampNanos(self) -> int:
return self._timestamp_nanos
def getPrimaryPhy(self) -> int:
return self._primary_phy
def getSecondaryPhy(self) -> int:
return self._secondary_phy
def getAdvertisingSid(self) -> int:
return self._advertising_sid
def getTxPower(self) -> int:
return self._tx_power
def getPeriodicAdvertisingInterval(self) -> int:
return self._periodic_interval
def getDataStatus(self) -> int:
return self._data_status
# ═══════════════════════════════════════════════════════════════
# ScanCallback
# ═══════════════════════════════════════════════════════════════
class ScanCallback(Stub):
"""ScanCallback — 扫描回调"""
SCAN_FAILED_ALREADY_STARTED = 1
SCAN_FAILED_APPLICATION_REGISTRATION_FAILED = 2
SCAN_FAILED_INTERNAL_ERROR = 3
SCAN_FAILED_FEATURE_UNSUPPORTED = 4
SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES = 5
SCAN_FAILED_SCANNING_TOO_FREQUENTLY = 6
def onScanResult(self, callback_type: int, result: ScanResult):
pass
def onBatchScanResults(self, results: List[ScanResult]):
pass
def onScanFailed(self, error_code: int):
pass
# ═══════════════════════════════════════════════════════════════
# BluetoothLeScanner
# ═══════════════════════════════════════════════════════════════
class BluetoothLeScanner(Stub):
"""BluetoothLeScanner — BLE 扫描器"""
def __init__(self):
super().__init__()
def startScan(self, filters: Optional[List[ScanFilter]] = None,
settings: Optional[ScanSettings] = None,
callback: Optional[ScanCallback] = None):
pass
def startScanWithFilters(self, filters: List[ScanFilter],
callback: ScanCallback):
self.startScan(filters, None, callback)
def stopScan(self, callback: Optional[ScanCallback] = None):
pass
def flushPendingScanResults(self, callback: ScanCallback):
pass
# ═══════════════════════════════════════════════════════════════
# AdvertiseData / AdvertiseSettings / AdvertiseCallback
# ═══════════════════════════════════════════════════════════════
class AdvertiseData(Stub):
"""AdvertiseData — 广播数据"""
class Builder(Stub):
def __init__(self):
super().__init__()
self._include_device_name = False
self._include_tx_power_level = False
self._service_uuids: List[str] = []
self._service_data: Dict[str, bytes] = {}
self._manufacturer_data: Dict[int, bytes] = {}
def setIncludeDeviceName(self, include: bool) -> "AdvertiseData.Builder":
self._include_device_name = include
return self
def setIncludeTxPowerLevel(self, include: bool) -> "AdvertiseData.Builder":
self._include_tx_power_level = include
return self
def addServiceUuid(self, uuid_str: str) -> "AdvertiseData.Builder":
self._service_uuids.append(uuid_str)
return self
def addServiceData(self, uuid_str: str, data: bytes) -> "AdvertiseData.Builder":
self._service_data[uuid_str] = data
return self
def addManufacturerData(self, manufacturer_id: int, data: bytes) -> "AdvertiseData.Builder":
self._manufacturer_data[manufacturer_id] = data
return self
def build(self) -> "AdvertiseData":
return AdvertiseData(
include_device_name=self._include_device_name,
include_tx_power_level=self._include_tx_power_level,
service_uuids=self._service_uuids,
service_data=self._service_data,
manufacturer_data=self._manufacturer_data,
)
def __init__(self, include_device_name: bool = False,
include_tx_power_level: bool = False,
service_uuids: Optional[List[str]] = None,
service_data: Optional[Dict[str, bytes]] = None,
manufacturer_data: Optional[Dict[int, bytes]] = None):
super().__init__()
self._include_device_name = include_device_name
self._include_tx_power_level = include_tx_power_level
self._service_uuids = service_uuids or []
self._service_data = service_data or {}
self._manufacturer_data = manufacturer_data or {}
class AdvertiseDataBuilder(AdvertiseData.Builder):
pass
class AdvertiseSettings(Stub):
"""AdvertiseSettings — 广播设置"""
ADVERTISE_MODE_LOW_POWER = 0
ADVERTISE_MODE_BALANCED = 1
ADVERTISE_MODE_LOW_LATENCY = 2
ADVERTISE_TX_POWER_ULTRA_LOW = 0
ADVERTISE_TX_POWER_LOW = 1
ADVERTISE_TX_POWER_MEDIUM = 2
ADVERTISE_TX_POWER_HIGH = 3
class Builder(Stub):
def __init__(self):
super().__init__()
self._advertise_mode = AdvertiseSettings.ADVERTISE_MODE_LOW_POWER
self._tx_power_level = AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM
self._connectable = True
self._timeout = 0
def setAdvertiseMode(self, mode: int) -> "AdvertiseSettings.Builder":
self._advertise_mode = mode
return self
def setTxPowerLevel(self, power: int) -> "AdvertiseSettings.Builder":
self._tx_power_level = power
return self
def setConnectable(self, connectable: bool) -> "AdvertiseSettings.Builder":
self._connectable = connectable
return self
def setTimeout(self, timeout: int) -> "AdvertiseSettings.Builder":
self._timeout = timeout
return self
def build(self) -> "AdvertiseSettings":
return AdvertiseSettings(
advertise_mode=self._advertise_mode,
tx_power_level=self._tx_power_level,
connectable=self._connectable,
timeout=self._timeout,
)
def __init__(self, advertise_mode: int = ADVERTISE_MODE_LOW_POWER,
tx_power_level: int = ADVERTISE_TX_POWER_MEDIUM,
connectable: bool = True, timeout: int = 0):
super().__init__()
self._advertise_mode = advertise_mode
self._tx_power_level = tx_power_level
self._connectable = connectable
self._timeout = timeout
def getAdvertiseMode(self) -> int:
return self._advertise_mode
def getTxPowerLevel(self) -> int:
return self._tx_power_level
def isConnectable(self) -> bool:
return self._connectable
def getTimeout(self) -> int:
return self._timeout
class AdvertiseCallback(Stub):
"""AdvertiseCallback — 广播回调"""
ADVERTISE_FAILED_DATA_TOO_LARGE = 1
ADVERTISE_FAILED_TOO_MANY_ADVERTISERS = 2
ADVERTISE_FAILED_ALREADY_STARTED = 3
ADVERTISE_FAILED_INTERNAL_ERROR = 4
ADVERTISE_FAILED_FEATURE_UNSUPPORTED = 5
def onStartSuccess(self, settings_in_effect: AdvertiseSettings):
pass
def onStartFailure(self, error_code: int):
pass
class BluetoothLeAdvertiser(Stub):
"""BluetoothLeAdvertiser — BLE 广播器"""
def startAdvertising(self, settings: AdvertiseSettings,
advertise_data: AdvertiseData,
scan_response: Optional[AdvertiseData],
callback: AdvertiseCallback):
try:
callback.onStartSuccess(settings)
except Exception:
pass
def stopAdvertising(self, callback: AdvertiseCallback):
pass
class PeriodicAdvertisingManager(Stub):
pass
class PeriodicAdvertisingCallback(Stub):
pass
class DistanceMeasurementManager(Stub):
pass
class DistanceMeasurementResult(Stub):
pass
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"BluetoothLeScanner",
"ScanCallback",
"ScanResult",
"ScanFilter",
"ScanFilterBuilder",
"ScanSettings",
"ScanRecord",
"BluetoothLeAdvertiser",
"AdvertiseCallback",
"AdvertiseData",
"AdvertiseDataBuilder",
"AdvertiseSettings",
"PeriodicAdvertisingManager",
"PeriodicAdvertisingCallback",
"DistanceMeasurementManager",
"DistanceMeasurementResult",
]
File diff suppressed because it is too large. Load diff
+801
View File
@@ -0,0 +1,801 @@
"""
Android PackageManager 组件 — PackageManager, PackageInfo, ApplicationInfo 等
方法签名完全对齐 Android SDK
"""
import json
import os
import sys
from typing import Any, Dict, List, Optional, Set
from .._core import (
Stub, AndroidOnlyError, android_context,
resolve_android_path, to_android_path,
_sdcard_root, _app_data_dir, _package_name,
)
# ═══════════════════════════════════════════════════════════════
# 数据存储路径
# ═══════════════════════════════════════════════════════════════
_PACKAGE_DATA_DIR = os.path.join(_app_data_dir, "package_data")
os.makedirs(_PACKAGE_DATA_DIR, exist_ok=True)
_DEFAULT_PACKAGE_INFO_PATH = os.path.join(_PACKAGE_DATA_DIR, f"{_package_name}.json")
def _get_default_package_info() -> Dict[str, Any]:
"""获取默认包信息"""
return {
"packageName": _package_name,
"versionName": "1.0.0",
"versionCode": 1,
"applicationInfo": {
"name": _package_name,
"packageName": _package_name,
"className": f"{_package_name}.MainApplication",
"icon": 0,
"targetSdkVersion": 34,
"sourceDir": os.path.join(_app_data_dir, "base.apk"),
"dataDir": _app_data_dir,
"enabled": True,
"flags": 0,
"theme": 0,
"labelRes": 0,
"descriptionRes": 0,
"logo": 0,
"banner": 0,
"processName": _package_name,
"uid": 10001,
"manageSpaceActivityName": "",
"taskAffinity": "",
"permission": "",
},
"activities": [],
"services": [],
"receivers": [],
"providers": [],
"permissions": [],
"requestedPermissions": [
"android.permission.INTERNET",
"android.permission.ACCESS_NETWORK_STATE",
],
"features": {
"android.hardware.camera": True,
"android.hardware.wifi": True,
"android.hardware.bluetooth": True,
"android.hardware.location": True,
"android.hardware.sensor.accelerometer": True,
"android.hardware.sensor.gyroscope": True,
"android.hardware.touchscreen": True,
"android.hardware.microphone": True,
"android.hardware.nfc": False,
"android.hardware.telephony": True,
"android.software.picture_in_picture": True,
"android.hardware.usb.host": False,
}
}
# ═══════════════════════════════════════════════════════════════
# PackageItemInfo (基类)
# ═══════════════════════════════════════════════════════════════
class PackageItemInfo(Stub):
"""PackageItemInfo — 包条目信息基类"""
def __init__(self, data: Optional[Dict[str, Any]] = None):
super().__init__()
data = data or {}
self.name = data.get("name", "")
self.packageName = data.get("packageName", "")
self.labelRes = data.get("labelRes", 0)
self.nonLocalizedLabel = data.get("nonLocalizedLabel", None)
self.icon = data.get("icon", 0)
self.banner = data.get("banner", 0)
self.logo = data.get("logo", 0)
def loadLabel(self, pm: "PackageManager") -> str:
return self.nonLocalizedLabel or self.name.split(".")[-1] if self.name else ""
def loadIcon(self, pm: "PackageManager") -> Any:
return None
def loadLogo(self, pm: "PackageManager") -> Any:
return None
def loadBanner(self, pm: "PackageManager") -> Any:
return None
def toString(self) -> str:
return f"{self.__class__.__name__}({self.packageName}:{self.name})"
class ComponentInfo(PackageItemInfo):
"""ComponentInfo — 组件信息基类"""
def __init__(self, data: Optional[Dict[str, Any]] = None):
super().__init__(data)
data = data or {}
self.processName = data.get("processName", self.packageName)
self.splitName = data.get("splitName", None)
self.enabled = data.get("enabled", True)
self.exported = data.get("exported", False)
self.directBootAware = data.get("directBootAware", False)
def getComponentName(self) -> Any:
from .content import ComponentName
return ComponentName(self.packageName, self.name)
def isEnabled(self) -> bool:
return self.enabled
# ═══════════════════════════════════════════════════════════════
# ApplicationInfo
# ═══════════════════════════════════════════════════════════════
class ApplicationInfo(ComponentInfo):
"""ApplicationInfo — 应用信息"""
FLAG_SYSTEM = 1
FLAG_DEBUGGABLE = 2
FLAG_HAS_CODE = 4
FLAG_ALLOW_CLEAR_USER_DATA = 0x00400000
FLAG_ALLOW_BACKUP = 0x00008000
FLAG_EXTERNAL_STORAGE = 0x00100000
FLAG_LARGE_HEAP = 0x00100000
FLAG_MULTIPROCESS = 0x00010000
FLAG_TEST_ONLY = 0x00000100
FLAG_STOPPED = 0x00200000
FLAG_RESTART_ON_UPGRADE = 0x00000020
def __init__(self, data: Optional[Dict[str, Any]] = None):
super().__init__(data)
data = data or {}
self.taskAffinity = data.get("taskAffinity", "")
self.permission = data.get("permission", "")
self.processName = data.get("processName", self.packageName)
self.className = data.get("className", "")
self.descriptionRes = data.get("descriptionRes", 0)
self.theme = data.get("theme", 0)
self.flags = data.get("flags", 0)
self.sourceDir = data.get("sourceDir", "")
self.publicSourceDir = data.get("publicSourceDir", self.sourceDir)
self.dataDir = data.get("dataDir", "")
self.nativeLibraryDir = data.get("nativeLibraryDir", "")
self.metaData = data.get("metaData", None)
self.manageSpaceActivityName = data.get("manageSpaceActivityName", "")
self.targetSdkVersion = data.get("targetSdkVersion", 34)
self.uid = data.get("uid", 10001)
self.backupAgentName = data.get("backupAgentName", "")
self.enabled = data.get("enabled", True)
def loadDescription(self, pm: "PackageManager") -> Optional[str]:
return None
def toString(self) -> str:
return f"ApplicationInfo({self.packageName}, targetSdk={self.targetSdkVersion})"
# ═══════════════════════════════════════════════════════════════
# ActivityInfo
# ═══════════════════════════════════════════════════════════════
class ActivityInfo(ComponentInfo):
"""ActivityInfo — Activity 组件信息"""
CONFIG_MCC = 0x0001
CONFIG_MNC = 0x0002
CONFIG_LOCALE = 0x0004
CONFIG_TOUCHSCREEN = 0x0008
CONFIG_KEYBOARD = 0x0010
CONFIG_KEYBOARD_HIDDEN = 0x0020
CONFIG_NAVIGATION = 0x0040
CONFIG_ORIENTATION = 0x0080
CONFIG_SCREEN_LAYOUT = 0x0100
CONFIG_UI_MODE = 0x0200
CONFIG_SCREEN_SIZE = 0x0400
CONFIG_SMALLEST_SCREEN_SIZE = 0x0800
CONFIG_DENSITY = 0x1000
CONFIG_LAYOUT_DIRECTION = 0x2000
CONFIG_COLOR_MODE = 0x4000
LAUNCH_MULTIPLE = 0
LAUNCH_SINGLE_TOP = 1
LAUNCH_SINGLE_TASK = 2
LAUNCH_SINGLE_INSTANCE = 3
LAUNCH_SINGLE_INSTANCE_PER_TASK = 4
SCREEN_ORIENTATION_UNSPECIFIED = -1
SCREEN_ORIENTATION_LANDSCAPE = 0
SCREEN_ORIENTATION_PORTRAIT = 1
SCREEN_ORIENTATION_USER = 2
SCREEN_ORIENTATION_BEHIND = 3
SCREEN_ORIENTATION_SENSOR = 4
SCREEN_ORIENTATION_NOSENSOR = 5
SCREEN_ORIENTATION_SENSOR_LANDSCAPE = 6
SCREEN_ORIENTATION_SENSOR_PORTRAIT = 7
SCREEN_ORIENTATION_REVERSE_LANDSCAPE = 8
SCREEN_ORIENTATION_REVERSE_PORTRAIT = 9
SCREEN_ORIENTATION_FULL_SENSOR = 10
SCREEN_ORIENTATION_USER_LANDSCAPE = 11
SCREEN_ORIENTATION_USER_PORTRAIT = 12
SCREEN_ORIENTATION_FULL_USER = 13
SCREEN_ORIENTATION_LOCKED = 14
def __init__(self, data: Optional[Dict[str, Any]] = None):
super().__init__(data)
data = data or {}
self.launchMode = data.get("launchMode", self.LAUNCH_MULTIPLE)
self.screenOrientation = data.get("screenOrientation", self.SCREEN_ORIENTATION_UNSPECIFIED)
self.configChanges = data.get("configChanges", 0)
self.softInputMode = data.get("softInputMode", 0)
self.theme = data.get("theme", 0)
self.parentActivityName = data.get("parentActivityName", "")
self.permission = data.get("permission", "")
self.taskAffinity = data.get("taskAffinity", "")
self.targetActivity = data.get("targetActivity", "")
def toString(self) -> str:
return f"ActivityInfo({self.name}, launchMode={self.launchMode})"
# ═══════════════════════════════════════════════════════════════
# ServiceInfo
# ═══════════════════════════════════════════════════════════════
class ServiceInfo(ComponentInfo):
"""ServiceInfo — Service 组件信息"""
FLAG_STOP_WITH_TASK = 0x01
FLAG_ISOLATED_PROCESS = 0x02
FLAG_EXTERNAL_SERVICE = 0x04
FLAG_SINGLE_USER = 0x40000000
FLAG_USE_APP_OP = 0x08
def __init__(self, data: Optional[Dict[str, Any]] = None):
super().__init__(data)
data = data or {}
self.flags = data.get("flags", 0)
self.permission = data.get("permission", "")
self.foregroundServiceType = data.get("foregroundServiceType", 0)
def toString(self) -> str:
return f"ServiceInfo({self.name})"
# ═══════════════════════════════════════════════════════════════
# ProviderInfo
# ═══════════════════════════════════════════════════════════════
class ProviderInfo(ComponentInfo):
"""ProviderInfo — ContentProvider 组件信息"""
def __init__(self, data: Optional[Dict[str, Any]] = None):
super().__init__(data)
data = data or {}
self.authority = data.get("authority", "")
self.readPermission = data.get("readPermission", "")
self.writePermission = data.get("writePermission", "")
self.grantUriPermissions = data.get("grantUriPermissions", False)
self.multiprocess = data.get("multiprocess", False)
self.initOrder = data.get("initOrder", 0)
self.flags = data.get("flags", 0)
def toString(self) -> str:
return f"ProviderInfo({self.name}, authority={self.authority})"
# ═══════════════════════════════════════════════════════════════
# PermissionInfo
# ═══════════════════════════════════════════════════════════════
class PermissionInfo(PackageItemInfo):
"""PermissionInfo — 权限信息"""
PROTECTION_NORMAL = 0
PROTECTION_DANGEROUS = 1
PROTECTION_SIGNATURE = 2
PROTECTION_SIGNATURE_OR_SYSTEM = 3
PROTECTION_INTERNAL = 4
FLAG_COSTS_MONEY = 0x01
FLAG_HARDWARE_RESTRICTED = 0x04
FLAG_IMMUTABLY_RESTRICTED = 0x08
FLAG_INSTALLED = 0x10
def __init__(self, data: Optional[Dict[str, Any]] = None):
super().__init__(data)
data = data or {}
self.group = data.get("group", "")
self.descriptionRes = data.get("descriptionRes", 0)
self.protectionLevel = data.get("protectionLevel", self.PROTECTION_NORMAL)
self.flags = data.get("flags", 0)
def loadDescription(self, pm: "PackageManager") -> Optional[str]:
return None
def toString(self) -> str:
return f"PermissionInfo({self.name}, protectionLevel={self.protectionLevel})"
# ═══════════════════════════════════════════════════════════════
# ResolveInfo
# ═══════════════════════════════════════════════════════════════
class ResolveInfo(Stub):
"""ResolveInfo — 解析意图的结果"""
def __init__(self):
super().__init__()
self.activityInfo: Optional[ActivityInfo] = None
self.serviceInfo: Optional[ServiceInfo] = None
self.providerInfo: Optional[ProviderInfo] = None
self.filter: Optional[Any] = None
self.priority = 0
self.match = 0
self.preferredOrder = 0
self.isDefault = False
self.labelRes = 0
self.nonLocalizedLabel = None
self.icon = 0
self.resolvePackageName = ""
self.specificIndex = -1
self.system = False
def loadLabel(self, pm: "PackageManager") -> str:
if self.activityInfo:
return self.activityInfo.name.split(".")[-1]
if self.serviceInfo:
return self.serviceInfo.name.split(".")[-1]
return ""
def loadIcon(self, pm: "PackageManager"):
return None
def getIconResource(self) -> int:
return self.icon
def toString(self) -> str:
return f"ResolveInfo(priority={self.priority})"
# ═══════════════════════════════════════════════════════════════
# PackageInfo
# ═══════════════════════════════════════════════════════════════
class PackageInfo(Stub):
"""PackageInfo — 包信息"""
INSTALL_LOCATION_AUTO = 0
INSTALL_LOCATION_INTERNAL_ONLY = 1
INSTALL_LOCATION_PREFER_EXTERNAL = 2
REQUESTED_PERMISSION_REQUIRED = 0x00100000
REQUESTED_PERMISSION_GRANTED = 0x01000000
REQUESTED_PERMISSION_DENIED = 0x02000000
def __init__(self):
super().__init__()
self.packageName = ""
self.versionName = ""
self.versionCode = 0
self.applicationInfo: Optional[ApplicationInfo] = None
self.activities: List[ActivityInfo] = []
self.services: List[ServiceInfo] = []
self.receivers: List[ActivityInfo] = []
self.providers: List[ProviderInfo] = []
self.permissions: List[PermissionInfo] = []
self.requestedPermissions: List[str] = []
self.requestedPermissionsFlags: List[int] = []
self.sharedUserId = ""
self.sharedUserLabel = 0
self.firstInstallTime = 0
self.lastUpdateTime = 0
self.gids: List[int] = []
self.installLocation = self.INSTALL_LOCATION_AUTO
self.configPreferences: List = []
self.featureGroups: List = []
self.packageName_ = ""
def toString(self) -> str:
return (f"PackageInfo({self.packageName} v{self.versionName} "
f"code={self.versionCode})")
# ═══════════════════════════════════════════════════════════════
# Signature
# ═══════════════════════════════════════════════════════════════
class Signature(Stub):
"""Signature — APK 签名"""
def __init__(self, signature: str = ""):
super().__init__()
self._signature = signature
def toCharsString(self) -> str:
return self._signature
def toByteArray(self) -> bytes:
return self._signature.encode("utf-8") if self._signature else b""
def equals(self, other) -> bool:
if isinstance(other, Signature):
return self._signature == other._signature
return False
def hashCode(self) -> int:
return hash(self._signature) & 0x7FFFFFFF
def toString(self) -> str:
return self._signature[:64] if len(self._signature) > 64 else self._signature
# ═══════════════════════════════════════════════════════════════
# PackageStats
# ═══════════════════════════════════════════════════════════════
class PackageStats(Stub):
"""PackageStats — 包存储统计"""
def __init__(self, package_name: str = ""):
super().__init__()
self.packageName = package_name
self.codeSize = 0
self.dataSize = 0
self.cacheSize = 0
self.externalCodeSize = 0
self.externalDataSize = 0
self.externalCacheSize = 0
self.externalMediaSize = 0
self.externalObbSize = 0
def toString(self) -> str:
return (f"PackageStats({self.packageName}, "
f"code={self.codeSize}, data={self.dataSize})")
# ═══════════════════════════════════════════════════════════════
# PackageManager
# ═══════════════════════════════════════════════════════════════
class PackageManager(Stub):
"""PackageManager — 包管理器"""
# Flag 常量
GET_ACTIVITIES = 1
GET_CONFIGURATIONS = 2
GET_GIDS = 4
GET_INSTRUMENTATION = 8
GET_INTENT_FILTERS = 16
GET_META_DATA = 128
GET_PERMISSIONS = 4096
GET_PROVIDERS = 8
GET_RECEIVERS = 4
GET_SERVICES = 2
GET_SHARED_LIBRARY_FILES = 32
GET_SIGNATURES = 64
GET_UNINSTALLED_PACKAGES = 512
GET_URI_PERMISSION_PATTERNS = 256
MATCH_ALL = 0x10000
MATCH_DISABLED_COMPONENTS = 0x200
MATCH_DISABLED_UNTIL_USED_COMPONENTS = 0x8000
MATCH_DEFAULT_ONLY = 0x10000
MATCH_SYSTEM_ONLY = 0x800000
MATCH_UNINSTALLED_PACKAGES = 0x2000000
PERMISSION_DENIED = -1
PERMISSION_GRANTED = 0
# 特性常量
FEATURE_CAMERA = "android.hardware.camera"
FEATURE_CAMERA_AUTOFOCUS = "android.hardware.camera.autofocus"
FEATURE_CAMERA_FLASH = "android.hardware.camera.flash"
FEATURE_CAMERA_FRONT = "android.hardware.camera.front"
FEATURE_LOCATION = "android.hardware.location"
FEATURE_LOCATION_GPS = "android.hardware.location.gps"
FEATURE_LOCATION_NETWORK = "android.hardware.location.network"
FEATURE_MICROPHONE = "android.hardware.microphone"
FEATURE_NFC = "android.hardware.nfc"
FEATURE_SENSOR_ACCELEROMETER = "android.hardware.sensor.accelerometer"
FEATURE_SENSOR_BAROMETER = "android.hardware.sensor.barometer"
FEATURE_SENSOR_COMPASS = "android.hardware.sensor.compass"
FEATURE_SENSOR_GYROSCOPE = "android.hardware.sensor.gyroscope"
FEATURE_SENSOR_LIGHT = "android.hardware.sensor.light"
FEATURE_SENSOR_PROXIMITY = "android.hardware.sensor.proximity"
FEATURE_TELEPHONY = "android.hardware.telephony"
FEATURE_TOUCHSCREEN = "android.hardware.touchscreen"
FEATURE_USB_HOST = "android.hardware.usb.host"
FEATURE_WIFI = "android.hardware.wifi"
FEATURE_BLUETOOTH = "android.hardware.bluetooth"
def __init__(self):
super().__init__()
self._packages: Dict[str, Dict[str, Any]] = {}
self._load_packages()
def _load_packages(self):
"""从 JSON 文件加载包信息"""
try:
if os.path.exists(_DEFAULT_PACKAGE_INFO_PATH):
with open(_DEFAULT_PACKAGE_INFO_PATH, "r", encoding="utf-8") as f:
data = json.load(f)
if data.get("packageName"):
self._packages[data["packageName"]] = data
else:
# 创建默认信息
default_info = _get_default_package_info()
self._packages[_package_name] = default_info
os.makedirs(os.path.dirname(_DEFAULT_PACKAGE_INFO_PATH), exist_ok=True)
with open(_DEFAULT_PACKAGE_INFO_PATH, "w", encoding="utf-8") as f:
json.dump(default_info, f, indent=2, ensure_ascii=False)
except (json.JSONDecodeError, OSError):
self._packages[_package_name] = _get_default_package_info()
def _make_app_info(self, data: Dict) -> ApplicationInfo:
ai_data = data.get("applicationInfo", data)
return ApplicationInfo(ai_data)
def _make_activity_info(self, data: Dict) -> ActivityInfo:
return ActivityInfo(data)
def _make_service_info(self, data: Dict) -> ServiceInfo:
return ServiceInfo(data)
def _make_provider_info(self, data: Dict) -> ProviderInfo:
return ProviderInfo(data)
def getPackageInfo(self, package_name: str, flags: int) -> Optional[PackageInfo]:
if package_name not in self._packages:
return None
raw = self._packages[package_name]
pi = PackageInfo()
pi.packageName = raw.get("packageName", package_name)
pi.versionName = raw.get("versionName", "1.0.0")
pi.versionCode = raw.get("versionCode", 1)
pi.applicationInfo = self._make_app_info(raw)
if flags & self.GET_ACTIVITIES:
pi.activities = [self._make_activity_info(a) for a in raw.get("activities", [])]
if flags & self.GET_SERVICES:
pi.services = [self._make_service_info(s) for s in raw.get("services", [])]
if flags & self.GET_RECEIVERS:
pi.receivers = [self._make_activity_info(r) for r in raw.get("receivers", [])]
if flags & self.GET_PROVIDERS:
pi.providers = [self._make_provider_info(p) for p in raw.get("providers", [])]
if flags & self.GET_PERMISSIONS:
pi.permissions = [PermissionInfo(p) for p in raw.get("permissions", [])]
pi.requestedPermissions = raw.get("requestedPermissions", [])
return pi
def getApplicationInfo(self, package_name: str, flags: int) -> Optional[ApplicationInfo]:
if package_name not in self._packages:
return None
return self._make_app_info(self._packages[package_name])
def getActivityInfo(self, component: Any, flags: int) -> Optional[ActivityInfo]:
cn = component
pkg = getattr(cn, "getPackageName", lambda: "")()
cls = getattr(cn, "getClassName", lambda: "")()
if pkg in self._packages:
for a in self._packages[pkg].get("activities", []):
if a.get("name") == cls:
return ActivityInfo(a)
return ActivityInfo({"name": cls, "packageName": pkg})
def getServiceInfo(self, component: Any, flags: int) -> Optional[ServiceInfo]:
cn = component
pkg = getattr(cn, "getPackageName", lambda: "")()
cls = getattr(cn, "getClassName", lambda: "")()
if pkg in self._packages:
for s in self._packages[pkg].get("services", []):
if s.get("name") == cls:
return ServiceInfo(s)
return ServiceInfo({"name": cls, "packageName": pkg})
def getInstalledPackages(self, flags: int) -> List[PackageInfo]:
result = []
for pkg_name in self._packages:
pi = self.getPackageInfo(pkg_name, flags)
if pi:
result.append(pi)
return result
def getInstalledApplications(self, flags: int) -> List[ApplicationInfo]:
result = []
for pkg_name in self._packages:
ai = self.getApplicationInfo(pkg_name, flags)
if ai:
result.append(ai)
return result
def queryIntentActivities(self, intent: Any, flags: int) -> List[ResolveInfo]:
"""查询匹配 Intent 的 Activity 列表"""
results = []
action = getattr(intent, "getAction", lambda: None)()
for pkg_name, raw in self._packages.items():
for a in raw.get("activities", []):
ri = ResolveInfo()
ri.activityInfo = ActivityInfo(a)
ri.priority = 0
ri.match = 0
results.append(ri)
return results
def queryBroadcastReceivers(self, intent: Any, flags: int) -> List[ResolveInfo]:
results = []
for pkg_name, raw in self._packages.items():
for r in raw.get("receivers", []):
ri = ResolveInfo()
ri.activityInfo = ActivityInfo(r)
results.append(ri)
return results
def queryIntentServices(self, intent: Any, flags: int) -> List[ResolveInfo]:
results = []
for pkg_name, raw in self._packages.items():
for s in raw.get("services", []):
ri = ResolveInfo()
ri.serviceInfo = ServiceInfo(s)
results.append(ri)
return results
def queryIntentContentProviders(self, intent: Any, flags: int) -> List[ResolveInfo]:
results = []
for pkg_name, raw in self._packages.items():
for p in raw.get("providers", []):
ri = ResolveInfo()
ri.providerInfo = ProviderInfo(p)
results.append(ri)
return results
def resolveActivity(self, intent: Any, flags: int) -> Optional[ResolveInfo]:
activities = self.queryIntentActivities(intent, flags)
return activities[0] if activities else None
def resolveService(self, intent: Any, flags: int) -> Optional[ResolveInfo]:
services = self.queryIntentServices(intent, flags)
return services[0] if services else None
def checkPermission(self, permission: str, package_name: str) -> int:
if package_name in self._packages:
perms = self._packages[package_name].get("requestedPermissions", [])
if permission in perms:
return self.PERMISSION_GRANTED
return self.PERMISSION_DENIED if permission else self.PERMISSION_GRANTED
def getPermissionInfo(self, name: str, flags: int) -> Optional[PermissionInfo]:
return PermissionInfo({"name": name, "packageName": "android"})
def getDefaultActivityIcon(self) -> Any:
return None
def getApplicationLabel(self, info: ApplicationInfo) -> str:
return info.name.split(".")[-1] if info.name else ""
def getApplicationIcon(self, package_name: str) -> Any:
return None
def hasSystemFeature(self, feature: str) -> bool:
for pkg_name, raw in self._packages.items():
features = raw.get("features", {})
if feature in features:
return features[feature]
return False
def getLaunchIntentForPackage(self, package_name: str) -> Any:
from .app import Intent
if package_name in self._packages:
intent = Intent(Intent.ACTION_MAIN)
intent.setPackage(package_name)
return intent
return None
def getLeanbackLaunchIntentForPackage(self, package_name: str) -> Any:
return self.getLaunchIntentForPackage(package_name)
def getPackageSizeInfo(self, package_name: str, observer: Any):
pass
def getPackageGids(self, package_name: str) -> Optional[List[int]]:
return []
def getPackageUid(self, package_name: str, flags: int) -> int:
return 10001
def getNameForUid(self, uid: int) -> Optional[str]:
return _package_name
def getPackagesForUid(self, uid: int) -> Optional[List[str]]:
return [_package_name]
def getResourcesForActivity(self, component: Any) -> Any:
return None
def getResourcesForApplication(self, app_info: ApplicationInfo) -> Any:
return None
def isSafeMode(self) -> bool:
return False
def addPackageToPreferred(self, package_name: str):
pass
def removePackageFromPreferred(self, package_name: str):
pass
def getPreferredPackages(self, flags: int) -> List[PackageInfo]:
return []
def setApplicationEnabledSetting(self, package_name: str, new_state: int, flags: int):
pass
def getApplicationEnabledSetting(self, package_name: str) -> int:
return 0
def getComponentEnabledSetting(self, component_name: Any) -> int:
return 1
def setComponentEnabledSetting(self, component_name: Any, new_state: int, flags: int):
pass
def clearInstantAppCookie(self):
pass
def getInstantAppCookie(self) -> bytes:
return b""
def getInstantAppCookieMaxSize(self) -> int:
return 4096
def getMimeGroup(self, mime_group: str) -> List[str]:
return []
def isInstantApp(self) -> bool:
return False
def isInstantAppPackageName(self, package_name: str) -> bool:
return False
def getSyntheticAppDetailsActivityEnabled(self, package_name: str) -> int:
return 1
def setSyntheticAppDetailsActivityEnabled(self, package_name: str, enabled: int):
pass
def canRequestPackageInstalls(self) -> bool:
return True
def currentToCanonicalPackageNames(self, names: List[str]) -> List[str]:
return list(names)
def canonicalToCurrentPackageNames(self, names: List[str]) -> List[str]:
return list(names)
def getInstallerPackageName(self, package_name: str) -> Optional[str]:
return None
def toString(self) -> str:
return f"PackageManager(packages={len(self._packages)})"
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"PackageManager",
"PackageInfo",
"ApplicationInfo",
"ActivityInfo",
"ServiceInfo",
"ProviderInfo",
"PermissionInfo",
"ResolveInfo",
"PackageItemInfo",
"ComponentInfo",
"Signature",
"PackageStats",
]
File diff suppressed because it is too large. Load diff
+965
View File
@@ -0,0 +1,965 @@
"""
Android SQLite 组件 — SQLiteDatabase, SQLiteOpenHelper, SQLiteStatement 等
使用 Python sqlite3 模块作为后端
方法签名完全对齐 Android SDK
"""
import os
import sys
import threading
import sqlite3 as _sqlite3
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,
)
from .database import (
Cursor, AbstractCursor, ContentObserver, DataSetObserver, SQLException as BaseSQLException,
)
# ═══════════════════════════════════════════════════════════════
# SQLiteException hierarchy
# ═══════════════════════════════════════════════════════════════
class SQLiteException(BaseSQLException):
"""SQLiteException — SQLite 异常基类"""
def __init__(self, message: str = ""):
super().__init__(message)
self._error_message = message
class SQLiteAbortException(SQLiteException):
pass
class SQLiteAccessPermException(SQLiteException):
pass
class SQLiteBindOrColumnIndexOutOfRangeException(SQLiteException):
pass
class SQLiteBlobTooBigException(SQLiteException):
pass
class SQLiteCantOpenDatabaseException(SQLiteException):
pass
class SQLiteConstraintException(SQLiteException):
pass
class SQLiteDatabaseCorruptException(SQLiteException):
pass
class SQLiteDatabaseLockedException(SQLiteException):
pass
class SQLiteDatatypeMismatchException(SQLiteException):
pass
class SQLiteDiskIOException(SQLiteException):
pass
class SQLiteDoneException(SQLiteException):
pass
class SQLiteException_General(SQLiteException):
pass
class SQLiteFullException(SQLiteException):
pass
class SQLiteMisuseException(SQLiteException):
pass
class SQLiteOutOfMemoryException(SQLiteException):
pass
class SQLiteReadOnlyDatabaseException(SQLiteException):
pass
class SQLiteTableLockedException(SQLiteException):
pass
class SQLiteUnsupportedOperationException(SQLiteException):
pass
# ═══════════════════════════════════════════════════════════════
# SQLiteClosable
# ═══════════════════════════════════════════════════════════════
class SQLiteClosable(Stub):
"""SQLiteClosable — 可关闭资源基类"""
def __init__(self):
super().__init__()
self._closed = False
self._reference_count = 1
def acquireReference(self):
if self._closed:
raise IllegalStateException("already closed")
self._reference_count += 1
def releaseReference(self):
self._reference_count -= 1
if self._reference_count <= 0:
self.onAllReferencesReleased()
def releaseReferenceFromContainer(self):
self.releaseReference()
def onAllReferencesReleased(self):
self.close()
def close(self):
self._closed = True
def isClosed(self) -> bool:
return self._closed
class IllegalStateException(RuntimeError):
pass
# ═══════════════════════════════════════════════════════════════
# SQLiteProgram
# ═══════════════════════════════════════════════════════════════
class SQLiteProgram(SQLiteClosable):
"""SQLiteProgram — SQL 程序基类"""
def __init__(self, db: "SQLiteDatabase", sql: str):
super().__init__()
self._db = db
self._sql = sql
self._bindings: Dict[int, Any] = {}
self._next_bind_index = 1
def getDatabase(self) -> "SQLiteDatabase":
return self._db
def getSql(self) -> str:
return self._sql
def bindNull(self, index: int):
self._bindings[index] = None
def bindLong(self, index: int, value: int):
self._bindings[index] = value
def bindDouble(self, index: int, value: float):
self._bindings[index] = value
def bindString(self, index: int, value: str):
self._bindings[index] = value
def bindBlob(self, index: int, value: bytes):
self._bindings[index] = value
def clearBindings(self):
self._bindings.clear()
def _bind_parameters(self, cursor: _sqlite3.Cursor):
for idx, val in sorted(self._bindings.items()):
cursor.execute(f"PRAGMA dummy={idx}")
# ═══════════════════════════════════════════════════════════════
# SQLiteStatement
# ═══════════════════════════════════════════════════════════════
class SQLiteStatement(SQLiteProgram):
"""SQLiteStatement — 预编译语句"""
def __init__(self, db: "SQLiteDatabase", sql: str):
super().__init__(db, sql)
def execute(self):
try:
conn = self._db._acquire_connection()
cursor = conn.execute(self._sql, list(self._bindings.values()))
conn.commit()
except _sqlite3.DatabaseError as e:
raise _map_sqlite_error(str(e), self._sql)
def executeUpdateDelete(self) -> int:
try:
conn = self._db._acquire_connection()
cursor = conn.execute(self._sql, list(self._bindings.values()))
conn.commit()
return cursor.rowcount
except _sqlite3.DatabaseError as e:
raise _map_sqlite_error(str(e), self._sql)
def executeInsert(self) -> int:
try:
conn = self._db._acquire_connection()
cursor = conn.execute(self._sql, list(self._bindings.values()))
conn.commit()
return cursor.lastrowid
except _sqlite3.DatabaseError as e:
raise _map_sqlite_error(str(e), self._sql)
def simpleQueryForLong(self) -> int:
try:
conn = self._db._acquire_connection()
cursor = conn.execute(self._sql, list(self._bindings.values()))
row = cursor.fetchone()
conn.commit()
if row:
return int(row[0]) if row[0] is not None else 0
raise SQLiteDoneException("no results")
except _sqlite3.DatabaseError as e:
raise _map_sqlite_error(str(e), self._sql)
def simpleQueryForString(self) -> Optional[str]:
try:
conn = self._db._acquire_connection()
cursor = conn.execute(self._sql, list(self._bindings.values()))
row = cursor.fetchone()
conn.commit()
if row:
return str(row[0]) if row[0] is not None else None
raise SQLiteDoneException("no results")
except _sqlite3.DatabaseError as e:
raise _map_sqlite_error(str(e), self._sql)
# ═══════════════════════════════════════════════════════════════
# SQLiteQuery
# ═══════════════════════════════════════════════════════════════
class SQLiteQuery(SQLiteProgram):
"""SQLiteQuery — 查询对象"""
def __init__(self, db: "SQLiteDatabase", sql: str):
super().__init__(db, sql)
def toString(self) -> str:
return f"SQLiteQuery: {self._sql}"
def sql(self) -> str:
return self._sql
def bind(self, index: int, value: Any):
self._bindings[index] = value
# ═══════════════════════════════════════════════════════════════
# SQLiteCursor
# ═══════════════════════════════════════════════════════════════
class SQLiteCursor(AbstractCursor):
"""SQLiteCursor — SQLite 游标实现"""
def __init__(self, db: "SQLiteDatabase", query: SQLiteQuery):
super().__init__()
self._db = db
self._query = query
self._column_names: List[str] = []
self._rows: List[List[Any]] = []
self._current_row: Optional[List[Any]] = None
self._loaded = False
self._closed = False
def _load(self):
if self._loaded or self._closed:
return
self._loaded = True
try:
conn = self._db._acquire_connection()
cursor = conn.execute(self._query.getSql(), list(self._query._bindings.values()))
self._column_names = [desc[0] for desc in cursor.description] if cursor.description else []
self._rows = cursor.fetchall()
except _sqlite3.DatabaseError as e:
raise _map_sqlite_error(str(e), self._query.getSql())
def getCount(self) -> int:
self._load()
return len(self._rows)
def getColumnNames(self) -> List[str]:
self._load()
return list(self._column_names)
def getColumnIndex(self, column_name: str) -> int:
self._load()
try:
return self._column_names.index(column_name)
except ValueError:
return -1
def moveToPosition(self, position: int) -> bool:
self._load()
if position < -1 or position >= len(self._rows):
return False
self._pos = position
if 0 <= position < len(self._rows):
self._current_row = self._rows[position]
else:
self._current_row = None
return True
def getString(self, column_index: int) -> Optional[str]:
if self._current_row is None or column_index < 0 or column_index >= len(self._current_row):
return None
v = self._current_row[column_index]
return str(v) if v is not None else None
def getInt(self, column_index: int) -> int:
if self._current_row is None or column_index < 0 or column_index >= len(self._current_row):
return 0
v = self._current_row[column_index]
if v is None:
return 0
try:
return int(v)
except (ValueError, TypeError):
return 0
def getLong(self, column_index: int) -> int:
return self.getInt(column_index)
def getFloat(self, column_index: int) -> float:
if self._current_row is None or column_index < 0 or column_index >= len(self._current_row):
return 0.0
v = self._current_row[column_index]
if v is None:
return 0.0
try:
return float(v)
except (ValueError, TypeError):
return 0.0
def getDouble(self, column_index: int) -> float:
return self.getFloat(column_index)
def getShort(self, column_index: int) -> int:
return self.getInt(column_index)
def getBlob(self, column_index: int) -> Optional[bytes]:
if self._current_row is None or column_index < 0 or column_index >= len(self._current_row):
return None
v = self._current_row[column_index]
if isinstance(v, bytes):
return v
if v is not None:
return bytes(str(v), "utf-8")
return None
def getType(self, column_index: int) -> int:
self._load()
if self._current_row is None or column_index < 0 or column_index >= len(self._current_row):
return Cursor.FIELD_TYPE_NULL
v = self._current_row[column_index]
if v is None:
return Cursor.FIELD_TYPE_NULL
if isinstance(v, int):
return Cursor.FIELD_TYPE_INTEGER
if isinstance(v, float):
return Cursor.FIELD_TYPE_FLOAT
if isinstance(v, bytes):
return Cursor.FIELD_TYPE_BLOB
return Cursor.FIELD_TYPE_STRING
def isNull(self, column_index: int) -> bool:
return self.getType(column_index) == Cursor.FIELD_TYPE_NULL
def close(self):
self._closed = True
self._rows.clear()
self._current_row = None
def requery(self) -> bool:
self._loaded = False
self._rows.clear()
self._current_row = None
self._pos = -1
return True
# ═══════════════════════════════════════════════════════════════
# SQLiteQueryBuilder
# ═══════════════════════════════════════════════════════════════
class SQLiteQueryBuilder(Stub):
"""SQLiteQueryBuilder — 查询构建器"""
def __init__(self):
super().__init__()
self._tables: str = ""
self._distinct: bool = False
self._where: Optional[str] = None
def setTables(self, tables: str):
self._tables = tables
def getTables(self) -> str:
return self._tables
def appendWhere(self, where: str):
if self._where:
self._where = f"({self._where}) AND ({where})"
else:
self._where = where
def appendWhereStandalone(self, where: str):
self._where = where
def setDistinct(self, distinct: bool):
self._distinct = distinct
def buildQueryString(self, distinct: bool, tables: str,
columns: Optional[List[str]], where: Optional[str],
group_by: Optional[str], having: Optional[str],
order_by: Optional[str], limit: Optional[str]) -> str:
sql = "SELECT "
if distinct:
sql += "DISTINCT "
if columns and len(columns) > 0:
sql += ", ".join(columns)
else:
sql += "*"
sql += f" FROM {tables}"
if where:
sql += f" WHERE ({where})"
if group_by:
sql += f" GROUP BY {group_by}"
if having:
sql += f" HAVING {having}"
if order_by:
sql += f" ORDER BY {order_by}"
if limit:
sql += f" LIMIT {limit}"
return sql
def buildQuery(self, columns: Optional[List[str]], where: Optional[str],
group_by: Optional[str], having: Optional[str],
order_by: Optional[str], limit: Optional[str]) -> str:
return self.buildQueryString(self._distinct, self._tables,
columns, where or self._where,
group_by, having, order_by, limit)
def query(self, db: "SQLiteDatabase", columns: Optional[List[str]],
where: Optional[str], group_by: Optional[str],
having: Optional[str], order_by: Optional[str],
limit: Optional[str]) -> SQLiteCursor:
sql = self.buildQuery(columns, where, group_by, having, order_by, limit)
return db.rawQuery(sql, None)
# ═══════════════════════════════════════════════════════════════
# SQLiteDatabase
# ═══════════════════════════════════════════════════════════════
def _map_sqlite_error(msg: str, sql: str = "") -> SQLiteException:
msg_lower = msg.lower()
if "constraint" in msg_lower:
return SQLiteConstraintException(msg)
if "locked" in msg_lower:
return SQLiteDatabaseLockedException(msg)
if "corrupt" in msg_lower:
return SQLiteDatabaseCorruptException(msg)
if "readonly" in msg_lower or "read only" in msg_lower:
return SQLiteReadOnlyDatabaseException(msg)
if "disk" in msg_lower or "full" in msg_lower:
return SQLiteDiskIOException(msg) if "disk" in msg_lower else SQLiteFullException(msg)
if "abort" in msg_lower:
return SQLiteAbortException(msg)
if "permission" in msg_lower:
return SQLiteAccessPermException(msg)
if "out of memory" in msg_lower:
return SQLiteOutOfMemoryException(msg)
if "not a database" in msg_lower or "cantopen" in msg_lower:
return SQLiteCantOpenDatabaseException(msg)
if "misuse" in msg_lower:
return SQLiteMisuseException(msg)
return SQLiteException(msg)
class SQLiteDatabase(SQLiteClosable):
"""SQLiteDatabase — 数据库操作类"""
CONFLICT_ROLLBACK = 1
CONFLICT_ABORT = 2
CONFLICT_FAIL = 3
CONFLICT_IGNORE = 4
CONFLICT_REPLACE = 5
OPEN_READWRITE = 0
OPEN_READONLY = 1
CREATE_IF_NECESSARY = 268435456
NO_LOCALIZED_COLLATORS = 16
_db_registry: Dict[str, "SQLiteDatabase"] = {}
_lock = threading.Lock()
def __init__(self, path: str, flags: int = 0):
super().__init__()
self._path = path
self._flags = flags
self._conn: Optional[_sqlite3.Connection] = None
self._in_transaction = False
self._transaction_successful = False
self._version: int = 0
self._max_size: int = 0
self._page_size: int = 4096
self._enable_wal = False
self._attached_dbs: List[Dict[str, Any]] = []
@staticmethod
def openDatabase(path: str, cursor_factory: Any = None,
flags: int = 0, error_handler: Any = None) -> "SQLiteDatabase":
db = SQLiteDatabase(path, flags)
try:
db._conn = _sqlite3.connect(path)
db._conn.execute("PRAGMA journal_mode=WAL")
conn_cursor = db._conn.execute("PRAGMA schema_version")
row = conn_cursor.fetchone()
if row:
db._version = row[0]
except _sqlite3.DatabaseError as e:
raise _map_sqlite_error(str(e))
SQLiteDatabase._db_registry[path] = db
return db
@staticmethod
def openOrCreateDatabase(path: str, cursor_factory: Any = None,
error_handler: Any = None) -> "SQLiteDatabase":
return SQLiteDatabase.openDatabase(path, cursor_factory,
SQLiteDatabase.CREATE_IF_NECESSARY, error_handler)
@staticmethod
def create(cursor_factory: Any = None, error_handler: Any = None) -> "SQLiteDatabase":
import tempfile
tmp = tempfile.NamedTemporaryFile(suffix=".db", delete=False)
tmp.close()
return SQLiteDatabase.openOrCreateDatabase(tmp.name, cursor_factory, error_handler)
@staticmethod
def deleteDatabase(path: str) -> bool:
try:
if path in SQLiteDatabase._db_registry:
db = SQLiteDatabase._db_registry[path]
db.close()
del SQLiteDatabase._db_registry[path]
os.remove(path)
wal_path = path + "-wal"
if os.path.exists(wal_path):
os.remove(wal_path)
shm_path = path + "-shm"
if os.path.exists(shm_path):
os.remove(shm_path)
return True
except (OSError, Exception):
return False
@staticmethod
def databaseList() -> List[str]:
return list(SQLiteDatabase._db_registry.keys())
def _acquire_connection(self) -> _sqlite3.Connection:
if self._conn is None:
raise SQLiteCantOpenDatabaseException("Database not open")
return self._conn
def execSQL(self, sql: str, bind_args: Optional[List[Any]] = None):
self._ensure_open()
try:
conn = self._acquire_connection()
args = bind_args or []
conn.execute(sql, args)
if not self._in_transaction:
conn.commit()
except _sqlite3.DatabaseError as e:
raise _map_sqlite_error(str(e), sql)
def rawQuery(self, sql: str, selection_args: Optional[List[Any]] = None) -> SQLiteCursor:
self._ensure_open()
query = SQLiteQuery(self, sql)
if selection_args:
for i, arg in enumerate(selection_args):
query.bind(i + 1, arg)
cursor = SQLiteCursor(self, query)
return cursor
def insert(self, table: str, null_column_hack: Optional[str],
values: Any) -> int:
self._ensure_open()
if values is None:
return -1
cols = []
vals = []
if hasattr(values, "valueSet"):
items = list(values.valueSet())
elif hasattr(values, "key_set"):
items = [(k, getattr(values, f"get{k[0].upper()}{k[1:]}", lambda: None)())
for k in values.key_set()]
elif isinstance(values, dict):
items = list(values.items())
else:
return -1
for k, v in items:
cols.append(str(k))
vals.append(v)
if len(cols) == 0:
cols = ["_id"]
vals = [None]
col_str = ", ".join(cols)
placeholders = ", ".join(["?"] * len(vals))
sql = f"INSERT INTO {table} ({col_str}) VALUES ({placeholders})"
try:
conn = self._acquire_connection()
cursor = conn.execute(sql, vals)
if not self._in_transaction:
conn.commit()
return cursor.lastrowid
except _sqlite3.DatabaseError as e:
raise _map_sqlite_error(str(e), sql)
def update(self, table: str, values: Any,
where_clause: Optional[str] = None,
where_args: Optional[List[Any]] = None) -> int:
self._ensure_open()
if values is None:
return 0
set_pairs = []
vals = []
if isinstance(values, dict):
items = list(values.items())
elif hasattr(values, "valueSet"):
items = list(values.valueSet())
else:
return 0
for k, v in items:
set_pairs.append(f"{k} = ?")
vals.append(v)
set_str = ", ".join(set_pairs)
sql = f"UPDATE {table} SET {set_str}"
if where_clause:
sql += f" WHERE {where_clause}"
if where_args:
vals.extend(where_args)
try:
conn = self._acquire_connection()
cursor = conn.execute(sql, vals)
if not self._in_transaction:
conn.commit()
return cursor.rowcount
except _sqlite3.DatabaseError as e:
raise _map_sqlite_error(str(e), sql)
def delete(self, table: str, where_clause: Optional[str] = None,
where_args: Optional[List[Any]] = None) -> int:
self._ensure_open()
sql = f"DELETE FROM {table}"
vals: List[Any] = []
if where_clause:
sql += f" WHERE {where_clause}"
if where_args:
vals = list(where_args)
try:
conn = self._acquire_connection()
cursor = conn.execute(sql, vals)
if not self._in_transaction:
conn.commit()
return cursor.rowcount
except _sqlite3.DatabaseError as e:
raise _map_sqlite_error(str(e), sql)
def query(self, table: str, columns: Optional[List[str]] = None,
selection: Optional[str] = None,
selection_args: Optional[List[Any]] = None,
group_by: Optional[str] = None, having: Optional[str] = None,
order_by: Optional[str] = None,
limit: Optional[str] = None) -> SQLiteCursor:
col_str = ", ".join(columns) if columns else "*"
sql = f"SELECT {col_str} FROM {table}"
if selection:
sql += f" WHERE {selection}"
if group_by:
sql += f" GROUP BY {group_by}"
if having:
sql += f" HAVING {having}"
if order_by:
sql += f" ORDER BY {order_by}"
if limit:
sql += f" LIMIT {limit}"
return self.rawQuery(sql, selection_args)
def compileStatement(self, sql: str) -> SQLiteStatement:
return SQLiteStatement(self, sql)
def beginTransaction(self):
self._ensure_open()
try:
conn = self._acquire_connection()
conn.execute("BEGIN TRANSACTION")
self._in_transaction = True
self._transaction_successful = False
except _sqlite3.DatabaseError as e:
raise _map_sqlite_error(str(e))
def endTransaction(self):
if self._in_transaction and self._conn:
try:
if not self._transaction_successful:
self._conn.rollback()
else:
self._conn.commit()
except _sqlite3.DatabaseError:
pass
self._in_transaction = False
def setTransactionSuccessful(self):
if not self._in_transaction:
raise IllegalStateException("Not in transaction")
self._transaction_successful = True
def inTransaction(self) -> bool:
return self._in_transaction
def isOpen(self) -> bool:
return self._conn is not None
def getPath(self) -> str:
return self._path
def getVersion(self) -> int:
return self._version
def setVersion(self, version: int):
self._version = version
if self._conn:
try:
self._conn.execute(f"PRAGMA schema_version = {version}")
except _sqlite3.DatabaseError:
pass
def onCreate(self, db: "SQLiteDatabase"):
pass
def onConfigure(self, db: "SQLiteDatabase"):
pass
def enableWriteAheadLogging(self) -> bool:
self._ensure_open()
try:
self._conn.execute("PRAGMA journal_mode=WAL")
self._enable_wal = True
return True
except _sqlite3.DatabaseError:
return False
def disableWriteAheadLogging(self):
try:
self._conn.execute("PRAGMA journal_mode=DELETE")
self._enable_wal = False
except _sqlite3.DatabaseError:
pass
def isWriteAheadLoggingEnabled(self) -> bool:
return self._enable_wal
def getMaximumSize(self) -> int:
return self._max_size
def getPageSize(self) -> int:
return self._page_size
def setMaximumSize(self, size: int) -> int:
self._max_size = size
return size
def setPageSize(self, size: int):
self._page_size = size
if self._conn:
try:
self._conn.execute(f"PRAGMA page_size = {size}")
except _sqlite3.DatabaseError:
pass
def getPageCount(self) -> int:
if self._conn:
try:
cursor = self._conn.execute("PRAGMA page_count")
row = cursor.fetchone()
if row:
return row[0]
except _sqlite3.DatabaseError:
pass
return 0
def yieldIfContendedSafely(self, sleep_time: int = 0) -> bool:
return True
def needUpgrade(self, new_version: int) -> bool:
return new_version > self._version
def getAttachedDbs(self) -> Optional[List[Dict[str, Any]]]:
return self._attached_dbs
def isReadOnly(self) -> bool:
return bool(self._flags & self.OPEN_READONLY)
def close(self):
if self._conn:
try:
self._conn.close()
except Exception:
pass
self._conn = None
super().close()
def _ensure_open(self):
if self._conn is None:
raise SQLiteCantOpenDatabaseException("Database not open")
def toString(self) -> str:
return f"SQLiteDatabase(path={self._path})"
# ═══════════════════════════════════════════════════════════════
# SQLiteOpenHelper
# ═══════════════════════════════════════════════════════════════
class SQLiteOpenHelper(Stub):
"""SQLiteOpenHelper — 数据库打开辅助类"""
def __init__(self, context: Any, name: Optional[str],
factory: Any, version: int,
error_handler: Any = None):
super().__init__()
self._context = context
self._name = name
self._factory = factory
self._version = version
self._error_handler = error_handler
self._readable_db: Optional[SQLiteDatabase] = None
self._writable_db: Optional[SQLiteDatabase] = None
self._database_path: Optional[str] = None
self._is_initialized = False
def _get_database_path(self) -> str:
if self._database_path:
return self._database_path
db_dir = os.path.join(_app_data_dir, "databases")
os.makedirs(db_dir, exist_ok=True)
if self._name:
self._database_path = os.path.join(db_dir, self._name)
else:
import tempfile
tmp = tempfile.NamedTemporaryFile(suffix=".db", delete=False)
tmp.close()
self._database_path = tmp.name
return self._database_path
def getWritableDatabase(self) -> SQLiteDatabase:
if self._writable_db and self._writable_db.isOpen():
return self._writable_db
path = self._get_database_path()
db = SQLiteDatabase.openOrCreateDatabase(path, self._factory, self._error_handler)
if not self._is_initialized:
self._is_initialized = True
current_version = db.getVersion()
if current_version == 0:
self.onCreate(db)
db.setVersion(self._version)
elif current_version < self._version:
self.onUpgrade(db, current_version, self._version)
elif current_version > self._version:
self.onDowngrade(db, current_version, self._version)
self.onOpen(db)
self._writable_db = db
self._readable_db = db
return db
def getReadableDatabase(self) -> SQLiteDatabase:
return self.getWritableDatabase()
def onCreate(self, db: SQLiteDatabase):
pass
def onUpgrade(self, db: SQLiteDatabase, old_version: int, new_version: int):
pass
def onDowngrade(self, db: SQLiteDatabase, old_version: int, new_version: int):
pass
def onOpen(self, db: SQLiteDatabase):
pass
def close(self):
if self._writable_db:
self._writable_db.close()
self._writable_db = None
if self._readable_db:
self._readable_db = None
def getDatabaseName(self) -> Optional[str]:
return self._name
def getReadableDB(self) -> Optional[SQLiteDatabase]:
return self._readable_db
def getWriteableDB(self) -> Optional[SQLiteDatabase]:
return self._writable_db
def toString(self) -> str:
return f"SQLiteOpenHelper(name={self._name}, version={self._version})"
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"SQLiteDatabase",
"SQLiteOpenHelper",
"SQLiteStatement",
"SQLiteCursor",
"SQLiteQuery",
"SQLiteQueryBuilder",
"SQLiteClosable",
"SQLiteProgram",
"SQLiteAbortException",
"SQLiteAccessPermException",
"SQLiteBindOrColumnIndexOutOfRangeException",
"SQLiteBlobTooBigException",
"SQLiteCantOpenDatabaseException",
"SQLiteConstraintException",
"SQLiteDatabaseCorruptException",
"SQLiteDatabaseLockedException",
"SQLiteDatatypeMismatchException",
"SQLiteDiskIOException",
"SQLiteDoneException",
"SQLiteException",
"SQLiteFullException",
"SQLiteMisuseException",
"SQLiteOutOfMemoryException",
"SQLiteReadOnlyDatabaseException",
"SQLiteTableLockedException",
"SQLiteUnsupportedOperationException",
]
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+335
View File
@@ -0,0 +1,335 @@
"""
Android Graphics PDF 组件 — PdfDocument, PdfRenderer, ParcelFileDescriptor
方法签名完全对齐 Android SDK
"""
import os
import io
import tempfile
from typing import Any, Dict, List, Optional, Tuple
from .._core import Stub, AndroidOnlyError, android_context, HAS_PIL, HAS_PYMUPDF, HAS_PDF2IMAGE, resolve_android_path
# ═══════════════════════════════════════════════════════════════
# ParcelFileDescriptor
# ═══════════════════════════════════════════════════════════════
class ParcelFileDescriptor(Stub):
"""ParcelFileDescriptor — 文件描述符"""
MODE_READ_ONLY = 0x10000000
MODE_WRITE_ONLY = 0x20000000
MODE_READ_WRITE = 0x30000000
MODE_CREATE = 0x08000000
MODE_TRUNCATE = 0x04000000
MODE_APPEND = 0x02000000
def __init__(self, fd: int = -1, file_path: str = ""):
super().__init__()
self._fd = fd
self._file_path = file_path
self._closed = False
self._file_obj = None
if file_path and fd < 0:
resolved = resolve_android_path(file_path)
self._file_path = resolved
@staticmethod
def open(file_path: str, mode: int) -> "ParcelFileDescriptor":
resolved = resolve_android_path(file_path)
fmode = "rb"
if mode & ParcelFileDescriptor.MODE_WRITE_ONLY:
fmode = "wb"
elif mode & ParcelFileDescriptor.MODE_READ_WRITE:
fmode = "r+b"
if mode & ParcelFileDescriptor.MODE_CREATE:
if not os.path.exists(os.path.dirname(resolved)):
os.makedirs(os.path.dirname(resolved), exist_ok=True)
if not os.path.exists(resolved):
open(resolved, "w").close()
return ParcelFileDescriptor(-1, file_path)
@staticmethod
def parseFromFd(fd: int) -> "ParcelFileDescriptor":
return ParcelFileDescriptor(fd)
@staticmethod
def parseFromString(path: str) -> "ParcelFileDescriptor":
resolved = resolve_android_path(path)
return ParcelFileDescriptor(-1, resolved)
@staticmethod
def adoptFd(fd: int) -> "ParcelFileDescriptor":
return ParcelFileDescriptor(fd)
@staticmethod
def createPipe() -> List["ParcelFileDescriptor"]:
r, w = os.pipe()
return [ParcelFileDescriptor(r), ParcelFileDescriptor(w)]
def getFileDescriptor(self) -> int:
return self._fd
def getStatSize(self) -> int:
if self._file_path and os.path.exists(self._file_path):
return os.path.getsize(self._file_path)
return 0
def getFd(self) -> int:
return self._fd
def close(self):
if not self._closed:
self._closed = True
if self._file_obj:
self._file_obj.close()
elif self._fd >= 0:
try:
os.close(self._fd)
except OSError:
pass
def getFilePath(self) -> str:
return self._file_path
def toString(self) -> str:
return f"ParcelFileDescriptor(fd={self._fd})"
# ═══════════════════════════════════════════════════════════════
# PdfDocument
# ═══════════════════════════════════════════════════════════════
class PdfDocument(Stub):
"""PdfDocument — PDF 文档"""
class Page(Stub):
"""PdfDocument.Page — PDF 页面"""
def __init__(self, page_number: int, width: int, height: int):
super().__init__()
self._page_number = page_number
self._width = width
self._height = height
self._canvas = None
def getCanvas(self) -> "Canvas":
from .graphics import Canvas
if self._canvas is None:
self._canvas = Canvas(self._width, self._height)
return self._canvas
def getWidth(self) -> int:
return self._width
def getHeight(self) -> int:
return self._height
def start(self):
pass
def finish(self):
pass
def __init__(self):
super().__init__()
self._pages: List[PdfDocument.Page] = []
def startPage(self, page_size: "Page") -> "Canvas":
width = page_size.getWidth() if hasattr(page_size, "getWidth") else 595
height = page_size.getHeight() if hasattr(page_size, "getHeight") else 842
page = PdfDocument.Page(len(self._pages), width, height)
self._pages.append(page)
return page.getCanvas()
def finishPage(self, page: "Page"):
page.finish()
def getPages(self) -> List["Page"]:
return list(self._pages)
def writeTo(self, out_stream, close: bool = True):
if HAS_PYMUPDF:
import fitz
doc = fitz.open()
for p in self._pages:
page = doc.new_page(width=p._width, height=p._height)
if HAS_PIL and hasattr(p._canvas, "_image") and p._canvas._image:
img_bytes = io.BytesIO()
p._canvas._image.save(img_bytes, format="PNG")
img_bytes.seek(0)
rect = page.rect
page.insert_image(rect, stream=img_bytes.read())
doc.save(out_stream)
doc.close()
elif HAS_PIL:
from PIL import Image
images = []
for p in self._pages:
if hasattr(p._canvas, "_image") and p._canvas._image:
images.append(p._canvas._image.convert("RGB"))
if images:
images[0].save(out_stream, format="PDF", save_all=True,
append_images=images[1:])
else:
blank = Image.new("RGB", (595, 842), (255, 255, 255))
blank.save(out_stream, format="PDF")
else:
if close:
out_stream.close()
def close(self):
self._pages.clear()
def __enter__(self):
return self
def __exit__(self, *args):
self.close()
# ═══════════════════════════════════════════════════════════════
# PdfRenderer
# ═══════════════════════════════════════════════════════════════
class PdfRenderer(Stub):
"""PdfRenderer — PDF 渲染器"""
RENDER_MODE_FOR_DISPLAY = 1
RENDER_MODE_FOR_PRINT = 2
RENDER_MODE_FOR_ALL = 3
class Page(Stub):
"""PdfRenderer.Page — PDF 渲染页面"""
def __init__(self, renderer: "PdfRenderer", index: int):
super().__init__()
self._renderer = renderer
self._index = index
self._opened = True
if renderer._doc:
self._fitz_page = renderer._doc[index]
rect = self._fitz_page.rect
self._width = int(rect.width)
self._height = int(rect.height)
else:
self._fitz_page = None
self._width = 595
self._height = 842
def getWidth(self) -> int:
return self._width
def getHeight(self) -> int:
return self._height
def getIndex(self) -> int:
return self._index
def render(self, canvas: "Canvas", destLeft: int, destTop: int,
destRight: int, destBottom: int, flags: int = 0):
if not self._opened:
return
if HAS_PYMUPDF and self._fitz_page:
from .graphics import Bitmap
mat = fitz.Matrix(2, 2) # 2x zoom for quality
pix = self._fitz_page.get_pixmap(matrix=mat)
img_data = pix.tobytes("png")
if HAS_PIL and hasattr(canvas, "_image") and canvas._image:
from PIL import Image
import io as _io
img = Image.open(io.BytesIO(img_data))
img = img.resize((destRight - destLeft, destBottom - destTop))
canvas._image.paste(img, (destLeft, destTop))
elif HAS_PDF2IMAGE and HAS_PIL and hasattr(canvas, "_image"):
from pdf2image import convert_from_bytes
if self._renderer._pdf_bytes:
images = convert_from_bytes(
self._renderer._pdf_bytes,
first_page=self._index + 1,
last_page=self._index + 1
)
if images:
img = images[0].resize((destRight - destLeft, destBottom - destTop))
canvas._image.paste(img.convert("RGBA"), (destLeft, destTop))
def start(self):
self._opened = True
def finish(self):
self._opened = False
def close(self):
self._opened = False
def __init__(self, pfd: ParcelFileDescriptor, closePfd: bool = True):
super().__init__()
self._pfd = pfd
self._close_pfd = closePfd
self._page_count = 0
self._pdf_bytes = None
self._doc = None
file_path = pfd.getFilePath()
if file_path and os.path.exists(file_path):
with open(file_path, "rb") as f:
self._pdf_bytes = f.read()
elif pfd._fd >= 0:
try:
self._pdf_bytes = os.read(pfd._fd, 10 * 1024 * 1024)
except OSError:
self._pdf_bytes = None
if HAS_PYMUPDF and self._pdf_bytes:
import fitz
self._doc = fitz.open(stream=self._pdf_bytes, filetype="pdf")
self._page_count = len(self._doc)
elif self._pdf_bytes:
self._page_count = 1 # fallback
else:
self._page_count = 0
def openPage(self, index: int) -> "Page":
if 0 <= index < self._page_count:
return PdfRenderer.Page(self, index)
raise IndexError(f"Page index {index} out of range (0-{self._page_count - 1})")
def close(self):
if self._doc and HAS_PYMUPDF:
self._doc.close()
self._doc = None
if self._close_pfd and self._pfd:
self._pfd.close()
self._pdf_bytes = None
def getPageCount(self) -> int:
return self._page_count
def shouldScale(self) -> bool:
return False
def __enter__(self):
return self
def __exit__(self, *args):
self.close()
# ═══════════════════════════════════════════════════════════════
# PdfDocumentPage (alias for PdfDocument.Page)
# ═══════════════════════════════════════════════════════════════
PdfDocumentPage = PdfDocument.Page
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"PdfDocument",
"PdfDocumentPage",
"PdfRenderer",
"ParcelFileDescriptor",
]
+533
View File
@@ -0,0 +1,533 @@
"""
Android Hardware 组件 — Sensor, SensorManager, SensorEvent, 等
方法签名完全对齐 Android SDK
"""
import math
import time
import random
from typing import Any, Callable, Dict, List, Optional, Tuple
from .._core import Stub, AndroidOnlyError, android_context, HAS_PIL
# ═══════════════════════════════════════════════════════════════
# Sensor
# ═══════════════════════════════════════════════════════════════
class Sensor(Stub):
"""Sensor — 传感器"""
TYPE_ACCELEROMETER = 1
TYPE_MAGNETIC_FIELD = 2
TYPE_ORIENTATION = 3
TYPE_GYROSCOPE = 4
TYPE_LIGHT = 5
TYPE_PRESSURE = 6
TYPE_PROXIMITY = 8
TYPE_GRAVITY = 9
TYPE_LINEAR_ACCELERATION = 10
TYPE_ROTATION_VECTOR = 11
TYPE_RELATIVE_HUMIDITY = 12
TYPE_AMBIENT_TEMPERATURE = 13
TYPE_STEP_COUNTER = 19
TYPE_STEP_DETECTOR = 18
TYPE_HEART_RATE = 21
TYPE_LOW_LATENCY_OFFBODY_DETECT = 34
TYPE_ACCELEROMETER_LIMITED_AXES = 35
TYPE_DEVICE_ORIENTATION = 27
TYPE_STATIONARY_DETECT = 28
REPORTING_MODE_CONTINUOUS = 0
REPORTING_MODE_ON_CHANGE = 1
REPORTING_MODE_ONE_SHOT = 2
REPORTING_MODE_SPECIAL_TRIGGER = 3
def __init__(self, type_: int, name: str = "Simulated Sensor",
vendor: str = "Android-pyApi", version: int = 1,
resolution: float = 1.0, max_range: float = 100.0,
power: float = 0.5, min_delay: int = 10000):
super().__init__()
self._type = type_
self._name = name
self._vendor = vendor
self._version = version
self._resolution = resolution
self._max_range = max_range
self._power = power
self._min_delay = min_delay
# Assign default values based on sensor type
type_defaults = {
Sensor.TYPE_ACCELEROMETER: ("Accelerometer", 0.01, 39.24, 3.0, 10000),
Sensor.TYPE_GYROSCOPE: ("Gyroscope", 0.001, 34.91, 6.1, 10000),
Sensor.TYPE_MAGNETIC_FIELD: ("Magnetic Field", 0.01, 2000.0, 4.0, 10000),
Sensor.TYPE_LIGHT: ("Light", 1.0, 40000.0, 0.5, 10000),
Sensor.TYPE_PROXIMITY: ("Proximity", 1.0, 10.0, 0.5, 10000),
Sensor.TYPE_PRESSURE: ("Pressure", 0.01, 1100.0, 1.0, 10000),
Sensor.TYPE_GRAVITY: ("Gravity", 0.01, 39.24, 3.0, 10000),
Sensor.TYPE_LINEAR_ACCELERATION: ("Linear Acceleration", 0.01, 39.24, 3.0, 10000),
Sensor.TYPE_ROTATION_VECTOR: ("Rotation Vector", 0.0001, 1.0, 5.0, 10000),
Sensor.TYPE_RELATIVE_HUMIDITY: ("Relative Humidity", 0.1, 100.0, 0.5, 10000),
Sensor.TYPE_AMBIENT_TEMPERATURE: ("Ambient Temperature", 0.01, 80.0, 0.5, 10000),
Sensor.TYPE_STEP_COUNTER: ("Step Counter", 1.0, 100000.0, 0.1, 100000),
Sensor.TYPE_STEP_DETECTOR: ("Step Detector", 1.0, 1.0, 0.1, 100000),
Sensor.TYPE_HEART_RATE: ("Heart Rate", 1.0, 220.0, 0.5, 100000),
}
if type_ in type_defaults:
n, res, mr, p, md = type_defaults[type_]
self._name = name if name != "Simulated Sensor" else n
self._resolution = resolution if resolution != 1.0 else res
self._max_range = max_range if max_range != 100.0 else mr
self._power = power if power != 0.5 else p
self._min_delay = min_delay if min_delay != 10000 else md
def getType(self) -> int:
return self._type
def getName(self) -> str:
return self._name
def getVendor(self) -> str:
return self._vendor
def getVersion(self) -> int:
return self._version
def getMaximumRange(self) -> float:
return self._max_range
def getMinDelay(self) -> int:
return self._min_delay
def getPower(self) -> float:
return self._power
def getResolution(self) -> float:
return self._resolution
def getFifoMaxEventCount(self) -> int:
return 0
def getFifoReservedEventCount(self) -> int:
return 0
def getReportingMode(self) -> int:
return Sensor.REPORTING_MODE_CONTINUOUS
def isWakeUpSensor(self) -> bool:
return False
def getStringType(self) -> str:
type_names = {
Sensor.TYPE_ACCELEROMETER: "android.sensor.accelerometer",
Sensor.TYPE_MAGNETIC_FIELD: "android.sensor.magnetic_field",
Sensor.TYPE_GYROSCOPE: "android.sensor.gyroscope",
Sensor.TYPE_LIGHT: "android.sensor.light",
Sensor.TYPE_PRESSURE: "android.sensor.pressure",
Sensor.TYPE_PROXIMITY: "android.sensor.proximity",
Sensor.TYPE_GRAVITY: "android.sensor.gravity",
Sensor.TYPE_LINEAR_ACCELERATION: "android.sensor.linear_acceleration",
Sensor.TYPE_ROTATION_VECTOR: "android.sensor.rotation_vector",
Sensor.TYPE_RELATIVE_HUMIDITY: "android.sensor.relative_humidity",
Sensor.TYPE_AMBIENT_TEMPERATURE: "android.sensor.ambient_temperature",
Sensor.TYPE_STEP_COUNTER: "android.sensor.step_counter",
Sensor.TYPE_STEP_DETECTOR: "android.sensor.step_detector",
Sensor.TYPE_HEART_RATE: "android.sensor.heart_rate",
}
return type_names.get(self._type, "android.sensor.unknown")
def toString(self) -> str:
return f"{self._name} (type={self._type}, vendor={self._vendor}, ver={self._version})"
# ═══════════════════════════════════════════════════════════════
# SensorEvent
# ═══════════════════════════════════════════════════════════════
class SensorEvent(Stub):
"""SensorEvent — 传感器事件"""
def __init__(self, sensor: Sensor, accuracy: int = 3):
super().__init__()
self.sensor = sensor
self.accuracy = accuracy
self.timestamp = int(time.time() * 1_000_000_000) # nanoseconds
self.values: List[float] = []
# Generate appropriate values based on sensor type
if sensor.getType() == Sensor.TYPE_ACCELEROMETER:
self.values = [random.uniform(-1, 1), random.uniform(-1, 1),
9.8 + random.uniform(-0.5, 0.5)]
elif sensor.getType() == Sensor.TYPE_GYROSCOPE:
self.values = [random.uniform(-0.1, 0.1)] * 3
elif sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD:
self.values = [random.uniform(-50, 50)] * 3
elif sensor.getType() == Sensor.TYPE_LIGHT:
self.values = [random.uniform(100, 1000)]
elif sensor.getType() == Sensor.TYPE_PROXIMITY:
self.values = [5.0 if random.random() > 0.5 else 0.0]
elif sensor.getType() == Sensor.TYPE_PRESSURE:
self.values = [random.uniform(980, 1050)]
elif sensor.getType() in (Sensor.TYPE_GRAVITY, Sensor.TYPE_LINEAR_ACCELERATION):
self.values = [random.uniform(-1, 1)] * 3
elif sensor.getType() == Sensor.TYPE_ROTATION_VECTOR:
self.values = [random.uniform(-1, 1)] * 5
elif sensor.getType() == Sensor.TYPE_RELATIVE_HUMIDITY:
self.values = [random.uniform(30, 70)]
elif sensor.getType() == Sensor.TYPE_AMBIENT_TEMPERATURE:
self.values = [random.uniform(15, 35)]
elif sensor.getType() == Sensor.TYPE_STEP_COUNTER:
self.values = [float(random.randint(0, 10000))]
elif sensor.getType() == Sensor.TYPE_STEP_DETECTOR:
self.values = [1.0 if random.random() > 0.95 else 0.0]
elif sensor.getType() == Sensor.TYPE_HEART_RATE:
self.values = [random.uniform(60, 100)]
else:
self.values = [0.0]
# ═══════════════════════════════════════════════════════════════
# SensorEventListener
# ═══════════════════════════════════════════════════════════════
class SensorEventListener(Stub):
"""SensorEventListener — 传感器事件监听器"""
def onSensorChanged(self, event: SensorEvent):
pass
def onAccuracyChanged(self, sensor: Sensor, accuracy: int):
pass
# ═══════════════════════════════════════════════════════════════
# SensorDelay
# ═══════════════════════════════════════════════════════════════
class SensorDelay(Stub):
"""SensorDelay — 传感器采样延迟常量"""
FASTEST = 0
GAME = 1
UI = 2
NORMAL = 3
# ═══════════════════════════════════════════════════════════════
# SensorManager
# ═══════════════════════════════════════════════════════════════
class SensorManager(Stub):
"""SensorManager — 传感器管理器"""
SENSOR_DELAY_FASTEST = 0
SENSOR_DELAY_GAME = 1
SENSOR_DELAY_UI = 2
SENSOR_DELAY_NORMAL = 3
AXIS_X = 1
AXIS_Y = 2
AXIS_Z = 3
AXIS_MINUS_X = 129
AXIS_MINUS_Y = 130
AXIS_MINUS_Z = 131
def __init__(self):
super().__init__()
self._sensors: Dict[int, Sensor] = {}
self._listeners: Dict[int, List[Tuple[SensorEventListener, Sensor, int]]] = {}
# Create default sensors
for type_ in [
Sensor.TYPE_ACCELEROMETER, Sensor.TYPE_GYROSCOPE,
Sensor.TYPE_MAGNETIC_FIELD, Sensor.TYPE_LIGHT,
Sensor.TYPE_PROXIMITY, Sensor.TYPE_PRESSURE,
Sensor.TYPE_GRAVITY, Sensor.TYPE_LINEAR_ACCELERATION,
Sensor.TYPE_ROTATION_VECTOR, Sensor.TYPE_RELATIVE_HUMIDITY,
Sensor.TYPE_AMBIENT_TEMPERATURE, Sensor.TYPE_STEP_COUNTER,
Sensor.TYPE_STEP_DETECTOR, Sensor.TYPE_HEART_RATE,
]:
self._sensors[type_] = Sensor(type_)
def getDefaultSensor(self, type_: int) -> Optional[Sensor]:
return self._sensors.get(type_)
def getSensorList(self, type_: int) -> List[Sensor]:
if type_ == -1: # ALL
return list(self._sensors.values())
sensor = self._sensors.get(type_)
return [sensor] if sensor else []
def getSensors(self) -> List[Sensor]:
return list(self._sensors.values())
def registerListener(self, listener: SensorEventListener, sensor: Sensor,
samplingPeriodUs: int, maxReportLatencyUs: int = 0) -> bool:
if sensor.getType() not in self._sensors:
return False
if listener not in self._listeners:
self._listeners[id(listener)] = []
self._listeners[id(listener)].append((listener, sensor, samplingPeriodUs))
return True
def unregisterListener(self, listener: SensorEventListener, sensor: Sensor = None) -> bool:
lid = id(listener)
if lid in self._listeners:
if sensor:
self._listeners[lid] = [
(l, s, d) for l, s, d in self._listeners[lid]
if s.getType() != sensor.getType()
]
if not self._listeners[lid]:
del self._listeners[lid]
else:
del self._listeners[lid]
return True
return False
@staticmethod
def getOrientation(R: List[float], values: List[float]) -> List[float]:
if len(R) >= 9 and len(values) >= 3:
values[0] = math.degrees(math.atan2(R[1], R[4])) # azimuth
values[1] = math.degrees(math.asin(-R[7])) # pitch
values[2] = math.degrees(math.atan2(-R[6], R[8])) # roll
return values
@staticmethod
def getRotationMatrix(R: List[float], I: List[float],
gravity: List[float], geomagnetic: List[float]) -> bool:
if len(R) < 9:
R.extend([0] * (9 - len(R)))
if len(R) >= 9:
R[0] = 1.0
R[1] = 0.0
R[2] = 0.0
R[3] = 0.0
R[4] = 1.0
R[5] = 0.0
R[6] = 0.0
R[7] = 0.0
R[8] = 1.0
return True
@staticmethod
def getAltitude(p0: float, p: float) -> float:
return 44330.0 * (1.0 - math.pow(p / p0, 1.0 / 5.255))
@staticmethod
def getInclination(I: List[float]) -> float:
return 0.0
@staticmethod
def remapCoordinateSystem(inR: List[float], X: int, Y: int,
outR: List[float]) -> bool:
if len(inR) >= 9 and len(outR) >= 9:
for i in range(9):
outR[i] = inR[i]
return True
def getDeviceId(self) -> str:
return "sensor_hub_0"
# ═══════════════════════════════════════════════════════════════
# HardwareBuffer
# ═══════════════════════════════════════════════════════════════
class HardwareBuffer(Stub):
"""HardwareBuffer — 硬件缓冲 (API 26+)"""
USAGE_CPU_READ_OFTEN = 0x00000003
USAGE_CPU_WRITE_OFTEN = 0x00000030
USAGE_GPU_SAMPLED_IMAGE = 0x00000100
USAGE_GPU_COLOR_OUTPUT = 0x00000200
USAGE_GPU_DATA_BUFFER = 0x00001000
USAGE_SENSOR_DIRECT_DATA = 0x00400000
USAGE_PROTECTED_CONTENT = 0x00004000
USAGE_VIDEO_ENCODE = 0x00010000
USAGE_GPU_FRAMEBUFFER = 0x00020000
USAGE_COMPOSER_OVERLAY = 0x00000800
USAGE_FRONT_BUFFER_QUEUE = 0x00008000
BLOB = 0x21
RGBA_8888 = 1
RGBA_FP16 = 22
RGBA_1010102 = 43
RGBX_8888 = 2
RGB_888 = 3
RGB_565 = 4
YCBCR_420_888 = 35
def __init__(self, width: int, height: int, format: int, layers: int,
usage: int, buffer_size: int = 0):
super().__init__()
self._width = width
self._height = height
self._format = format
self._layers = layers
self._usage = usage
self._buffer_size = buffer_size
self._closed = False
@property
def width(self) -> int:
return self._width
@property
def height(self) -> int:
return self._height
@property
def format(self) -> int:
return self._format
@property
def layers(self) -> int:
return self._layers
@property
def usage(self) -> int:
return self._usage
def destroy(self):
self.close()
def close(self):
self._closed = True
def isClosed(self) -> bool:
return self._closed
def isEmpty(self) -> bool:
return self._width == 0 or self._height == 0
def getWidth(self) -> int:
return self._width
def getHeight(self) -> int:
return self._height
def getFormat(self) -> int:
return self._format
def getLayers(self) -> int:
return self._layers
def getUsage(self) -> int:
return self._usage
def getStride(self) -> int:
return self._width
# ═══════════════════════════════════════════════════════════════
# BatteryManager (Constants)
# ═══════════════════════════════════════════════════════════════
class BatteryManager(Stub):
"""BatteryManager — 电池管理器常量 (实际实现在 android.os)"""
BATTERY_PROPERTY_CHARGE_COUNTER = 1
BATTERY_PROPERTY_CURRENT_NOW = 2
BATTERY_PROPERTY_CURRENT_AVERAGE = 3
BATTERY_PROPERTY_CAPACITY = 4
BATTERY_PROPERTY_STATUS = 5
BATTERY_PROPERTY_ENERGY_COUNTER = 6
BATTERY_STATUS_UNKNOWN = 1
BATTERY_STATUS_CHARGING = 2
BATTERY_STATUS_DISCHARGING = 3
BATTERY_STATUS_NOT_CHARGING = 4
BATTERY_STATUS_FULL = 5
BATTERY_HEALTH_UNKNOWN = 1
BATTERY_HEALTH_GOOD = 2
BATTERY_HEALTH_OVERHEAT = 3
BATTERY_HEALTH_DEAD = 4
BATTERY_HEALTH_OVER_VOLTAGE = 5
BATTERY_HEALTH_UNSPECIFIED_FAILURE = 6
BATTERY_HEALTH_COLD = 7
BATTERY_PLUGGED_AC = 1
BATTERY_PLUGGED_USB = 2
BATTERY_PLUGGED_WIRELESS = 4
ACTION_CHARGING = "android.intent.action.ACTION_CHARGING"
ACTION_DISCHARGING = "android.intent.action.ACTION_DISCHARGING"
EXTRA_STATUS = "status"
EXTRA_HEALTH = "health"
EXTRA_LEVEL = "level"
EXTRA_SCALE = "scale"
EXTRA_PLUGGED = "plugged"
EXTRA_TEMPERATURE = "temperature"
EXTRA_VOLTAGE = "voltage"
EXTRA_TECHNOLOGY = "technology"
EXTRA_CHARGE_COUNTER = "charge_counter"
EXTRA_PRESENT = "present"
def __init__(self):
super().__init__()
def getIntProperty(self, id: int) -> int:
id_values = {
BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER: 3500,
BatteryManager.BATTERY_PROPERTY_CAPACITY: 85,
BatteryManager.BATTERY_PROPERTY_STATUS: BatteryManager.BATTERY_STATUS_DISCHARGING,
}
return id_values.get(id, 0)
def getLongProperty(self, id: int) -> int:
return 0
# ═══════════════════════════════════════════════════════════════
# BiometricManager (lightweight)
# ═══════════════════════════════════════════════════════════════
class BiometricManager(Stub):
"""BiometricManager — 生物识别管理器 (轻量)"""
BIOMETRIC_SUCCESS = 0
BIOMETRIC_ERROR_HW_UNAVAILABLE = 1
BIOMETRIC_ERROR_NONE_ENROLLED = 11
BIOMETRIC_ERROR_NO_HARDWARE = 12
BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15
def __init__(self):
super().__init__()
def canAuthenticate(self) -> int:
return BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE
# ═══════════════════════════════════════════════════════════════
# Flashlight support
# ═══════════════════════════════════════════════════════════════
class CameraFlashlight(Stub):
"""CameraFlashlight — 手电筒支持"""
def __init__(self):
super().__init__()
self._on = False
def setTorchMode(self, cameraId: str, enabled: bool):
self._on = enabled
def isTorchModeEnabled(self, cameraId: str) -> bool:
return self._on
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"Sensor", "SensorManager", "SensorEvent", "SensorEventListener",
"SensorDelay",
"HardwareBuffer",
"BatteryManager",
"BiometricManager",
"CameraFlashlight",
]
@@ -0,0 +1,291 @@
"""
Android Biometrics 组件 — BiometricManager, BiometricPrompt, 等
方法签名完全对齐 Android SDK
"""
from typing import Any, Callable, Dict, List, Optional, Tuple
from .._core import Stub, AndroidOnlyError, android_context
# ═══════════════════════════════════════════════════════════════
# BiometricConstants
# ═══════════════════════════════════════════════════════════════
class BiometricConstants(Stub):
"""BiometricConstants — 生物识别常量"""
BIOMETRIC_SUCCESS = 0
BIOMETRIC_ERROR_HW_UNAVAILABLE = 1
BIOMETRIC_ERROR_VENDOR = 8
BIOMETRIC_ERROR_LOCKOUT = 7
BIOMETRIC_ERROR_LOCKOUT_PERMANENT = 9
BIOMETRIC_ERROR_USER_CANCELED = 10
BIOMETRIC_ERROR_NO_BIOMETRIC = 11
BIOMETRIC_ERROR_HW_NOT_PRESENT = 12
BIOMETRIC_ERROR_NEGATIVE_BUTTON = 13
BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL = 14
BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15
BIOMETRIC_ERROR_UNSUPPORTED = 65536
BIOMETRIC_ERROR_CANCELED = 5
BIOMETRIC_ERROR_TIMEOUT = 3
BIOMETRIC_ERROR_NONE_ENROLLED = 11
BIOMETRIC_ERROR_NO_HARDWARE = 12
BIOMETRIC_STATUS_OK = 0
# Authenticator types
BIOMETRIC_STRONG = 0x0F
BIOMETRIC_WEAK = 0xFF
DEVICE_CREDENTIAL = 0x8000
# ═══════════════════════════════════════════════════════════════
# BiometricPrompt
# ═══════════════════════════════════════════════════════════════
class BiometricPrompt(Stub):
"""BiometricPrompt — 生物识别提示 (API 28+)"""
class AuthenticationCallback(Stub):
"""BiometricPrompt.AuthenticationCallback — 认证回调"""
def onAuthenticationError(self, errorCode: int, errString: str):
pass
def onAuthenticationSucceeded(self, result: "BiometricPrompt.AuthenticationResult"):
pass
def onAuthenticationFailed(self):
pass
def onAuthenticationAcquired(self, acquireInfo: int):
pass
class AuthenticationResult(Stub):
"""BiometricPrompt.AuthenticationResult — 认证结果"""
def __init__(self, cryptoObject: "BiometricPromptCryptoObject" = None,
authenticationType: int = BiometricConstants.BIOMETRIC_STRONG):
super().__init__()
self._crypto_object = cryptoObject
self._authentication_type = authenticationType
def getCryptoObject(self) -> Optional["BiometricPromptCryptoObject"]:
return self._crypto_object
def getAuthenticationType(self) -> int:
return self._authentication_type
class PromptInfo(Stub):
"""BiometricPrompt.PromptInfo — 提示信息"""
class Builder(Stub):
"""BiometricPrompt.PromptInfo.Builder — 提示信息构建器"""
def __init__(self):
super().__init__()
self._title = ""
self._subtitle = ""
self._description = ""
self._device_credential_allowed = False
self._allowed_authenticators = 0
self._confirmation_required = True
self._negative_button_text = ""
def setTitle(self, title: str) -> "BiometricPrompt.PromptInfo.Builder":
self._title = title
return self
def setSubtitle(self, subtitle: str) -> "BiometricPrompt.PromptInfo.Builder":
self._subtitle = subtitle
return self
def setDescription(self, description: str) -> "BiometricPrompt.PromptInfo.Builder":
self._description = description
return self
def setDeviceCredentialAllowed(self, allowed: bool) -> "BiometricPrompt.PromptInfo.Builder":
self._device_credential_allowed = allowed
return self
def setAllowedAuthenticators(self, authenticators: int) -> "BiometricPrompt.PromptInfo.Builder":
self._allowed_authenticators = authenticators
return self
def setConfirmationRequired(self, required: bool) -> "BiometricPrompt.PromptInfo.Builder":
self._confirmation_required = required
return self
def setNegativeButtonText(self, text: str) -> "BiometricPrompt.PromptInfo.Builder":
self._negative_button_text = text
return self
def build(self) -> "BiometricPrompt.PromptInfo":
info = BiometricPrompt.PromptInfo()
info._title = self._title
info._subtitle = self._subtitle
info._description = self._description
info._device_credential_allowed = self._device_credential_allowed
info._allowed_authenticators = self._allowed_authenticators
info._confirmation_required = self._confirmation_required
info._negative_button_text = self._negative_button_text
return info
def __init__(self):
super().__init__()
self._title = ""
self._subtitle = ""
self._description = ""
self._device_credential_allowed = False
self._allowed_authenticators = 0
self._confirmation_required = True
self._negative_button_text = ""
def getTitle(self) -> str:
return self._title
def getSubtitle(self) -> str:
return self._subtitle
def getDescription(self) -> str:
return self._description
def isDeviceCredentialAllowed(self) -> bool:
return self._device_credential_allowed
def getAllowedAuthenticators(self) -> int:
return self._allowed_authenticators
def isConfirmationRequired(self) -> bool:
return self._confirmation_required
def getNegativeButtonText(self) -> str:
return self._negative_button_text
def __init__(self, context, callback: AuthenticationCallback = None,
handler=None):
super().__init__()
self._context = context
self._callback = callback
self._cancelled = False
def authenticate(self, crypto: "BiometricPromptCryptoObject" = None,
cancel: Any = None, executor=None,
callback: AuthenticationCallback = None):
self._cancelled = False
if callback:
self._callback = callback
if self._callback:
# Simulate failed authentication on desktop
self._callback.onAuthenticationError(
BiometricConstants.BIOMETRIC_ERROR_NO_HARDWARE,
"No biometric hardware available on this device"
)
def authenticate(self, promptInfo: PromptInfo, crypto: "BiometricPromptCryptoObject" = None):
self._cancelled = False
if self._callback:
self._callback.onAuthenticationError(
BiometricConstants.BIOMETRIC_ERROR_NO_HARDWARE,
"No biometric hardware available on this device"
)
def cancelAuthentication(self):
self._cancelled = True
if self._callback:
self._callback.onAuthenticationError(
BiometricConstants.BIOMETRIC_ERROR_CANCELED,
"Authentication cancelled"
)
def cancel(self):
self.cancelAuthentication()
def isCancelled(self) -> bool:
return self._cancelled
# ═══════════════════════════════════════════════════════════════
# BiometricPromptCryptoObject
# ═══════════════════════════════════════════════════════════════
class BiometricPromptCryptoObject(Stub):
"""BiometricPromptCryptoObject — 加密对象"""
def __init__(self, signature=None, cipher=None, mac=None):
super().__init__()
self._signature = signature
self._cipher = cipher
self._mac = mac
def getSignature(self):
return self._signature
def getCipher(self):
return self._cipher
def getMac(self):
return self._mac
# ═══════════════════════════════════════════════════════════════
# BiometricPromptAuthenticationResult
# ═══════════════════════════════════════════════════════════════
BiometricPromptAuthenticationResult = BiometricPrompt.AuthenticationResult
# ═══════════════════════════════════════════════════════════════
# BiometricManager
# ═══════════════════════════════════════════════════════════════
class BiometricManager(Stub):
"""BiometricManager — 生物识别管理器 (API 29+)"""
BIOMETRIC_SUCCESS = BiometricConstants.BIOMETRIC_SUCCESS
BIOMETRIC_ERROR_HW_UNAVAILABLE = BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE
BIOMETRIC_ERROR_NONE_ENROLLED = BiometricConstants.BIOMETRIC_ERROR_NONE_ENROLLED
BIOMETRIC_ERROR_NO_HARDWARE = BiometricConstants.BIOMETRIC_ERROR_NO_HARDWARE
BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = BiometricConstants.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED
BIOMETRIC_ERROR_UNSUPPORTED = BiometricConstants.BIOMETRIC_ERROR_UNSUPPORTED
BIOMETRIC_STATUS_OK = BiometricConstants.BIOMETRIC_STATUS_OK
BIOMETRIC_ERROR_VENDOR = BiometricConstants.BIOMETRIC_ERROR_VENDOR
BIOMETRIC_ERROR_USER_CANCELED = BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED
BIOMETRIC_ERROR_CANCELED = BiometricConstants.BIOMETRIC_ERROR_CANCELED
BIOMETRIC_ERROR_TIMEOUT = BiometricConstants.BIOMETRIC_ERROR_TIMEOUT
BIOMETRIC_ERROR_LOCKOUT = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT
BIOMETRIC_ERROR_LOCKOUT_PERMANENT = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT
# Authenticators
AUTHENTICATOR_BIOMETRIC_STRONG = BiometricConstants.BIOMETRIC_STRONG
AUTHENTICATOR_BIOMETRIC_WEAK = BiometricConstants.BIOMETRIC_WEAK
AUTHENTICATOR_DEVICE_CREDENTIAL = BiometricConstants.DEVICE_CREDENTIAL
def __init__(self, context=None):
super().__init__()
self._context = context
def canAuthenticate(self, authenticators: int = AUTHENTICATOR_BIOMETRIC_STRONG) -> int:
"""Check if biometric authentication is available"""
return BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE
@staticmethod
def fromContext(context) -> "BiometricManager":
"""Get BiometricManager from context"""
return BiometricManager(context)
def toString(self) -> str:
return "BiometricManager(desktop - no hardware)"
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"BiometricManager",
"BiometricPrompt",
"BiometricPromptAuthenticationResult",
"BiometricPromptCryptoObject",
"BiometricConstants",
]
+761
View File
@@ -0,0 +1,761 @@
"""
Android Camera2 组件 — CameraManager, CameraDevice, CaptureRequest 等
方法签名完全对齐 Android SDK
"""
import time
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union
from .._core import Stub, AndroidOnlyError, android_context, HAS_PIL
# ═══════════════════════════════════════════════════════════════
# CameraMetadata
# ═══════════════════════════════════════════════════════════════
class CameraMetadata(Stub):
"""CameraMetadata — 相机元数据基类"""
# CONTROL_AE modes
CONTROL_AE_MODE_OFF = 0
CONTROL_AE_MODE_ON = 1
CONTROL_AE_MODE_ON_AUTO_FLASH = 2
CONTROL_AE_MODE_ON_ALWAYS_FLASH = 3
CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE = 4
CONTROL_AE_MODE_EXTERNAL_FLASH = 5
# CONTROL_AF modes
CONTROL_AF_MODE_OFF = 0
CONTROL_AF_MODE_AUTO = 1
CONTROL_AF_MODE_MACRO = 2
CONTROL_AF_MODE_CONTINUOUS_VIDEO = 3
CONTROL_AF_MODE_CONTINUOUS_PICTURE = 4
CONTROL_AF_MODE_EDOF = 5
# CONTROL_AWB modes
CONTROL_AWB_MODE_OFF = 0
CONTROL_AWB_MODE_AUTO = 1
CONTROL_AWB_MODE_INCANDESCENT = 2
CONTROL_AWB_MODE_FLUORESCENT = 3
CONTROL_AWB_MODE_WARM_FLUORESCENT = 4
CONTROL_AWB_MODE_DAYLIGHT = 5
CONTROL_AWB_MODE_CLOUDY_DAYLIGHT = 6
CONTROL_AWB_MODE_TWILIGHT = 7
CONTROL_AWB_MODE_SHADE = 8
# CONTROL modes
CONTROL_MODE_OFF = 0
CONTROL_MODE_AUTO = 1
CONTROL_MODE_USE_SCENE_MODE = 2
CONTROL_MODE_OFF_KEEP_STATE = 3
# FLASH modes
FLASH_MODE_OFF = 0
FLASH_MODE_SINGLE = 1
FLASH_MODE_TORCH = 2
# STATISTICS_FACE_DETECT modes
STATISTICS_FACE_DETECT_MODE_OFF = 0
STATISTICS_FACE_DETECT_MODE_SIMPLE = 1
STATISTICS_FACE_DETECT_MODE_FULL = 2
# EDGE modes
EDGE_MODE_OFF = 0
EDGE_MODE_FAST = 1
EDGE_MODE_HIGH_QUALITY = 2
EDGE_MODE_ZERO_SHUTTER_LAG = 3
# NOISE_REDUCTION modes
NOISE_REDUCTION_MODE_OFF = 0
NOISE_REDUCTION_MODE_FAST = 1
NOISE_REDUCTION_MODE_HIGH_QUALITY = 2
NOISE_REDUCTION_MODE_MINIMAL = 3
NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG = 4
# TONEMAP modes
TONEMAP_MODE_CONTRAST_CURVE = 0
TONEMAP_MODE_FAST = 1
TONEMAP_MODE_HIGH_QUALITY = 2
TONEMAP_MODE_GAMMA_VALUE = 3
TONEMAP_MODE_PRESET_CURVE = 4
# COLOR_CORRECTION modes
COLOR_CORRECTION_MODE_TRANSFORM_MATRIX = 0
COLOR_CORRECTION_MODE_FAST = 1
COLOR_CORRECTION_MODE_HIGH_QUALITY = 2
# COLOR_CORRECTION_ABERRATION modes
COLOR_CORRECTION_ABERRATION_MODE_OFF = 0
COLOR_CORRECTION_ABERRATION_MODE_FAST = 1
COLOR_CORRECTION_ABERRATION_MODE_HIGH_QUALITY = 2
# CONTROL_AE_PRECAPTURE_TRIGGER
CONTROL_AE_PRECAPTURE_TRIGGER_IDLE = 0
CONTROL_AE_PRECAPTURE_TRIGGER_START = 1
CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL = 2
# CONTROL_EFFECT modes
CONTROL_EFFECT_MODE_OFF = 0
CONTROL_EFFECT_MODE_MONO = 1
CONTROL_EFFECT_MODE_NEGATIVE = 2
CONTROL_EFFECT_MODE_SOLARIZE = 3
CONTROL_EFFECT_MODE_SEPIA = 4
CONTROL_EFFECT_MODE_POSTERIZE = 5
CONTROL_EFFECT_MODE_WHITEBOARD = 6
CONTROL_EFFECT_MODE_BLACKBOARD = 7
CONTROL_EFFECT_MODE_AQUA = 8
# CONTROL_SCENE modes
CONTROL_SCENE_MODE_DISABLED = 0
CONTROL_SCENE_MODE_FACE_PRIORITY = 1
CONTROL_SCENE_MODE_ACTION = 2
CONTROL_SCENE_MODE_PORTRAIT = 3
CONTROL_SCENE_MODE_LANDSCAPE = 4
CONTROL_SCENE_MODE_NIGHT = 5
CONTROL_SCENE_MODE_NIGHT_PORTRAIT = 6
CONTROL_SCENE_MODE_THEATRE = 7
CONTROL_SCENE_MODE_BEACH = 8
CONTROL_SCENE_MODE_SNOW = 9
CONTROL_SCENE_MODE_SUNSET = 10
CONTROL_SCENE_MODE_STEADYPHOTO = 11
CONTROL_SCENE_MODE_FIREWORKS = 12
CONTROL_SCENE_MODE_SPORTS = 13
CONTROL_SCENE_MODE_PARTY = 14
CONTROL_SCENE_MODE_CANDLELIGHT = 15
CONTROL_SCENE_MODE_BARCODE = 16
CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO = 17
CONTROL_SCENE_MODE_HDR = 18
# CONTROL_VIDEO_STABILIZATION modes
CONTROL_VIDEO_STABILIZATION_MODE_OFF = 0
CONTROL_VIDEO_STABILIZATION_MODE_ON = 1
# DISTORTION_CORRECTION modes
DISTORTION_CORRECTION_MODE_OFF = 0
DISTORTION_CORRECTION_MODE_FAST = 1
DISTORTION_CORRECTION_MODE_HIGH_QUALITY = 2
# HOT_PIXEL modes
HOT_PIXEL_MODE_OFF = 0
HOT_PIXEL_MODE_FAST = 1
HOT_PIXEL_MODE_HIGH_QUALITY = 2
# LENS_OPTICAL_STABILIZATION modes
LENS_OPTICAL_STABILIZATION_MODE_OFF = 0
LENS_OPTICAL_STABILIZATION_MODE_ON = 1
# LENS_FACING
LENS_FACING_FRONT = 0
LENS_FACING_BACK = 1
LENS_FACING_EXTERNAL = 2
# INFO_SUPPORTED_HARDWARE_LEVEL
INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0
INFO_SUPPORTED_HARDWARE_LEVEL_FULL = 1
INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY = 2
INFO_SUPPORTED_HARDWARE_LEVEL_3 = 3
INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL = 4
# ═══════════════════════════════════════════════════════════════
# CameraCharacteristics
# ═══════════════════════════════════════════════════════════════
class CameraCharacteristics(CameraMetadata):
"""CameraCharacteristics — 相机特性"""
LENS_FACING = "android.lens.facing"
SENSOR_ORIENTATION = "android.sensor.orientation"
FLASH_INFO_AVAILABLE = "android.flash.info.available"
INFO_SUPPORTED_HARDWARE_LEVEL = "android.info.supportedHardwareLevel"
SCALER_STREAM_CONFIGURATION_MAP = "android.scaler.streamConfigurationMap"
CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES = "android.control.aeAvailableTargetFpsRanges"
CONTROL_AE_AVAILABLE_MODES = "android.control.aeAvailableModes"
CONTROL_AF_AVAILABLE_MODES = "android.control.afAvailableModes"
CONTROL_AVAILABLE_EFFECTS = "android.control.availableEffects"
CONTROL_AVAILABLE_SCENE_MODES = "android.control.availableSceneModes"
CONTROL_MAX_REGIONS_AE = "android.control.maxRegionsAe"
CONTROL_MAX_REGIONS_AF = "android.control.maxRegionsAf"
CONTROL_MAX_REGIONS_AWB = "android.control.maxRegionsAwb"
JPEG_AVAILABLE_THUMBNAIL_SIZES = "android.jpeg.availableThumbnailSizes"
JPEG_MAX_SIZE = "android.jpeg.maxSize"
SENSOR_INFO_PIXEL_ARRAY_SIZE = "android.sensor.info.pixelArraySize"
SENSOR_INFO_ACTIVE_ARRAY_SIZE = "android.sensor.info.activeArraySize"
FLASH_MODE = "android.flash.mode"
def __init__(self, camera_id: str = "0"):
super().__init__()
self._properties: Dict[str, Any] = {
CameraCharacteristics.LENS_FACING: CameraCharacteristics.LENS_FACING_BACK,
CameraCharacteristics.SENSOR_ORIENTATION: 90,
CameraCharacteristics.FLASH_INFO_AVAILABLE: True,
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL:
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP: {
"output_sizes": [(1920, 1080), (1280, 720), (640, 480)],
"input_sizes": [(1920, 1080)],
},
CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES: [(15, 30), (30, 30)],
CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES: [0, 1, 2, 3, 4],
CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES: [0, 1, 2, 3, 4, 5],
CameraCharacteristics.CONTROL_AVAILABLE_EFFECTS: list(range(9)),
CameraCharacteristics.CONTROL_AVAILABLE_SCENE_MODES: list(range(19)),
CameraCharacteristics.CONTROL_MAX_REGIONS_AE: 1,
CameraCharacteristics.CONTROL_MAX_REGIONS_AF: 1,
CameraCharacteristics.CONTROL_MAX_REGIONS_AWB: 0,
CameraCharacteristics.JPEG_AVAILABLE_THUMBNAIL_SIZES: [(320, 240), (160, 120)],
CameraCharacteristics.JPEG_MAX_SIZE: 5000000,
CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE: (4032, 3024),
CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE: (4032, 3024),
}
def get(self, key: str) -> Any:
return self._properties.get(key)
def keys(self) -> Set[str]:
return set(self._properties.keys())
def query(self, key: str) -> Any:
return self._properties.get(key)
# ═══════════════════════════════════════════════════════════════
# CameraId
# ═══════════════════════════════════════════════════════════════
class CameraId(Stub):
"""CameraId — 相机ID封装"""
def __init__(self, id_string: str):
super().__init__()
self._id = id_string
def getId(self) -> str:
return self._id
def toString(self) -> str:
return self._id
def equals(self, other) -> bool:
if isinstance(other, CameraId):
return self._id == other._id
if isinstance(other, str):
return self._id == other
return False
def hashCode(self) -> int:
return hash(self._id)
# ═══════════════════════════════════════════════════════════════
# CameraCharacteristicsKey
# ═══════════════════════════════════════════════════════════════
class CameraCharacteristicsKey(Stub):
"""CameraCharacteristicsKey — 相机特性键"""
def __init__(self, name: str, native_type: type = object):
super().__init__()
self._name = name
self._native_type = native_type
def getName(self) -> str:
return self._name
def getNativeType(self) -> type:
return self._native_type
def equals(self, other) -> bool:
if isinstance(other, CameraCharacteristicsKey):
return self._name == other._name
return False
def hashCode(self) -> int:
return hash(self._name)
def toString(self) -> str:
return f"CameraCharacteristicsKey({self._name})"
# ═══════════════════════════════════════════════════════════════
# CaptureRequest
# ═══════════════════════════════════════════════════════════════
class CaptureRequest(CameraMetadata):
"""CaptureRequest — 捕获请求"""
CONTROL_AE_MODE = "android.control.aeMode"
CONTROL_AF_MODE = "android.control.afMode"
CONTROL_AWB_MODE = "android.control.awbMode"
CONTROL_MODE = "android.control.mode"
FLASH_MODE = "android.flash.mode"
SENSOR_EXPOSURE_TIME = "android.sensor.exposureTime"
SENSOR_SENSITIVITY = "android.sensor.sensitivity"
LENS_FOCUS_DISTANCE = "android.lens.focusDistance"
STATISTICS_FACE_DETECT_MODE = "android.statistics.faceDetectMode"
JPEG_QUALITY = "android.jpeg.quality"
JPEG_ORIENTATION = "android.jpeg.orientation"
JPEG_THUMBNAIL_QUALITY = "android.jpeg.thumbnailQuality"
JPEG_THUMBNAIL_SIZE = "android.jpeg.thumbnailSize"
SCALER_CROP_REGION = "android.scaler.cropRegion"
EDGE_MODE = "android.edge.mode"
NOISE_REDUCTION_MODE = "android.noiseReduction.mode"
TONEMAP_MODE = "android.tonemap.mode"
BLACK_LEVEL_LOCK = "android.blackLevel.lock"
CONTROL_AE_TARGET_FPS_RANGE = "android.control.aeTargetFpsRange"
CONTROL_AE_EXPOSURE_COMPENSATION = "android.control.aeExposureCompensation"
CONTROL_AE_LOCK = "android.control.aeLock"
CONTROL_AE_PRECAPTURE_TRIGGER = "android.control.aePrecaptureTrigger"
CONTROL_AWB_LOCK = "android.control.awbLock"
CONTROL_EFFECT_MODE = "android.control.effectMode"
CONTROL_SCENE_MODE = "android.control.sceneMode"
CONTROL_VIDEO_STABILIZATION_MODE = "android.control.videoStabilizationMode"
COLOR_CORRECTION_MODE = "android.colorCorrection.mode"
COLOR_CORRECTION_GAINS = "android.colorCorrection.gains"
COLOR_CORRECTION_TRANSFORM = "android.colorCorrection.transform"
COLOR_CORRECTION_ABERRATION_MODE = "android.colorCorrection.aberrationMode"
CONTROL_POST_RAW_SENSITIVITY_BOOST = "android.control.postRawSensitivityBoost"
DISTORTION_CORRECTION_MODE = "android.distortionCorrection.mode"
HOT_PIXEL_MODE = "android.hotPixel.mode"
LENS_OPTICAL_STABILIZATION_MODE = "android.lens.opticalStabilizationMode"
REQUEST_PIPELINE_MAX_DEPTH = "android.request.pipelineMaxDepth"
SENSOR_TIMESTAMP = "android.sensor.timestamp"
STATISTICS_LENS_SHADING_MAP_MODE = "android.statistics.lensShadingMapMode"
STATISTICS_OIS_DATA_MODE = "android.statistics.oisDataMode"
STATISTICS_SECTION = "android.statistics.section"
TONEMAP_CURVE = "android.tonemap.curve"
TONEMAP_GAMMA = "android.tonemap.gamma"
class Builder(Stub):
"""CaptureRequest.Builder — 捕获请求构建器"""
def __init__(self, camera_device: "CameraDevice"):
super().__init__()
self._parameters: Dict[str, Any] = {}
self._tag = None
self._camera_device = camera_device
def set(self, key: str, value: Any):
self._parameters[key] = value
def get(self, key: str) -> Any:
return self._parameters.get(key)
def setTag(self, tag: Any):
self._tag = tag
def build(self) -> "CaptureRequest":
req = CaptureRequest()
req._parameters = dict(self._parameters)
req._tag = self._tag
return req
def addTarget(self, surface):
pass
def removeTarget(self, surface):
pass
def __init__(self):
super().__init__()
self._parameters: Dict[str, Any] = {
CaptureRequest.CONTROL_AE_MODE: CameraMetadata.CONTROL_AE_MODE_ON,
CaptureRequest.CONTROL_AF_MODE: CameraMetadata.CONTROL_AF_MODE_AUTO,
CaptureRequest.CONTROL_AWB_MODE: CameraMetadata.CONTROL_AWB_MODE_AUTO,
CaptureRequest.CONTROL_MODE: CameraMetadata.CONTROL_MODE_AUTO,
CaptureRequest.FLASH_MODE: CameraMetadata.FLASH_MODE_OFF,
CaptureRequest.JPEG_QUALITY: 95,
CaptureRequest.JPEG_ORIENTATION: 0,
}
self._tag = None
self._targets: List[Any] = []
def get(self, key: str) -> Any:
return self._parameters.get(key)
def keys(self) -> Set[str]:
return set(self._parameters.keys())
def getTag(self) -> Any:
return self._tag
# ═══════════════════════════════════════════════════════════════
# CaptureResult
# ═══════════════════════════════════════════════════════════════
class CaptureResult(CameraMetadata):
"""CaptureResult — 捕获结果"""
# Same keys as CaptureRequest
CONTROL_AE_MODE = CaptureRequest.CONTROL_AE_MODE
CONTROL_AF_MODE = CaptureRequest.CONTROL_AF_MODE
CONTROL_AWB_MODE = CaptureRequest.CONTROL_AWB_MODE
CONTROL_MODE = CaptureRequest.CONTROL_MODE
FLASH_MODE = CaptureRequest.FLASH_MODE
SENSOR_EXPOSURE_TIME = CaptureRequest.SENSOR_EXPOSURE_TIME
SENSOR_SENSITIVITY = CaptureRequest.SENSOR_SENSITIVITY
LENS_FOCUS_DISTANCE = CaptureRequest.LENS_FOCUS_DISTANCE
STATISTICS_FACE_DETECT_MODE = CaptureRequest.STATISTICS_FACE_DETECT_MODE
JPEG_QUALITY = CaptureRequest.JPEG_QUALITY
JPEG_ORIENTATION = CaptureRequest.JPEG_ORIENTATION
JPEG_THUMBNAIL_QUALITY = CaptureRequest.JPEG_THUMBNAIL_QUALITY
JPEG_THUMBNAIL_SIZE = CaptureRequest.JPEG_THUMBNAIL_SIZE
SCALER_CROP_REGION = CaptureRequest.SCALER_CROP_REGION
EDGE_MODE = CaptureRequest.EDGE_MODE
NOISE_REDUCTION_MODE = CaptureRequest.NOISE_REDUCTION_MODE
TONEMAP_MODE = CaptureRequest.TONEMAP_MODE
BLACK_LEVEL_LOCK = CaptureRequest.BLACK_LEVEL_LOCK
CONTROL_AE_TARGET_FPS_RANGE = CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE
CONTROL_AE_EXPOSURE_COMPENSATION = CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION
CONTROL_AE_LOCK = CaptureRequest.CONTROL_AE_LOCK
CONTROL_AE_PRECAPTURE_TRIGGER = CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER
CONTROL_AWB_LOCK = CaptureRequest.CONTROL_AWB_LOCK
CONTROL_EFFECT_MODE = CaptureRequest.CONTROL_EFFECT_MODE
CONTROL_SCENE_MODE = CaptureRequest.CONTROL_SCENE_MODE
CONTROL_VIDEO_STABILIZATION_MODE = CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE
COLOR_CORRECTION_MODE = CaptureRequest.COLOR_CORRECTION_MODE
COLOR_CORRECTION_GAINS = CaptureRequest.COLOR_CORRECTION_GAINS
COLOR_CORRECTION_TRANSFORM = CaptureRequest.COLOR_CORRECTION_TRANSFORM
COLOR_CORRECTION_ABERRATION_MODE = CaptureRequest.COLOR_CORRECTION_ABERRATION_MODE
CONTROL_POST_RAW_SENSITIVITY_BOOST = CaptureRequest.CONTROL_POST_RAW_SENSITIVITY_BOOST
DISTORTION_CORRECTION_MODE = CaptureRequest.DISTORTION_CORRECTION_MODE
HOT_PIXEL_MODE = CaptureRequest.HOT_PIXEL_MODE
LENS_OPTICAL_STABILIZATION_MODE = CaptureRequest.LENS_OPTICAL_STABILIZATION_MODE
REQUEST_PIPELINE_MAX_DEPTH = CaptureRequest.REQUEST_PIPELINE_MAX_DEPTH
SENSOR_TIMESTAMP = CaptureRequest.SENSOR_TIMESTAMP
STATISTICS_LENS_SHADING_MAP_MODE = CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE
STATISTICS_OIS_DATA_MODE = CaptureRequest.STATISTICS_OIS_DATA_MODE
STATISTICS_SECTION = CaptureRequest.STATISTICS_SECTION
TONEMAP_CURVE = CaptureRequest.TONEMAP_CURVE
TONEMAP_GAMMA = CaptureRequest.TONEMAP_GAMMA
def __init__(self, request: CaptureRequest = None):
super().__init__()
self._results: Dict[str, Any] = {
CaptureResult.CONTROL_AE_MODE: CameraMetadata.CONTROL_AE_MODE_ON,
CaptureResult.CONTROL_AF_MODE: CameraMetadata.CONTROL_AF_MODE_AUTO,
CaptureResult.CONTROL_AWB_MODE: CameraMetadata.CONTROL_AWB_MODE_AUTO,
CaptureResult.CONTROL_MODE: CameraMetadata.CONTROL_MODE_AUTO,
CaptureResult.FLASH_MODE: CameraMetadata.FLASH_MODE_OFF,
CaptureResult.SENSOR_EXPOSURE_TIME: 33333333,
CaptureResult.SENSOR_SENSITIVITY: 100,
CaptureResult.SENSOR_TIMESTAMP: int(time.time() * 1e9),
}
self._request = request
self._sequence_id = -1
self._frame_number = -1
def get(self, key: str) -> Any:
return self._results.get(key)
def keys(self) -> Set[str]:
return set(self._results.keys())
def getRequest(self) -> Optional[CaptureRequest]:
return self._request
def getSequenceId(self) -> int:
return self._sequence_id
def getFrameNumber(self) -> int:
return self._frame_number
# ═══════════════════════════════════════════════════════════════
# TotalCaptureResult
# ═══════════════════════════════════════════════════════════════
class TotalCaptureResult(CaptureResult):
"""TotalCaptureResult — 完整捕获结果"""
def __init__(self, request: CaptureRequest = None,
partial_results: List[CaptureResult] = None):
super().__init__(request)
self._partial_results = partial_results or []
def getPartialResults(self) -> List[CaptureResult]:
return list(self._partial_results)
# ═══════════════════════════════════════════════════════════════
# CameraCaptureSession
# ═══════════════════════════════════════════════════════════════
class CameraCaptureSession(Stub):
"""CameraCaptureSession — 捕获会话"""
class StateCallback(Stub):
"""CameraCaptureSession.StateCallback — 会话状态回调"""
def onConfigured(self, session: "CameraCaptureSession"):
pass
def onConfigureFailed(self, session: "CameraCaptureSession"):
pass
def onReady(self, session: "CameraCaptureSession"):
pass
def onActive(self, session: "CameraCaptureSession"):
pass
def onClosed(self, session: "CameraCaptureSession"):
pass
def onSurfacePrepared(self, session: "CameraCaptureSession", surface):
pass
class CaptureCallback(Stub):
"""CameraCaptureSession.CaptureCallback — 捕获回调"""
def onCaptureStarted(self, session: "CameraCaptureSession", request: CaptureRequest,
timestamp: int, frameNumber: int):
pass
def onCaptureCompleted(self, session: "CameraCaptureSession", request: CaptureRequest,
result: TotalCaptureResult):
pass
def onCaptureFailed(self, session: "CameraCaptureSession", request: CaptureRequest,
failure):
pass
def onCaptureProgressed(self, session: "CameraCaptureSession", request: CaptureRequest,
result: CaptureResult):
pass
def onCaptureSequenceCompleted(self, session: "CameraCaptureSession",
sequenceId: int, frameNumber: int):
pass
def onCaptureSequenceAborted(self, session: "CameraCaptureSession", sequenceId: int):
pass
def onCaptureBufferLost(self, session: "CameraCaptureSession", request: CaptureRequest,
target, frameNumber: int):
pass
def __init__(self, device: "CameraDevice"):
super().__init__()
self._device = device
self._closed = False
self._repeating_request = None
self._state_callback = None
self._session_id = int(time.time() * 1000)
def capture(self, request: CaptureRequest, callback: CaptureCallback = None,
handler=None) -> int:
seq_id = int(time.time() * 1000)
result = TotalCaptureResult(request)
if callback:
callback.onCaptureStarted(self, request, int(time.time() * 1e9), 0)
callback.onCaptureCompleted(self, request, result)
callback.onCaptureSequenceCompleted(self, seq_id, 0)
return seq_id
def setRepeatingRequest(self, request: CaptureRequest, callback: CaptureCallback = None,
handler=None) -> int:
self._repeating_request = request
seq_id = int(time.time() * 1000)
result = TotalCaptureResult(request)
if callback:
callback.onCaptureStarted(self, request, int(time.time() * 1e9), 0)
callback.onCaptureCompleted(self, request, result)
return seq_id
def stopRepeatingRequest(self) -> None:
self._repeating_request = None
def abortCaptures(self) -> None:
pass
def close(self):
self._closed = True
def getDevice(self) -> "CameraDevice":
return self._device
def isClosed(self) -> bool:
return self._closed
# ═══════════════════════════════════════════════════════════════
# CameraDevice
# ═══════════════════════════════════════════════════════════════
class CameraDevice(Stub):
"""CameraDevice — 相机设备"""
STATE_OPENED = 0
STATE_CLOSED = 3
STATE_DISCONNECTED = 1
STATE_ERROR = 2
class StateCallback(Stub):
"""CameraDevice.StateCallback — 设备状态回调"""
def onOpened(self, device: "CameraDevice"):
pass
def onClosed(self, device: "CameraDevice"):
pass
def onDisconnected(self, device: "CameraDevice"):
pass
def onError(self, device: "CameraDevice", error: int):
pass
def __init__(self, camera_id: str = "0"):
super().__init__()
self._id = camera_id
self._state = CameraDevice.STATE_OPENED
self._state_callback = None
self._sessions: List[CameraCaptureSession] = []
def getId(self) -> str:
return self._id
def createCaptureRequest(self, templateType: int) -> CaptureRequest.Builder:
return CaptureRequest.Builder(self)
def createCaptureSession(self, outputs: List[Any],
callback: CameraCaptureSession.StateCallback,
handler=None):
session = CameraCaptureSession(self)
session._state_callback = callback
self._sessions.append(session)
if callback:
callback.onConfigured(session)
return session
def close(self):
self._state = CameraDevice.STATE_CLOSED
for session in self._sessions:
session.close()
def getState(self) -> int:
return self._state
def toString(self) -> str:
return f"CameraDevice@{self._id}"
# ═══════════════════════════════════════════════════════════════
# CameraManager
# ═══════════════════════════════════════════════════════════════
class CameraManager(Stub):
"""CameraManager — 相机管理器"""
def __init__(self):
super().__init__()
self._cameras: Dict[str, CameraCharacteristics] = {
"0": CameraCharacteristics("0"),
}
self._open_devices: Dict[str, CameraDevice] = {}
self._availability_callbacks: List[Any] = []
self._torch_callbacks: List[Any] = []
def getCameraIdList(self) -> List[str]:
return list(self._cameras.keys())
def getCameraCharacteristics(self, cameraId: str) -> CameraCharacteristics:
if cameraId not in self._cameras:
raise AndroidOnlyError(f"Camera {cameraId} not available")
return self._cameras[cameraId]
def openCamera(self, cameraId: str, callback: CameraDevice.StateCallback,
handler=None):
if cameraId not in self._cameras:
if callback:
callback.onError(None, CameraDevice.STATE_ERROR)
return
device = CameraDevice(cameraId)
self._open_devices[cameraId] = device
if callback:
callback.onOpened(device)
def registerAvailabilityCallback(self, callback):
self._availability_callbacks.append(callback)
def unregisterAvailabilityCallback(self, callback):
if callback in self._availability_callbacks:
self._availability_callbacks.remove(callback)
def registerTorchCallback(self, callback, handler=None):
self._torch_callbacks.append(callback)
def unregisterTorchCallback(self, callback):
if callback in self._torch_callbacks:
self._torch_callbacks.remove(callback)
def setTorchMode(self, cameraId: str, enabled: bool):
pass
# ═══════════════════════════════════════════════════════════════
# CameraOfflineSession
# ═══════════════════════════════════════════════════════════════
class CameraOfflineSession(CameraCaptureSession):
"""CameraOfflineSession — 离线会话 (API 30+)"""
def __init__(self, device: CameraDevice):
super().__init__(device)
def switchToOffline(self):
pass
# ═══════════════════════════════════════════════════════════════
# CameraExtensionSession
# ═══════════════════════════════════════════════════════════════
class CameraExtensionSession(Stub):
"""CameraExtensionSession — 扩展会话 (API 31+)"""
class StateCallback(Stub):
def onConfigured(self, session: "CameraExtensionSession"):
pass
def onConfigureFailed(self, session: "CameraExtensionSession"):
pass
def onClosed(self, session: "CameraExtensionSession"):
pass
class ExtensionCaptureCallback(Stub):
def onCaptureStarted(self, session: "CameraExtensionSession", request: CaptureRequest,
timestamp: int):
pass
def onCaptureProcessStarted(self, session: "CameraExtensionSession", request: CaptureRequest):
pass
def onCaptureFailed(self, session: "CameraExtensionSession", request: CaptureRequest):
pass
def onCaptureResultAvailable(self, session: "CameraExtensionSession",
request: CaptureRequest, result: TotalCaptureResult):
pass
def __init__(self):
super().__init__()
self._closed = False
def close(self):
self._closed = True
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"CameraManager", "CameraDevice", "CameraCaptureSession",
"CaptureRequest", "CaptureResult", "CameraCharacteristics",
"CameraMetadata", "CameraId", "CameraCharacteristicsKey",
"TotalCaptureResult", "CameraOfflineSession", "CameraExtensionSession",
]
+804
View File
@@ -0,0 +1,804 @@
"""
Android InputMethod 组件 — InputMethodService, Keyboard, KeyboardView, Key 等
方法签名完全对齐 Android SDK
"""
import sys
from typing import Any, Callable, Dict, List, Optional, Tuple
from .._core import Stub, AndroidOnlyError, android_context
# ═══════════════════════════════════════════════════════════════
# InputMethod
# ═══════════════════════════════════════════════════════════════
class InputMethod(Stub):
"""InputMethod — 输入法接口"""
def __init__(self):
super().__init__()
def createSession(self, callback: Any) -> Optional["InputMethodSession"]:
"""创建输入法会话"""
return None
def startInput(self, input_connection: Any, editor_info: Any) -> None:
"""开始输入"""
pass
def restartInput(self, input_connection: Any, editor_info: Any) -> None:
"""重启输入"""
pass
def dispatchKeyEvent(self, event: Any) -> None:
"""分发按键事件"""
pass
def dispatchTrackballEvent(self, event: Any) -> None:
"""分发轨迹球事件"""
pass
def dispatchGenericMotionEvent(self, event: Any) -> None:
"""分发通用运动事件"""
pass
# ═══════════════════════════════════════════════════════════════
# InputMethodSession
# ═══════════════════════════════════════════════════════════════
class InputMethodSession(Stub):
"""InputMethodSession — 输入法会话接口"""
def __init__(self):
super().__init__()
def finishInput(self):
"""结束输入"""
pass
def updateSelection(self, old_sel_start: int, old_sel_end: int,
new_sel_start: int, new_sel_end: int,
candidates_start: int, candidates_end: int):
"""更新选区"""
pass
def updateCursor(self, old_cursor: Any, new_cursor: Any):
"""更新光标"""
pass
def displayCompletions(self, completions: List["CompletionInfo"]):
"""显示补全"""
pass
def appPrivateCommand(self, action: str, data: Any):
"""应用私有命令"""
pass
def viewClicked(self, focus_changed: bool):
"""视图点击"""
pass
def updateExtractedText(self, token: int, extracted_text: Any):
"""更新提取文本"""
pass
def dispatchKeyEvent(self, key_code: int, event: Any) -> None:
"""分发按键事件"""
pass
def dispatchTrackballEvent(self, event: Any) -> None:
pass
def dispatchGenericMotionEvent(self, event: Any) -> None:
pass
def toggleSoftInput(self, show_flags: int, hide_flags: int):
"""切换软键盘"""
pass
def notifyImeHidden(self):
"""通知输入法隐藏"""
pass
def removeImeSurface(self):
"""移除输入法界面"""
pass
# ═══════════════════════════════════════════════════════════════
# AbstractInputMethodService
# ═══════════════════════════════════════════════════════════════
class AbstractInputMethodService(Stub):
"""AbstractInputMethodService — 抽象输入法服务基类"""
def __init__(self):
super().__init__()
self._input_method: Optional[InputMethod] = None
self._input_method_session: Optional[InputMethodSession] = None
def onCreate(self):
"""服务创建"""
pass
def onDestroy(self):
"""服务销毁"""
pass
def onBind(self, intent: Any) -> Any:
"""绑定"""
return None
def onUnbind(self, intent: Any) -> bool:
"""解绑"""
return False
def onCreateInputMethod(self) -> InputMethod:
"""创建输入法实例"""
return InputMethod()
def onCreateInputMethodSession(self) -> InputMethodSession:
"""创建输入法会话"""
return InputMethodSession()
def getKeyDispatcherState(self) -> Any:
"""获取按键调度状态"""
return None
# ═══════════════════════════════════════════════════════════════
# InputMethodImpl
# ═══════════════════════════════════════════════════════════════
class InputMethodImpl(InputMethod):
"""InputMethodImpl — 输入法实现"""
def __init__(self):
super().__init__()
self._session: Optional[InputMethodSession] = None
def createSession(self, callback: Any) -> Optional["InputMethodSession"]:
self._session = InputMethodSessionImpl()
return self._session
def startInput(self, input_connection: Any, editor_info: Any):
sys.stderr.write("[InputMethodImpl] startInput\n")
def restartInput(self, input_connection: Any, editor_info: Any):
sys.stderr.write("[InputMethodImpl] restartInput\n")
def dispatchKeyEvent(self, event: Any):
sys.stderr.write("[InputMethodImpl] dispatchKeyEvent\n")
def dispatchTrackballEvent(self, event: Any):
pass
def dispatchGenericMotionEvent(self, event: Any):
pass
# ═══════════════════════════════════════════════════════════════
# InputMethodSessionImpl
# ═══════════════════════════════════════════════════════════════
class InputMethodSessionImpl(InputMethodSession):
"""InputMethodSessionImpl — 输入法会话实现"""
def __init__(self):
super().__init__()
def finishInput(self):
sys.stderr.write("[InputMethodSessionImpl] finishInput\n")
def updateSelection(self, old_sel_start: int, old_sel_end: int,
new_sel_start: int, new_sel_end: int,
candidates_start: int, candidates_end: int):
pass
def displayCompletions(self, completions: List["CompletionInfo"]):
pass
def dispatchKeyEvent(self, key_code: int, event: Any):
pass
# ═══════════════════════════════════════════════════════════════
# InputMethodService
# ═══════════════════════════════════════════════════════════════
class InputMethodService(AbstractInputMethodService):
"""InputMethodService — 输入法服务"""
SHOW_EXPLICIT = 0x0001
SHOW_IMPLICIT = 0x0002
def __init__(self):
super().__init__()
self._input_view: Optional[Any] = None
self._candidates_view: Optional[Any] = None
self._input_connection: Optional[Any] = None
self._input_editor_info: Optional["EditorInfo"] = None
self._input_binding: Optional["InputBinding"] = None
self._input_view_shown: bool = False
self._fullscreen_mode: bool = False
self._extract_to_fullscreen_mode: bool = False
self._is_fullscreen: bool = False
self._keyboard: Optional["Keyboard"] = None
self._settings_observer: Any = None
self._candidates_view_shown: bool = False
self._show_input_requested: bool = False
self._token: Any = None
self._window_visible: bool = False
self._input_started: bool = False
def onCreate(self):
"""创建服务"""
sys.stderr.write("[InputMethodService] onCreate\n")
def onCreateInputView(self) -> Optional[Any]:
"""创建输入视图"""
return None
def onCreateCandidatesView(self) -> Optional[Any]:
"""创建候选视图"""
return None
def onStartInputView(self, info: "EditorInfo", restarting: bool):
"""开始输入视图"""
pass
def onFinishInputView(self, finishing_input: bool):
"""结束输入视图"""
pass
def onStartInput(self, attribute: "EditorInfo", restarting: bool):
"""开始输入"""
self._input_editor_info = attribute
self._input_started = True
def onFinishInput(self):
"""结束输入"""
self._input_started = False
def onBindInput(self, binding: "InputBinding"):
"""绑定输入"""
self._input_binding = binding
def onUnbindInput(self) -> bool:
"""解绑输入"""
self._input_binding = None
return False
def onDestroy(self):
"""销毁"""
self._input_view = None
self._candidates_view = None
self._input_connection = None
def showWindow(self, show_flags: int):
"""显示窗口"""
self._window_visible = True
sys.stderr.write(f"[InputMethodService] showWindow: flags={show_flags}\n")
def hideWindow(self):
"""隐藏窗口"""
self._window_visible = False
def setInputView(self, view: Any):
"""设置输入视图"""
self._input_view = view
def setCandidatesView(self, view: Any):
"""设置候选视图"""
self._candidates_view = view
def updateInputViewShown(self):
"""更新输入视图显示状态"""
pass
def updateFullscreenMode(self):
"""更新全屏模式"""
pass
def updateExtractToFullscreenMode(self):
"""更新提取全屏模式"""
pass
def requestHideSelf(self, flags: int):
"""请求隐藏自身"""
sys.stderr.write(f"[InputMethodService] requestHideSelf: flags={flags}\n")
def switchInputMethod(self, id: str):
"""切换输入法"""
sys.stderr.write(f"[InputMethodService] switchInputMethod: id={id}\n")
def sendDefaultEditorAction(self, from_enter_key: bool):
"""发送默认编辑器动作"""
pass
def sendDownUpKeyEvents(self, key_event_code: int):
"""发送按键按下和释放事件"""
pass
def getCurrentInputBinding(self) -> Optional["InputBinding"]:
"""获取当前输入绑定"""
return self._input_binding
def getCurrentInputEditorInfo(self) -> Optional["EditorInfo"]:
"""获取当前编辑器信息"""
return self._input_editor_info
def getCurrentInputConnection(self) -> Optional["InputConnection"]:
"""获取当前输入连接"""
return self._input_connection
def getTextForImeAction(self, ime_options: int) -> str:
"""获取IME动作的文本"""
action_map = {
0x0001: "None",
0x0002: "Go",
0x0003: "Search",
0x0004: "Send",
0x0005: "Next",
0x0006: "Done",
0x0007: "Previous",
}
action = ime_options & 0x000000FF
return action_map.get(action, "Unknown")
def doStartInput(self, input_connection: Any, editor_info: "EditorInfo", restarting: bool):
"""执行启动输入"""
self._input_connection = input_connection
self._input_editor_info = editor_info
self.onStartInput(editor_info, restarting)
if self._window_visible:
self.onStartInputView(editor_info, restarting)
def doFinishInput(self):
"""执行结束输入"""
self.onFinishInput()
self.onFinishInputView(True)
def onEvaluateInputViewShown(self) -> bool:
"""评估是否显示输入视图"""
return self._input_view is not None
def onEvaluateFullscreenMode(self) -> bool:
"""评估是否全屏模式"""
return self._fullscreen_mode
def onConfigureWindow(self, win: Any, is_fullscreen: bool, is_candidates: bool):
"""配置窗口"""
pass
def isInputViewShown(self) -> bool:
"""输入视图是否已显示"""
return self._input_view_shown
def updateWindow(self, show_input: bool = False):
"""更新窗口"""
pass
def setExtractView(self, view: Any):
"""设置提取视图"""
pass
def getExtractView(self) -> Optional[Any]:
"""获取提取视图"""
return None
def getInputView(self) -> Optional[Any]:
"""获取输入视图"""
return self._input_view
def getCandidatesView(self) -> Optional[Any]:
"""获取候选视图"""
return self._candidates_view
def isFullscreenMode(self) -> bool:
"""是否全屏模式"""
return self._is_fullscreen
def onDisplayCompletions(self, completions: List["CompletionInfo"]):
"""显示补全"""
pass
def onUpdateSelection(self, old_sel_start: int, old_sel_end: int,
new_sel_start: int, new_sel_end: int,
candidates_start: int, candidates_end: int):
"""更新选区"""
pass
def onViewClicked(self, focus_changed: bool):
"""视图点击"""
pass
def onWindowShown(self):
"""窗口显示"""
self._window_visible = True
def onWindowHidden(self):
"""窗口隐藏"""
self._window_visible = False
def setKeyboard(self, keyboard: "Keyboard"):
"""设置键盘"""
self._keyboard = keyboard
def getKeyboard(self) -> Optional["Keyboard"]:
"""获取键盘"""
return self._keyboard
# ═══════════════════════════════════════════════════════════════
# Key
# ═══════════════════════════════════════════════════════════════
class Key(Stub):
"""Key — 键盘按键"""
def __init__(self, row: "KeyboardRow" = None):
super().__init__()
self.codes: List[int] = []
self.label: Optional[str] = None
self.icon: Any = None
self.sticky: bool = False
self.modifier: bool = False
self.repeatable: bool = False
self.edgeFlags: int = 0
self.popupCharacters: Optional[str] = None
self.popupResId: int = 0
self._x: int = 0
self._y: int = 0
self._width: int = 0
self._height: int = 0
self._gap: int = 0
self._row: Optional["KeyboardRow"] = row
self._pressed: bool = False
self._on: bool = False
def getCode(self) -> int:
"""获取按键码"""
return self.codes[0] if self.codes else 0
def isPressed(self) -> bool:
"""是否按下"""
return self._pressed
def setPressed(self, pressed: bool):
"""设置按下状态"""
self._pressed = pressed
def isOn(self) -> bool:
"""是否开启"""
return self._on
def setOn(self, on_state: bool):
"""设置开启状态"""
self._on = on_state
def squaredDistance(self, x: int, y: int) -> int:
"""计算距离平方"""
center_x = self._x + self._width // 2
center_y = self._y + self._height // 2
return (x - center_x) ** 2 + (y - center_y) ** 2
def hitTest(self, x: int, y: int) -> bool:
"""命中测试"""
return (self._x <= x <= self._x + self._width and
self._y <= y <= self._y + self._height)
def getX(self) -> int:
return self._x
def getY(self) -> int:
return self._y
def getWidth(self) -> int:
return self._width
def getHeight(self) -> int:
return self._height
def toString(self) -> str:
return f"Key(code={self.getCode()}, label={self.label})"
# ═══════════════════════════════════════════════════════════════
# KeyboardRow
# ═══════════════════════════════════════════════════════════════
class KeyboardRow(Stub):
"""KeyboardRow — 键盘行"""
def __init__(self, keyboard: "Keyboard" = None):
super().__init__()
self._keys: List[Key] = []
self._keyboard: Optional["Keyboard"] = keyboard
self._default_width: int = 0
self._default_height: int = 0
self._row_edge_flags: int = 0
self._mode: str = ""
def getKeys(self) -> List[Key]:
return list(self._keys)
def addKey(self, key: Key):
self._keys.append(key)
def getKeyboard(self) -> Optional["Keyboard"]:
return self._keyboard
def getDefaultWidth(self) -> int:
return self._default_width
def getDefaultHeight(self) -> int:
return self._default_height
def toString(self) -> str:
return f"KeyboardRow(keys={len(self._keys)})"
# ═══════════════════════════════════════════════════════════════
# Keyboard
# ═══════════════════════════════════════════════════════════════
class Keyboard(Stub):
"""Keyboard — 键盘布局"""
def __init__(self, context: Any = None, xml_layout_res_id: int = 0):
super().__init__()
self._keys: List[Key] = []
self._rows: List[KeyboardRow] = []
self._modifier_keys: List[Key] = []
self._height: int = 0
self._min_width: int = 0
self._key_width: int = 0
self._key_height: int = 0
self._horizontal_gap: int = 0
self._vertical_gap: int = 0
self._shift_keys: List[Key] = []
self._alt_keys: List[Key] = []
def getKeys(self) -> List[Key]:
return list(self._keys)
def getHeight(self) -> int:
return self._height
def getMinWidth(self) -> int:
return self._min_width
def getKeyWidth(self) -> int:
return self._key_width
def getKeyHeight(self) -> int:
return self._key_height
def getHorizontalGap(self) -> int:
return self._horizontal_gap
def getVerticalGap(self) -> int:
return self._vertical_gap
def getModifierKeys(self) -> List[Key]:
return list(self._modifier_keys)
def getNearestKeys(self, x: int, y: int) -> List[Key]:
"""获取最近的按键"""
nearest: List[Key] = []
for key in self._keys:
if key.hitTest(x, y):
nearest.append(key)
return nearest
def setKeys(self, keys: List[Key]):
"""设置按键列表"""
self._keys = keys
def getRows(self) -> List[KeyboardRow]:
return list(self._rows)
def setShifted(self, shift_state: bool):
"""设置Shift状态"""
pass
def isShifted(self) -> bool:
"""是否Shift状态"""
return False
def getShiftKeyIndex(self) -> int:
"""获取Shift按键索引"""
for i, key in enumerate(self._keys):
if key.modifier:
return i
return -1
def setVerticalGap(self, gap: int):
self._vertical_gap = gap
def setHorizontalGap(self, gap: int):
self._horizontal_gap = gap
def setKeyWidth(self, width: int):
self._key_width = width
def setKeyHeight(self, height: int):
self._key_height = height
def toString(self) -> str:
return f"Keyboard(keys={len(self._keys)}, rows={len(self._rows)})"
# ═══════════════════════════════════════════════════════════════
# KeyboardView
# ═══════════════════════════════════════════════════════════════
class KeyboardView(Stub):
"""KeyboardView — 键盘视图"""
class OnKeyboardActionListener(Stub):
"""OnKeyboardActionListener — 键盘动作监听器"""
def onKey(self, primary_code: int):
pass
def onPress(self, primary_code: int):
pass
def onRelease(self, primary_code: int):
pass
def onText(self, text: str):
pass
def swipeLeft(self):
pass
def swipeRight(self):
pass
def swipeDown(self):
pass
def swipeUp(self):
pass
def __init__(self, context: Any = None, attrs: Any = None):
super().__init__()
self._keyboard: Optional[Keyboard] = None
self._on_keyboard_action_listener: Optional["KeyboardView.OnKeyboardActionListener"] = None
self._preview_enabled: bool = True
self._proximity_correction_enabled: bool = True
self._vertical_correction: int = 0
self._key_text_color: int = 0xFF000000
self._key_text_size: int = 20
self._shadow_color: int = 0x00000000
self._shadow_radius: float = 0.0
self._background_color: int = 0xFF888888
def setKeyboard(self, keyboard: Keyboard):
self._keyboard = keyboard
def getKeyboard(self) -> Optional[Keyboard]:
return self._keyboard
def onDraw(self, canvas: Any):
"""绘制键盘"""
pass
def setOnKeyboardActionListener(self, listener: "KeyboardView.OnKeyboardActionListener"):
self._on_keyboard_action_listener = listener
def getOnKeyboardActionListener(self) -> Optional["KeyboardView.OnKeyboardActionListener"]:
return self._on_keyboard_action_listener
def setPreviewEnabled(self, preview_enabled: bool):
self._preview_enabled = preview_enabled
def isPreviewEnabled(self) -> bool:
return self._preview_enabled
def setProximityCorrectionEnabled(self, enabled: bool):
self._proximity_correction_enabled = enabled
def isProximityCorrectionEnabled(self) -> bool:
return self._proximity_correction_enabled
def setVerticalCorrection(self, vertical_correction: int):
self._vertical_correction = vertical_correction
def getVerticalCorrection(self) -> int:
return self._vertical_correction
def setKeyTextColor(self, color: int):
self._key_text_color = color
def setKeyTextSize(self, size: int):
self._key_text_size = size
def setShadowColor(self, color: int):
self._shadow_color = color
def setShadowRadius(self, radius: float):
self._shadow_radius = radius
def setBackgroundColor(self, color: int):
self._background_color = color
def invalidateAllKeys(self):
"""使所有按键失效"""
pass
def invalidateKey(self, key_index: int):
"""使指定按键失效"""
pass
def onClick(self, key: Key):
"""点击按键"""
if self._on_keyboard_action_listener and key.codes:
self._on_keyboard_action_listener.onKey(key.codes[0])
def onLongPress(self, key: Key) -> bool:
"""长按按键"""
if key.popupCharacters:
return True
return False
def closing(self):
"""关闭"""
pass
def handleBack(self) -> bool:
"""处理返回键"""
return False
def setKeys(self, keys: List[Key]):
"""设置按键"""
if self._keyboard:
self._keyboard.setKeys(keys)
# ═══════════════════════════════════════════════════════════════
# KeyEventCallback
# ═══════════════════════════════════════════════════════════════
class KeyEventCallback(Stub):
"""KeyEventCallback — 按键事件回调"""
def onKeyDown(self, key_code: int, event: Any) -> bool:
"""按键按下"""
return False
def onKeyLongPress(self, key_code: int, event: Any) -> bool:
"""按键长按"""
return False
def onKeyUp(self, key_code: int, event: Any) -> bool:
"""按键释放"""
return False
def onKeyMultiple(self, key_code: int, count: int, event: Any) -> bool:
"""按键重复"""
return False
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"InputMethodService",
"InputMethodImpl",
"InputMethodSessionImpl",
"AbstractInputMethodService",
"InputMethod",
"InputMethodSession",
"Keyboard",
"KeyboardView",
"Key",
"KeyEventCallback",
"KeyboardRow",
]
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+390
View File
@@ -0,0 +1,390 @@
"""
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",
]
File diff suppressed because it is too large. Load diff
+654
View File
@@ -0,0 +1,654 @@
"""
Android Preference 组件 — Preference, PreferenceScreen, PreferenceManager 等
方法签名完全对齐 Android SDK
"""
import os
import sys
import json
from typing import Any, Callable, 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,
)
# ═══════════════════════════════════════════════════════════════
# PreferenceChangeListener
# ═══════════════════════════════════════════════════════════════
class PreferenceChangeListener(Stub):
"""OnPreferenceChangeListener — 偏好变化监听器"""
def onPreferenceChange(self, preference: "Preference", new_value: Any) -> bool:
return True
class PreferenceOnPreferenceClickListener(Stub):
"""OnPreferenceClickListener — 偏好点击监听器"""
def onPreferenceClick(self, preference: "Preference") -> bool:
return True
# ═══════════════════════════════════════════════════════════════
# PreferenceManager
# ═══════════════════════════════════════════════════════════════
class PreferenceManager(Stub):
"""PreferenceManager — 偏好管理器"""
KEY_HAS_SET_DEFAULT_VALUES = "_has_set_default_values"
def __init__(self, context: Any):
super().__init__()
self._context = context
self._shared_prefs_name: str = f"{_package_name}_preferences"
self._shared_prefs_mode: int = 0
self._prefs_data: Dict[str, Any] = {}
self._prefs_path = os.path.join(_app_data_dir, "shared_prefs",
f"{self._shared_prefs_name}.json")
self._load()
def _load(self):
os.makedirs(os.path.dirname(self._prefs_path), exist_ok=True)
if os.path.exists(self._prefs_path):
try:
with open(self._prefs_path, "r") as f:
self._prefs_data = json.load(f)
except (json.JSONDecodeError, OSError):
self._prefs_data = {}
else:
self._prefs_data = {}
def _save(self):
os.makedirs(os.path.dirname(self._prefs_path), exist_ok=True)
with open(self._prefs_path, "w") as f:
json.dump(self._prefs_data, f)
def getSharedPreferences(self) -> Any:
return self._prefs_data
def setSharedPreferencesName(self, name: str):
self._shared_prefs_name = name
self._prefs_path = os.path.join(_app_data_dir, "shared_prefs",
f"{name}.json")
self._load()
def setSharedPreferencesMode(self, mode: int):
self._shared_prefs_mode = mode
@staticmethod
def getDefaultSharedPreferences(context: Any) -> Any:
mgr = PreferenceManager(context)
return mgr.getSharedPreferences()
@staticmethod
def setDefaultValues(context: Any, res_id: int, read_again: bool):
pass
@staticmethod
def setDefaultValues(context: Any, shared_prefs_name: str,
shared_prefs_mode: int, res_id: int,
read_again: bool):
pass
def getPreference(self, key: str, default: Any = None) -> Any:
return self._prefs_data.get(key, default)
def putPreference(self, key: str, value: Any):
self._prefs_data[key] = value
self._save()
def removePreference(self, key: str):
if key in self._prefs_data:
del self._prefs_data[key]
self._save()
# ═══════════════════════════════════════════════════════════════
# Preference
# ═══════════════════════════════════════════════════════════════
class Preference(Stub):
"""Preference — 偏好设置项基类"""
def __init__(self, context: Any = None, attrs: Any = None,
def_style_attr: int = 0):
super().__init__()
self._context = context
self._title: str = ""
self._summary: str = ""
self._key: Optional[str] = None
self._enabled: bool = True
self._default_value: Any = None
self._order: int = 0
self._persistent: bool = True
self._icon: Any = None
self._dependents: List["Preference"] = []
self._dependency_key: Optional[str] = None
self._preference_manager: Optional[PreferenceManager] = None
self._parent: Optional["PreferenceGroup"] = None
self._on_change_listener: Optional[PreferenceChangeListener] = None
self._on_click_listener: Optional[PreferenceOnPreferenceClickListener] = None
def setTitle(self, title: str):
self._title = title
def getTitle(self) -> str:
return self._title
def setSummary(self, summary: str):
self._summary = summary
def getSummary(self) -> str:
return self._summary
def setKey(self, key: str):
self._key = key
def getKey(self) -> Optional[str]:
return self._key
def setEnabled(self, enabled: bool):
self._enabled = enabled
def isEnabled(self) -> bool:
return self._enabled
def setDefaultValue(self, value: Any):
self._default_value = value
def setOnPreferenceClickListener(self, listener: PreferenceOnPreferenceClickListener):
self._on_click_listener = listener
def setOnPreferenceChangeListener(self, listener: PreferenceChangeListener):
self._on_change_listener = listener
def getSharedPreferences(self) -> Any:
if self._preference_manager:
return self._preference_manager.getSharedPreferences()
return {}
def getEditor(self) -> Any:
if self._preference_manager:
return self._preference_manager
return None
def shouldCommit(self) -> bool:
return True
def shouldPersist(self) -> bool:
return self._persistent and self._key is not None
def persistString(self, value: str) -> bool:
if self.shouldPersist() and self._preference_manager:
self._preference_manager.putPreference(self._key, value)
return True
return False
def persistInt(self, value: int) -> bool:
if self.shouldPersist() and self._preference_manager:
self._preference_manager.putPreference(self._key, value)
return True
return False
def persistBoolean(self, value: bool) -> bool:
if self.shouldPersist() and self._preference_manager:
self._preference_manager.putPreference(self._key, value)
return True
return False
def persistFloat(self, value: float) -> bool:
if self.shouldPersist() and self._preference_manager:
self._preference_manager.putPreference(self._key, value)
return True
return False
def persistLong(self, value: int) -> bool:
return self.persistInt(value)
def getPersistedString(self, default_return: Optional[str] = None) -> Optional[str]:
if self.shouldPersist() and self._preference_manager:
val = self._preference_manager.getPreference(self._key, default_return)
return str(val) if val is not None else default_return
return default_return
def getPersistedInt(self, default_return: int = 0) -> int:
if self.shouldPersist() and self._preference_manager:
return int(self._preference_manager.getPreference(self._key, default_return))
return default_return
def getPersistedBoolean(self, default_return: bool = False) -> bool:
if self.shouldPersist() and self._preference_manager:
val = self._preference_manager.getPreference(self._key, default_return)
if isinstance(val, bool):
return val
return str(val).lower() in ("true", "1", "yes")
return default_return
def getPersistedFloat(self, default_return: float = 0.0) -> float:
if self.shouldPersist() and self._preference_manager:
return float(self._preference_manager.getPreference(self._key, default_return))
return default_return
def getPersistedLong(self, default_return: int = 0) -> int:
return self.getPersistedInt(default_return)
def notifyChanged(self):
pass
def notifyHierarchyChanged(self):
pass
def onBindView(self, view: Any):
pass
def onClick(self):
if self._on_click_listener:
self._on_click_listener.onPreferenceClick(self)
def setOrder(self, order: int):
self._order = order
def getOrder(self) -> int:
return self._order
def setPersistent(self, persistent: bool):
self._persistent = persistent
def isPersistent(self) -> bool:
return self._persistent
def setDependency(self, dependency_key: str):
self._dependency_key = dependency_key
def getDependency(self) -> Optional[str]:
return self._dependency_key
def toString(self) -> str:
return f"Preference(key={self._key}, title={self._title})"
def hashCode(self) -> int:
return hash(self._key) & 0x7FFFFFFF if self._key else 0
# ═══════════════════════════════════════════════════════════════
# PreferenceGroup
# ═══════════════════════════════════════════════════════════════
class PreferenceGroup(Preference):
"""PreferenceGroup — 偏好组"""
def __init__(self, context: Any = None, attrs: Any = None):
super().__init__(context, attrs)
self._preferences: List[Preference] = []
def addPreference(self, preference: Preference) -> bool:
if preference not in self._preferences:
self._preferences.append(preference)
preference._parent = self
return True
return False
def removePreference(self, preference: Preference) -> bool:
if preference in self._preferences:
self._preferences.remove(preference)
preference._parent = None
return True
return False
def removeAll(self):
for p in self._preferences:
p._parent = None
self._preferences.clear()
def getPreference(self, index: int) -> Optional[Preference]:
if 0 <= index < len(self._preferences):
return self._preferences[index]
return None
def getPreferenceCount(self) -> int:
return len(self._preferences)
def findPreference(self, key: str) -> Optional[Preference]:
for p in self._preferences:
if p.getKey() == key:
return p
if isinstance(p, PreferenceGroup):
result = p.findPreference(key)
if result:
return result
return None
def toString(self) -> str:
return f"PreferenceGroup(count={len(self._preferences)})"
# ═══════════════════════════════════════════════════════════════
# PreferenceScreen
# ═══════════════════════════════════════════════════════════════
class PreferenceScreen(PreferenceGroup):
"""PreferenceScreen — 根偏好屏幕"""
def __init__(self, context: Any = None, attrs: Any = None):
super().__init__(context, attrs)
self._root: bool = True
# ═══════════════════════════════════════════════════════════════
# PreferenceCategory
# ═══════════════════════════════════════════════════════════════
class PreferenceCategory(PreferenceGroup):
"""PreferenceCategory — 偏好分类标题"""
def __init__(self, context: Any = None, attrs: Any = None):
super().__init__(context, attrs)
# ═══════════════════════════════════════════════════════════════
# TwoStatePreference
# ═══════════════════════════════════════════════════════════════
class TwoStatePreference(Preference):
"""TwoStatePreference — 双状态偏好基类"""
def __init__(self, context: Any = None, attrs: Any = None):
super().__init__(context, attrs)
self._checked: bool = False
self._summary_on: str = ""
self._summary_off: str = ""
self._disable_dependents_state: bool = False
def isChecked(self) -> bool:
return self._checked
def setChecked(self, checked: bool):
self._checked = checked
self.persistBoolean(checked)
def setSummaryOn(self, summary: str):
self._summary_on = summary
def setSummaryOff(self, summary: str):
self._summary_off = summary
def setDisableDependentsState(self, state: bool):
self._disable_dependents_state = state
# ═══════════════════════════════════════════════════════════════
# CheckBoxPreference
# ═══════════════════════════════════════════════════════════════
class CheckBoxPreference(TwoStatePreference):
"""CheckBoxPreference — 复选框偏好"""
def __init__(self, context: Any = None, attrs: Any = None):
super().__init__(context, attrs)
# ═══════════════════════════════════════════════════════════════
# SwitchPreference
# ═══════════════════════════════════════════════════════════════
class SwitchPreference(TwoStatePreference):
"""SwitchPreference — 开关偏好"""
def __init__(self, context: Any = None, attrs: Any = None):
super().__init__(context, attrs)
self._switch_text_on: str = ""
self._switch_text_off: str = ""
def setSwitchTextOn(self, text: str):
self._switch_text_on = text
def setSwitchTextOff(self, text: str):
self._switch_text_off = text
def getSwitchTextOn(self) -> str:
return self._switch_text_on
def getSwitchTextOff(self) -> str:
return self._switch_text_off
# ═══════════════════════════════════════════════════════════════
# DialogPreference
# ═══════════════════════════════════════════════════════════════
class DialogPreference(Preference):
"""DialogPreference — 对话框偏好基类"""
def __init__(self, context: Any = None, attrs: Any = None):
super().__init__(context, attrs)
self._dialog_title: str = ""
self._dialog_message: str = ""
self._dialog_icon: Any = None
self._positive_button_text: str = ""
self._negative_button_text: str = ""
# ═══════════════════════════════════════════════════════════════
# EditTextPreference
# ═══════════════════════════════════════════════════════════════
class EditTextPreference(DialogPreference):
"""EditTextPreference — 文本编辑偏好"""
def __init__(self, context: Any = None, attrs: Any = None):
super().__init__(context, attrs)
self._text: str = ""
def getText(self) -> str:
return self._text
def setText(self, text: str):
self._text = text
self.persistString(text)
# ═══════════════════════════════════════════════════════════════
# ListPreference
# ═══════════════════════════════════════════════════════════════
class ListPreference(DialogPreference):
"""ListPreference — 列表偏好"""
def __init__(self, context: Any = None, attrs: Any = None):
super().__init__(context, attrs)
self._entries: List[str] = []
self._entry_values: List[str] = []
self._value: str = ""
def getEntries(self) -> List[str]:
return list(self._entries)
def setEntries(self, entries: List[str]):
self._entries = list(entries)
def getEntryValues(self) -> List[str]:
return list(self._entry_values)
def setEntryValues(self, values: List[str]):
self._entry_values = list(values)
def getValue(self) -> str:
return self._value
def setValue(self, value: str):
self._value = value
self.persistString(value)
def getEntry(self) -> Optional[str]:
try:
idx = self._entry_values.index(self._value)
if 0 <= idx < len(self._entries):
return self._entries[idx]
except ValueError:
pass
return None
def getSummary(self) -> str:
entry = self.getEntry()
if entry:
return entry
return self._summary
def setDefaultValue(self, value: Any):
super().setDefaultValue(value)
# ═══════════════════════════════════════════════════════════════
# MultiSelectListPreference
# ═══════════════════════════════════════════════════════════════
class MultiSelectListPreference(DialogPreference):
"""MultiSelectListPreference — 多选列表偏好"""
def __init__(self, context: Any = None, attrs: Any = None):
super().__init__(context, attrs)
self._entries: List[str] = []
self._entry_values: List[str] = []
self._values: Set[str] = set()
def getEntries(self) -> List[str]:
return list(self._entries)
def setEntries(self, entries: List[str]):
self._entries = list(entries)
def getEntryValues(self) -> List[str]:
return list(self._entry_values)
def setEntryValues(self, values: List[str]):
self._entry_values = list(values)
def getValues(self) -> Set[str]:
return set(self._values)
def setValues(self, values: Set[str]):
self._values = set(values)
# ═══════════════════════════════════════════════════════════════
# RingtonePreference / SeekBarPreference / DropDownPreference
# ═══════════════════════════════════════════════════════════════
class RingtonePreference(Preference):
"""RingtonePreference — 铃声偏好"""
def __init__(self, context: Any = None, attrs: Any = None):
super().__init__(context, attrs)
self._ringtone_type: int = 1
self._show_default: bool = True
self._show_silent: bool = True
class SeekBarPreference(Preference):
"""SeekBarPreference — 滑块偏好"""
def __init__(self, context: Any = None, attrs: Any = None):
super().__init__(context, attrs)
self._min: int = 0
self._max: int = 100
self._current: int = 50
self._show_progress: bool = True
self._adjustable: bool = True
def getMin(self) -> int:
return self._min
def setMin(self, min_val: int):
self._min = min_val
def getMax(self) -> int:
return self._max
def setMax(self, max_val: int):
self._max = max_val
def getValue(self) -> int:
return self._current
def setValue(self, value: int):
self._current = value
self.persistInt(value)
class DropDownPreference(ListPreference):
"""DropDownPreference — 下拉列表偏好"""
def __init__(self, context: Any = None, attrs: Any = None):
super().__init__(context, attrs)
# ═══════════════════════════════════════════════════════════════
# PreferenceFragment / PreferenceFragmentCompat
# ═══════════════════════════════════════════════════════════════
class PreferenceFragment(Stub):
"""PreferenceFragment — 偏好片段"""
def __init__(self):
super().__init__()
self._preference_manager: Optional[PreferenceManager] = None
def onCreate(self, savedInstanceState: Any):
pass
def onCreatePreferences(self, savedInstanceState: Any, root_key: Optional[str]):
pass
def addPreferencesFromResource(self, res_id: int):
pass
def setPreferenceScreen(self, screen: PreferenceScreen):
pass
def getPreferenceScreen(self) -> Optional[PreferenceScreen]:
return None
def findPreference(self, key: str) -> Optional[Preference]:
screen = self.getPreferenceScreen()
if screen:
return screen.findPreference(key)
return None
class PreferenceFragmentCompat(PreferenceFragment):
"""PreferenceFragmentCompat — 兼容偏好片段"""
def __init__(self):
super().__init__()
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"Preference",
"PreferenceScreen",
"PreferenceCategory",
"PreferenceManager",
"CheckBoxPreference",
"SwitchPreference",
"ListPreference",
"EditTextPreference",
"MultiSelectListPreference",
"PreferenceFragment",
"PreferenceFragmentCompat",
"PreferenceGroup",
"PreferenceChangeListener",
"PreferenceOnPreferenceClickListener",
"TwoStatePreference",
"DialogPreference",
"RingtonePreference",
"SeekBarPreference",
"DropDownPreference",
]
+550
View File
@@ -0,0 +1,550 @@
"""
Android Provider 组件 — Settings, MediaStore, ContactsContract 等
方法签名完全对齐 Android SDK
"""
import os
import sys
import json
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,
)
# ═══════════════════════════════════════════════════════════════
# OpenableColumns
# ═══════════════════════════════════════════════════════════════
class OpenableColumns(Stub):
DISPLAY_NAME = "_display_name"
SIZE = "_size"
# ═══════════════════════════════════════════════════════════════
# Settings
# ═══════════════════════════════════════════════════════════════
class _SettingsBase(Stub):
"""Settings 基类 — 存储到 JSON 文件"""
_DATA_DIR = os.path.join(_app_data_dir, "shared_prefs")
_cache: Dict[str, Dict[str, Any]] = {}
@classmethod
def _get_path(cls, name: str) -> str:
os.makedirs(cls._DATA_DIR, exist_ok=True)
return os.path.join(cls._DATA_DIR, f"{name}.json")
@classmethod
def _load(cls, name: str) -> Dict[str, Any]:
if name in cls._cache:
return cls._cache[name]
path = cls._get_path(name)
if os.path.exists(path):
try:
with open(path, "r") as f:
data = json.load(f)
cls._cache[name] = data
return data
except (json.JSONDecodeError, OSError):
pass
cls._cache[name] = {}
return cls._cache[name]
@classmethod
def _save(cls, name: str):
path = cls._get_path(name)
try:
with open(path, "w") as f:
json.dump(cls._cache.get(name, {}), f)
except OSError:
pass
@staticmethod
def putString(name: str, key: str, value: str) -> bool:
data = _SettingsBase._load(name)
data[key] = value
_SettingsBase._save(name)
return True
@staticmethod
def getString(name: str, key: str, default_value: Optional[str] = None) -> Optional[str]:
data = _SettingsBase._load(name)
return data.get(key, default_value)
@staticmethod
def putInt(name: str, key: str, value: int) -> bool:
data = _SettingsBase._load(name)
data[key] = value
_SettingsBase._save(name)
return True
@staticmethod
def getInt(name: str, key: str, default_value: int = 0) -> int:
data = _SettingsBase._load(name)
return data.get(key, default_value)
@staticmethod
def putLong(name: str, key: str, value: int) -> bool:
return _SettingsBase.putInt(name, key, value)
@staticmethod
def getLong(name: str, key: str, default_value: int = 0) -> int:
return _SettingsBase.getInt(name, key, default_value)
@staticmethod
def putFloat(name: str, key: str, value: float) -> bool:
data = _SettingsBase._load(name)
data[key] = value
_SettingsBase._save(name)
return True
@staticmethod
def getFloat(name: str, key: str, default_value: float = 0.0) -> float:
data = _SettingsBase._load(name)
return data.get(key, default_value)
class SettingsSystem(_SettingsBase):
_NAME = "settings_system"
@staticmethod
def putInt(key: str, value: int) -> bool:
return _SettingsBase.putInt(SettingsSystem._NAME, key, value)
@staticmethod
def getInt(key: str, default_value: int = 0) -> int:
return _SettingsBase.getInt(SettingsSystem._NAME, key, default_value)
@staticmethod
def putString(key: str, value: str) -> bool:
return _SettingsBase.putString(SettingsSystem._NAME, key, value)
@staticmethod
def getString(key: str, default_value: Optional[str] = None) -> Optional[str]:
return _SettingsBase.getString(SettingsSystem._NAME, key, default_value)
@staticmethod
def putFloat(key: str, value: float) -> bool:
return _SettingsBase.putFloat(SettingsSystem._NAME, key, value)
@staticmethod
def getFloat(key: str, default_value: float = 0.0) -> float:
return _SettingsBase.getFloat(SettingsSystem._NAME, key, default_value)
@staticmethod
def putLong(key: str, value: int) -> bool:
return _SettingsBase.putLong(SettingsSystem._NAME, key, value)
@staticmethod
def getLong(key: str, default_value: int = 0) -> int:
return _SettingsBase.getLong(SettingsSystem._NAME, key, default_value)
class SettingsSecure(_SettingsBase):
_NAME = "settings_secure"
@staticmethod
def putInt(key: str, value: int) -> bool:
return _SettingsBase.putInt(SettingsSecure._NAME, key, value)
@staticmethod
def getInt(key: str, default_value: int = 0) -> int:
return _SettingsBase.getInt(SettingsSecure._NAME, key, default_value)
@staticmethod
def putString(key: str, value: str) -> bool:
return _SettingsBase.putString(SettingsSecure._NAME, key, value)
@staticmethod
def getString(key: str, default_value: Optional[str] = None) -> Optional[str]:
return _SettingsBase.getString(SettingsSecure._NAME, key, default_value)
@staticmethod
def putFloat(key: str, value: float) -> bool:
return _SettingsBase.putFloat(SettingsSecure._NAME, key, value)
@staticmethod
def getFloat(key: str, default_value: float = 0.0) -> float:
return _SettingsBase.getFloat(SettingsSecure._NAME, key, default_value)
@staticmethod
def putLong(key: str, value: int) -> bool:
return _SettingsBase.putLong(SettingsSecure._NAME, key, value)
@staticmethod
def getLong(key: str, default_value: int = 0) -> int:
return _SettingsBase.getLong(SettingsSecure._NAME, key, default_value)
class SettingsGlobal(_SettingsBase):
_NAME = "settings_global"
@staticmethod
def putInt(key: str, value: int) -> bool:
return _SettingsBase.putInt(SettingsGlobal._NAME, key, value)
@staticmethod
def getInt(key: str, default_value: int = 0) -> int:
return _SettingsBase.getInt(SettingsGlobal._NAME, key, default_value)
@staticmethod
def putString(key: str, value: str) -> bool:
return _SettingsBase.putString(SettingsGlobal._NAME, key, value)
@staticmethod
def getString(key: str, default_value: Optional[str] = None) -> Optional[str]:
return _SettingsBase.getString(SettingsGlobal._NAME, key, default_value)
@staticmethod
def putFloat(key: str, value: float) -> bool:
return _SettingsBase.putFloat(SettingsGlobal._NAME, key, value)
@staticmethod
def getFloat(key: str, default_value: float = 0.0) -> float:
return _SettingsBase.getFloat(SettingsGlobal._NAME, key, default_value)
@staticmethod
def putLong(key: str, value: int) -> bool:
return _SettingsBase.putLong(SettingsGlobal._NAME, key, value)
@staticmethod
def getLong(key: str, default_value: int = 0) -> int:
return _SettingsBase.getLong(SettingsGlobal._NAME, key, default_value)
class Settings(Stub):
"""Settings — 系统设置"""
System = SettingsSystem
Secure = SettingsSecure
Global = SettingsGlobal
ACTION_SETTINGS = "android.settings.SETTINGS"
ACTION_APPLICATION_DETAILS_SETTINGS = "android.settings.APPLICATION_DETAILS_SETTINGS"
EXTRA_APP_PACKAGE = "android.intent.extra.PACKAGE_NAME"
# ═══════════════════════════════════════════════════════════════
# MediaStore
# ═══════════════════════════════════════════════════════════════
class MediaStore(Stub):
"""MediaStore — 媒体存储"""
class MediaColumns(Stub):
_ID = 0
DATA = 1
SIZE = 2
DISPLAY_NAME = 3
MIME_TYPE = 4
TITLE = 5
DATE_ADDED = 6
DATE_MODIFIED = 7
ORIENTATION = 8
WIDTH = 9
HEIGHT = 10
class Files(Stub):
@staticmethod
def getContentUri(volume_name: str) -> str:
return f"content://media/{volume_name}/file"
class Images(Stub):
class Media(Stub):
EXTERNAL_CONTENT_URI = "content://media/external/images/media"
INTERNAL_CONTENT_URI = "content://media/internal/images/media"
class Thumbnails(Stub):
EXTERNAL_CONTENT_URI = "content://media/external/images/thumbnails"
class Video(Stub):
class Media(Stub):
EXTERNAL_CONTENT_URI = "content://media/external/video/media"
class Audio(Stub):
class Media(Stub):
EXTERNAL_CONTENT_URI = "content://media/external/audio/media"
@staticmethod
def getVersion(context: Any, uri: str) -> str:
return "0"
@staticmethod
def setVersion(context: Any, uri: str, version: str) -> bool:
return True
# ═══════════════════════════════════════════════════════════════
# DocumentsContract
# ═══════════════════════════════════════════════════════════════
class DocumentsContract(Stub):
"""DocumentsContract — 文档合约"""
EXTRA_INITIAL_URI = "android.provider.extra.INITIAL_URI"
EXTRA_LOADING = "android.provider.extra.LOADING"
EXTRA_INFO = "android.provider.extra.INFO"
EXTRA_PROMPT = "android.provider.extra.PROMPT"
@staticmethod
def buildDocumentUri(authority: str, document_id: str) -> str:
return f"content://{authority}/document/{document_id}"
@staticmethod
def buildDocumentUriUsingTree(tree_uri: str, document_id: str) -> str:
return f"{tree_uri}/document/{document_id}"
@staticmethod
def buildChildDocumentsUri(authority: str, parent_document_id: str) -> str:
return f"content://{authority}/document/{parent_document_id}/children"
@staticmethod
def buildChildDocumentsUriUsingTree(tree_uri: str, parent_document_id: str) -> str:
return f"{tree_uri}/document/{parent_document_id}/children"
@staticmethod
def getDocumentId(document_uri: str) -> str:
return document_uri.rsplit("/", 1)[-1]
@staticmethod
def getTreeDocumentId(document_uri: str) -> str:
parts = document_uri.split("/")
if "document" in parts:
idx = parts.index("document")
if idx + 1 < len(parts):
return parts[idx + 1]
return ""
@staticmethod
def getRootId(root_uri: str) -> str:
parts = root_uri.split("/")
if "root" in parts:
idx = parts.index("root")
if idx + 1 < len(parts):
return parts[idx + 1]
return ""
# ═══════════════════════════════════════════════════════════════
# ContactsContract
# ═══════════════════════════════════════════════════════════════
class ContactsContract(Stub):
"""ContactsContract — 联系人合约"""
class Contacts(Stub):
CONTENT_URI = "content://com.android.contacts/contacts"
CONTENT_FILTER_URI = "content://com.android.contacts/contacts/filter"
DISPLAY_NAME = "display_name"
PHOTO_URI = "photo_uri"
_ID = "_id"
class CommonDataKinds(Stub):
class Email(Stub):
CONTENT_URI = "content://com.android.contacts/data/emails"
class Phone(Stub):
CONTENT_URI = "content://com.android.contacts/data/phones"
class Data(Stub):
CONTENT_URI = "content://com.android.contacts/data"
class ContactsContractContacts(ContactsContract.Contacts):
pass
class ContactsContractData(ContactsContract.Data):
pass
# ═══════════════════════════════════════════════════════════════
# CallLog
# ═══════════════════════════════════════════════════════════════
class CallLog(Stub):
"""CallLog — 通话记录"""
class Calls(Stub):
CONTENT_URI = "content://call_log/calls"
_ID = "_id"
NUMBER = "number"
TYPE = "type"
TYPE_INCOMING = 1
TYPE_OUTGOING = 2
TYPE_MISSED = 3
TYPE_VOICEMAIL = 4
TYPE_REJECTED = 5
TYPE_BLOCKED = 6
TYPE_ANSWERED_EXTERNALLY = 7
DATE = "date"
DURATION = "duration"
NEW = "new"
VOICEMAIL_URI = "voicemail_uri"
CACHED_NAME = "cached_name"
CACHED_NUMBER_LABEL = "cached_number_label"
GEOCODED_LOCATION = "geocoded_location"
COUNTRY_ISO = "country_iso"
@staticmethod
def getLastOutgoingCall(context: Any) -> Optional[str]:
return None
# ═══════════════════════════════════════════════════════════════
# CalendarContract
# ═══════════════════════════════════════════════════════════════
class CalendarContract(Stub):
"""CalendarContract — 日历合约"""
class Events(Stub):
CONTENT_URI = "content://com.android.calendar/events"
_ID = "_id"
TITLE = "title"
DESCRIPTION = "description"
EVENT_LOCATION = "event_location"
DTSTART = "dtstart"
DTEND = "dtend"
EVENT_TIMEZONE = "event_timezone"
DURATION = "duration"
ALL_DAY = "allDay"
CALENDAR_ID = "calendar_id"
ORGANIZER = "organizer"
STATUS = "eventStatus"
AVAILABILITY = "availability"
ACCESS_LEVEL = "accessLevel"
class Attendees(Stub):
CONTENT_URI = "content://com.android.calendar/attendees"
class Reminders(Stub):
CONTENT_URI = "content://com.android.calendar/reminders"
# ═══════════════════════════════════════════════════════════════
# Browser / SearchRecentSuggestions / UserDictionary
# ═══════════════════════════════════════════════════════════════
class Browser(Stub):
"""Browser — 浏览器书签"""
class BookmarkColumns(Stub):
TITLE = "title"
URL = "url"
_ID = "_id"
BOOKMARK = "bookmark"
DATE = "date"
CREATED = "created"
VISITS = "visits"
class SearchColumns(Stub):
URL = "url"
SEARCH = "search"
DATE = "date"
HISTORY_PROJECTION = ["_id", "url", "title", "date", "visits", "bookmark", "created"]
SEARCHES_PROJECTION = ["_id", "search", "date"]
class SearchRecentSuggestions(Stub):
"""SearchRecentSuggestions — 搜索建议"""
def __init__(self, context: Any, authority: str, mode: int):
super().__init__()
self._authority = authority
self._mode = mode
def saveRecentQuery(self, query_string: str, line2: str):
pass
def clearHistory(self):
pass
class UserDictionary(Stub):
"""UserDictionary — 用户词典"""
class Words(Stub):
CONTENT_URI = "content://user_dictionary/words"
_ID = "_id"
WORD = "word"
FREQUENCY = "frequency"
LOCALE = "locale"
APP_ID = "app_id"
SHORTCUT = "shortcut"
# ═══════════════════════════════════════════════════════════════
# SyncStateContract / LiveFolders
# ═══════════════════════════════════════════════════════════════
class SyncStateContract(Stub):
"""SyncStateContract — 同步状态合约"""
class Columns(Stub):
_ID = "_id"
ACCOUNT_NAME = "account_name"
ACCOUNT_TYPE = "account_type"
DATA = "data"
class Helpers(Stub):
@staticmethod
def get(cr: Any, provider_uri: str, account: Any) -> Optional[bytes]:
return None
@staticmethod
def set(cr: Any, provider_uri: str, account: Any, data: bytes):
pass
@staticmethod
def insert(cr: Any, provider_uri: str, account: Any, data: bytes):
pass
@staticmethod
def update(cr: Any, provider_uri: str, account: Any, data: bytes):
pass
class LiveFolders(Stub):
"""LiveFolders — 实时文件夹"""
NAME = "name"
DESCRIPTION = "description"
INTENT = "intent"
ICON_BITMAP = "icon_bitmap"
ICON_PACKAGE = "icon_package"
ICON_RESOURCE = "icon_resource"
DISPLAY_MODE = "display_mode"
DISPLAY_MODE_GRID = 1
DISPLAY_MODE_LIST = 2
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"Settings",
"SettingsSystem",
"SettingsSecure",
"SettingsGlobal",
"CallLog",
"ContactsContract",
"ContactsContractContacts",
"ContactsContractData",
"MediaStore",
"DocumentsContract",
"CalendarContract",
"Browser",
"SearchRecentSuggestions",
"UserDictionary",
"SyncStateContract",
"LiveFolders",
"OpenableColumns",
]
+401
View File
@@ -0,0 +1,401 @@
"""
Android Speech/TTS 组件 — TextToSpeech, Voice, SynthesisCallback 等
方法签名完全对齐 Android SDK
"""
import os
import sys
import locale as _locale
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,
)
# ═══════════════════════════════════════════════════════════════
# TextToSpeech.OnInitListener
# ═══════════════════════════════════════════════════════════════
class TextToSpeechOnInitListener(Stub):
"""OnInitListener — 初始化监听器"""
def onInit(self, status: int):
pass
# ═══════════════════════════════════════════════════════════════
# UtteranceProgressListener
# ═══════════════════════════════════════════════════════════════
class UtteranceProgressListener(Stub):
"""UtteranceProgressListener — 发音进度监听器"""
def onStart(self, utterance_id: str):
pass
def onDone(self, utterance_id: str):
pass
def onError(self, utterance_id: str, error_code: int = -1):
pass
def onStop(self, utterance_id: str, interrupted: bool = False):
pass
def onBeginSynthesis(self, utterance_id: str, sample_rate_in_hz: int,
audio_format: int, channel_count: int):
pass
def onRangeStart(self, utterance_id: str, start: int, end: int, frame: int):
pass
class TextToSpeechOnUtteranceCompletedListener(Stub):
"""OnUtteranceCompletedListener — 旧版发音完成监听器"""
def onUtteranceCompleted(self, utterance_id: str):
pass
# ═══════════════════════════════════════════════════════════════
# SynthesisRequest
# ═══════════════════════════════════════════════════════════════
class SynthesisRequest(Stub):
"""SynthesisRequest — 合成请求"""
def __init__(self):
super().__init__()
self._text: str = ""
self._speech_rate: float = 1.0
self._voice: Optional["Voice"] = None
self._calling_uid: int = 0
self._country: str = ""
self._language: str = ""
self._variant: str = ""
self._params: Dict[str, str] = {}
def getText(self) -> str:
return self._text
def getSpeechRate(self) -> float:
return self._speech_rate
def getVoice(self) -> Optional["Voice"]:
return self._voice
def getCallingUid(self) -> int:
return self._calling_uid
def getCountry(self) -> str:
return self._country
def getLanguage(self) -> str:
return self._language
def getVariant(self) -> str:
return self._variant
def getParams(self) -> Dict[str, str]:
return dict(self._params)
# ═══════════════════════════════════════════════════════════════
# SynthesisCallback
# ═══════════════════════════════════════════════════════════════
class SynthesisCallback(Stub):
"""SynthesisCallback — 合成回调"""
def start(self, sample_rate_in_hz: int, audio_format: int, channel_count: int) -> int:
return 0
def getMaxBufferSize(self) -> int:
return 8192
def getChannelCount(self) -> int:
return 1
def getAudioFormat(self) -> int:
return 1
def getSampleRate(self) -> int:
return 22050
def availableBuffer(self, buffer_size: int) -> int:
return buffer_size
def audioAvailable(self, audio: bytes) -> int:
return len(audio)
def done(self) -> int:
return 0
def error(self) -> int:
return -1
def hasStarted(self) -> bool:
return False
def hasFinished(self) -> bool:
return False
# ═══════════════════════════════════════════════════════════════
# Voice
# ═══════════════════════════════════════════════════════════════
class Voice(Stub):
"""Voice — 语音"""
QUALITY_DEFAULT = -1
QUALITY_VERY_LOW = 100
QUALITY_LOW = 200
QUALITY_NORMAL = 300
QUALITY_HIGH = 400
QUALITY_VERY_HIGH = 500
LATENCY_DEFAULT = -1
LATENCY_VERY_LOW = 100
LATENCY_LOW = 200
LATENCY_NORMAL = 300
LATENCY_HIGH = 400
LATENCY_VERY_HIGH = 500
def __init__(self, name: str = "", locale: Any = None,
quality: int = QUALITY_NORMAL, latency: int = LATENCY_NORMAL,
requires_network: bool = False,
features: Optional[List[str]] = None):
super().__init__()
self._name = name
self._locale = locale or _locale.getdefaultlocale()[0]
self._quality = quality
self._latency = latency
self._requires_network = requires_network
self._features = features or []
def getName(self) -> str:
return self._name
def getLocale(self) -> Any:
return self._locale
def getQuality(self) -> int:
return self._quality
def getLatency(self) -> int:
return self._latency
def isNetworkConnectionRequired(self) -> bool:
return self._requires_network
def getFeatures(self) -> List[str]:
return list(self._features)
def toString(self) -> str:
return f"Voice(name={self._name}, locale={self._locale})"
# ═══════════════════════════════════════════════════════════════
# TextToSpeech.Engine
# ═══════════════════════════════════════════════════════════════
class TextToSpeechEngine(Stub):
"""TextToSpeech.Engine — TTS 引擎元数据"""
ACTION_CHECK_TTS_DATA = "android.speech.tts.engine.CHECK_TTS_DATA"
ACTION_INSTALL_TTS_DATA = "android.speech.tts.engine.INSTALL_TTS_DATA"
ACTION_GET_SAMPLE_TEXT = "android.speech.tts.engine.GET_SAMPLE_TEXT"
KEY_PARAM_STREAM = "streamType"
KEY_PARAM_VOLUME = "volume"
KEY_PARAM_PAN = "pan"
KEY_PARAM_UTTERANCE_ID = "utteranceId"
KEY_PARAM_SESSION_ID = "sessionId"
KEY_FEATURE_NETWORK_SYNTHESIS = "networkSynthesis"
KEY_FEATURE_EMBEDDED_SYNTHESIS = "embeddedSynthesis"
KEY_FEATURE_NOT_INSTALLED = "notInstalled"
DEFAULT_STREAM = 3
DEFAULT_LANG = "eng"
DEFAULT_COUNTRY = "USA"
DEFAULT_VARIANT = ""
@staticmethod
def getLocaleLang(lang: str) -> str:
return lang[:3] if len(lang) >= 3 else lang
@staticmethod
def getLocaleCountry(lang: str) -> str:
return lang[3:6] if len(lang) >= 6 else ""
@staticmethod
def getLocaleVariant(lang: str) -> str:
return lang[6:] if len(lang) > 6 else ""
Engine = TextToSpeechEngine
# ═══════════════════════════════════════════════════════════════
# TextToSpeech
# ═══════════════════════════════════════════════════════════════
class TextToSpeech(Stub):
"""TextToSpeech — 文字转语音"""
SUCCESS = 0
ERROR = -1
STOPPED = -2
QUEUE_FLUSH = 0
QUEUE_ADD = 1
LANG_COUNTRY_AVAILABLE = 1
LANG_AVAILABLE = 0
LANG_COUNTRY_VAR_AVAILABLE = 2
LANG_MISSING_DATA = -1
LANG_NOT_SUPPORTED = -2
Engine = TextToSpeechEngine
def __init__(self, context: Any = None, listener: Optional[TextToSpeechOnInitListener] = None):
super().__init__()
self._context = context
self._listener = listener
self._utterance_progress_listener: Optional[UtteranceProgressListener] = None
self._language: str = ""
self._country: str = ""
self._pitch: float = 1.0
self._speech_rate: float = 1.0
self._voice: Optional[Voice] = None
self._voices: List[Voice] = [
Voice("en-us-1", "en_US", Voice.QUALITY_HIGH, Voice.LATENCY_NORMAL),
Voice("en-gb-1", "en_GB", Voice.QUALITY_NORMAL, Voice.LATENCY_NORMAL),
Voice("zh-cn-1", "zh_CN", Voice.QUALITY_HIGH, Voice.LATENCY_NORMAL),
]
self._engine_package: str = ""
self._initialized = False
if listener:
try:
listener.onInit(self.SUCCESS)
except Exception:
pass
self._initialized = True
def speak(self, text: str, queue_mode: int = QUEUE_FLUSH,
params: Any = None, utterance_id: str = "") -> int:
return self.SUCCESS
def stop(self) -> int:
return self.SUCCESS
def shutdown(self):
self._initialized = False
def setLanguage(self, language: str, country: str = "", variant: str = "") -> int:
self._language = language
self._country = country
return self.LANG_AVAILABLE
def getLanguage(self) -> Tuple[str, str, str]:
return (self._language, self._country, "")
def isLanguageAvailable(self, language: str, country: str = "",
variant: str = "") -> int:
return self.LANG_AVAILABLE
def setOnUtteranceProgressListener(self, listener: UtteranceProgressListener):
self._utterance_progress_listener = listener
def setOnUtteranceCompletedListener(self, listener: TextToSpeechOnUtteranceCompletedListener):
pass
def setPitch(self, pitch: float) -> int:
self._pitch = pitch
return self.SUCCESS
def setSpeechRate(self, rate: float) -> int:
self._speech_rate = rate
return self.SUCCESS
def getVoice(self) -> Optional[Voice]:
return self._voice
def setVoice(self, voice: Voice) -> int:
self._voice = voice
return self.SUCCESS
def getVoices(self) -> Set[Voice]:
return set(self._voices)
def addSpeech(self, text: str, filename: str) -> int:
return self.SUCCESS
def addEyes(self, text: str, filename: str) -> int:
return self.SUCCESS
def synthesizeToFile(self, text: str, params: Any, filename: str) -> int:
return self.SUCCESS
def setEngineByPackageName(self, package_name: str):
self._engine_package = package_name
def getDefaultEngine(self) -> Optional[str]:
return "com.android.tts"
def getEngines(self) -> List["EngineInfo"]:
return []
def getFeatures(self, locale: Any) -> Set[str]:
return set()
def areDefaultsEnforced(self) -> bool:
return False
def setQueueMode(self, queue_mode: int):
pass
def isSpeaking(self) -> bool:
return False
def toString(self) -> str:
return f"TextToSpeech(lang={self._language})"
# ═══════════════════════════════════════════════════════════════
# EngineInfo
# ═══════════════════════════════════════════════════════════════
class EngineInfo(Stub):
"""EngineInfo — 引擎信息"""
def __init__(self):
super().__init__()
self.name: str = ""
self.label: str = ""
self.icon: int = 0
self.priority: int = 0
self.system: bool = False
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"TextToSpeech",
"TextToSpeechEngine",
"TextToSpeechOnInitListener",
"TextToSpeechOnUtteranceCompletedListener",
"SynthesisCallback",
"SynthesisRequest",
"Voice",
"UtteranceProgressListener",
"Engine",
"EngineInfo",
]
+676
View File
@@ -0,0 +1,676 @@
"""
Android Telephony 组件 — TelephonyManager, CellInfo, PhoneStateListener 等
方法签名完全对齐 Android SDK
"""
import os
import sys
import random
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,
)
# ═══════════════════════════════════════════════════════════════
# CellIdentity 基类集合
# ═══════════════════════════════════════════════════════════════
class CellIdentity(Stub):
def __init__(self):
super().__init__()
class CellIdentityGsm(CellIdentity):
def __init__(self, mcc: int = 0, mnc: int = 0, lac: int = 0, cid: int = 0, arfcn: int = 0):
super().__init__()
self._mcc = mcc
self._mnc = mnc
self._lac = lac
self._cid = cid
self._arfcn = arfcn
def getMcc(self) -> int: return self._mcc
def getMnc(self) -> int: return self._mnc
def getLac(self) -> int: return self._lac
def getCid(self) -> int: return self._cid
def getArfcn(self) -> int: return self._arfcn
class CellIdentityCdma(CellIdentity):
def __init__(self, network_id: int = 0, system_id: int = 0, basestation_id: int = 0,
latitude: int = 0, longitude: int = 0):
super().__init__()
self._network_id = network_id
self._system_id = system_id
self._basestation_id = basestation_id
self._latitude = latitude
self._longitude = longitude
def getNetworkId(self) -> int: return self._network_id
def getSystemId(self) -> int: return self._system_id
def getBasestationId(self) -> int: return self._basestation_id
def getLatitude(self) -> int: return self._latitude
def getLongitude(self) -> int: return self._longitude
class CellIdentityLte(CellIdentity):
def __init__(self, mcc: int = 0, mnc: int = 0, ci: int = 0, pci: int = 0, tac: int = 0):
super().__init__()
self._mcc = mcc; self._mnc = mnc
self._ci = ci; self._pci = pci; self._tac = tac
def getMcc(self) -> int: return self._mcc
def getMnc(self) -> int: return self._mnc
def getCi(self) -> int: return self._ci
def getPci(self) -> int: return self._pci
def getTac(self) -> int: return self._tac
class CellIdentityWcdma(CellIdentity):
def __init__(self, mcc: int = 0, mnc: int = 0, lac: int = 0, cid: int = 0, psc: int = 0):
super().__init__()
self._mcc = mcc; self._mnc = mnc
self._lac = lac; self._cid = cid; self._psc = psc
def getMcc(self) -> int: return self._mcc
def getMnc(self) -> int: return self._mnc
def getLac(self) -> int: return self._lac
def getCid(self) -> int: return self._cid
def getPsc(self) -> int: return self._psc
class CellIdentityNr(CellIdentity):
def __init__(self, mcc: int = 0, mnc: int = 0, nci: int = 0, pci: int = 0, tac: int = 0):
super().__init__()
self._mcc = mcc; self._mnc = mnc
self._nci = nci; self._pci = pci; self._tac = tac
def getMcc(self) -> int: return self._mcc
def getMnc(self) -> int: return self._mnc
def getNci(self) -> int: return self._nci
def getPci(self) -> int: return self._pci
def getTac(self) -> int: return self._tac
# ═══════════════════════════════════════════════════════════════
# CellSignalStrength 基类集合
# ═══════════════════════════════════════════════════════════════
class CellSignalStrength(Stub):
SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0
SIGNAL_STRENGTH_POOR = 1
SIGNAL_STRENGTH_MODERATE = 2
SIGNAL_STRENGTH_GOOD = 3
SIGNAL_STRENGTH_GREAT = 4
def getLevel(self) -> int:
return self.SIGNAL_STRENGTH_NONE_OR_UNKNOWN
def getAsuLevel(self) -> int:
return 0
def getDbm(self) -> int:
return 0
class CellSignalStrengthGsm(CellSignalStrength):
def __init__(self, rssi: int = 0, ber: int = 0, ta: int = 0):
super().__init__()
self._rssi = rssi; self._ber = ber; self._ta = ta
def getRssi(self) -> int: return self._rssi
def getDbm(self) -> int: return self._rssi - 113 if self._rssi > 0 else 0
class CellSignalStrengthCdma(CellSignalStrength):
def __init__(self, dbm: int = 0, ecio: int = 0, evdo_dbm: int = 0, evdo_ecio: int = 0,
evdo_snr: int = 0):
super().__init__()
self._dbm = dbm; self._ecio = ecio
self._evdo_dbm = evdo_dbm; self._evdo_ecio = evdo_ecio; self._evdo_snr = evdo_snr
def getDbm(self) -> int: return self._dbm
class CellSignalStrengthLte(CellSignalStrength):
def __init__(self, rssi: int = 0, rsrp: int = 0, rsrq: int = 0, rssnr: int = 0,
cqi: int = 0, ta: int = 0):
super().__init__()
self._rssi = rssi; self._rsrp = rsrp; self._rsrq = rsrq
self._rssnr = rssnr; self._cqi = cqi; self._ta = ta
def getRsrp(self) -> int: return self._rsrp
def getRsrq(self) -> int: return self._rsrq
def getRssnr(self) -> int: return self._rssnr
def getDbm(self) -> int: return self._rsrp
class CellSignalStrengthWcdma(CellSignalStrength):
def __init__(self, rscp: int = 0, ecno: int = 0):
super().__init__()
self._rscp = rscp; self._ecno = ecno
def getRscp(self) -> int: return self._rscp
def getEcno(self) -> int: return self._ecno
def getDbm(self) -> int: return self._rscp - 113 if self._rscp > 0 else 0
class CellSignalStrengthNr(CellSignalStrength):
def __init__(self, ssRsrp: int = 0, ssRsrq: int = 0, ssSinr: int = 0, csiRsrp: int = 0):
super().__init__()
self._ssRsrp = ssRsrp; self._ssRsrq = ssRsrq
self._ssSinr = ssSinr; self._csiRsrp = csiRsrp
def getSsRsrp(self) -> int: return self._ssRsrp
def getDbm(self) -> int: return self._ssRsrp
# ═══════════════════════════════════════════════════════════════
# CellInfo 基类集合
# ═══════════════════════════════════════════════════════════════
class CellInfo(Stub):
UNAVAILABLE = 0x7FFFFFFF
TIMESTAMP_TYPE_UNKNOWN = 0
TIMESTAMP_TYPE_GSM = 1
TIMESTAMP_TYPE_UMTS = 2
TIMESTAMP_TYPE_CDMA = 3
TIMESTAMP_TYPE_LTE = 4
TIMESTAMP_TYPE_WCDMA = 5
TIMESTAMP_TYPE_NR = 6
def __init__(self):
super().__init__()
self._registered = False
self._timestamp = 0
self._connection_status = 0
def isRegistered(self) -> bool: return self._registered
def getTimeStamp(self) -> int: return self._timestamp
def getCellConnectionStatus(self) -> int: return self._connection_status
class CellInfoGsm(CellInfo):
def __init__(self):
super().__init__()
self._identity = CellIdentityGsm()
self._signal_strength = CellSignalStrengthGsm()
def getCellIdentity(self) -> CellIdentityGsm: return self._identity
def getCellSignalStrength(self) -> CellSignalStrengthGsm: return self._signal_strength
class CellInfoCdma(CellInfo):
def __init__(self):
super().__init__()
self._identity = CellIdentityCdma()
self._signal_strength = CellSignalStrengthCdma()
def getCellIdentity(self) -> CellIdentityCdma: return self._identity
def getCellSignalStrength(self) -> CellSignalStrengthCdma: return self._signal_strength
class CellInfoLte(CellInfo):
def __init__(self):
super().__init__()
self._identity = CellIdentityLte()
self._signal_strength = CellSignalStrengthLte()
def getCellIdentity(self) -> CellIdentityLte: return self._identity
def getCellSignalStrength(self) -> CellSignalStrengthLte: return self._signal_strength
class CellInfoWcdma(CellInfo):
def __init__(self):
super().__init__()
self._identity = CellIdentityWcdma()
self._signal_strength = CellSignalStrengthWcdma()
def getCellIdentity(self) -> CellIdentityWcdma: return self._identity
def getCellSignalStrength(self) -> CellSignalStrengthWcdma: return self._signal_strength
class CellInfoNr(CellInfo):
def __init__(self):
super().__init__()
self._identity = CellIdentityNr()
self._signal_strength = CellSignalStrengthNr()
def getCellIdentity(self) -> CellIdentityNr: return self._identity
def getCellSignalStrength(self) -> CellSignalStrengthNr: return self._signal_strength
# ═══════════════════════════════════════════════════════════════
# PhoneStateListener
# ═══════════════════════════════════════════════════════════════
class PhoneStateListener(Stub):
LISTEN_NONE = 0
LISTEN_SERVICE_STATE = 1
LISTEN_SIGNAL_STRENGTHS = 2
LISTEN_MESSAGE_WAITING_INDICATOR = 4
LISTEN_CALL_FORWARDING_INDICATOR = 8
LISTEN_CELL_LOCATION = 16
LISTEN_CALL_STATE = 32
LISTEN_DATA_CONNECTION_STATE = 64
LISTEN_DATA_ACTIVITY = 128
LISTEN_SIGNAL_STRENGTH = 256
LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH = 512
LISTEN_CELL_INFO = 1024
LISTEN_PRECISE_CALL_STATE = 2048
LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096
LISTEN_DATA_CONNECTION_REAL_TIME_INFO = 8192
LISTEN_OEM_HOOK_RAW_EVENT = 32768
LISTEN_SRVCC_STATE_CHANGED = 65536
LISTEN_CARRIER_NETWORK_CHANGE = 131072
LISTEN_VOICE_ACTIVATION_STATE = 262144
LISTEN_DATA_ACTIVATION_STATE = 524288
LISTEN_USER_MOBILE_DATA_STATE = 1048576
LISTEN_DISPLAY_INFO_CHANGED = 2097152
LISTEN_PHONE_CAPABILITY_CHANGE = 4194304
LISTEN_ACTIVE_PHONE_TYPE_CHANGE = 8388608
LISTEN_EMERGENCY_NUMBER_LIST = 16777216
LISTEN_EMERGENCY_NUMBER_SOURCE = 33554432
LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864
LISTEN_BARRING_INFO_CHANGED = 134217728
LISTEN_ALL = 2147483647
def __init__(self, looper: Any = None):
super().__init__()
self._looper = looper
def onServiceStateChanged(self, service_state: "ServiceState"):
pass
def onSignalStrengthsChanged(self, signal_strength: "SignalStrength"):
pass
def onMessageWaitingIndicatorChanged(self, mwi: bool):
pass
def onCallForwardingIndicatorChanged(self, cfi: bool):
pass
def onCellLocationChanged(self, location: Any):
pass
def onCallStateChanged(self, state: int, phone_number: str):
pass
def onDataConnectionStateChanged(self, state: int, network_type: int = 0):
pass
def onDataActivity(self, direction: int):
pass
def onSignalStrengthChanged(self, asu: int):
pass
def onCellInfoChanged(self, cell_info: List[CellInfo]):
pass
# ═══════════════════════════════════════════════════════════════
# ServiceState / SignalStrength
# ═══════════════════════════════════════════════════════════════
class ServiceState(Stub):
STATE_IN_SERVICE = 0
STATE_OUT_OF_SERVICE = 1
STATE_EMERGENCY_ONLY = 2
STATE_POWER_OFF = 3
def __init__(self):
super().__init__()
self._state = self.STATE_OUT_OF_SERVICE
self._roaming = False
self._operator_alpha_long: str = ""
self._operator_alpha_short: str = ""
self._operator_numeric: str = ""
self._is_manual_selection = False
self._cdma_default_roaming_indicator = -1
self._cdma_eri_icon_index = -1
self._cdma_eri_icon_mode = -1
self._cdma_roaming_indicator = -1
self._channel_number = 0
def getState(self) -> int: return self._state
def setState(self, state: int): self._state = state
def getRoaming(self) -> bool: return self._roaming
def setRoaming(self, roaming: bool): self._roaming = roaming
def getOperatorAlphaLong(self) -> str: return self._operator_alpha_long
def getOperatorAlphaShort(self) -> str: return self._operator_alpha_short
def getOperatorNumeric(self) -> str: return self._operator_numeric
def getIsManualSelection(self) -> bool: return self._is_manual_selection
def getCdmaDefaultRoamingIndicator(self) -> int: return self._cdma_default_roaming_indicator
def getCdmaEriIconIndex(self) -> int: return self._cdma_eri_icon_index
def getCdmaEriIconMode(self) -> int: return self._cdma_eri_icon_mode
def getCdmaRoamingIndicator(self) -> int: return self._cdma_roaming_indicator
def getChannelNumber(self) -> int: return self._channel_number
def getNetworkRegistrationInfo(self, domain: int, transport_type: int) -> Any:
return None
def toString(self) -> str:
return f"ServiceState(state={self._state}, roaming={self._roaming})"
class SignalStrength(Stub):
NUM_SIGNAL_STRENGTH_BINS = 5
SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0
SIGNAL_STRENGTH_POOR = 1
SIGNAL_STRENGTH_MODERATE = 2
SIGNAL_STRENGTH_GOOD = 3
SIGNAL_STRENGTH_GREAT = 4
def __init__(self):
super().__init__()
self._gsm_signal_strength = 0
self._cdma_dbm = 0
self._cdma_ecio = 0
self._evdo_dbm = 0
self._evdo_ecio = 0
self._evdo_snr = 0
self._lte_signal_strength = 0
self._td_scdma_rscp = 0
def getGsmSignalStrength(self) -> int: return self._gsm_signal_strength
def getCdmaDbm(self) -> int: return self._cdma_dbm
def getCdmaEcio(self) -> int: return self._cdma_ecio
def getEvdoDbm(self) -> int: return self._evdo_dbm
def getEvdoEcio(self) -> int: return self._evdo_ecio
def getEvdoSnr(self) -> int: return self._evdo_snr
def getLteSignalStrength(self) -> int: return self._lte_signal_strength
def getLevel(self) -> int: return self.SIGNAL_STRENGTH_NONE_OR_UNKNOWN
def getAsuLevel(self) -> int: return 0
def getDbm(self) -> int: return 0
# ═══════════════════════════════════════════════════════════════
# SmsManager
# ═══════════════════════════════════════════════════════════════
class SmsManager(Stub):
RESULT_ERROR_GENERIC_FAILURE = 1
RESULT_ERROR_RADIO_OFF = 2
RESULT_ERROR_NULL_PDU = 3
RESULT_ERROR_NO_SERVICE = 4
RESULT_ERROR_LIMIT_EXCEEDED = 5
RESULT_ERROR_FDN_CHECK_FAILURE = 6
RESULT_ERROR_SHORT_CODE_NOT_ALLOWED = 7
RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED = 8
RESULT_RIL_GENERIC_FAILURE = 9
RESULT_RIL_RADIO_NOT_AVAILABLE = 10
STATUS_ON_ICC_FREE = 0
STATUS_ON_ICC_READ = 1
STATUS_ON_ICC_UNREAD = 3
STATUS_ON_ICC_SENT = 5
STATUS_ON_ICC_UNSENT = 7
_instance: Optional["SmsManager"] = None
def __init__(self):
super().__init__()
@staticmethod
def getDefault() -> "SmsManager":
if SmsManager._instance is None:
SmsManager._instance = SmsManager()
return SmsManager._instance
def sendTextMessage(self, dest_address: str, sc_address: Optional[str],
text: str, sent_intent: Any, delivery_intent: Any):
pass
def sendMultipartTextMessage(self, dest_address: str, sc_address: Optional[str],
parts: List[str], sent_intents: Optional[List[Any]],
delivery_intents: Optional[List[Any]]):
pass
def sendDataMessage(self, dest_address: str, sc_address: Optional[str],
dest_port: int, data: bytes, sent_intent: Any,
delivery_intent: Any):
pass
def sendMultimediaMessage(self, context: Any, content_uri: str,
location_url: Optional[str],
config_overrides: Optional[Any],
sent_intent: Any):
pass
# ═══════════════════════════════════════════════════════════════
# PhoneNumberUtils
# ═══════════════════════════════════════════════════════════════
class PhoneNumberUtils(Stub):
FORMAT_JAPAN = 2
FORMAT_NANP = 1
FORMAT_UNKNOWN = 0
@staticmethod
def formatNumber(source: str) -> str:
return source
@staticmethod
def isGlobalPhoneNumber(phone_number: str) -> bool:
return bool(phone_number and len(phone_number) >= 7)
@staticmethod
def compare(a: str, b: str) -> bool:
return a.replace("-", "").replace(" ", "") == b.replace("-", "").replace(" ", "")
@staticmethod
def formatNumberToE164(phone_number: str, default_country_iso: str) -> Optional[str]:
return phone_number
@staticmethod
def formatNumberToRFC3966(phone_number: str, default_country_iso: str) -> Optional[str]:
return phone_number
@staticmethod
def isEmergencyNumber(phone_number: str) -> bool:
return phone_number in ("911", "112", "999", "000", "110", "119", "120", "122")
@staticmethod
def isVoiceMailNumber(phone_number: str) -> bool:
return False
@staticmethod
def convertKeypadLettersToDigits(input_: str) -> str:
mapping = {"A": "2", "B": "2", "C": "2", "D": "3", "E": "3",
"F": "3", "G": "4", "H": "4", "I": "4", "J": "5",
"K": "5", "L": "5", "M": "6", "N": "6", "O": "6",
"P": "7", "Q": "7", "R": "7", "S": "7", "T": "8",
"U": "8", "V": "8", "W": "9", "X": "9", "Y": "9", "Z": "9"}
result = ""
for c in input_.upper():
result += mapping.get(c, c)
return result
@staticmethod
def toCallerIDMinMatch(phone_number: str) -> str:
return phone_number[-7:] if len(phone_number) >= 7 else phone_number
# ═══════════════════════════════════════════════════════════════
# TelephonyManager
# ═══════════════════════════════════════════════════════════════
class TelephonyManager(Stub):
"""TelephonyManager — 电话管理器"""
PHONE_TYPE_NONE = 0
PHONE_TYPE_GSM = 1
PHONE_TYPE_CDMA = 2
PHONE_TYPE_SIP = 3
NETWORK_TYPE_UNKNOWN = 0
NETWORK_TYPE_GPRS = 1
NETWORK_TYPE_EDGE = 2
NETWORK_TYPE_UMTS = 3
NETWORK_TYPE_CDMA = 4
NETWORK_TYPE_EVDO_0 = 5
NETWORK_TYPE_EVDO_A = 6
NETWORK_TYPE_1xRTT = 7
NETWORK_TYPE_HSDPA = 8
NETWORK_TYPE_HSUPA = 9
NETWORK_TYPE_HSPA = 10
NETWORK_TYPE_IDEN = 11
NETWORK_TYPE_EVDO_B = 12
NETWORK_TYPE_LTE = 13
NETWORK_TYPE_EHRPD = 14
NETWORK_TYPE_HSPAP = 15
NETWORK_TYPE_GSM = 16
NETWORK_TYPE_TD_SCDMA = 17
NETWORK_TYPE_IWLAN = 18
NETWORK_TYPE_LTE_CA = 19
NETWORK_TYPE_NR = 20
SIM_STATE_UNKNOWN = 0
SIM_STATE_ABSENT = 1
SIM_STATE_PIN_REQUIRED = 2
SIM_STATE_PUK_REQUIRED = 3
SIM_STATE_NETWORK_LOCKED = 4
SIM_STATE_READY = 5
SIM_STATE_NOT_READY = 6
SIM_STATE_PERM_DISABLED = 7
DATA_DISCONNECTED = 0
DATA_CONNECTING = 1
DATA_CONNECTED = 2
DATA_SUSPENDED = 3
CALL_STATE_IDLE = 0
CALL_STATE_RINGING = 1
CALL_STATE_OFFHOOK = 2
def __init__(self, context: Any = None):
super().__init__()
self._context = context
def getNetworkOperatorName(self) -> str:
return "MockOperator"
def getNetworkOperator(self) -> str:
return "310260"
def getNetworkCountryIso(self) -> str:
return "us"
def getNetworkType(self) -> int:
return self.NETWORK_TYPE_LTE
def getPhoneType(self) -> int:
return self.PHONE_TYPE_GSM
def getDataNetworkType(self) -> int:
return self.NETWORK_TYPE_LTE
def getVoiceNetworkType(self) -> int:
return self.NETWORK_TYPE_LTE
def getSimOperator(self) -> str:
return "310260"
def getSimOperatorName(self) -> str:
return "MockSIM"
def getSimCountryIso(self) -> str:
return "us"
def getSimSerialNumber(self) -> Optional[str]:
return "89014103211118510720"
def getSubscriberId(self) -> Optional[str]:
return "310260123456789"
def getDeviceId(self) -> Optional[str]:
return "000000000000000"
def getDeviceSoftwareVersion(self) -> Optional[str]:
return "00.00.00.00"
def getLine1Number(self) -> Optional[str]:
return "15551234567"
def getGroupIdLevel1(self) -> Optional[str]:
return ""
def getIccAuthentication(self, app_type: int, aid: str, data: bytes) -> Optional[bytes]:
return None
def getVoiceMailNumber(self) -> Optional[str]:
return "15551234567"
def getVoiceMailAlphaTag(self) -> str:
return "Voicemail"
def isNetworkRoaming(self) -> bool:
return False
def hasIccCard(self) -> bool:
return True
def getSimState(self) -> int:
return self.SIM_STATE_READY
def getCellLocation(self) -> Any:
return None
def getAllCellInfo(self) -> Optional[List[CellInfo]]:
return []
def getMmsUserAgent(self) -> str:
return "Android-Mms/1.0"
def getMmsUAProfUrl(self) -> str:
return "http://www.example.com/uaprof.xml"
def listen(self, listener: PhoneStateListener, events: int):
pass
def getDataState(self) -> int:
return self.DATA_CONNECTED
def getDataActivity(self) -> int:
return 0 # DATA_ACTIVITY_NONE
def getCallState(self) -> int:
return self.CALL_STATE_IDLE
def toString(self) -> str:
return "TelephonyManager()"
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"TelephonyManager",
"CellInfo",
"CellInfoGsm",
"CellInfoCdma",
"CellInfoLte",
"CellInfoWcdma",
"CellInfoNr",
"CellIdentityGsm",
"CellIdentityCdma",
"CellIdentityLte",
"CellIdentityWcdma",
"CellIdentityNr",
"CellSignalStrengthGsm",
"CellSignalStrengthCdma",
"CellSignalStrengthLte",
"CellSignalStrengthWcdma",
"CellSignalStrengthNr",
"PhoneStateListener",
"PhoneNumberUtils",
"ServiceState",
"SignalStrength",
"SmsManager",
]
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+935
View File
@@ -0,0 +1,935 @@
"""
Android View Animation 组件 — Animation, AnimationSet, Interpolators,
AnimationUtils, Transformation 等
方法签名完全对齐 Android SDK
"""
import math
import sys
import time
from typing import Any, Callable, Dict, List, Optional, Tuple
from .._core import Stub, AndroidOnlyError, android_context
# ═══════════════════════════════════════════════════════════════
# Interpolator
# ═══════════════════════════════════════════════════════════════
class Interpolator(Stub):
"""Interpolator — 插值器接口"""
def getInterpolation(self, input: float) -> float:
return input
# ═══════════════════════════════════════════════════════════════
# AccelerateInterpolator
# ═══════════════════════════════════════════════════════════════
class AccelerateInterpolator(Interpolator):
"""AccelerateInterpolator — 加速插值器(先慢后快)"""
def __init__(self, factor: float = 1.0):
super().__init__()
self._factor = factor
self._double_factor = 2.0 * factor
def getInterpolation(self, input: float) -> float:
if self._factor == 1.0:
return input * input
return input ** (self._double_factor)
# ═══════════════════════════════════════════════════════════════
# DecelerateInterpolator
# ═══════════════════════════════════════════════════════════════
class DecelerateInterpolator(Interpolator):
"""DecelerateInterpolator — 减速插值器(先快后慢)"""
def __init__(self, factor: float = 1.0):
super().__init__()
self._factor = factor
def getInterpolation(self, input: float) -> float:
if self._factor == 1.0:
return 1.0 - (1.0 - input) * (1.0 - input)
return 1.0 - (1.0 - input) ** (2.0 * self._factor)
# ═══════════════════════════════════════════════════════════════
# AccelerateDecelerateInterpolator
# ═══════════════════════════════════════════════════════════════
class AccelerateDecelerateInterpolator(Interpolator):
"""AccelerateDecelerateInterpolator — 加速减速插值器"""
def getInterpolation(self, input: float) -> float:
return float(math.cos((input + 1) * math.pi) / 2.0 + 0.5)
# ═══════════════════════════════════════════════════════════════
# LinearInterpolator
# ═══════════════════════════════════════════════════════════════
class LinearInterpolator(Interpolator):
"""LinearInterpolator — 线性插值器"""
def getInterpolation(self, input: float) -> float:
return input
# ═══════════════════════════════════════════════════════════════
# OvershootInterpolator
# ═══════════════════════════════════════════════════════════════
class OvershootInterpolator(Interpolator):
"""OvershootInterpolator — 超出插值器(越过目标再回弹)"""
def __init__(self, tension: float = 2.0):
super().__init__()
self._tension = tension
def getInterpolation(self, input: float) -> float:
input -= 1.0
return input * input * ((self._tension + 1) * input + self._tension) + 1.0
# ═══════════════════════════════════════════════════════════════
# AnticipateInterpolator
# ═══════════════════════════════════════════════════════════════
class AnticipateInterpolator(Interpolator):
"""AnticipateInterpolator — 预判插值器(先反向后正向前进)"""
def __init__(self, tension: float = 2.0):
super().__init__()
self._tension = tension
def getInterpolation(self, input: float) -> float:
return input * input * ((self._tension + 1) * input - self._tension)
# ═══════════════════════════════════════════════════════════════
# AnticipateOvershootInterpolator
# ═══════════════════════════════════════════════════════════════
class AnticipateOvershootInterpolator(Interpolator):
"""AnticipateOvershootInterpolator — 预判超出插值器"""
def __init__(self, tension: float = 2.0, extra_tension: float = 1.5):
super().__init__()
self._tension = tension
self._extra_tension = extra_tension
def getInterpolation(self, input: float) -> float:
if input < 0.5:
t = input * 2.0
return 0.5 * t * t * ((self._tension + 1) * t - self._tension)
else:
t = input * 2.0 - 2.0
return 0.5 * (t * t * ((self._tension + 1) * t + self._tension) + 2.0)
# ═══════════════════════════════════════════════════════════════
# BounceInterpolator
# ═══════════════════════════════════════════════════════════════
class BounceInterpolator(Interpolator):
"""BounceInterpolator — 弹跳插值器"""
@staticmethod
def _bounce(t: float) -> float:
t2 = t * t
return t2 * 8.0
def getInterpolation(self, input: float) -> float:
if input < 0.3535:
return BounceInterpolator._bounce(input)
elif input < 0.7408:
return BounceInterpolator._bounce(input - 0.54719) + 0.7
elif input < 0.9644:
return BounceInterpolator._bounce(input - 0.8526) + 0.9
else:
return BounceInterpolator._bounce(input - 1.0435) + 0.95
# ═══════════════════════════════════════════════════════════════
# CycleInterpolator
# ═══════════════════════════════════════════════════════════════
class CycleInterpolator(Interpolator):
"""CycleInterpolator — 周期插值器(正弦循环)"""
def __init__(self, cycles: float = 1.0):
super().__init__()
self._cycles = cycles
def getInterpolation(self, input: float) -> float:
return float(math.sin(2.0 * math.pi * self._cycles * input))
# ═══════════════════════════════════════════════════════════════
# PathInterpolator
# ═══════════════════════════════════════════════════════════════
class PathInterpolator(Interpolator):
"""PathInterpolator — 路径插值器"""
def __init__(self, control_x1: float = 0.0, control_y1: float = 0.0,
control_x2: float = 1.0, control_y2: float = 1.0):
super().__init__()
self._control_x1 = control_x1
self._control_y1 = control_y1
self._control_x2 = control_x2
self._control_y2 = control_y2
def getInterpolation(self, input: float) -> float:
return self._cubic_bezier(input)
def _cubic_bezier(self, t: float) -> float:
u = 1.0 - t
return (3.0 * u * u * t * self._control_y1 +
3.0 * u * t * t * self._control_y2 +
t * t * t)
# ═══════════════════════════════════════════════════════════════
# Transformation
# ═══════════════════════════════════════════════════════════════
class Transformation(Stub):
"""Transformation — 动画变换矩阵"""
TYPE_IDENTITY = 0
TYPE_ALPHA = 1
TYPE_BOTH = 2
def __init__(self):
super().__init__()
self._alpha: float = 1.0
self._matrix: Any = None # 占位,实际 Android 为 Matrix 对象
self._transformation_type: int = Transformation.TYPE_IDENTITY
def getAlpha(self) -> float:
return self._alpha
def setAlpha(self, alpha: float):
self._alpha = alpha
def getMatrix(self) -> Any:
return self._matrix
def setMatrix(self, matrix: Any):
self._matrix = matrix
def setTransformationType(self, transformation_type: int):
self._transformation_type = transformation_type
def getTransformationType(self) -> int:
return self._transformation_type
def clear(self):
self._alpha = 1.0
self._matrix = None
self._transformation_type = Transformation.TYPE_IDENTITY
def compose(self, other: "Transformation"):
if other is None:
return
self._alpha *= other._alpha
if self._matrix is None:
self._matrix = other._matrix
elif other._matrix is not None:
pass # 占位:矩阵乘法
self._transformation_type = max(self._transformation_type,
other._transformation_type)
def toString(self) -> str:
return (f"Transformation(alpha={self._alpha}, "
f"type={self._transformation_type})")
# ═══════════════════════════════════════════════════════════════
# Animation
# ═══════════════════════════════════════════════════════════════
class Animation(Stub):
"""Animation — 动画基类"""
INFINITE = -1
RESTART = 1
REVERSE = 2
ABSOLUTE = 0
RELATIVE_TO_SELF = 1
RELATIVE_TO_PARENT = 2
START_ON_FIRST_FRAME = -1
class AnimationListener(Stub):
"""AnimationListener — 动画监听器"""
def onAnimationStart(self, animation: "Animation"):
pass
def onAnimationEnd(self, animation: "Animation"):
pass
def onAnimationRepeat(self, animation: "Animation"):
pass
def __init__(self):
super().__init__()
self._duration: int = 0
self._start_offset: int = 0
self._repeat_count: int = 0
self._repeat_mode: int = Animation.RESTART
self._fill_before: bool = False
self._fill_after: bool = False
self._fill_enabled: bool = False
self._z_adjustment: int = 0
self._interpolator: Optional[Interpolator] = None
self._listener: Optional["Animation.AnimationListener"] = None
self._start_time: Optional[int] = None
self._started: bool = False
self._ended: bool = False
self._cycle: float = 0.0
self._current_repeat: int = 0
self._initialized: bool = False
self._scale_factor: float = 1.0
self._paused: bool = False
self._more: bool = True
def start(self):
self._start_time = self._current_time_millis()
self._started = True
self._ended = False
self._current_repeat = 0
self._more = True
if self._listener:
self._listener.onAnimationStart(self)
def startNow(self):
self.start()
def cancel(self):
self._ended = True
self._more = False
def reset(self):
self._start_time = None
self._started = False
self._ended = False
self._current_repeat = 0
self._cycle = 0.0
self._more = True
self._initialized = False
def setDuration(self, duration_millis: int):
self._duration = duration_millis
def getDuration(self) -> int:
return self._duration
def setStartOffset(self, start_offset: int):
self._start_offset = start_offset
def getStartOffset(self) -> int:
return self._start_offset
def setRepeatCount(self, repeat_count: int):
self._repeat_count = repeat_count
def getRepeatCount(self) -> int:
return self._repeat_count
def setRepeatMode(self, repeat_mode: int):
self._repeat_mode = repeat_mode
def getRepeatMode(self) -> int:
return self._repeat_mode
def setInterpolator(self, interpolator: Optional[Interpolator]):
self._interpolator = interpolator
def getInterpolator(self) -> Optional[Interpolator]:
return self._interpolator
def setAnimationListener(self, listener: "Animation.AnimationListener"):
self._listener = listener
def getAnimationListener(self) -> Optional["Animation.AnimationListener"]:
return self._listener
def getTransformation(self, current_time: int,
out_transformation: Transformation) -> bool:
if self._start_time is None:
self._start_time = current_time
elapsed = current_time - self._start_time - self._start_offset
if elapsed < 0:
out_transformation.clear()
return True
duration = self._duration
if duration == 0:
duration = 1
normalized = elapsed / duration
if normalized >= 1.0:
if self._current_repeat < self._repeat_count or self._repeat_count == self.INFINITE:
if self._repeat_mode == Animation.REVERSE:
normalized = 0.0
else:
normalized = 0.0
self._current_repeat += 1
if self._listener:
self._listener.onAnimationRepeat(self)
else:
normalized = 1.0
self._ended = True
self._more = False
self._apply_transformation(1.0, out_transformation)
if self._listener:
self._listener.onAnimationEnd(self)
return False
if normalized > 1.0:
normalized = 1.0
if self._interpolator:
interpolated = self._interpolator.getInterpolation(normalized)
else:
interpolated = normalized
self._cycle = normalized
self._apply_transformation(interpolated, out_transformation)
return self._more
def _apply_transformation(self, interpolated_time: float, t: Transformation):
pass
def hasEnded(self) -> bool:
return self._ended
def hasStarted(self) -> bool:
return self._started
def setFillBefore(self, fill_before: bool):
self._fill_before = fill_before
def getFillBefore(self) -> bool:
return self._fill_before
def setFillAfter(self, fill_after: bool):
self._fill_after = fill_after
def getFillAfter(self) -> bool:
return self._fill_after
def setFillEnabled(self, fill_enabled: bool):
self._fill_enabled = fill_enabled
def getFillEnabled(self) -> bool:
return self._fill_enabled
def setZAdjustment(self, z_adjustment: int):
self._z_adjustment = z_adjustment
def getZAdjustment(self) -> int:
return self._z_adjustment
def willChangeBounds(self) -> bool:
return True
def willChangeTransformationMatrix(self) -> bool:
return True
def initialize(self, width: int, height: int, parent_width: int, parent_height: int):
self._initialized = True
def restrictDuration(self, duration_millis: int):
if self._duration > duration_millis:
self._duration = duration_millis
def scaleCurrentDuration(self, scale: float):
self._duration = int(self._duration * scale)
@staticmethod
def _current_time_millis() -> int:
return int(time.time() * 1000)
def toString(self) -> str:
return (f"Animation(duration={self._duration}, "
f"repeatCount={self._repeat_count}, "
f"started={self._started})")
# ═══════════════════════════════════════════════════════════════
# AlphaAnimation
# ═══════════════════════════════════════════════════════════════
class AlphaAnimation(Animation):
"""AlphaAnimation — 透明度动画"""
def __init__(self, from_alpha: float, to_alpha: float):
super().__init__()
self._from_alpha = from_alpha
self._to_alpha = to_alpha
def _apply_transformation(self, interpolated_time: float, t: Transformation):
alpha = self._from_alpha + (self._to_alpha - self._from_alpha) * interpolated_time
t.setAlpha(alpha)
def getFromAlpha(self) -> float:
return self._from_alpha
def getToAlpha(self) -> float:
return self._to_alpha
# ═══════════════════════════════════════════════════════════════
# ScaleAnimation
# ═══════════════════════════════════════════════════════════════
class ScaleAnimation(Animation):
"""ScaleAnimation — 缩放动画"""
def __init__(self, from_x: float, to_x: float, from_y: float, to_y: float,
pivot_x: float = 0.0, pivot_y: float = 0.0,
pivot_x_type: int = Animation.ABSOLUTE,
pivot_y_type: int = Animation.ABSOLUTE):
super().__init__()
self._from_x = from_x
self._to_x = to_x
self._from_y = from_y
self._to_y = to_y
self._pivot_x = pivot_x
self._pivot_y = pivot_y
self._pivot_x_type = pivot_x_type
self._pivot_y_type = pivot_y_type
def _apply_transformation(self, interpolated_time: float, t: Transformation):
sx = self._from_x + (self._to_x - self._from_x) * interpolated_time
sy = self._from_y + (self._to_y - self._from_y) * interpolated_time
# 占位:Matrix 的缩放操作
if t.getMatrix() is not None:
pass
def getFromX(self) -> float:
return self._from_x
def getToX(self) -> float:
return self._to_x
def getFromY(self) -> float:
return self._from_y
def getToY(self) -> float:
return self._to_y
def getPivotX(self) -> float:
return self._pivot_x
def getPivotY(self) -> float:
return self._pivot_y
# ═══════════════════════════════════════════════════════════════
# TranslateAnimation
# ═══════════════════════════════════════════════════════════════
class TranslateAnimation(Animation):
"""TranslateAnimation — 平移动画"""
def __init__(self, from_x: float, to_x: float, from_y: float, to_y: float,
from_x_type: int = Animation.ABSOLUTE,
to_x_type: int = Animation.ABSOLUTE,
from_y_type: int = Animation.ABSOLUTE,
to_y_type: int = Animation.ABSOLUTE):
super().__init__()
self._from_x = from_x
self._to_x = to_x
self._from_y = from_y
self._to_y = to_y
self._from_x_type = from_x_type
self._to_x_type = to_x_type
self._from_y_type = from_y_type
self._to_y_type = to_y_type
def _apply_transformation(self, interpolated_time: float, t: Transformation):
dx = self._from_x + (self._to_x - self._from_x) * interpolated_time
dy = self._from_y + (self._to_y - self._from_y) * interpolated_time
if t.getMatrix() is not None:
pass
def getFromX(self) -> float:
return self._from_x
def getToX(self) -> float:
return self._to_x
def getFromY(self) -> float:
return self._from_y
def getToY(self) -> float:
return self._to_y
# ═══════════════════════════════════════════════════════════════
# RotateAnimation
# ═══════════════════════════════════════════════════════════════
class RotateAnimation(Animation):
"""RotateAnimation — 旋转动画"""
def __init__(self, from_degrees: float, to_degrees: float,
pivot_x: float = 0.0, pivot_y: float = 0.0,
pivot_x_type: int = Animation.ABSOLUTE,
pivot_y_type: int = Animation.ABSOLUTE):
super().__init__()
self._from_degrees = from_degrees
self._to_degrees = to_degrees
self._pivot_x = pivot_x
self._pivot_y = pivot_y
self._pivot_x_type = pivot_x_type
self._pivot_y_type = pivot_y_type
def _apply_transformation(self, interpolated_time: float, t: Transformation):
degrees = self._from_degrees + (self._to_degrees - self._from_degrees) * interpolated_time
if t.getMatrix() is not None:
pass
def getFromDegrees(self) -> float:
return self._from_degrees
def getToDegrees(self) -> float:
return self._to_degrees
def getPivotX(self) -> float:
return self._pivot_x
def getPivotY(self) -> float:
return self._pivot_y
# ═══════════════════════════════════════════════════════════════
# AnimationSet
# ═══════════════════════════════════════════════════════════════
class AnimationSet(Animation):
"""AnimationSet — 动画集合"""
def __init__(self, share_interpolator: bool = True):
super().__init__()
self._share_interpolator = share_interpolator
self._animations: List[Animation] = []
def addAnimation(self, animation: Animation):
self._animations.append(animation)
if self._share_interpolator and self._interpolator:
animation.setInterpolator(self._interpolator)
def getAnimations(self) -> List[Animation]:
return list(self._animations)
def setDuration(self, duration_millis: int):
super().setDuration(duration_millis)
for anim in self._animations:
anim.setDuration(duration_millis)
def setFillAfter(self, fill_after: bool):
super().setFillAfter(fill_after)
for anim in self._animations:
anim.setFillAfter(fill_after)
def setFillBefore(self, fill_before: bool):
super().setFillBefore(fill_before)
for anim in self._animations:
anim.setFillBefore(fill_before)
def setRepeatMode(self, repeat_mode: int):
super().setRepeatMode(repeat_mode)
for anim in self._animations:
anim.setRepeatMode(repeat_mode)
def setRepeatCount(self, repeat_count: int):
super().setRepeatCount(repeat_count)
for anim in self._animations:
anim.setRepeatCount(repeat_count)
def setStartOffset(self, start_offset: int):
super().setStartOffset(start_offset)
for anim in self._animations:
anim.setStartOffset(start_offset)
def setInterpolator(self, interpolator: Optional[Interpolator]):
super().setInterpolator(interpolator)
if self._share_interpolator:
for anim in self._animations:
anim.setInterpolator(interpolator)
def start(self):
super().start()
for anim in self._animations:
anim.start()
def reset(self):
super().reset()
for anim in self._animations:
anim.reset()
def cancel(self):
super().cancel()
for anim in self._animations:
anim.cancel()
def getTransformation(self, current_time: int,
out_transformation: Transformation) -> bool:
more = False
for anim in self._animations:
local_t = Transformation()
if anim.getTransformation(current_time, local_t):
more = True
out_transformation.compose(local_t)
return more
def initialize(self, width: int, height: int, parent_width: int, parent_height: int):
super().initialize(width, height, parent_width, parent_height)
for anim in self._animations:
anim.initialize(width, height, parent_width, parent_height)
def willChangeBounds(self) -> bool:
for anim in self._animations:
if anim.willChangeBounds():
return True
return False
def willChangeTransformationMatrix(self) -> bool:
for anim in self._animations:
if anim.willChangeTransformationMatrix():
return True
return False
def getAnimations(self) -> List[Animation]:
return list(self._animations)
def restrictDuration(self, duration_millis: int):
super().restrictDuration(duration_millis)
for anim in self._animations:
anim.restrictDuration(duration_millis)
def scaleCurrentDuration(self, scale: float):
super().scaleCurrentDuration(scale)
for anim in self._animations:
anim.scaleCurrentDuration(scale)
# ═══════════════════════════════════════════════════════════════
# Animation.AnimationListener (已定义在 Animation 类中)
# ═══════════════════════════════════════════════════════════════
# Animation.AnimationListener is defined inside the Animation class above
# ═══════════════════════════════════════════════════════════════
# AnimationUtils
# ═══════════════════════════════════════════════════════════════
class AnimationUtils(Stub):
"""AnimationUtils — 动画工具类"""
@staticmethod
def loadAnimation(context: Any, id: int) -> Animation:
"""加载动画资源(占位实现)"""
sys.stderr.write(f"[AnimationUtils] loadAnimation: id={id}\n")
return Animation()
@staticmethod
def makeInAnimation(context: Any, from_left: bool) -> Animation:
"""创建进入动画"""
anim = AlphaAnimation(0.0, 1.0)
anim.setDuration(300)
return anim
@staticmethod
def makeOutAnimation(context: Any, to_left: bool) -> Animation:
"""创建退出动画"""
anim = AlphaAnimation(1.0, 0.0)
anim.setDuration(300)
return anim
@staticmethod
def makeInChildBottomAnimation(context: Any) -> Animation:
"""创建子视图从底部进入动画"""
anim = TranslateAnimation(0, 0, 1.0, 0.0,
from_x_type=Animation.RELATIVE_TO_SELF,
to_x_type=Animation.RELATIVE_TO_SELF,
from_y_type=Animation.RELATIVE_TO_SELF,
to_y_type=Animation.RELATIVE_TO_SELF)
anim.setDuration(300)
return anim
@staticmethod
def currentAnimationTimeMillis() -> int:
return int(time.time() * 1000)
# ═══════════════════════════════════════════════════════════════
# LayoutAnimationController
# ═══════════════════════════════════════════════════════════════
class LayoutAnimationController(Stub):
"""LayoutAnimationController — 布局动画控制器"""
ORDER_NORMAL = 0
ORDER_REVERSE = 1
ORDER_RANDOM = 2
def __init__(self, animation: Optional[Animation] = None):
super().__init__()
self._animation: Optional[Animation] = animation
self._delay: float = 0.5
self._order: int = LayoutAnimationController.ORDER_NORMAL
self._initialized: bool = False
self._max_delay: int = 0
def setAnimation(self, animation: Animation):
self._animation = animation
def getAnimation(self) -> Optional[Animation]:
return self._animation
def setOrder(self, order: int):
self._order = order
def getOrder(self) -> int:
return self._order
def setDelay(self, delay: float):
self._delay = delay
def getDelay(self) -> float:
return self._delay
def getDelayForView(self, view: Any) -> int:
if self._order == LayoutAnimationController.ORDER_NORMAL:
return 0
elif self._order == LayoutAnimationController.ORDER_REVERSE:
return 0
elif self._order == LayoutAnimationController.ORDER_RANDOM:
import random
return int(random.random() * self._max_delay)
return 0
def willOverlap(self) -> bool:
return self._delay < 1.0
def start(self):
self._initialized = True
def isDone(self) -> bool:
return True
def getAnimationForView(self, view: Any) -> Optional[Animation]:
return self._animation
def setInitialized(self, initialized: bool):
self._initialized = initialized
# ═══════════════════════════════════════════════════════════════
# GridLayoutAnimationController
# ═══════════════════════════════════════════════════════════════
class GridLayoutAnimationController(LayoutAnimationController):
"""GridLayoutAnimationController — 网格布局动画控制器"""
DIRECTION_LEFT_TO_RIGHT = 0x0
DIRECTION_RIGHT_TO_LEFT = 0x1
DIRECTION_TOP_TO_BOTTOM = 0x2
DIRECTION_BOTTOM_TO_TOP = 0x3
DIRECTION_HORIZONTAL_MASK = 0x1
DIRECTION_VERTICAL_MASK = 0x2
def __init__(self, animation: Optional[Animation] = None):
super().__init__(animation)
self._column_delay: float = 0.5
self._row_delay: float = 0.5
self._direction: int = GridLayoutAnimationController.DIRECTION_LEFT_TO_RIGHT
self._columns: int = 1
self._rows: int = 1
def setColumnDelay(self, column_delay: float):
self._column_delay = column_delay
def getColumnDelay(self) -> float:
return self._column_delay
def setRowDelay(self, row_delay: float):
self._row_delay = row_delay
def getRowDelay(self) -> float:
return self._row_delay
def setDirection(self, direction: int):
self._direction = direction
def getDirection(self) -> int:
return self._direction
def setColumns(self, columns: int):
self._columns = columns
def getColumns(self) -> int:
return self._columns
def setRows(self, rows: int):
self._rows = rows
def getRows(self) -> int:
return self._rows
def getDelayForView(self, view: Any) -> int:
duration = 0
if self._animation:
duration = self._animation.getDuration()
return int(duration * self._column_delay)
def willOverlap(self) -> bool:
return self._column_delay < 1.0 or self._row_delay < 1.0
def getAnimationForView(self, view: Any) -> Optional[Animation]:
return self._animation
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"Animation",
"AnimationSet",
"AlphaAnimation",
"ScaleAnimation",
"TranslateAnimation",
"RotateAnimation",
"AnimationUtils",
"Interpolator",
"AccelerateInterpolator",
"DecelerateInterpolator",
"AccelerateDecelerateInterpolator",
"LinearInterpolator",
"OvershootInterpolator",
"AnticipateInterpolator",
"AnticipateOvershootInterpolator",
"BounceInterpolator",
"CycleInterpolator",
"PathInterpolator",
"Transformation",
"LayoutAnimationController",
"GridLayoutAnimationController",
]
+859
View File
@@ -0,0 +1,859 @@
"""
Android View InputMethod 组件 — InputMethodManager, EditorInfo,
InputConnection, ExtractedText 等
方法签名完全对齐 Android SDK
"""
import sys
from typing import Any, Dict, List, Optional, Tuple
from .._core import Stub, AndroidOnlyError, android_context
# ═══════════════════════════════════════════════════════════════
# InputMethodManager
# ═══════════════════════════════════════════════════════════════
class InputMethodManager(Stub):
"""InputMethodManager — 输入法管理器"""
SHOW_IMPLICIT = 0x0001
SHOW_FORCED = 0x0002
HIDE_IMPLICIT_ONLY = 0x0001
HIDE_NOT_ALWAYS = 0x0002
RESULT_SHOWN = 2
RESULT_HIDDEN = 3
RESULT_UNCHANGED_SHOWN = 4
RESULT_UNCHANGED_HIDDEN = 5
def __init__(self):
super().__init__()
self._active: bool = False
self._current_input_connection: Optional["InputConnection"] = None
self._is_accepting_text: bool = True
self._fullscreen_mode: bool = False
self._cursor_x: int = 0
self._cursor_y: int = 0
self._selection_start: int = -1
self._selection_end: int = -1
self._composing_start: int = -1
self._composing_end: int = -1
def showSoftInput(self, view: Any, flags: int,
result_receiver: Any = None) -> bool:
self._active = True
sys.stderr.write(f"[InputMethodManager] showSoftInput: flags={flags}\n")
return True
def hideSoftInputFromWindow(self, window_token: Any, flags: int,
result_receiver: Any = None) -> bool:
self._active = False
sys.stderr.write(f"[InputMethodManager] hideSoftInputFromWindow: flags={flags}\n")
return True
def toggleSoftInput(self, show_flags: int, hide_flags: int):
self._active = not self._active
sys.stderr.write(f"[InputMethodManager] toggleSoftInput: "
f"show={show_flags}, hide={hide_flags}\n")
def restartInput(self, view: Any):
sys.stderr.write("[InputMethodManager] restartInput\n")
def startInput(self, view: Any) -> bool:
sys.stderr.write("[InputMethodManager] startInput\n")
return True
def updateSelection(self, view: Any, sel_start: int, sel_end: int,
composing_start: int, composing_end: int):
self._selection_start = sel_start
self._selection_end = sel_end
self._composing_start = composing_start
self._composing_end = composing_end
def updateCursorAnchorInfo(self, view: Any, cursor_anchor_info: "CursorAnchorInfo"):
self._cursor_x = cursor_anchor_info.getInsertionMarkerBaseline()
self._cursor_y = cursor_anchor_info.getInsertionMarkerTop()
def displayCompletions(self, view: Any, completions: List["CompletionInfo"]):
sys.stderr.write(f"[InputMethodManager] displayCompletions: "
f"{len(completions) if completions else 0} items\n")
def sendKeyChar(self, key_char: str):
sys.stderr.write(f"[InputMethodManager] sendKeyChar: '{key_char}'\n")
def clearMetaKeyStates(self, view: Any, states: int):
sys.stderr.write(f"[InputMethodManager] clearMetaKeyStates: {states}\n")
def isAcceptingText(self) -> bool:
return self._is_accepting_text
def isActive(self) -> bool:
return self._active
def isFullscreenMode(self) -> bool:
return self._fullscreen_mode
def setFullscreenMode(self, fullscreen: bool):
self._fullscreen_mode = fullscreen
def getCurrentInputMethodSubtype(self) -> Optional["InputMethodSubtype"]:
return None
def getShortcutInputMethodsAndSubtypes(self) -> List[Any]:
return []
def showStatusIcon(self, icon: Any):
pass
def hideStatusIcon(self):
pass
def registerSuggestionSpansForNotification(self, spans: List[Any], sender: Any):
pass
def notifySuggestionPicked(self, suggestion: Any):
pass
def windowDismissed(self, window_token: Any):
self._active = False
def toString(self) -> str:
return f"InputMethodManager(active={self._active})"
# ═══════════════════════════════════════════════════════════════
# EditorInfo
# ═══════════════════════════════════════════════════════════════
class EditorInfo(Stub):
"""EditorInfo — 编辑器信息"""
TYPE_CLASS_TEXT = 0x0001
TYPE_CLASS_NUMBER = 0x0002
TYPE_CLASS_PHONE = 0x0004
TYPE_CLASS_DATETIME = 0x0008
TYPE_TEXT_VARIATION_NORMAL = 0x0000
TYPE_TEXT_VARIATION_URI = 0x0010
TYPE_TEXT_VARIATION_EMAIL_ADDRESS = 0x0020
TYPE_TEXT_VARIATION_EMAIL_SUBJECT = 0x0030
TYPE_TEXT_VARIATION_SHORT_MESSAGE = 0x0040
TYPE_TEXT_VARIATION_LONG_MESSAGE = 0x0050
TYPE_TEXT_VARIATION_PERSON_NAME = 0x0060
TYPE_TEXT_VARIATION_POSTAL_ADDRESS = 0x0070
TYPE_TEXT_VARIATION_PASSWORD = 0x0080
TYPE_TEXT_VARIATION_VISIBLE_PASSWORD = 0x0090
TYPE_TEXT_VARIATION_WEB_EDIT_TEXT = 0x00A0
TYPE_TEXT_VARIATION_FILTER = 0x00B0
TYPE_TEXT_VARIATION_PHONETIC = 0x00C0
TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS = 0x00D0
TYPE_TEXT_VARIATION_WEB_PASSWORD = 0x00E0
TYPE_NUMBER_VARIATION_NORMAL = 0x0000
TYPE_NUMBER_VARIATION_PASSWORD = 0x0010
TYPE_DATETIME_VARIATION_NORMAL = 0x0000
TYPE_DATETIME_VARIATION_DATE = 0x0010
TYPE_DATETIME_VARIATION_TIME = 0x0020
TYPE_NULL = 0x0000
IME_ACTION_NONE = 0x0001
IME_ACTION_GO = 0x0002
IME_ACTION_SEARCH = 0x0003
IME_ACTION_SEND = 0x0004
IME_ACTION_NEXT = 0x0005
IME_ACTION_DONE = 0x0006
IME_ACTION_PREVIOUS = 0x0007
IME_FLAG_NO_EXTRACT_UI = 0x10000001
IME_FLAG_NO_ACCESSORY_ACTION = 0x20000000
IME_FLAG_NO_ENTER_ACTION = 0x40000000
IME_FLAG_NAVIGATE_NEXT = 0x08000000
IME_FLAG_NAVIGATE_PREVIOUS = 0x04000000
IME_FLAG_NO_FULLSCREEN = 0x02000000
IME_FLAG_FORCE_ASCII = 0x80000000
IME_MASK_ACTION = 0x000000FF
def __init__(self):
super().__init__()
self.inputType: int = EditorInfo.TYPE_CLASS_TEXT
self.imeOptions: int = EditorInfo.IME_ACTION_NONE
self.actionLabel: Optional[str] = None
self.actionId: int = 0
self.initialSelStart: int = -1
self.initialSelEnd: int = -1
self.initialCapsMode: int = 0
self.hintText: Optional[str] = None
self.label: Optional[str] = None
self.packageName: Optional[str] = None
self.fieldId: int = 0
self.fieldName: Optional[str] = None
self.extras: Optional[Any] = None
def toString(self) -> str:
return (f"EditorInfo(inputType={hex(self.inputType)}, "
f"imeOptions={hex(self.imeOptions)}, "
f"actionLabel={self.actionLabel})")
def dump(self, printer: Any, prefix: str):
sys.stderr.write(f"{prefix}EditorInfo: inputType={hex(self.inputType)}\n")
# ═══════════════════════════════════════════════════════════════
# InputConnection
# ═══════════════════════════════════════════════════════════════
class InputConnection(Stub):
"""InputConnection — 输入连接接口"""
def __init__(self):
super().__init__()
self._text: str = ""
self._selection_start: int = 0
self._selection_end: int = 0
self._composing_start: int = -1
self._composing_end: int = -1
self._batch_edit_count: int = 0
def getTextBeforeCursor(self, length: int, flags: int) -> Optional[str]:
end = self._selection_start
start = max(0, end - length)
return self._text[start:end]
def getTextAfterCursor(self, length: int, flags: int) -> Optional[str]:
start = self._selection_end
end = min(len(self._text), start + length)
return self._text[start:end]
def getExtractedText(self, request: "ExtractedTextRequest",
flags: int) -> Optional["ExtractedText"]:
result = ExtractedText()
result.text = self._text
result.startOffset = 0
result.selectionStart = self._selection_start
result.selectionEnd = self._selection_end
result.flags = 0
return result
def commitText(self, text: Any, new_cursor_position: int) -> bool:
if isinstance(text, str):
committed = text
else:
committed = str(text) if text else ""
before = self._text[:self._selection_start]
after = self._text[self._selection_end:]
self._text = before + committed + after
if new_cursor_position > 0:
self._selection_start = len(before) + new_cursor_position - 1
else:
self._selection_start = len(before) + len(committed) + new_cursor_position
self._selection_end = self._selection_start
self._composing_start = -1
self._composing_end = -1
return True
def setComposingText(self, text: Any, new_cursor_position: int) -> bool:
if self._composing_start < 0:
self._composing_start = self._selection_start
self._composing_end = self._selection_end
before = self._text[:self._composing_start]
after = self._text[self._composing_end:]
if isinstance(text, str):
composing = text
else:
composing = str(text) if text else ""
self._text = before + composing + after
self._composing_end = self._composing_start + len(composing)
self._selection_start = self._composing_end + new_cursor_position
self._selection_end = self._selection_start
return True
def setSelection(self, start: int, end: int) -> bool:
self._selection_start = max(0, min(start, len(self._text)))
self._selection_end = max(0, min(end, len(self._text)))
return True
def deleteSurroundingText(self, before_length: int, after_length: int) -> bool:
start = max(0, self._selection_start - before_length)
end = min(len(self._text), self._selection_end + after_length)
self._text = self._text[:start] + self._text[end:]
new_cursor = max(0, self._selection_start - before_length)
self._selection_start = new_cursor
self._selection_end = new_cursor
return True
def commitCompletion(self, completion: "CompletionInfo") -> bool:
sys.stderr.write(f"[InputConnection] commitCompletion: "
f"{completion.getText()}\n")
return True
def commitCorrection(self, correction: "CorrectionInfo") -> bool:
sys.stderr.write(f"[InputConnection] commitCorrection: "
f"{correction.getNewText()}\n")
return True
def getSelectedText(self, flags: int) -> Optional[str]:
if self._selection_start == self._selection_end:
return ""
return self._text[self._selection_start:self._selection_end]
def finishComposingText(self) -> bool:
self._composing_start = -1
self._composing_end = -1
return True
def beginBatchEdit(self) -> bool:
self._batch_edit_count += 1
return True
def endBatchEdit(self) -> bool:
if self._batch_edit_count > 0:
self._batch_edit_count -= 1
return True
def sendKeyEvent(self, event: Any) -> bool:
sys.stderr.write(f"[InputConnection] sendKeyEvent\n")
return True
def clearMetaKeyStates(self, states: int) -> bool:
return True
def performEditorAction(self, action_code: int) -> bool:
sys.stderr.write(f"[InputConnection] performEditorAction: {action_code}\n")
return True
def performContextMenuAction(self, id: int) -> bool:
sys.stderr.write(f"[InputConnection] performContextMenuAction: {id}\n")
return True
def commitContent(self, input_content: "InputContentInfo",
flags: int, opts: Optional[Any] = None) -> bool:
sys.stderr.write("[InputConnection] commitContent\n")
return True
def requestCursorUpdates(self, cursor_update_mode: int) -> bool:
return True
def closeConnection(self):
pass
def reportFullscreenMode(self, enabled: bool):
pass
def getSurroundingText(self, before_length: int, after_length: int,
flags: int) -> Optional["SurroundingText"]:
start = max(0, self._selection_start - before_length)
end = min(len(self._text), self._selection_end + after_length)
return SurroundingText(
text=self._text[start:end],
selection_start=self._selection_start - start,
selection_end=self._selection_end - start,
offset=start,
)
def takeSnapshot(self) -> Optional[Any]:
"""获取输入快照"""
return None
def setComposingRegion(self, start: int, end: int) -> bool:
self._composing_start = start
self._composing_end = end
return True
def getCursorCapsMode(self, req_modes: int) -> int:
return 0
def getTextBeforeCursor(self, length: int, flags: int) -> Optional[str]:
end = self._selection_start
start = max(0, end - length)
return self._text[start:end]
def getTextAfterCursor(self, length: int, flags: int) -> Optional[str]:
start = self._selection_end
end = min(len(self._text), start + length)
return self._text[start:end]
# ═══════════════════════════════════════════════════════════════
# ExtractedText
# ═══════════════════════════════════════════════════════════════
class ExtractedText(Stub):
"""ExtractedText — 提取的文本"""
FLAG_SINGLE_LINE = 0x0001
FLAG_SELECTING = 0x0002
def __init__(self):
super().__init__()
self.text: Optional[str] = None
self.startOffset: int = 0
self.partialStartOffset: int = -1
self.partialEndOffset: int = -1
self.selectionStart: int = -1
self.selectionEnd: int = -1
self.flags: int = 0
def toString(self) -> str:
return (f"ExtractedText(startOffset={self.startOffset}, "
f"text={self.text}, "
f"selection=({self.selectionStart}, {self.selectionEnd}))")
# ═══════════════════════════════════════════════════════════════
# ExtractedTextRequest
# ═══════════════════════════════════════════════════════════════
class ExtractedTextRequest(Stub):
"""ExtractedTextRequest — 提取文本请求"""
def __init__(self):
super().__init__()
self.token: int = 0
self.flags: int = 0
self.hintMaxLines: int = 0
self.hintMaxChars: int = 0
# ═══════════════════════════════════════════════════════════════
# InputBinding
# ═══════════════════════════════════════════════════════════════
class InputBinding(Stub):
"""InputBinding — 输入绑定"""
def __init__(self, connection: Optional["InputConnection"] = None,
uid: int = -1, pid: int = -1,
private_ime_options: str = ""):
super().__init__()
self._connection: Optional["InputConnection"] = connection
self._uid: int = uid
self._pid: int = pid
self._private_ime_options: str = private_ime_options
def getConnection(self) -> Optional["InputConnection"]:
return self._connection
def getUid(self) -> int:
return self._uid
def getPid(self) -> int:
return self._pid
def getPrivateImeOptions(self) -> str:
return self._private_ime_options
def toString(self) -> str:
return (f"InputBinding(uid={self._uid}, pid={self._pid}, "
f"connection={self._connection})")
# ═══════════════════════════════════════════════════════════════
# InputMethodSubtype
# ═══════════════════════════════════════════════════════════════
class InputMethodSubtype(Stub):
"""InputMethodSubtype — 输入法子类型"""
SUBTYPE_ID_NONE = -1
def __init__(self, locale: str = "", mode: str = "",
extra_value: str = "", is_auxiliary: bool = False,
overrides_ime_theme: bool = False,
subtype_id: int = SUBTYPE_ID_NONE):
super().__init__()
self._locale: str = locale
self._mode: str = mode
self._extra_value: str = extra_value
self._is_auxiliary: bool = is_auxiliary
self._overrides_ime_theme: bool = overrides_ime_theme
self._subtype_id: int = subtype_id
self._display_name_res: int = 0
self._icon_res: int = 0
def getLocale(self) -> str:
return self._locale
def getMode(self) -> str:
return self._mode
def getExtraValue(self) -> str:
return self._extra_value
def getSubtypeId(self) -> int:
return self._subtype_id
def containsExtraValueKey(self, key: str) -> bool:
if not self._extra_value:
return False
pairs = self._extra_value.split(",")
for pair in pairs:
kv = pair.split("=", 1)
if len(kv) == 2 and kv[0].strip() == key:
return True
return False
def getDisplayName(self, context: Any, package_name: str,
app_info: Any) -> str:
return self._locale or self._mode
def isAuxiliary(self) -> bool:
return self._is_auxiliary
def overridesImeTheme(self) -> bool:
return self._overrides_ime_theme
def hashCode(self) -> int:
return hash((self._locale, self._mode, self._extra_value)) & 0x7FFFFFFF
def equals(self, other: Any) -> bool:
if isinstance(other, InputMethodSubtype):
return (self._locale == other._locale and
self._mode == other._mode and
self._extra_value == other._extra_value)
return False
def toString(self) -> str:
return (f"InputMethodSubtype(locale={self._locale}, "
f"mode={self._mode})")
# ═══════════════════════════════════════════════════════════════
# CursorAnchorInfo
# ═══════════════════════════════════════════════════════════════
class CursorAnchorInfo(Stub):
"""CursorAnchorInfo — 光标锚点信息"""
FLAG_HAS_VISIBLE_REGION = 0x01
FLAG_IS_RTL = 0x02
def __init__(self):
super().__init__()
self._insertion_marker_baseline: float = 0.0
self._insertion_marker_top: float = 0.0
self._insertion_marker_bottom: float = 0.0
self._matrix: Optional[Any] = None
self._character_bounds: Dict[int, Tuple[float, float, float, float]] = {}
self._character_bounds_flags: Dict[int, int] = {}
self._composing_text: Optional[str] = None
self._composing_text_start: int = -1
def getInsertionMarkerBaseline(self) -> float:
return self._insertion_marker_baseline
def setInsertionMarkerBaseline(self, baseline: float):
self._insertion_marker_baseline = baseline
def getInsertionMarkerTop(self) -> float:
return self._insertion_marker_top
def setInsertionMarkerTop(self, top: float):
self._insertion_marker_top = top
def getInsertionMarkerBottom(self) -> float:
return self._insertion_marker_bottom
def setInsertionMarkerBottom(self, bottom: float):
self._insertion_marker_bottom = bottom
def getMatrix(self) -> Optional[Any]:
return self._matrix
def setMatrix(self, matrix: Any):
self._matrix = matrix
def getCharacterBounds(self, index: int) -> Optional[Tuple[float, float, float, float]]:
return self._character_bounds.get(index)
def setCharacterBounds(self, index: int, left: float, top: float,
right: float, bottom: float, flags: int):
self._character_bounds[index] = (left, top, right, bottom)
self._character_bounds_flags[index] = flags
def getCharacterBoundsFlags(self, index: int) -> int:
return self._character_bounds_flags.get(index, 0)
def getComposingText(self) -> Optional[str]:
return self._composing_text
def getComposingTextStart(self) -> int:
return self._composing_text_start
def toString(self) -> str:
return (f"CursorAnchorInfo(baseline={self._insertion_marker_baseline}, "
f"top={self._insertion_marker_top})")
# ═══════════════════════════════════════════════════════════════
# CorrectionInfo
# ═══════════════════════════════════════════════════════════════
class CorrectionInfo(Stub):
"""CorrectionInfo — 校正信息"""
def __init__(self, old_text: str = "", new_text: str = ""):
super().__init__()
self._old_text: str = old_text
self._new_text: str = new_text
def getOldText(self) -> str:
return self._old_text
def getNewText(self) -> str:
return self._new_text
def toString(self) -> str:
return f"CorrectionInfo('{self._old_text}' -> '{self._new_text}')"
# ═══════════════════════════════════════════════════════════════
# CompletionInfo
# ═══════════════════════════════════════════════════════════════
class CompletionInfo(Stub):
"""CompletionInfo — 补全信息"""
def __init__(self, id: int = 0, position: int = 0,
text: str = "", label: str = ""):
super().__init__()
self._id: int = id
self._position: int = position
self._text: str = text
self._label: str = label
def getId(self) -> int:
return self._id
def getPosition(self) -> int:
return self._position
def getText(self) -> str:
return self._text
def getLabel(self) -> str:
return self._label
def toString(self) -> str:
return f"CompletionInfo(id={self._id}, text='{self._text}')"
# ═══════════════════════════════════════════════════════════════
# SurroundingText
# ═══════════════════════════════════════════════════════════════
class SurroundingText(Stub):
"""SurroundingText — 周围文本"""
def __init__(self, text: str = "", selection_start: int = 0,
selection_end: int = 0, offset: int = 0):
super().__init__()
self._text: str = text
self._selection_start: int = selection_start
self._selection_end: int = selection_end
self._offset: int = offset
def getText(self) -> str:
return self._text
def getSelectionStart(self) -> int:
return self._selection_start
def getSelectionEnd(self) -> int:
return self._selection_end
def getOffset(self) -> int:
return self._offset
def toString(self) -> str:
return (f"SurroundingText(text='{self._text}', "
f"selection=({self._selection_start}, {self._selection_end}), "
f"offset={self._offset})")
# ═══════════════════════════════════════════════════════════════
# InputContentInfo
# ═══════════════════════════════════════════════════════════════
class InputContentInfo(Stub):
"""InputContentInfo — 输入内容信息"""
def __init__(self, uri: Any = None, description: Any = None,
link_uri: Any = None):
super().__init__()
self._uri: Any = uri
self._description: Any = description
self._link_uri: Any = link_uri
def getUri(self) -> Any:
return self._uri
def getDescription(self) -> Any:
return self._description
def getLinkUri(self) -> Any:
return self._link_uri
def requestPermission(self):
pass
def releasePermission(self):
pass
def toString(self) -> str:
return (f"InputContentInfo(uri={self._uri}, "
f"description={self._description})")
# ═══════════════════════════════════════════════════════════════
# InputConnectionWrapper
# ═══════════════════════════════════════════════════════════════
class InputConnectionWrapper(InputConnection):
"""InputConnectionWrapper — 输入连接包装器"""
def __init__(self, target: InputConnection, mutable: bool = True):
super().__init__()
self._target: InputConnection = target
self._mutable: bool = mutable
def setTarget(self, target: InputConnection):
if self._mutable:
self._target = target
def getTarget(self) -> InputConnection:
return self._target
def getTextBeforeCursor(self, length: int, flags: int) -> Optional[str]:
return self._target.getTextBeforeCursor(length, flags)
def getTextAfterCursor(self, length: int, flags: int) -> Optional[str]:
return self._target.getTextAfterCursor(length, flags)
def getSelectedText(self, flags: int) -> Optional[str]:
return self._target.getSelectedText(flags)
def getSurroundingText(self, before_length: int, after_length: int,
flags: int) -> Optional["SurroundingText"]:
return self._target.getSurroundingText(before_length, after_length, flags)
def getCursorCapsMode(self, req_modes: int) -> int:
return self._target.getCursorCapsMode(req_modes)
def getExtractedText(self, request: "ExtractedTextRequest",
flags: int) -> Optional["ExtractedText"]:
return self._target.getExtractedText(request, flags)
def commitText(self, text: Any, new_cursor_position: int) -> bool:
return self._target.commitText(text, new_cursor_position)
def commitCompletion(self, completion: "CompletionInfo") -> bool:
return self._target.commitCompletion(completion)
def commitCorrection(self, correction: "CorrectionInfo") -> bool:
return self._target.commitCorrection(correction)
def commitContent(self, input_content: "InputContentInfo",
flags: int, opts: Optional[Any] = None) -> bool:
return self._target.commitContent(input_content, flags, opts)
def setComposingText(self, text: Any, new_cursor_position: int) -> bool:
return self._target.setComposingText(text, new_cursor_position)
def setComposingRegion(self, start: int, end: int) -> bool:
return self._target.setComposingRegion(start, end)
def finishComposingText(self) -> bool:
return self._target.finishComposingText()
def setSelection(self, start: int, end: int) -> bool:
return self._target.setSelection(start, end)
def performEditorAction(self, action_code: int) -> bool:
return self._target.performEditorAction(action_code)
def performContextMenuAction(self, id: int) -> bool:
return self._target.performContextMenuAction(id)
def beginBatchEdit(self) -> bool:
return self._target.beginBatchEdit()
def endBatchEdit(self) -> bool:
return self._target.endBatchEdit()
def sendKeyEvent(self, event: Any) -> bool:
return self._target.sendKeyEvent(event)
def clearMetaKeyStates(self, states: int) -> bool:
return self._target.clearMetaKeyStates(states)
def deleteSurroundingText(self, before_length: int, after_length: int) -> bool:
return self._target.deleteSurroundingText(before_length, after_length)
def requestCursorUpdates(self, cursor_update_mode: int) -> bool:
return self._target.requestCursorUpdates(cursor_update_mode)
def closeConnection(self):
self._target.closeConnection()
def reportFullscreenMode(self, enabled: bool):
self._target.reportFullscreenMode(enabled)
# ═══════════════════════════════════════════════════════════════
# BaseInputConnection
# ═══════════════════════════════════════════════════════════════
class BaseInputConnection(InputConnection):
"""BaseInputConnection — 输入连接基类"""
def __init__(self, view: Any, full_editor: bool = True):
super().__init__()
self._view = view
self._full_editor = full_editor
self._text: str = ""
self._selection_start: int = 0
self._selection_end: int = 0
self._composing_start: int = -1
self._composing_end: int = -1
def getEditable(self) -> Optional[Any]:
return None
def reportFullscreenMode(self, enabled: bool):
pass
def requestCursorUpdates(self, cursor_update_mode: int) -> bool:
return self._full_editor
def closeConnection(self):
pass
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"InputMethodManager",
"EditorInfo",
"InputConnection",
"ExtractedText",
"ExtractedTextRequest",
"InputBinding",
"InputMethodSubtype",
"CursorAnchorInfo",
"CorrectionInfo",
"CompletionInfo",
"SurroundingText",
"InputContentInfo",
"InputConnectionWrapper",
"BaseInputConnection",
]
+992
View File
@@ -0,0 +1,992 @@
"""
Android WebKit 组件 — WebView, WebSettings, WebViewClient, WebChromeClient 等
方法签名完全对齐 Android SDK
"""
import os
import sys
from typing import Any, Callable, 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,
)
# ═══════════════════════════════════════════════════════════════
# WebResourceRequest / WebResourceResponse / WebResourceError
# ═══════════════════════════════════════════════════════════════
class WebResourceRequest(Stub):
"""WebResourceRequest — 资源请求"""
def __init__(self, url: str = "", is_for_main_frame: bool = False,
is_redirect: bool = False, method: str = "GET",
request_headers: Optional[Dict[str, str]] = None):
super().__init__()
self._url = url
self._is_for_main_frame = is_for_main_frame
self._is_redirect = is_redirect
self._method = method
self._request_headers = request_headers or {}
def getUrl(self) -> str:
return self._url
def isForMainFrame(self) -> bool:
return self._is_for_main_frame
def isRedirect(self) -> bool:
return self._is_redirect
def getMethod(self) -> str:
return self._method
def getRequestHeaders(self) -> Dict[str, str]:
return dict(self._request_headers)
def toString(self) -> str:
return f"WebResourceRequest(url={self._url})"
class WebResourceResponse(Stub):
"""WebResourceResponse — 资源响应"""
def __init__(self, mime_type: str = "", encoding: str = "",
data: Any = None, status_code: int = 200,
reason_phrase: str = "OK",
response_headers: Optional[Dict[str, str]] = None):
super().__init__()
self._mime_type = mime_type
self._encoding = encoding
self._data = data
self._status_code = status_code
self._reason_phrase = reason_phrase
self._response_headers = response_headers or {}
def getMimeType(self) -> str:
return self._mime_type
def getEncoding(self) -> str:
return self._encoding
def getData(self) -> Any:
return self._data
def getStatusCode(self) -> int:
return self._status_code
def getReasonPhrase(self) -> str:
return self._reason_phrase
def getResponseHeaders(self) -> Dict[str, str]:
return dict(self._response_headers)
class WebResourceError(Stub):
"""WebResourceError — 资源错误"""
ERROR_UNKNOWN = -1
ERROR_HOST_LOOKUP = -2
ERROR_UNSUPPORTED_AUTH_SCHEME = -3
ERROR_AUTHENTICATION = -4
ERROR_PROXY_AUTHENTICATION = -5
ERROR_CONNECT = -6
ERROR_IO = -7
ERROR_TIMEOUT = -8
ERROR_REDIRECT_LOOP = -9
ERROR_UNSUPPORTED_SCHEME = -10
ERROR_FAILED_SSL_HANDSHAKE = -11
ERROR_BAD_URL = -12
ERROR_FILE = -13
ERROR_FILE_NOT_FOUND = -14
ERROR_TOO_MANY_REQUESTS = -15
def __init__(self, error_code: int = ERROR_UNKNOWN, description: str = ""):
super().__init__()
self._error_code = error_code
self._description = description
def getErrorCode(self) -> int:
return self._error_code
def getDescription(self) -> str:
return self._description
# ═══════════════════════════════════════════════════════════════
# WebMessage / WebMessagePort
# ═══════════════════════════════════════════════════════════════
class WebMessage(Stub):
"""WebMessage — Web 消息"""
def __init__(self, data: str = ""):
super().__init__()
self._data = data
def getData(self) -> str:
return self._data
class WebMessagePort(Stub):
"""WebMessagePort — Web 消息端口"""
def postMessage(self, message: WebMessage):
pass
def close(self):
pass
def setWebMessageCallback(self, callback: Any):
pass
# ═══════════════════════════════════════════════════════════════
# JavascriptInterface
# ═══════════════════════════════════════════════════════════════
class JavascriptInterface(Stub):
"""JavascriptInterface — JavaScript 接口注解"""
def __init__(self, value: str = ""):
super().__init__()
self._value = value
def getValue(self) -> str:
return self._value
# ═══════════════════════════════════════════════════════════════
# JsResult / JsPromptResult
# ═══════════════════════════════════════════════════════════════
class JsResult(Stub):
"""JsResult — JS 对话框结果"""
def __init__(self):
super().__init__()
self._confirmed = False
def confirm(self):
self._confirmed = True
def cancel(self):
self._confirmed = False
class JsPromptResult(JsResult):
"""JsPromptResult — JS 提示结果"""
def __init__(self):
super().__init__()
self._string_result: str = ""
def confirm(self, result: str = ""):
self._confirmed = True
self._string_result = result
def getStringResult(self) -> str:
return self._string_result
# ═══════════════════════════════════════════════════════════════
# HttpAuthHandler / SslErrorHandler
# ═══════════════════════════════════════════════════════════════
class HttpAuthHandler(Stub):
"""HttpAuthHandler — HTTP 认证处理器"""
def __init__(self):
super().__init__()
self._proceeded = False
self._cancelled = False
def proceed(self, username: str, password: str):
self._proceeded = True
def cancel(self):
self._cancelled = True
def useHttpAuthUsernamePassword(self) -> bool:
return False
class SslErrorHandler(Stub):
"""SslErrorHandler — SSL 错误处理器"""
def __init__(self):
super().__init__()
self._proceeded = False
self._cancelled = False
def proceed(self):
self._proceeded = True
def cancel(self):
self._cancelled = True
# ═══════════════════════════════════════════════════════════════
# PermissionRequest
# ═══════════════════════════════════════════════════════════════
class PermissionRequest(Stub):
"""PermissionRequest — 权限请求"""
RESOURCE_VIDEO_CAPTURE = "android.webkit.resource.VIDEO_CAPTURE"
RESOURCE_AUDIO_CAPTURE = "android.webkit.resource.AUDIO_CAPTURE"
RESOURCE_PROTECTED_MEDIA_ID = "android.webkit.resource.PROTECTED_MEDIA_ID"
RESOURCE_MIDI_SYSEX = "android.webkit.resource.MIDI_SYSEX"
def __init__(self):
super().__init__()
self._origin: Any = None
self._resources: List[str] = []
self._granted_resources: List[str] = []
def getOrigin(self) -> Any:
return self._origin
def getResources(self) -> List[str]:
return list(self._resources)
def getGrantedResources(self) -> List[str]:
return list(self._granted_resources)
def grant(self, resources: List[str]):
self._granted_resources = list(resources)
def deny(self):
self._granted_resources.clear()
# ═══════════════════════════════════════════════════════════════
# GeolocationPermissions / WebStorage
# ═══════════════════════════════════════════════════════════════
class GeolocationPermissions(Stub):
"""GeolocationPermissions — 地理位置权限"""
@staticmethod
def getInstance() -> "GeolocationPermissions":
return GeolocationPermissions()
def allow(self, origin: str):
pass
def clear(self, origin: str):
pass
def clearAll(self):
pass
def getAllowed(self, origin: str, callback: Any):
pass
def getOrigins(self, callback: Any):
pass
class WebStorage(Stub):
"""WebStorage — Web 存储"""
@staticmethod
def getInstance() -> "WebStorage":
return WebStorage()
def deleteAllData(self):
pass
def deleteOrigin(self, origin: str):
pass
def getOrigins(self, callback: Any):
pass
def getQuotaForOrigin(self, origin: str, callback: Any):
pass
def getUsageForOrigin(self, origin: str, callback: Any):
pass
# ═══════════════════════════════════════════════════════════════
# WebBackForwardList / WebHistoryItem
# ═══════════════════════════════════════════════════════════════
class WebHistoryItem(Stub):
"""WebHistoryItem — 历史项"""
def __init__(self):
super().__init__()
self._url: str = ""
self._original_url: str = ""
self._title: str = ""
self._favicon: Any = None
def getUrl(self) -> str:
return self._url
def getOriginalUrl(self) -> str:
return self._original_url
def getTitle(self) -> str:
return self._title
def getFavicon(self) -> Any:
return self._favicon
def toString(self) -> str:
return f"WebHistoryItem(url={self._url})"
class WebBackForwardList(Stub):
"""WebBackForwardList — 前进后退列表"""
def __init__(self):
super().__init__()
self._items: List[WebHistoryItem] = []
self._current_index: int = -1
def getCurrentItem(self) -> Optional[WebHistoryItem]:
if 0 <= self._current_index < len(self._items):
return self._items[self._current_index]
return None
def getCurrentIndex(self) -> int:
return self._current_index
def getItemAtIndex(self, index: int) -> Optional[WebHistoryItem]:
if 0 <= index < len(self._items):
return self._items[index]
return None
def getSize(self) -> int:
return len(self._items)
# ═══════════════════════════════════════════════════════════════
# CookieManager / CookieSyncManager
# ═══════════════════════════════════════════════════════════════
class CookieManager(Stub):
"""CookieManager — Cookie 管理器"""
_instance: Optional["CookieManager"] = None
def __init__(self):
super().__init__()
self._accept_cookies: bool = True
self._accept_third_party: bool = True
self._cookies: Dict[str, str] = {}
@staticmethod
def getInstance() -> "CookieManager":
if CookieManager._instance is None:
CookieManager._instance = CookieManager()
return CookieManager._instance
def setAcceptCookies(self, accept: bool):
self._accept_cookies = accept
def acceptCookies(self) -> bool:
return self._accept_cookies
def setCookie(self, url: str, value: str):
if self._accept_cookies:
self._cookies[url] = value
def getCookie(self, url: str) -> Optional[str]:
return self._cookies.get(url)
def setAcceptThirdPartyCookies(self, webview: Any, accept: bool):
self._accept_third_party = accept
def acceptThirdPartyCookies(self) -> bool:
return self._accept_third_party
def removeSessionCookie(self):
pass
def removeAllCookies(self):
self._cookies.clear()
def removeAllCookiesSync(self):
self._cookies.clear()
def removeSessionCookies(self):
pass
def flush(self):
pass
def hasCookies(self) -> bool:
return bool(self._cookies)
def removeExpiredCookie(self):
pass
class CookieSyncManager(Stub):
"""CookieSyncManager — Cookie 同步管理器"""
_instance: Optional["CookieSyncManager"] = None
@staticmethod
def createInstance(context: Any) -> "CookieSyncManager":
if CookieSyncManager._instance is None:
CookieSyncManager._instance = CookieSyncManager()
return CookieSyncManager._instance
@staticmethod
def getInstance() -> "CookieSyncManager":
if CookieSyncManager._instance is None:
CookieSyncManager._instance = CookieSyncManager()
return CookieSyncManager._instance
def sync(self):
pass
def startSync(self):
pass
def stopSync(self):
pass
def resetSync(self):
pass
# ═══════════════════════════════════════════════════════════════
# WebSettings
# ═══════════════════════════════════════════════════════════════
class WebSettings(Stub):
"""WebSettings — WebView 设置"""
LOAD_DEFAULT = -1
LOAD_CACHE_ELSE_NETWORK = 1
LOAD_NO_CACHE = 2
LOAD_CACHE_ONLY = 3
MIXED_CONTENT_ALWAYS_ALLOW = 0
MIXED_CONTENT_NEVER_ALLOW = 1
MIXED_CONTENT_COMPATIBILITY_MODE = 2
class LayoutAlgorithm(Stub):
NORMAL = 0
SINGLE_COLUMN = 1
NARROW_COLUMNS = 2
TEXT_AUTOSIZING = 3
def __init__(self):
super().__init__()
self._js_enabled: bool = True
self._dom_storage_enabled: bool = True
self._allow_file_access: bool = True
self._allow_content_access: bool = True
self._allow_file_access_from_urls: bool = False
self._allow_universal_access_from_urls: bool = False
self._built_in_zoom_controls: bool = True
self._display_zoom_controls: bool = True
self._support_zoom: bool = True
self._load_with_overview_mode: bool = False
self._use_wide_viewport: bool = False
self._user_agent_string: str = "Mozilla/5.0 (Linux; Android 14) AppleWebKit/537.36"
self._default_text_encoding: str = "UTF-8"
self._cache_mode: int = self.LOAD_DEFAULT
self._app_cache_enabled: bool = False
self._app_cache_path: str = ""
self._database_enabled: bool = False
self._geolocation_enabled: bool = False
self._js_can_open_windows: bool = False
self._loads_images_automatically: bool = True
self._block_network_image: bool = False
self._block_network_loads: bool = False
self._minimum_font_size: int = 8
self._default_font_size: int = 16
self._text_zoom: int = 100
self._layout_algorithm: int = WebSettings.LayoutAlgorithm.NORMAL
self._mixed_content_mode: int = self.MIXED_CONTENT_NEVER_ALLOW
self._offscreen_pre_raster: bool = False
self._safe_browsing_enabled: bool = True
self._javascript_can_open_windows_automatically: bool = False
def setJavaScriptEnabled(self, enabled: bool):
self._js_enabled = enabled
def setDomStorageEnabled(self, enabled: bool):
self._dom_storage_enabled = enabled
def setAllowFileAccess(self, allow: bool):
self._allow_file_access = allow
def setAllowContentAccess(self, allow: bool):
self._allow_content_access = allow
def setAllowFileAccessFromFileURLs(self, allow: bool):
self._allow_file_access_from_urls = allow
def setAllowUniversalAccessFromFileURLs(self, allow: bool):
self._allow_universal_access_from_urls = allow
def setBuiltInZoomControls(self, enabled: bool):
self._built_in_zoom_controls = enabled
def setDisplayZoomControls(self, enabled: bool):
self._display_zoom_controls = enabled
def setSupportZoom(self, support: bool):
self._support_zoom = support
def setLoadWithOverviewMode(self, overview: bool):
self._load_with_overview_mode = overview
def setUseWideViewPort(self, use: bool):
self._use_wide_viewport = use
def setUserAgentString(self, ua: str):
self._user_agent_string = ua
def getUserAgentString(self) -> str:
return self._user_agent_string
def setDefaultTextEncodingName(self, encoding: str):
self._default_text_encoding = encoding
def getDefaultTextEncodingName(self) -> str:
return self._default_text_encoding
def setCacheMode(self, mode: int):
self._cache_mode = mode
def getCacheMode(self) -> int:
return self._cache_mode
def setAppCacheEnabled(self, enabled: bool):
self._app_cache_enabled = enabled
def setAppCachePath(self, path: str):
self._app_cache_path = path
def setDatabaseEnabled(self, enabled: bool):
self._database_enabled = enabled
def setGeolocationEnabled(self, enabled: bool):
self._geolocation_enabled = enabled
def setJavaScriptCanOpenWindowsAutomatically(self, auto: bool):
self._javascript_can_open_windows_automatically = auto
def setLoadsImagesAutomatically(self, auto: bool):
self._loads_images_automatically = auto
def setBlockNetworkImage(self, block: bool):
self._block_network_image = block
def setBlockNetworkLoads(self, block: bool):
self._block_network_loads = block
def setMinimumFontSize(self, size: int):
self._minimum_font_size = size
def getMinimumFontSize(self) -> int:
return self._minimum_font_size
def setDefaultFontSize(self, size: int):
self._default_font_size = size
def getDefaultFontSize(self) -> int:
return self._default_font_size
def setTextZoom(self, zoom: int):
self._text_zoom = zoom
def getTextZoom(self) -> int:
return self._text_zoom
def setLayoutAlgorithm(self, algo: int):
self._layout_algorithm = algo
def getLayoutAlgorithm(self) -> int:
return self._layout_algorithm
def setMixedContentMode(self, mode: int):
self._mixed_content_mode = mode
def getMixedContentMode(self) -> int:
return self._mixed_content_mode
def setOffscreenPreRaster(self, enabled: bool):
self._offscreen_pre_raster = enabled
def getOffscreenPreRaster(self) -> bool:
return self._offscreen_pre_raster
def setSafeBrowsingEnabled(self, enabled: bool):
self._safe_browsing_enabled = enabled
def getSafeBrowsingEnabled(self) -> bool:
return self._safe_browsing_enabled
def toString(self) -> str:
return f"WebSettings(js={self._js_enabled})"
# ═══════════════════════════════════════════════════════════════
# WebViewClient
# ═══════════════════════════════════════════════════════════════
class WebViewClient(Stub):
"""WebViewClient — WebView 客户端"""
def onPageStarted(self, view: "WebView", url: str, favicon: Any):
pass
def onPageFinished(self, view: "WebView", url: str):
pass
def onLoadResource(self, view: "WebView", url: str):
pass
def shouldOverrideUrlLoading(self, view: "WebView", request: Any) -> bool:
return False
def shouldOverrideUrlLoading(self, view: "WebView", url: str) -> bool:
return False
def shouldInterceptRequest(self, view: "WebView", request: WebResourceRequest) -> Optional[WebResourceResponse]:
return None
def shouldInterceptRequest(self, view: "WebView", url: str) -> Optional[WebResourceResponse]:
return None
def onReceivedHttpError(self, view: "WebView", request: WebResourceRequest,
error_response: WebResourceResponse):
pass
def onReceivedSslError(self, view: "WebView", handler: SslErrorHandler, error: Any):
pass
def onReceivedClientCertRequest(self, view: "WebView", request: Any):
pass
def onReceivedHttpAuthRequest(self, view: "WebView", handler: HttpAuthHandler,
host: str, realm: str):
pass
def onFormResubmission(self, view: "WebView", dont_resend: Any, resend: Any):
pass
def doUpdateVisitedHistory(self, view: "WebView", url: str, is_reload: bool):
pass
def onScaleChanged(self, view: "WebView", old_scale: float, new_scale: float):
pass
def onUnhandledKeyEvent(self, view: "WebView", event: Any):
pass
def shouldOverrideKeyEvent(self, view: "WebView", event: Any) -> bool:
return False
# ═══════════════════════════════════════════════════════════════
# WebChromeClient
# ═══════════════════════════════════════════════════════════════
class WebChromeClient(Stub):
"""WebChromeClient — WebView Chrome 客户端"""
def onProgressChanged(self, view: "WebView", new_progress: int):
pass
def onReceivedTitle(self, view: "WebView", title: str):
pass
def onReceivedIcon(self, view: "WebView", icon: Any):
pass
def onReceivedTouchIconUrl(self, view: "WebView", url: str, precomposed: bool):
pass
def onShowCustomView(self, view: Any, callback: Any):
pass
def onHideCustomView(self):
pass
def onCreateWindow(self, view: "WebView", is_dialog: bool,
is_user_gesture: bool, result_msg: Any) -> bool:
return False
def onRequestFocus(self, view: "WebView"):
pass
def onCloseWindow(self, window: "WebView"):
pass
def onJsAlert(self, view: "WebView", url: str, message: str,
result: JsResult) -> bool:
return False
def onJsConfirm(self, view: "WebView", url: str, message: str,
result: JsResult) -> bool:
return False
def onJsPrompt(self, view: "WebView", url: str, message: str,
default_value: str, result: JsPromptResult) -> bool:
return False
def onJsBeforeUnload(self, view: "WebView", url: str, message: str,
result: JsResult) -> bool:
return False
def onExceededDatabaseQuota(self, url: str, database_identifier: str,
quota: int, estimated_database_size: int,
total_quota: int, quota_updater: Any):
pass
def onReachedMaxAppCacheSize(self, space_needed: int, total_quota: int,
quota_updater: Any):
pass
def onGeolocationPermissionsShowPrompt(self, origin: str,
callback: Any):
pass
def onGeolocationPermissionsHidePrompt(self):
pass
def onPermissionRequest(self, request: PermissionRequest):
pass
def onPermissionRequestCanceled(self, request: PermissionRequest):
pass
def onConsoleMessage(self, message: Any) -> bool:
return True
# ═══════════════════════════════════════════════════════════════
# WebView
# ═══════════════════════════════════════════════════════════════
class WebView(Stub):
"""WebView — Web 视图"""
_instance_count = 0
def __init__(self, context: Any = None, attrs: Any = None):
super().__init__()
WebView._instance_count += 1
self._context = context
self._settings = WebSettings()
self._webview_client: Optional[WebViewClient] = None
self._chrome_client: Optional[WebChromeClient] = None
self._current_url: str = ""
self._original_url: str = ""
self._title: str = ""
self._progress: int = 100
self._content_height: int = 0
self._scale: float = 1.0
self._background_color: int = 0xFFFFFFFF
self._back_forward_list = WebBackForwardList()
self._javascript_interfaces: Dict[str, Any] = {}
self._paused: bool = False
self._timers_paused: bool = False
self._network_available: bool = True
def loadUrl(self, url: str, additional_headers: Optional[Dict[str, str]] = None):
self._current_url = url
self._original_url = url
self._title = url
self._progress = 0
if self._webview_client:
try:
self._webview_client.onPageStarted(self, url, None)
self._webview_client.onPageFinished(self, url)
except Exception:
pass
self._progress = 100
def loadData(self, data: str, mime_type: str = "text/html",
encoding: str = "utf-8"):
self._current_url = "data:text/html;charset=utf-8,"
self._progress = 100
def loadDataWithBaseURL(self, base_url: str, data: str,
mime_type: str, encoding: str,
history_url: str):
self._current_url = history_url or base_url
self._progress = 100
def setWebViewClient(self, client: WebViewClient):
self._webview_client = client
def setWebChromeClient(self, client: WebChromeClient):
self._chrome_client = client
def getSettings(self) -> WebSettings:
return self._settings
def goBack(self):
pass
def goForward(self):
pass
def canGoBack(self) -> bool:
return False
def canGoForward(self) -> bool:
return False
def goBackOrForward(self, steps: int):
pass
def reload(self):
pass
def stopLoading(self):
pass
def clearHistory(self):
pass
def clearCache(self, include_disk_files: bool):
pass
def clearView(self):
pass
def destroy(self):
WebView._instance_count -= 1
def getUrl(self) -> Optional[str]:
return self._current_url or None
def getTitle(self) -> Optional[str]:
return self._title or None
def getProgress(self) -> int:
return self._progress
def getContentHeight(self) -> int:
return self._content_height
def getScale(self) -> float:
return self._scale
def addJavascriptInterface(self, obj: Any, interface_name: str):
self._javascript_interfaces[interface_name] = obj
def removeJavascriptInterface(self, interface_name: str):
if interface_name in self._javascript_interfaces:
del self._javascript_interfaces[interface_name]
def evaluateJavascript(self, script: str, callback: Any = None):
pass
def setInitialScale(self, scale_in_percent: int):
self._scale = scale_in_percent / 100.0
def setBackgroundColor(self, color: int):
self._background_color = color
def scrollTo(self, x: int, y: int):
pass
def scrollBy(self, x: int, y: int):
pass
def pageUp(self, top: bool) -> bool:
return True
def pageDown(self, bottom: bool) -> bool:
return True
def findNext(self, forward: bool):
pass
def findAll(self, find: str) -> int:
return 0
def findAllAsync(self, find: str):
pass
def clearMatches(self):
pass
def zoomIn(self):
self._scale *= 1.25
def zoomOut(self):
self._scale /= 1.25
def setNetworkAvailable(self, available: bool):
self._network_available = available
def pauseTimers(self):
self._timers_paused = True
def resumeTimers(self):
self._timers_paused = False
def onPause(self):
self._paused = True
def onResume(self):
self._paused = False
def onWindowFocusChanged(self, has_focus: bool):
pass
def setLayoutParams(self, params: Any):
pass
def requestFocus(self, direction: int = 0):
pass
def getOriginalUrl(self) -> Optional[str]:
return self._original_url or None
def canZoomIn(self) -> bool:
return True
def canZoomOut(self) -> bool:
return self._scale > 0.25
def toString(self) -> str:
return f"WebView(url={self._current_url})"
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"WebView",
"WebSettings",
"WebViewClient",
"WebChromeClient",
"WebResourceRequest",
"WebResourceResponse",
"WebResourceError",
"WebMessage",
"WebMessagePort",
"JavascriptInterface",
"CookieManager",
"CookieSyncManager",
"JsResult",
"JsPromptResult",
"HttpAuthHandler",
"SslErrorHandler",
"PermissionRequest",
"GeolocationPermissions",
"WebStorage",
"WebBackForwardList",
"WebHistoryItem",
]
File diff suppressed because it is too large. Load diff
View File
Whitespace-only changes.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+628
View File
@@ -0,0 +1,628 @@
"""
Java crypto standard library APIs implemented in Python.
javax.crypto package - Cipher, KeyGenerator, Mac, SecretKey, etc.
"""
from .._core import Stub, AndroidOnlyError
from .lang import RuntimeException
import os as _os
# Try to import cryptography library
try:
from cryptography.hazmat.primitives.ciphers import Cipher as _Cipher
from cryptography.hazmat.primitives.ciphers import algorithms as _algorithms
from cryptography.hazmat.primitives.ciphers import modes as _modes
from cryptography.hazmat.primitives import hashes as _hashes
from cryptography.hazmat.primitives.asymmetric import rsa as _rsa
from cryptography.hazmat.primitives.asymmetric import padding as _padding
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC as _PBKDF2
from cryptography.hazmat.backends import default_backend as _crypto_backend
HAS_CRYPTOGRAPHY = True
except ImportError:
HAS_CRYPTOGRAPHY = False
from .security import (
GeneralSecurityException, InvalidKeyException,
NoSuchAlgorithmException, NoSuchProviderException,
Key, SecretKeySpec, AlgorithmParameters, AlgorithmParametersSpi,
AlgorithmParameterGenerator, AlgorithmParameterGeneratorSpi,
Provider, SecureRandom,
)
IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {})
AEADBadTagException = type("AEADBadTagException", (GeneralSecurityException,), {})
BadPaddingException = type("BadPaddingException", (GeneralSecurityException,), {})
IllegalBlockSizeException = type("IllegalBlockSizeException", (GeneralSecurityException,), {})
NoSuchPaddingException = type("NoSuchPaddingException", (GeneralSecurityException,), {})
ShortBufferException = type("ShortBufferException", (GeneralSecurityException,), {})
ExemptionMechanismException = type("ExemptionMechanismException", (GeneralSecurityException,), {})
__all__ = [
"Cipher", "CipherSpi",
"KeyGenerator", "KeyGeneratorSpi",
"SecretKey", "SecretKeyFactory", "SecretKeyFactorySpi",
"Mac", "MacSpi",
"IvParameterSpec", "PBEParameterSpec", "GCMParameterSpec",
"OAEPParameterSpec", "PSource", "MGF1ParameterSpec",
"DHParameterSpec", "DSAParameterSpec",
"ECParameterSpec", "ECGenParameterSpec", "ECPoint",
"ECField", "ECFieldF2m", "ECFieldFp", "EllipticCurve",
"AlgorithmParameters", "AlgorithmParameterGenerator",
"AlgorithmParametersSpi",
"AEADBadTagException", "BadPaddingException",
"IllegalBlockSizeException", "NoSuchPaddingException",
"ShortBufferException",
"ExemptionMechanism", "ExemptionMechanismException",
"ExemptionMechanismSpi",
]
# ============================================================
# SecretKey
# ============================================================
class SecretKey(Key):
def __init__(self, key_bytes=None, algorithm=""):
super().__init__()
self._key = key_bytes or b""
self._algorithm = algorithm
def getAlgorithm(self):
return self._algorithm
def getFormat(self):
return "RAW"
def getEncoded(self):
return self._key
# ============================================================
# Cipher
# ============================================================
class Cipher(Stub):
ENCRYPT_MODE = 1
DECRYPT_MODE = 2
WRAP_MODE = 3
UNWRAP_MODE = 4
PUBLIC_KEY = 1
PRIVATE_KEY = 2
SECRET_KEY = 3
def __init__(self, transformation):
super().__init__()
self._transformation = transformation
self._opmode = 0
self._key = None
self._iv = None
self._provider = Provider("AndroidOpenSSL", 1.0, "Android OpenSSL provider")
@staticmethod
def getInstance(transformation, provider=None):
return Cipher(transformation)
def init(self, opmode, key_or_cert, params=None, random=None):
self._opmode = opmode
self._key = key_or_cert
if isinstance(params, IvParameterSpec):
self._iv = params.getIV()
elif isinstance(params, GCMParameterSpec):
self._iv = params.getIV()
def doFinal(self, input_data=None):
if input_data is None:
return b""
if HAS_CRYPTOGRAPHY and self._key is not None:
try:
algo = self._transformation.split("/")[0].upper()
key_bytes = self._key.getEncoded() if hasattr(self._key, "getEncoded") else bytes(self._key)
iv = self._iv or b"\x00" * 16
if algo == "AES":
if len(iv) == 12:
mode = _modes.GCM(iv)
else:
mode = _modes.CBC(iv)
cipher = _Cipher(_algorithms.AES(key_bytes), mode, backend=_crypto_backend())
elif algo == "DES":
cipher = _Cipher(_algorithms.TripleDES(key_bytes), _modes.CBC(iv), backend=_crypto_backend())
else:
return self._basic_cipher(input_data)
if self._opmode == Cipher.ENCRYPT_MODE:
encryptor = cipher.encryptor()
result = encryptor.update(input_data) + encryptor.finalize()
return result
else:
decryptor = cipher.decryptor()
result = decryptor.update(input_data) + decryptor.finalize()
return result
except Exception:
return self._basic_cipher(input_data)
else:
return self._basic_cipher(input_data)
def _basic_cipher(self, data):
if self._opmode == Cipher.ENCRYPT_MODE:
if self._key is not None:
key_bytes = self._key.getEncoded() if hasattr(self._key, "getEncoded") else bytes(self._key)
return bytes([(b ^ key_bytes[i % len(key_bytes)]) & 0xFF for i, b in enumerate(data)])
return data
else:
if self._key is not None:
key_bytes = self._key.getEncoded() if hasattr(self._key, "getEncoded") else bytes(self._key)
return bytes([(b ^ key_bytes[i % len(key_bytes)]) & 0xFF for i, b in enumerate(data)])
return data
def update(self, input_data):
return self._basic_cipher(input_data)
def getBlockSize(self):
algo = self._transformation.split("/")[0].upper()
if algo == "AES":
return 16
if algo == "DES" or algo == "DESede":
return 8
return 1
def getOutputSize(self, input_len):
bs = self.getBlockSize()
if bs > 1:
return input_len + bs - (input_len % bs)
return input_len
def getIV(self):
return self._iv
def getParameters(self):
if self._iv:
return AlgorithmParameters(self._transformation.split("/")[0])
return None
def getAlgorithm(self):
return self._transformation
def getProvider(self):
return self._provider
def getExemptionMechanism(self):
return None
def unwrap(self, wrapped_key, wrapped_key_algorithm, wrapped_key_type):
return SecretKey(wrapped_key, wrapped_key_algorithm)
def wrap(self, key):
key_bytes = key.getEncoded() if hasattr(key, "getEncoded") else b""
return self.doFinal(key_bytes)
@staticmethod
def maxAllowedKeyLength(transformation):
return 2147483647
class CipherSpi(Stub):
pass
# ============================================================
# KeyGenerator
# ============================================================
class KeyGenerator(Stub):
def __init__(self, algorithm):
super().__init__()
self._algorithm = algorithm
self._key_size = 128
self._secure_random = None
@staticmethod
def getInstance(algorithm, provider=None):
return KeyGenerator(algorithm)
def init(self, size_or_random, random=None):
if isinstance(size_or_random, int):
self._key_size = size_or_random
self._secure_random = random
else:
self._secure_random = size_or_random
def generateKey(self):
key_bytes = _os.urandom(self._key_size // 8)
return SecretKey(key_bytes, self._algorithm)
def getAlgorithm(self):
return self._algorithm
def getProvider(self):
return Provider("AndroidOpenSSL", 1.0, "Android OpenSSL provider")
class KeyGeneratorSpi(Stub):
pass
# ============================================================
# SecretKeyFactory
# ============================================================
class SecretKeyFactory(Stub):
def __init__(self, algorithm):
super().__init__()
self._algorithm = algorithm
@staticmethod
def getInstance(algorithm, provider=None):
return SecretKeyFactory(algorithm)
def generateSecret(self, key_spec):
if isinstance(key_spec, SecretKeySpec):
return SecretKey(key_spec.getEncoded(), self._algorithm)
return SecretKey(b"", self._algorithm)
def getKeySpec(self, secret_key, key_spec_cls):
return SecretKeySpec(secret_key.getEncoded(), self._algorithm)
def translateKey(self, key):
return key
def getAlgorithm(self):
return self._algorithm
class SecretKeyFactorySpi(Stub):
pass
# ============================================================
# Mac
# ============================================================
class Mac(Stub):
def __init__(self, algorithm):
super().__init__()
self._algorithm = algorithm
self._key = None
self._state = "UNINITIALIZED"
self._data = b""
@staticmethod
def getInstance(algorithm, provider=None):
return Mac(algorithm)
def init(self, key, params=None):
self._key = key
self._state = "INITIALIZED"
self._data = b""
def update(self, input_data, offset=0, length=-1):
if length < 0:
self._data += bytes(input_data)
else:
self._data += bytes(input_data[offset:offset+length])
def doFinal(self, input_data=None):
if input_data is not None:
self._data += bytes(input_data)
if HAS_CRYPTOGRAPHY and self._key is not None:
try:
key_bytes = self._key.getEncoded() if hasattr(self._key, "getEncoded") else bytes(self._key)
algo = self._algorithm.lower().replace("-", "")
if "sha256" in algo or "sha-256" in self._algorithm:
h = _hashlib.sha256(key_bytes + self._data)
elif "sha1" in algo or "sha-1" in self._algorithm:
h = _hashlib.sha1(key_bytes + self._data)
elif "md5" in algo:
h = _hashlib.md5(key_bytes + self._data)
else:
h = _hashlib.sha256(key_bytes + self._data)
result = h.digest()
self._data = b""
return result
except Exception:
pass
import hashlib as _hashlib
key_bytes = self._key.getEncoded() if self._key and hasattr(self._key, "getEncoded") else b"key"
h = _hashlib.sha256(key_bytes + self._data)
result = h.digest()
self._data = b""
return result
def reset(self):
self._data = b""
def getAlgorithm(self):
return self._algorithm
def getMacLength(self):
return 32
def getProvider(self):
return Provider("AndroidOpenSSL", 1.0, "Android OpenSSL provider")
class MacSpi(Stub):
pass
# ============================================================
# Parameter specs
# ============================================================
class IvParameterSpec(Stub):
def __init__(self, iv):
super().__init__()
self._iv = bytes(iv)
def getIV(self):
return self._iv
class PBEParameterSpec(Stub):
def __init__(self, salt, iteration_count):
super().__init__()
self._salt = bytes(salt)
self._iteration_count = iteration_count
def getSalt(self):
return self._salt
def getIterationCount(self):
return self._iteration_count
class GCMParameterSpec(Stub):
def __init__(self, t_len, src):
super().__init__()
self._t_len = t_len
self._iv = bytes(src)
def getTLen(self):
return self._t_len
def getIV(self):
return self._iv
class OAEPParameterSpec(Stub):
def __init__(self, md_name, mgf_name, mgf_spec, p_src):
super().__init__()
self._md_name = md_name
self._mgf_name = mgf_name
self._mgf_spec = mgf_spec
self._p_src = p_src
def getDigestAlgorithm(self):
return self._md_name
def getMGFAlgorithm(self):
return self._mgf_name
def getMGFParameters(self):
return self._mgf_spec
def getPSource(self):
return self._p_src
class PSource(Stub):
class PSpecified(Stub):
DEFAULT = None
def __init__(self, p):
super().__init__()
self._p = p
def getValue(self):
return self._p
def __init__(self, p_src_id):
super().__init__()
self._p_src_id = p_src_id
def getAlgorithm(self):
return self._p_src_id
PSource.PSpecified.DEFAULT = PSource.PSpecified(b"")
class MGF1ParameterSpec(Stub):
SHA1 = None
SHA256 = None
SHA384 = None
SHA512 = None
def __init__(self, md_name):
super().__init__()
self._md_name = md_name
def getDigestAlgorithm(self):
return self._md_name
def toString(self):
return f"MGF1 {self._md_name}"
MGF1ParameterSpec.SHA1 = MGF1ParameterSpec("SHA-1")
MGF1ParameterSpec.SHA256 = MGF1ParameterSpec("SHA-256")
MGF1ParameterSpec.SHA384 = MGF1ParameterSpec("SHA-384")
MGF1ParameterSpec.SHA512 = MGF1ParameterSpec("SHA-512")
# ============================================================
# DH / DSA / EC parameter specs (stubs)
# ============================================================
class DHParameterSpec(Stub):
def __init__(self, p, g, l=0):
super().__init__()
self._p = p
self._g = g
self._l = l
def getP(self):
return self._p
def getG(self):
return self._g
def getL(self):
return self._l
class DSAParameterSpec(Stub):
def __init__(self, p, q, g):
super().__init__()
self._p = p
self._q = q
self._g = g
def getP(self):
return self._p
def getQ(self):
return self._q
def getG(self):
return self._g
class ECPoint(Stub):
def __init__(self, affine_x, affine_y):
super().__init__()
self._affine_x = affine_x
self._affine_y = affine_y
def getAffineX(self):
return self._affine_x
def getAffineY(self):
return self._affine_y
def equals(self, other):
if isinstance(other, ECPoint):
return self._affine_x == other._affine_x and self._affine_y == other._affine_y
return False
def hashCode(self):
return hash((self._affine_x, self._affine_y))
class ECField(Stub):
def getFieldSize(self):
raise NotImplementedError
class ECFieldFp(ECField):
def __init__(self, p):
super().__init__()
self._p = p
def getP(self):
return self._p
def getFieldSize(self):
return self._p.bit_length()
class ECFieldF2m(ECField):
def __init__(self, m, ks=None):
super().__init__()
self._m = m
self._ks = ks
def getM(self):
return self._m
def getMidTermsOfReductionPolynomial(self):
return self._ks
def getFieldSize(self):
return self._m
class EllipticCurve(Stub):
def __init__(self, field, a, b, seed=None):
super().__init__()
self._field = field
self._a = a
self._b = b
self._seed = seed
def getField(self):
return self._field
def getA(self):
return self._a
def getB(self):
return self._b
def getSeed(self):
return self._seed
class ECParameterSpec(Stub):
def __init__(self, curve, generator, order, cofactor, seed=None):
super().__init__()
self._curve = curve
self._generator = generator
self._order = order
self._cofactor = cofactor
def getCurve(self):
return self._curve
def getGenerator(self):
return self._generator
def getOrder(self):
return self._order
def getCofactor(self):
return self._cofactor
class ECGenParameterSpec(Stub):
def __init__(self, name):
super().__init__()
self._name = name
def getName(self):
return self._name
# ============================================================
# ExemptionMechanism
# ============================================================
class ExemptionMechanism(Stub):
def __init__(self, name):
super().__init__()
self._name = name
self._state = False
@staticmethod
def getInstance(name, provider=None):
return ExemptionMechanism(name)
def init(self, key):
self._state = False
def isCryptoAllowed(self, key):
return True
def genExemptionBlob(self, data=None):
return b""
def getName(self):
return self._name
def getOutputSize(self, input_len):
return input_len
def storeMechanism(self, mechanism):
pass
class ExemptionMechanismSpi(Stub):
pass
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+683
View File
@@ -0,0 +1,683 @@
"""
Java math standard library APIs implemented in Python.
java.math package - BigInteger, BigDecimal, MathContext, RoundingMode.
"""
from .._core import Stub
from .lang import Comparable
from decimal import (
Decimal as _Decimal,
ROUND_DOWN as _DEC_ROUND_DOWN,
ROUND_HALF_UP as _DEC_ROUND_HALF_UP,
ROUND_HALF_EVEN as _DEC_ROUND_HALF_EVEN,
ROUND_UP as _DEC_ROUND_UP,
ROUND_CEILING as _DEC_ROUND_CEILING,
ROUND_FLOOR as _DEC_ROUND_FLOOR,
ROUND_HALF_DOWN as _DEC_ROUND_HALF_DOWN,
localcontext as _local_context,
getcontext as _get_context,
)
import random as _random
import math as _py_math
import sys as _sys
__all__ = [
"BigInteger", "BigDecimal", "MathContext", "RoundingMode",
]
# ============================================================
# RoundingMode
# ============================================================
class RoundingMode(Stub):
UP = 0
DOWN = 1
CEILING = 2
FLOOR = 3
HALF_UP = 4
HALF_DOWN = 5
HALF_EVEN = 6
UNNECESSARY = 7
_names = {
0: "UP", 1: "DOWN", 2: "CEILING", 3: "FLOOR",
4: "HALF_UP", 5: "HALF_DOWN", 6: "HALF_EVEN", 7: "UNNECESSARY",
}
_py_map = {
0: _DEC_ROUND_UP,
1: _DEC_ROUND_DOWN,
2: _DEC_ROUND_CEILING,
3: _DEC_ROUND_FLOOR,
4: _DEC_ROUND_HALF_UP,
5: _DEC_ROUND_HALF_DOWN,
6: _DEC_ROUND_HALF_EVEN,
7: _DEC_ROUND_HALF_EVEN, # closest approximation
}
def __init__(self, value):
super().__init__()
self._value = value
def toString(self):
return self._names.get(self._value, "UNKNOWN")
# ============================================================
# MathContext
# ============================================================
class MathContext(Stub):
UNLIMITED = None
DECIMAL32 = None
DECIMAL64 = None
DECIMAL128 = None
def __init__(self, *args):
super().__init__()
if len(args) == 1:
if isinstance(args[0], str):
s = args[0].strip().lower()
if s == "precision=7 roundingmode=half_even":
self._precision = 7
self._rounding_mode = RoundingMode.HALF_EVEN
elif s == "precision=16 roundingmode=half_even":
self._precision = 16
self._rounding_mode = RoundingMode.HALF_EVEN
elif s == "precision=34 roundingmode=half_even":
self._precision = 34
self._rounding_mode = RoundingMode.HALF_EVEN
else:
self._precision = 0
self._rounding_mode = RoundingMode.HALF_UP
else:
self._precision = int(args[0])
self._rounding_mode = RoundingMode.HALF_UP
elif len(args) == 2:
self._precision = int(args[0])
self._rounding_mode = args[1]
def getPrecision(self):
return self._precision
def getRoundingMode(self):
return self._rounding_mode
def equals(self, other):
if isinstance(other, MathContext):
return (self._precision == other._precision and
self._rounding_mode == other._rounding_mode)
return False
def hashCode(self):
return hash((self._precision, self._rounding_mode)) & 0x7FFFFFFF
def toString(self):
return f"precision={self._precision} roundingMode={self._rounding_mode.toString()}"
MathContext.UNLIMITED = MathContext(0)
MathContext.DECIMAL32 = MathContext(7, RoundingMode.HALF_EVEN)
MathContext.DECIMAL64 = MathContext(16, RoundingMode.HALF_EVEN)
MathContext.DECIMAL128 = MathContext(34, RoundingMode.HALF_EVEN)
# ============================================================
# BigInteger
# ============================================================
class BigInteger(Comparable):
ZERO = None
ONE = None
TEN = None
def __init__(self, *args):
super().__init__()
if len(args) == 0:
self._value = 0
elif len(args) == 1:
if isinstance(args[0], str):
self._value = int(str(args[0]))
elif isinstance(args[0], bytes):
self._value = int.from_bytes(args[0], byteorder="big", signed=True)
elif isinstance(args[0], bytearray):
self._value = int.from_bytes(bytes(args[0]), byteorder="big", signed=True)
elif isinstance(args[0], int):
self._value = args[0]
elif isinstance(args[0], BigInteger):
self._value = args[0]._value
else:
self._value = int(args[0])
elif len(args) == 2:
if isinstance(args[1], _random.Random):
bit_length, rng = args
self._value = rng.getrandbits(bit_length)
elif isinstance(args[1], int):
val_str, radix = args
self._value = int(str(val_str), radix)
else:
self._value = int.from_bytes(bytes(args[0]), byteorder="big", signed=True)
else:
self._value = 0
@staticmethod
def valueOf(val):
if isinstance(val, int):
if val == 0:
return BigInteger.ZERO
if val == 1:
return BigInteger.ONE
if val == 10:
return BigInteger.TEN
return BigInteger(val)
def add(self, other):
if isinstance(other, BigInteger):
return BigInteger(self._value + other._value)
return BigInteger(self._value + int(other))
def subtract(self, other):
if isinstance(other, BigInteger):
return BigInteger(self._value - other._value)
return BigInteger(self._value - int(other))
def multiply(self, other):
if isinstance(other, BigInteger):
return BigInteger(self._value * other._value)
return BigInteger(self._value * int(other))
def divide(self, other):
if isinstance(other, BigInteger):
return BigInteger(self._value // other._value)
return BigInteger(self._value // int(other))
def pow(self, exponent):
return BigInteger(self._value ** exponent)
def gcd(self, other):
return BigInteger(_py_math.gcd(self._value, other._value if isinstance(other, BigInteger) else int(other)))
def abs(self):
return BigInteger(abs(self._value))
def negate(self):
return BigInteger(-self._value)
def signum(self):
if self._value > 0:
return 1
if self._value < 0:
return -1
return 0
def mod(self, m):
if isinstance(m, BigInteger):
return BigInteger(self._value % m._value)
return BigInteger(self._value % int(m))
def remainder(self, other):
if isinstance(other, BigInteger):
return BigInteger(self._value % other._value)
return BigInteger(self._value % int(other))
def and_(self, other):
return BigInteger(self._value & (other._value if isinstance(other, BigInteger) else int(other)))
def or_(self, other):
return BigInteger(self._value | (other._value if isinstance(other, BigInteger) else int(other)))
def xor(self, other):
return BigInteger(self._value ^ (other._value if isinstance(other, BigInteger) else int(other)))
def not_(self):
return BigInteger(~self._value)
def andNot(self, other):
return BigInteger(self._value & ~(other._value if isinstance(other, BigInteger) else int(other)))
def shiftLeft(self, n):
return BigInteger(self._value << n)
def shiftRight(self, n):
return BigInteger(self._value >> n)
def getLowestSetBit(self):
if self._value == 0:
return -1
return (self._value & -self._value).bit_length() - 1
def bitLength(self):
if self._value == 0:
return 0
return self._value.bit_length()
def bitCount(self):
return bin(self._value & ((1 << self.bitLength()) - 1) if self._value >= 0 else self._value).count("1")
def isProbablePrime(self, certainty):
if abs(self._value) < 2:
return False
if abs(self._value) == 2 or abs(self._value) == 3:
return True
if self._value % 2 == 0:
return False
d = self._value - 1
s = 0
while d % 2 == 0:
d //= 2
s += 1
for _ in range(min(certainty, 20)):
a = _random.randrange(2, min(self._value - 1, 1000000))
x = pow(a, d, self._value)
if x == 1 or x == self._value - 1:
continue
for _ in range(s - 1):
x = pow(x, 2, self._value)
if x == self._value - 1:
break
else:
return False
return True
def nextProbablePrime(self):
v = self._value + 1
if v < 3:
return BigInteger(2) if v <= 2 else BigInteger(3)
while True:
if v % 2 == 0:
v += 1
for i in range(3, int(_py_math.sqrt(v)) + 1, 2):
if v % i == 0:
break
else:
return BigInteger(v)
v += 2
def compareTo(self, other):
if isinstance(other, BigInteger):
return (self._value > other._value) - (self._value < other._value)
return -1
def equals(self, other):
if isinstance(other, BigInteger):
return self._value == other._value
return False
def toByteArray(self):
if self._value == 0:
return b"\x00"
byte_length = (self._value.bit_length() + 8) // 8
return self._value.to_bytes(byte_length, byteorder="big", signed=True)
def toString(self, radix=10):
if radix == 10:
return str(self._value)
if radix == 16:
return hex(self._value)[2:]
if radix == 8:
return oct(self._value)[2:]
if radix == 2:
return bin(self._value)[2:]
return str(self._value)
def intValue(self):
return self._value & 0xFFFFFFFF
def longValue(self):
return self._value
def floatValue(self):
return float(self._value)
def doubleValue(self):
return float(self._value)
def min(self, other):
return BigInteger(min(self._value, other._value if isinstance(other, BigInteger) else int(other)))
def max(self, other):
return BigInteger(max(self._value, other._value if isinstance(other, BigInteger) else int(other)))
def modPow(self, exponent, m):
if isinstance(exponent, BigInteger):
exp_val = exponent._value
else:
exp_val = int(exponent)
mod_val = m._value if isinstance(m, BigInteger) else int(m)
return BigInteger(pow(self._value, exp_val, mod_val))
def modInverse(self, m):
mod_val = m._value if isinstance(m, BigInteger) else int(m)
return BigInteger(pow(self._value, -1, mod_val))
def divideAndRemainder(self, other):
if isinstance(other, BigInteger):
divisor = other._value
else:
divisor = int(other)
q = self._value // divisor
r = self._value % divisor
return [BigInteger(q), BigInteger(r)]
def __int__(self):
return self._value
def __str__(self):
return str(self._value)
def __neg__(self):
return self.negate()
def __abs__(self):
return self.abs()
def __add__(self, other):
return self.add(other)
def __sub__(self, other):
return self.subtract(other)
def __mul__(self, other):
return self.multiply(other)
def __truediv__(self, other):
return self.divide(other)
def __floordiv__(self, other):
return self.divide(other)
def __mod__(self, other):
return self.mod(other)
def __pow__(self, other):
return self.pow(other)
def __and__(self, other):
return self.and_(other)
def __or__(self, other):
return self.or_(other)
def __xor__(self, other):
return self.xor(other)
def __invert__(self):
return self.not_()
def __lshift__(self, n):
return self.shiftLeft(n)
def __rshift__(self, n):
return self.shiftRight(n)
BigInteger.ZERO = BigInteger(0)
BigInteger.ONE = BigInteger(1)
BigInteger.TEN = BigInteger(10)
# ============================================================
# BigDecimal
# ============================================================
class BigDecimal(Comparable):
ZERO = None
ONE = None
TEN = None
ROUND_UP = 0
ROUND_DOWN = 1
ROUND_CEILING = 2
ROUND_FLOOR = 3
ROUND_HALF_UP = 4
ROUND_HALF_DOWN = 5
ROUND_HALF_EVEN = 6
ROUND_UNNECESSARY = 7
_round_map = {
0: _DEC_ROUND_UP,
1: _DEC_ROUND_DOWN,
2: _DEC_ROUND_CEILING,
3: _DEC_ROUND_FLOOR,
4: _DEC_ROUND_HALF_UP,
5: _DEC_ROUND_HALF_DOWN,
6: _DEC_ROUND_HALF_EVEN,
7: _DEC_ROUND_HALF_EVEN,
}
def __init__(self, *args):
super().__init__()
if len(args) == 1:
val = args[0]
if isinstance(val, str):
self._dec = _Decimal(str(val))
elif isinstance(val, float):
self._dec = _Decimal(repr(val))
elif isinstance(val, int):
self._dec = _Decimal(val)
elif isinstance(val, BigInteger):
self._dec = _Decimal(val._value)
elif isinstance(val, BigDecimal):
self._dec = _Decimal(val._dec)
elif isinstance(val, _Decimal):
self._dec = val
else:
self._dec = _Decimal(str(val))
elif len(args) == 2:
val, mc = args
if isinstance(val, str):
self._dec = _Decimal(str(val))
elif isinstance(val, float):
self._dec = _Decimal(repr(val))
else:
self._dec = _Decimal(val)
if mc and hasattr(mc, 'getPrecision') and mc.getPrecision() > 0:
self._dec = +self._dec
@staticmethod
def valueOf(val):
if isinstance(val, int):
if val == 0:
return BigDecimal.ZERO
if val == 1:
return BigDecimal.ONE
if val == 10:
return BigDecimal.TEN
if isinstance(val, float):
return BigDecimal(repr(val))
return BigDecimal(val)
def add(self, other):
if isinstance(other, BigDecimal):
return BigDecimal(self._dec + other._dec)
return BigDecimal(self._dec + _Decimal(str(other)))
def subtract(self, other):
if isinstance(other, BigDecimal):
return BigDecimal(self._dec - other._dec)
return BigDecimal(self._dec - _Decimal(str(other)))
def multiply(self, other):
if isinstance(other, BigDecimal):
return BigDecimal(self._dec * other._dec)
return BigDecimal(self._dec * _Decimal(str(other)))
def divide(self, *args):
if len(args) == 1:
other = args[0]
if isinstance(other, BigDecimal):
return BigDecimal(self._dec / other._dec)
return BigDecimal(self._dec / _Decimal(str(other)))
elif len(args) == 2:
other, rounding_mode = args
if isinstance(other, BigDecimal):
result = self._dec / other._dec
else:
result = self._dec / _Decimal(str(other))
return BigDecimal(result)
elif len(args) == 3:
other, scale, rounding_mode = args
if isinstance(other, BigDecimal):
result = self._dec / other._dec
else:
result = self._dec / _Decimal(str(other))
rounded = BigDecimal(result)
return rounded.setScale(scale, rounding_mode)
def divideToIntegralValue(self, other):
if isinstance(other, BigDecimal):
return BigDecimal(int(self._dec / other._dec))
return BigDecimal(int(self._dec / _Decimal(str(other))))
def remainder(self, other):
if isinstance(other, BigDecimal):
return BigDecimal(self._dec % other._dec)
return BigDecimal(self._dec % _Decimal(str(other)))
def pow(self, n):
return BigDecimal(self._dec ** n)
def abs(self):
return BigDecimal(abs(self._dec))
def negate(self):
return BigDecimal(-self._dec)
def signum(self):
if self._dec > 0:
return 1
if self._dec < 0:
return -1
return 0
def scale(self):
return abs(self._dec.as_tuple().exponent)
def precision(self):
return len(self._dec.as_tuple().digits)
def round(self, mc):
if mc.getPrecision() > 0:
with _local_context() as ctx:
ctx.prec = mc.getPrecision()
return BigDecimal(+self._dec)
return BigDecimal(self._dec)
def setScale(self, new_scale, rounding_mode=None):
if rounding_mode is None:
qe = _Decimal(10) ** (-new_scale)
return BigDecimal(self._dec.quantize(qe))
py_mode = self._round_map.get(rounding_mode, _DEC_ROUND_HALF_UP)
qe = _Decimal(10) ** (-new_scale)
return BigDecimal(self._dec.quantize(qe, rounding=py_mode))
def ulp(self):
exponent = self._dec.as_tuple().exponent
return BigDecimal(_Decimal(10) ** exponent)
def unscaledValue(self):
t = self._dec.as_tuple()
digits = t.digits
val = int("".join(str(d) for d in digits))
return BigInteger(-val if t.sign else val)
def byteValueExact(self):
val = self.intValueExact()
if val < -128 or val > 127:
raise ArithmeticError("Overflow")
return val
def shortValueExact(self):
val = self.intValueExact()
if val < -32768 or val > 32767:
raise ArithmeticError("Overflow")
return val
def intValueExact(self):
if self._dec != int(self._dec):
raise ArithmeticError("Rounding necessary")
return int(self._dec)
def longValueExact(self):
if self._dec != int(self._dec):
raise ArithmeticError("Rounding necessary")
return int(self._dec)
def floatValue(self):
return float(self._dec)
def doubleValue(self):
return float(self._dec)
def intValue(self):
return int(self._dec)
def longValue(self):
return int(self._dec)
def toString(self):
return str(self._dec)
def toEngineeringString(self):
return format(self._dec, "e")
def toPlainString(self):
return format(self._dec, "f")
def compareTo(self, other):
if isinstance(other, BigDecimal):
return (self._dec > other._dec) - (self._dec < other._dec)
return -1
def equals(self, other):
if isinstance(other, BigDecimal):
return self._dec == other._dec
return False
def hashCode(self):
return hash(self._dec) & 0x7FFFFFFF
def min(self, other):
return BigDecimal(min(self._dec, other._dec if isinstance(other, BigDecimal) else _Decimal(str(other))))
def max(self, other):
return BigDecimal(max(self._dec, other._dec if isinstance(other, BigDecimal) else _Decimal(str(other))))
def __str__(self):
return str(self._dec)
def __int__(self):
return int(self._dec)
def __float__(self):
return float(self._dec)
def __neg__(self):
return self.negate()
def __abs__(self):
return self.abs()
def __add__(self, other):
return self.add(other)
def __sub__(self, other):
return self.subtract(other)
def __mul__(self, other):
return self.multiply(other)
def __truediv__(self, other):
return self.divide(other)
def __mod__(self, other):
return self.remainder(other)
def __pow__(self, other):
return self.pow(other)
BigDecimal.ZERO = BigDecimal(0)
BigDecimal.ONE = BigDecimal(1)
BigDecimal.TEN = BigDecimal(10)
File diff suppressed because it is too large. Load diff
+622
View File
@@ -0,0 +1,622 @@
"""
Java NIO standard library APIs implemented in Python.
java.nio package - Buffer, ByteBuffer, CharBuffer, etc.
"""
from .._core import Stub
from .lang import RuntimeException
from .io import IOException
import struct as _struct
import sys as _sys
IllegalStateException = type("IllegalStateException", (RuntimeException,), {})
IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {})
__all__ = [
"Buffer", "ByteBuffer", "CharBuffer", "ShortBuffer",
"IntBuffer", "LongBuffer", "FloatBuffer", "DoubleBuffer",
"MappedByteBuffer", "ByteOrder",
"BufferOverflowException", "BufferUnderflowException",
"ReadOnlyBufferException", "InvalidMarkException",
]
# ============================================================
# Exception classes
# ============================================================
class BufferOverflowException(RuntimeException):
pass
class BufferUnderflowException(RuntimeException):
pass
class ReadOnlyBufferException(RuntimeException):
pass
class InvalidMarkException(IllegalStateException):
pass
# ============================================================
# ByteOrder
# ============================================================
class ByteOrder(Stub):
BIG_ENDIAN = None
LITTLE_ENDIAN = None
def __init__(self, name):
super().__init__()
self._name = name
@staticmethod
def nativeOrder():
if _sys.byteorder == "little":
return ByteOrder.LITTLE_ENDIAN
return ByteOrder.BIG_ENDIAN
def toString(self):
return self._name
ByteOrder.BIG_ENDIAN = ByteOrder("BIG_ENDIAN")
ByteOrder.LITTLE_ENDIAN = ByteOrder("LITTLE_ENDIAN")
# ============================================================
# Buffer
# ============================================================
class Buffer(Stub):
def __init__(self, capacity, backing=None):
super().__init__()
self._capacity = capacity
self._position = 0
self._limit = capacity
self._mark = -1
self._backing = backing if backing is not None else bytearray(capacity)
self._read_only = False
def capacity(self):
return self._capacity
def position(self):
return self._position
def position(self, new_position):
if new_position < 0 or new_position > self._limit:
raise IllegalArgumentException("Invalid position")
self._position = new_position
return self
def limit(self):
return self._limit
def limit(self, new_limit):
if new_limit < 0 or new_limit > self._capacity:
raise IllegalArgumentException("Invalid limit")
self._limit = new_limit
if self._position > self._limit:
self._position = self._limit
if self._mark > self._limit:
self._mark = -1
return self
def mark(self):
self._mark = self._position
return self
def reset(self):
if self._mark < 0:
raise InvalidMarkException()
self._position = self._mark
return self
def clear(self):
self._position = 0
self._limit = self._capacity
self._mark = -1
return self
def flip(self):
self._limit = self._position
self._position = 0
self._mark = -1
return self
def rewind(self):
self._position = 0
self._mark = -1
return self
def remaining(self):
return self._limit - self._position
def hasRemaining(self):
return self._position < self._limit
def isReadOnly(self):
return self._read_only
def isDirect(self):
return False
def array(self):
if self._read_only:
raise ReadOnlyBufferException()
return self._backing
def arrayOffset(self):
return 0
# ============================================================
# ByteBuffer
# ============================================================
class ByteBuffer(Buffer):
def __init__(self, capacity, backing=None, order=None):
super().__init__(capacity, backing)
self._order = order or ByteOrder.BIG_ENDIAN
@staticmethod
def allocate(capacity):
return ByteBuffer(capacity, bytearray(capacity))
@staticmethod
def allocateDirect(capacity):
return ByteBuffer(capacity, bytearray(capacity))
@staticmethod
def wrap(array, offset=0, length=None):
if length is None:
length = len(array) - offset
buf = ByteBuffer(length, bytearray(array))
buf._position = offset
buf._limit = offset + length
return buf
def put(self, b, index=None):
if self._read_only:
raise ReadOnlyBufferException()
if index is None:
if isinstance(b, int):
if self._position >= self._limit:
raise BufferOverflowException()
self._backing[self._position] = b & 0xFF
self._position += 1
elif isinstance(b, ByteBuffer):
n = b.remaining()
if self.remaining() < n:
raise BufferOverflowException()
self._backing[self._position:self._position+n] = b._backing[b._position:b._limit]
self._position += n
b._position = b._limit
elif isinstance(b, (bytes, bytearray)):
if self.remaining() < len(b):
raise BufferOverflowException()
self._backing[self._position:self._position+len(b)] = b
self._position += len(b)
else:
self._backing[index] = b & 0xFF
return self
def get(self, index=None):
if index is None:
if self._position >= self._limit:
raise BufferUnderflowException()
val = self._backing[self._position]
self._position += 1
return val & 0xFF
return self._backing[index] & 0xFF
def putInt(self, value, index=None):
data = _struct.pack(">i" if self._order == ByteOrder.BIG_ENDIAN else "<i", value)
if index is not None:
self._backing[index:index+4] = data
else:
if self.remaining() < 4:
raise BufferOverflowException()
self._backing[self._position:self._position+4] = data
self._position += 4
return self
def getInt(self, index=None):
if index is not None:
data = self._backing[index:index+4]
else:
if self.remaining() < 4:
raise BufferUnderflowException()
data = self._backing[self._position:self._position+4]
self._position += 4
return _struct.unpack(">i" if self._order == ByteOrder.BIG_ENDIAN else "<i", bytes(data))[0]
def putLong(self, value, index=None):
data = _struct.pack(">q" if self._order == ByteOrder.BIG_ENDIAN else "<q", value)
if index is not None:
self._backing[index:index+8] = data
else:
if self.remaining() < 8:
raise BufferOverflowException()
self._backing[self._position:self._position+8] = data
self._position += 8
return self
def getLong(self, index=None):
if index is not None:
data = self._backing[index:index+8]
else:
if self.remaining() < 8:
raise BufferUnderflowException()
data = self._backing[self._position:self._position+8]
self._position += 8
return _struct.unpack(">q" if self._order == ByteOrder.BIG_ENDIAN else "<q", bytes(data))[0]
def putFloat(self, value, index=None):
data = _struct.pack(">f" if self._order == ByteOrder.BIG_ENDIAN else "<f", value)
if index is not None:
self._backing[index:index+4] = data
else:
if self.remaining() < 4:
raise BufferOverflowException()
self._backing[self._position:self._position+4] = data
self._position += 4
return self
def getFloat(self, index=None):
if index is not None:
data = self._backing[index:index+4]
else:
if self.remaining() < 4:
raise BufferUnderflowException()
data = self._backing[self._position:self._position+4]
self._position += 4
return _struct.unpack(">f" if self._order == ByteOrder.BIG_ENDIAN else "<f", bytes(data))[0]
def putDouble(self, value, index=None):
data = _struct.pack(">d" if self._order == ByteOrder.BIG_ENDIAN else "<d", value)
if index is not None:
self._backing[index:index+8] = data
else:
if self.remaining() < 8:
raise BufferOverflowException()
self._backing[self._position:self._position+8] = data
self._position += 8
return self
def getDouble(self, index=None):
if index is not None:
data = self._backing[index:index+8]
else:
if self.remaining() < 8:
raise BufferUnderflowException()
data = self._backing[self._position:self._position+8]
self._position += 8
return _struct.unpack(">d" if self._order == ByteOrder.BIG_ENDIAN else "<d", bytes(data))[0]
def putShort(self, value, index=None):
data = _struct.pack(">h" if self._order == ByteOrder.BIG_ENDIAN else "<h", value)
if index is not None:
self._backing[index:index+2] = data
else:
if self.remaining() < 2:
raise BufferOverflowException()
self._backing[self._position:self._position+2] = data
self._position += 2
return self
def getShort(self, index=None):
if index is not None:
data = self._backing[index:index+2]
else:
if self.remaining() < 2:
raise BufferUnderflowException()
data = self._backing[self._position:self._position+2]
self._position += 2
return _struct.unpack(">h" if self._order == ByteOrder.BIG_ENDIAN else "<h", bytes(data))[0]
def putChar(self, value, index=None):
return self.putShort(ord(value), index)
def getChar(self, index=None):
return chr(self.getShort(index))
def asIntBuffer(self):
return IntBuffer(self.remaining() // 4, self._backing, self._position, self._order)
def asLongBuffer(self):
return LongBuffer(self.remaining() // 8, self._backing, self._position, self._order)
def asFloatBuffer(self):
return FloatBuffer(self.remaining() // 4, self._backing, self._position, self._order)
def asDoubleBuffer(self):
return DoubleBuffer(self.remaining() // 8, self._backing, self._position, self._order)
def asShortBuffer(self):
return ShortBuffer(self.remaining() // 2, self._backing, self._position, self._order)
def asCharBuffer(self):
return CharBuffer(self.remaining() // 2, self._backing, self._position, self._order)
def slice(self):
n = self.remaining()
buf = ByteBuffer(n, self._backing[self._position:self._limit])
return buf
def duplicate(self):
buf = ByteBuffer(self._capacity, bytearray(self._backing))
buf._position = self._position
buf._limit = self._limit
buf._order = self._order
return buf
def asReadOnlyBuffer(self):
buf = self.duplicate()
buf._read_only = True
return buf
def order(self, order=None):
if order is not None:
self._order = order
return self
return self._order
def compact(self):
if self._read_only:
raise ReadOnlyBufferException()
remaining = self.remaining()
self._backing[:remaining] = self._backing[self._position:self._limit]
self._position = remaining
self._limit = self._capacity
self._mark = -1
return self
def toString(self):
return f"ByteBuffer[pos={self._position} lim={self._limit} cap={self._capacity}]"
def hashCode(self):
return hash(bytes(self._backing)) & 0x7FFFFFFF
def equals(self, other):
if isinstance(other, ByteBuffer):
return bytes(self._backing[self._position:self._limit]) == bytes(other._backing[other._position:other._limit])
return False
def compareTo(self, other):
if isinstance(other, ByteBuffer):
a = bytes(self._backing[self._position:self._limit])
b = bytes(other._backing[other._position:other._limit])
return (a > b) - (a < b)
return -1
# ============================================================
# IntBuffer
# ============================================================
class IntBuffer(Buffer):
def __init__(self, capacity, backing, offset=0, order=None):
super().__init__(capacity, backing)
self._int_order = order or ByteOrder.BIG_ENDIAN
self._int_offset = offset
def get(self, index=None):
if index is not None:
idx = self._int_offset + index * 4
return _struct.unpack(">i" if self._int_order == ByteOrder.BIG_ENDIAN else "<i",
bytes(self._backing[idx:idx+4]))[0]
if self.remaining() < 4:
raise BufferUnderflowException()
val = _struct.unpack(">i" if self._int_order == ByteOrder.BIG_ENDIAN else "<i",
bytes(self._backing[self._position:self._position+4]))[0]
self._position += 4
return val
def put(self, value, index=None):
data = _struct.pack(">i" if self._int_order == ByteOrder.BIG_ENDIAN else "<i", value)
if index is not None:
idx = self._int_offset + index * 4
self._backing[idx:idx+4] = data
else:
if self.remaining() < 4:
raise BufferOverflowException()
self._backing[self._position:self._position+4] = data
self._position += 4
return self
# ============================================================
# LongBuffer
# ============================================================
class LongBuffer(Buffer):
def __init__(self, capacity, backing, offset=0, order=None):
super().__init__(capacity, backing)
self._long_order = order or ByteOrder.BIG_ENDIAN
self._long_offset = offset
def get(self, index=None):
if index is not None:
idx = self._long_offset + index * 8
return _struct.unpack(">q" if self._long_order == ByteOrder.BIG_ENDIAN else "<q",
bytes(self._backing[idx:idx+8]))[0]
if self.remaining() < 8:
raise BufferUnderflowException()
val = _struct.unpack(">q" if self._long_order == ByteOrder.BIG_ENDIAN else "<q",
bytes(self._backing[self._position:self._position+8]))[0]
self._position += 8
return val
def put(self, value, index=None):
data = _struct.pack(">q" if self._long_order == ByteOrder.BIG_ENDIAN else "<q", value)
if index is not None:
idx = self._long_offset + index * 8
self._backing[idx:idx+8] = data
else:
if self.remaining() < 8:
raise BufferOverflowException()
self._backing[self._position:self._position+8] = data
self._position += 8
return self
# ============================================================
# FloatBuffer
# ============================================================
class FloatBuffer(Buffer):
def __init__(self, capacity, backing, offset=0, order=None):
super().__init__(capacity, backing)
self._float_order = order or ByteOrder.BIG_ENDIAN
self._float_offset = offset
def get(self, index=None):
if index is not None:
idx = self._float_offset + index * 4
return _struct.unpack(">f" if self._float_order == ByteOrder.BIG_ENDIAN else "<f",
bytes(self._backing[idx:idx+4]))[0]
if self.remaining() < 4:
raise BufferUnderflowException()
val = _struct.unpack(">f" if self._float_order == ByteOrder.BIG_ENDIAN else "<f",
bytes(self._backing[self._position:self._position+4]))[0]
self._position += 4
return val
def put(self, value, index=None):
data = _struct.pack(">f" if self._float_order == ByteOrder.BIG_ENDIAN else "<f", value)
if index is not None:
idx = self._float_offset + index * 4
self._backing[idx:idx+4] = data
else:
if self.remaining() < 4:
raise BufferOverflowException()
self._backing[self._position:self._position+4] = data
self._position += 4
return self
# ============================================================
# DoubleBuffer
# ============================================================
class DoubleBuffer(Buffer):
def __init__(self, capacity, backing, offset=0, order=None):
super().__init__(capacity, backing)
self._double_order = order or ByteOrder.BIG_ENDIAN
self._double_offset = offset
def get(self, index=None):
if index is not None:
idx = self._double_offset + index * 8
return _struct.unpack(">d" if self._double_order == ByteOrder.BIG_ENDIAN else "<d",
bytes(self._backing[idx:idx+8]))[0]
if self.remaining() < 8:
raise BufferUnderflowException()
val = _struct.unpack(">d" if self._double_order == ByteOrder.BIG_ENDIAN else "<d",
bytes(self._backing[self._position:self._position+8]))[0]
self._position += 8
return val
def put(self, value, index=None):
data = _struct.pack(">d" if self._double_order == ByteOrder.BIG_ENDIAN else "<d", value)
if index is not None:
idx = self._double_offset + index * 8
self._backing[idx:idx+8] = data
else:
if self.remaining() < 8:
raise BufferOverflowException()
self._backing[self._position:self._position+8] = data
self._position += 8
return self
# ============================================================
# ShortBuffer
# ============================================================
class ShortBuffer(Buffer):
def __init__(self, capacity, backing, offset=0, order=None):
super().__init__(capacity, backing)
self._short_order = order or ByteOrder.BIG_ENDIAN
self._short_offset = offset
def get(self, index=None):
if index is not None:
idx = self._short_offset + index * 2
return _struct.unpack(">h" if self._short_order == ByteOrder.BIG_ENDIAN else "<h",
bytes(self._backing[idx:idx+2]))[0]
if self.remaining() < 2:
raise BufferUnderflowException()
val = _struct.unpack(">h" if self._short_order == ByteOrder.BIG_ENDIAN else "<h",
bytes(self._backing[self._position:self._position+2]))[0]
self._position += 2
return val
def put(self, value, index=None):
data = _struct.pack(">h" if self._short_order == ByteOrder.BIG_ENDIAN else "<h", value)
if index is not None:
idx = self._short_offset + index * 2
self._backing[idx:idx+2] = data
else:
if self.remaining() < 2:
raise BufferOverflowException()
self._backing[self._position:self._position+2] = data
self._position += 2
return self
# ============================================================
# CharBuffer
# ============================================================
class CharBuffer(Buffer):
def __init__(self, capacity, backing, offset=0, order=None):
super().__init__(capacity, backing)
self._char_order = order or ByteOrder.BIG_ENDIAN
self._char_offset = offset
def get(self, index=None):
if index is not None:
idx = self._char_offset + index * 2
val = _struct.unpack(">h" if self._char_order == ByteOrder.BIG_ENDIAN else "<h",
bytes(self._backing[idx:idx+2]))[0]
return chr(val)
if self.remaining() < 2:
raise BufferUnderflowException()
val = _struct.unpack(">h" if self._char_order == ByteOrder.BIG_ENDIAN else "<h",
bytes(self._backing[self._position:self._position+2]))[0]
self._position += 2
return chr(val)
def put(self, value, index=None):
char_val = ord(value) if isinstance(value, str) else value
data = _struct.pack(">h" if self._char_order == ByteOrder.BIG_ENDIAN else "<h", char_val)
if index is not None:
idx = self._char_offset + index * 2
self._backing[idx:idx+2] = data
else:
if self.remaining() < 2:
raise BufferOverflowException()
self._backing[self._position:self._position+2] = data
self._position += 2
return self
# ============================================================
# MappedByteBuffer
# ============================================================
class MappedByteBuffer(ByteBuffer):
def __init__(self, capacity, backing=None):
super().__init__(capacity, backing)
def force(self):
return self
def isLoaded(self):
return True
def load(self):
return self
+703
View File
@@ -0,0 +1,703 @@
"""
Java NIO File standard library APIs implemented in Python.
java.nio.file package - Path, Files, FileSystem, FileVisitor, etc.
"""
from .._core import Stub
from .io import IOException
from .lang import RuntimeException, Comparable
import os as _os
import shutil as _shutil
import stat as _stat
from pathlib import Path as _PathLibPath
import tempfile as _tempfile
IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {})
__all__ = [
"Path", "Paths", "Files", "FileSystem", "FileSystems",
"FileStore", "FileVisitResult", "FileVisitor", "SimpleFileVisitor",
"DirectoryStream", "DirectoryStreamFilter",
"PathMatcher", "WatchService", "WatchKey", "WatchEvent", "Watchable",
"CopyOption", "StandardCopyOption", "LinkOption", "OpenOption",
"StandardOpenOption",
"FileAlreadyExistsException", "DirectoryNotEmptyException",
"NoSuchFileException", "FileSystemLoopException",
"FileSystemNotFoundException", "ProviderMismatchException",
"ProviderNotFoundException", "NotDirectoryException", "NotLinkException",
"AtomicMoveNotSupportedException", "AccessDeniedException",
"AccessMode",
]
# ============================================================
# Exception classes
# ============================================================
class FileAlreadyExistsException(IOException):
pass
class DirectoryNotEmptyException(IOException):
pass
class NoSuchFileException(IOException):
def __init__(self, file=None, other=None, reason=None):
msg = str(file) if file else ""
super().__init__(msg)
class FileSystemLoopException(IOException):
pass
class FileSystemNotFoundException(IOException):
pass
class ProviderMismatchException(IllegalArgumentException):
pass
class ProviderNotFoundException(IllegalArgumentException):
pass
class NotDirectoryException(IOException):
pass
class NotLinkException(IOException):
pass
class AtomicMoveNotSupportedException(IOException):
pass
class AccessDeniedException(IOException):
pass
# ============================================================
# Enums / Constants
# ============================================================
class FileVisitResult:
CONTINUE = 0
TERMINATE = 1
SKIP_SUBTREE = 2
SKIP_SIBLINGS = 3
class StandardCopyOption:
REPLACE_EXISTING = 0
COPY_ATTRIBUTES = 1
ATOMIC_MOVE = 2
class StandardOpenOption:
READ = 0
WRITE = 1
APPEND = 2
TRUNCATE_EXISTING = 3
CREATE = 4
CREATE_NEW = 5
DELETE_ON_CLOSE = 6
SPARSE = 7
SYNC = 8
DSYNC = 9
class LinkOption:
NOFOLLOW_LINKS = 0
class CopyOption(Stub):
pass
class OpenOption(Stub):
pass
class AccessMode:
READ = 0
WRITE = 1
EXECUTE = 2
# ============================================================
# Path
# ============================================================
class Path(Comparable):
def __init__(self, path_str, *more):
super().__init__()
if more:
path_str = _os.path.join(path_str, *more)
self._path = str(path_str)
self._pypath = _PathLibPath(self._path)
def getFileName(self):
return Path(self._pypath.name)
def getParent(self):
parent = self._pypath.parent
if parent == self._pypath:
return None
return Path(str(parent))
def getRoot(self):
root = self._pypath.anchor
return Path(root) if root else None
def toAbsolutePath(self):
return Path(str(self._pypath.absolute()))
def toRealPath(self):
return Path(str(self._pypath.resolve()))
def getName(self, index):
parts = self._pypath.parts
if self.getRoot():
parts = parts[1:]
if index < 0 or index >= len(parts):
raise IllegalArgumentException("Invalid index")
return Path(parts[index])
def getNameCount(self):
parts = self._pypath.parts
if self.getRoot():
parts = parts[1:]
return len(parts)
def subpath(self, beginIndex, endIndex):
parts = self._pypath.parts
if self.getRoot():
parts = parts[1:]
return Path(_os.path.join(*parts[beginIndex:endIndex]))
def startsWith(self, other):
if isinstance(other, Path):
return str(self._pypath).startswith(str(other._pypath))
return str(self._pypath).startswith(str(other))
def endsWith(self, other):
if isinstance(other, Path):
return str(self._pypath).endswith(str(other._pypath))
return str(self._pypath).endswith(str(other))
def normalize(self):
return Path(_os.path.normpath(self._path))
def resolve(self, other):
if isinstance(other, Path):
other_path = str(other)
else:
other_path = str(other)
if _os.path.isabs(other_path):
return Path(other_path)
return Path(_os.path.join(self._path, other_path))
def resolveSibling(self, other):
parent = self.getParent()
if parent:
return parent.resolve(other)
return Path(other)
def relativize(self, other):
if isinstance(other, Path):
other_str = str(other._pypath)
else:
other_str = str(other)
try:
rel = _os.path.relpath(other_str, self._path)
return Path(rel)
except ValueError:
return other
def compareTo(self, other):
if isinstance(other, Path):
return (self._path > other._path) - (self._path < other._path)
return -1
def toString(self):
return self._path
def toFile(self):
from ..io import File
return File(self._path)
def isAbsolute(self):
return _os.path.isabs(self._path)
def getFileSystem(self):
return FileSystems.getDefault()
def iterator(self):
parts = self._pypath.parts
if self.getRoot():
parts = parts[1:]
return iter([Path(p) for p in parts])
def register(self, watcher, events, *modifiers):
raise AndroidOnlyError("WatchService nio")
def __str__(self):
return self._path
def __eq__(self, other):
if isinstance(other, Path):
return self._path == other._path
return NotImplemented
def __hash__(self):
return hash(self._path)
# ============================================================
# Paths
# ============================================================
class Paths(Stub):
@staticmethod
def get(first, *more):
parts = [str(first)]
parts.extend(str(m) for m in more)
return Path(_os.path.join(*parts))
# ============================================================
# FileSystem
# ============================================================
class FileSystem(Stub):
def __init__(self):
super().__init__()
self._separator = _os.sep
self._open = True
def getPath(self, first, *more):
return Paths.get(first, *more)
def getSeparator(self):
return self._separator
def getRootDirectories(self):
if _os.name == "nt":
import string
return [Path(d + ":\\") for d in string.ascii_uppercase if _os.path.exists(d + ":\\")]
return [Path("/")]
def getUserPrincipalLookupService(self):
raise AndroidOnlyError("UserPrincipalLookupService")
def newWatchService(self):
raise AndroidOnlyError("WatchService")
def getFileStores(self):
return [FileStore("/")]
def supportedFileAttributeViews(self):
return ["basic", "owner", "posix"]
def close(self):
self._open = False
def isOpen(self):
return self._open
def isReadOnly(self):
return False
class FileSystems(Stub):
_default = None
@staticmethod
def getDefault():
if FileSystems._default is None:
FileSystems._default = FileSystem()
return FileSystems._default
# ============================================================
# FileStore
# ============================================================
class FileStore(Stub):
def __init__(self, path):
super().__init__()
self._path = path
def name(self):
return _os.path.basename(self._path) or "/"
def type(self):
return "ext4"
def isReadOnly(self):
return False
def getTotalSpace(self):
try:
st = _os.statvfs(self._path)
return st.f_frsize * st.f_blocks
except OSError:
return 0
def getUsableSpace(self):
try:
st = _os.statvfs(self._path)
return st.f_frsize * st.f_bavail
except OSError:
return 0
def getUnallocatedSpace(self):
try:
st = _os.statvfs(self._path)
return st.f_frsize * st.f_bfree
except OSError:
return 0
def toString(self):
return f"{self.name()} ({self.type()})"
# ============================================================
# FileVisitor / SimpleFileVisitor
# ============================================================
class FileVisitor(Stub):
def preVisitDirectory(self, dir, attrs):
return FileVisitResult.CONTINUE
def postVisitDirectory(self, dir, exc):
return FileVisitResult.CONTINUE
def visitFile(self, file, attrs):
return FileVisitResult.CONTINUE
def visitFileFailed(self, file, exc):
return FileVisitResult.CONTINUE
class SimpleFileVisitor(FileVisitor):
pass
# ============================================================
# DirectoryStream / Filter
# ============================================================
class DirectoryStream(Stub):
def __init__(self, path, filter=None):
super().__init__()
self._path = path
self._filter = filter
def __iter__(self):
try:
entries = _os.listdir(str(self._path))
for entry in entries:
p = Path(self._path, entry)
if self._filter is None or self._filter.accept(p):
yield p
except OSError:
raise IOException(f"Failed to list directory: {self._path}")
def close(self):
pass
class DirectoryStreamFilter(Stub):
def accept(self, entry):
raise NotImplementedError
# ============================================================
# PathMatcher
# ============================================================
class PathMatcher(Stub):
def __init__(self, pattern):
super().__init__()
self._pattern = pattern
def matches(self, path):
import fnmatch
return fnmatch.fnmatch(str(path), self._pattern)
# ============================================================
# WatchService / WatchKey / WatchEvent / Watchable (stubs)
# ============================================================
class Watchable(Stub):
def register(self, watcher, events, *modifiers):
raise AndroidOnlyError("WatchService")
class WatchService(Stub):
def close(self):
pass
def poll(self):
return None
def take(self):
raise AndroidOnlyError("WatchService.take")
def poll(self, timeout, unit):
return None
class WatchKey(Stub):
def isValid(self):
return True
def pollEvents(self):
return []
def reset(self):
return True
def cancel(self):
pass
def watchable(self):
return None
class WatchEvent(Stub):
class Kind(Stub):
def name(self):
return ""
def type(self):
return None
def kind(self):
return None
def count(self):
return 0
def context(self):
return None
# ============================================================
# Files
# ============================================================
class Files(Stub):
@staticmethod
def copy(source, target, *options):
src = str(source)
dst = str(target)
replace = any(isinstance(o, StandardCopyOption) and o == StandardCopyOption.REPLACE_EXISTING for o in options)
try:
if replace and _os.path.exists(dst):
_os.remove(dst)
if _os.path.isdir(src):
_shutil.copytree(src, dst)
else:
_shutil.copy2(src, dst)
except OSError as e:
raise IOException(str(e))
@staticmethod
def move(source, target, *options):
src = str(source)
dst = str(target)
replace = any(isinstance(o, StandardCopyOption) and o == StandardCopyOption.REPLACE_EXISTING for o in options)
try:
if replace and _os.path.exists(dst):
_os.remove(dst)
_shutil.move(src, dst)
except OSError as e:
raise IOException(str(e))
@staticmethod
def delete(path):
try:
p = str(path)
if _os.path.isdir(p):
_os.rmdir(p)
else:
_os.remove(p)
except OSError as e:
raise NoSuchFileException(str(path))
@staticmethod
def deleteIfExists(path):
try:
Files.delete(path)
return True
except NoSuchFileException:
return False
@staticmethod
def createDirectories(path):
_os.makedirs(str(path), exist_ok=True)
return path
@staticmethod
def createDirectory(path, attrs=None):
_os.makedirs(str(path), exist_ok=True)
return path
@staticmethod
def createFile(path, attrs=None):
p = str(path)
if _os.path.exists(p):
raise FileAlreadyExistsException(str(path))
open(p, "a").close()
return path
@staticmethod
def createTempFile(prefix, suffix, dir=None):
f = _tempfile.NamedTemporaryFile(prefix=prefix, suffix=suffix,
dir=str(dir) if dir else None, delete=False)
p = f.name
f.close()
return Path(p)
@staticmethod
def createTempDirectory(prefix, attrs=None):
d = _tempfile.mkdtemp(prefix=prefix)
return Path(d)
@staticmethod
def exists(path, *options):
return _os.path.exists(str(path))
@staticmethod
def notExists(path, *options):
return not _os.path.exists(str(path))
@staticmethod
def isReadable(path):
return _os.access(str(path), _os.R_OK)
@staticmethod
def isWritable(path):
return _os.access(str(path), _os.W_OK)
@staticmethod
def isExecutable(path):
return _os.access(str(path), _os.X_OK)
@staticmethod
def isDirectory(path, *options):
return _os.path.isdir(str(path))
@staticmethod
def isRegularFile(path, *options):
return _os.path.isfile(str(path))
@staticmethod
def isSymbolicLink(path):
return _os.path.islink(str(path))
@staticmethod
def isHidden(path):
return _os.path.basename(str(path)).startswith(".")
@staticmethod
def getLastModifiedTime(path, *options):
return int(_os.path.getmtime(str(path)) * 1000)
@staticmethod
def setLastModifiedTime(path, time):
_os.utime(str(path), (time / 1000, time / 1000))
@staticmethod
def getAttribute(path, attribute, *options):
return None
@staticmethod
def setAttribute(path, attribute, value, *options):
pass
@staticmethod
def getPosixFilePermissions(path, *options):
return []
@staticmethod
def setPosixFilePermissions(path, perms, *options):
pass
@staticmethod
def newInputStream(path, *options):
from ..io import FileInputStream
return FileInputStream(str(path))
@staticmethod
def newOutputStream(path, *options):
from ..io import FileOutputStream
append = any(isinstance(o, StandardOpenOption) and o == StandardOpenOption.APPEND for o in options)
return FileOutputStream(str(path), append)
@staticmethod
def newBufferedReader(path, charset=None):
from ..io import BufferedReader, InputStreamReader, FileInputStream
inp = FileInputStream(str(path))
reader = InputStreamReader(inp, charset or "utf-8")
return BufferedReader(reader)
@staticmethod
def newBufferedWriter(path, charset=None, *options):
from ..io import BufferedWriter, OutputStreamWriter, FileOutputStream
append = any(isinstance(o, StandardOpenOption) and o == StandardOpenOption.APPEND for o in options)
out = FileOutputStream(str(path), append)
writer = OutputStreamWriter(out, charset or "utf-8")
return BufferedWriter(writer)
@staticmethod
def readAllBytes(path):
with open(str(path), "rb") as f:
return f.read()
@staticmethod
def readAllLines(path, charset=None):
with open(str(path), "r", encoding=charset or "utf-8") as f:
return f.read().splitlines()
@staticmethod
def readString(path, charset=None):
with open(str(path), "r", encoding=charset or "utf-8") as f:
return f.read()
@staticmethod
def write(path, content, *options):
mode = "wb" if isinstance(content, bytes) else "w"
open_mode = mode
if any(isinstance(o, StandardOpenOption) and o == StandardOpenOption.APPEND for o in options):
open_mode = "ab" if mode == "wb" else "a"
with open(str(path), open_mode) as f:
f.write(content)
@staticmethod
def writeString(path, content, *options):
open_mode = "w"
if any(isinstance(o, StandardOpenOption) and o == StandardOpenOption.APPEND for o in options):
open_mode = "a"
with open(str(path), open_mode, encoding="utf-8") as f:
f.write(content)
@staticmethod
def list(path):
try:
return [Path(_os.path.join(str(path), entry)) for entry in _os.listdir(str(path))]
except OSError:
raise IOException(f"Failed to list directory: {path}")
@staticmethod
def walk(path, maxDepth=None):
for root, dirs, files in _os.walk(str(path)):
yield Path(root)
@staticmethod
def find(start, maxDepth, matcher, *options):
for root, dirs, files in _os.walk(str(start)):
for f in files:
p = Path(_os.path.join(root, f))
if matcher.matches(p):
yield p
@staticmethod
def lines(path, charset=None):
with open(str(path), "r", encoding=charset or "utf-8") as f:
for line in f:
yield line.rstrip("\n")
@staticmethod
def getFileStore(path):
return FileStore(str(path))
@staticmethod
def probeContentType(path):
return "application/octet-stream"
@staticmethod
def getOwner(path, *options):
raise AndroidOnlyError("getOwner")
@staticmethod
def setOwner(path, owner, *options):
raise AndroidOnlyError("setOwner")
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+287
View File
@@ -0,0 +1,287 @@
"""
Java regex standard library APIs implemented in Python.
java.util.regex package - Pattern, Matcher, MatchResult, PatternSyntaxException.
"""
from .._core import Stub
from .io import IOException
from .lang import RuntimeException
import re as _re
IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {})
IllegalStateException = type("IllegalStateException", (RuntimeException,), {})
__all__ = [
"Pattern", "Matcher", "MatchResult", "PatternSyntaxException",
]
# ============================================================
# PatternSyntaxException
# ============================================================
class PatternSyntaxException(IllegalArgumentException):
def __init__(self, desc, regex, index):
super().__init__(f"{desc}: {regex} at index {index}")
self._desc = desc
self._regex = regex
self._index = index
def getDescription(self):
return self._desc
def getPattern(self):
return self._regex
def getIndex(self):
return self._index
def getMessage(self):
return f"{self._desc} near index {self._index}: {self._regex}"
# ============================================================
# MatchResult
# ============================================================
class MatchResult(Stub):
def start(self, group=None):
raise NotImplementedError
def end(self, group=None):
raise NotImplementedError
def group(self, group=None):
raise NotImplementedError
def groupCount(self):
raise NotImplementedError
# ============================================================
# Matcher
# ============================================================
class Matcher(MatchResult):
def __init__(self, pattern, input_str):
super().__init__()
self._pattern = pattern
self._input = input_str
self._re_pattern = pattern._pattern
self._match = None
self._region_start = 0
self._region_end = len(input_str)
self._transparent_bounds = False
self._anchoring_bounds = True
self._last_match = None
def matches(self):
self._match = self._re_pattern.fullmatch(self._input, self._region_start, self._region_end)
self._last_match = self._match
return self._match is not None
def find(self, start=None):
if start is not None:
self._match = self._re_pattern.search(self._input, start, self._region_end)
else:
if self._match is None:
self._match = self._re_pattern.search(self._input, self._region_start, self._region_end)
else:
pos = self._match.end()
self._match = self._re_pattern.search(self._input, pos, self._region_end)
if self._match:
self._last_match = self._match
return self._match is not None
def lookingAt(self):
self._match = self._re_pattern.match(self._input, self._region_start, self._region_end)
self._last_match = self._match
return self._match is not None
def start(self, group=0):
if self._match is None:
raise IllegalStateException("No match available")
if group == 0:
return self._match.start()
return self._match.start(group)
def end(self, group=0):
if self._match is None:
raise IllegalStateException("No match available")
if group == 0:
return self._match.end()
return self._match.end(group)
def group(self, group=0):
if self._match is None:
raise IllegalStateException("No match available")
if isinstance(group, str):
return self._match.group(group)
return self._match.group(group)
def groupCount(self):
return self._re_pattern.groups
def replaceAll(self, replacement):
return self._re_pattern.sub(replacement, self._input)
def replaceFirst(self, replacement):
return self._re_pattern.sub(replacement, self._input, count=1)
def appendReplacement(self, sb, replacement):
if self._match is None:
raise IllegalStateException("No match available")
self._append_pos = getattr(self, '_append_pos', 0)
sb.append(self._input[self._append_pos:self._match.start()])
sb.append(self._re_pattern.sub(replacement, self._match.group(), count=1))
self._append_pos = self._match.end()
return self
def appendTail(self, sb):
self._append_pos = getattr(self, '_append_pos', 0)
sb.append(self._input[self._append_pos:])
return sb
def reset(self, input_str=None):
if input_str is not None:
self._input = input_str
self._region_end = len(input_str)
self._match = None
self._last_match = None
return self
def region(self, start, end):
self._region_start = start
self._region_end = end
return self
def regionStart(self):
return self._region_start
def regionEnd(self):
return self._region_end
def hasTransparentBounds(self):
return self._transparent_bounds
def hasAnchoringBounds(self):
return self._anchoring_bounds
def useAnchoringBounds(self, value):
self._anchoring_bounds = value
return self
def useTransparentBounds(self, value):
self._transparent_bounds = value
return self
def hitEnd(self):
return self._match is not None and self._match.end() >= self._region_end
def requireEnd(self):
return self._match is not None and not self.hitEnd()
def toMatchResult(self):
result = Matcher(self._pattern, self._input)
result._match = self._match
result._last_match = self._last_match
return result
def pattern(self):
return self._pattern
def toString(self):
return f"Matcher[pattern={self._pattern.pattern()}]"
def namedGroups(self):
return self._re_pattern.groupindex
class IllegalStateException(RuntimeException):
pass
# ============================================================
# Pattern
# ============================================================
class Pattern(Stub):
CANON_EQ = 128
CASE_INSENSITIVE = 2
COMMENTS = 4
DOTALL = 32
LITERAL = 16
MULTILINE = 8
UNICODE_CASE = 64
UNICODE_CHARACTER_CLASS = 256
UNIX_LINES = 1
_re_flags_map = {
0: 0, # default
2: _re.IGNORECASE,
8: _re.MULTILINE,
32: _re.DOTALL,
64: _re.UNICODE,
4: _re.VERBOSE,
}
def __init__(self, regex, flags=0):
super().__init__()
self._regex = str(regex)
self._flags = flags
re_flags = 0
for java_flag, py_flag in self._re_flags_map.items():
if flags & java_flag:
re_flags |= py_flag
try:
self._pattern = _re.compile(self._regex, re_flags)
except _re.error as e:
raise PatternSyntaxException(str(e), self._regex, e.pos if hasattr(e, 'pos') else 0)
@staticmethod
def compile(regex, flags=0):
return Pattern(regex, flags)
def matcher(self, input_str):
return Matcher(self, input_str)
def split(self, input_str, limit=0):
if limit <= 0:
parts = self._pattern.split(str(input_str))
if limit == 0:
while parts and parts[-1] == "":
parts.pop()
else:
parts = self._pattern.split(str(input_str), maxsplit=limit - 1)
while len(parts) < limit:
parts.append("")
return parts
def splitAsStream(self, input_str):
return iter(self.split(input_str))
@staticmethod
def matches(regex, input_str):
return bool(_re.fullmatch(regex, str(input_str)))
@staticmethod
def quote(s):
return _re.escape(str(s))
def flags(self):
return self._flags
def pattern(self):
return self._regex
def toString(self):
return self._regex
def predicate(self):
return lambda s: bool(self._pattern.fullmatch(str(s)))
def asPredicate(self):
return self.predicate()
def asMatchPredicate(self):
return lambda s: bool(self._pattern.fullmatch(str(s)))
+697
View File
@@ -0,0 +1,697 @@
"""
Java zip standard library APIs implemented in Python.
java.util.zip package - ZipFile, ZipEntry, GZIP, CRC, Deflater, Inflater.
"""
from .._core import Stub
from .io import IOException, Closeable, Flushable
import zipfile as _zipfile
import gzip as _gzip
import zlib as _zlib
import io as _pyio
import os as _os
from datetime import datetime as _datetime
__all__ = [
"ZipFile", "ZipEntry", "ZipOutputStream", "ZipInputStream",
"GZIPInputStream", "GZIPOutputStream",
"CRC32", "CheckedInputStream", "CheckedOutputStream",
"Adler32",
"DataFormatException", "ZipException",
"Deflater", "Inflater",
"DeflaterOutputStream", "InflaterInputStream",
]
# ============================================================
# Exception classes
# ============================================================
class ZipException(IOException):
pass
class DataFormatException(Exception):
pass
# ============================================================
# CRC32
# ============================================================
class CRC32(Stub):
def __init__(self):
super().__init__()
self._value = 0
def getValue(self):
return self._value
def reset(self):
self._value = 0
def update(self, b, off=None, length=None):
if isinstance(b, int):
self._value = _zlib.crc32(bytes([b & 0xFF]), self._value) & 0xFFFFFFFF
elif isinstance(b, (bytes, bytearray)):
if off is not None and length is not None:
self._value = _zlib.crc32(bytes(b[off:off+length]), self._value) & 0xFFFFFFFF
else:
self._value = _zlib.crc32(bytes(b), self._value) & 0xFFFFFFFF
# ============================================================
# Adler32
# ============================================================
class Adler32(Stub):
def __init__(self):
super().__init__()
self._value = 1
def getValue(self):
return self._value
def reset(self):
self._value = 1
def update(self, b, off=None, length=None):
if isinstance(b, int):
self._value = _zlib.adler32(bytes([b & 0xFF]), self._value) & 0xFFFFFFFF
elif isinstance(b, (bytes, bytearray)):
if off is not None and length is not None:
self._value = _zlib.adler32(bytes(b[off:off+length]), self._value) & 0xFFFFFFFF
else:
self._value = _zlib.adler32(bytes(b), self._value) & 0xFFFFFFFF
# ============================================================
# CheckedInputStream / CheckedOutputStream
# ============================================================
class CheckedInputStream(Closeable):
def __init__(self, inp, cksum):
super().__init__()
self._in = inp
self._cksum = cksum
def read(self, b=None, off=None, length=None):
if b is None:
result = self._in.read()
if result >= 0:
self._cksum.update(result)
return result
if off is None:
off = 0
if length is None:
length = len(b)
result = self._in.read(b, off, length)
if result > 0:
self._cksum.update(b[off:off+result])
return result
def getChecksum(self):
return self._cksum
def close(self):
self._in.close()
def skip(self, n):
return self._in.skip(n)
class CheckedOutputStream(Closeable, Flushable):
def __init__(self, out, cksum):
super().__init__()
self._out = out
self._cksum = cksum
def write(self, b, off=None, length=None):
if isinstance(b, int):
self._out.write(b)
self._cksum.update(b)
else:
if off is not None and length is not None:
data = bytes(b[off:off+length])
else:
data = bytes(b)
self._out.write(data)
self._cksum.update(data)
def getChecksum(self):
return self._cksum
def flush(self):
self._out.flush()
def close(self):
self._out.close()
# ============================================================
# ZipEntry
# ============================================================
class ZipEntry(Stub):
STORED = 0
DEFLATED = 8
def __init__(self, name):
super().__init__()
self._name = name
self._method = ZipEntry.DEFLATED
self._size = 0
self._compressed_size = 0
self._crc = 0
self._comment = ""
self._time = 0
self._extra = b""
self._is_directory = name.endswith("/")
def getName(self):
return self._name
def getMethod(self):
return self._method
def setMethod(self, method):
self._method = method
def getSize(self):
return self._size
def setSize(self, size):
self._size = size
def getCompressedSize(self):
return self._compressed_size
def setCompressedSize(self, size):
self._compressed_size = size
def getCrc(self):
return self._crc
def setCrc(self, crc):
self._crc = crc
def getComment(self):
return self._comment
def setComment(self, comment):
self._comment = comment
def getTime(self):
return self._time
def setTime(self, time):
self._time = time
def getExtra(self):
return self._extra
def setExtra(self, extra):
self._extra = extra
def isDirectory(self):
return self._is_directory
def toString(self):
return self._name
def __str__(self):
return self._name
# ============================================================
# ZipFile
# ============================================================
class ZipFile(Closeable):
OPEN_READ = 1
OPEN_DELETE = 2
def __init__(self, file, mode="r"):
super().__init__()
if isinstance(file, str):
path = file
else:
path = file.getPath()
self._path = path
self._mode = mode
try:
self._zfile = _zipfile.ZipFile(path, mode)
except Exception as e:
raise ZipException(str(e))
def entries(self):
return iter(self._zfile.infolist())
def getEntry(self, name):
try:
info = self._zfile.getinfo(name)
entry = ZipEntry(info.filename)
entry.setMethod(info.compress_type)
entry.setSize(info.file_size)
entry.setCompressedSize(info.compress_size)
entry.setCrc(info.CRC)
entry.setTime(int(_datetime(*info.date_time).timestamp() * 1000) if info.date_time else 0)
return entry
except KeyError:
return None
def getInputStream(self, entry):
try:
data = self._zfile.read(entry.getName() if isinstance(entry, ZipEntry) else entry)
from ..io import ByteArrayInputStream
return ByteArrayInputStream(data)
except Exception as e:
raise ZipException(str(e))
def close(self):
self._zfile.close()
def size(self):
return len(self._zfile.namelist())
def getName(self):
return self._path
def getComment(self):
return ""
# ============================================================
# ZipOutputStream
# ============================================================
class ZipOutputStream(Closeable, Flushable):
STORED = 0
DEFLATED = 8
def __init__(self, out):
super().__init__()
self._out = out
self._zfile = _zipfile.ZipFile(out, "w", _zipfile.ZIP_DEFLATED)
self._method = ZipOutputStream.DEFLATED
self._level = -1
self._comment = ""
def putNextEntry(self, entry):
zi = _zipfile.ZipInfo(entry.getName() if isinstance(entry, ZipEntry) else str(entry))
if hasattr(entry, 'getTime') and entry.getTime():
dt = _datetime.fromtimestamp(entry.getTime() / 1000.0)
zi.date_time = dt.timetuple()[:6]
self._zfile.writestr(zi, b"")
def closeEntry(self):
pass
def write(self, b, off=None, length=None):
if off is not None and length is not None:
self._zfile._zout.write(bytes(b[off:off+length]))
else:
self._zfile._zout.write(bytes(b) if not isinstance(b, (bytes, bytearray)) else b)
def finish(self):
self._zfile.close()
def close(self):
self._zfile.close()
def setMethod(self, method):
self._method = method
def setLevel(self, level):
self._level = level
def setComment(self, comment):
self._comment = comment
def flush(self):
self._zfile._zout.flush() if hasattr(self._zfile._zout, 'flush') else None
# ============================================================
# ZipInputStream
# ============================================================
class ZipInputStream(Closeable):
def __init__(self, inp):
super().__init__()
self._inp = inp
self._zfile = _zipfile.ZipFile(inp)
self._entries = iter(self._zfile.infolist())
self._current_entry = None
self._current_data = b""
self._current_pos = 0
def getNextEntry(self):
try:
info = next(self._entries)
entry = ZipEntry(info.filename)
entry.setMethod(info.compress_type)
entry.setSize(info.file_size)
entry.setCompressedSize(info.compress_size)
entry.setCrc(info.CRC)
self._current_entry = info
self._current_data = self._zfile.read(info)
self._current_pos = 0
return entry
except StopIteration:
self._current_entry = None
self._current_data = b""
return None
def read(self, b=None, off=None, length=None):
if self._current_entry is None:
return -1
avail = len(self._current_data) - self._current_pos
if avail <= 0:
return -1
if b is None:
val = self._current_data[self._current_pos]
self._current_pos += 1
return val
if off is None:
off = 0
if length is None:
length = len(b)
n = min(length, avail)
if isinstance(b, bytearray):
b[off:off+n] = self._current_data[self._current_pos:self._current_pos+n]
self._current_pos += n
return n
def closeEntry(self):
self._current_entry = None
self._current_data = b""
def available(self):
if self._current_entry is None:
return 0
return len(self._current_data) - self._current_pos
def close(self):
self._zfile.close()
# ============================================================
# GZIPInputStream
# ============================================================
class GZIPInputStream(Closeable):
def __init__(self, inp, size=512):
super().__init__()
self._inp = inp
self._gzip_file = _gzip.GzipFile(fileobj=_pyio.BufferedReader(inp) if hasattr(inp, 'read') else inp)
def read(self, b=None, off=None, length=None):
if b is None:
data = self._gzip_file.read(1)
return data[0] if data else -1
if off is None:
off = 0
if length is None:
length = len(b)
data = self._gzip_file.read(length)
if not data:
return -1
n = len(data)
if isinstance(b, bytearray):
b[off:off+n] = data
else:
ba = bytearray(b)
ba[off:off+n] = data
b[:] = bytes(ba)
return n
def close(self):
self._gzip_file.close()
# ============================================================
# GZIPOutputStream
# ============================================================
class GZIPOutputStream(Closeable, Flushable):
def __init__(self, out, size=512):
super().__init__()
self._out = out
self._gzip_file = _gzip.GzipFile(fileobj=out, mode="wb")
def write(self, b, off=None, length=None):
if isinstance(b, int):
self._gzip_file.write(bytes([b & 0xFF]))
elif isinstance(b, (bytes, bytearray)):
if off is not None and length is not None:
self._gzip_file.write(bytes(b[off:off+length]))
else:
self._gzip_file.write(bytes(b))
def finish(self):
self._gzip_file.close()
def close(self):
self._gzip_file.close()
def flush(self):
self._gzip_file.flush()
# ============================================================
# Deflater
# ============================================================
class Deflater(Stub):
BEST_COMPRESSION = 9
BEST_SPEED = 1
DEFAULT_COMPRESSION = -1
DEFAULT_STRATEGY = 0
FILTERED = 1
HUFFMAN_ONLY = 2
NO_COMPRESSION = 0
NO_FLUSH = 0
SYNC_FLUSH = 2
FULL_FLUSH = 3
def __init__(self, level=-1, nowrap=False):
super().__init__()
self._level = level
self._nowrap = nowrap
self._compress = _zlib.compressobj(level=level if level >= 0 else 6,
method=_zlib.DEFLATED,
wbits=-_zlib.MAX_WBITS if nowrap else _zlib.MAX_WBITS)
self._input_buffer = b""
self._finished = False
def setInput(self, data, off=0, length=-1):
if length < 0:
length = len(data) - off
self._input_buffer = bytes(data[off:off+length])
def needsInput(self):
return len(self._input_buffer) == 0
def setDictionary(self, dictionary):
pass
def getTotalIn(self):
return len(self._input_buffer)
def getTotalOut(self):
return 0
def deflate(self, output, off=0, length=-1):
if length < 0:
length = len(output) - off
data = self._compress.compress(self._input_buffer)
self._input_buffer = b""
n = min(len(data), length)
if isinstance(output, bytearray):
output[off:off+n] = data[:n]
else:
ba = bytearray(output)
ba[off:off+n] = data[:n]
output[:] = bytes(ba)
return n
def finish(self):
self._finished = True
def finished(self):
return self._finished
def end(self):
pass
def getAdler(self):
return 1
def reset(self):
self._compress = _zlib.compressobj(level=self._level if self._level >= 0 else 6,
method=_zlib.DEFLATED,
wbits=-_zlib.MAX_WBITS if self._nowrap else _zlib.MAX_WBITS)
self._input_buffer = b""
self._finished = False
# ============================================================
# Inflater
# ============================================================
class Inflater(Stub):
def __init__(self, nowrap=False):
super().__init__()
self._nowrap = nowrap
wbits = -_zlib.MAX_WBITS if nowrap else _zlib.MAX_WBITS
self._decompress = _zlib.decompressobj(wbits)
self._input_buffer = b""
self._finished = False
def setInput(self, data, off=0, length=-1):
if length < 0:
length = len(data) - off
self._input_buffer = bytes(data[off:off+length])
def needsInput(self):
return len(self._input_buffer) == 0
def setDictionary(self, dictionary):
pass
def getTotalIn(self):
return len(self._input_buffer)
def getTotalOut(self):
return 0
def inflate(self, output, off=0, length=-1):
if length < 0:
length = len(output) - off
try:
data = self._decompress.decompress(self._input_buffer)
self._input_buffer = b""
n = min(len(data), length)
if isinstance(output, bytearray):
output[off:off+n] = data[:n]
else:
ba = bytearray(output)
ba[off:off+n] = data[:n]
output[:] = bytes(ba)
return n
except _zlib.error as e:
raise DataFormatException(str(e))
def finished(self):
return self._finished
def end(self):
pass
def getAdler(self):
return 1
def needsDictionary(self):
return False
def reset(self):
wbits = -_zlib.MAX_WBITS if self._nowrap else _zlib.MAX_WBITS
self._decompress = _zlib.decompressobj(wbits)
self._input_buffer = b""
self._finished = False
# ============================================================
# DeflaterOutputStream
# ============================================================
class DeflaterOutputStream(Closeable, Flushable):
def __init__(self, out, deflater=None, size=512):
super().__init__()
self._out = out
self._deflater = deflater or Deflater()
self._buffer = bytearray(size)
def write(self, b, off=None, length=None):
if isinstance(b, int):
self._out.write(bytes([b & 0xFF]))
else:
if off is not None and length is not None:
self._out.write(bytes(b[off:off+length]))
else:
self._deflater.setInput(bytes(b) if isinstance(b, (bytes, bytearray)) else bytes(b))
while not self._deflater.needsInput():
n = self._deflater.deflate(self._buffer)
if n > 0:
self._out.write(bytes(self._buffer[:n]))
def finish(self):
self._deflater.finish()
while not self._deflater.finished():
n = self._deflater.deflate(self._buffer)
if n > 0:
self._out.write(bytes(self._buffer[:n]))
def close(self):
self.finish()
self._deflater.end()
self._out.close()
def flush(self):
self._out.flush()
# ============================================================
# InflaterInputStream
# ============================================================
class InflaterInputStream(Closeable):
def __init__(self, inp, inflater=None, size=512):
super().__init__()
self._in = inp
self._inflater = inflater or Inflater()
self._buffer = bytearray(size)
def read(self, b=None, off=None, length=None):
if b is None:
buf = bytearray(1)
n = self._read_from_inflater(buf, 0, 1)
return buf[0] if n > 0 else -1
if off is None:
off = 0
if length is None:
length = len(b)
return self._read_from_inflater(b, off, length)
def _read_from_inflater(self, b, off, length):
while not self._inflater.finished():
if self._inflater.needsInput():
data = self._in.read(self._buffer)
if data is None or data == -1 or data == 0:
break
if isinstance(data, int):
break
self._inflater.setInput(data)
try:
n = self._inflater.inflate(b, off, length)
if n > 0:
return n
except DataFormatException:
break
return -1
def available(self):
return 0
def close(self):
self._inflater.end()
self._in.close()
def mark(self, readlimit):
pass
def reset(self):
raise IOException("mark/reset not supported")
def markSupported(self):
return False
Loaded 100 of 104 files, more files were not shown because too many files have changed in this diff. Show more