feat: 新增 42 个 Android API 模块

新增模块覆盖:
系统层 (14): system, security, print, opengl, renderscript, transition,
test, service_notification, service_controls, service_quicksettings,
service_wallpaper, service_autofill, service_dreams, service_vr

应用层 (13): app_admin, app_backup, app_job, app_usage, app_role,
app_people, appwidget, companion, credentials, mtp, sip, telecom, speech

硬件媒体层 (15): hardware_display, hardware_fingerprint, hardware_usb,
hardware_input, media_audiofx, media_browse, media_session, media_tv,
media_midi, media_projection, uwb, se, view_autofill, view_textclassifier,
view_translation

总计 94 个模块,75,342 行代码
This commit is contained in:
shuiyu1123 committed 2026-06-24 12:00:28 +00:00
1 parent 8ad62be068
commit 506f9ac725
43 files changed
+16704 -23

No files matched your search

+424
View File
@@ -0,0 +1,424 @@
"""
Android Device Administration API — DevicePolicyManager, DeviceAdminInfo, DeviceAdminReceiver, SystemUpdatePolicy
方法签名完全对齐 Android SDK
"""
import os as _os
import sys
import tempfile
import shutil
from typing import Any, Dict, List, Optional, Tuple, Callable
from .._core import Stub, AndroidOnlyError
from ..java.lang import RuntimeException
IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {})
__all__ = [
"DevicePolicyManager",
"DeviceAdminInfo",
"DeviceAdminReceiver",
"SystemUpdatePolicy",
]
# ═══════════════════════════════════════════════════════════════
# DevicePolicyManager 常量
# ═══════════════════════════════════════════════════════════════
class DevicePolicyManager(Stub):
"""DevicePolicyManager — 设备策略管理器 (桌面存根)"""
# --- Action 常量 ---
ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN"
ACTION_DEVICE_ADMIN_SET_DOWNLOAD_URL = "android.app.action.DEVICE_ADMIN_SET_DOWNLOAD_URL"
ACTION_ADMIN_POLICY_COMPLIANCE = "android.app.action.ADMIN_POLICY_COMPLIANCE"
ACTION_PROVISION_MANAGED_PROFILE = "android.app.action.PROVISION_MANAGED_PROFILE"
ACTION_PROVISION_FINANCED_DEVICE = "android.app.action.PROVISION_FINANCED_DEVICE"
ACTION_STATE_USER_SETUP_COMPLETE = "android.app.action.STATE_USER_SETUP_COMPLETE"
ACTION_DEVICE_OWNER_CHANGED = "android.app.action.DEVICE_OWNER_CHANGED"
ACTION_PROFILE_OWNER_CHANGED = "android.app.action.PROFILE_OWNER_CHANGED"
ACTION_SET_NEW_PARENT_PROFILE_PASSWORD = "android.app.action.SET_NEW_PARENT_PROFILE_PASSWORD"
ACTION_SET_NEW_PASSWORD = "android.app.action.SET_NEW_PASSWORD"
ACTION_START_ENCRYPTION = "android.app.action.START_ENCRYPTION"
ACTION_SYSTEM_UPDATE_POLICY_CHANGED = "android.app.action.SYSTEM_UPDATE_POLICY_CHANGED"
ACTION_APPLICATION_VERB = "android.app.action.APPLICATION_VERB"
ACTION_MANAGED_PROFILE_PROVISIONED = "android.app.action.MANAGED_PROFILE_PROVISIONED"
ACTION_ORG_OWNED_PROVISIONING = "android.app.action.ORG_OWNED_PROVISIONING"
ACTION_GET_PROVISIONING_MODE = "android.app.action.GET_PROVISIONING_MODE"
ACTION_ADMIN_SERVICE = "android.app.action.ADMIN_SERVICE"
# --- Password quality constants ---
PASSWORD_QUALITY_UNSPECIFIED = 0
PASSWORD_QUALITY_BIOMETRIC_WEAK = 0x8000
PASSWORD_QUALITY_SOMETHING = 0x10000
PASSWORD_QUALITY_NUMERIC = 0x20000
PASSWORD_QUALITY_NUMERIC_COMPLEX = 0x30000
PASSWORD_QUALITY_ALPHABETIC = 0x40000
PASSWORD_QUALITY_ALPHANUMERIC = 0x50000
PASSWORD_QUALITY_COMPLEX = 0x60000
PASSWORD_QUALITY_MANAGED = 0x80000
# --- Encryption status ---
ENCRYPTION_STATUS_UNSUPPORTED = 0
ENCRYPTION_STATUS_INACTIVE = 1
ENCRYPTION_STATUS_ACTIVATING = 2
ENCRYPTION_STATUS_ACTIVE = 3
ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY = 4
# --- Keyguard disable flags ---
KEYGUARD_DISABLE_FEATURES_NONE = 0
KEYGUARD_DISABLE_FEATURES_ALL = 0x7FFFFFFF
KEYGUARD_DISABLE_SECURE_CAMERA = 2
KEYGUARD_DISABLE_SECURE_NOTIFICATIONS = 4
KEYGUARD_DISABLE_TRUST_AGENTS = 8
KEYGUARD_DISABLE_FINGERPRINT = 16
KEYGUARD_DISABLE_REMOTE_INPUT = 32
KEYGUARD_DISABLE_FACE = 64
KEYGUARD_DISABLE_IRIS = 128
KEYGUARD_DISABLE_PASSCODE = 256
# --- Lock screen info flags ---
FLAG_EVOLVING_DEVICE_ADMIN = 1 << 0
FLAG_PARENT_CAN_ACCESS_LOCK_SCREEN = 1 << 1
FLAG_SELF_PROFILE_DISABLED = 1 << 2
# --- Other constants ---
PERMISSION_GRANT_STATE_DEFAULT = 0
PERMISSION_GRANT_STATE_GRANTED = 1
PERMISSION_GRANT_STATE_DENIED = 2
STATE_USER_SETUP_COMPLETE = 1
STATE_USER_UNMANAGED = 0
STATE_USER_MANAGED = 2
LEAVE_ALL_KEYS = 0xFFFFFFFF
WIPE_EXTERNAL_STORAGE = 1
WIPE_RESET_PROTECTION_DATA = 2
WIPE_SILENTLY = 4
SKIP_SETUP_WIZARD = 1
SETTINGS_HOME = "settings_home"
SETTINGS_GLOBAL = "settings_global"
SETTINGS_SECURE = "settings_secure"
SETTINGS_SYSTEM = "settings_system"
LOCK_TASK_FEATURE_NONE = 0
LOCK_TASK_FEATURE_SYSTEM_INFO = 1
LOCK_TASK_FEATURE_KEYGUARD = 2
LOCK_TASK_FEATURE_HOME = 4
LOCK_TASK_FEATURE_GLOBAL_ACTIONS = 8
LOCK_TASK_FEATURE_NOTIFICATIONS = 16
LOCK_TASK_FEATURE_OVERVIEW = 32
LOCK_TASK_FEATURE_CREDENTIALS = 64
LOCK_TASK_FEATURE_ACCESSIBILITY = 128
LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START = 256
def __init__(self):
super().__init__()
self._active_admins: List[Dict[str, Any]] = []
self._password_quality = self.PASSWORD_QUALITY_UNSPECIFIED
self._camera_disabled = False
self._keyguard_disabled = False
def isAdminActive(self, admin: "DeviceAdminInfo") -> bool:
"""检查设备管理器是否已激活"""
return any(a.getComponent() == admin.getComponent() for a in self._active_admins)
def isDeviceOwnerApp(self, packageName: str) -> bool:
"""检查应用是否为设备所有者"""
return False
def isProfileOwnerApp(self, packageName: str) -> bool:
"""检查应用是否为配置文件所有者"""
return False
def resetPassword(self, password: str, flags: int = 0) -> bool:
"""重置设备密码 (桌面存根)"""
raise AndroidOnlyError("DevicePolicyManager.resetPassword() 仅在 Android 设备上可用")
def lockNow(self):
"""立即锁定设备"""
raise AndroidOnlyError("DevicePolicyManager.lockNow() 仅在 Android 设备上可用")
def wipeData(self, flags: int = 0):
"""擦除设备数据"""
raise AndroidOnlyError("DevicePolicyManager.wipeData() 仅在 Android 设备上可用")
def setCameraDisabled(self, admin: "DeviceAdminInfo", disabled: bool):
"""设置相机禁用状态"""
self._camera_disabled = disabled
def setKeyguardDisabled(self, admin: "DeviceAdminInfo", disabled: bool) -> bool:
"""设置锁屏禁用状态"""
self._keyguard_disabled = disabled
return True
def installCaCert(self, admin: "DeviceAdminInfo", certBuffer: bytes) -> bool:
"""安装 CA 证书 (桌面存根)"""
raise AndroidOnlyError("DevicePolicyManager.installCaCert() 仅在 Android 设备上可用")
def uninstallCaCert(self, admin: "DeviceAdminInfo", certBuffer: bytes):
"""卸载 CA 证书 (桌面存根)"""
raise AndroidOnlyError("DevicePolicyManager.uninstallCaCert() 仅在 Android 设备上可用")
def getActiveAdmins(self) -> Optional[List["DeviceAdminInfo"]]:
"""获取活跃的管理器列表"""
return list(self._active_admins) if self._active_admins else None
def createAdminProfileInfo(
self,
packageName: str,
localizedName: str,
parentComponent: Any = None,
adminInfo: Any = None,
profileOwner: bool = False,
) -> "DeviceAdminInfo":
"""创建管理员配置文件信息"""
return DeviceAdminInfo(
packageName=packageName,
localizedName=localizedName,
parentComponent=parentComponent,
adminInfo=adminInfo,
profileOwner=profileOwner,
)
def toString(self) -> str:
return f"DevicePolicyManager@active_admins={len(self._active_admins)}"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# DeviceAdminInfo
# ═══════════════════════════════════════════════════════════════
class DeviceAdminInfo(Stub):
"""DeviceAdminInfo — 设备管理员信息"""
USES_POLICY_WATCH_LOG = "uses_policy_watch_log"
USES_POLICY_DISABLE_KEYGUARD_FEATURES = "uses_policy_disable_keyguard_features"
USES_POLICY_DISABLE_CAMERA = "uses_policy_disable_camera"
USES_POLICY_RESET_PASSWORD = "uses_policy_reset_password"
USES_POLICY_FORCE_LOCK = "uses_policy_force_lock"
USES_POLICY_WIPE_DATA = "uses_policy_wipe_data"
USES_POLICY_EXPIRE_PASSWORD = "uses_policy_expire_password"
USES_POLICY_LIMIT_PASSWORD = "uses_policy_limit_password"
USES_ENCRYPTED_STORAGE = "uses_encrypted_storage"
USES_POLICY_SETS_DEVICE_OWNER = "uses_policy_sets_device_owner"
USES_POLICY_SETS_PROFILE_OWNER = "uses_policy_sets_profile_owner"
USES_POLICY_INSTALL_CERT = "uses_policy_install_cert"
USES_POLICY_UNINSTALL_CERT = "uses_policy_uninstall_cert"
def __init__(
self,
packageName: str = "",
localizedName: str = "",
parentComponent: Any = None,
adminInfo: Any = None,
profileOwner: bool = False,
):
super().__init__()
self._packageName = packageName
self._localizedName = localizedName
self._parentComponent = parentComponent
self._adminInfo = adminInfo
self._profileOwner = profileOwner
def getComponent(self) -> Optional[str]:
"""获取组件标识"""
return self._parentComponent
def getPackageName(self) -> str:
return self._packageName
def isVisible(self) -> bool:
return True
def isProfileOwner(self) -> bool:
return self._profileOwner
def toString(self) -> str:
return f"DeviceAdminInfo(package={self._packageName})"
def equals(self, other) -> bool:
if not isinstance(other, DeviceAdminInfo):
return False
return self._packageName == other._packageName and self._parentComponent == other._parentComponent
def hashCode(self) -> int:
return hash(self._packageName) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# DeviceAdminReceiver
# ═══════════════════════════════════════════════════════════════
class DeviceAdminReceiver(Stub):
"""DeviceAdminReceiver — 设备管理员广播接收器"""
ACTION_DEVICE_ADMIN_ENABLED = "android.app.action.DEVICE_ADMIN_ENABLED"
ACTION_DEVICE_ADMIN_DISABLED = "android.app.action.DEVICE_ADMIN_DISABLED"
ACTION_DEVICE_ADMIN_DISABLE_REQUESTED = "android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED"
ACTION_PASSWORD_CHANGED = "android.app.action.ACTION_PASSWORD_CHANGED"
ACTION_PASSWORD_FAILED = "android.app.action.ACTION_PASSWORD_FAILED"
ACTION_PASSWORD_SUCCEEDED = "android.app.action.ACTION_PASSWORD_SUCCEEDED"
ACTION_PASSWORD_EXPIRING = "android.app.action.ACTION_PASSWORD_EXPIRING"
ACTION_PROFILE_PROVISIONING_COMPLETE = "android.app.action.PROFILE_PROVISIONING_COMPLETE"
ACTION_BUGREPORT_SHARING_ACCEPTED = "android.app.action.BUGREPORT_SHARING_ACCEPTED"
ACTION_BUGREPORT_SHARING_DECLINED = "android.app.action.BUGREPORT_SHARING_DECLINED"
ACTION_BUGREPORT_FAILED = "android.app.action.BUGREPORT_FAILED"
ACTION_NOTIFY_PENDING_SYSTEM_UPDATE = "android.app.action.NOTIFY_PENDING_SYSTEM_UPDATE"
ACTION_CHOOSE_PRIVATE_KEY_ALIAS = "android.app.action.CHOOSE_PRIVATE_KEY_ALIAS"
ACTION_NETWORK_LOGS_AVAILABLE = "android.app.action.NETWORK_LOGS_AVAILABLE"
ACTION_SECURITY_LOGS_AVAILABLE = "android.app.action.SECURITY_LOGS_AVAILABLE"
ACTION_USER_ADDED = "android.app.action.USER_ADDED"
ACTION_USER_REMOVED = "android.app.action.USER_REMOVED"
ACTION_USER_STARTED = "android.app.action.USER_STARTED"
ACTION_USER_STOPPED = "android.app.action.USER_STOPPED"
ACTION_USER_SWITCHED = "android.app.action.USER_SWITCHED"
ACTION_LOCK_TASK_ENTERING = "android.app.action.LOCK_TASK_ENTERING"
ACTION_LOCK_TASK_EXITING = "android.app.action.LOCK_TASK_EXITING"
def __init__(self):
super().__init__()
def onEnabled(self, context: Any, intent: Any):
"""管理员启用回调"""
pass
def onDisabled(self, context: Any, intent: Any):
"""管理员禁用回调"""
pass
def onPasswordChanged(self, context: Any, intent: Any):
"""密码更改回调"""
pass
def onPasswordExpiring(self, context: Any, intent: Any):
"""密码过期回调"""
pass
def onPasswordFailed(self, context: Any, intent: Any):
"""密码失败回调"""
pass
def onPasswordSucceeded(self, context: Any, intent: Any):
"""密码成功回调"""
pass
def onProfileProvisioningComplete(self, context: Any, intent: Any):
"""配置文件配置完成回调"""
pass
def onBugreportSharingAccepted(self, context: Any, intent: Any, bugreportHash: bytes):
"""错误报告分享接受回调"""
pass
def onBugreportSharingDeclined(self, context: Any, intent: Any):
"""错误报告分享拒绝回调"""
pass
def onBugreportFailed(self, context: Any, intent: Any, bugreportHash: bytes):
"""错误报告失败回调"""
pass
def onNotifyPendingSystemUpdate(self, context: Any, intent: Any, updateReceivedTime: int):
"""系统更新待处理通知回调"""
pass
def onNetworkLogsAvailable(self, context: Any, intent: Any, batchToken: int, networkLogsCount: int):
"""网络日志可用回调"""
pass
def onSecurityLogsAvailable(self, context: Any, intent: Any):
"""安全日志可用回调"""
pass
def onUserAdded(self, context: Any, intent: Any, newUserHandle: Any):
"""用户添加回调"""
pass
def onUserRemoved(self, context: Any, intent: Any, removedUserHandle: Any):
"""用户移除回调"""
pass
def onUserStarted(self, context: Any, intent: Any, userHandle: Any):
"""用户启动回调"""
pass
def onUserStopped(self, context: Any, intent: Any, userHandle: Any):
"""用户停止回调"""
pass
def onUserSwitched(self, context: Any, intent: Any, switchedUserHandle: Any):
"""用户切换回调"""
pass
def onLockTaskModeEntering(self, context: Any, intent: Any, pkg: str):
"""锁任务模式进入回调"""
pass
def onLockTaskModeExiting(self, context: Any, intent: Any):
"""锁任务模式退出回调"""
pass
def toString(self) -> str:
return f"DeviceAdminReceiver@{hex(id(self))}"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# SystemUpdatePolicy
# ═══════════════════════════════════════════════════════════════
class SystemUpdatePolicy(Stub):
"""SystemUpdatePolicy — 系统更新策略"""
TYPE_INSTALL_AUTOMATIC = 1
TYPE_INSTALL_WINDOWED = 2
TYPE_POSTPONE = 3
def __init__(self, policyType: int = TYPE_INSTALL_AUTOMATIC):
super().__init__()
self._policyType = policyType
self._startTime: Optional[str] = None
self._endTime: Optional[str] = None
def getPolicyType(self) -> int:
return self._policyType
def setPolicyType(self, policyType: int):
self._policyType = policyType
def setInstallWindow(self, startTime: str, endTime: str):
"""设置安装窗口时间段 (格式 HH:mm)"""
self._startTime = startTime
self._endTime = endTime
def getStartTime(self) -> Optional[str]:
return self._startTime
def getEndTime(self) -> Optional[str]:
return self._endTime
@staticmethod
def createPolicy(policyType: int) -> "SystemUpdatePolicy":
return SystemUpdatePolicy(policyType=policyType)
def toString(self) -> str:
return f"SystemUpdatePolicy(type={self._policyType})"
def equals(self, other) -> bool:
if not isinstance(other, SystemUpdatePolicy):
return False
return self._policyType == other._policyType
def hashCode(self) -> int:
return self._policyType & 0x7FFFFFFF
+477
View File
@@ -0,0 +1,477 @@
"""
Android Backup API — BackupManager, BackupAgent, BackupDataInput/Output, RestoreObserver 等
方法签名完全对齐 Android SDK
"""
import json
import os as _os
import sys
import tempfile
import shutil
import time
from datetime import datetime
from typing import Any, Dict, List, Optional, Set, Tuple, Callable, BinaryIO
from .._core import Stub, AndroidOnlyError, resolve_android_path, _app_data_dir
from ..java.lang import RuntimeException
IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {})
__all__ = [
"BackupManager",
"BackupAgent",
"BackupDataInput",
"BackupDataOutput",
"FullBackupDataOutput",
"BackupHelper",
"FileBackupHelper",
"SharedPreferencesBackupHelper",
"RestoreObserver",
"BackupDataInputStream",
]
# ═══════════════════════════════════════════════════════════════
# 备份存储路径
# ═══════════════════════════════════════════════════════════════
_BACKUP_DIR = _os.path.join(_app_data_dir, "backups")
def _ensure_backup_dir():
_os.makedirs(_BACKUP_DIR, exist_ok=True)
_ensure_backup_dir()
# ═══════════════════════════════════════════════════════════════
# BackupManager
# ═══════════════════════════════════════════════════════════════
class BackupManager(Stub):
"""BackupManager — 备份管理器 (桌面文件备份模拟)"""
def __init__(self, context: Any = None):
super().__init__()
self._context = context
self._backup_enabled = True
def dataChanged(self, packageName: str = None):
"""通知系统数据已更改,需要备份"""
if packageName:
flag_path = _os.path.join(_BACKUP_DIR, f"{packageName}.pending")
elif self._context:
flag_path = _os.path.join(_BACKUP_DIR, f"{self._context.getPackageName()}.pending")
else:
flag_path = _os.path.join(_BACKUP_DIR, "default.pending")
with open(flag_path, "w") as f:
f.write(time.time())
def requestRestore(self, observer: "RestoreObserver") -> int:
"""请求恢复备份数据"""
raise AndroidOnlyError("BackupManager.requestRestore() 仅在 Android 设备上可用")
def getCurrentTransport(self) -> Optional[str]:
"""获取当前备份传输名称"""
return "com.android.localtransport.LocalTransport"
def listAllTransports(self) -> List[str]:
"""列出所有可用备份传输"""
return ["com.android.localtransport.LocalTransport"]
def isBackupEnabled(self) -> bool:
"""检查备份是否启用"""
return self._backup_enabled
def setBackupEnabled(self, isEnabled: bool):
"""设置备份启用状态"""
self._backup_enabled = isEnabled
def toString(self) -> str:
return f"BackupManager(backup_enabled={self._backup_enabled})"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# BackupAgent
# ═══════════════════════════════════════════════════════════════
class BackupAgent(Stub):
"""BackupAgent — 备份代理基类"""
def __init__(self):
super().__init__()
def onCreate(self):
"""创建备份代理时调用"""
pass
def onBackup(
self,
oldState: "BackupDataInput",
data: "BackupDataOutput",
newState: "BackupDataOutput",
):
"""执行备份操作"""
pass
def onRestore(
self,
data: "BackupDataInput",
appVersionCode: int,
newState: "BackupDataOutput",
):
"""执行恢复操作"""
pass
def toString(self) -> str:
return f"BackupAgent@{hex(id(self))}"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# BackupDataInput
# ═══════════════════════════════════════════════════════════════
class BackupDataInput(Stub):
"""BackupDataInput — 备份数据输入流"""
def __init__(self, filepath: str = ""):
super().__init__()
self._filepath = filepath
self._data: Dict[str, Any] = {}
self._keys: List[str] = []
self._index = 0
if filepath and _os.path.exists(filepath):
with open(filepath, "r") as f:
self._data = json.load(f)
self._keys = list(self._data.keys())
def readNextHeader(self) -> bool:
"""读取下一个数据头"""
if self._index < len(self._keys):
return True
return False
def getKey(self) -> str:
"""获取当前键名"""
if self._index < len(self._keys):
return self._keys[self._index]
return ""
def getDataSize(self) -> int:
"""获取当前数据大小"""
if self._index < len(self._keys):
val = self._data[self._keys[self._index]]
return len(str(val))
return 0
def readEntityData(self, data: bytearray, offset: int, size: int) -> int:
"""读取实体数据"""
if self._index < len(self._keys):
val = str(self._data[self._keys[self._index]])
raw = val.encode("utf-8")
copy_len = min(size, len(raw) - offset)
for i in range(copy_len):
data[offset + i] = raw[i]
self._index += 1
return copy_len
return -1
def skipEntityData(self):
"""跳过当前实体数据"""
self._index += 1
def toString(self) -> str:
return f"BackupDataInput(file={self._filepath})"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# BackupDataOutput
# ═══════════════════════════════════════════════════════════════
class BackupDataOutput(Stub):
"""BackupDataOutput — 备份数据输出流"""
def __init__(self, filepath: str = ""):
super().__init__()
self._filepath = filepath
self._data: Dict[str, str] = {}
def writeEntityHeader(self, key: str, dataSize: int):
"""写入实体数据头"""
pass
def writeEntityData(self, data: bytes, size: int):
"""写入实体数据"""
key = f"entity_{len(self._data)}"
self._data[key] = data[:size].decode("utf-8", errors="replace")
if self._filepath:
_os.makedirs(_os.path.dirname(self._filepath), exist_ok=True)
with open(self._filepath, "w") as f:
json.dump(self._data, f)
def toString(self) -> str:
return f"BackupDataOutput(file={self._filepath})"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# FullBackupDataOutput
# ═══════════════════════════════════════════════════════════════
class FullBackupDataOutput(Stub):
"""FullBackupDataOutput — 完整备份数据输出"""
def __init__(self, quota: int = 0, transportFlags: int = 0):
super().__init__()
self._quota = quota
self._transportFlags = transportFlags
def getQuota(self) -> int:
return self._quota
def getTransportFlags(self) -> int:
return self._transportFlags
def toString(self) -> str:
return f"FullBackupDataOutput(quota={self._quota})"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# BackupHelper
# ═══════════════════════════════════════════════════════════════
class BackupHelper(Stub):
"""BackupHelper — 备份辅助接口"""
def __init__(self):
super().__init__()
def performBackup(
self,
oldState: "BackupDataInput",
data: "BackupDataOutput",
newState: "BackupDataOutput",
):
"""执行备份"""
raise AndroidOnlyError("BackupHelper.performBackup() 仅在 Android 设备上可用")
def restoreEntity(self, data: "BackupDataInput", appVersionCode: int):
"""恢复实体"""
raise AndroidOnlyError("BackupHelper.restoreEntity() 仅在 Android 设备上可用")
def toString(self) -> str:
return f"BackupHelper@{hex(id(self))}"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# FileBackupHelper
# ═══════════════════════════════════════════════════════════════
class FileBackupHelper(BackupHelper):
"""FileBackupHelper — 文件备份辅助"""
def __init__(self, context: Any, files: List[str]):
super().__init__()
self._context = context
self._files = list(files)
def performBackup(
self,
oldState: "BackupDataInput",
data: "BackupDataOutput",
newState: "BackupDataOutput",
):
"""执行文件备份"""
for fname in self._files:
if self._context:
filepath = _os.path.join(self._context.getFilesDir(), fname)
if _os.path.exists(filepath):
with open(filepath, "rb") as f:
content = f.read()
data.writeEntityHeader(fname, len(content))
data.writeEntityData(content, len(content))
def restoreEntity(self, data: "BackupDataInput", appVersionCode: int):
"""恢复文件实体"""
key = data.getKey()
if self._context and key in self._files:
filepath = _os.path.join(self._context.getFilesDir(), key)
size = data.getDataSize()
buf = bytearray(size)
data.readEntityData(buf, 0, size)
_os.makedirs(_os.path.dirname(filepath), exist_ok=True)
with open(filepath, "wb") as f:
f.write(buf)
def toString(self) -> str:
return f"FileBackupHelper(files={self._files})"
def equals(self, other) -> bool:
if not isinstance(other, FileBackupHelper):
return False
return self._files == other._files
def hashCode(self) -> int:
return hash(tuple(self._files)) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# SharedPreferencesBackupHelper
# ═══════════════════════════════════════════════════════════════
class SharedPreferencesBackupHelper(BackupHelper):
"""SharedPreferencesBackupHelper — SharedPreferences 备份辅助"""
def __init__(self, context: Any, prefNames: List[str]):
super().__init__()
self._context = context
self._prefNames = list(prefNames)
def performBackup(
self,
oldState: "BackupDataInput",
data: "BackupDataOutput",
newState: "BackupDataOutput",
):
"""执行 SharedPreferences 备份"""
if not self._context:
return
prefs_dir = _os.path.join(_app_data_dir, "shared_prefs")
for name in self._prefNames:
filepath = _os.path.join(prefs_dir, f"{name}.xml")
if _os.path.exists(filepath):
with open(filepath, "rb") as f:
content = f.read()
data.writeEntityHeader(name, len(content))
data.writeEntityData(content, len(content))
def restoreEntity(self, data: "BackupDataInput", appVersionCode: int):
"""恢复 SharedPreferences"""
key = data.getKey()
if self._context and key in self._prefNames:
prefs_dir = _os.path.join(_app_data_dir, "shared_prefs")
filepath = _os.path.join(prefs_dir, f"{key}.xml")
size = data.getDataSize()
buf = bytearray(size)
data.readEntityData(buf, 0, size)
_os.makedirs(prefs_dir, exist_ok=True)
with open(filepath, "wb") as f:
f.write(buf)
def toString(self) -> str:
return f"SharedPreferencesBackupHelper(prefs={self._prefNames})"
def equals(self, other) -> bool:
if not isinstance(other, SharedPreferencesBackupHelper):
return False
return self._prefNames == other._prefNames
def hashCode(self) -> int:
return hash(tuple(self._prefNames)) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# RestoreObserver
# ═══════════════════════════════════════════════════════════════
class RestoreObserver(Stub):
"""RestoreObserver — 恢复过程观察者"""
def __init__(self):
super().__init__()
def onUpdate(self, nowBeingRestored: int, currentPackage: str):
"""恢复进度更新回调"""
pass
def onRestoreStarting(self, numPackages: int):
"""恢复开始回调"""
pass
def onRestoreFinished(self, error: int):
"""恢复完成回调"""
pass
def toString(self) -> str:
return f"RestoreObserver@{hex(id(self))}"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# BackupDataInputStream
# ═══════════════════════════════════════════════════════════════
class BackupDataInputStream(Stub):
"""BackupDataInputStream — 备份数据输入流包装器"""
def __init__(self, dataInput: "BackupDataInput" = None):
super().__init__()
self._input = dataInput
self._pos = 0
self._size = 0
def read(self) -> int:
"""读取一个字节"""
if self._input and self._pos < self._size:
self._pos += 1
return 0
return -1
def readFully(self, data: bytearray, offset: int, length: int):
"""读取完整数据块"""
if self._input:
self._input.readEntityData(data, offset, length)
def size(self) -> int:
return self._size
def toString(self) -> str:
return f"BackupDataInputStream(size={self._size})"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
+444
View File
@@ -0,0 +1,444 @@
"""
Android Job Scheduler API — JobScheduler, JobInfo, JobService, JobParameters, JobWorkItem
方法签名完全对齐 Android SDK
"""
import heapq
import threading
import time
from datetime import datetime, timedelta
from typing import Any, Dict, List, Optional, Set, Tuple, Callable
from .._core import Stub, AndroidOnlyError
from ..java.lang import RuntimeException
IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {})
__all__ = [
"JobScheduler",
"JobInfo",
"JobService",
"JobParameters",
"JobWorkItem",
]
# ═══════════════════════════════════════════════════════════════
# JobScheduler
# ═══════════════════════════════════════════════════════════════
class JobScheduler(Stub):
"""JobScheduler — 作业调度器 (桌面线程模拟)"""
def __init__(self):
super().__init__()
self._lock = threading.Lock()
self._jobs: Dict[int, "JobInfo"] = {}
self._next_id = 1
self._worker_thread: Optional[threading.Thread] = None
self._running = False
self._start_worker()
def _start_worker(self):
"""启动后台工作线程"""
if self._worker_thread is None or not self._worker_thread.is_alive():
self._running = True
self._worker_thread = threading.Thread(target=self._run_loop, daemon=True)
self._worker_thread.start()
def _run_loop(self):
"""运行作业调度循环"""
while self._running:
with self._lock:
now = time.time()
for job_id, info in list(self._jobs.items()):
if info._deadline and now >= info._deadline:
self._jobs.pop(job_id, None)
time.sleep(1.0)
def schedule(self, job: "JobInfo") -> int:
"""调度一个作业"""
with self._lock:
job_id = self._next_id
self._next_id += 1
job._id = job_id
if job._periodic:
job._deadline = time.time() + job._period_ms / 1000.0
elif job._deadline_ms:
job._deadline = time.time() + job._deadline_ms / 1000.0
else:
job._deadline = None
self._jobs[job_id] = job
return job_id
def enqueue(self, job: "JobInfo", workItem: "JobWorkItem") -> int:
"""将作业加入队列"""
return self.schedule(job)
def cancel(self, jobId: int):
"""取消指定作业"""
with self._lock:
self._jobs.pop(jobId, None)
def cancelAll(self):
"""取消所有作业"""
with self._lock:
self._jobs.clear()
def getAllPendingJobs(self) -> List["JobInfo"]:
"""获取所有待处理作业"""
with self._lock:
return list(self._jobs.values())
def getPendingJob(self, jobId: int) -> Optional["JobInfo"]:
"""获取指定待处理作业"""
with self._lock:
return self._jobs.get(jobId)
def getTransientNetwork(self) -> Optional[Any]:
"""获取瞬时网络"""
return None
def getStartedJobs(self) -> List["JobInfo"]:
"""获取已开始的作业"""
return []
def toString(self) -> str:
with self._lock:
return f"JobScheduler(pending={len(self._jobs)})"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# JobInfo
# ═══════════════════════════════════════════════════════════════
class JobInfo(Stub):
"""JobInfo — 作业信息"""
# --- NetworkType 常量 ---
NETWORK_TYPE_NONE = 0
NETWORK_TYPE_ANY = 1
NETWORK_TYPE_UNMETERED = 2
NETWORK_TYPE_NOT_ROAMING = 3
NETWORK_TYPE_CELLULAR = 4
# --- Backoff 策略 ---
BACKOFF_POLICY_LINEAR = 0
BACKOFF_POLICY_EXPONENTIAL = 1
def __init__(self):
super().__init__()
self._id = 0
self._service = None
self._required_network_type = self.NETWORK_TYPE_NONE
self._requires_charging = False
self._requires_device_idle = False
self._deadline_ms = 0
self._min_latency_ms = 0
self._period_ms = 0
self._periodic = False
self._backoff_policy = self.BACKOFF_POLICY_EXPONENTIAL
self._backoff_delay_ms = 30000
self._extras = None
self._transient_network = False
self._estimated_network_bytes = -1
self._prefetch_network_bytes = -1
self._requires_storage_not_low = False
def getId(self) -> int:
return self._id
def getService(self) -> Optional[Any]:
return self._service
def getRequiredNetworkType(self) -> int:
return self._required_network_type
def isRequiresCharging(self) -> bool:
return self._requires_charging
def isRequiresDeviceIdle(self) -> bool:
return self._requires_device_idle
def isRequiresStorageNotLow(self) -> bool:
return self._requires_storage_not_low
def getMinLatencyMillis(self) -> int:
return self._min_latency_ms
def getMaxExecutionDelayMillis(self) -> int:
return self._deadline_ms
def isPeriodic(self) -> bool:
return self._periodic
def getBackoffPolicy(self) -> int:
return self._backoff_policy
def getInitialBackoffMillis(self) -> int:
return self._backoff_delay_ms
def getEstimatedNetworkBytes(self) -> int:
return self._estimated_network_bytes
def getPrefetchNetworkBytes(self) -> int:
return self._prefetch_network_bytes
def getTransientNetwork(self) -> Optional[Any]:
return None
def isPersisted(self) -> bool:
return False
def getExtras(self) -> Optional[Any]:
return self._extras
# ═══════════════════════════════════════════════════════════
# Builder
# ═══════════════════════════════════════════════════════════
class Builder(Stub):
"""JobInfo.Builder — 作业构建器"""
def __init__(self, jobId: int, jobService: Any):
super().__init__()
self._info = JobInfo()
self._info._id = jobId
self._info._service = jobService
def setRequiredNetworkType(self, networkType: int) -> "JobInfo.Builder":
self._info._required_network_type = networkType
return self
def setRequiresCharging(self, requiresCharging: bool) -> "JobInfo.Builder":
self._info._requires_charging = requiresCharging
return self
def setRequiresDeviceIdle(self, requiresDeviceIdle: bool) -> "JobInfo.Builder":
self._info._requires_device_idle = requiresDeviceIdle
return self
def setRequiresStorageNotLow(self, requiresStorageNotLow: bool) -> "JobInfo.Builder":
self._info._requires_storage_not_low = requiresStorageNotLow
return self
def setOverrideDeadline(self, deadlineMillis: int) -> "JobInfo.Builder":
self._info._deadline_ms = deadlineMillis
return self
def setMinimumLatency(self, minLatencyMillis: int) -> "JobInfo.Builder":
self._info._min_latency_ms = minLatencyMillis
return self
def setBackoffCriteria(
self, initialBackoffMillis: int, backoffPolicy: int
) -> "JobInfo.Builder":
self._info._backoff_delay_ms = initialBackoffMillis
self._info._backoff_policy = backoffPolicy
return self
def setPeriodic(self, intervalMillis: int) -> "JobInfo.Builder":
self._info._periodic = True
self._info._period_ms = intervalMillis
return self
def setTransientNetwork(self, transient: bool) -> "JobInfo.Builder":
self._info._transient_network = transient
return self
def setEstimatedNetworkBytes(self, estimatedBytes: int) -> "JobInfo.Builder":
self._info._estimated_network_bytes = estimatedBytes
return self
def setPrefetchNetworkBytes(self, prefetchBytes: int) -> "JobInfo.Builder":
self._info._prefetch_network_bytes = prefetchBytes
return self
def setExtras(self, extras: Any) -> "JobInfo.Builder":
self._info._extras = extras
return self
def build(self) -> "JobInfo":
return self._info
def toString(self) -> str:
return f"Builder(jobId={self._info._id})"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
def toString(self) -> str:
return f"JobInfo(id={self._id}, network={self._required_network_type})"
def equals(self, other) -> bool:
if not isinstance(other, JobInfo):
return False
return self._id == other._id
def hashCode(self) -> int:
return self._id & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# JobService
# ═══════════════════════════════════════════════════════════════
class JobService(Stub):
"""JobService — 作业服务基类"""
def __init__(self):
super().__init__()
def onStartJob(self, params: "JobParameters") -> bool:
"""作业开始时调用。返回 True 表示需要后台线程处理。"""
return False
def onStopJob(self, params: "JobParameters") -> bool:
"""作业停止时调用。返回 True 表示应重试。"""
return False
def jobFinished(self, params: "JobParameters", needsReschedule: bool):
"""通知系统作业已完成"""
pass
def toString(self) -> str:
return f"JobService@{hex(id(self))}"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# JobParameters
# ═══════════════════════════════════════════════════════════════
class JobParameters(Stub):
"""JobParameters — 作业参数"""
# --- StopReason 常量 ---
STOP_REASON_UNDEFINED = 0
STOP_REASON_DATA_CLEARED = 1
STOP_REASON_APP_BACKGROUND = 2
STOP_REASON_APP_STANDBY = 3
STOP_REASON_DEVICE_IDLE = 4
STOP_REASON_PREEXISTING = 5
STOP_REASON_ESTIMATED_NETWORK_BYTES_EXCEEDED = 6
STOP_REASON_QUOTA = 7
STOP_REASON_TAPPED_CANCEL = 8
STOP_REASON_USER = 9
STOP_REASON_CONSTRAINT_BATTERY_NOT_OK = 10
STOP_REASON_CONSTRAINT_NETWORK_NOT_OK = 11
STOP_REASON_CONSTRAINT_CONNECTIVITY_NOT_OK = 12
STOP_REASON_CONSTRAINT_STORAGE_NOT_OK = 13
STOP_REASON_CONSTRAINT_CHARGING_NOT_OK = 14
STOP_REASON_CONSTRAINT_IDLE_NOT_OK = 15
STOP_REASON_CANCELED = 16
STOP_REASON_CANCELLED_BY_APP = 17
STOP_REASON_DEVICE_STATE = 18
STOP_REASON_SYSTEM_PROCESSING = 19
STOP_REASON_WORK_POLICY = 20
STOP_REASON_SYSTEM_EXECUTION = 21
STOP_REASON_RESTRICTED_BACKGROUND = 22
STOP_REASON_TIMEOUT = 23
def __init__(self, jobId: int = 0):
super().__init__()
self._job_id = jobId
self._extras = None
self._network = None
self._transient_network = None
self._stop_reason = 0
self._override_deadline_expired = False
self._requires_charging = False
self._requires_device_idle = False
self._requires_storage_not_low = False
self._required_network_type = JobInfo.NETWORK_TYPE_NONE
def getJobId(self) -> int:
return self._job_id
def getExtras(self) -> Optional[Any]:
return self._extras
def getNetwork(self) -> Optional[Any]:
return self._network
def getTransientNetwork(self) -> Optional[Any]:
return self._transient_network
def getStopReason(self) -> int:
return self._stop_reason
def isOverrideDeadlineExpired(self) -> bool:
return self._override_deadline_expired
def isRequiresCharging(self) -> bool:
return self._requires_charging
def isRequiresDeviceIdle(self) -> bool:
return self._requires_device_idle
def isRequiresStorageNotLow(self) -> bool:
return self._requires_storage_not_low
def getRequiredNetworkType(self) -> int:
return self._required_network_type
def toString(self) -> str:
return f"JobParameters(jobId={self._job_id})"
def equals(self, other) -> bool:
if not isinstance(other, JobParameters):
return False
return self._job_id == other._job_id
def hashCode(self) -> int:
return self._job_id & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# JobWorkItem
# ═══════════════════════════════════════════════════════════════
class JobWorkItem(Stub):
"""JobWorkItem — 作业工作项"""
def __init__(self, intent: Any = None):
super().__init__()
self._intent = intent
self._delivery_count = 0
self._latest_delivery_intent = None
def getIntent(self) -> Optional[Any]:
return self._intent
def getDeliveryCount(self) -> int:
return self._delivery_count
def getLatestDeliveryIntent(self) -> Optional[Any]:
return self._latest_delivery_intent
def toString(self) -> str:
return f"JobWorkItem(deliveryCount={self._delivery_count})"
def equals(self, other) -> bool:
if not isinstance(other, JobWorkItem):
return False
return self._intent == other._intent
def hashCode(self) -> int:
return hash(str(self._intent)) & 0x7FFFFFFF
+268
View File
@@ -0,0 +1,268 @@
"""
Android People API — PeopleManager, ConversationStatus, PeopleManager.Person, PeopleManager.Conversation
方法签名完全对齐 Android SDK
"""
import sys
from typing import Any, Dict, List, Optional, Set, Tuple, Callable
from .._core import Stub, AndroidOnlyError
from ..java.lang import RuntimeException
IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {})
__all__ = [
"PeopleManager",
"ConversationStatus",
]
# ═══════════════════════════════════════════════════════════════
# PeopleManager
# ═══════════════════════════════════════════════════════════════
class PeopleManager(Stub):
"""PeopleManager — 人物管理器 (桌面存根)"""
ACTION_PEOPLE_CONVERSATION = "android.app.people.action.PEOPLE_CONVERSATION"
ACTION_REMOVE_STATUS = "android.app.people.action.REMOVE_STATUS"
def __init__(self, context: Any = None):
super().__init__()
self._context = context
self._statuses: Dict[str, "ConversationStatus"] = {}
self._people: Dict[str, "PeopleManager.Person"] = {}
self._conversations: Dict[str, "PeopleManager.Conversation"] = {}
self._listeners: List[Callable] = []
def getStatus(self, packageName: str, personUri: str) -> Optional["ConversationStatus"]:
"""获取指定人的状态"""
key = f"{packageName}:{personUri}"
return self._statuses.get(key)
def getDataIdFromPhoneNumber(self, phoneNumber: str) -> Optional[str]:
"""根据电话号码获取数据 ID"""
return None
def addOrUpdateStatus(
self, packageName: str, status: "ConversationStatus"
):
"""添加或更新状态"""
key = f"{packageName}:{status.getPersonUri()}"
self._statuses[key] = status
def removeStatus(self, packageName: str, personUri: str):
"""移除状态"""
key = f"{packageName}:{personUri}"
self._statuses.pop(key, None)
def getStoredPeople(self) -> List["PeopleManager.Person"]:
"""获取已存储的人物列表"""
return list(self._people.values())
def isConversation(self, packageName: str, personUri: str) -> bool:
"""检查是否为对话"""
key = f"{packageName}:{personUri}"
return key in self._conversations
def getConversation(
self, packageName: str, personUri: str
) -> Optional["PeopleManager.Conversation"]:
"""获取对话信息"""
key = f"{packageName}:{personUri}"
return self._conversations.get(key)
# ═══════════════════════════════════════════════════════════
# Status - 状态内部类
# ═══════════════════════════════════════════════════════════
class Status(Stub):
"""PeopleManager.Status — 状态基类"""
def __init__(self):
super().__init__()
def toString(self) -> str:
return f"Status@{hex(id(self))}"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════
# Person - 人物内部类
# ═══════════════════════════════════════════════════════════
class Person(Stub):
"""PeopleManager.Person — 人物信息"""
def __init__(
self,
name: str = "",
key: str = "",
uri: str = "",
importantForPeople: bool = False,
):
super().__init__()
self._name = name
self._key = key
self._uri = uri
self._important_for_people = importantForPeople
def getName(self) -> str:
return self._name
def getKey(self) -> str:
return self._key
def getUri(self) -> str:
return self._uri
def getImportantForPeople(self) -> bool:
return self._important_for_people
def toString(self) -> str:
return f"Person(name={self._name}, key={self._key})"
def equals(self, other) -> bool:
if not isinstance(other, PeopleManager.Person):
return False
return self._key == other._key
def hashCode(self) -> int:
return hash(self._key) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════
# Conversation - 对话内部类
# ═══════════════════════════════════════════════════════════
class Conversation(Stub):
"""PeopleManager.Conversation — 对话信息"""
def __init__(
self,
conv_id: str = "",
person: "PeopleManager.Person" = None,
notificationChannelId: str = "",
shortcutInfo: Any = None,
parentUserHandle: Any = None,
):
super().__init__()
self._id = conv_id
self._person = person
self._notification_channel_id = notificationChannelId
self._shortcut_info = shortcutInfo
self._parent_user_handle = parentUserHandle
def getId(self) -> str:
return self._id
def getPerson(self) -> Optional["PeopleManager.Person"]:
return self._person
def getNotificationChannelId(self) -> str:
return self._notification_channel_id
def getShortcutInfo(self) -> Optional[Any]:
return self._shortcut_info
def getParentUserHandle(self) -> Optional[Any]:
return self._parent_user_handle
def toString(self) -> str:
return f"Conversation(id={self._id})"
def equals(self, other) -> bool:
if not isinstance(other, PeopleManager.Conversation):
return False
return self._id == other._id
def hashCode(self) -> int:
return hash(self._id) & 0x7FFFFFFF
def toString(self) -> str:
return f"PeopleManager@{hex(id(self))}"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# ConversationStatus
# ═══════════════════════════════════════════════════════════════
class ConversationStatus(Stub):
"""ConversationStatus — 对话状态"""
def __init__(
self,
userId: int = 0,
packageName: str = "",
personUri: str = "",
status: int = 0,
statusIcon: int = 0,
notificationKey: str = "",
notificationContentDescription: str = "",
):
super().__init__()
self._user_id = userId
self._package_name = packageName
self._person_uri = personUri
self._status = status
self._status_icon = statusIcon
self._notification_key = notificationKey
self._notification_content_description = notificationContentDescription
self._active = False
self._muted = False
def getUserId(self) -> int:
return self._user_id
def getPackageName(self) -> str:
return self._package_name
def getPersonUri(self) -> str:
return self._person_uri
def getStatus(self) -> int:
return self._status
def getStatusIcon(self) -> int:
return self._status_icon
def getNotificationKey(self) -> str:
return self._notification_key
def getNotificationContentDescription(self) -> str:
return self._notification_content_description
def isActive(self) -> bool:
return self._active
def isMuted(self) -> bool:
return self._muted
def setActive(self, active: bool):
self._active = active
def setMuted(self, muted: bool):
self._muted = muted
def toString(self) -> str:
return (f"ConversationStatus(pkg={self._package_name}, "
f"uri={self._person_uri}, active={self._active})")
def equals(self, other) -> bool:
if not isinstance(other, ConversationStatus):
return False
return (self._package_name == other._package_name
and self._person_uri == other._person_uri)
def hashCode(self) -> int:
return hash((self._package_name, self._person_uri)) & 0x7FFFFFFF
+117
View File
@@ -0,0 +1,117 @@
"""
Android Role API — RoleManager
方法签名完全对齐 Android SDK
"""
import sys
from typing import Any, Dict, List, Optional, Set, Tuple, Callable
from .._core import Stub, AndroidOnlyError
from ..java.lang import RuntimeException
IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {})
__all__ = [
"RoleManager",
]
# ═══════════════════════════════════════════════════════════════
# RoleManager
# ═══════════════════════════════════════════════════════════════
class RoleManager(Stub):
"""RoleManager — 角色管理器 (桌面存根)"""
ACTION_REQUEST_ROLE = "android.app.role.action.REQUEST_ROLE"
# --- 角色常量 ---
ROLE_ASSISTANT = "android.app.role.ASSISTANT"
ROLE_BROWSER = "android.app.role.BROWSER"
ROLE_DIALER = "android.app.role.DIALER"
ROLE_SMS = "android.app.role.SMS"
ROLE_HOME = "android.app.role.HOME"
ROLE_EMERGENCY = "android.app.role.EMERGENCY"
ROLE_CALL_REDIRECTION = "android.app.role.CALL_REDIRECTION"
ROLE_SYSTEM_EXCLUSIVE = "android.app.role.SYSTEM_EXCLUSIVE"
ROLE_WALLPAPER = "android.app.role.WALLPAPER"
ROLE_NOTIFICATION_ACCESS = "android.app.role.NOTIFICATION_ACCESS"
ROLE_FOREGROUND_SERVICE = "android.app.role.FOREGROUND_SERVICE"
def __init__(self, context: Any = None):
super().__init__()
self._context = context
# 模拟桌面角色持有者 (key=角色名, value=持有者包名列表)
self._role_holders: Dict[str, List[str]] = {
self.ROLE_HOME: ["com.example.launcher"],
self.ROLE_BROWSER: ["org.mozilla.firefox"],
self.ROLE_DIALER: ["com.google.android.dialer"],
self.ROLE_SMS: ["com.google.android.messaging"],
}
self._listeners: List[Callable] = []
def isRoleAvailable(self, roleName: str) -> bool:
"""检查角色是否可用"""
available_roles = [
self.ROLE_ASSISTANT, self.ROLE_BROWSER, self.ROLE_DIALER,
self.ROLE_SMS, self.ROLE_HOME, self.ROLE_EMERGENCY,
self.ROLE_CALL_REDIRECTION, self.ROLE_SYSTEM_EXCLUSIVE,
self.ROLE_WALLPAPER, self.ROLE_NOTIFICATION_ACCESS,
self.ROLE_FOREGROUND_SERVICE,
]
return roleName in available_roles
def isRoleHeld(self, roleName: str) -> bool:
"""检查当前应用是否持有指定角色"""
if not self._context:
return False
pkg = self._context.getPackageName() if hasattr(self._context, "getPackageName") else ""
holders = self._role_holders.get(roleName, [])
return pkg in holders
def createRequestRoleIntent(self, roleName: str) -> Any:
"""创建请求角色的 Intent"""
from .app import Intent
intent = Intent(self.ACTION_REQUEST_ROLE)
intent.putExtra("android.app.role.extra.ROLE_NAME", roleName)
return intent
def getRoleHolders(self, roleName: str) -> List[str]:
"""获取角色持有者列表"""
return list(self._role_holders.get(roleName, []))
def addOnRoleHoldersChangedListener(
self, executor: Any, listener: Callable
):
"""添加角色持有者变更监听器"""
if listener not in self._listeners:
self._listeners.append(listener)
def removeOnRoleHoldersChangedListener(self, listener: Callable):
"""移除角色持有者变更监听器"""
if listener in self._listeners:
self._listeners.remove(listener)
def getRoleNamesFromController(self) -> List[str]:
"""获取控制器管理的角色名列表"""
return [
self.ROLE_ASSISTANT, self.ROLE_BROWSER, self.ROLE_DIALER,
self.ROLE_SMS, self.ROLE_HOME, self.ROLE_EMERGENCY,
self.ROLE_CALL_REDIRECTION, self.ROLE_SYSTEM_EXCLUSIVE,
self.ROLE_WALLPAPER, self.ROLE_NOTIFICATION_ACCESS,
self.ROLE_FOREGROUND_SERVICE,
]
def getApplicationVisibleRoleNames(self) -> List[str]:
"""获取应用可见的角色名列表"""
return self.getRoleNamesFromController()
def toString(self) -> str:
return f"RoleManager@{hex(id(self))}"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
+527
View File
@@ -0,0 +1,527 @@
"""
Android Usage Stats API — UsageStatsManager, UsageStats, UsageEvents, ConfigurationStats, StorageStatsManager
方法签名完全对齐 Android SDK
"""
import json
import os as _os
import sys
import time
from datetime import datetime, timedelta
from typing import Any, Dict, List, Optional, Set, Tuple, Callable
from collections import defaultdict
from .._core import Stub, AndroidOnlyError, resolve_android_path, _app_data_dir, _sdcard_root
from ..java.lang import RuntimeException
IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {})
__all__ = [
"UsageStatsManager",
"UsageStats",
"UsageEvents",
"ConfigurationStats",
"CacheUsageStats",
"NetworkUsageStats",
"UsageStatsManagerInternal",
"StorageStatsManager",
"StorageStats",
]
# ═══════════════════════════════════════════════════════════════
# UsageStatsManager
# ═══════════════════════════════════════════════════════════════
class UsageStatsManager(Stub):
"""UsageStatsManager — 使用统计管理器 (桌面模拟)"""
INTERVAL_DAILY = 0
INTERVAL_WEEKLY = 1
INTERVAL_MONTHLY = 2
INTERVAL_YEARLY = 3
INTERVAL_BEST = 4
INTERVAL_COUNT = 5
USAGE_SOURCE_UNSPECIFIED = 0
USAGE_SOURCE_CURRENT_ACTIVITY = 1
USAGE_SOURCE_TASK_ROOT_ACTIVITY = 2
def __init__(self):
super().__init__()
self._app_inactive: Dict[str, bool] = {}
def queryUsageStats(
self, intervalType: int, beginTime: int, endTime: int
) -> List["UsageStats"]:
"""查询使用统计信息"""
result: List[UsageStats] = []
stats_dir = _os.path.join(_app_data_dir, "usagestats")
if _os.path.exists(stats_dir):
for fname in _os.listdir(stats_dir):
if fname.endswith(".json"):
filepath = _os.path.join(stats_dir, fname)
try:
with open(filepath, "r") as f:
data = json.load(f)
stats = UsageStats()
stats._package_name = data.get("package", fname[:-5])
stats._first_time = data.get("first_time", beginTime)
stats._last_time = data.get("last_time", endTime)
stats._last_used = data.get("last_used", beginTime)
stats._total_foreground = data.get("total_foreground", 0)
stats._launch_count = data.get("launch_count", 0)
if stats._last_time >= beginTime and stats._last_time <= endTime:
result.append(stats)
except (json.JSONDecodeError, IOError):
pass
return result
def queryConfigurations(
self, intervalType: int, beginTime: int, endTime: int
) -> List["ConfigurationStats"]:
"""查询配置统计"""
return []
def queryEvents(
self, beginTime: int, endTime: int
) -> "UsageEvents":
"""查询使用事件"""
return UsageEvents()
def isAppInactive(self, packageName: str) -> bool:
"""检查应用是否处于非活跃状态"""
return self._app_inactive.get(packageName, False)
def setAppInactive(self, packageName: str, inactive: bool):
"""设置应用非活跃状态"""
self._app_inactive[packageName] = inactive
def onCarrierPrivilegedAppsChanged(self):
"""运营商特权应用变更 (桌面存根)"""
pass
def toString(self) -> str:
return f"UsageStatsManager@{hex(id(self))}"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# UsageStats
# ═══════════════════════════════════════════════════════════════
class UsageStats(Stub):
"""UsageStats — 应用使用统计"""
def __init__(self):
super().__init__()
self._package_name = ""
self._first_time = 0
self._last_time = 0
self._last_used = 0
self._total_foreground = 0
self._launch_count = 0
def getPackageName(self) -> str:
return self._package_name
def getFirstTimeStamp(self) -> int:
return self._first_time
def getLastTimeStamp(self) -> int:
return self._last_time
def getLastUsedTime(self) -> int:
return self._last_used
def getTotalTimeInForeground(self) -> int:
return self._total_foreground
def getAppLaunchCount(self) -> int:
return self._launch_count
def toString(self) -> str:
return (f"UsageStats(pkg={self._package_name}, "
f"foreground={self._total_foreground}ms, "
f"launches={self._launch_count})")
def equals(self, other) -> bool:
if not isinstance(other, UsageStats):
return False
return self._package_name == other._package_name
def hashCode(self) -> int:
return hash(self._package_name) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# UsageEvents
# ═══════════════════════════════════════════════════════════════
class UsageEvents(Stub):
"""UsageEvents — 使用事件列表"""
def __init__(self):
super().__init__()
self._events: List["UsageEvents.Event"] = []
self._index = 0
def getNextEvent(self, eventOut: "UsageEvents.Event") -> bool:
"""获取下一个事件"""
if self._index < len(self._events):
ev = self._events[self._index]
eventOut._package_name = ev._package_name
eventOut._class_name = ev._class_name
eventOut._action = ev._action
eventOut._time_stamp = ev._time_stamp
self._index += 1
return True
return False
def hasNextEvent(self) -> bool:
"""检查是否还有下一个事件"""
return self._index < len(self._events)
def resetToStart(self):
"""重置到起始位置"""
self._index = 0
# ═══════════════════════════════════════════════════════════
# Event — 内部事件类
# ═══════════════════════════════════════════════════════════
class Event(Stub):
"""UsageEvents.Event — 使用事件"""
# --- Event action constants ---
MOVE_TO_FOREGROUND = 1
MOVE_TO_BACKGROUND = 2
CONFIGURATION_CHANGE = 3
SHORTCUT_INVOCATION = 4
STANDBY_BUDGET_CHANGED = 5
USER_INTERACTION = 6
NOTIFICATION_SEEN = 7
NOTIFICATION_INTERRUPTION = 8
CHOOSER_ACTION = 9
APP_COMPONENT_USED = 10
ACTIVITY_STOPPED = 11
SYSTEM_INTERACTION = 12
FLAG_IS_PACKAGE_INSTALL = 13
FLAG_NO_INTERRUPTION = 14
def __init__(self):
super().__init__()
self._package_name = ""
self._class_name = ""
self._action = 0
self._time_stamp = 0
def getPackageName(self) -> str:
return self._package_name
def getClassName(self) -> str:
return self._class_name
def getAction(self) -> int:
return self._action
def getTimeStamp(self) -> int:
return self._time_stamp
def toString(self) -> str:
return (f"Event(pkg={self._package_name}, "
f"action={self._action}, time={self._time_stamp})")
def equals(self, other) -> bool:
if not isinstance(other, UsageEvents.Event):
return False
return (self._package_name == other._package_name
and self._action == other._action
and self._time_stamp == other._time_stamp)
def hashCode(self) -> int:
return hash((self._package_name, self._action, self._time_stamp)) & 0x7FFFFFFF
def toString(self) -> str:
return f"UsageEvents(count={len(self._events)})"
def equals(self, other) -> bool:
if not isinstance(other, UsageEvents):
return False
return self._events == other._events
def hashCode(self) -> int:
return hash(len(self._events)) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# ConfigurationStats
# ═══════════════════════════════════════════════════════════════
class ConfigurationStats(Stub):
"""ConfigurationStats — 配置统计"""
def __init__(self):
super().__init__()
self._first_time = 0
self._last_time = 0
self._last_active = 0
self._total_active = 0
self._activation_count = 0
def getFirstTimeStamp(self) -> int:
return self._first_time
def getLastTimeStamp(self) -> int:
return self._last_time
def getLastTimeActive(self) -> int:
return self._last_active
def getTotalTimeActive(self) -> int:
return self._total_active
def getActivationCount(self) -> int:
return self._activation_count
def toString(self) -> str:
return f"ConfigurationStats(active={self._total_active}ms, count={self._activation_count})"
def equals(self, other) -> bool:
if not isinstance(other, ConfigurationStats):
return False
return (self._first_time == other._first_time
and self._last_time == other._last_time)
def hashCode(self) -> int:
return hash((self._first_time, self._last_time)) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# CacheUsageStats
# ═══════════════════════════════════════════════════════════════
class CacheUsageStats(Stub):
"""CacheUsageStats — 缓存使用统计"""
def __init__(self):
super().__init__()
self._cache_size = 0
self._total_size = 0
def getCacheSize(self) -> int:
return self._cache_size
def getTotalSize(self) -> int:
return self._total_size
def toString(self) -> str:
return f"CacheUsageStats(cache={self._cache_size}B)"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# NetworkUsageStats
# ═══════════════════════════════════════════════════════════════
class NetworkUsageStats(Stub):
"""NetworkUsageStats — 网络使用统计"""
def __init__(self):
super().__init__()
self._rx_bytes = 0
self._tx_bytes = 0
self._rx_packets = 0
self._tx_packets = 0
def getRxBytes(self) -> int:
return self._rx_bytes
def getTxBytes(self) -> int:
return self._tx_bytes
def getRxPackets(self) -> int:
return self._rx_packets
def getTxPackets(self) -> int:
return self._tx_packets
def toString(self) -> str:
return f"NetworkUsageStats(rx={self._rx_bytes}B, tx={self._tx_bytes}B)"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# UsageStatsManagerInternal
# ═══════════════════════════════════════════════════════════════
class UsageStatsManagerInternal(Stub):
"""UsageStatsManagerInternal — 内部使用统计管理器"""
def __init__(self):
super().__init__()
def reportEvent(self, event: Any):
"""报告使用事件"""
pass
def toString(self) -> str:
return f"UsageStatsManagerInternal@{hex(id(self))}"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# StorageStatsManager
# ═══════════════════════════════════════════════════════════════
class StorageStatsManager(Stub):
"""StorageStatsManager — 存储统计管理器"""
def __init__(self):
super().__init__()
def queryStatsForPackage(
self, storageUuid: Any, packageName: str, userHandle: Any
) -> "StorageStats":
"""查询指定应用存储统计"""
return self._query_stats(storageUuid, packageName)
def queryStatsForUid(
self, storageUuid: Any, uid: int
) -> "StorageStats":
"""查询指定 UID 存储统计"""
return self._query_stats(storageUuid, f"uid_{uid}")
def queryStatsForUser(
self, storageUuid: Any, userHandle: Any
) -> "StorageStats":
"""查询指定用户存储统计"""
return self._query_stats(storageUuid, f"user_{userHandle}")
def queryExternalStatsForUser(
self, storageUuid: Any, userHandle: Any
) -> "StorageStats":
"""查询外部用户存储统计"""
return self._query_stats(storageUuid, f"external_user_{userHandle}")
def getFreeBytes(self, storageUuid: Any) -> int:
"""获取空闲字节数"""
try:
st = _os.statvfs(_sdcard_root)
return st.f_bavail * st.f_frsize
except (OSError, AttributeError):
return 1024 * 1024 * 1024 # 默认 1GB
def getTotalBytes(self, storageUuid: Any) -> int:
"""获取总字节数"""
try:
st = _os.statvfs(_sdcard_root)
return st.f_blocks * st.f_frsize
except (OSError, AttributeError):
return 64 * 1024 * 1024 * 1024 # 默认 64GB
def _query_stats(self, storage_uuid: Any, tag: str) -> "StorageStats":
"""内部查询存储统计"""
stats = StorageStats()
pkg_dir = _os.path.join(_app_data_dir, "files")
if _os.path.exists(pkg_dir):
for dirpath, dirnames, filenames in _os.walk(pkg_dir):
for fname in filenames:
fpath = _os.path.join(dirpath, fname)
try:
fsize = _os.path.getsize(fpath)
ext = _os.path.splitext(fname)[1].lower()
if ext in (".jpg", ".png", ".gif", ".webp", ".bmp"):
stats._image_bytes += fsize
elif ext in (".mp3", ".wav", ".flac", ".ogg", ".aac"):
stats._music_bytes += fsize
elif ext in (".mp4", ".avi", ".mkv", ".mov", ".webm"):
stats._video_bytes += fsize
else:
stats._data_bytes += fsize
except OSError:
pass
return stats
def toString(self) -> str:
return f"StorageStatsManager@{hex(id(self))}"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# StorageStats
# ═══════════════════════════════════════════════════════════════
class StorageStats(Stub):
"""StorageStats — 存储统计"""
def __init__(self):
super().__init__()
self._app_bytes = 0
self._data_bytes = 0
self._cache_bytes = 0
self._music_bytes = 0
self._video_bytes = 0
self._image_bytes = 0
self._game_bytes = 0
def getAppBytes(self) -> int:
return self._app_bytes
def getDataBytes(self) -> int:
return self._data_bytes
def getCacheBytes(self) -> int:
return self._cache_bytes
def getMusicBytes(self) -> int:
return self._music_bytes
def getVideoBytes(self) -> int:
return self._video_bytes
def getImageBytes(self) -> int:
return self._image_bytes
def getGameBytes(self) -> int:
return self._game_bytes
def toString(self) -> str:
return (f"StorageStats(app={self._app_bytes}B, data={self._data_bytes}B, "
f"cache={self._cache_bytes}B, music={self._music_bytes}B, "
f"video={self._video_bytes}B, image={self._image_bytes}B)")
def equals(self, other) -> bool:
if not isinstance(other, StorageStats):
return False
return (self._app_bytes == other._app_bytes
and self._data_bytes == other._data_bytes)
def hashCode(self) -> int:
return hash((self._app_bytes, self._data_bytes)) & 0x7FFFFFFF
+360
View File
@@ -0,0 +1,360 @@
"""
Android App Widget API — AppWidgetProvider, AppWidgetManager, AppWidgetProviderInfo, AppWidgetHost, AppWidgetHostView
方法签名完全对齐 Android SDK
"""
import sys
from typing import Any, Dict, List, Optional, Set, Tuple, Callable
from .._core import Stub, AndroidOnlyError
from ..java.lang import RuntimeException
IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {})
__all__ = [
"AppWidgetProvider",
"AppWidgetManager",
"AppWidgetProviderInfo",
"AppWidgetHost",
"AppWidgetHostView",
]
# ═══════════════════════════════════════════════════════════════
# AppWidgetProvider
# ═══════════════════════════════════════════════════════════════
class AppWidgetProvider(Stub):
"""AppWidgetProvider — 应用小部件提供者"""
def __init__(self):
super().__init__()
def onUpdate(self, context: Any, appWidgetManager: "AppWidgetManager", appWidgetIds: List[int]):
"""小部件更新回调"""
pass
def onDeleted(self, context: Any, appWidgetIds: List[int]):
"""小部件删除回调"""
pass
def onEnabled(self, context: Any):
"""首个小部件启用回调"""
pass
def onDisabled(self, context: Any):
"""最后一个小部件禁用回调"""
pass
def onAppWidgetOptionsChanged(
self, context: Any, appWidgetManager: "AppWidgetManager",
appWidgetId: int, newOptions: Dict[str, Any]
):
"""小部件选项变更回调"""
pass
def onReceive(self, context: Any, intent: Any):
"""接收广播回调"""
pass
def onRestored(self, context: Any, oldWidgetIds: List[int], newWidgetId: int):
"""小部件恢复回调"""
pass
def onProviderChanged(
self, context: Any, appWidgetManager: "AppWidgetManager",
appWidgetId: int, newInfo: "AppWidgetProviderInfo"
):
"""提供者变更回调"""
pass
def toString(self) -> str:
return f"AppWidgetProvider@{hex(id(self))}"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# AppWidgetManager
# ═══════════════════════════════════════════════════════════════
class AppWidgetManager(Stub):
"""AppWidgetManager — 应用小部件管理器 (桌面存根)"""
ACTION_APPWIDGET_UPDATE = "android.appwidget.action.APPWIDGET_UPDATE"
ACTION_APPWIDGET_DELETED = "android.appwidget.action.APPWIDGET_DELETED"
ACTION_APPWIDGET_DISABLED = "android.appwidget.action.APPWIDGET_DISABLED"
ACTION_APPWIDGET_ENABLED = "android.appwidget.action.APPWIDGET_ENABLED"
ACTION_APPWIDGET_OPTIONS_CHANGED = "android.appwidget.action.APPWIDGET_OPTIONS_CHANGED"
ACTION_APPWIDGET_RESTORED = "android.appwidget.action.APPWIDGET_RESTORED"
ACTION_APPWIDGET_HOST_RESTORED = "android.appwidget.action.APPWIDGET_HOST_RESTORED"
EXTRA_APPWIDGET_ID = "appWidgetId"
EXTRA_APPWIDGET_IDS = "appWidgetIds"
EXTRA_APPWIDGET_OLD_IDS = "appWidgetOldIds"
EXTRA_APPWIDGET_OPTIONS = "appWidgetOptions"
EXTRA_APPWIDGET_PROVIDER = "appWidgetProvider"
EXTRA_APPWIDGET_PROVIDER_PROFILE = "appWidgetProviderProfile"
WIDGET_CATEGORY_HOME_SCREEN = 1
WIDGET_CATEGORY_KEYGUARD = 2
WIDGET_CATEGORY_SEARCHBOX = 4
OPTION_APPWIDGET_MIN_WIDTH = "appWidgetMinWidth"
OPTION_APPWIDGET_MIN_HEIGHT = "appWidgetMinHeight"
OPTION_APPWIDGET_MAX_WIDTH = "appWidgetMaxWidth"
OPTION_APPWIDGET_MAX_HEIGHT = "appWidgetMaxHeight"
def __init__(self, context: Any = None):
super().__init__()
self._context = context
self._widget_views: Dict[int, Any] = {}
self._widget_options: Dict[int, Dict[str, Any]] = {}
self._providers: List["AppWidgetProviderInfo"] = []
self._next_widget_id = 1
@staticmethod
def getInstance(context: Any) -> "AppWidgetManager":
"""获取 AppWidgetManager 实例"""
return AppWidgetManager(context)
def getAppWidgetIds(self, info: "AppWidgetProviderInfo") -> List[int]:
"""获取指定提供者的小部件 ID 列表"""
return list(self._widget_views.keys())
def updateAppWidget(self, appWidgetId: int, view: Any):
"""更新小部件视图"""
self._widget_views[appWidgetId] = view
def updateAppWidgetOptions(self, appWidgetId: int, options: Dict[str, Any]):
"""更新小部件选项"""
if appWidgetId in self._widget_options:
self._widget_options[appWidgetId].update(options)
else:
self._widget_options[appWidgetId] = dict(options)
def getAppWidgetOptions(self, appWidgetId: int) -> Dict[str, Any]:
"""获取小部件选项"""
return self._widget_options.get(appWidgetId, {})
def bindAppWidgetIdIfAllowed(
self, appWidgetId: int, info: "AppWidgetProviderInfo"
) -> bool:
"""绑定小部件 ID(如允许)"""
return True
def getAppWidgetInfo(self, appWidgetId: int) -> Optional["AppWidgetProviderInfo"]:
"""获取小部件信息"""
if appWidgetId in self._widget_views:
return self._providers[0] if self._providers else None
return None
def getInstalledProviders(self) -> List["AppWidgetProviderInfo"]:
"""获取已安装的提供者列表"""
return list(self._providers)
def isBindAppWidgetIdAllowed(self, appWidgetId: int, info: "AppWidgetProviderInfo") -> bool:
"""检查是否允许绑定小部件 ID"""
return True
def createAppWidgetConfigIntentSender(
self, info: "AppWidgetProviderInfo", appWidgetId: int
) -> Optional[Any]:
"""创建小部件配置 IntentSender"""
return None
def requestPinAppWidget(
self, info: "AppWidgetProviderInfo", successCallback: Any = None
) -> bool:
"""请求固定小部件"""
return True
def getAppWidgetHostViews(self, host: "AppWidgetHost") -> List["AppWidgetHostView"]:
"""获取主机视图列表"""
return []
def toString(self) -> str:
return f"AppWidgetManager(widgets={len(self._widget_views)})"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# AppWidgetProviderInfo
# ═══════════════════════════════════════════════════════════════
class AppWidgetProviderInfo(Stub):
"""AppWidgetProviderInfo — 小部件提供者信息"""
# --- ResizeMode ---
RESIZE_MODE_NONE = 0
RESIZE_MODE_HORIZONTAL = 1
RESIZE_MODE_VERTICAL = 2
RESIZE_MODE_RESIZE_ALL = 3
# --- WidgetCategory ---
WIDGET_CATEGORY_HOME_SCREEN = 1
WIDGET_CATEGORY_KEYGUARD = 2
WIDGET_CATEGORY_SEARCHBOX = 4
# --- WidgetFeatures ---
WIDGET_FEATURE_HIDE_FROM_PICKER = 1
WIDGET_FEATURE_CONFIGURATION_OPTIONAL = 2
WIDGET_FEATURE_RECONFIGURABLE = 4
def __init__(self):
super().__init__()
self.provider: Any = None
self.configure: Any = None
self.icon: int = 0
self.label: str = ""
self.previewImage: int = 0
self.previewLayout: int = 0
self.autoAdvanceViewId: int = -1
self.resizeMode: int = self.RESIZE_MODE_NONE
self.widgetCategory: int = self.WIDGET_CATEGORY_HOME_SCREEN
self.widgetFeatures: int = 0
self.description: str = ""
self.targetCellWidth: int = 0
self.targetCellHeight: int = 0
self.minWidth: int = 0
self.minHeight: int = 0
self.maxWidth: int = 0
self.maxHeight: int = 0
self.minResizeWidth: int = 0
self.minResizeHeight: int = 0
self.updatePeriodMillis: int = 0
self.initialLayout: int = 0
def getProfile(self) -> Optional[Any]:
"""获取配置文件"""
return None
def getActivityInfo(self) -> Optional[Any]:
"""获取 Activity 信息"""
return None
def getIcon(self) -> int:
return self.icon
def getLabel(self) -> str:
return self.label
def loadPreviewImage(self, context: Any, density: int) -> Any:
"""加载预览图像"""
return None
def toString(self) -> str:
return f"AppWidgetProviderInfo(label={self.label}, icon={self.icon})"
def equals(self, other) -> bool:
if not isinstance(other, AppWidgetProviderInfo):
return False
return self.provider == other.provider and self.label == other.label
def hashCode(self) -> int:
return hash((str(self.provider), self.label)) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# AppWidgetHost
# ═══════════════════════════════════════════════════════════════
class AppWidgetHost(Stub):
"""AppWidgetHost — 小部件主机"""
def __init__(self, context: Any, hostId: int):
super().__init__()
self._context = context
self._host_id = hostId
self._widget_ids: List[int] = []
self._listening = False
def createView(
self, context: Any, appWidgetId: int, info: "AppWidgetProviderInfo"
) -> "AppWidgetHostView":
"""创建小部件视图"""
view = AppWidgetHostView(context)
view.setAppWidget(appWidgetId, info)
return view
def startListening(self):
"""开始监听"""
self._listening = True
def stopListening(self):
"""停止监听"""
self._listening = False
def allocateAppWidgetId(self) -> int:
"""分配小部件 ID"""
widget_id = len(self._widget_ids) + 1000 + self._host_id
self._widget_ids.append(widget_id)
return widget_id
def deleteAppWidgetId(self, appWidgetId: int):
"""删除小部件 ID"""
if appWidgetId in self._widget_ids:
self._widget_ids.remove(appWidgetId)
def deleteHost(self):
"""删除主机"""
self._widget_ids.clear()
def clearViews(self):
"""清除所有视图"""
self._widget_ids.clear()
def toString(self) -> str:
return f"AppWidgetHost(hostId={self._host_id}, widgets={len(self._widget_ids)})"
def equals(self, other) -> bool:
if not isinstance(other, AppWidgetHost):
return False
return self._host_id == other._host_id
def hashCode(self) -> int:
return self._host_id & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# AppWidgetHostView
# ═══════════════════════════════════════════════════════════════
class AppWidgetHostView(Stub):
"""AppWidgetHostView — 小部件主机视图"""
def __init__(self, context: Any):
super().__init__()
self._context = context
self._app_widget_id = -1
self._info: Optional["AppWidgetProviderInfo"] = None
def setAppWidget(self, appWidgetId: int, info: "AppWidgetProviderInfo"):
"""设置小部件"""
self._app_widget_id = appWidgetId
self._info = info
def updateAppWidget(self, info: "AppWidgetProviderInfo"):
"""更新小部件"""
self._info = info
def getAppWidgetInfo(self) -> Optional["AppWidgetProviderInfo"]:
return self._info
def toString(self) -> str:
return f"AppWidgetHostView(widgetId={self._app_widget_id})"
def equals(self, other) -> bool:
if not isinstance(other, AppWidgetHostView):
return False
return self._app_widget_id == other._app_widget_id
def hashCode(self) -> int:
return self._app_widget_id & 0x7FFFFFFF
+415
View File
@@ -0,0 +1,415 @@
"""
Android Companion Device API — CompanionDeviceManager, AssociationInfo, DeviceFilters
方法签名完全对齐 Android SDK
"""
import sys
from typing import Any, Dict, List, Optional, Set, Tuple, Callable
from .._core import Stub, AndroidOnlyError
from ..java.lang import RuntimeException
IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {})
__all__ = [
"CompanionDeviceManager",
"AssociationInfo",
"WifiDeviceFilter",
"BluetoothDeviceFilter",
"BluetoothLeDeviceFilter",
"CommsDevice",
"DeviceActivityCallback",
"CompanionService",
]
# ═══════════════════════════════════════════════════════════════
# CompanionDeviceManager
# ═══════════════════════════════════════════════════════════════
class CompanionDeviceManager(Stub):
"""CompanionDeviceManager — 配套设备管理器 (桌面存根)"""
EXTRA_DEVICE = "android.companion.extra.DEVICE"
EXTRA_ASSOCIATION = "android.companion.extra.ASSOCIATION"
def __init__(self, context: Any = None):
super().__init__()
self._context = context
self._associations: Dict[str, "AssociationInfo"] = {}
self._callbacks: List[Callable] = []
def associate(
self,
request: Any,
callback: "CompanionDeviceManager.Callback",
handler: Any = None,
):
"""发起设备关联请求"""
raise AndroidOnlyError("CompanionDeviceManager.associate() 仅在 Android 设备上可用")
def associateCallback(
self,
request: Any,
callback: Callable,
executor: Any = None,
):
"""使用回调方式发起设备关联"""
raise AndroidOnlyError("CompanionDeviceManager.associateCallback() 仅在 Android 设备上可用")
def requestNotificationAccess(self, pairingDevice: "AssociationInfo"):
"""请求通知访问权限"""
raise AndroidOnlyError("CompanionDeviceManager.requestNotificationAccess() 仅在 Android 设备上可用")
def requestSystemAppStore(self, pairingDevice: "AssociationInfo"):
"""请求系统应用商店"""
pass
def startObservingDevicePresence(self, deviceAddress: str):
"""开始监听设备存在"""
pass
def stopObservingDevicePresence(self, deviceAddress: str):
"""停止监听设备存在"""
pass
def isDeviceAssociated(self, deviceAddress: str) -> bool:
"""检查设备是否已关联"""
return deviceAddress in self._associations
def getDeviceId(self, deviceAddress: str) -> Optional[str]:
"""获取设备 ID"""
info = self._associations.get(deviceAddress)
return info.getSelfManagedDeviceId() if info else None
def getAssociations(self) -> List["AssociationInfo"]:
"""获取所有关联信息"""
return list(self._associations.values())
def removeAssociation(self, deviceAddress: str):
"""移除设备关联"""
self._associations.pop(deviceAddress, None)
# ═══════════════════════════════════════════════════════════
# Callback - 回调类
# ═══════════════════════════════════════════════════════════
class Callback(Stub):
"""CompanionDeviceManager.Callback — 设备发现回调"""
def __init__(self):
super().__init__()
def onDeviceFound(self, intent: Any):
"""设备找到回调"""
pass
def onFailure(self, error: str):
"""设备发现失败回调"""
pass
def toString(self) -> str:
return f"Callback@{hex(id(self))}"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
def toString(self) -> str:
return f"CompanionDeviceManager(associations={len(self._associations)})"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# AssociationInfo
# ═══════════════════════════════════════════════════════════════
class AssociationInfo(Stub):
"""AssociationInfo — 设备关联信息"""
def __init__(
self,
userId: int = 0,
deviceProfile: str = "",
selfManagedDeviceId: str = "",
associatedDevice: Any = None,
isSelfManaged: bool = False,
isNotAssociated: bool = False,
):
super().__init__()
self._user_id = userId
self._device_profile = deviceProfile
self._self_managed_device_id = selfManagedDeviceId
self._associated_device = associatedDevice
self._self_managed = isSelfManaged
self._not_associated = isNotAssociated
def getUserId(self) -> int:
return self._user_id
def getDeviceProfile(self) -> str:
return self._device_profile
def getSelfManagedDeviceId(self) -> str:
return self._self_managed_device_id
def getAssociatedDevice(self) -> Optional[Any]:
return self._associated_device
def isSelfManaged(self) -> bool:
return self._self_managed
def isNotAssociated(self) -> bool:
return self._not_associated
def toString(self) -> str:
return (f"AssociationInfo(deviceId={self._self_managed_device_id}, "
f"profile={self._device_profile})")
def equals(self, other) -> bool:
if not isinstance(other, AssociationInfo):
return False
return self._self_managed_device_id == other._self_managed_device_id
def hashCode(self) -> int:
return hash(self._self_managed_device_id) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# WifiDeviceFilter
# ═══════════════════════════════════════════════════════════════
class WifiDeviceFilter(Stub):
"""WifiDeviceFilter — WiFi 设备过滤器"""
def __init__(self, namePattern: str = ""):
super().__init__()
self._name_pattern = namePattern
def getNamePattern(self) -> str:
return self._name_pattern
def toString(self) -> str:
return f"WifiDeviceFilter(pattern={self._name_pattern})"
def equals(self, other) -> bool:
if not isinstance(other, WifiDeviceFilter):
return False
return self._name_pattern == other._name_pattern
def hashCode(self) -> int:
return hash(self._name_pattern) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# BluetoothDeviceFilter
# ═══════════════════════════════════════════════════════════════
class BluetoothDeviceFilter(Stub):
"""BluetoothDeviceFilter — 蓝牙设备过滤器"""
class Builder(Stub):
"""BluetoothDeviceFilter.Builder — 蓝牙过滤器构建器"""
def __init__(self):
super().__init__()
self._name_pattern = ""
self._address = ""
def setNamePattern(self, pattern: str) -> "BluetoothDeviceFilter.Builder":
self._name_pattern = pattern
return self
def setAddress(self, address: str) -> "BluetoothDeviceFilter.Builder":
self._address = address
return self
def build(self) -> "BluetoothDeviceFilter":
return BluetoothDeviceFilter(
namePattern=self._name_pattern,
address=self._address,
)
def toString(self) -> str:
return f"Builder(pattern={self._name_pattern})"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
def __init__(self, namePattern: str = "", address: str = ""):
super().__init__()
self._name_pattern = namePattern
self._address = address
def getNamePattern(self) -> str:
return self._name_pattern
def getAddress(self) -> str:
return self._address
def toString(self) -> str:
return f"BluetoothDeviceFilter(address={self._address})"
def equals(self, other) -> bool:
if not isinstance(other, BluetoothDeviceFilter):
return False
return self._address == other._address
def hashCode(self) -> int:
return hash(self._address) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# BluetoothLeDeviceFilter
# ═══════════════════════════════════════════════════════════════
class BluetoothLeDeviceFilter(Stub):
"""BluetoothLeDeviceFilter — BLE 设备过滤器"""
class Builder(Stub):
"""BluetoothLeDeviceFilter.Builder — BLE 过滤器构建器"""
def __init__(self):
super().__init__()
self._name_pattern = ""
self._scan_filter = None
def setNamePattern(self, pattern: str) -> "BluetoothLeDeviceFilter.Builder":
self._name_pattern = pattern
return self
def setScanFilter(self, scanFilter: Any) -> "BluetoothLeDeviceFilter.Builder":
self._scan_filter = scanFilter
return self
def build(self) -> "BluetoothLeDeviceFilter":
return BluetoothLeDeviceFilter(
namePattern=self._name_pattern,
scanFilter=self._scan_filter,
)
def toString(self) -> str:
return f"BLEBuilder(pattern={self._name_pattern})"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
def __init__(self, namePattern: str = "", scanFilter: Any = None):
super().__init__()
self._name_pattern = namePattern
self._scan_filter = scanFilter
def getNamePattern(self) -> str:
return self._name_pattern
def getScanFilter(self) -> Optional[Any]:
return self._scan_filter
def toString(self) -> str:
return f"BluetoothLeDeviceFilter(pattern={self._name_pattern})"
def equals(self, other) -> bool:
if not isinstance(other, BluetoothLeDeviceFilter):
return False
return self._name_pattern == other._name_pattern
def hashCode(self) -> int:
return hash(self._name_pattern) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# CommsDevice
# ═══════════════════════════════════════════════════════════════
class CommsDevice(Stub):
"""CommsDevice — 通信设备信息"""
def __init__(self, deviceId: str = "", displayName: str = ""):
super().__init__()
self._device_id = deviceId
self._display_name = displayName
def getDeviceId(self) -> str:
return self._device_id
def getDisplayName(self) -> str:
return self._display_name
def toString(self) -> str:
return f"CommsDevice(id={self._device_id}, name={self._display_name})"
def equals(self, other) -> bool:
if not isinstance(other, CommsDevice):
return False
return self._device_id == other._device_id
def hashCode(self) -> int:
return hash(self._device_id) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# DeviceActivityCallback
# ═══════════════════════════════════════════════════════════════
class DeviceActivityCallback(Stub):
"""DeviceActivityCallback — 设备活动回调"""
def __init__(self):
super().__init__()
def onDeviceActivityChanged(self, associationInfo: "AssociationInfo", active: bool):
"""设备活动状态变更回调"""
pass
def toString(self) -> str:
return f"DeviceActivityCallback@{hex(id(self))}"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# CompanionService
# ═══════════════════════════════════════════════════════════════
class CompanionService(Stub):
"""CompanionService — 配套服务"""
SERVICE_INTERFACE = "android.companion.CompanionDeviceService"
def __init__(self):
super().__init__()
def onConnected(self, deviceInfo: Any):
"""已连接回调"""
pass
def onDisconnected(self, deviceInfo: Any):
"""已断开回调"""
pass
def toString(self) -> str:
return f"CompanionService@{hex(id(self))}"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
+418
View File
@@ -0,0 +1,418 @@
"""
Android Credentials API — CredentialManager, CreateCredentialRequest, GetCredentialRequest 等
方法签名完全对齐 Android SDK
"""
import sys
from typing import Any, Dict, List, Optional, Set, Tuple, Callable
from .._core import Stub, AndroidOnlyError
from ..java.lang import RuntimeException
IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {})
__all__ = [
"CredentialManager",
"CreateCredentialRequest",
"CreateCredentialResponse",
"GetCredentialRequest",
"GetCredentialResponse",
"CredentialDescription",
"CredentialManagerState",
"PrepareGetCredentialResponse",
]
# ═══════════════════════════════════════════════════════════════
# CredentialManager
# ═══════════════════════════════════════════════════════════════
class CredentialManager(Stub):
"""CredentialManager — 凭据管理器 (桌面存根)"""
ACTION_CREATE_CREDENTIAL = "android.credentials.action.CREATE_CREDENTIAL"
ACTION_GET_CREDENTIAL = "android.credentials.action.GET_CREDENTIAL"
ACTION_CLEAR_CREDENTIAL = "android.credentials.action.CLEAR_CREDENTIAL"
def __init__(self, context: Any = None):
super().__init__()
self._context = context
self._descriptions: List["CredentialDescription"] = []
def createCredential(
self,
request: "CreateCredentialRequest",
cancellationSignal: Any = None,
executor: Any = None,
callback: Callable = None,
):
"""创建凭据"""
raise AndroidOnlyError("CredentialManager.createCredential() 仅在 Android 设备上可用")
def getCredential(
self,
request: "GetCredentialRequest",
cancellationSignal: Any = None,
executor: Any = None,
callback: Callable = None,
):
"""获取凭据"""
raise AndroidOnlyError("CredentialManager.getCredential() 仅在 Android 设备上可用")
def clearCredentialState(
self,
request: Any = None,
cancellationSignal: Any = None,
executor: Any = None,
callback: Callable = None,
):
"""清除凭据状态"""
raise AndroidOnlyError("CredentialManager.clearCredentialState() 仅在 Android 设备上可用")
def registerCredentialDescription(
self,
credentialDescription: "CredentialDescription",
cancellationSignal: Any = None,
executor: Any = None,
callback: Callable = None,
):
"""注册凭据描述"""
if credentialDescription not in self._descriptions:
self._descriptions.append(credentialDescription)
def unregisterCredentialDescription(
self,
credentialDescription: "CredentialDescription",
):
"""注销凭据描述"""
if credentialDescription in self._descriptions:
self._descriptions.remove(credentialDescription)
def getCredentialManagerState(self) -> "CredentialManagerState":
"""获取凭据管理器状态"""
return CredentialManagerState()
def toString(self) -> str:
return f"CredentialManager@{hex(id(self))}"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# CreateCredentialRequest
# ═══════════════════════════════════════════════════════════════
class CreateCredentialRequest(Stub):
"""CreateCredentialRequest — 创建凭据请求"""
class Builder(Stub):
"""CreateCredentialRequest.Builder — 创建凭据请求构建器"""
def __init__(self, credentialType: str = ""):
super().__init__()
self._credential_type = credentialType
self._credential_data = None
self._candidate_query_data = None
self._always_send_app_uri = False
self._origin = ""
def setCredentialData(self, data: Any) -> "CreateCredentialRequest.Builder":
self._credential_data = data
return self
def setCandidateQueryData(
self, data: Any
) -> "CreateCredentialRequest.Builder":
self._candidate_query_data = data
return self
def setAlwaysSendAppUri(
self, alwaysSend: bool
) -> "CreateCredentialRequest.Builder":
self._always_send_app_uri = alwaysSend
return self
def setOrigin(self, origin: str) -> "CreateCredentialRequest.Builder":
self._origin = origin
return self
def build(self) -> "CreateCredentialRequest":
req = CreateCredentialRequest.__new__(CreateCredentialRequest)
req._credential_type = self._credential_type
req._credential_data = self._credential_data
req._candidate_query_data = self._candidate_query_data
req._always_send_app_uri = self._always_send_app_uri
req._origin = self._origin
return req
def toString(self) -> str:
return f"CreateCredentialRequest.Builder(type={self._credential_type})"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
def __init__(self):
super().__init__()
self._credential_type = ""
self._credential_data = None
self._candidate_query_data = None
self._always_send_app_uri = False
self._origin = ""
def getCredentialData(self) -> Optional[Any]:
return self._credential_data
def getCredentialType(self) -> str:
return self._credential_type
def getCandidateQueryData(self) -> Optional[Any]:
return self._candidate_query_data
def alwaysSendAppUri(self) -> bool:
return self._always_send_app_uri
def getOrigin(self) -> str:
return self._origin
def toString(self) -> str:
return f"CreateCredentialRequest(type={self._credential_type})"
def equals(self, other) -> bool:
if not isinstance(other, CreateCredentialRequest):
return False
return self._credential_type == other._credential_type
def hashCode(self) -> int:
return hash(self._credential_type) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# CreateCredentialResponse
# ═══════════════════════════════════════════════════════════════
class CreateCredentialResponse(Stub):
"""CreateCredentialResponse — 创建凭据响应"""
def __init__(self):
super().__init__()
self._credential_data = None
self._credential_data_result = None
def getCredentialData(self) -> Optional[Any]:
return self._credential_data
def getCredentialDataResult(self) -> Optional[Any]:
return self._credential_data_result
def toString(self) -> str:
return f"CreateCredentialResponse@{hex(id(self))}"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# GetCredentialRequest
# ═══════════════════════════════════════════════════════════════
class GetCredentialRequest(Stub):
"""GetCredentialRequest — 获取凭据请求"""
class Builder(Stub):
"""GetCredentialRequest.Builder — 获取凭据请求构建器"""
def __init__(self):
super().__init__()
self._credential_data = None
self._origin = ""
self._always_send_app_uri = False
self._candidate_query_data = None
def setCredentialData(
self, data: Any
) -> "GetCredentialRequest.Builder":
self._credential_data = data
return self
def setOrigin(self, origin: str) -> "GetCredentialRequest.Builder":
self._origin = origin
return self
def setAlwaysSendAppUri(
self, alwaysSend: bool
) -> "GetCredentialRequest.Builder":
self._always_send_app_uri = alwaysSend
return self
def setCandidateQueryData(
self, data: Any
) -> "GetCredentialRequest.Builder":
self._candidate_query_data = data
return self
def build(self) -> "GetCredentialRequest":
req = GetCredentialRequest.__new__(GetCredentialRequest)
req._credential_data = self._credential_data
req._origin = self._origin
req._always_send_app_uri = self._always_send_app_uri
req._candidate_query_data = self._candidate_query_data
return req
def toString(self) -> str:
return f"GetCredentialRequest.Builder@{hex(id(self))}"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
def __init__(self):
super().__init__()
self._credential_data = None
self._origin = ""
self._always_send_app_uri = False
self._candidate_query_data = None
def getCredentialData(self) -> Optional[Any]:
return self._credential_data
def getOrigin(self) -> str:
return self._origin
def alwaysSendAppUri(self) -> bool:
return self._always_send_app_uri
def getCandidateQueryData(self) -> Optional[Any]:
return self._candidate_query_data
def toString(self) -> str:
return f"GetCredentialRequest@{hex(id(self))}"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# GetCredentialResponse
# ═══════════════════════════════════════════════════════════════
class GetCredentialResponse(Stub):
"""GetCredentialResponse — 获取凭据响应"""
def __init__(self):
super().__init__()
self._credential_data_results: List[Any] = []
def getCredentialDataResults(self) -> List[Any]:
return list(self._credential_data_results)
def toString(self) -> str:
return f"GetCredentialResponse(results={len(self._credential_data_results)})"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# CredentialDescription
# ═══════════════════════════════════════════════════════════════
class CredentialDescription(Stub):
"""CredentialDescription — 凭据描述"""
def __init__(self, credentialType: str = "", origin: str = ""):
super().__init__()
self._credential_type = credentialType
self._origin = origin
def getCredentialType(self) -> str:
return self._credential_type
def getOrigin(self) -> str:
return self._origin
def toString(self) -> str:
return f"CredentialDescription(type={self._credential_type})"
def equals(self, other) -> bool:
if not isinstance(other, CredentialDescription):
return False
return (self._credential_type == other._credential_type
and self._origin == other._origin)
def hashCode(self) -> int:
return hash((self._credential_type, self._origin)) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# CredentialManagerState
# ═══════════════════════════════════════════════════════════════
class CredentialManagerState(Stub):
"""CredentialManagerState — 凭据管理器状态"""
def __init__(self):
super().__init__()
self._registered_descriptions: List["CredentialDescription"] = []
self._is_enabled = True
def getRegisteredCredentialDescriptions(self) -> List["CredentialDescription"]:
return list(self._registered_descriptions)
def isEnabled(self) -> bool:
return self._is_enabled
def toString(self) -> str:
return f"CredentialManagerState(enabled={self._is_enabled})"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# PrepareGetCredentialResponse
# ═══════════════════════════════════════════════════════════════
class PrepareGetCredentialResponse(Stub):
"""PrepareGetCredentialResponse — 预备获取凭据响应"""
def __init__(self):
super().__init__()
self._has_results = False
self._pending_credential = None
def hasResults(self) -> bool:
return self._has_results
def getPendingGetCredentialResponse(self) -> Optional["GetCredentialResponse"]:
return self._pending_credential
def toString(self) -> str:
return f"PrepareGetCredentialResponse(hasResults={self._has_results})"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
+299
View File
@@ -0,0 +1,299 @@
"""
Android hardware.display 组件 — DisplayManager, VirtualDisplay, WifiDisplay 等
方法签名完全对齐 Android SDK
"""
import time
from typing import Any, Callable, Dict, List, Optional, Tuple
from .._core import Stub, AndroidOnlyError
# ═══════════════════════════════════════════════════════════════
# DisplayManager
# ═══════════════════════════════════════════════════════════════
class DisplayManager(Stub):
"""DisplayManager — 显示管理器"""
VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR = 1
VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY = 4
VIRTUAL_DISPLAY_FLAG_PUBLIC_DISPLAY = 8
VIRTUAL_DISPLAY_FLAG_PRESENTATION = 2
VIRTUAL_DISPLAY_FLAG_SECURE = 16
VIRTUAL_DISPLAY_FLAG_SUPPORTS_HDR = 32
MATCH_DEFAULT_DISPLAY = -1
MATCH_ALL_DISPLAYS = 0
# ═══════════════════════════════════════════════════════════
# DisplayListener
# ═══════════════════════════════════════════════════════════
class DisplayListener(Stub):
"""DisplayManager.DisplayListener — 显示监听器"""
def onDisplayAdded(self, displayId: int):
pass
def onDisplayRemoved(self, displayId: int):
pass
def onDisplayChanged(self, displayId: int):
pass
def __init__(self):
super().__init__()
self._displays: Dict[int, Display] = {}
self._listeners: Dict[int, DisplayManager.DisplayListener] = {}
self._next_display_id = 1
# Create default display
self._displays[0] = Display(0, "Built-in Display")
def getDisplay(self, displayId: int) -> Optional["Display"]:
return self._displays.get(displayId)
def getDisplays(self) -> List["Display"]:
if DisplayManager.MATCH_ALL_DISPLAYS:
return list(self._displays.values())
return []
def registerDisplayListener(self, listener: DisplayListener, handler: Any = None):
lid = id(listener)
self._listeners[lid] = listener
def unregisterDisplayListener(self, listener: DisplayListener):
lid = id(listener)
self._listeners.pop(lid, None)
def createVirtualDisplay(self, name: str, width: int, height: int, densityDpi: int,
surface: Any, flags: int, callback: Callable = None,
handler: Any = None) -> "VirtualDisplay":
display_id = self._next_display_id
self._next_display_id += 1
vd = VirtualDisplay(display_id, name, width, height, densityDpi, surface, flags)
return vd
def getStableDisplay(self) -> Optional["Display"]:
return self._displays.get(0)
# ═══════════════════════════════════════════════════════════════
# Display
# ═══════════════════════════════════════════════════════════════
class Display(Stub):
"""Display — 显示信息"""
DEFAULT_DISPLAY = 0
STATE_UNKNOWN = 0
STATE_OFF = 1
STATE_ON = 2
STATE_DOZE = 3
STATE_DOZE_SUSPEND = 4
FLAG_PRESENTATION = 2
FLAG_PRIVATE = 4
FLAG_ROUND = 8
FLAG_CAN_SHOW_WITH_INSETS = 16
FLAG_SECURE = 32
FLAG_SUPPORTS_HDR = 64
def __init__(self, displayId: int = 0, name: str = "Display",
width: int = 1920, height: int = 1080,
densityDpi: int = 420, refreshRate: float = 60.0):
super().__init__()
self._display_id = displayId
self._name = name
self._width = width
self._height = height
self._density_dpi = densityDpi
self._refresh_rate = refreshRate
self._state = Display.STATE_ON
self._flags = 0
self._app_width = width
self._app_height = height
def getDisplayId(self) -> int:
return self._display_id
def getName(self) -> str:
return self._name
def getWidth(self) -> int:
return self._width
def getHeight(self) -> int:
return self._height
def getRotation(self) -> int:
return 0
def getState(self) -> int:
return self._state
def getRefreshRate(self) -> float:
return self._refresh_rate
def isHdr(self) -> bool:
return bool(self._flags & Display.FLAG_SUPPORTS_HDR)
def getFlags(self) -> int:
return self._flags
def getMode(self) -> Any:
return None
def getSupportedModes(self) -> List[Any]:
return []
def getSupportedHdrTypes(self) -> List[int]:
return []
def getAppWidth(self) -> int:
return self._app_width
def getAppHeight(self) -> int:
return self._app_height
def getDensityDpi(self) -> int:
return self._density_dpi
def toString(self) -> str:
return f"Display(id={self._display_id}, name={self._name}, {self._width}x{self._height}," \
f" dpi={self._density_dpi})"
# ═══════════════════════════════════════════════════════════════
# VirtualDisplay
# ═══════════════════════════════════════════════════════════════
class VirtualDisplay(Stub):
"""VirtualDisplay — 虚拟显示"""
def __init__(self, displayId: int, name: str, width: int, height: int,
densityDpi: int, surface: Any, flags: int):
super().__init__()
self._display_id = displayId
self._name = name
self._width = width
self._height = height
self._density_dpi = densityDpi
self._surface = surface
self._flags = flags
self._display = Display(displayId, name, width, height, densityDpi)
self._released = False
def setSurface(self, surface: Any):
self._surface = surface
def resize(self, width: int, height: int, densityDpi: int):
self._width = width
self._height = height
self._density_dpi = densityDpi
def release(self):
self._released = True
def getDisplay(self) -> Display:
return self._display
def getSurface(self) -> Any:
return self._surface
def getFlags(self) -> int:
return self._flags
def toString(self) -> str:
return f"VirtualDisplay(id={self._display_id}, name={self._name}," \
f" {self._width}x{self._height}, released={self._released})"
# ═══════════════════════════════════════════════════════════════
# WifiDisplayStatus
# ═══════════════════════════════════════════════════════════════
class WifiDisplayStatus(Stub):
"""WifiDisplayStatus — Wi-Fi 显示状态"""
FEATURE_STATE_DISABLED = 0
FEATURE_STATE_ON = 1
FEATURE_STATE_OFF = 2
FEATURE_STATE_UNAVAILABLE = 3
def __init__(self):
super().__init__()
self._active_display: Optional[WifiDisplay] = None
self._scan_results: List[WifiDisplay] = []
self._feature_state = WifiDisplayStatus.FEATURE_STATE_DISABLED
def getActiveDisplay(self) -> Optional["WifiDisplay"]:
return self._active_display
def getScanResults(self) -> List["WifiDisplay"]:
return self._scan_results
def getFeatureState(self) -> int:
return self._feature_state
def toString(self) -> str:
return f"WifiDisplayStatus(featureState={self._feature_state})"
# ═══════════════════════════════════════════════════════════════
# WifiDisplay
# ═══════════════════════════════════════════════════════════════
class WifiDisplay(Stub):
"""WifiDisplay — Wi-Fi 显示设备"""
def __init__(self, deviceAddress: str = "", deviceName: str = "",
deviceAlias: str = "", available: bool = False,
remembered: bool = False):
super().__init__()
self._device_address = deviceAddress
self._device_name = deviceName
self._device_alias = deviceAlias
self._available = available
self._remembered = remembered
def getDeviceAddress(self) -> str:
return self._device_address
def getDeviceName(self) -> str:
return self._device_name
def getDeviceAlias(self) -> str:
return self._device_alias
def isAvailable(self) -> bool:
return self._available
def isRemembered(self) -> bool:
return self._remembered
def equals(self, other: Any) -> bool:
if not isinstance(other, WifiDisplay):
return False
return self._device_address == other._device_address
def hashCode(self) -> int:
return hash(self._device_address) & 0x7FFFFFFF
def toString(self) -> str:
return f"WifiDisplay(address={self._device_address}, name={self._device_name}," \
f" available={self._available})"
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"DisplayManager",
"Display",
"VirtualDisplay",
"WifiDisplayStatus",
"WifiDisplay",
]
@@ -0,0 +1,179 @@
"""
Android hardware.fingerprint 组件 — FingerprintManager, Fingerprint 等
方法签名完全对齐 Android SDK
"""
from typing import Any, Callable, Dict, List, Optional
from .._core import Stub, AndroidOnlyError
from ..java.lang import RuntimeException
IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {})
# ═══════════════════════════════════════════════════════════════
# FingerprintManager
# ═══════════════════════════════════════════════════════════════
class FingerprintManager(Stub):
"""FingerprintManager — 指纹管理器"""
FINGERPRINT_ACQUIRED_GOOD = 0
FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3
FINGERPRINT_ACQUIRED_INSUFFICIENT = 2
FINGERPRINT_ACQUIRED_PARTIAL = 1
FINGERPRINT_ACQUIRED_TOO_FAST = 5
FINGERPRINT_ACQUIRED_TOO_SLOW = 4
FINGERPRINT_ERROR_HW_UNAVAILABLE = 1
FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2
FINGERPRINT_ERROR_TIMEOUT = 3
FINGERPRINT_ERROR_NO_SPACE = 4
FINGERPRINT_ERROR_CANCELED = 5
FINGERPRINT_ERROR_LOCKOUT = 7
FINGERPRINT_ERROR_VENDOR = 8
FINGERPRINT_ERROR_USER_CANCELED = 10
FINGERPRINT_ERROR_BAD_CALIBRATION = 11
FINGERPRINT_ERROR_POWER_PRESS = 12
FINGERPRINT_ERROR_IDLE_NOT_ENROLLED = 13
# ═══════════════════════════════════════════════════════════
# AuthenticationCallback
# ═══════════════════════════════════════════════════════════
class AuthenticationCallback(Stub):
"""FingerprintManager.AuthenticationCallback — 认证回调"""
def onAuthenticationError(self, errorCode: int, errString: str):
pass
def onAuthenticationHelp(self, helpCode: int, helpString: str):
pass
def onAuthenticationSucceeded(self, result: "FingerprintManager.AuthenticationResult"):
pass
def onAuthenticationFailed(self):
pass
def onAuthenticationAcquired(self, acquireInfo: int):
pass
# ═══════════════════════════════════════════════════════════
# AuthenticationResult
# ═══════════════════════════════════════════════════════════
class AuthenticationResult(Stub):
"""FingerprintManager.AuthenticationResult — 认证结果"""
def __init__(self, fingerprint: "Fingerprint" = None,
cryptoObject: "FingerprintManager.CryptoObject" = None):
super().__init__()
self._fingerprint = fingerprint
self._crypto_object = cryptoObject
def getFingerprint(self) -> Optional["Fingerprint"]:
return self._fingerprint
def getCryptoObject(self) -> Optional["FingerprintManager.CryptoObject"]:
return self._crypto_object
# ═══════════════════════════════════════════════════════════
# CryptoObject
# ═══════════════════════════════════════════════════════════
class CryptoObject(Stub):
"""FingerprintManager.CryptoObject — 加密对象"""
def __init__(self, cipher: Any = None, signature: Any = None, mac: Any = None):
super().__init__()
self._cipher = cipher
self._signature = signature
self._mac = mac
def getCipher(self) -> Any:
return self._cipher
def getSignature(self) -> Any:
return self._signature
def getMac(self) -> Any:
return self._mac
def __init__(self):
super().__init__()
self._enrolled_fingerprints: List[Fingerprint] = []
self._callbacks: Dict[int, FingerprintManager.AuthenticationCallback] = {}
def isHardwareDetected(self) -> bool:
return False
def hasEnrolledFingerprints(self) -> bool:
return len(self._enrolled_fingerprints) > 0
def authenticate(self, crypto: Optional[CryptoObject] = None,
cancel: Any = None, flags: int = 0,
callback: Optional[AuthenticationCallback] = None,
handler: Any = None):
if callback:
callback.onAuthenticationError(
FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE,
"Fingerprint hardware not available on desktop"
)
def enroll(self, token: Any, userId: int = 0,
callback: Optional[AuthenticationCallback] = None):
raise AndroidOnlyError("FingerprintManager.enroll() is not available on desktop")
def remove(self, fingerprint: "Fingerprint", userId: int = 0):
if fingerprint in self._enrolled_fingerprints:
self._enrolled_fingerprints.remove(fingerprint)
def rename(self, fingerprint: "Fingerprint", newName: str):
if fingerprint:
fingerprint._name = newName
def getEnrolledFingerprints(self) -> List["Fingerprint"]:
return list(self._enrolled_fingerprints)
# ═══════════════════════════════════════════════════════════════
# Fingerprint
# ═══════════════════════════════════════════════════════════════
class Fingerprint(Stub):
"""Fingerprint — 指纹数据"""
def __init__(self, name: str = "", fingerId: int = 0, groupId: int = 0,
deviceId: int = 0):
super().__init__()
self._name = name
self._finger_id = fingerId
self._group_id = groupId
self._device_id = deviceId
def getName(self) -> str:
return self._name
def getFingerId(self) -> int:
return self._finger_id
def getGroupId(self) -> int:
return self._group_id
def getDeviceId(self) -> int:
return self._device_id
def toString(self) -> str:
return f"Fingerprint(name={self._name}, fingerId={self._finger_id})"
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"FingerprintManager",
"Fingerprint",
]
+254
View File
@@ -0,0 +1,254 @@
"""
Android hardware.input 组件 — InputManager, InputDevice, MotionRange 等
方法签名完全对齐 Android SDK
"""
import sys
from typing import Any, Callable, Dict, List, Optional
from .._core import Stub, AndroidOnlyError
# ═══════════════════════════════════════════════════════════════
# InputManager
# ═══════════════════════════════════════════════════════════════
class InputManager(Stub):
"""InputManager — 输入管理器"""
ACTION_QUERY_KEYBOARD_LAYOUTS = "android.hardware.input.action.QUERY_KEYBOARD_LAYOUTS"
# ═══════════════════════════════════════════════════════════
# InputDeviceListener
# ═══════════════════════════════════════════════════════════
class InputDeviceListener(Stub):
"""InputManager.InputDeviceListener — 输入设备监听器"""
def onInputDeviceAdded(self, deviceId: int):
pass
def onInputDeviceRemoved(self, deviceId: int):
pass
def onInputDeviceChanged(self, deviceId: int):
pass
def __init__(self):
super().__init__()
self._devices: Dict[int, InputDevice] = {}
self._listeners: Dict[int, InputManager.InputDeviceListener] = {}
self._next_id = 1
self._init_default_devices()
def _init_default_devices(self):
# Keyboard
kb = InputDevice(
deviceId=0, name="Virtual Keyboard",
descriptor="keyboard_v1", sources=InputDevice.SOURCE_KEYBOARD,
vendorId=0x1, productId=0x1,
keyboardType=InputDevice.KEYBOARD_TYPE_ALPHABETIC,
hasMicrophone=False, isVirtual=True
)
self._devices[0] = kb
# Mouse
mouse = InputDevice(
deviceId=1, name="Virtual Mouse",
descriptor="mouse_v1", sources=InputDevice.SOURCE_MOUSE,
vendorId=0x2, productId=0x2,
keyboardType=InputDevice.KEYBOARD_TYPE_NONE,
hasMicrophone=False, isVirtual=True
)
self._devices[1] = mouse
# Trackpad
tp = InputDevice(
deviceId=2, name="Virtual Trackpad",
descriptor="trackpad_v1", sources=InputDevice.SOURCE_TOUCHPAD,
vendorId=0x3, productId=0x3,
keyboardType=InputDevice.KEYBOARD_TYPE_NONE,
hasMicrophone=False, isVirtual=False
)
self._devices[2] = tp
def getDevice(self, deviceId: int) -> Optional["InputDevice"]:
return self._devices.get(deviceId)
def getDeviceIds(self) -> List[int]:
return list(self._devices.keys())
def registerInputDeviceListener(self, listener: InputDeviceListener, handler: Any = None):
lid = id(listener)
self._listeners[lid] = listener
def unregisterInputDeviceListener(self, listener: InputDeviceListener):
lid = id(listener)
self._listeners.pop(lid, None)
def getInputDevice(self, deviceId: int) -> Optional["InputDevice"]:
return self.getDevice(deviceId)
def isInTabletMode(self) -> bool:
return False
def getCurrentKeyboardLayoutForInputDevice(self, device: "InputDevice") -> Optional[str]:
return None
def setKeyboardLayoutForInputDevice(self, device: "InputDevice",
keyboardLayoutDescriptor: str):
pass
# ═══════════════════════════════════════════════════════════════
# InputDevice
# ═══════════════════════════════════════════════════════════════
class InputDevice(Stub):
"""InputDevice — 输入设备"""
KEYBOARD_TYPE_ALPHABETIC = 2
KEYBOARD_TYPE_NON_ALPHABETIC = 1
KEYBOARD_TYPE_NONE = 0
SOURCE_CLASS_JOYSTICK = 16
SOURCE_CLASS_MASK = 0x000000FF
SOURCE_CLASS_NONE = 0x00000000
SOURCE_CLASS_POINTER = 0x00000002
SOURCE_CLASS_POSITION = 0x00000008
SOURCE_CLASS_TRACKBALL = 0x00000004
SOURCE_CLASS_BUTTON = 0x00000001
SOURCE_GAMEPAD = 0x00000401
SOURCE_HDMI = 0x02000000
SOURCE_JOYSTICK = 0x01000010
SOURCE_KEYBOARD = 0x00000101
SOURCE_MOUSE = 0x00002002
SOURCE_MOUSE_RELATIVE = 0x00002000
SOURCE_ROTARY_ENCODER = 0x00800000
SOURCE_STYLUS = 0x00004002
SOURCE_TOUCHPAD = 0x00100008
SOURCE_TOUCHSCREEN = 0x00001002
SOURCE_TOUCH_NAVIGATION = 0x00200008
SOURCE_TRACKBALL = 0x00010004
SOURCE_UNKNOWN = 0x00000000
SOURCE_ANY = 0xFFFFFFFE
# ═══════════════════════════════════════════════════════════
# MotionRange
# ═══════════════════════════════════════════════════════════
class MotionRange(Stub):
"""InputDevice.MotionRange — 运动范围"""
def __init__(self, axis: int = 0, source: int = 0,
min: float = 0.0, max: float = 1.0,
flat: float = 0.0, fuzz: float = 0.0,
resolution: float = 0.0):
super().__init__()
self._axis = axis
self._source = source
self._min = min
self._max = max
self._flat = flat
self._fuzz = fuzz
self._resolution = resolution
def getAxis(self) -> int:
return self._axis
def getSource(self) -> int:
return self._source
def getMin(self) -> float:
return self._min
def getMax(self) -> float:
return self._max
def getFlat(self) -> float:
return self._flat
def getFuzz(self) -> float:
return self._fuzz
def getRange(self) -> float:
return self._max - self._min
def getResolution(self) -> float:
return self._resolution
def toString(self) -> str:
return f"MotionRange(axis={self._axis}, source={self._source}," \
f" range=[{self._min}, {self._max}])"
def __init__(self, deviceId: int = 0, name: str = "", descriptor: str = "",
sources: int = 0, vendorId: int = 0, productId: int = 0,
controllerNumber: int = 0, keyboardType: int = KEYBOARD_TYPE_NONE,
hasMicrophone: bool = False, isVirtual: bool = False):
super().__init__()
self._device_id = deviceId
self._name = name
self._descriptor = descriptor
self._sources = sources
self._vendor_id = vendorId
self._product_id = productId
self._controller_number = controllerNumber
self._keyboard_type = keyboardType
self._has_microphone = hasMicrophone
self._is_virtual = isVirtual
self._motion_ranges: List[InputDevice.MotionRange] = []
def getId(self) -> int:
return self._device_id
def getName(self) -> str:
return self._name
def getDescriptor(self) -> str:
return self._descriptor
def getSources(self) -> int:
return self._sources
def getVendorId(self) -> int:
return self._vendor_id
def getProductId(self) -> int:
return self._product_id
def getControllerNumber(self) -> int:
return self._controller_number
def getKeyCharacterMap(self) -> Any:
return None
def getMotionRange(self, axis: int = -1, source: int = -1) -> List[MotionRange]:
results = []
for mr in self._motion_ranges:
if axis >= 0 and mr.getAxis() != axis:
continue
if source >= 0 and mr.getSource() != source:
continue
results.append(mr)
return results
def hasMicrophone(self) -> bool:
return self._has_microphone
def isVirtual(self) -> bool:
return self._is_virtual
def toString(self) -> str:
return f"InputDevice(id={self._device_id}, name={self._name}," \
f" sources={self._sources:#x})"
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"InputManager",
"InputDevice",
]
+409
View File
@@ -0,0 +1,409 @@
from __future__ import annotations
"""
Android hardware.usb 组件 — UsbManager, UsbDevice, UsbDeviceConnection 等
方法签名完全对齐 Android SDK
"""
from typing import Any, Dict, List, Optional, Set
from .._core import Stub, AndroidOnlyError
# ═══════════════════════════════════════════════════════════════
# UsbConstants
# ═══════════════════════════════════════════════════════════════
class UsbConstants(Stub):
"""UsbConstants — USB 常量"""
USB_DIR_IN = 0x80
USB_DIR_OUT = 0
USB_TYPE_MASK = 0x60
USB_TYPE_CLASS = 0x20
USB_TYPE_VENDOR = 0x40
USB_TYPE_STANDARD = 0x00
USB_ENDPOINT_XFER_CONTROL = 0
USB_ENDPOINT_XFER_ISOC = 1
USB_ENDPOINT_XFER_BULK = 2
USB_ENDPOINT_XFER_INT = 3
USB_CLASS_APP_SPEC = 0xFE
USB_CLASS_AUDIO = 0x01
USB_CLASS_CDC_DATA = 0x0A
USB_CLASS_COMM = 0x02
USB_CLASS_CONTENT_SEC = 0x0D
USB_CLASS_CSCID = 0x0B
USB_CLASS_HID = 0x03
USB_CLASS_HUB = 0x09
USB_CLASS_MASS_STORAGE = 0x08
USB_CLASS_MISC = 0xEF
USB_CLASS_PER_INTERFACE = 0x00
USB_CLASS_PHYSICA = 0x05
USB_CLASS_PRINTER = 0x07
USB_CLASS_STILL_IMAGE = 0x06
USB_CLASS_VENDOR_SPEC = 0xFF
USB_CLASS_VIDEO = 0x0E
USB_CLASS_WIRELESS = 0xE0
# ═══════════════════════════════════════════════════════════════
# UsbManager
# ═══════════════════════════════════════════════════════════════
class UsbManager(Stub):
"""UsbManager — USB 管理器"""
ACTION_USB_DEVICE_ATTACHED = "android.hardware.usb.action.USB_DEVICE_ATTACHED"
ACTION_USB_DEVICE_DETACHED = "android.hardware.usb.action.USB_DEVICE_DETACHED"
ACTION_USB_ACCESSORY_ATTACHED = "android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
ACTION_USB_ACCESSORY_DETACHED = "android.hardware.usb.action.USB_ACCESSORY_DETACHED"
EXTRA_DEVICE = "device"
EXTRA_ACCESSORY = "accessory"
EXTRA_PERMISSION_GRANTED = "permission"
def __init__(self):
super().__init__()
self._devices: Dict[str, UsbDevice] = {}
self._permissions: Set[str] = set()
self._accessories: List[UsbAccessory] = []
self._connections: Dict[str, UsbDeviceConnection] = {}
# Create some mock devices
self._devices["001"] = UsbDevice(
deviceId=1, deviceName="/dev/bus/usb/001/001",
vendorId=0x18D1, productId=0x4EE1,
deviceClass=UsbConstants.USB_CLASS_MISC,
deviceSubclass=0, deviceProtocol=0,
manufacturerName="Google", productName="Pixel Device",
version="3.1", serialNumber="ABC123"
)
def getDeviceList(self) -> Dict[str, UsbDevice]:
return dict(self._devices)
def openDevice(self, device: "UsbDevice") -> Optional["UsbDeviceConnection"]:
if not device:
return None
conn = UsbDeviceConnection(device)
self._connections[str(device.getDeviceId())] = conn
return conn
def hasPermission(self, device: "UsbDevice") -> bool:
return device.getDeviceName() in self._permissions
def requestPermission(self, device: "UsbDevice"):
self._permissions.add(device.getDeviceName())
def grantPermission(self, device: "UsbDevice"):
self._permissions.add(device.getDeviceName())
def revokePermission(self, device: "UsbDevice"):
self._permissions.discard(device.getDeviceName())
def setCurrentFunction(self, function: str, usbDataUnlocked: bool = False):
pass
def setUsbDataUnlocked(self, unlocked: bool):
pass
def getCurrentAccessory(self) -> Optional["UsbAccessory"]:
return self._accessories[0] if self._accessories else None
def openAccessory(self, accessory: "UsbAccessory") -> Optional["UsbDeviceConnection"]:
if not accessory:
return None
conn = UsbDeviceConnection(accessory)
return conn
# ═══════════════════════════════════════════════════════════════
# UsbDevice
# ═══════════════════════════════════════════════════════════════
class UsbDevice(Stub):
"""UsbDevice — USB 设备"""
def __init__(self, deviceId: int = 0, deviceName: str = "",
vendorId: int = 0, productId: int = 0,
deviceClass: int = 0, deviceSubclass: int = 0,
deviceProtocol: int = 0, configurationCount: int = 1,
interfaceCount: int = 0,
manufacturerName: str = "", productName: str = "",
version: str = "", serialNumber: str = ""):
super().__init__()
self._device_id = deviceId
self._device_name = deviceName
self._vendor_id = vendorId
self._product_id = productId
self._device_class = deviceClass
self._device_subclass = deviceSubclass
self._device_protocol = deviceProtocol
self._configuration_count = configurationCount
self._interface_count = interfaceCount
self._manufacturer_name = manufacturerName
self._product_name = productName
self._version = version
self._serial_number = serialNumber
self._interfaces: List[UsbInterface] = []
def getDeviceId(self) -> int:
return self._device_id
def getDeviceName(self) -> str:
return self._device_name
def getVendorId(self) -> int:
return self._vendor_id
def getProductId(self) -> int:
return self._product_id
def getDeviceClass(self) -> int:
return self._device_class
def getDeviceSubclass(self) -> int:
return self._device_subclass
def getDeviceProtocol(self) -> int:
return self._device_protocol
def getConfigurationCount(self) -> int:
return self._configuration_count
def getInterfaceCount(self) -> int:
return len(self._interfaces)
def getInterface(self, index: int) -> Optional["UsbInterface"]:
if 0 <= index < len(self._interfaces):
return self._interfaces[index]
return None
def getInterfaces(self) -> List["UsbInterface"]:
return list(self._interfaces)
def getManufacturerName(self) -> str:
return self._manufacturer_name
def getProductName(self) -> str:
return self._product_name
def getVersion(self) -> str:
return self._version
def getSerialNumber(self) -> str:
return self._serial_number
def describeContents(self) -> int:
return 0
def equals(self, other: Any) -> bool:
if not isinstance(other, UsbDevice):
return False
return self._device_id == other._device_id
def hashCode(self) -> int:
return hash(self._device_id) & 0x7FFFFFFF
def toString(self) -> str:
return f"UsbDevice(name={self._device_name}, vid={self._vendor_id:x}," \
f" pid={self._product_id:x})"
# ═══════════════════════════════════════════════════════════════
# UsbDeviceConnection
# ═══════════════════════════════════════════════════════════════
class UsbDeviceConnection(Stub):
"""UsbDeviceConnection — USB 设备连接"""
def __init__(self, device: Any):
super().__init__()
self._device = device
self._claimed_interfaces: Set[int] = set()
self._fd = -1
def claimInterface(self, intf: "UsbInterface", force: bool = False) -> bool:
self._claimed_interfaces.add(intf.getId())
return True
def releaseInterface(self, intf: "UsbInterface") -> bool:
self._claimed_interfaces.discard(intf.getId())
return True
def bulkTransfer(self, endpoint: "UsbEndpoint", buffer: bytes,
length: int, timeout: int) -> int:
return -1
def controlTransfer(self, requestType: int, request: int,
value: int, index: int, buffer: bytes,
length: int, timeout: int) -> int:
return -1
def getRawDescriptors(self) -> bytes:
return b""
def getFileDescriptor(self) -> int:
return self._fd
def serial(self) -> Optional[str]:
if hasattr(self._device, 'getSerialNumber'):
return self._device.getSerialNumber()
return None
def close(self):
self._claimed_interfaces.clear()
def toString(self) -> str:
return f"UsbDeviceConnection(device={self._device})"
# ═══════════════════════════════════════════════════════════════
# UsbInterface
# ═══════════════════════════════════════════════════════════════
class UsbInterface(Stub):
"""UsbInterface — USB 接口"""
def __init__(self, id: int = 0, alternateSetting: int = 0,
name: str = "", interfaceClass: int = 0,
interfaceSubclass: int = 0, interfaceProtocol: int = 0):
super().__init__()
self._id = id
self._alternate_setting = alternateSetting
self._name = name
self._interface_class = interfaceClass
self._interface_subclass = interfaceSubclass
self._interface_protocol = interfaceProtocol
self._endpoints: List[UsbEndpoint] = []
def getId(self) -> int:
return self._id
def getAlternateSetting(self) -> int:
return self._alternate_setting
def getName(self) -> str:
return self._name
def getInterfaceClass(self) -> int:
return self._interface_class
def getInterfaceSubclass(self) -> int:
return self._interface_subclass
def getInterfaceProtocol(self) -> int:
return self._interface_protocol
def getEndpointCount(self) -> int:
return len(self._endpoints)
def getEndpoint(self, index: int) -> Optional["UsbEndpoint"]:
if 0 <= index < len(self._endpoints):
return self._endpoints[index]
return None
def toString(self) -> str:
return f"UsbInterface(id={self._id}, name={self._name})"
# ═══════════════════════════════════════════════════════════════
# UsbEndpoint
# ═══════════════════════════════════════════════════════════════
class UsbEndpoint(Stub):
"""UsbEndpoint — USB 端点"""
def __init__(self, address: int = 0, endpointNumber: int = 0,
attributes: int = 0, direction: int = 0,
maxPacketSize: int = 64, interval: int = 0,
type_: int = 0):
super().__init__()
self._address = address
self._endpoint_number = endpointNumber
self._attributes = attributes
self._direction = direction
self._max_packet_size = maxPacketSize
self._interval = interval
self._type = type_
def getAddress(self) -> int:
return self._address
def getEndpointNumber(self) -> int:
return self._endpoint_number
def getAttributes(self) -> int:
return self._attributes
def getDirection(self) -> int:
return self._direction
def getMaxPacketSize(self) -> int:
return self._max_packet_size
def getInterval(self) -> int:
return self._interval
def getType(self) -> int:
return self._type
def toString(self) -> str:
return f"UsbEndpoint(addr={self._address:#04x}, type={self._type})"
# ═══════════════════════════════════════════════════════════════
# UsbAccessory
# ═══════════════════════════════════════════════════════════════
class UsbAccessory(Stub):
"""UsbAccessory — USB 附件"""
def __init__(self, manufacturer: str = "", model: str = "",
description: str = "", version: str = "",
uri: str = "", serial: str = ""):
super().__init__()
self._manufacturer = manufacturer
self._model = model
self._description = description
self._version = version
self._uri = uri
self._serial = serial
def getManufacturer(self) -> str:
return self._manufacturer
def getModel(self) -> str:
return self._model
def getDescription(self) -> str:
return self._description
def getVersion(self) -> str:
return self._version
def getUri(self) -> str:
return self._uri
def getSerial(self) -> str:
return self._serial
def toString(self) -> str:
return f"UsbAccessory(manufacturer={self._manufacturer}, model={self._model})"
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"UsbManager",
"UsbDevice",
"UsbDeviceConnection",
"UsbInterface",
"UsbEndpoint",
"UsbConstants",
"UsbAccessory",
]
+547
View File
@@ -0,0 +1,547 @@
"""
Android media.audiofx 组件 — AudioEffect, Equalizer, BassBoost, Virtualizer 等
方法签名完全对齐 Android SDK
"""
import math
from typing import Any, Callable, Dict, List, Optional, Tuple
from .._core import Stub, AndroidOnlyError
# ═══════════════════════════════════════════════════════════════
# AudioEffect
# ═══════════════════════════════════════════════════════════════
class AudioEffect(Stub):
"""AudioEffect — 音频效果基类"""
EFFECT_SUCCESS = 0
EFFECT_ERROR = -1
EFFECT_ALREADY_EXISTS = -2
EFFECT_NO_MORE_SESSION = -3
EFFECT_REQUEST_INIT = -4
EFFECT_INIT_FAILED = -5
EFFECT_INSERT = 1
EFFECT_AUXILIARY = 2
EFFECT_PRE_PROCESSING = 4
EFFECT_POST_PROCESSING = 8
CONTENT_TYPE_MUSIC = 2
CONTENT_TYPE_MOVIE = 3
CONTENT_TYPE_GAME = 4
CONTENT_TYPE_VOICE = 1
# ═══════════════════════════════════════════════════════════
# Descriptor
# ═══════════════════════════════════════════════════════════
class Descriptor(Stub):
"""AudioEffect.Descriptor — 效果描述符"""
def __init__(self, type: str = "", uuid: str = "",
connectMode: str = "", name: str = "",
implementor: str = ""):
super().__init__()
self.type = type
self.uuid = uuid
self.connectMode = connectMode
self.name = name
self.implementor = implementor
def toString(self) -> str:
return f"Descriptor(type={self.type}, uuid={self.uuid}, name={self.name})"
def __init__(self, effectType: str = "", priority: int = 0,
audioSession: int = 0):
super().__init__()
self._enabled = False
self._effect_type = effectType
self._priority = priority
self._audio_session = audioSession
self._id = hash(self) & 0x7FFFFFFF
self._control_status_listener: Optional[Callable] = None
self._enable_status_listener: Optional[Callable] = None
def getEnabled(self) -> bool:
return self._enabled
def setEnabled(self, enabled: bool) -> int:
self._enabled = enabled
return AudioEffect.EFFECT_SUCCESS
def getId(self) -> int:
return self._id
def getDescriptor(self) -> Descriptor:
return AudioEffect.Descriptor(
type=self._effect_type,
uuid="",
connectMode="",
name=self.__class__.__name__,
implementor="Android-pyApi"
)
def release(self):
self._enabled = False
def setControlStatusListener(self, listener: Callable):
self._control_status_listener = listener
def setEnableStatusListener(self, listener: Callable):
self._enable_status_listener = listener
# ═══════════════════════════════════════════════════════════════
# Equalizer
# ═══════════════════════════════════════════════════════════════
class Equalizer(AudioEffect):
"""Equalizer — 均衡器"""
PARAM_CURRENT_PRESET = 0
PARAM_GET_BAND = 1
PARAM_STRING_SIZE_MAX = 2
PARAM_BAND_LEVEL = 3
PARAM_LEVEL_RANGE = 4
PARAM_NUM_BANDS = 5
PARAM_BAND_FREQ_RANGE = 6
PARAM_CENTER_FREQ = 7
PARAM_GET_PRESET_NAME = 8
PARAM_NUM_PRESETS = 9
PARAM_PROPERTIES = 10
def __init__(self, priority: int = 0, audioSession: int = 0):
super().__init__("Equalizer", priority, audioSession)
self._num_bands = 5
self._band_levels: Dict[int, int] = {}
self._presets = [
"Normal", "Classical", "Dance", "Flat", "Folk",
"Heavy Metal", "Hip Hop", "Jazz", "Pop", "Rock"
]
self._current_preset = 0
self._band_freq_ranges: Dict[int, Tuple[int, int]] = {
0: (60, 200), 1: (200, 600), 2: (600, 2400),
3: (2400, 8000), 4: (8000, 16000)
}
def getNumberOfBands(self) -> int:
return self._num_bands
def getBand(self, frequency: int) -> int:
for band, (low, high) in self._band_freq_ranges.items():
if low <= frequency <= high:
return band
return 0
def getBandLevel(self, band: int) -> int:
return self._band_levels.get(band, 0)
def setBandLevel(self, band: int, level: int):
self._band_levels[band] = level
def getBandLevelRange(self) -> List[int]:
return [-1500, 1500]
def getCenterFreq(self, band: int) -> int:
if band in self._band_freq_ranges:
low, high = self._band_freq_ranges[band]
return (low + high) // 2
return 0
def getNumberOfPresets(self) -> int:
return len(self._presets)
def getPresetName(self, preset: int) -> str:
if 0 <= preset < len(self._presets):
return self._presets[preset]
return "Unknown"
def usePreset(self, preset: int):
if 0 <= preset < len(self._presets):
self._current_preset = preset
def getCurrentPreset(self) -> int:
return self._current_preset
def getProperties(self) -> Any:
return None
def setProperties(self, properties: Any):
pass
def toString(self) -> str:
return f"Equalizer(preset={self._presets[self._current_preset]}," \
f" bands={self._num_bands})"
# ═══════════════════════════════════════════════════════════════
# BassBoost
# ═══════════════════════════════════════════════════════════════
class BassBoost(AudioEffect):
"""BassBoost — 低音增强"""
PARAM_STRENGTH = 1
PARAM_STRENGTH_SUPPORTED = 2
def __init__(self, priority: int = 0, audioSession: int = 0):
super().__init__("BassBoost", priority, audioSession)
self._strength: int = 0
def setStrength(self, strength: int):
self._strength = max(0, min(1000, strength))
def getRoundedStrength(self) -> int:
return self._strength
def getStrengthSupported(self) -> bool:
return True
def toString(self) -> str:
return f"BassBoost(strength={self._strength})"
# ═══════════════════════════════════════════════════════════════
# Virtualizer
# ═══════════════════════════════════════════════════════════════
class Virtualizer(AudioEffect):
"""Virtualizer — 虚拟器 (空间音频)"""
PARAM_STRENGTH = 1
PARAM_STRENGTH_SUPPORTED = 2
PARAM_VIRTUALIZATION_MODE = 3
PARAM_FORCE_VIRTUALIZATION_MODE = 4
VIRTUALIZATION_MODE_AUTO = 0
VIRTUALIZATION_MODE_OFF = 1
VIRTUALIZATION_MODE_TRANSAURAL = 2
VIRTUALIZATION_MODE_BINAURAL = 3
def __init__(self, priority: int = 0, audioSession: int = 0):
super().__init__("Virtualizer", priority, audioSession)
self._strength: int = 0
self._virtualization_mode = Virtualizer.VIRTUALIZATION_MODE_AUTO
def setStrength(self, strength: int):
self._strength = max(0, min(1000, strength))
def getRoundedStrength(self) -> int:
return self._strength
def canVirtualize(self, channelMask: int, virtualizationMode: int) -> bool:
return True
def getVirtualizationMode(self) -> int:
return self._virtualization_mode
def toString(self) -> str:
return f"Virtualizer(strength={self._strength}, mode={self._virtualization_mode})"
# ═══════════════════════════════════════════════════════════════
# PresetReverb
# ═══════════════════════════════════════════════════════════════
class PresetReverb(AudioEffect):
"""PresetReverb — 预设混响"""
PRESET_NONE = 0
PRESET_SMALLROOM = 1
PRESET_MEDIUMROOM = 2
PRESET_LARGEROOM = 3
PRESET_MEDIUMHALL = 4
PRESET_LARGEHALL = 5
PRESET_PLATE = 6
PARAM_PRESET = 0
def __init__(self, priority: int = 0, audioSession: int = 0):
super().__init__("PresetReverb", priority, audioSession)
self._preset = PresetReverb.PRESET_NONE
self._preset_names = {
PresetReverb.PRESET_NONE: "None",
PresetReverb.PRESET_SMALLROOM: "Small Room",
PresetReverb.PRESET_MEDIUMROOM: "Medium Room",
PresetReverb.PRESET_LARGEROOM: "Large Room",
PresetReverb.PRESET_MEDIUMHALL: "Medium Hall",
PresetReverb.PRESET_LARGEHALL: "Large Hall",
PresetReverb.PRESET_PLATE: "Plate",
}
def setPreset(self, preset: int):
if preset in self._preset_names:
self._preset = preset
def getPreset(self) -> int:
return self._preset
def toString(self) -> str:
return f"PresetReverb(preset={self._preset_names.get(self._preset, 'Unknown')})"
# ═══════════════════════════════════════════════════════════════
# EnvironmentalReverb
# ═══════════════════════════════════════════════════════════════
class EnvironmentalReverb(AudioEffect):
"""EnvironmentalReverb — 环境混响"""
def __init__(self, priority: int = 0, audioSession: int = 0):
super().__init__("EnvironmentalReverb", priority, audioSession)
self._decay_time = 1500
self._decay_hf_ratio = 500
self._density = 500
self._diffusion = 500
self._reverb_level = 0
self._room_level = 0
self._room_hf_level = 0
self._reflections_delay = 20
self._reflections_level = -500
self._reverb_delay = 40
def setDecayTime(self, decayTime: int):
self._decay_time = decayTime
def getDecayTime(self) -> int:
return self._decay_time
def setDecayHFRatio(self, decayHFRatio: int):
self._decay_hf_ratio = decayHFRatio
def getDecayHFRatio(self) -> int:
return self._decay_hf_ratio
def setDensity(self, density: int):
self._density = density
def getDensity(self) -> int:
return self._density
def setDiffusion(self, diffusion: int):
self._diffusion = diffusion
def getDiffusion(self) -> int:
return self._diffusion
def setReverbLevel(self, reverbLevel: int):
self._reverb_level = reverbLevel
def getReverbLevel(self) -> int:
return self._reverb_level
def setRoomLevel(self, roomLevel: int):
self._room_level = roomLevel
def getRoomLevel(self) -> int:
return self._room_level
def setRoomHFLevel(self, roomHFLevel: int):
self._room_hf_level = roomHFLevel
def getRoomHFLevel(self) -> int:
return self._room_hf_level
def setReflectionsDelay(self, reflectionsDelay: int):
self._reflections_delay = reflectionsDelay
def getReflectionsDelay(self) -> int:
return self._reflections_delay
def setReflectionsLevel(self, reflectionsLevel: int):
self._reflections_level = reflectionsLevel
def getReflectionsLevel(self) -> int:
return self._reflections_level
def setReverbDelay(self, reverbDelay: int):
self._reverb_delay = reverbDelay
def getReverbDelay(self) -> int:
return self._reverb_delay
def toString(self) -> str:
return f"EnvironmentalReverb(decayTime={self._decay_time}," \
f" density={self._density})"
# ═══════════════════════════════════════════════════════════════
# LoudnessEnhancer
# ═══════════════════════════════════════════════════════════════
class LoudnessEnhancer(AudioEffect):
"""LoudnessEnhancer — 响度增强器"""
PARAM_TARGET_GAIN_MB = 0
def __init__(self, audioSession: int = 0):
super().__init__("LoudnessEnhancer", 0, audioSession)
self._target_gain = 0
def setTargetGain(self, gainMillibels: int):
self._target_gain = gainMillibels
def getTargetGain(self) -> int:
return self._target_gain
def toString(self) -> str:
return f"LoudnessEnhancer(targetGain={self._target_gain} mB)"
# ═══════════════════════════════════════════════════════════════
# DynamicsProcessing
# ═══════════════════════════════════════════════════════════════
class DynamicsProcessing(AudioEffect):
"""DynamicsProcessing — 动态处理"""
class Stage(Stub):
"""DynamicsProcessing.Stage — 处理阶段"""
def __init__(self, enabled: bool = True):
super().__init__()
self._enabled = enabled
self._in_use = True
def isEnabled(self) -> bool:
return self._enabled
def setEnabled(self, enabled: bool):
self._enabled = enabled
class EQBand(Stage):
"""DynamicsProcessing.EQBand — EQ 频段"""
def __init__(self, enabled: bool = True):
super().__init__(enabled)
self._gain = 0.0
self._center_freq = 1000.0
def setGain(self, gain: float):
self._gain = gain
def getGain(self) -> float:
return self._gain
def setCenterFreq(self, freq: float):
self._center_freq = freq
def getCenterFreq(self) -> float:
return self._center_freq
class EQ(Stage):
"""DynamicsProcessing.EQ — 均衡器"""
def __init__(self, enabled: bool = True, bandCount: int = 6):
super().__init__(enabled)
self._bands = [EQBand() for _ in range(bandCount)]
def getBand(self, band: int) -> "DynamicsProcessing.EQBand":
if 0 <= band < len(self._bands):
return self._bands[band]
raise IndexError(f"Band {band} out of range")
class MBCBand(Stage):
"""DynamicsProcessing.MBCBand — 多频段压缩频段"""
def __init__(self, enabled: bool = True):
super().__init__(enabled)
self._gain = 0.0
def setGain(self, gain: float):
self._gain = gain
def getGain(self) -> float:
return self._gain
class MBC(Stage):
"""DynamicsProcessing.MBC — 多频段压缩"""
def __init__(self, enabled: bool = True, bandCount: int = 6):
super().__init__(enabled)
self._bands = [MBCBand() for _ in range(bandCount)]
def getBand(self, band: int) -> "DynamicsProcessing.MBCBand":
if 0 <= band < len(self._bands):
return self._bands[band]
raise IndexError(f"Band {band} out of range")
class Limiter(Stage):
"""DynamicsProcessing.Limiter — 限制器"""
def __init__(self, enabled: bool = True):
super().__init__(enabled)
class Channel(Stub):
"""DynamicsProcessing.Channel — 处理通道"""
def __init__(self, stageCount: int = 1):
super().__init__()
self._stage_count = stageCount
class Config(Stub):
"""DynamicsProcessing.Config — 处理配置"""
def __init__(self, channelCount: int = 1, preEqCount: int = 1,
mbcCount: int = 1, postEqCount: int = 1,
limiterCount: int = 1, prefEqBandCount: int = 6,
postEqBandCount: int = 6, mbcBandCount: int = 6):
super().__init__()
self._channels = [DynamicsProcessing.Channel(0)
for _ in range(channelCount)]
def __init__(self, audioSession: int = 0):
super().__init__("DynamicsProcessing", 0, audioSession)
self._channel_count = 1
self._post_constraints: List[Any] = []
self._pre_eqs: List[DynamicsProcessing.EQ] = [DynamicsProcessing.EQ()]
self._post_eqs: List[DynamicsProcessing.EQ] = [DynamicsProcessing.EQ()]
self._mbcs: List[DynamicsProcessing.MBC] = [DynamicsProcessing.MBC()]
self._limiters: List[DynamicsProcessing.Limiter] = [DynamicsProcessing.Limiter()]
self._mbands: List[Any] = []
self._channel_stage: List[Any] = []
def getChannelCount(self) -> int:
return self._channel_count
def getPostConstraints(self) -> List[Any]:
return self._post_constraints
def getPreEQs(self) -> List[EQ]:
return self._pre_eqs
def getMbands(self) -> List[Any]:
return self._mbands
def getPostEQs(self) -> List[EQ]:
return self._post_eqs
def getLimiters(self) -> List[Limiter]:
return self._limiters
def getMbc(self) -> List[MBC]:
return self._mbcs
def getChannelStage(self) -> List[Any]:
return self._channel_stage
def toString(self) -> str:
return f"DynamicsProcessing(channels={self._channel_count})"
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"AudioEffect",
"Equalizer",
"BassBoost",
"Virtualizer",
"PresetReverb",
"EnvironmentalReverb",
"LoudnessEnhancer",
"DynamicsProcessing",
]
+310
View File
@@ -0,0 +1,310 @@
"""
Android media.browse 组件 — MediaBrowser, MediaItem, MediaDescription 等
方法签名完全对齐 Android SDK
"""
from typing import Any, Callable, Dict, List, Optional, Set
from .._core import Stub, AndroidOnlyError
# ═══════════════════════════════════════════════════════════════
# MediaBrowser
# ═══════════════════════════════════════════════════════════════
class MediaBrowser(Stub):
"""MediaBrowser — 媒体浏览器"""
EXTRA_PAGE = "android.media.browse.extra.PAGE"
EXTRA_PAGE_SIZE = "android.media.browse.extra.PAGE_SIZE"
# ═══════════════════════════════════════════════════════════
# ConnectionCallback
# ═══════════════════════════════════════════════════════════
class ConnectionCallback(Stub):
"""MediaBrowser.ConnectionCallback — 连接回调"""
def onConnected(self):
pass
def onConnectionSuspended(self):
pass
def onConnectionFailed(self):
pass
# ═══════════════════════════════════════════════════════════
# SubscriptionCallback
# ═══════════════════════════════════════════════════════════
class SubscriptionCallback(Stub):
"""MediaBrowser.SubscriptionCallback — 订阅回调"""
def onChildrenLoaded(self, parentId: str, children: List["MediaBrowser.MediaItem"]):
pass
def onError(self, parentId: str):
pass
def onChildrenLoadedWithOptions(self, parentId: str,
children: List["MediaBrowser.MediaItem"],
options: Any):
pass
# ═══════════════════════════════════════════════════════════
# ItemCallback
# ═══════════════════════════════════════════════════════════
class ItemCallback(Stub):
"""MediaBrowser.ItemCallback — 媒体项回调"""
def onItemLoaded(self, item: "MediaBrowser.MediaItem"):
pass
def onError(self, mediaId: str):
pass
# ═══════════════════════════════════════════════════════════
# MediaItem
# ═══════════════════════════════════════════════════════════
class MediaItem(Stub):
"""MediaBrowser.MediaItem — 媒体项"""
FLAG_BROWSABLE = 1
FLAG_PLAYABLE = 2
def __init__(self, description: "MediaDescription" = None, flags: int = FLAG_PLAYABLE):
super().__init__()
self._description = description or MediaDescription()
self._flags = flags
def getMediaId(self) -> Optional[str]:
return self._description.getMediaId()
def getDescription(self) -> "MediaDescription":
return self._description
def getFlags(self) -> int:
return self._flags
def isBrowsable(self) -> bool:
return bool(self._flags & MediaBrowser.MediaItem.FLAG_BROWSABLE)
def isPlayable(self) -> bool:
return bool(self._flags & MediaBrowser.MediaItem.FLAG_PLAYABLE)
def toString(self) -> str:
return f"MediaItem(mediaId={self.getMediaId()}, flags={self._flags})"
def __init__(self, context: Any = None, serviceComponent: Any = None,
connectionCallback: Optional[ConnectionCallback] = None,
rootHints: Any = None):
super().__init__()
self._connected = False
self._service_component = serviceComponent
self._root: str = ""
self._session_token: Any = None
self._connection_callback = connectionCallback
self._subscriptions: Dict[str, List[MediaBrowser.SubscriptionCallback]] = {}
self._media_tree: Dict[str, List[MediaBrowser.MediaItem]] = {}
self._item_cache: Dict[str, MediaBrowser.MediaItem] = {}
def connect(self):
self._connected = True
self._root = "__ROOT__"
# Build a simple in-memory media tree
self._media_tree["__ROOT__"] = [
MediaBrowser.MediaItem(
MediaDescription.Builder()
.setMediaId("__RECENT__")
.setTitle("Recently Played")
.setSubtitle("Your recently played media")
.build(),
flags=MediaBrowser.MediaItem.FLAG_BROWSABLE
),
MediaBrowser.MediaItem(
MediaDescription.Builder()
.setMediaId("__ALL__")
.setTitle("All Music")
.setSubtitle("All your music library")
.build(),
flags=MediaBrowser.MediaItem.FLAG_BROWSABLE
),
]
self._media_tree["__RECENT__"] = [
MediaBrowser.MediaItem(
MediaDescription.Builder()
.setMediaId("song_1")
.setTitle("Sample Song 1")
.setSubtitle("Artist 1")
.build(),
flags=MediaBrowser.MediaItem.FLAG_PLAYABLE
),
]
self._item_cache["song_1"] = self._media_tree["__RECENT__"][0]
if self._connection_callback:
self._connection_callback.onConnected()
def disconnect(self):
self._connected = False
self._subscriptions.clear()
if self._connection_callback:
self._connection_callback.onConnectionSuspended()
def getConnected(self) -> bool:
return self._connected
def getServiceComponent(self) -> Any:
return self._service_component
def getRoot(self) -> str:
return self._root
def isConnected(self) -> bool:
return self._connected
def getSessionToken(self) -> Any:
return self._session_token
def getItem(self, mediaId: str) -> Optional[MediaItem]:
return self._item_cache.get(mediaId)
def subscribe(self, parentId: str, callback: SubscriptionCallback):
if parentId not in self._subscriptions:
self._subscriptions[parentId] = []
self._subscriptions[parentId].append(callback)
children = self._media_tree.get(parentId, [])
if children:
callback.onChildrenLoaded(parentId, children)
else:
callback.onError(parentId)
def unsubscribe(self, parentId: str, callback: Optional[SubscriptionCallback] = None):
if parentId in self._subscriptions:
if callback:
self._subscriptions[parentId] = [
cb for cb in self._subscriptions[parentId]
if cb is not callback
]
if not self._subscriptions[parentId]:
del self._subscriptions[parentId]
else:
del self._subscriptions[parentId]
# ═══════════════════════════════════════════════════════════════
# MediaDescription
# ═══════════════════════════════════════════════════════════════
class MediaDescription(Stub):
"""MediaDescription — 媒体描述"""
class Builder(Stub):
"""MediaDescription.Builder — 媒体描述构建器"""
def __init__(self):
super().__init__()
self._media_id: Optional[str] = None
self._title: Optional[str] = None
self._subtitle: Optional[str] = None
self._description: Optional[str] = None
self._icon_bitmap: Any = None
self._icon_uri: Optional[str] = None
self._extras: Any = None
self._media_uri: Optional[str] = None
def setMediaId(self, mediaId: str) -> "MediaDescription.Builder":
self._media_id = mediaId
return self
def setTitle(self, title: str) -> "MediaDescription.Builder":
self._title = title
return self
def setSubtitle(self, subtitle: str) -> "MediaDescription.Builder":
self._subtitle = subtitle
return self
def setDescription(self, description: str) -> "MediaDescription.Builder":
self._description = description
return self
def setIconBitmap(self, icon: Any) -> "MediaDescription.Builder":
self._icon_bitmap = icon
return self
def setIconUri(self, iconUri: str) -> "MediaDescription.Builder":
self._icon_uri = iconUri
return self
def setExtras(self, extras: Any) -> "MediaDescription.Builder":
self._extras = extras
return self
def setMediaUri(self, mediaUri: str) -> "MediaDescription.Builder":
self._media_uri = mediaUri
return self
def build(self) -> "MediaDescription":
md = MediaDescription()
md._media_id = self._media_id
md._title = self._title
md._subtitle = self._subtitle
md._description = self._description
md._icon_bitmap = self._icon_bitmap
md._icon_uri = self._icon_uri
md._extras = self._extras
md._media_uri = self._media_uri
return md
def __init__(self):
super().__init__()
self._media_id: Optional[str] = None
self._title: Optional[str] = None
self._subtitle: Optional[str] = None
self._description: Optional[str] = None
self._icon_bitmap: Any = None
self._icon_uri: Optional[str] = None
self._extras: Any = None
self._media_uri: Optional[str] = None
def getMediaId(self) -> Optional[str]:
return self._media_id
def getTitle(self) -> Optional[str]:
return self._title
def getSubtitle(self) -> Optional[str]:
return self._subtitle
def getDescription(self) -> Optional[str]:
return self._description
def getIconBitmap(self) -> Any:
return self._icon_bitmap
def getIconUri(self) -> Optional[str]:
return self._icon_uri
def getExtras(self) -> Any:
return self._extras
def getMediaUri(self) -> Optional[str]:
return self._media_uri
def toString(self) -> str:
return f"MediaDescription(id={self._media_id}, title={self._title})"
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"MediaBrowser",
"MediaDescription",
]
+315
View File
@@ -0,0 +1,315 @@
"""
Android media.midi 组件 — MidiManager, MidiDevice, MidiReceiver, MidiSender 等
方法签名完全对齐 Android SDK
"""
from typing import Any, Callable, Dict, List, Optional, Set
from .._core import Stub, AndroidOnlyError
# ═══════════════════════════════════════════════════════════════
# MidiManager
# ═══════════════════════════════════════════════════════════════
class MidiManager(Stub):
"""MidiManager — MIDI 管理器"""
def __init__(self):
super().__init__()
self._devices: Dict[int, MidiDevice] = {}
self._callbacks: Dict[int, Callable] = {}
self._next_device_id = 1
self._init_default_devices()
def _init_default_devices(self):
info = MidiDeviceInfo(
id=0, inputPortCount=1, outputPortCount=1,
type=MidiDeviceInfo.TYPE_VIRTUAL,
properties={MidiDeviceInfo.PROPERTY_NAME: "Virtual MIDI"}
)
device = MidiDevice(info)
self._devices[0] = device
def getDevices(self) -> List["MidiDeviceInfo"]:
return [d.getInfo() for d in self._devices.values()]
def registerDeviceCallback(self, callback: Callable, handler: Any = None):
cid = id(callback)
self._callbacks[cid] = callback
def unregisterDeviceCallback(self, callback: Callable):
cid = id(callback)
self._callbacks.pop(cid, None)
def openDevice(self, deviceInfo: "MidiDeviceInfo",
callback: Callable, handler: Any = None):
device = self._devices.get(deviceInfo.getId())
if device:
callback(device)
def openBluetoothDevice(self, bluetoothDevice: Any) -> Optional["MidiDevice"]:
return None
def getDefaultDevice(self) -> Optional["MidiDevice"]:
for d in self._devices.values():
return d
return None
def deviceConnected(self, info: "MidiDeviceInfo"):
pass
def deviceDisconnected(self, info: "MidiDeviceInfo"):
pass
# ═══════════════════════════════════════════════════════════════
# MidiDevice
# ═══════════════════════════════════════════════════════════════
class MidiDevice(Stub):
"""MidiDevice — MIDI 设备"""
# ═══════════════════════════════════════════════════════════
# MidiConnection
# ═══════════════════════════════════════════════════════════
class MidiConnection(Stub):
"""MidiDevice.MidiConnection — MIDI 连接"""
def __init__(self):
super().__init__()
def close(self):
pass
def toString(self) -> str:
return "MidiConnection"
def __init__(self, info: "MidiDeviceInfo" = None):
super().__init__()
self._info = info or MidiDeviceInfo()
self._input_ports: List[MidiInputPort] = []
self._output_ports: List[MidiOutputPort] = []
self._closed = False
def getInfo(self) -> "MidiDeviceInfo":
return self._info
def openInputPort(self, portNumber: int) -> Optional["MidiInputPort"]:
if self._closed:
return None
port = MidiInputPort(portNumber)
self._input_ports.append(port)
return port
def openOutputPort(self, portNumber: int) -> Optional["MidiOutputPort"]:
if self._closed:
return None
port = MidiOutputPort(portNumber)
self._output_ports.append(port)
return port
def close(self):
self._closed = True
def toString(self) -> str:
return f"MidiDevice(info={self._info})"
# ═══════════════════════════════════════════════════════════════
# MidiDeviceInfo
# ═══════════════════════════════════════════════════════════════
class MidiDeviceInfo(Stub):
"""MidiDeviceInfo — MIDI 设备信息"""
TYPE_USB = 1
TYPE_BLUETOOTH = 2
TYPE_VIRTUAL = 3
PROPERTY_ALSA_CARD = "alsa_card"
PROPERTY_ALSA_DEVICE = "alsa_device"
PROPERTY_MANUFACTURER = "manufacturer"
PROPERTY_NAME = "name"
PROPERTY_PRODUCT = "product"
PROPERTY_SERIAL_NUMBER = "serial_number"
PROPERTY_USB_DEVICE = "usb_device"
def __init__(self, id: int = 0, inputPortCount: int = 0,
outputPortCount: int = 0, type_: int = TYPE_VIRTUAL,
properties: Optional[Dict[str, str]] = None):
super().__init__()
self._id = id
self._input_port_count = inputPortCount
self._output_port_count = outputPortCount
self._type = type_
self._properties = properties or {}
def getId(self) -> int:
return self._id
def getProperties(self) -> Dict[str, str]:
return dict(self._properties)
def getInputPortCount(self) -> int:
return self._input_port_count
def getOutputPortCount(self) -> int:
return self._output_port_count
def getType(self) -> int:
return self._type
def equals(self, other: Any) -> bool:
if not isinstance(other, MidiDeviceInfo):
return False
return self._id == other._id
def hashCode(self) -> int:
return hash(self._id) & 0x7FFFFFFF
def toString(self) -> str:
return f"MidiDeviceInfo(id={self._id}, type={self._type}," \
f" name={self._properties.get(MidiDeviceInfo.PROPERTY_NAME, '')})"
# ═══════════════════════════════════════════════════════════════
# MidiReceiver
# ═══════════════════════════════════════════════════════════════
class MidiReceiver(Stub):
"""MidiReceiver — MIDI 接收器"""
def __init__(self, maxMessageSize: int = 1024):
super().__init__()
self._max_message_size = maxMessageSize
def send(self, msg: bytes, offset: int, count: int):
self.onSend(msg, offset, count)
def onSend(self, msg: bytes, offset: int, count: int):
pass
def flush(self):
pass
def getMaxMessageSize(self) -> int:
return self._max_message_size
# ═══════════════════════════════════════════════════════════════
# MidiSender
# ═══════════════════════════════════════════════════════════════
class MidiSender(Stub):
"""MidiSender — MIDI 发送器"""
def __init__(self):
super().__init__()
self._receivers: List[MidiReceiver] = []
def send(self, msg: bytes, offset: int, count: int):
for r in self._receivers:
r.send(msg, offset, count)
def onConnect(self, receiver: MidiReceiver):
pass
def onDisconnect(self, receiver: MidiReceiver):
pass
def connect(self, receiver: MidiReceiver):
self._receivers.append(receiver)
self.onConnect(receiver)
def disconnect(self, receiver: MidiReceiver):
if receiver in self._receivers:
self._receivers.remove(receiver)
self.onDisconnect(receiver)
# ═══════════════════════════════════════════════════════════════
# MidiInputPort
# ═══════════════════════════════════════════════════════════════
class MidiInputPort(MidiReceiver):
"""MidiInputPort — MIDI 输入端口"""
def __init__(self, portNumber: int = 0):
super().__init__()
self._port_number = portNumber
self._closed = False
def send(self, msg: bytes, offset: int, count: int):
if not self._closed:
self.onSend(msg, offset, count)
def onSend(self, msg: bytes, offset: int, count: int):
pass
def flush(self):
pass
def close(self):
self._closed = True
# ═══════════════════════════════════════════════════════════════
# MidiOutputPort
# ═══════════════════════════════════════════════════════════════
class MidiOutputPort(MidiSender):
"""MidiOutputPort — MIDI 输出端口"""
def __init__(self, portNumber: int = 0):
super().__init__()
self._port_number = portNumber
def send(self, msg: bytes, offset: int, count: int):
super().send(msg, offset, count)
def onSend(self, msg: bytes, offset: int, count: int):
pass
def getPortNumber(self) -> int:
return self._port_number
# ═══════════════════════════════════════════════════════════════
# MidiEndpoint (stub)
# ═══════════════════════════════════════════════════════════════
class MidiEndpoint(Stub):
"""MidiEndpoint — MIDI 端点"""
def __init__(self):
super().__init__()
# ═══════════════════════════════════════════════════════════════
# MidiDeviceService
# ═══════════════════════════════════════════════════════════════
class MidiDeviceService(Stub):
"""MidiDeviceService — MIDI 设备服务"""
def __init__(self):
super().__init__()
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"MidiManager",
"MidiDevice",
"MidiDeviceInfo",
"MidiReceiver",
"MidiSender",
"MidiInputPort",
"MidiOutputPort",
"MidiEndpoint",
"MidiDeviceService",
]
+171
View File
@@ -0,0 +1,171 @@
"""
Android media.projection 组件 — MediaProjectionManager, MediaProjection, MediaProjectionInfo 等
方法签名完全对齐 Android SDK
"""
from typing import Any, Callable, Dict, List, Optional
from .._core import Stub, AndroidOnlyError
# ═══════════════════════════════════════════════════════════════
# MediaProjectionManager
# ═══════════════════════════════════════════════════════════════
class MediaProjectionManager(Stub):
"""MediaProjectionManager — 媒体投影管理器"""
def __init__(self):
super().__init__()
self._active_projections: Dict[int, "MediaProjection"] = {}
self._callbacks: Dict[int, Callable] = {}
def createScreenCaptureIntent(self) -> Any:
return "SCREEN_CAPTURE_INTENT"
def getMediaProjection(self, resultCode: int, data: Any) -> Optional["MediaProjection"]:
if resultCode == -1: # RESULT_OK
proj = MediaProjection()
self._active_projections[id(proj)] = proj
return proj
return None
def getActiveProjectionInfo(self) -> Optional["MediaProjectionInfo"]:
for proj in self._active_projections.values():
info = proj.getProjectionInfo()
if info:
return info
return None
def getDisplayProjection(self, displayId: int) -> Optional["MediaProjection"]:
return None
def addCallback(self, callback: Callable, handler: Any = None):
cid = id(callback)
self._callbacks[cid] = callback
def removeCallback(self, callback: Callable):
cid = id(callback)
self._callbacks.pop(cid, None)
def isMediaProjectionAllowed(self, packageName: str) -> bool:
return True
# ═══════════════════════════════════════════════════════════════
# MediaProjection
# ═══════════════════════════════════════════════════════════════
class MediaProjection(Stub):
"""MediaProjection — 媒体投影"""
# ═══════════════════════════════════════════════════════════
# Callback
# ═══════════════════════════════════════════════════════════
class Callback(Stub):
"""MediaProjection.Callback — 投影回调"""
def onStop(self):
pass
def onCapturedContentResize(self, width: int, height: int):
pass
def onCapturedContentVisibilityChanged(self, isVisible: bool):
pass
def __init__(self):
super().__init__()
self._stopped = False
self._callbacks: List[MediaProjection.Callback] = []
self._projection_info = MediaProjectionInfo(
packageName="com.android.systemui",
uid=1000,
isVirtualDisplay=False
)
def createVirtualDisplay(self, name: str, width: int, height: int,
densityDpi: int, flags: int, surface: Any,
callback: Callable = None, handler: Any = None) -> Any:
from .hardware_display import VirtualDisplay
vd = VirtualDisplay(99, name, width, height, densityDpi, surface, flags)
return vd
def registerCallback(self, callback: Callback):
self._callbacks.append(callback)
def unregisterCallback(self, callback: Callback):
if callback in self._callbacks:
self._callbacks.remove(callback)
def stop(self):
self._stopped = True
for cb in self._callbacks:
cb.onStop()
def getProjectionInfo(self) -> "MediaProjectionInfo":
return self._projection_info
def getDisplay(self) -> Optional[Any]:
from .hardware_display import Display
return Display(displayId=0, name="Projection Display")
def applyTransparentRegionOverlay(self, region: Any):
pass
def removeTransparentRegionOverlay(self):
pass
def toString(self) -> str:
return f"MediaProjection(stopped={self._stopped})"
# ═══════════════════════════════════════════════════════════════
# MediaProjectionInfo
# ═══════════════════════════════════════════════════════════════
class MediaProjectionInfo(Stub):
"""MediaProjectionInfo — 媒体投影信息"""
def __init__(self, packageName: str = "", uid: int = 0,
isVirtualDisplay: bool = False):
super().__init__()
self._package_name = packageName
self._uid = uid
self._is_virtual_display = isVirtualDisplay
def getProjectionType(self) -> int:
return 0
def getLaunchedFromPackage(self) -> str:
return self._package_name
def getLaunchedFromUid(self) -> int:
return self._uid
def isVirtualDisplay(self) -> bool:
return self._is_virtual_display
def equals(self, other: Any) -> bool:
if not isinstance(other, MediaProjectionInfo):
return False
return (self._package_name == other._package_name
and self._uid == other._uid)
def hashCode(self) -> int:
return hash((self._package_name, self._uid)) & 0x7FFFFFFF
def toString(self) -> str:
return f"MediaProjectionInfo(package={self._package_name}, uid={self._uid})"
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"MediaProjectionManager",
"MediaProjection",
"MediaProjectionInfo",
]
+854
View File
@@ -0,0 +1,854 @@
"""
Android media.session 组件 — MediaSession, MediaController, PlaybackState, MediaMetadata 等
方法签名完全对齐 Android SDK
"""
import time
from typing import Any, Callable, Dict, List, Optional, Set, Union
from .._core import Stub, AndroidOnlyError
# ═══════════════════════════════════════════════════════════════
# MediaSession
# ═══════════════════════════════════════════════════════════════
class MediaSession(Stub):
"""MediaSession — 媒体会话"""
FLAG_HANDLES_MEDIA_BUTTONS = 1
FLAG_HANDLES_TRANSPORT_CONTROLS = 2
FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 4
# ═══════════════════════════════════════════════════════════
# Callback
# ═══════════════════════════════════════════════════════════
class Callback(Stub):
"""MediaSession.Callback — 会话回调"""
def onPlay(self):
pass
def onPause(self):
pass
def onSkipToNext(self):
pass
def onSkipToPrevious(self):
pass
def onFastForward(self):
pass
def onRewind(self):
pass
def onStop(self):
pass
def onSeekTo(self, pos: int):
pass
def onSetRating(self, rating: "Rating"):
pass
def onSetRepeatMode(self, repeatMode: int):
pass
def onSetShuffleMode(self, shuffleMode: int):
pass
def onCommand(self, command: str, extras: Any, cb: Any):
pass
def onCustomAction(self, action: str, extras: Any):
pass
def onMediaButtonEvent(self, mediaButtonIntent: Any) -> bool:
return False
def onPlayFromMediaId(self, mediaId: str, extras: Any):
pass
def onPlayFromSearch(self, query: str, extras: Any):
pass
def onPlayFromUri(self, uri: str, extras: Any):
pass
def onPrepare(self):
pass
def onPrepareFromMediaId(self, mediaId: str, extras: Any):
pass
def onPrepareFromSearch(self, query: str, extras: Any):
pass
def onPrepareFromUri(self, uri: str, extras: Any):
pass
def __init__(self, context: Any = None, tag: str = "",
token: "SessionToken" = None):
super().__init__()
self._tag = tag
self._token = token or SessionToken()
self._active = False
self._flags = 0
self._session_activity: Any = None
self._callback: Optional[MediaSession.Callback] = None
self._metadata: Optional["MediaMetadata"] = None
self._playback_state: Optional["PlaybackState"] = None
self._queue: List["QueueItem"] = []
self._queue_title: Optional[str] = None
self._extras: Any = None
self._rating_type = Rating.RATING_NONE
self._repeat_mode = PlaybackState.REPEAT_MODE_NONE
self._shuffle_mode = PlaybackState.SHUFFLE_MODE_NONE
self._captioning_enabled = False
self._controllers: List[MediaController] = []
def setSessionActivity(self, sessionActivity: Any):
self._session_activity = sessionActivity
def sendSessionEvent(self, event: str, extras: Any):
for ctrl in self._controllers:
ctrl._callback.onSessionEvent(event, extras)
def release(self):
self._active = False
self._controllers.clear()
def getSessionToken(self) -> "SessionToken":
return self._token
def setFlags(self, flags: int):
self._flags = flags
def setPlaybackToLocal(self, volumeProvider: Any = None):
pass
def setPlaybackToRemote(self, volumeProvider: Any):
pass
def setActive(self, active: bool):
self._active = active
def isActive(self) -> bool:
return self._active
def setMediaMetadata(self, metadata: "MediaMetadata"):
self._metadata = metadata
for ctrl in self._controllers:
ctrl._callback.onMetadataChanged(metadata)
def setPlaybackState(self, state: "PlaybackState"):
self._playback_state = state
for ctrl in self._controllers:
ctrl._callback.onPlaybackStateChanged(state)
def setQueue(self, queue: List["QueueItem"]):
self._queue = queue
for ctrl in self._controllers:
ctrl._callback.onQueueChanged(queue)
def setQueueTitle(self, title: str):
self._queue_title = title
for ctrl in self._controllers:
ctrl._callback.onQueueTitleChanged(title)
def setExtras(self, extras: Any):
self._extras = extras
for ctrl in self._controllers:
ctrl._callback.onExtrasChanged(extras)
def setMetadata(self, metadata: "MediaMetadata"):
self.setMediaMetadata(metadata)
def setRatingType(self, ratingType: int):
self._rating_type = ratingType
def setRepeatMode(self, repeatMode: int):
self._repeat_mode = repeatMode
for ctrl in self._controllers:
ctrl._callback.onRepeatModeChanged(repeatMode)
def setShuffleMode(self, shuffleMode: int):
self._shuffle_mode = shuffleMode
for ctrl in self._controllers:
ctrl._callback.onShuffleModeChanged(shuffleMode)
def setCaptioningEnabled(self, enabled: bool):
self._captioning_enabled = enabled
for ctrl in self._controllers:
ctrl._callback.onCaptioningEnabledChanged(enabled)
def getController(self) -> "MediaController":
ctrl = MediaController(self)
self._controllers.append(ctrl)
return ctrl
def getCallerPackageName(self) -> str:
return "com.android.systemui"
def setCallback(self, callback: Callback, handler: Any = None):
self._callback = callback
# ═══════════════════════════════════════════════════════════════
# MediaController
# ═══════════════════════════════════════════════════════════════
class MediaController(Stub):
"""MediaController — 媒体控制器"""
# ═══════════════════════════════════════════════════════════
# Callback
# ═══════════════════════════════════════════════════════════
class Callback(Stub):
"""MediaController.Callback — 控制器回调"""
def onSessionReady(self):
pass
def onSessionDestroyed(self):
pass
def onSessionEvent(self, event: str, extras: Any):
pass
def onPlaybackStateChanged(self, state: "PlaybackState"):
pass
def onMetadataChanged(self, metadata: "MediaMetadata"):
pass
def onQueueChanged(self, queue: List["QueueItem"]):
pass
def onQueueTitleChanged(self, title: str):
pass
def onExtrasChanged(self, extras: Any):
pass
def onAudioInfoChanged(self, info: Any):
pass
def onCaptioningEnabledChanged(self, enabled: bool):
pass
def onRepeatModeChanged(self, repeatMode: int):
pass
def onShuffleModeChanged(self, shuffleMode: int):
pass
# ═══════════════════════════════════════════════════════════
# TransportControls
# ═══════════════════════════════════════════════════════════
class TransportControls(Stub):
"""MediaController.TransportControls — 传输控制"""
def __init__(self, session: "MediaSession"):
super().__init__()
self._session = session
def play(self):
if self._session._callback:
self._session._callback.onPlay()
def pause(self):
if self._session._callback:
self._session._callback.onPause()
def stop(self):
if self._session._callback:
self._session._callback.onStop()
def fastForward(self):
if self._session._callback:
self._session._callback.onFastForward()
def rewind(self):
if self._session._callback:
self._session._callback.onRewind()
def skipToNext(self):
if self._session._callback:
self._session._callback.onSkipToNext()
def skipToPrevious(self):
if self._session._callback:
self._session._callback.onSkipToPrevious()
def seekTo(self, pos: int):
if self._session._callback:
self._session._callback.onSeekTo(pos)
def setRating(self, rating: "Rating"):
if self._session._callback:
self._session._callback.onSetRating(rating)
def setRepeatMode(self, repeatMode: int):
if self._session._callback:
self._session._callback.onSetRepeatMode(repeatMode)
def setShuffleMode(self, shuffleMode: int):
if self._session._callback:
self._session._callback.onSetShuffleMode(shuffleMode)
def sendCustomAction(self, action: str, extras: Any):
if self._session._callback:
self._session._callback.onCustomAction(action, extras)
def playFromMediaId(self, mediaId: str, extras: Any):
if self._session._callback:
self._session._callback.onPlayFromMediaId(mediaId, extras)
def playFromSearch(self, query: str, extras: Any):
if self._session._callback:
self._session._callback.onPlayFromSearch(query, extras)
def playFromUri(self, uri: str, extras: Any):
if self._session._callback:
self._session._callback.onPlayFromUri(uri, extras)
def prepare(self):
if self._session._callback:
self._session._callback.onPrepare()
def prepareFromMediaId(self, mediaId: str, extras: Any):
if self._session._callback:
self._session._callback.onPrepareFromMediaId(mediaId, extras)
def prepareFromSearch(self, query: str, extras: Any):
if self._session._callback:
self._session._callback.onPrepareFromSearch(query, extras)
def prepareFromUri(self, uri: str, extras: Any):
if self._session._callback:
self._session._callback.onPrepareFromUri(uri, extras)
def __init__(self, session: "MediaSession" = None):
super().__init__()
self._session = session
self._callback = MediaController.Callback()
self._transport_controls = MediaController.TransportControls(session)
def getSessionToken(self) -> "SessionToken":
return self._session.getSessionToken() if self._session else SessionToken()
def getTransportControls(self) -> TransportControls:
return self._transport_controls
def getPlaybackState(self) -> Optional["PlaybackState"]:
return self._session._playback_state if self._session else None
def getMetadata(self) -> Optional["MediaMetadata"]:
return self._session._metadata if self._session else None
def getQueueTitle(self) -> Optional[str]:
return self._session._queue_title if self._session else None
def getQueue(self) -> List["QueueItem"]:
return self._session._queue if self._session else []
def getRepeatMode(self) -> int:
return self._session._repeat_mode if self._session else PlaybackState.REPEAT_MODE_NONE
def getShuffleMode(self) -> int:
return self._session._shuffle_mode if self._session else PlaybackState.SHUFFLE_MODE_NONE
def getRatingType(self) -> int:
return self._session._rating_type if self._session else Rating.RATING_NONE
def getExtras(self) -> Any:
return self._session._extras if self._session else None
def getFlags(self) -> int:
return self._session._flags if self._session else 0
def getPackageName(self) -> str:
return self._session.getCallerPackageName() if self._session else ""
def getSessionActivity(self) -> Any:
return self._session._session_activity if self._session else None
def registerCallback(self, callback: Callback, handler: Any = None):
self._callback = callback
def unregisterCallback(self, callback: Callback):
self._callback = MediaController.Callback()
def sendCommand(self, command: str, extras: Any, cb: Any):
if self._session and self._session._callback:
self._session._callback.onCommand(command, extras, cb)
def adjustVolume(self, direction: int, flags: int):
pass
def setVolumeTo(self, value: int, flags: int):
pass
def isSessionReady(self) -> bool:
return True
# ═══════════════════════════════════════════════════════════════
# PlaybackState
# ═══════════════════════════════════════════════════════════════
class PlaybackState(Stub):
"""PlaybackState — 播放状态"""
STATE_NONE = 0
STATE_STOPPED = 1
STATE_PAUSED = 2
STATE_PLAYING = 3
STATE_FAST_FORWARDING = 4
STATE_REWINDING = 5
STATE_BUFFERING = 6
STATE_ERROR = 7
STATE_CONNECTING = 8
STATE_SKIPPING_TO_PREVIOUS = 9
STATE_SKIPPING_TO_NEXT = 10
STATE_SKIPPING_TO_QUEUE_ITEM = 11
ACTION_PLAY = 1 << 0
ACTION_PAUSE = 1 << 1
ACTION_PLAY_PAUSE = 1 << 2
ACTION_STOP = 1 << 3
ACTION_SEEK_TO = 1 << 4
ACTION_FAST_FORWARD = 1 << 5
ACTION_REWIND = 1 << 6
ACTION_SKIP_TO_NEXT = 1 << 7
ACTION_SKIP_TO_PREVIOUS = 1 << 8
ACTION_SET_RATING = 1 << 9
ACTION_SET_REPEAT_MODE = 1 << 10
ACTION_SET_SHUFFLE_MODE = 1 << 11
ACTION_SET_CAPTIONING_ENABLED = 1 << 12
ACTION_SKIP_TO_QUEUE_ITEM = 1 << 13
ACTION_PLAY_FROM_MEDIA_ID = 1 << 14
ACTION_PLAY_FROM_SEARCH = 1 << 15
ACTION_PREPARE = 1 << 16
ACTION_PREPARE_FROM_MEDIA_ID = 1 << 17
ACTION_PREPARE_FROM_SEARCH = 1 << 18
ACTION_PREPARE_FROM_URI = 1 << 19
ACTION_PLAY_FROM_URI = 1 << 20
REPEAT_MODE_NONE = 0
REPEAT_MODE_ONE = 1
REPEAT_MODE_ALL = 2
REPEAT_MODE_GROUP = 3
SHUFFLE_MODE_NONE = 0
SHUFFLE_MODE_ALL = 1
SHUFFLE_MODE_GROUP = 2
class Builder(Stub):
"""PlaybackState.Builder — 播放状态构建器"""
def __init__(self):
super().__init__()
self._state = PlaybackState.STATE_NONE
self._position = 0
self._speed = 1.0
self._actions = 0
self._custom_actions: List[Any] = []
self._active_queue_item_id = -1
self._error_message: Optional[str] = None
self._extras: Any = None
self._buffered_position = 0
def setState(self, state: int, position: int, speed: float) -> "PlaybackState.Builder":
self._state = state
self._position = position
self._speed = speed
return self
def setActions(self, actions: int) -> "PlaybackState.Builder":
self._actions = actions
return self
def setCustomAction(self, action: str, name: str, icon: Any = None) -> "PlaybackState.Builder":
self._custom_actions.append((action, name, icon))
return self
def setActiveQueueItemId(self, id: int) -> "PlaybackState.Builder":
self._active_queue_item_id = id
return self
def setErrorMessage(self, error: str) -> "PlaybackState.Builder":
self._error_message = error
return self
def setExtras(self, extras: Any) -> "PlaybackState.Builder":
self._extras = extras
return self
def setBufferedPosition(self, bufferedPosition: int) -> "PlaybackState.Builder":
self._buffered_position = bufferedPosition
return self
def setPosition(self, position: int) -> "PlaybackState.Builder":
self._position = position
return self
def setSpeed(self, speed: float) -> "PlaybackState.Builder":
self._speed = speed
return self
def addCustomAction(self, action: "PlaybackState.CustomAction") -> "PlaybackState.Builder":
self._custom_actions.append(action)
return self
def build(self) -> "PlaybackState":
ps = PlaybackState()
ps._state = self._state
ps._position = self._position
ps._speed = self._speed
ps._actions = self._actions
ps._custom_actions = self._custom_actions
ps._active_queue_item_id = self._active_queue_item_id
ps._error_message = self._error_message
ps._extras = self._extras
ps._buffered_position = self._buffered_position
return ps
class CustomAction(Stub):
"""PlaybackState.CustomAction — 自定义操作"""
def __init__(self, action: str, name: str, icon: Any = None):
super().__init__()
self._action = action
self._name = name
self._icon = icon
def getAction(self) -> str:
return self._action
def getName(self) -> str:
return self._name
def getIcon(self) -> Any:
return self._icon
def __init__(self):
super().__init__()
self._state = PlaybackState.STATE_NONE
self._position = 0
self._speed = 1.0
self._actions = 0
self._custom_actions: List[Any] = []
self._active_queue_item_id = -1
self._error_message: Optional[str] = None
self._extras: Any = None
self._buffered_position = 0
self._last_position_update_time = int(time.time() * 1000)
def getState(self) -> int:
return self._state
def getPosition(self) -> int:
return self._position
def getPlaybackSpeed(self) -> float:
return self._speed
def getActions(self) -> int:
return self._actions
def getCustomActions(self) -> List[Any]:
return self._custom_actions
def getActiveQueueItemId(self) -> int:
return self._active_queue_item_id
def getErrorMessage(self) -> Optional[str]:
return self._error_message
def getExtras(self) -> Any:
return self._extras
def getBufferedPosition(self) -> int:
return self._buffered_position
def getLastPositionUpdateTime(self) -> int:
return self._last_position_update_time
def toString(self) -> str:
state_names = {
PlaybackState.STATE_NONE: "NONE",
PlaybackState.STATE_STOPPED: "STOPPED",
PlaybackState.STATE_PAUSED: "PAUSED",
PlaybackState.STATE_PLAYING: "PLAYING",
}
return f"PlaybackState(state={state_names.get(self._state, str(self._state))}," \
f" pos={self._position})"
# ═══════════════════════════════════════════════════════════════
# MediaMetadata
# ═══════════════════════════════════════════════════════════════
class MediaMetadata(Stub):
"""MediaMetadata — 媒体元数据"""
METADATA_KEY_ALBUM = "android.media.metadata.ALBUM"
METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART"
METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST"
METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI"
METADATA_KEY_ART = "android.media.metadata.ART"
METADATA_KEY_ARTIST = "android.media.metadata.ARTIST"
METADATA_KEY_ART_URI = "android.media.metadata.ART_URI"
METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR"
METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION"
METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER"
METADATA_KEY_DATE = "android.media.metadata.DATE"
METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER"
METADATA_KEY_DISPLAY_DESCRIPTION = "android.media.metadata.DISPLAY_DESCRIPTION"
METADATA_KEY_DISPLAY_ICON = "android.media.metadata.DISPLAY_ICON"
METADATA_KEY_DISPLAY_ICON_URI = "android.media.metadata.DISPLAY_ICON_URI"
METADATA_KEY_DISPLAY_SUBTITLE = "android.media.metadata.DISPLAY_SUBTITLE"
METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE"
METADATA_KEY_DURATION = "android.media.metadata.DURATION"
METADATA_KEY_GENRE = "android.media.metadata.GENRE"
METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID"
METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS"
METADATA_KEY_RATING = "android.media.metadata.RATING"
METADATA_KEY_TITLE = "android.media.metadata.TITLE"
METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER"
METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING"
METADATA_KEY_WRITER = "android.media.metadata.WRITER"
METADATA_KEY_YEAR = "android.media.metadata.YEAR"
class Builder(Stub):
"""MediaMetadata.Builder — 元数据构建器"""
def __init__(self):
super().__init__()
self._data: Dict[str, Any] = {}
def putString(self, key: str, value: str) -> "MediaMetadata.Builder":
self._data[key] = value
return self
def putLong(self, key: str, value: int) -> "MediaMetadata.Builder":
self._data[key] = value
return self
def putBitmap(self, key: str, value: Any) -> "MediaMetadata.Builder":
self._data[key] = value
return self
def putText(self, key: str, value: str) -> "MediaMetadata.Builder":
self._data[key] = value
return self
def putRating(self, key: str, value: "Rating") -> "MediaMetadata.Builder":
self._data[key] = value
return self
def build(self) -> "MediaMetadata":
mm = MediaMetadata()
mm._data = dict(self._data)
return mm
def __init__(self):
super().__init__()
self._data: Dict[str, Any] = {}
def getString(self, key: str) -> Optional[str]:
v = self._data.get(key)
return str(v) if v is not None else None
def getLong(self, key: str) -> int:
v = self._data.get(key, 0)
try:
return int(v)
except (ValueError, TypeError):
return 0
def getBitmap(self, key: str) -> Any:
return self._data.get(key)
def getText(self, key: str) -> Optional[str]:
return self.getString(key)
def getDescription(self) -> "MediaDescription":
from .media_browse import MediaDescription
builder = MediaDescription.Builder()
title = self.getString(MediaMetadata.METADATA_KEY_TITLE)
if title:
builder.setTitle(title)
artist = self.getString(MediaMetadata.METADATA_KEY_ARTIST)
if artist:
builder.setSubtitle(artist)
media_id = self.getString(MediaMetadata.METADATA_KEY_MEDIA_ID)
if media_id:
builder.setMediaId(media_id)
return builder.build()
def getRating(self, key: str) -> Optional["Rating"]:
v = self._data.get(key)
return v if isinstance(v, Rating) else None
def getBitmapDescription(self) -> Any:
return None
def containsKey(self, key: str) -> bool:
return key in self._data
def keySet(self) -> Set[str]:
return set(self._data.keys())
def getMediaId(self) -> Optional[str]:
return self.getString(MediaMetadata.METADATA_KEY_MEDIA_ID)
def toString(self) -> str:
title = self.getString(MediaMetadata.METADATA_KEY_TITLE)
return f"MediaMetadata(title={title})"
# ═══════════════════════════════════════════════════════════════
# QueueItem
# ═══════════════════════════════════════════════════════════════
class QueueItem(Stub):
"""QueueItem — 队列项"""
def __init__(self, description: "MediaDescription" = None, queueId: int = 0):
super().__init__()
self._description = description
self._queue_id = queueId
def getDescription(self) -> "MediaDescription":
return self._description
def getQueueId(self) -> int:
return self._queue_id
def toString(self) -> str:
return f"QueueItem(id={self._queue_id}, desc={self._description})"
# ═══════════════════════════════════════════════════════════════
# Rating
# ═══════════════════════════════════════════════════════════════
class Rating(Stub):
"""Rating — 评分"""
RATING_NONE = 0
RATING_HEART = 1
RATING_THUMB_UP_DOWN = 2
RATING_3_STARS = 3
RATING_4_STARS = 4
RATING_5_STARS = 5
RATING_PERCENTAGE = 6
def __init__(self, ratingStyle: int = RATING_NONE):
super().__init__()
self._rating_style = ratingStyle
self._rated = False
self._heart = False
self._thumb_up = False
self._star_rating = 0.0
self._percent_rating = 0.0
@staticmethod
def newHeartRating(hasHeart: bool) -> "Rating":
r = Rating(Rating.RATING_HEART)
r._rated = True
r._heart = hasHeart
return r
@staticmethod
def newThumbRating(thumbUp: bool) -> "Rating":
r = Rating(Rating.RATING_THUMB_UP_DOWN)
r._rated = True
r._thumb_up = thumbUp
return r
@staticmethod
def newStarRating(starRatingStyle: int, starRating: float) -> "Rating":
r = Rating(starRatingStyle)
r._rated = True
r._star_rating = starRating
return r
@staticmethod
def newPercentageRating(percent: float) -> "Rating":
r = Rating(Rating.RATING_PERCENTAGE)
r._rated = True
r._percent_rating = percent
return r
@staticmethod
def newUnratedRating(ratingStyle: int) -> "Rating":
return Rating(ratingStyle)
def isRated(self) -> bool:
return self._rated
def getRatingStyle(self) -> int:
return self._rating_style
def hasHeart(self) -> bool:
return self._heart
def isThumbUp(self) -> bool:
return self._thumb_up
def getStarRating(self) -> float:
return self._star_rating
def getPercentRating(self) -> float:
return self._percent_rating
def toString(self) -> str:
return f"Rating(style={self._rating_style}, rated={self._rated})"
# ═══════════════════════════════════════════════════════════════
# SessionToken
# ═══════════════════════════════════════════════════════════════
class SessionToken(Stub):
"""SessionToken — 会话令牌"""
TYPE_SESSION = 0
def __init__(self, type_: int = TYPE_SESSION):
super().__init__()
self._type = type_
self._binder = None
def getType(self) -> int:
return self._type
def getSessionBinder(self) -> Any:
return self._binder
def toString(self) -> str:
return f"SessionToken(type={self._type})"
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"MediaSession",
"MediaController",
"PlaybackState",
"MediaMetadata",
"QueueItem",
"Rating",
"SessionToken",
]
+359
View File
@@ -0,0 +1,359 @@
"""
Android media.tv 组件 — TvInputManager, TvInputInfo, TvInputService 等
方法签名完全对齐 Android SDK
"""
from typing import Any, Callable, Dict, List, Optional
from .._core import Stub, AndroidOnlyError
# ═══════════════════════════════════════════════════════════════
# TvInputManager
# ═══════════════════════════════════════════════════════════════
class TvInputManager(Stub):
"""TvInputManager — 电视输入管理器"""
ACTION_VIEW_RECORDING_SCHEDULES = "android.tv.action.VIEW_RECORDING_SCHEDULES"
ACTION_WATCH_NEXT = "android.tv.action.WATCH_NEXT"
TIME_SHIFT_STATUS_AVAILABLE = 0
TIME_SHIFT_STATUS_UNAVAILABLE = 1
TIME_SHIFT_STATUS_UNKNOWN = 2
TIME_SHIFT_STATUS_UNSUPPORTED = 3
VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY = 8
VIDEO_UNAVAILABLE_REASON_BUFFERING = 3
VIDEO_UNAVAILABLE_REASON_CAS = 7
VIDEO_UNAVAILABLE_REASON_INSUFFICIENT_RESOURCE = 5
VIDEO_UNAVAILABLE_REASON_NOT_CONNECTED = 6
VIDEO_UNAVAILABLE_REASON_PREEMPTED = 4
VIDEO_UNAVAILABLE_REASON_TUNING = 1
VIDEO_UNAVAILABLE_REASON_UNKNOWN = 0
VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL = 2
# ═══════════════════════════════════════════════════════════
# TvInputCallback
# ═══════════════════════════════════════════════════════════
class TvInputCallback(Stub):
"""TvInputManager.TvInputCallback — 电视输入回调"""
def onInputAdded(self, inputId: str):
pass
def onInputRemoved(self, inputId: str):
pass
def onInputChanged(self, inputId: str):
pass
def onInputStateChanged(self, inputId: str, state: int):
pass
def onTvInputInfoUpdated(self, inputInfo: "TvInputInfo"):
pass
def __init__(self):
super().__init__()
self._inputs: Dict[str, TvInputInfo] = {}
self._callbacks: Dict[int, TvInputManager.TvInputCallback] = {}
def getTvInputList(self) -> List["TvInputInfo"]:
return list(self._inputs.values())
def getTvInputInfo(self, inputId: str) -> Optional["TvInputInfo"]:
return self._inputs.get(inputId)
def getCurrentTvInputInfo(self) -> Optional["TvInputInfo"]:
for inp in self._inputs.values():
return inp
return None
def updateTvInputInfo(self, inputInfo: "TvInputInfo"):
if inputInfo:
self._inputs[inputInfo.getId()] = inputInfo
def registerCallback(self, callback: TvInputCallback, handler: Any = None):
lid = id(callback)
self._callbacks[lid] = callback
def unregisterCallback(self, callback: TvInputCallback):
lid = id(callback)
self._callbacks.pop(lid, None)
def createRecordingSession(self, recordingService: str, inputId: str):
raise AndroidOnlyError("TvInputManager.createRecordingSession() is not available on desktop")
def sendTvInputNotifyIntent(self, intent: Any, inputId: str):
pass
def isParentalControlsEnabled(self) -> bool:
return False
def isRatingBlocked(self, rating: str) -> bool:
return False
def getBlockedRatings(self) -> List[str]:
return []
def getAvailableTvInputs(self) -> List[str]:
return list(self._inputs.keys())
def getTvContentRatingSystems(self) -> List[str]:
return ["US_TV", "US_MOVIE"]
# ═══════════════════════════════════════════════════════════════
# TvInputInfo
# ═══════════════════════════════════════════════════════════════
class TvInputInfo(Stub):
"""TvInputInfo — 电视输入信息"""
TYPE_COMPONENT = 1005
TYPE_COMPOSITE = 1004
TYPE_HDMI = 1007
TYPE_OTHER = 1001
TYPE_SCART = 1003
TYPE_SVIDEO = 1006
TYPE_TUNER = 0
TYPE_DISPLAY_LINK = 1008
TYPE_VGA = 1002
def __init__(self, id: str = "", serviceInfo: Any = None,
parentId: str = "", type_: int = TYPE_TUNER,
hdmiDeviceInfo: Any = None,
isHidden: bool = False, canRecord: bool = False,
canPauseRecording: bool = False):
super().__init__()
self._id = id
self._service_info = serviceInfo
self._parent_id = parentId
self._type = type_
self._hdmi_device_info = hdmiDeviceInfo
self._is_hidden = isHidden
self._can_record = canRecord
self._can_pause_recording = canPauseRecording
def getId(self) -> str:
return self._id
def getServiceInfo(self) -> Any:
return self._service_info
def getParentId(self) -> str:
return self._parent_id
def getType(self) -> int:
return self._type
def getHdmiDeviceInfo(self) -> Any:
return self._hdmi_device_info
def loadIcon(self, context: Any) -> Any:
return None
def loadLogo(self, context: Any) -> Any:
return None
def createIntentForSetupScreen(self) -> Any:
return None
def isHidden(self) -> bool:
return self._is_hidden
def canRecord(self) -> bool:
return self._can_record
def canPauseRecording(self) -> bool:
return self._can_pause_recording
def toString(self) -> str:
type_names = {
TvInputInfo.TYPE_TUNER: "TUNER",
TvInputInfo.TYPE_HDMI: "HDMI",
TvInputInfo.TYPE_COMPOSITE: "COMPOSITE",
TvInputInfo.TYPE_COMPONENT: "COMPONENT",
}
return f"TvInputInfo(id={self._id}, type={type_names.get(self._type, str(self._type))})"
# ═══════════════════════════════════════════════════════════════
# TvInputService
# ═══════════════════════════════════════════════════════════════
class TvInputService(Stub):
"""TvInputService — 电视输入服务"""
RECORDING_ERROR_RECORDING_LOST = 0
RECORDING_ERROR_TIMEOUT = 1
RECORDING_ERROR_OTHER = 2
# ═══════════════════════════════════════════════════════════
# Session
# ═══════════════════════════════════════════════════════════
class Session(Stub):
"""TvInputService.Session — 电视会话"""
def __init__(self):
super().__init__()
self._main = False
self._stream_volume = 1.0
self._caption_enabled = False
def onSetSurface(self, surface: Any) -> bool:
return True
def onSurfaceChanged(self, format: int, width: int, height: int):
pass
def onSurfaceReleased(self):
pass
def onAppPrivateCommand(self, action: str, data: Any):
pass
def onCreateOverlayView(self) -> Any:
return None
def onRelayoutContent(self):
pass
def onSetCaptionEnabled(self, enabled: bool):
self._caption_enabled = enabled
def onSetStreamVolume(self, volume: float):
self._stream_volume = volume
def onTimeShiftSetCurrentPositionRequest(self, positionMs: int):
pass
def notifyTimeShiftStatusChanged(self, status: int):
pass
def scheduleOverlayView(self, view: Any):
pass
def timeShiftPlay(self, uri: str):
pass
def timeShiftPause(self):
pass
def timeShiftResume(self):
pass
def timeShiftSeekTo(self, positionMs: int):
pass
def timeShiftSetPlaybackParams(self, params: Any):
pass
def timeShiftEnablePositionTracking(self, enable: bool):
pass
def notifyVideoAvailable(self):
pass
def notifyVideoUnavailable(self, reason: int):
pass
def notifySessionEvent(self, eventType: str, eventArgs: Any):
pass
def notifyTracksChanged(self, tracks: List[Any]):
pass
def notifyTrackSelected(self, type_: str, trackId: str):
pass
def notifyContentAllowed(self):
pass
def notifyContentBlocked(self, rating: str):
pass
def setMain(self, main: bool):
self._main = main
def setStreamVolume(self, volume: float):
self._stream_volume = volume
def __init__(self):
super().__init__()
self._sessions: List[TvInputService.Session] = []
def onCreateSession(self, inputId: str) -> Session:
session = TvInputService.Session()
self._sessions.append(session)
return session
def onSetupFinished(self, inputId: str):
pass
def sessionCreated(self, session: Session):
pass
def notifyHardwareFailed(self, deviceId: int):
pass
# ═══════════════════════════════════════════════════════════════
# TvRecordingClient / TvRecordingService (stubs)
# ═══════════════════════════════════════════════════════════════
class TvRecordingClient(Stub):
"""TvRecordingClient — 电视录制客户端"""
def __init__(self):
super().__init__()
def startRecording(self, uri: str):
pass
def stopRecording(self):
pass
def pauseRecording(self):
pass
def resumeRecording(self):
pass
def release(self):
pass
class TvRecordingService(Stub):
"""TvRecordingService — 电视录制服务"""
def __init__(self):
super().__init__()
def onStartRecording(self, inputId: str, sessionId: str):
pass
def onStopRecording(self, sessionId: str):
pass
def onPauseRecording(self, sessionId: str):
pass
def onResumeRecording(self, sessionId: str):
pass
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"TvInputManager",
"TvInputInfo",
"TvInputService",
"TvRecordingClient",
"TvRecordingService",
]
+476
View File
@@ -0,0 +1,476 @@
"""
Android Media Transfer Protocol API — MtpDevice, MtpStorageInfo, MtpObjectInfo, MtpConstants
方法签名完全对齐 Android SDK
"""
import os as _os
import sys
import time
from datetime import datetime
from typing import Any, Dict, List, Optional, Set, Tuple, Callable, BinaryIO
from .._core import Stub, AndroidOnlyError
from ..java.lang import RuntimeException
IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {})
__all__ = [
"MtpDevice",
"MtpStorageInfo",
"MtpObjectInfo",
"MtpConstants",
"MtpPropertyGroup",
"MtpDeviceInfo",
"MtpStorage",
]
# ═══════════════════════════════════════════════════════════════
# MtpConstants — 50+ 格式常量
# ═══════════════════════════════════════════════════════════════
class MtpConstants(Stub):
"""MTP 协议常量"""
# --- 格式常量 ---
FORMAT_UNDEFINED = 0x3000
FORMAT_ASSOCIATION = 0x3001
FORMAT_SCRIPT = 0x3002
FORMAT_EXECUTABLE = 0x3003
FORMAT_TEXT = 0x3004
FORMAT_HTML = 0x3005
FORMAT_DPOF = 0x3006
FORMAT_AIFF = 0x3007
FORMAT_WAV = 0x3008
FORMAT_MP3 = 0x3009
FORMAT_AVI = 0x300A
FORMAT_MPEG = 0x300B
FORMAT_ASF = 0x300C
FORMAT_DEF = 0x300D
FORMAT_EXIF_JPEG = 0x3801
FORMAT_TIFF_EP = 0x3802
FORMAT_FLASHPIX = 0x3803
FORMAT_BMP = 0x3804
FORMAT_CIFF = 0x3805
FORMAT_GIF = 0x3807
FORMAT_JFIF = 0x3808
FORMAT_PCD = 0x3809
FORMAT_PICT = 0x380A
FORMAT_PNG = 0x380B
FORMAT_TIFF = 0x380D
FORMAT_TIFF_IT = 0x380E
FORMAT_JP2 = 0x380F
FORMAT_JPX = 0x3810
FORMAT_XML = 0x3811
FORMAT_FIRMWARE = 0x3802
FORMAT_WMA = 0xB901
FORMAT_WMV = 0xB980
FORMAT_WPL = 0xBA05
FORMAT_FLAC = 0xB906
FORMAT_OGG = 0xB902
FORMAT_AAC = 0xB903
FORMAT_MP4_CONTAINER = 0xB904
FORMAT_MP2 = 0xB905
FORMAT_3GP = 0xB907
FORMAT_3GP_CONTAINER = 0xB908
FORMAT_AVC = 0xB909
FORMAT_HEVC = 0xB90A
FORMAT_DNG = 0xB90B
FORMAT_ARW = 0xB90C
FORMAT_CR2 = 0xB90D
FORMAT_RAF = 0xB90E
FORMAT_NEF = 0xB90F
FORMAT_ORF = 0xB910
FORMAT_RW2 = 0xB911
FORMAT_SRW = 0xB912
# --- 保护状态 ---
PROTECTION_STATUS_NONE = 0
PROTECTION_STATUS_READ_ONLY = 0x8001
PROTECTION_STATUS_PROTECTED = 0x8002
# --- 关联类型 ---
ASSOCIATION_TYPE_UNDEFINED = 0x0000
ASSOCIATION_TYPE_GENERIC_FOLDER = 0x0001
# --- 属性格式常量 ---
PROP_FORMAT_UNDEFINED = 0x0000
PROP_FORMAT_STRING = 0xFFFF
PROP_FORMAT_UINT8 = 0x0001
PROP_FORMAT_UINT16 = 0x0002
PROP_FORMAT_UINT32 = 0x0004
PROP_FORMAT_UINT64 = 0x0008
PROP_FORMAT_INT8 = 0x0009
PROP_FORMAT_INT16 = 0x000A
PROP_FORMAT_INT32 = 0x000B
PROP_FORMAT_INT64 = 0x000C
PROP_FORMAT_BOOL = 0x4001
PROP_FORMAT_DATE = 0x4002
# --- 操作码 ---
OPERATION_GET_DEVICE_INFO = 0x1001
OPERATION_OPEN_SESSION = 0x1002
OPERATION_CLOSE_SESSION = 0x1003
OPERATION_GET_STORAGE_IDS = 0x1004
OPERATION_GET_STORAGE_INFO = 0x1005
OPERATION_GET_OBJECT_PROP_VALUE = 0x9802
OPERATION_SET_OBJECT_PROP_VALUE = 0x9803
OPERATION_GET_OBJECT_INFO = 0x1007
OPERATION_GET_OBJECT = 0x1009
OPERATION_GET_THUMB = 0x100A
OPERATION_DELETE_OBJECT = 0x100B
OPERATION_SEND_OBJECT_INFO = 0x100C
OPERATION_SEND_OBJECT = 0x100D
OPERATION_GET_OBJECT_REFERENCES = 0x1010
OPERATION_SET_OBJECT_REFERENCES = 0x1011
def __init__(self):
super().__init__()
# ═══════════════════════════════════════════════════════════════
# MtpDevice
# ═══════════════════════════════════════════════════════════════
class MtpDevice(Stub):
"""MtpDevice — MTP 设备 (桌面存根)"""
def __init__(self, device: Any = None):
super().__init__()
self._device = device
self._opened = False
def open(self) -> bool:
"""打开 MTP 设备"""
raise AndroidOnlyError("MtpDevice.open() 仅在 Android 设备上可用")
def close(self):
"""关闭 MTP 设备"""
self._opened = False
def getDeviceInfo(self) -> "MtpDeviceInfo":
"""获取设备信息"""
return MtpDeviceInfo()
def getDeviceName(self) -> str:
"""获取设备名称"""
return "MTP Device"
def getObjectHandles(
self, storageId: int, format: int, objectHandle: int
) -> List[int]:
"""获取对象句柄列表"""
return []
def getObjectInfo(self, objectHandle: int) -> Optional["MtpObjectInfo"]:
"""获取对象信息"""
return None
def getObject(self, objectHandle: int, dataSize: int) -> Optional[bytes]:
"""获取对象数据"""
return None
def getThumbnail(self, objectHandle: int) -> Optional[bytes]:
"""获取缩略图"""
return None
def getStorageIds(self) -> List[int]:
"""获取存储 ID 列表"""
return [0x00010001]
def getStorageInfo(self, storageId: int) -> "MtpStorageInfo":
"""获取存储信息"""
return MtpStorageInfo()
def importFile(self, objectHandle: int, destPath: str) -> bool:
"""导入文件"""
raise AndroidOnlyError("MtpDevice.importFile() 仅在 Android 设备上可用")
def deleteObject(self, objectHandle: int) -> bool:
"""删除对象"""
raise AndroidOnlyError("MtpDevice.deleteObject() 仅在 Android 设备上可用")
def getParent(self, objectHandle: int) -> int:
"""获取父对象句柄"""
return 0xFFFFFFFF
def getObjectReferences(self, objectHandle: int) -> List[int]:
"""获取对象引用列表"""
return []
def setObjectReferences(self, objectHandle: int, references: List[int]) -> bool:
"""设置对象引用"""
raise AndroidOnlyError("MtpDevice.setObjectReferences() 仅在 Android 设备上可用")
def sendObject(self, objectHandle: int, data: bytes) -> bool:
"""发送对象"""
raise AndroidOnlyError("MtpDevice.sendObject() 仅在 Android 设备上可用")
def sendObjectInfo(
self, storageId: int, parent: int, info: "MtpObjectInfo"
) -> int:
"""发送对象信息"""
raise AndroidOnlyError("MtpDevice.sendObjectInfo() 仅在 Android 设备上可用")
def toString(self) -> str:
return f"MtpDevice(name={self.getDeviceName()})"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# MtpStorageInfo
# ═══════════════════════════════════════════════════════════════
class MtpStorageInfo(Stub):
"""MtpStorageInfo — MTP 存储信息"""
def __init__(self):
super().__init__()
self._storage_id = 0x00010001
self._max_capacity = 32 * 1024 * 1024 * 1024 # 32GB
self._free_space = 16 * 1024 * 1024 * 1024 # 16GB
self._description = "Internal Storage"
self._volume_identifier = ""
def getStorageId(self) -> int:
return self._storage_id
def getMaxCapacity(self) -> int:
return self._max_capacity
def getFreeSpace(self) -> int:
return self._free_space
def getDescription(self) -> str:
return self._description
def getVolumeIdentifier(self) -> str:
return self._volume_identifier
def toString(self) -> str:
return (f"MtpStorageInfo(id={self._storage_id}, "
f"free={self._free_space}B)")
def equals(self, other) -> bool:
if not isinstance(other, MtpStorageInfo):
return False
return self._storage_id == other._storage_id
def hashCode(self) -> int:
return self._storage_id & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# MtpObjectInfo
# ═══════════════════════════════════════════════════════════════
class MtpObjectInfo(Stub):
"""MtpObjectInfo — MTP 对象信息"""
def __init__(self):
super().__init__()
self._object_handle = 0
self._storage_id = 0
self._format = MtpConstants.FORMAT_UNDEFINED
self._protection_status = MtpConstants.PROTECTION_STATUS_NONE
self._compressed_size = 0
self._thumb_format = MtpConstants.FORMAT_UNDEFINED
self._thumb_compressed_size = 0
self._thumb_pix_width = 0
self._thumb_pix_height = 0
self._image_pix_width = 0
self._image_pix_height = 0
self._image_pix_depth = 0
self._parent = 0
self._association_desc = 0
self._association_type = 0
self._sequence_number = 0
self._name = ""
self._date_created = 0
self._date_modified = 0
self._keywords = ""
def getObjectHandle(self) -> int:
return self._object_handle
def getStorageId(self) -> int:
return self._storage_id
def getFormat(self) -> int:
return self._format
def getProtectionStatus(self) -> int:
return self._protection_status
def getCompressedSize(self) -> int:
return self._compressed_size
def getThumbFormat(self) -> int:
return self._thumb_format
def getThumbCompressedSize(self) -> int:
return self._thumb_compressed_size
def getThumbPixWidth(self) -> int:
return self._thumb_pix_width
def getThumbPixHeight(self) -> int:
return self._thumb_pix_height
def getImagePixWidth(self) -> int:
return self._image_pix_width
def getImagePixHeight(self) -> int:
return self._image_pix_height
def getImagePixDepth(self) -> int:
return self._image_pix_depth
def getParent(self) -> int:
return self._parent
def getAssociationDesc(self) -> int:
return self._association_desc
def getAssociationType(self) -> int:
return self._association_type
def getSequenceNumber(self) -> int:
return self._sequence_number
def getName(self) -> str:
return self._name
def getDateCreated(self) -> int:
return self._date_created
def getDateModified(self) -> int:
return self._date_modified
def getKeywords(self) -> str:
return self._keywords
def toString(self) -> str:
return f"MtpObjectInfo(name={self._name}, handle={self._object_handle})"
def equals(self, other) -> bool:
if not isinstance(other, MtpObjectInfo):
return False
return self._object_handle == other._object_handle
def hashCode(self) -> int:
return self._object_handle & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# MtpPropertyGroup
# ═══════════════════════════════════════════════════════════════
class MtpPropertyGroup(Stub):
"""MtpPropertyGroup — MTP 属性组"""
def __init__(self):
super().__init__()
self._properties: Dict[int, Any] = {}
def getPropertyValue(self, propertyCode: int) -> Optional[Any]:
return self._properties.get(propertyCode)
def toString(self) -> str:
return f"MtpPropertyGroup(properties={len(self._properties)})"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# MtpDeviceInfo
# ═══════════════════════════════════════════════════════════════
class MtpDeviceInfo(Stub):
"""MtpDeviceInfo — MTP 设备信息"""
def __init__(self):
super().__init__()
self._manufacturer = "Unknown"
self._model = "MTP Device"
self._device_version = "1.0"
self._serial_number = "00000000"
self._operations_supported: List[int] = []
self._events_supported: List[int] = []
self._device_properties_supported: List[int] = []
def getManufacturer(self) -> str:
return self._manufacturer
def getModel(self) -> str:
return self._model
def getDeviceVersion(self) -> str:
return self._device_version
def getSerialNumber(self) -> str:
return self._serial_number
def getOperationsSupported(self) -> List[int]:
return list(self._operations_supported)
def getEventsSupported(self) -> List[int]:
return list(self._events_supported)
def getDevicePropertySupported(self) -> List[int]:
return list(self._device_properties_supported)
def toString(self) -> str:
return f"MtpDeviceInfo(manufacturer={self._manufacturer}, model={self._model})"
def equals(self, other) -> bool:
if not isinstance(other, MtpDeviceInfo):
return False
return self._serial_number == other._serial_number
def hashCode(self) -> int:
return hash(self._serial_number) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# MtpStorage
# ═══════════════════════════════════════════════════════════════
class MtpStorage(Stub):
"""MtpStorage — MTP 存储"""
def __init__(self, storageId: int = 0x00010001, path: str = ""):
super().__init__()
self._storage_id = storageId
self._path = path
self._description = "Storage"
def getStorageId(self) -> int:
return self._storage_id
def getPath(self) -> str:
return self._path
def getDescription(self) -> str:
return self._description
def toString(self) -> str:
return f"MtpStorage(id={self._storage_id}, path={self._path})"
def equals(self, other) -> bool:
if not isinstance(other, MtpStorage):
return False
return self._storage_id == other._storage_id
def hashCode(self) -> int:
return self._storage_id & 0x7FFFFFFF
+464
View File
@@ -0,0 +1,464 @@
"""
Android OpenGL 组件 — GLSurfaceView, GLU, GLUtils, EGL14 等
方法签名完全对齐 Android SDK (android.opengl 包)
"""
import math
from typing import Any, Callable, List, Optional, Tuple
from .._core import Stub, AndroidOnlyError
# ═══════════════════════════════════════════════════════════════
# EGL 常量
# ═══════════════════════════════════════════════════════════════
class EGL14(Stub):
"""EGL14 — EGL 1.4 API 常量 (桌面仅提供常量)"""
# EGL 布尔值
EGL_TRUE = 1
EGL_FALSE = 0
# EGL 错误码
EGL_SUCCESS = 0x3000
EGL_NOT_INITIALIZED = 0x3001
EGL_BAD_ACCESS = 0x3002
EGL_BAD_ALLOC = 0x3003
EGL_BAD_ATTRIBUTE = 0x3004
EGL_BAD_CONFIG = 0x3005
EGL_BAD_CONTEXT = 0x3006
EGL_BAD_CURRENT_SURFACE = 0x3007
EGL_BAD_DISPLAY = 0x3008
EGL_BAD_MATCH = 0x3009
EGL_BAD_NATIVE_PIXMAP = 0x300A
EGL_BAD_NATIVE_WINDOW = 0x300B
EGL_BAD_PARAMETER = 0x300C
EGL_BAD_SURFACE = 0x300D
EGL_CONTEXT_LOST = 0x300E
# EGL 配置属性
EGL_BUFFER_SIZE = 0x3020
EGL_ALPHA_SIZE = 0x3021
EGL_BLUE_SIZE = 0x3022
EGL_GREEN_SIZE = 0x3023
EGL_RED_SIZE = 0x3024
EGL_DEPTH_SIZE = 0x3025
EGL_STENCIL_SIZE = 0x3026
EGL_CONFIG_CAVEAT = 0x3027
EGL_CONFIG_ID = 0x3028
EGL_LEVEL = 0x3029
EGL_MAX_PBUFFER_HEIGHT = 0x302A
EGL_MAX_PBUFFER_PIXELS = 0x302B
EGL_MAX_PBUFFER_WIDTH = 0x302C
EGL_NATIVE_RENDERABLE = 0x302D
EGL_NATIVE_VISUAL_ID = 0x302E
EGL_NATIVE_VISUAL_TYPE = 0x302F
EGL_PRESERVED_RESOURCES = 0x3030
EGL_SAMPLES = 0x3031
EGL_SAMPLE_BUFFERS = 0x3032
EGL_SURFACE_TYPE = 0x3033
EGL_TRANSPARENT_TYPE = 0x3034
EGL_TRANSPARENT_BLUE_VALUE = 0x3035
EGL_TRANSPARENT_GREEN_VALUE = 0x3036
EGL_TRANSPARENT_RED_VALUE = 0x3037
EGL_NONE = 0x3038
EGL_BIND_TO_TEXTURE_RGB = 0x3039
EGL_BIND_TO_TEXTURE_RGBA = 0x303A
EGL_MIN_SWAP_INTERVAL = 0x303B
EGL_MAX_SWAP_INTERVAL = 0x303C
# EGL 表面属性
EGL_WIDTH = 0x3057
EGL_HEIGHT = 0x3056
EGL_LARGEST_PBUFFER = 0x3058
EGL_TEXTURE_FORMAT = 0x3080
EGL_TEXTURE_TARGET = 0x3081
EGL_MIPMAP_TEXTURE = 0x3082
EGL_MIPMAP_LEVEL = 0x3083
# EGL 渲染上下文属性
EGL_CONTEXT_CLIENT_TYPE = 0x3097
EGL_CONTEXT_CLIENT_VERSION = 0x3098
# EGL 显示
EGL_DEFAULT_DISPLAY = 0
# EGL 版本
EGL_VERSION_1_4 = 1
# OpenGL ES 位标记
EGL_OPENGL_ES_BIT = 0x0001
EGL_OPENGL_ES2_BIT = 0x0004
EGL_OPENGL_ES3_BIT = 0x0040
def __init__(self):
super().__init__()
@staticmethod
def eglGetDisplay(display_id: Any) -> "EGLDisplay":
"""获取 EGL 显示 (桌面仅返回存根)"""
raise AndroidOnlyError("EGL14.eglGetDisplay() 仅在 Android 设备上可用")
@staticmethod
def eglInitialize(display: "EGLDisplay", major_minor: Optional[List[int]] = None) -> bool:
"""初始化 EGL (桌面仅返回存根)"""
raise AndroidOnlyError("EGL14.eglInitialize() 仅在 Android 设备上可用")
@staticmethod
def eglChooseConfig(display: "EGLDisplay", attrib_list: List[int],
configs: List[Any], config_size: int,
num_config: List[int]) -> bool:
"""选择 EGL 配置 (桌面仅返回存根)"""
raise AndroidOnlyError("EGL14.eglChooseConfig() 仅在 Android 设备上可用")
@staticmethod
def eglCreateContext(display: "EGLDisplay", config: "EGLConfig",
share_context: "EGLContext",
attrib_list: List[int]) -> "EGLContext":
"""创建 EGL 上下文 (桌面仅返回存根)"""
raise AndroidOnlyError("EGL14.eglCreateContext() 仅在 Android 设备上可用")
@staticmethod
def eglCreateWindowSurface(display: "EGLDisplay", config: "EGLConfig",
win: Any, attrib_list: List[int]) -> "EGLSurface":
"""创建窗口表面 (桌面仅返回存根)"""
raise AndroidOnlyError("EGL14.eglCreateWindowSurface() 仅在 Android 设备上可用")
@staticmethod
def eglMakeCurrent(display: "EGLDisplay", draw: "EGLSurface",
read: "EGLSurface", context: "EGLContext") -> bool:
"""绑定 EGL 上下文 (桌面仅返回存根)"""
raise AndroidOnlyError("EGL14.eglMakeCurrent() 仅在 Android 设备上可用")
@staticmethod
def eglSwapBuffers(display: "EGLDisplay", surface: "EGLSurface") -> bool:
"""交换缓冲区 (桌面仅返回存根)"""
raise AndroidOnlyError("EGL14.eglSwapBuffers() 仅在 Android 设备上可用")
@staticmethod
def eglDestroyContext(display: "EGLDisplay", context: "EGLContext") -> bool:
"""销毁 EGL 上下文 (桌面仅返回存根)"""
raise AndroidOnlyError("EGL14.eglDestroyContext() 仅在 Android 设备上可用")
@staticmethod
def eglDestroySurface(display: "EGLDisplay", surface: "EGLSurface") -> bool:
"""销毁表面 (桌面仅返回存根)"""
raise AndroidOnlyError("EGL14.eglDestroySurface() 仅在 Android 设备上可用")
@staticmethod
def eglTerminate(display: "EGLDisplay") -> bool:
"""终止 EGL (桌面仅返回存根)"""
raise AndroidOnlyError("EGL14.eglTerminate() 仅在 Android 设备上可用")
@staticmethod
def eglGetError() -> int:
"""获取 EGL 错误码"""
return EGL14.EGL_SUCCESS
@staticmethod
def eglQueryString(display: "EGLDisplay", name: int) -> str:
"""查询 EGL 字符串"""
if name == 0x3029: # EGL_VERSION
return "1.4 (desktop stub)"
return ""
def toString(self) -> str:
return "EGL14(desktop-stub)"
class EGLDisplay(Stub):
"""EGLDisplay — EGL 显示连接"""
def __init__(self):
super().__init__()
def toString(self) -> str:
return f"EGLDisplay@{hex(id(self))}"
def hashCode(self) -> int:
return hash(id(self)) & 0x7FFFFFFF
def equals(self, other) -> bool:
return self is other
class EGLConfig(Stub):
"""EGLConfig — EGL 帧缓冲配置"""
def __init__(self):
super().__init__()
def toString(self) -> str:
return f"EGLConfig@{hex(id(self))}"
def hashCode(self) -> int:
return hash(id(self)) & 0x7FFFFFFF
def equals(self, other) -> bool:
return self is other
class EGLContext(Stub):
"""EGLContext — EGL 渲染上下文"""
def __init__(self):
super().__init__()
def toString(self) -> str:
return f"EGLContext@{hex(id(self))}"
def hashCode(self) -> int:
return hash(id(self)) & 0x7FFFFFFF
def equals(self, other) -> bool:
return self is other
class EGLSurface(Stub):
"""EGLSurface — EGL 渲染表面"""
def __init__(self):
super().__init__()
def toString(self) -> str:
return f"EGLSurface@{hex(id(self))}"
def hashCode(self) -> int:
return hash(id(self)) & 0x7FFFFFFF
def equals(self, other) -> bool:
return self is other
# ═══════════════════════════════════════════════════════════════
# GLSurfaceView
# ═══════════════════════════════════════════════════════════════
class GLSurfaceView(Stub):
"""GLSurfaceView — OpenGL 渲染视图 (桌面存根)"""
# 渲染模式
RENDERMODE_CONTINUOUSLY = 1
RENDERMODE_WHEN_DIRTY = 0
# 调试标志
DEBUG_CHECK_GL_ERROR = 1
DEBUG_LOG_GL_CALLS = 2
def __init__(self, context: Any = None):
super().__init__()
self._context = context
self._renderer: Optional[Any] = None
self._render_mode: int = GLSurfaceView.RENDERMODE_CONTINUOUSLY
self._egl_context_client_version: int = 2
self._preserve_egl_context_on_pause: bool = False
self._debug_flags: int = 0
self._paused: bool = False
self._queue: List[Callable] = []
def setRenderer(self, renderer: Any):
"""设置渲染器"""
self._renderer = renderer
if renderer:
renderer.onSurfaceCreated(None, None)
renderer.onSurfaceChanged(None, 0, 0)
def setRenderMode(self, render_mode: int):
"""设置渲染模式"""
self._render_mode = render_mode
def getRenderMode(self) -> int:
"""获取渲染模式"""
return self._render_mode
def onPause(self):
"""暂停渲染"""
self._paused = True
def onResume(self):
"""恢复渲染"""
self._paused = False
def queueEvent(self, r: Callable):
"""将事件排入 GL 线程队列"""
self._queue.append(r)
# 立即执行 (桌面模拟)
try:
r()
except Exception:
pass
def requestRender(self):
"""请求渲染"""
if self._renderer and not self._paused:
self._renderer.onDrawFrame(None)
def setEGLContextClientVersion(self, version: int):
"""设置 EGL 客户端版本"""
self._egl_context_client_version = version
def setPreserveEGLContextOnPause(self, preserve: bool):
"""设置暂停时是否保留 EGL 上下文"""
self._preserve_egl_context_on_pause = preserve
def setDebugFlags(self, debug_flags: int):
"""设置调试标志"""
self._debug_flags = debug_flags
def getPreserveEGLContextOnPause(self) -> bool:
return self._preserve_egl_context_on_pause
def surfaceCreated(self, holder: Any):
"""表面创建回调"""
pass
def surfaceDestroyed(self, holder: Any):
"""表面销毁回调"""
pass
def surfaceChanged(self, holder: Any, format: int, w: int, h: int):
"""表面变化回调"""
pass
def toString(self) -> str:
return f"GLSurfaceView(renderMode={self._render_mode}, paused={self._paused})"
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
def equals(self, other) -> bool:
return self is other
# ═══════════════════════════════════════════════════════════════
# GLU
# ═══════════════════════════════════════════════════════════════
class GLU(Stub):
"""GLU — OpenGL 实用工具函数"""
def __init__(self):
super().__init__()
@staticmethod
def gluLookAt(eye_x: float, eye_y: float, eye_z: float,
center_x: float, center_y: float, center_z: float,
up_x: float, up_y: float, up_z: float):
"""定义视图变换 (桌面仅抛异常)"""
raise AndroidOnlyError("GLU.gluLookAt() 仅在 Android (OpenGL) 设备上可用")
@staticmethod
def gluPerspective(fov_y: float, aspect: float, z_near: float, z_far: float):
"""设置透视投影 (桌面仅抛异常)"""
raise AndroidOnlyError("GLU.gluPerspective() 仅在 Android (OpenGL) 设备上可用")
@staticmethod
def gluOrtho2D(left: float, right: float, bottom: float, top: float):
"""设置正交投影 (桌面仅抛异常)"""
raise AndroidOnlyError("GLU.gluOrtho2D() 仅在 Android (OpenGL) 设备上可用")
@staticmethod
def gluProject(obj_x: float, obj_y: float, obj_z: float,
model: List[float], proj: List[float],
viewport: List[int]) -> Tuple[bool, float, float, float]:
"""将对象坐标映射到窗口坐标"""
win_x = (obj_x * proj[0] + 1.0) * viewport[2] / 2.0 + viewport[0]
win_y = (obj_y * proj[5] + 1.0) * viewport[3] / 2.0 + viewport[1]
win_z = obj_z * proj[10] + proj[11]
return True, win_x, win_y, win_z
@staticmethod
def gluUnProject(win_x: float, win_y: float, win_z: float,
model: List[float], proj: List[float],
viewport: List[int]) -> Tuple[bool, float, float, float]:
"""将窗口坐标映射到对象坐标"""
obj_x = 2.0 * (win_x - viewport[0]) / viewport[2] - 1.0
obj_y = 2.0 * (win_y - viewport[1]) / viewport[3] - 1.0
obj_z = win_z * 2.0 - 1.0
return True, obj_x, obj_y, obj_z
@staticmethod
def gluErrorString(error: int) -> str:
"""返回 OpenGL 错误描述字符串"""
errors = {
0x0500: "GL_INVALID_ENUM",
0x0501: "GL_INVALID_VALUE",
0x0502: "GL_INVALID_OPERATION",
0x0503: "GL_STACK_OVERFLOW",
0x0504: "GL_STACK_UNDERFLOW",
0x0505: "GL_OUT_OF_MEMORY",
0x0506: "GL_INVALID_FRAMEBUFFER_OPERATION",
}
return errors.get(error, f"Unknown error 0x{error:04X}")
def toString(self) -> str:
return "GLU(desktop-stub)"
def hashCode(self) -> int:
return hash("android.opengl.GLU") & 0x7FFFFFFF
def equals(self, other) -> bool:
return isinstance(other, GLU)
# ═══════════════════════════════════════════════════════════════
# GLUtils
# ═══════════════════════════════════════════════════════════════
class GLUtils(Stub):
"""GLUtils — OpenGL 工具函数"""
def __init__(self):
super().__init__()
@staticmethod
def getInternalFormat(bitmap: Any) -> int:
"""获取位图的内部格式"""
return 0x1908 # GL_RGBA
@staticmethod
def getType(bitmap: Any) -> int:
"""获取位图数据类型"""
return 0x1401 # GL_UNSIGNED_BYTE
@staticmethod
def texImage2D(target: int, level: int, internal_format: int,
bitmap: Any, border: int = 0):
"""上传 2D 纹理图像"""
raise AndroidOnlyError("GLUtils.texImage2D() 仅在 Android (OpenGL) 设备上可用")
@staticmethod
def texSubImage2D(target: int, level: int, xoffset: int, yoffset: int,
bitmap: Any, format: int = -1, type_: int = -1):
"""更新 2D 纹理子图像"""
raise AndroidOnlyError("GLUtils.texSubImage2D() 仅在 Android (OpenGL) 设备上可用")
@staticmethod
def toString(bitmap_or_buffer: Any) -> str:
return str(bitmap_or_buffer)
def toString(self) -> str:
return "GLUtils(desktop-stub)"
def hashCode(self) -> int:
return hash("android.opengl.GLUtils") & 0x7FFFFFFF
def equals(self, other) -> bool:
return isinstance(other, GLUtils)
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"GLSurfaceView",
"GLU",
"GLUtils",
"EGL14",
"EGLConfig",
"EGLContext",
"EGLDisplay",
"EGLSurface",
]
+581
View File
@@ -0,0 +1,581 @@
"""
Android Print 组件 — PrintManager, PrintJob, PrintDocumentAdapter, PdfPrint 等
方法签名完全对齐 Android SDK (android.print 包)
"""
import os
import time
from typing import Any, Callable, Dict, List, Optional, Tuple
from .._core import Stub, AndroidOnlyError
# ═══════════════════════════════════════════════════════════════
# PrintAttributes
# ═══════════════════════════════════════════════════════════════
class PrintAttributes(Stub):
"""PrintAttributes — 打印属性"""
# 颜色模式
COLOR_MODE_MONOCHROME = 1
COLOR_MODE_COLOR = 2
# 方向
ORIENTATION_PORTRAIT = 1
ORIENTATION_LANDSCAPE = 2
# 媒质尺寸 ID
MEDIA_SIZE_UNKNOWN = "unknown"
MEDIA_SIZE_A4 = "A4"
MEDIA_SIZE_A3 = "A3"
MEDIA_SIZE_LETTER = "letter"
MEDIA_SIZE_LEGAL = "legal"
MEDIA_SIZE_ISO_A4 = "iso_a4"
MEDIA_SIZE_ISO_A3 = "iso_a3"
MEDIA_SIZE_NA_LETTER = "na_letter"
def __init__(self):
super().__init__()
self._media_size: Optional["PrintAttributes.MediaSize"] = None
self._resolution: Optional["PrintAttributes.Resolution"] = None
self._min_margins: Optional["PrintAttributes.Margins"] = None
self._color_mode: int = PrintAttributes.COLOR_MODE_COLOR
self._orientation: int = PrintAttributes.ORIENTATION_PORTRAIT
def getMediaSize(self) -> Optional["MediaSize"]:
return self._media_size
def getResolution(self) -> Optional["Resolution"]:
return self._resolution
def getMinMargins(self) -> Optional["Margins"]:
return self._min_margins
def getColorMode(self) -> int:
return self._color_mode
def getOrientation(self) -> int:
return self._orientation
def isPortrait(self) -> bool:
return self._orientation == PrintAttributes.ORIENTATION_PORTRAIT
def toString(self) -> str:
return (f"PrintAttributes(colorMode={self._color_mode}, "
f"orientation={self._orientation})")
def hashCode(self) -> int:
return hash((self._color_mode, self._orientation)) & 0x7FFFFFFF
def equals(self, other) -> bool:
if not isinstance(other, PrintAttributes):
return False
return (self._color_mode == other._color_mode
and self._orientation == other._orientation)
# ── 内部嵌套类 ──
class MediaSize(Stub):
"""MediaSize — 媒质尺寸"""
def __init__(self, id: str, label: str, width_mils: int, height_mils: int):
super().__init__()
self._id = id
self._label = label
self._width_mils = width_mils
self._height_mils = height_mils
def getId(self) -> str:
return self._id
def getLabel(self, package_manager: Any = None) -> str:
return self._label
def getWidthMils(self) -> int:
return self._width_mils
def getHeightMils(self) -> int:
return self._height_mils
def asPortrait(self) -> "PrintAttributes.MediaSize":
if self._width_mils > self._height_mils:
return PrintAttributes.MediaSize(
self._id, self._label, self._height_mils, self._width_mils)
return self
def asLandscape(self) -> "PrintAttributes.MediaSize":
if self._width_mils < self._height_mils:
return PrintAttributes.MediaSize(
self._id, self._label, self._height_mils, self._width_mils)
return self
def toString(self) -> str:
return f"MediaSize({self._id}: {self._width_mils}x{self._height_mils}mils)"
def hashCode(self) -> int:
return hash(self._id) & 0x7FFFFFFF
def equals(self, other) -> bool:
if not isinstance(other, PrintAttributes.MediaSize):
return False
return self._id == other._id
class Resolution(Stub):
"""Resolution — 打印分辨率"""
def __init__(self, id: str, label: str, horizontal_dpi: int, vertical_dpi: int):
super().__init__()
self._id = id
self._label = label
self._horizontal_dpi = horizontal_dpi
self._vertical_dpi = vertical_dpi
def getId(self) -> str:
return self._id
def getLabel(self) -> str:
return self._label
def getHorizontalDpi(self) -> int:
return self._horizontal_dpi
def getVerticalDpi(self) -> int:
return self._vertical_dpi
def toString(self) -> str:
return f"Resolution({self._id}: {self._horizontal_dpi}x{self._vertical_dpi}dpi)"
class Margins(Stub):
"""Margins — 页边距"""
NO_MARGINS: "PrintAttributes.Margins" = None
def __init__(self, left_mils: int, top_mils: int,
right_mils: int, bottom_mils: int):
super().__init__()
self._left_mils = left_mils
self._top_mils = top_mils
self._right_mils = right_mils
self._bottom_mils = bottom_mils
def getLeftMils(self) -> int:
return self._left_mils
def getTopMils(self) -> int:
return self._top_mils
def getRightMils(self) -> int:
return self._right_mils
def getBottomMils(self) -> int:
return self._bottom_mils
def toString(self) -> str:
return f"Margins({self._left_mils},{self._top_mils},{self._right_mils},{self._bottom_mils})"
class Builder(Stub):
"""Builder — PrintAttributes 构建器"""
def __init__(self):
super().__init__()
self._attrs = PrintAttributes()
def setMediaSize(self, media_size: "PrintAttributes.MediaSize") -> "PrintAttributes.Builder":
self._attrs._media_size = media_size
return self
def setResolution(self, resolution: "PrintAttributes.Resolution") -> "PrintAttributes.Builder":
self._attrs._resolution = resolution
return self
def setMinMargins(self, margins: "PrintAttributes.Margins") -> "PrintAttributes.Builder":
self._attrs._min_margins = margins
return self
def setColorMode(self, color_mode: int) -> "PrintAttributes.Builder":
self._attrs._color_mode = color_mode
return self
def setOrientation(self, orientation: int) -> "PrintAttributes.Builder":
self._attrs._orientation = orientation
return self
def build(self) -> "PrintAttributes":
return self._attrs
PrintAttributes.Margins.NO_MARGINS = PrintAttributes.Margins(0, 0, 0, 0)
# ═══════════════════════════════════════════════════════════════
# PrintJobInfo
# ═══════════════════════════════════════════════════════════════
class PrintJobInfo(Stub):
"""PrintJobInfo — 打印任务信息"""
# 状态常量
STATE_CREATED = 1
STATE_QUEUED = 2
STATE_STARTED = 3
STATE_BLOCKED = 4
STATE_COMPLETED = 5
STATE_FAILED = 6
STATE_CANCELED = 7
def __init__(self):
super().__init__()
self._label: str = ""
self._id: Optional[Any] = None
self._state: int = PrintJobInfo.STATE_CREATED
self._attributes: Optional[PrintAttributes] = None
self._copies: int = 1
self._creation_time: int = int(time.time() * 1000)
self._canceling: bool = False
self._pages: Optional[List[int]] = None
self._has_advanced_options: bool = False
def getLabel(self) -> str:
return self._label
def setLabel(self, label: str):
self._label = label
def getId(self) -> Optional[Any]:
return self._id
def getState(self) -> int:
return self._state
def setState(self, state: int):
self._state = state
def getAttributes(self) -> Optional[PrintAttributes]:
return self._attributes
def setAttributes(self, attributes: PrintAttributes):
self._attributes = attributes
def getCopies(self) -> int:
return self._copies
def setCopies(self, copies: int):
self._copies = copies
def getCreationTime(self) -> int:
return self._creation_time
def isCanceling(self) -> bool:
return self._canceling
def getPages(self) -> Optional[List[int]]:
return self._pages
def setPages(self, pages: Optional[List[int]]):
self._pages = pages
def hasAdvancedOptions(self) -> bool:
return self._has_advanced_options
def toString(self) -> str:
return f"PrintJobInfo(label={self._label}, state={self._state}, copies={self._copies})"
def hashCode(self) -> int:
return hash(self._id) & 0x7FFFFFFF
def equals(self, other) -> bool:
if not isinstance(other, PrintJobInfo):
return False
return self._id == other._id
# ── Builder ──
class Builder(Stub):
"""Builder — PrintJobInfo 构建器"""
def __init__(self, info: Optional["PrintJobInfo"] = None):
super().__init__()
self._info = PrintJobInfo()
if info:
self._info._label = info._label
self._info._id = info._id
self._info._attributes = info._attributes
self._info._copies = info._copies
def setCopies(self, copies: int) -> "PrintJobInfo.Builder":
self._info._copies = copies
return self
def setAttributes(self, attributes: PrintAttributes) -> "PrintJobInfo.Builder":
self._info._attributes = attributes
return self
def setLabel(self, label: str) -> "PrintJobInfo.Builder":
self._info._label = label
return self
def setPages(self, pages: Optional[List[int]]) -> "PrintJobInfo.Builder":
self._info._pages = pages
return self
def build(self) -> "PrintJobInfo":
return self._info
# ═══════════════════════════════════════════════════════════════
# PrintJob
# ═══════════════════════════════════════════════════════════════
class PrintJob(Stub):
"""PrintJob — 打印任务"""
def __init__(self, info: PrintJobInfo):
super().__init__()
self._info = info
self._cancelled = False
self._result: Optional[Any] = None
def getId(self) -> Optional[Any]:
return self._info.getId()
def getInfo(self) -> PrintJobInfo:
return self._info
def isStarted(self) -> bool:
return self._info.getState() >= PrintJobInfo.STATE_STARTED
def isBlocked(self) -> bool:
return self._info.getState() == PrintJobInfo.STATE_BLOCKED
def isCompleted(self) -> bool:
return self._info.getState() == PrintJobInfo.STATE_COMPLETED
def isFailed(self) -> bool:
return self._info.getState() == PrintJobInfo.STATE_FAILED
def isCancelled(self) -> bool:
return self._cancelled
def cancel(self):
"""取消打印任务"""
self._cancelled = True
self._info.setState(PrintJobInfo.STATE_CANCELED)
def restart(self):
"""重新启动打印任务"""
self._cancelled = False
self._info.setState(PrintJobInfo.STATE_QUEUED)
def setResult(self, result: Any):
self._result = result
def getResult(self) -> Optional[Any]:
return self._result
def toString(self) -> str:
return f"PrintJob(id={self.getId()}, state={self._info.getState()})"
def hashCode(self) -> int:
return hash(self.getId()) & 0x7FFFFFFF
def equals(self, other) -> bool:
if not isinstance(other, PrintJob):
return False
return self.getId() == other.getId()
# ═══════════════════════════════════════════════════════════════
# PrintDocumentAdapter
# ═══════════════════════════════════════════════════════════════
class PrintDocumentAdapter(Stub):
"""PrintDocumentAdapter — 打印文档适配器"""
EXTRA_PRINT_PREVIEW = "EXTRA_PRINT_PREVIEW"
def __init__(self):
super().__init__()
def onStart(self):
"""打印开始回调"""
pass
def onFinish(self):
"""打印结束回调"""
pass
def onLayout(self, old_attributes: PrintAttributes, new_attributes: PrintAttributes,
cancellation_signal: Any, callback: "PrintDocumentAdapterLayoutCallback",
metadata: Any):
"""布局回调 - 桌面实现直接完成"""
if callback:
callback.onLayoutFinished(self, new_attributes)
def onWrite(self, pages: List[int], destination: Any,
cancellation_signal: Any,
callback: "PrintDocumentAdapterWriteCallback"):
"""写入回调 - 桌面实现直接完成"""
if callback:
callback.onWriteFinished(pages)
class PrintDocumentAdapterLayoutCallback(Stub):
"""PrintDocumentAdapterLayoutCallback — 布局回调"""
def __init__(self):
super().__init__()
def onLayoutFinished(self, adapter: PrintDocumentAdapter, attributes: PrintAttributes):
"""布局完成"""
pass
def onLayoutFailed(self, error: Optional[str] = None):
"""布局失败"""
pass
def onLayoutCancelled(self):
"""布局取消"""
pass
class PrintDocumentAdapterWriteCallback(Stub):
"""PrintDocumentAdapterWriteCallback — 写入回调"""
def __init__(self):
super().__init__()
def onWriteFinished(self, pages: List[int]):
"""写入完成"""
pass
def onWriteFailed(self, error: Optional[str] = None):
"""写入失败"""
pass
def onWriteCancelled(self):
"""写入取消"""
pass
# ═══════════════════════════════════════════════════════════════
# PdfPrint
# ═══════════════════════════════════════════════════════════════
class PdfPrint(Stub):
"""PdfPrint — PDF 打印辅助类"""
def __init__(self, attributes: Optional[PrintAttributes] = None):
super().__init__()
self._attributes = attributes or PrintAttributes()
def getAttributes(self) -> PrintAttributes:
return self._attributes
def setAttributes(self, attributes: PrintAttributes):
self._attributes = attributes
def print(self, print_adapter: PrintDocumentAdapter, output_path: str) -> bool:
"""执行 PDF 打印 (桌面实现: 写入文件)"""
try:
os.makedirs(os.path.dirname(os.path.abspath(output_path)), exist_ok=True)
# 在桌面环境, PDF 打印由适配器的 onWrite 实现处理
callback = PrintDocumentAdapterWriteCallback()
print_adapter.onWrite([1], output_path, None, callback)
return True
except Exception:
return False
def toString(self) -> str:
return f"PdfPrint(attributes={self._attributes})"
def hashCode(self) -> int:
return hash(str(self._attributes)) & 0x7FFFFFFF
def equals(self, other) -> bool:
if not isinstance(other, PdfPrint):
return False
return str(self._attributes) == str(other._attributes)
# ═══════════════════════════════════════════════════════════════
# PrintManager
# ═══════════════════════════════════════════════════════════════
class PrintManager(Stub):
"""PrintManager — 打印管理器"""
# 打印任务 ID 生成
_next_job_id = 1
def __init__(self, context: Any = None):
super().__init__()
self._context = context
self._print_jobs: Dict[str, PrintJob] = {}
def getPrintJobs(self) -> List[PrintJob]:
"""获取打印任务列表"""
return list(self._print_jobs.values())
def print(self, job_name: str, document_adapter: PrintDocumentAdapter,
attributes: Optional[PrintAttributes] = None) -> PrintJob:
"""提交打印任务"""
info = PrintJobInfo()
info.setLabel(job_name)
info.setAttributes(attributes or PrintAttributes())
info.setState(PrintJobInfo.STATE_QUEUED)
job_id = f"print_job_{PrintManager._next_job_id}"
PrintManager._next_job_id += 1
job = PrintJob(info)
self._print_jobs[job_id] = job
# 自动触发布局和写入
cancellation_signal = type("CancellationSignal", (), {"isCanceled": lambda: False})()
document_adapter.onStart()
layout_cb = PrintDocumentAdapterLayoutCallback()
document_adapter.onLayout(None, info.getAttributes(),
cancellation_signal, layout_cb, None)
info.setState(PrintJobInfo.STATE_COMPLETED)
document_adapter.onFinish()
return job
def getPrintJobById(self, job_id: str) -> Optional[PrintJob]:
"""根据 ID 获取打印任务"""
return self._print_jobs.get(job_id)
def cancelPrintJob(self, job_id: str) -> bool:
"""取消打印任务"""
job = self._print_jobs.get(job_id)
if job:
job.cancel()
return True
return False
def toString(self) -> str:
return f"PrintManager(jobs={len(self._print_jobs)})"
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
def equals(self, other) -> bool:
return self is other
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"PrintManager",
"PrintJob",
"PrintJobInfo",
"PrintDocumentAdapter",
"PrintAttributes",
"PrintDocumentAdapterLayoutCallback",
"PrintDocumentAdapterWriteCallback",
"PdfPrint",
]
+903
View File
@@ -0,0 +1,903 @@
from __future__ import annotations
"""
Android RenderScript 组件 — RenderScript, Allocation, Element, Type, Script 等
方法签名完全对齐 Android SDK (android.renderscript 包)
"""
import math
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
from .._core import Stub, AndroidOnlyError
# ═══════════════════════════════════════════════════════════════
# RenderScript
# ═══════════════════════════════════════════════════════════════
class RenderScript(Stub):
"""RenderScript — RenderScript 运行时上下文 (桌面仅存根)"""
# 优先级
Priority_LOW = 0
Priority_NORMAL = 1
Priority_HIGH = 2
# 上下文类型
ContextType_NORMAL = 0
ContextType_DEBUG = 1
ContextType_PROFILE = 2
def __init__(self):
super().__init__()
self._context_type = RenderScript.ContextType_NORMAL
self._error_handler: Optional[Callable] = None
self._message_handler: Optional[Callable] = None
self._priority = RenderScript.Priority_NORMAL
self._initialized = False
@staticmethod
def create(context: Any = None) -> "RenderScript":
"""创建 RenderScript 上下文"""
rs = RenderScript()
rs._initialized = True
return rs
@staticmethod
def createMultiContext(context: Any = None,
context_type: int = ContextType_NORMAL,
flags: int = 0,
api_version: int = 0) -> "RenderScript":
"""创建多上下文 RenderScript"""
rs = RenderScript()
rs._context_type = context_type
rs._initialized = True
return rs
def setPriority(self, priority: int):
"""设置优先级"""
self._priority = priority
def getPriority(self) -> int:
return self._priority
def setErrorHandler(self, handler: Callable):
"""设置错误处理器"""
self._error_handler = handler
def setMessageHandler(self, handler: Callable):
"""设置消息处理器"""
self._message_handler = handler
def finish(self):
"""等待所有挂起渲染完成"""
pass
def destroy(self):
"""销毁 RenderScript 上下文"""
self._initialized = False
def getApplicationContext(self) -> Any:
return None
def getErrorHandler(self) -> Optional[Callable]:
return self._error_handler
def getMessageHandler(self) -> Optional[Callable]:
return self._message_handler
def isDestroyed(self) -> bool:
return not self._initialized
def nvLoadAll(self):
pass
def nvLoadFont(self, name: str) -> int:
return 0
def nvLoadProp(self, name: str) -> int:
return 0
def toString(self) -> str:
return f"RenderScript(priority={self._priority}, initialized={self._initialized})"
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
def equals(self, other) -> bool:
return self is other
# ═══════════════════════════════════════════════════════════════
# Element
# ═══════════════════════════════════════════════════════════════
class Element(Stub):
"""Element — RenderScript 数据类型元素"""
# 预定义数据类型
F32: "Element" = None
F64: "Element" = None
I8: "Element" = None
I16: "Element" = None
I32: "Element" = None
I64: "Element" = None
U8: "Element" = None
U16: "Element" = None
U32: "Element" = None
U64: "Element" = None
BOOLEAN: "Element" = None
ALLOCATION: "Element" = None
A_8: "Element" = None
RGB_565: "Element" = None
RGBA_4444: "Element" = None
RGBA_8888: "Element" = None
F32_2: "Element" = None
F32_3: "Element" = None
F32_4: "Element" = None
F64_2: "Element" = None
F64_3: "Element" = None
F64_4: "Element" = None
I8_2: "Element" = None
I8_3: "Element" = None
I8_4: "Element" = None
I16_2: "Element" = None
I16_3: "Element" = None
I16_4: "Element" = None
I32_2: "Element" = None
I32_3: "Element" = None
I32_4: "Element" = None
I64_2: "Element" = None
I64_3: "Element" = None
I64_4: "Element" = None
U8_2: "Element" = None
U8_3: "Element" = None
U8_4: "Element" = None
U16_2: "Element" = None
U16_3: "Element" = None
U16_4: "Element" = None
U32_2: "Element" = None
U32_3: "Element" = None
U32_4: "Element" = None
U64_2: "Element" = None
U64_3: "Element" = None
U64_4: "Element" = None
# 数据类型
DataType = type("DataType", (), {
"NONE": 0, "FLOAT_32": 1, "FLOAT_64": 2,
"SIGNED_8": 3, "SIGNED_16": 4, "SIGNED_32": 5, "SIGNED_64": 6,
"UNSIGNED_8": 7, "UNSIGNED_16": 8, "UNSIGNED_32": 9, "UNSIGNED_64": 10,
"BOOLEAN": 11, "UNSIGNED_5_6_5": 12, "UNSIGNED_5_5_5_1": 13,
"UNSIGNED_4_4_4_4": 14, "MATRIX_4X4": 15, "MATRIX_3X3": 16,
"UNSIGNED_2_10_10_10": 17, "RS_ELEMENT": 18,
})
# 数据类型大小
DataType = DataType()
def __init__(self, rs: Optional[RenderScript] = None, data_type: int = 0,
vector_size: int = 1):
super().__init__()
self._rs = rs
self._data_type = data_type
self._vector_size = vector_size
self._element_size = self._compute_size()
def _compute_size(self) -> int:
base_sizes = {
Element.DataType.FLOAT_32: 4, Element.DataType.FLOAT_64: 8,
Element.DataType.SIGNED_8: 1, Element.DataType.SIGNED_16: 2,
Element.DataType.SIGNED_32: 4, Element.DataType.SIGNED_64: 8,
Element.DataType.UNSIGNED_8: 1, Element.DataType.UNSIGNED_16: 2,
Element.DataType.UNSIGNED_32: 4, Element.DataType.UNSIGNED_64: 8,
Element.DataType.BOOLEAN: 1,
Element.DataType.UNSIGNED_5_6_5: 2,
Element.DataType.UNSIGNED_5_5_5_1: 2,
Element.DataType.UNSIGNED_4_4_4_4: 2,
Element.DataType.MATRIX_4X4: 64,
Element.DataType.MATRIX_3X3: 36,
Element.DataType.RS_ELEMENT: 4,
}
return base_sizes.get(self._data_type, 4) * self._vector_size
def getBytesSize(self) -> int:
return self._element_size
def getVectorSize(self) -> int:
return self._vector_size
def getDataType(self) -> int:
return self._data_type
def isCompatible(self, e: "Element") -> bool:
return self._data_type == e._data_type and self._vector_size == e._vector_size
def getSubElement(self, index: int) -> Optional["Element"]:
return None
def getSubElementCount(self) -> int:
return 0
def getSubElementArraySize(self, index: int) -> int:
return 0
def getSubElementOffsetBytes(self, index: int) -> int:
return 0
def toString(self) -> str:
return f"Element(dataType={self._data_type}, vectorSize={self._vector_size})"
def hashCode(self) -> int:
return hash((self._data_type, self._vector_size)) & 0x7FFFFFFF
def equals(self, other) -> bool:
if not isinstance(other, Element):
return False
return self._data_type == other._data_type and self._vector_size == other._vector_size
@staticmethod
def BOOL(rs: RenderScript) -> "Element":
return Element(rs, Element.DataType.BOOLEAN, 1)
@staticmethod
def U8(rs: RenderScript) -> "Element":
return Element(rs, Element.DataType.UNSIGNED_8, 1)
@staticmethod
def I8(rs: RenderScript) -> "Element":
return Element(rs, Element.DataType.SIGNED_8, 1)
@staticmethod
def U16(rs: RenderScript) -> "Element":
return Element(rs, Element.DataType.UNSIGNED_16, 1)
@staticmethod
def I16(rs: RenderScript) -> "Element":
return Element(rs, Element.DataType.SIGNED_16, 1)
@staticmethod
def U32(rs: RenderScript) -> "Element":
return Element(rs, Element.DataType.UNSIGNED_32, 1)
@staticmethod
def I32(rs: RenderScript) -> "Element":
return Element(rs, Element.DataType.SIGNED_32, 1)
@staticmethod
def U64(rs: RenderScript) -> "Element":
return Element(rs, Element.DataType.UNSIGNED_64, 1)
@staticmethod
def I64(rs: RenderScript) -> "Element":
return Element(rs, Element.DataType.SIGNED_64, 1)
@staticmethod
def F32(rs: RenderScript) -> "Element":
return Element(rs, Element.DataType.FLOAT_32, 1)
@staticmethod
def F64(rs: RenderScript) -> "Element":
return Element(rs, Element.DataType.FLOAT_64, 1)
@staticmethod
def createVector(rs: RenderScript, data_type: int, size: int) -> "Element":
return Element(rs, data_type, size)
@staticmethod
def createPixel(rs: RenderScript, data_type: int, pixel_type: int) -> "Element":
return Element(rs, data_type, 4)
# ═══════════════════════════════════════════════════════════════
# Type
# ═══════════════════════════════════════════════════════════════
class Type(Stub):
"""Type — RenderScript 内存分配类型"""
CubemapFace = type("CubemapFace", (), {
"POSITIVE_X": 0, "NEGATIVE_X": 1,
"POSITIVE_Y": 2, "NEGATIVE_Y": 3,
"POSITIVE_Z": 4, "NEGATIVE_Z": 5,
})
def __init__(self, rs: Optional[RenderScript] = None,
element: Optional[Element] = None,
dim_x: int = 0, dim_y: int = 0, dim_z: int = 0,
mipmaps: bool = False, faces: bool = False):
super().__init__()
self._rs = rs
self._element = element
self._dim_x = dim_x
self._dim_y = dim_y
self._dim_z = dim_z
self._mipmaps = mipmaps
self._faces = faces
self._yuv_format = 0
def getElement(self) -> Optional[Element]:
return self._element
def getX(self) -> int:
return self._dim_x
def getY(self) -> int:
return self._dim_y
def getZ(self) -> int:
return self._dim_z
def getCount(self) -> int:
return self._dim_x * max(self._dim_y, 1) * max(self._dim_z, 1)
def hasMipmaps(self) -> bool:
return self._mipmaps
def hasFaces(self) -> bool:
return self._faces
def getYuvFormat(self) -> int:
return self._yuv_format
def setYuvFormat(self, yuv_format: int):
self._yuv_format = yuv_format
def toString(self) -> str:
return (f"Type(element={self._element}, dims={self._dim_x}x{self._dim_y}"
f"x{self._dim_z}, mipmaps={self._mipmaps})")
def hashCode(self) -> int:
return hash((self._dim_x, self._dim_y, self._dim_z)) & 0x7FFFFFFF
def equals(self, other) -> bool:
if not isinstance(other, Type):
return False
return (self._dim_x == other._dim_x and self._dim_y == other._dim_y
and self._dim_z == other._dim_z)
# ── Builder ──
class Builder(Stub):
"""Builder — Type 构建器"""
def __init__(self, rs: RenderScript, element: Element):
super().__init__()
self._rs = rs
self._element = element
self._dim_x = 0
self._dim_y = 0
self._dim_z = 0
self._mipmaps = False
self._faces = False
self._yuv_format = 0
def setX(self, value: int) -> "Type.Builder":
self._dim_x = value
return self
def setY(self, value: int) -> "Type.Builder":
self._dim_y = value
return self
def setZ(self, value: int) -> "Type.Builder":
self._dim_z = value
return self
def setMipmaps(self, enable: bool) -> "Type.Builder":
self._mipmaps = enable
return self
def setFaces(self, enable: bool) -> "Type.Builder":
self._faces = enable
return self
def setYuvFormat(self, yuv_format: int) -> "Type.Builder":
self._yuv_format = yuv_format
return self
def create(self) -> Type:
t = Type(self._rs, self._element, self._dim_x, self._dim_y,
self._dim_z, self._mipmaps, self._faces)
t._yuv_format = self._yuv_format
return t
# ═══════════════════════════════════════════════════════════════
# Allocation
# ═══════════════════════════════════════════════════════════════
class Allocation(Stub):
"""Allocation — RenderScript 内存分配"""
# 使用类型
USAGE_SCRIPT = 0x0001
USAGE_GRAPHICS_TEXTURE = 0x0002
USAGE_GRAPHICS_VERTEX = 0x0004
USAGE_GRAPHICS_CONSTANTS = 0x0008
USAGE_GRAPHICS_RENDER_TARGET = 0x0010
USAGE_IO_INPUT = 0x0020
USAGE_IO_OUTPUT = 0x0040
USAGE_SHARED = 0x0080
# Mipmap 控制
MIPMAP_FULL = 0
MIPMAP_ON_SYNC_TO_TEXTURE = 1
def __init__(self, rs: Optional[RenderScript] = None,
type_: Optional[Type] = None,
usage: int = USAGE_SCRIPT):
super().__init__()
self._rs = rs
self._type = type_
self._usage = usage
self._data: Any = None
self._element_size = type_.getElement().getBytesSize() if type_ and type_.getElement() else 0
@staticmethod
def createTyped(rs: RenderScript, type_: Type,
usage: int = USAGE_SCRIPT) -> "Allocation":
"""创建分配"""
alloc = Allocation(rs, type_, usage)
total_size = type_.getCount() * type_.getElement().getBytesSize() if type_.getElement() else 0
alloc._data = bytearray(total_size)
return alloc
@staticmethod
def createFromBitmap(rs: RenderScript, bitmap: Any,
usage: int = USAGE_SCRIPT) -> "Allocation":
"""从位图创建分配"""
# 尝试获取位图尺寸
width, height = 0, 0
try:
width = bitmap.getWidth() if hasattr(bitmap, 'getWidth') else 0
height = bitmap.getHeight() if hasattr(bitmap, 'getHeight') else 0
except Exception:
pass
element = Element(rs, Element.DataType.UNSIGNED_8, 4)
type_ = Type(rs, element, width, height)
alloc = Allocation(rs, type_, usage)
alloc._data = bytearray(width * height * 4)
return alloc
@staticmethod
def createFromBitmapBoxed(rs: RenderScript, bitmap: Any,
usage: int = USAGE_SCRIPT) -> "Allocation":
return Allocation.createFromBitmap(rs, bitmap, usage)
def copyFrom(self, data: Any):
"""从数组或位图复制数据"""
if isinstance(data, (bytes, bytearray)):
self._data = bytearray(data)
elif hasattr(data, '__iter__'):
import struct
try:
self._data = bytearray(struct.pack(f'<{len(list(data))}f',
*[float(v) for v in data]))
except Exception:
self._data = bytearray(len(list(data)) * 4)
else:
self._data = bytearray(1024)
def copyTo(self, data: Any):
"""将数据复制到目标"""
if isinstance(data, bytearray) and self._data:
data[:] = self._data
elif isinstance(data, list):
data.clear()
if self._data:
for i in range(0, len(self._data), 4):
data.append(int.from_bytes(self._data[i:i+4], 'little'))
def resize(self, dim_x: int):
"""重新调整分配大小"""
if self._type:
self._type._dim_x = dim_x
self._data = bytearray(self._type.getCount() * self._element_size)
def getType(self) -> Optional[Type]:
return self._type
def getUsage(self) -> int:
return self._usage
def getElement(self) -> Optional[Element]:
return self._type.getElement() if self._type else None
def syncAll(self, src_location: int):
pass
def destroy(self):
self._data = None
def toString(self) -> str:
return (f"Allocation(type={self._type}, usage={self._usage}, "
f"data_size={len(self._data) if self._data else 0})")
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
def equals(self, other) -> bool:
return self is other
# ═══════════════════════════════════════════════════════════════
# Matrix4f
# ═══════════════════════════════════════════════════════════════
class Matrix4f(Stub):
"""Matrix4f — 4x4 浮点矩阵"""
def __init__(self):
super().__init__()
self._matrix = [0.0] * 16
self._matrix[0] = 1.0 # 单位矩阵
self._matrix[5] = 1.0
self._matrix[10] = 1.0
self._matrix[15] = 1.0
def getArray(self) -> List[float]:
return list(self._matrix)
def setArray(self, array: List[float]):
if len(array) >= 16:
self._matrix = list(array[:16])
def loadIdentity(self):
self._matrix = [0.0] * 16
self._matrix[0] = 1.0
self._matrix[5] = 1.0
self._matrix[10] = 1.0
self._matrix[15] = 1.0
def load(self, src: "Matrix4f"):
self._matrix = list(src._matrix)
def loadRotate(self, rot: float, x: float, y: float, z: float):
angle_rad = math.radians(rot)
c = math.cos(angle_rad)
s = math.sin(angle_rad)
self.loadIdentity()
if x == 1 and y == 0 and z == 0:
self._matrix[5] = c
self._matrix[6] = -s
self._matrix[9] = s
self._matrix[10] = c
elif x == 0 and y == 1 and z == 0:
self._matrix[0] = c
self._matrix[2] = s
self._matrix[8] = -s
self._matrix[10] = c
elif x == 0 and y == 0 and z == 1:
self._matrix[0] = c
self._matrix[1] = -s
self._matrix[4] = s
self._matrix[5] = c
def loadScale(self, x: float, y: float, z: float):
self.loadIdentity()
self._matrix[0] = x
self._matrix[5] = y
self._matrix[10] = z
def loadTranslate(self, x: float, y: float, z: float):
self.loadIdentity()
self._matrix[12] = x
self._matrix[13] = y
self._matrix[14] = z
def loadMultiply(self, lhs: "Matrix4f", rhs: "Matrix4f"):
for i in range(4):
for j in range(4):
self._matrix[i * 4 + j] = sum(
lhs._matrix[i * 4 + k] * rhs._matrix[k * 4 + j]
for k in range(4)
)
def multiply(self, rhs: "Matrix4f"):
result = [0.0] * 16
for i in range(4):
for j in range(4):
result[i * 4 + j] = sum(
self._matrix[i * 4 + k] * rhs._matrix[k * 4 + j]
for k in range(4)
)
self._matrix = result
def transpose(self):
for i in range(4):
for j in range(i + 1, 4):
self._matrix[i * 4 + j], self._matrix[j * 4 + i] = \
self._matrix[j * 4 + i], self._matrix[i * 4 + j]
def toString(self) -> str:
rows = []
for i in range(4):
row = self._matrix[i*4:(i+1)*4]
rows.append(f"[{', '.join(f'{v:.4f}' for v in row)}]")
return f"Matrix4f({', '.join(rows)})"
def hashCode(self) -> int:
return hash(tuple(self._matrix)) & 0x7FFFFFFF
def equals(self, other) -> bool:
if not isinstance(other, Matrix4f):
return False
return self._matrix == other._matrix
# ═══════════════════════════════════════════════════════════════
# Matrix3f
# ═══════════════════════════════════════════════════════════════
class Matrix3f(Stub):
"""Matrix3f — 3x3 浮点矩阵"""
def __init__(self):
super().__init__()
self._matrix = [0.0] * 9
self._matrix[0] = 1.0
self._matrix[4] = 1.0
self._matrix[8] = 1.0
def getArray(self) -> List[float]:
return list(self._matrix)
def setArray(self, array: List[float]):
if len(array) >= 9:
self._matrix = list(array[:9])
def loadIdentity(self):
self._matrix = [0.0] * 9
self._matrix[0] = 1.0
self._matrix[4] = 1.0
self._matrix[8] = 1.0
def load(self, src: "Matrix3f"):
self._matrix = list(src._matrix)
def loadMultiply(self, lhs: "Matrix3f", rhs: "Matrix3f"):
for i in range(3):
for j in range(3):
self._matrix[i * 3 + j] = sum(
lhs._matrix[i * 3 + k] * rhs._matrix[k * 3 + j]
for k in range(3)
)
def multiply(self, rhs: "Matrix3f"):
result = [0.0] * 9
for i in range(3):
for j in range(3):
result[i * 3 + j] = sum(
self._matrix[i * 3 + k] * rhs._matrix[k * 3 + j]
for k in range(3)
)
self._matrix = result
def transpose(self):
for i in range(3):
for j in range(i + 1, 3):
self._matrix[i * 3 + j], self._matrix[j * 3 + i] = \
self._matrix[j * 3 + i], self._matrix[i * 3 + j]
def toString(self) -> str:
rows = []
for i in range(3):
row = self._matrix[i*3:(i+1)*3]
rows.append(f"[{', '.join(f'{v:.4f}' for v in row)}]")
return f"Matrix3f({', '.join(rows)})"
def hashCode(self) -> int:
return hash(tuple(self._matrix)) & 0x7FFFFFFF
def equals(self, other) -> bool:
if not isinstance(other, Matrix3f):
return False
return self._matrix == other._matrix
# ═══════════════════════════════════════════════════════════════
# Script (base)
# ═══════════════════════════════════════════════════════════════
class Script(Stub):
"""Script — RenderScript 脚本基类"""
def __init__(self, rs: Optional[RenderScript] = None):
super().__init__()
self._rs = rs
self._kernel_groups: List[Any] = []
def setVar(self, index: int, value: Any):
"""设置脚本变量"""
pass
def getVar(self, index: int) -> Any:
return None
def setTimeZone(self, timezone: str):
pass
def forEach(self, slot: int, ains: List[Allocation],
aout: Allocation, vdata: Any = None):
"""执行 forEach 内核"""
raise AndroidOnlyError("Script.forEach() 仅在 Android (RenderScript) 设备上可用")
def invoke(self, slot: int):
"""调用可调用函数"""
pass
def bindAllocation(self, va: Allocation, slot: int):
"""绑定分配"""
pass
def destroy(self):
pass
def toString(self) -> str:
return f"Script@{hex(id(self))}"
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
def equals(self, other) -> bool:
return self is other
# ═══════════════════════════════════════════════════════════════
# ScriptC
# ═══════════════════════════════════════════════════════════════
class ScriptC(Script):
"""ScriptC — 编译的 RenderScript 内核脚本"""
def __init__(self, rs: RenderScript, resource_id: int = 0):
super().__init__(rs)
self._resource_id = resource_id
def toString(self) -> str:
return f"ScriptC(resourceId={self._resource_id})"
# ═══════════════════════════════════════════════════════════════
# ScriptIntrinsicBlur
# ═══════════════════════════════════════════════════════════════
class ScriptIntrinsicBlur(Script):
"""ScriptIntrinsicBlur — 高斯模糊内在函数"""
def __init__(self, rs: RenderScript):
super().__init__(rs)
self._radius: float = 5.0
self._input: Optional[Allocation] = None
@staticmethod
def create(rs: RenderScript, e: Element) -> "ScriptIntrinsicBlur":
"""创建模糊内在函数"""
return ScriptIntrinsicBlur(rs)
def setRadius(self, radius: float):
"""设置模糊半径"""
self._radius = max(0.0, min(radius, 25.0))
def setInput(self, alloc: Allocation):
"""设置输入分配"""
self._input = alloc
def forEach(self, aout: Allocation):
"""执行模糊"""
if self._input and aout:
pass # 在桌面环境不执行实际模糊
def toString(self) -> str:
return f"ScriptIntrinsicBlur(radius={self._radius})"
# ═══════════════════════════════════════════════════════════════
# ScriptIntrinsicConvolve3x3
# ═══════════════════════════════════════════════════════════════
class ScriptIntrinsicConvolve3x3(Script):
"""ScriptIntrinsicConvolve3x3 — 3x3 卷积内在函数"""
def __init__(self, rs: RenderScript):
super().__init__(rs)
self._coefficients: List[float] = [0.0] * 9
self._input: Optional[Allocation] = None
@staticmethod
def create(rs: RenderScript, e: Element) -> "ScriptIntrinsicConvolve3x3":
"""创建卷积内在函数"""
return ScriptIntrinsicConvolve3x3(rs)
def setCoefficients(self, coeffs: List[float]):
"""设置卷积系数"""
for i in range(min(len(coeffs), 9)):
self._coefficients[i] = coeffs[i]
def setInput(self, alloc: Allocation):
"""设置输入分配"""
self._input = alloc
def forEach(self, aout: Allocation):
"""执行卷积"""
pass
def toString(self) -> str:
return f"ScriptIntrinsicConvolve3x3(coefficients={self._coefficients})"
# ═══════════════════════════════════════════════════════════════
# 初始化 Element 静态实例
# ═══════════════════════════════════════════════════════════════
def _init_elements():
"""初始化 Element 预定义实例(在模块导入时调用)"""
rs = RenderScript.create()
Element.F32 = Element.F32(rs)
Element.F64 = Element.F64(rs)
Element.I8 = Element.I8(rs)
Element.I16 = Element.I16(rs)
Element.I32 = Element.I32(rs)
Element.I64 = Element.I64(rs)
Element.U8 = Element.U8(rs)
Element.U16 = Element.U16(rs)
Element.U32 = Element.U32(rs)
Element.U64 = Element.U64(rs)
Element.BOOLEAN = Element.BOOL(rs)
Element.ALLOCATION = Element(rs, Element.DataType.RS_ELEMENT, 1)
Element.A_8 = Element.createPixel(rs, Element.DataType.UNSIGNED_8, 1)
Element.RGB_565 = Element.createPixel(rs, Element.DataType.UNSIGNED_5_6_5, 3)
Element.RGBA_4444 = Element.createPixel(rs, Element.DataType.UNSIGNED_4_4_4_4, 4)
Element.RGBA_8888 = Element.createPixel(rs, Element.DataType.UNSIGNED_8, 4)
Element.F32_2 = Element.createVector(rs, Element.DataType.FLOAT_32, 2)
Element.F32_3 = Element.createVector(rs, Element.DataType.FLOAT_32, 3)
Element.F32_4 = Element.createVector(rs, Element.DataType.FLOAT_32, 4)
Element.F64_2 = Element.createVector(rs, Element.DataType.FLOAT_64, 2)
Element.F64_3 = Element.createVector(rs, Element.DataType.FLOAT_64, 3)
Element.F64_4 = Element.createVector(rs, Element.DataType.FLOAT_64, 4)
Element.I8_2 = Element.createVector(rs, Element.DataType.SIGNED_8, 2)
Element.I8_3 = Element.createVector(rs, Element.DataType.SIGNED_8, 3)
Element.I8_4 = Element.createVector(rs, Element.DataType.SIGNED_8, 4)
Element.I16_2 = Element.createVector(rs, Element.DataType.SIGNED_16, 2)
Element.I16_3 = Element.createVector(rs, Element.DataType.SIGNED_16, 3)
Element.I16_4 = Element.createVector(rs, Element.DataType.SIGNED_16, 4)
Element.I32_2 = Element.createVector(rs, Element.DataType.SIGNED_32, 2)
Element.I32_3 = Element.createVector(rs, Element.DataType.SIGNED_32, 3)
Element.I32_4 = Element.createVector(rs, Element.DataType.SIGNED_32, 4)
Element.I64_2 = Element.createVector(rs, Element.DataType.SIGNED_64, 2)
Element.I64_3 = Element.createVector(rs, Element.DataType.SIGNED_64, 3)
Element.I64_4 = Element.createVector(rs, Element.DataType.SIGNED_64, 4)
Element.U8_2 = Element.createVector(rs, Element.DataType.UNSIGNED_8, 2)
Element.U8_3 = Element.createVector(rs, Element.DataType.UNSIGNED_8, 3)
Element.U8_4 = Element.createVector(rs, Element.DataType.UNSIGNED_8, 4)
Element.U16_2 = Element.createVector(rs, Element.DataType.UNSIGNED_16, 2)
Element.U16_3 = Element.createVector(rs, Element.DataType.UNSIGNED_16, 3)
Element.U16_4 = Element.createVector(rs, Element.DataType.UNSIGNED_16, 4)
Element.U32_2 = Element.createVector(rs, Element.DataType.UNSIGNED_32, 2)
Element.U32_3 = Element.createVector(rs, Element.DataType.UNSIGNED_32, 3)
Element.U32_4 = Element.createVector(rs, Element.DataType.UNSIGNED_32, 4)
Element.U64_2 = Element.createVector(rs, Element.DataType.UNSIGNED_64, 2)
Element.U64_3 = Element.createVector(rs, Element.DataType.UNSIGNED_64, 3)
Element.U64_4 = Element.createVector(rs, Element.DataType.UNSIGNED_64, 4)
_init_elements()
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"RenderScript",
"Allocation",
"Element",
"Type",
"Script",
"ScriptIntrinsicBlur",
"ScriptIntrinsicConvolve3x3",
"ScriptC",
"Matrix4f",
"Matrix3f",
]
+175
View File
@@ -0,0 +1,175 @@
"""
Android se 组件 — SecureElement, Session, Channel, Reader, OmapiConnection 等
方法签名完全对齐 Android SDK (SecureElement API)
"""
from typing import Any, Callable, Dict, List, Optional
from .._core import Stub, AndroidOnlyError
# ═══════════════════════════════════════════════════════════════
# SecureElement
# ═══════════════════════════════════════════════════════════════
class SecureElement(Stub):
"""SecureElement — 安全元件"""
SERVICE_INTERFACE = "android.se.SECURE_ELEMENT_SERVICE"
EXTRA_READER = "android.se.extra.READER"
EXTRA_READER_NAME = "android.se.extra.READER_NAME"
EXTRA_SLOT_ID = "android.se.extra.SLOT_ID"
EXTRA_CONNECT_RESPONSE = "android.se.extra.CONNECT_RESPONSE"
def __init__(self):
super().__init__()
# ═══════════════════════════════════════════════════════════════
# Reader
# ═══════════════════════════════════════════════════════════════
class Reader(Stub):
"""Reader — 安全元件读取器"""
def __init__(self, name: str = "eSE"):
super().__init__()
self._name = name
self._sessions: List[Session] = []
def getName(self) -> str:
return self._name
def openSession(self) -> "Session":
session = Session(self)
self._sessions.append(session)
return session
def closeSessions(self):
for s in self._sessions:
s.close()
self._sessions.clear()
def isSecureElementPresent(self) -> bool:
return True
def toString(self) -> str:
return f"Reader(name={self._name})"
# ═══════════════════════════════════════════════════════════════
# Session
# ═══════════════════════════════════════════════════════════════
class Session(Stub):
"""Session — 安全元件会话"""
def __init__(self, reader: Optional[Reader] = None):
super().__init__()
self._reader = reader
self._channels: List[Channel] = []
self._closed = False
def openLogicalChannel(self, aid: bytes) -> Optional["Channel"]:
if self._closed:
return None
channel = Channel(self, logical=True)
self._channels.append(channel)
channel._select_response = bytes([0x90, 0x00]) # SW_OK
return channel
def openBasicChannel(self, aid: bytes) -> Optional["Channel"]:
if self._closed:
return None
channel = Channel(self, logical=False)
self._channels.append(channel)
channel._select_response = bytes([0x90, 0x00]) # SW_OK
return channel
def close(self):
for c in self._channels:
c.close()
self._channels.clear()
self._closed = True
def getSessions(self) -> List["Session"]:
return [self]
def getChannels(self) -> List["Channel"]:
return list(self._channels)
def toString(self) -> str:
return f"Session(reader={self._reader}, channels={len(self._channels)})"
# ═══════════════════════════════════════════════════════════════
# Channel
# ═══════════════════════════════════════════════════════════════
class Channel(Stub):
"""Channel — 安全元件通道"""
def __init__(self, session: Session, logical: bool = True):
super().__init__()
self._session = session
self._logical = logical
self._closed = False
self._select_response: Optional[bytes] = None
def close(self):
self._closed = True
def sendData(self, command: bytes) -> bytes:
if self._closed:
return bytes([0x6F, 0x00]) # No such EF
# Simulate successful APDU response
return bytes([0x90, 0x00])
def getSelectResponse(self) -> Optional[bytes]:
return self._select_response
def getSession(self) -> Session:
return self._session
def isBasicChannel(self) -> bool:
return not self._logical
def isClosed(self) -> bool:
return self._closed
def toString(self) -> str:
return f"Channel(logical={self._logical}, closed={self._closed})"
# ═══════════════════════════════════════════════════════════════
# OmapiConnection
# ═══════════════════════════════════════════════════════════════
class OmapiConnection(Stub):
"""OmapiConnection — OMAPI 连接入口"""
def __init__(self):
super().__init__()
@staticmethod
def getInstance(context: Any = None) -> "OmapiConnection":
return OmapiConnection()
def getReaders(self) -> List[Reader]:
return [Reader("eSE")]
def getReader(self, name: str) -> Optional[Reader]:
return Reader(name)
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"SecureElement",
"Reader",
"Session",
"Channel",
"OmapiConnection",
]
+182
View File
@@ -0,0 +1,182 @@
"""
Android Security 组件 — KeyChain, NetworkSecurityPolicy, AppUriAuthenticationPolicy 等
方法签名完全对齐 Android SDK (android.security 包)
"""
from typing import Any, Callable, Dict, List, Optional
from .._core import Stub, AndroidOnlyError
# ═══════════════════════════════════════════════════════════════
# KeyChain
# ═══════════════════════════════════════════════════════════════
class KeyChain(Stub):
"""KeyChain — 系统级密钥链访问 (桌面环境不可用)"""
ACTION_KEYCHAIN_CHANGED = "android.security.action.KEYCHAIN_CHANGED"
ACTION_KEY_ACCESS_CHANGED = "android.security.action.KEY_ACCESS_CHANGED"
EXTRA_KEY_ACCESSIBLE = "android.security.extra.KEY_ACCESSIBLE"
EXTRA_KEY_ALIAS = "android.security.extra.KEY_ALIAS"
EXTRA_RESULT = "android.security.extra.RESULT"
def __init__(self):
super().__init__()
@staticmethod
def createInstallIntent() -> Any:
"""创建安装证书的 Intent (Android-only)"""
raise AndroidOnlyError("KeyChain.createInstallIntent() 仅在 Android 设备上可用")
@staticmethod
def choosePrivateKeyAlias(activity: Any, response: Any,
keyTypes: Optional[List[str]] = None,
issuers: Optional[List[Any]] = None,
host: Optional[str] = None,
port: int = -1,
alias: Optional[str] = None):
"""选择私钥别名 (Android-only)"""
raise AndroidOnlyError("KeyChain.choosePrivateKeyAlias() 仅在 Android 设备上可用")
@staticmethod
def isKeyAlgorithmSupported(algorithm: str) -> bool:
"""检查密钥算法是否支持"""
supported = {"RSA", "EC", "DSA", "AES", "HmacSHA256", "HmacSHA1"}
return algorithm in supported
@staticmethod
def isBoundKeyAlgorithm(algorithm: str) -> bool:
"""检查是否是绑定到硬件的密钥算法 (桌面环境不支持)"""
return False
@staticmethod
def getPrivateKey(context: Any, alias: str) -> Any:
"""获取私钥 (Android-only)"""
raise AndroidOnlyError("KeyChain.getPrivateKey() 仅在 Android 设备上可用")
@staticmethod
def getCertificateChain(context: Any, alias: str) -> Optional[List[Any]]:
"""获取证书链 (Android-only)"""
raise AndroidOnlyError("KeyChain.getCertificateChain() 仅在 Android 设备上可用")
def toString(self) -> str:
return "KeyChain(desktop-stub)"
def hashCode(self) -> int:
return hash("android.security.KeyChain") & 0x7FFFFFFF
def equals(self, other) -> bool:
return isinstance(other, KeyChain)
# ═══════════════════════════════════════════════════════════════
# AppUriAuthenticationPolicy
# ═══════════════════════════════════════════════════════════════
class AppUriAuthenticationPolicy(Stub):
"""AppUriAuthenticationPolicy — URI 认证策略 (Android 11+)"""
def __init__(self):
super().__init__()
self._rules: Dict[str, List[str]] = {}
def addAppAndUriMapping(self, app: Any, uri: Any, alias: str):
"""添加应用与 URI 映射"""
app_pkg = str(app) if app else ""
uri_str = str(uri) if uri else ""
if app_pkg not in self._rules:
self._rules[app_pkg] = []
self._rules[app_pkg].append(uri_str)
def getAppAndUriMappings(self) -> Dict[str, List[str]]:
"""获取所有规则映射"""
return dict(self._rules)
def toString(self) -> str:
return f"AppUriAuthenticationPolicy(rules={self._rules})"
def hashCode(self) -> int:
return hash(str(self._rules)) & 0x7FFFFFFF
def equals(self, other) -> bool:
if not isinstance(other, AppUriAuthenticationPolicy):
return False
return self._rules == other._rules
# ═══════════════════════════════════════════════════════════════
# KeyChainAliasResponse
# ═══════════════════════════════════════════════════════════════
class KeyChainAliasResponse(Stub):
"""KeyChainAliasResponse — 密钥别名选择响应"""
def __init__(self, alias: Optional[str] = None):
super().__init__()
self._alias = alias
def getAlias(self) -> Optional[str]:
"""获取选定的密钥别名"""
return self._alias
def toString(self) -> str:
return f"KeyChainAliasResponse(alias={self._alias})"
def hashCode(self) -> int:
return hash(self._alias) & 0x7FFFFFFF
def equals(self, other) -> bool:
if not isinstance(other, KeyChainAliasResponse):
return False
return self._alias == other._alias
# ═══════════════════════════════════════════════════════════════
# NetworkSecurityPolicy
# ═══════════════════════════════════════════════════════════════
class NetworkSecurityPolicy(Stub):
"""NetworkSecurityPolicy — 网络安全策略"""
_instance: Optional["NetworkSecurityPolicy"] = None
def __init__(self):
super().__init__()
self._cleartext_permitted = True
@staticmethod
def getInstance() -> "NetworkSecurityPolicy":
"""获取全局 NetworkSecurityPolicy 实例"""
if NetworkSecurityPolicy._instance is None:
NetworkSecurityPolicy._instance = NetworkSecurityPolicy()
return NetworkSecurityPolicy._instance
def isCleartextTrafficPermitted(self, host: Optional[str] = None) -> bool:
"""检查是否允许明文流量 (桌面默认允许)"""
return self._cleartext_permitted
def setCleartextTrafficPermitted(self, permitted: bool):
"""设置是否允许明文流量"""
self._cleartext_permitted = permitted
def toString(self) -> str:
return f"NetworkSecurityPolicy(cleartextPermitted={self._cleartext_permitted})"
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
def equals(self, other) -> bool:
return self is other
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"KeyChain",
"AppUriAuthenticationPolicy",
"KeyChainAliasResponse",
"NetworkSecurityPolicy",
]
+423
View File
@@ -0,0 +1,423 @@
from __future__ import annotations
"""
Android Service Autofill 组件 — AutofillService, FillRequest, FillResponse,
SaveRequest, SaveInfo, Dataset, AutofillFieldClassificationService 等
方法签名完全对齐 Android SDK (android.service.autofill 包)
"""
import time
from typing import Any, Callable, Dict, List, Optional, Set
from .._core import Stub, AndroidOnlyError
# ═══════════════════════════════════════════════════════════════
# FillRequest
# ═══════════════════════════════════════════════════════════════
class FillRequest(Stub):
"""FillRequest — 自动填充请求"""
# 请求标志
FLAG_MANUAL_REQUEST = 0x0001
FLAG_COMPATIBILITY_MODE_REQUEST = 0x0002
def __init__(self, request_id: int = 0, flags: int = 0,
fill_contexts: Optional[List[Any]] = None):
super().__init__()
self._request_id = request_id
self._flags = flags
self._fill_contexts = fill_contexts or []
self._client_state: Optional[Any] = None
def getId(self) -> int:
"""获取请求 ID"""
return self._request_id
def getFlags(self) -> int:
"""获取标志"""
return self._flags
def getFillContexts(self) -> List[Any]:
"""获取填充上下文"""
return list(self._fill_contexts)
def getClientState(self) -> Optional[Any]:
return self._client_state
def setClientState(self, state: Any):
self._client_state = state
def toString(self) -> str:
return (f"FillRequest(id={self._request_id}, "
f"flags={self._flags}, contexts={len(self._fill_contexts)})")
def hashCode(self) -> int:
return self._request_id & 0x7FFFFFFF
def equals(self, other) -> bool:
if not isinstance(other, FillRequest):
return False
return self._request_id == other._request_id
# ═══════════════════════════════════════════════════════════════
# Dataset
# ═══════════════════════════════════════════════════════════════
class Dataset(Stub):
"""Dataset — 自动填充数据集"""
def __init__(self):
super().__init__()
self._field_values: Dict[Any, Any] = {}
self._id: Optional[Any] = None
self._authentication: Optional[Any] = None
def getFieldValues(self) -> Dict[Any, Any]:
return dict(self._field_values)
def getId(self) -> Optional[Any]:
return self._id
def setId(self, id: Any):
self._id = id
def toString(self) -> str:
return f"Dataset(fields={len(self._field_values)}, id={self._id})"
def hashCode(self) -> int:
return hash(str(self._id)) & 0x7FFFFFFF
def equals(self, other) -> bool:
if not isinstance(other, Dataset):
return False
return self._id == other._id
# ── Builder ──
class Builder(Stub):
"""Builder — Dataset 构建器"""
def __init__(self):
super().__init__()
self._dataset = Dataset()
def setId(self, id: Any) -> "Dataset.Builder":
self._dataset._id = id
return self
def setValue(self, id: Any, value: Any) -> "Dataset.Builder":
self._dataset._field_values[id] = value
return self
def setAuthentication(self, authentication: Any) -> "Dataset.Builder":
self._dataset._authentication = authentication
return self
def build(self) -> Dataset:
return self._dataset
# ═══════════════════════════════════════════════════════════════
# SaveInfo
# ═══════════════════════════════════════════════════════════════
class SaveInfo(Stub):
"""SaveInfo — 保存信息"""
# 保存类型
SAVE_DATA_TYPE_EMAIL = 0x0001
SAVE_DATA_TYPE_PASSWORD = 0x0002
SAVE_DATA_TYPE_USERNAME = 0x0004
SAVE_DATA_TYPE_CREDIT_CARD = 0x0008
SAVE_DATA_TYPE_ADDRESS = 0x0010
SAVE_DATA_TYPE_GENERIC = 0x0020
SAVE_DATA_TYPE_PASSWORD_V2 = 0x0040
SAVE_DATA_TYPE_SMS_2FA = 0x0080
SAVE_DATA_TYPE_BIRTH_DATE = 0x0100
SAVE_DATA_TYPE_EMAIL_ADDRESS = 0x0200
SAVE_DATA_TYPE_PHONE = 0x0400
SAVE_DATA_TYPE_USERNAME_V2 = 0x0800
SAVE_DATA_TYPE_CARD_NUMBER = 0x1000
SAVE_DATA_TYPE_CARD_EXPIRATION_DATE = 0x2000
SAVE_DATA_TYPE_CARD_SECURITY_CODE = 0x4000
SAVE_DATA_TYPE_FI_ACCOUNT_NUMBER = 0x8000
SAVE_DATA_TYPE_FI_ROUTING_NUMBER = 0x10000
SAVE_DATA_TYPE_PASSPORT = 0x20000
SAVE_DATA_TYPE_DRIVERS_LICENSE = 0x40000
SAVE_DATA_TYPE_SSN = 0x80000
# 标志
FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE = 0x0001
def __init__(self):
super().__init__()
self._type: int = SaveInfo.SAVE_DATA_TYPE_GENERIC
self._required_ids: List[Any] = []
self._optional_ids: List[Any] = []
self._description: Optional[Any] = None
self._flags: int = 0
self._trigger_ids: List[Any] = []
self._validator: Optional[Any] = None
def getType(self) -> int:
return self._type
def getRequiredIds(self) -> List[Any]:
return list(self._required_ids)
def getOptionalIds(self) -> List[Any]:
return list(self._optional_ids)
def getDescription(self) -> Optional[Any]:
return self._description
def getFlags(self) -> int:
return self._flags
def toString(self) -> str:
return (f"SaveInfo(type={self._type}, required={len(self._required_ids)}, "
f"optional={len(self._optional_ids)})")
def hashCode(self) -> int:
return hash((self._type, tuple(self._required_ids))) & 0x7FFFFFFF
def equals(self, other) -> bool:
if not isinstance(other, SaveInfo):
return False
return self._type == other._type
# ── Builder ──
class Builder(Stub):
"""Builder — SaveInfo 构建器"""
def __init__(self, type_: int, required_ids: List[Any]):
super().__init__()
self._save_info = SaveInfo()
self._save_info._type = type_
self._save_info._required_ids = list(required_ids)
def setOptionalIds(self, optional_ids: List[Any]) -> "SaveInfo.Builder":
self._save_info._optional_ids = list(optional_ids)
return self
def setDescription(self, description: Any) -> "SaveInfo.Builder":
self._save_info._description = description
return self
def setFlags(self, flags: int) -> "SaveInfo.Builder":
self._save_info._flags = flags
return self
def setTriggerIds(self, trigger_ids: List[Any]) -> "SaveInfo.Builder":
self._save_info._trigger_ids = list(trigger_ids)
return self
def setValidator(self, validator: Any) -> "SaveInfo.Builder":
self._save_info._validator = validator
return self
def build(self) -> SaveInfo:
return self._save_info
# ═══════════════════════════════════════════════════════════════
# FillResponse
# ═══════════════════════════════════════════════════════════════
class FillResponse(Stub):
"""FillResponse — 自动填充响应"""
def __init__(self):
super().__init__()
self._datasets: List[Dataset] = []
self._save_info: Optional[SaveInfo] = None
self._client_state: Optional[Any] = None
self._authentication_result: Optional[Any] = None
def getDatasets(self) -> List[Dataset]:
return list(self._datasets)
def getSaveInfo(self) -> Optional[SaveInfo]:
return self._save_info
def getClientState(self) -> Optional[Any]:
return self._client_state
def toString(self) -> str:
return (f"FillResponse(datasets={len(self._datasets)}, "
f"saveInfo={'set' if self._save_info else 'none'})")
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
def equals(self, other) -> bool:
return self is other
# ── Builder ──
class Builder(Stub):
"""Builder — FillResponse 构建器"""
def __init__(self):
super().__init__()
self._response = FillResponse()
def addDataset(self, dataset: Dataset) -> "FillResponse.Builder":
self._response._datasets.append(dataset)
return self
def setDatasets(self, datasets: List[Dataset]) -> "FillResponse.Builder":
self._response._datasets = list(datasets)
return self
def setSaveInfo(self, save_info: SaveInfo) -> "FillResponse.Builder":
self._response._save_info = save_info
return self
def setClientState(self, client_state: Any) -> "FillResponse.Builder":
self._response._client_state = client_state
return self
def setAuthenticationResult(self, result: Any) -> "FillResponse.Builder":
self._response._authentication_result = result
return self
def build(self) -> FillResponse:
return self._response
# ═══════════════════════════════════════════════════════════════
# SaveRequest
# ═══════════════════════════════════════════════════════════════
class SaveRequest(Stub):
"""SaveRequest — 保存请求"""
def __init__(self, request_id: int = 0,
client_state: Optional[Any] = None,
fill_contexts: Optional[List[Any]] = None):
super().__init__()
self._request_id = request_id
self._client_state = client_state
self._fill_contexts = fill_contexts or []
def getId(self) -> int:
return self._request_id
def getClientState(self) -> Optional[Any]:
return self._client_state
def getFillContexts(self) -> List[Any]:
return list(self._fill_contexts)
def toString(self) -> str:
return (f"SaveRequest(id={self._request_id}, "
f"contexts={len(self._fill_contexts)})")
def hashCode(self) -> int:
return self._request_id & 0x7FFFFFFF
def equals(self, other) -> bool:
if not isinstance(other, SaveRequest):
return False
return self._request_id == other._request_id
# ═══════════════════════════════════════════════════════════════
# AutofillService
# ═══════════════════════════════════════════════════════════════
class AutofillService(Stub):
"""AutofillService — 自动填充服务"""
SERVICE_INTERFACE = "android.service.autofill.AutofillService"
SERVICE_META_DATA = "android.autofill"
def __init__(self):
super().__init__()
def onFillRequest(self, request: FillRequest,
cancellation_signal: Any,
callback: Any):
"""填充请求回调"""
response = FillResponse()
if callback and hasattr(callback, 'onSuccess'):
callback.onSuccess(response)
def onSaveRequest(self, request: SaveRequest, callback: Any):
"""保存请求回调"""
if callback and hasattr(callback, 'onSuccess'):
callback.onSuccess()
def onConnected(self):
"""服务已连接"""
pass
def onDisconnected(self):
"""服务已断开"""
pass
def onUnbind(self, intent: Any) -> bool:
return False
def toString(self) -> str:
return f"AutofillService@{hex(id(self))}"
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
def equals(self, other) -> bool:
return self is other
# ═══════════════════════════════════════════════════════════════
# AutofillFieldClassificationService
# ═══════════════════════════════════════════════════════════════
class AutofillFieldClassificationService(Stub):
"""AutofillFieldClassificationService — 自动填充字段分类服务"""
SERVICE_INTERFACE = (
"android.service.autofill.AutofillFieldClassificationService"
)
def __init__(self):
super().__init__()
def onConnected(self):
pass
def onDisconnected(self):
pass
def onClassificationRequest(self, request: Any, callback: Any):
if callback and hasattr(callback, 'onSuccess'):
callback.onSuccess(None)
def toString(self) -> str:
return f"AutofillFieldClassificationService@{hex(id(self))}"
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
def equals(self, other) -> bool:
return self is other
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"AutofillService",
"FillRequest",
"FillResponse",
"SaveRequest",
"SaveInfo",
"Dataset",
"AutofillFieldClassificationService",
]
+350
View File
@@ -0,0 +1,350 @@
"""
Android Service Controls 组件 — ControlsProviderService, Control, DeviceTypes,
StatefulControl, StatelessControl 等
方法签名完全对齐 Android SDK (android.service.controls 包)
"""
from typing import Any, Callable, Dict, List, Optional, Set
from .._core import Stub, AndroidOnlyError
# ═══════════════════════════════════════════════════════════════
# DeviceTypes
# ═══════════════════════════════════════════════════════════════
class DeviceTypes(Stub):
"""DeviceTypes — 设备类型常量"""
TYPE_AC_HEATER = "AC_HEATER"
TYPE_AC_UNIT = "AC_UNIT"
TYPE_AIR_FRESHENER = "AIR_FRESHENER"
TYPE_AIR_PURIFIER = "AIR_PURIFIER"
TYPE_BLINDS = "BLINDS"
TYPE_CAMERA = "CAMERA"
TYPE_CLOSET = "CLOSET"
TYPE_COFFEE_MAKER = "COFFEE_MAKER"
TYPE_CURTAIN = "CURTAIN"
TYPE_DEHUMIDIFIER = "DEHUMIDIFIER"
TYPE_DISHWASHER = "DISHWASHER"
TYPE_DISPLAY = "DISPLAY"
TYPE_DOOR = "DOOR"
TYPE_DOORBELL = "DOORBELL"
TYPE_DRAWER = "DRAWER"
TYPE_DRYER = "DRYER"
TYPE_FAN = "FAN"
TYPE_GARAGE = "GARAGE"
TYPE_GATE = "GATE"
TYPE_HEATER = "HEATER"
TYPE_HOOD = "HOOD"
TYPE_HUMIDIFIER = "HUMIDIFIER"
TYPE_KETTLE = "KETTLE"
TYPE_LIGHT = "LIGHT"
TYPE_LIGHTBULB = "LIGHTBULB"
TYPE_LOCK = "LOCK"
TYPE_MICROWAVE = "MICROWAVE"
TYPE_MOP = "MOP"
TYPE_MOWER = "MOWER"
TYPE_MULTICOOKER = "MULTICOOKER"
TYPE_OUTLET = "OUTLET"
TYPE_OVEN = "OVEN"
TYPE_PERGOLLA = "PERGOLLA"
TYPE_RADIATOR = "RADIATOR"
TYPE_REFRIGERATOR = "REFRIGERATOR"
TYPE_REMOTE_STAND = "REMOTE_STAND"
TYPE_RICE_COOKER = "RICE_COOKER"
TYPE_ROUTINE = "ROUTINE"
TYPE_SCENE = "SCENE"
TYPE_SECURITY_SYSTEM = "SECURITY_SYSTEM"
TYPE_SET_TOP = "SET_TOP"
TYPE_SHOWER = "SHOWER"
TYPE_SHUTTER = "SHUTTER"
TYPE_SMOKE_DETECTOR = "SMOKE_DETECTOR"
TYPE_SOUNDBAR = "SOUNDBAR"
TYPE_SPRINKLER = "SPRINKLER"
TYPE_STANDMIXER = "STANDMIXER"
TYPE_THERMOSTAT = "THERMOSTAT"
TYPE_TV = "TV"
TYPE_VACUUM = "VACUUM"
TYPE_VALVE = "VALVE"
TYPE_WASHER = "WASHER"
TYPE_WATER_HEATER = "WATER_HEATER"
TYPE_WINDOW = "WINDOW"
TYPE_WINE_COOLER = "WINE_COOLER"
def __init__(self):
super().__init__()
# ═══════════════════════════════════════════════════════════════
# Control
# ═══════════════════════════════════════════════════════════════
class Control(Stub):
"""Control — 设备控件基类"""
# 状态
STATUS_UNKNOWN = 0
STATUS_OK = 1
STATUS_NOT_RESPONDING = 2
STATUS_ERROR = 3
STATUS_FIRMWARE_UPDATE = 4
def __init__(self, control_id: str, title: str,
subtitle: str, device_type: str):
super().__init__()
self._control_id = control_id
self._title = title
self._subtitle = subtitle
self._device_type = device_type
self._status = Control.STATUS_OK
self._status_text: str = ""
self._custom_icon: Optional[Any] = None
self._custom_color: int = 0
self._is_auth_required: bool = False
self._structure: str = ""
self._zone: str = ""
self._is_favorite: bool = False
self._device_types_list: List[str] = [device_type]
self._parent_id: Optional[str] = None
def getControlId(self) -> str:
"""获取控件 ID"""
return self._control_id
def getTitle(self) -> str:
"""获取标题"""
return self._title
def getSubtitle(self) -> str:
"""获取副标题"""
return self._subtitle
def getDeviceType(self) -> str:
"""获取设备类型"""
return self._device_type
def getStatus(self) -> int:
return self._status
def setStatus(self, status: int):
self._status = status
def getStatusText(self) -> str:
return self._status_text
def setStatusText(self, status_text: str):
self._status_text = status_text
def getStructure(self) -> str:
return self._structure
def setStructure(self, structure: str):
self._structure = structure
def getZone(self) -> str:
return self._zone
def setZone(self, zone: str):
self._zone = zone
def isFavorite(self) -> bool:
return self._is_favorite
def setFavorite(self, favorite: bool):
self._is_favorite = favorite
def toString(self) -> str:
return (f"Control(id={self._control_id}, title={self._title}, "
f"type={self._device_type}, status={self._status})")
def hashCode(self) -> int:
return hash(self._control_id) & 0x7FFFFFFF
def equals(self, other) -> bool:
if not isinstance(other, Control):
return False
return self._control_id == other._control_id
# ═══════════════════════════════════════════════════════════════
# StatefulControl
# ═══════════════════════════════════════════════════════════════
class StatefulControl(Control):
"""StatefulControl — 有状态控件"""
def __init__(self, control_id: str, title: str,
subtitle: str, device_type: str):
super().__init__(control_id, title, subtitle, device_type)
self._current_value: Any = None
self._min_value: Optional[float] = None
self._max_value: Optional[float] = None
self._step: Optional[float] = None
self._unit: Optional[str] = None
self._is_on: bool = False
self._mode: str = ""
def setStatus(self, status: int) -> "StatefulControl":
"""设置状态"""
self._status = status
return self
def setStatusText(self, status_text: str) -> "StatefulControl":
"""设置状态文本"""
self._status_text = status_text
return self
def getCurrentValue(self) -> Any:
return self._current_value
def setCurrentValue(self, value: Any) -> "StatefulControl":
self._current_value = value
return self
def getMinValue(self) -> Optional[float]:
return self._min_value
def setMinValue(self, min_value: float) -> "StatefulControl":
self._min_value = min_value
return self
def getMaxValue(self) -> Optional[float]:
return self._max_value
def setMaxValue(self, max_value: float) -> "StatefulControl":
self._max_value = max_value
return self
def getStep(self) -> Optional[float]:
return self._step
def setStep(self, step: float) -> "StatefulControl":
self._step = step
return self
def isOn(self) -> bool:
return self._is_on
def setOn(self, is_on: bool) -> "StatefulControl":
self._is_on = is_on
return self
def getMode(self) -> str:
return self._mode
def setMode(self, mode: str) -> "StatefulControl":
self._mode = mode
return self
def toString(self) -> str:
return (f"StatefulControl(id={self._control_id}, title={self._title}, "
f"type={self._device_type}, on={self._is_on}, "
f"value={self._current_value})")
# ═══════════════════════════════════════════════════════════════
# StatelessControl
# ═══════════════════════════════════════════════════════════════
class StatelessControl(Control):
"""StatelessControl — 无状态控件"""
def __init__(self, control_id: str, title: str,
subtitle: str, device_type: str):
super().__init__(control_id, title, subtitle, device_type)
self._action_text: str = ""
def getActionText(self) -> str:
return self._action_text
def setActionText(self, action_text: str) -> "StatelessControl":
self._action_text = action_text
return self
def toString(self) -> str:
return (f"StatelessControl(id={self._control_id}, "
f"title={self._title}, type={self._device_type})")
# ═══════════════════════════════════════════════════════════════
# ControlsProviderService
# ═══════════════════════════════════════════════════════════════
class ControlsProviderService(Stub):
"""ControlsProviderService — 控件提供者服务"""
SERVICE_INTERFACE = "android.service.controls.ControlsProviderService"
# 控件类型
CONTROL_TYPE_TOGGLE = 1
CONTROL_TYPE_RANGE = 2
CONTROL_TYPE_STATELESS = 3
CONTROL_TYPE_BUTTON = 4
def __init__(self):
super().__init__()
self._controls: Dict[str, Control] = {}
self._publishers: Dict[str, Any] = {}
self._subscribers: Dict[str, Any] = {}
def onCreatePublisherFor(self, control_ids: List[str]) -> Any:
"""为指定控件创建发布者"""
publisher = type("Publisher", (), {
"onUnsubscribe": lambda: None,
"onRequest": lambda cid: None,
})()
for cid in control_ids:
self._publishers[cid] = publisher
return publisher
def onGetSubscriberFor(self, control_ids: List[str]) -> Any:
"""获取控件订阅者"""
subscriber = type("Subscriber", (), {
"onSubscribe": lambda cid: None,
"onUnsubscribe": lambda cid: None,
})()
for cid in control_ids:
self._subscribers[cid] = subscriber
return subscriber
def publishControls(self, control_ids: List[str], controls: List[Control]):
"""发布控件状态"""
for control in controls:
self._controls[control.getControlId()] = control
def addControl(self, control: Control):
"""添加控件"""
self._controls[control.getControlId()] = control
def removeControl(self, control_id: str):
"""移除控件"""
self._controls.pop(control_id, None)
def getControl(self, control_id: str) -> Optional[Control]:
return self._controls.get(control_id)
def getControls(self) -> List[Control]:
return list(self._controls.values())
def toString(self) -> str:
return f"ControlsProviderService(controls={len(self._controls)})"
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
def equals(self, other) -> bool:
return self is other
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"ControlsProviderService",
"Control",
"DeviceTypes",
"StatefulControl",
"StatelessControl",
]
+140
View File
@@ -0,0 +1,140 @@
"""
Android Service Dreams 组件 — DreamService 等
方法签名完全对齐 Android SDK (android.service.dreams 包)
"""
from typing import Any, Callable, Dict, List, Optional
from .._core import Stub, AndroidOnlyError
# ═══════════════════════════════════════════════════════════════
# DreamService
# ═══════════════════════════════════════════════════════════════
class DreamService(Stub):
"""DreamService — 屏保服务基类"""
# 服务接口
SERVICE_INTERFACE = "android.service.dreams.DreamService"
SERVICE_META_DATA = "android.service.dream"
# 退出原因
EXIT_CAUSE_UNKNOWN = 0
EXIT_CAUSE_TIMEOUT = 1
EXIT_CAUSE_USER = 2
EXIT_CAUSE_DREAM_FINISHED = 3
def __init__(self):
super().__init__()
self._fullscreen: bool = False
self._interactive: bool = False
self._dreaming: bool = False
self._window: Optional[Any] = None
self._content_view: Optional[Any] = None
self._exit_cause: int = DreamService.EXIT_CAUSE_UNKNOWN
self._created: bool = False
self._screen_bright: bool = True
self._doze_component: Optional[Any] = None
self._doze_mode: bool = False
self._debug: bool = False
def setContentView(self, view: Any):
"""设置内容视图"""
self._content_view = view
def getContentView(self) -> Optional[Any]:
return self._content_view
def setFullscreen(self, fullscreen: bool):
"""设置全屏模式"""
self._fullscreen = fullscreen
def isFullscreen(self) -> bool:
"""检查是否全屏"""
return self._fullscreen
def isInteractive(self) -> bool:
"""检查是否可交互"""
return self._interactive
def setInteractive(self, interactive: bool):
"""设置可交互"""
self._interactive = interactive
def getWindow(self) -> Optional[Any]:
"""获取窗口"""
return self._window
def startDream(self):
"""开始屏保"""
self._dreaming = True
def awaken(self):
"""唤醒/结束屏保"""
self._dreaming = False
def onDreamingStarted(self):
"""屏保开始时回调"""
self._dreaming = True
def onDreamingStopped(self):
"""屏保停止时回调"""
self._dreaming = False
def onCreate(self):
"""创建时回调"""
self._created = True
def onDestroy(self):
"""销毁时回调"""
self._created = False
self._dreaming = False
def onAttachedToWindow(self):
"""附加到窗口时回调"""
pass
def onDetachedFromWindow(self):
"""从窗口分离时回调"""
pass
def isDreaming(self) -> bool:
"""检查是否正在运行屏保"""
return self._dreaming
def getExitCause(self) -> int:
return self._exit_cause
def setExitCause(self, cause: int):
self._exit_cause = cause
def onWakeUp(self):
"""唤醒回调"""
self.awaken()
def canDoze(self) -> bool:
return self._doze_mode
def setDozeComponent(self, component: Any):
self._doze_component = component
def toString(self) -> str:
return (f"DreamService(dreaming={self._dreaming}, "
f"fullscreen={self._fullscreen}, "
f"interactive={self._interactive})")
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
def equals(self, other) -> bool:
return self is other
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"DreamService",
]
@@ -0,0 +1,378 @@
"""
Android Service Notification 组件 — NotificationListenerService, StatusBarNotification, RankingMap 等
方法签名完全对齐 Android SDK (android.service.notification 包)
"""
import time
from typing import Any, Callable, Dict, List, Optional, Set, Tuple
from .._core import Stub, AndroidOnlyError
# ═══════════════════════════════════════════════════════════════
# StatusBarNotification
# ═══════════════════════════════════════════════════════════════
class StatusBarNotification(Stub):
"""StatusBarNotification — 状态栏通知"""
def __init__(self, pkg: str = "", op_pkg: str = "",
id: int = 0, tag: Optional[str] = None,
uid: int = 10001, pid: int = 1000,
user: Any = None, notification: Any = None,
post_time: int = 0):
super().__init__()
self._pkg = pkg
self._op_pkg = op_pkg or pkg
self._id = id
self._tag = tag
self._uid = uid
self._pid = pid
self._user = user
self._notification = notification
self._post_time = post_time or int(time.time() * 1000)
self._is_clearable = True
self._is_ongoing = False
self._key = f"{pkg}|{id}|{tag or ''}"
self._override_group_key: Optional[str] = None
self._override_public = False
self._initial_source_id = 0
self._initial_source_uid = uid
self._initial_source_pkg = pkg
def getPackageName(self) -> str:
"""获取通知所属包名"""
return self._pkg
def getOpPkg(self) -> str:
return self._op_pkg
def getNotification(self) -> Any:
"""获取 Notification 对象"""
return self._notification
def getId(self) -> int:
"""获取通知 ID"""
return self._id
def getTag(self) -> Optional[str]:
"""获取通知标签"""
return self._tag
def getPostTime(self) -> int:
"""获取发送时间 (毫秒)"""
return self._post_time
def getUid(self) -> int:
return self._uid
def getPid(self) -> int:
return self._pid
def getUser(self) -> Any:
return self._user
def getKey(self) -> str:
return self._key
def isClearable(self) -> bool:
"""是否可以清除"""
return self._is_clearable
def isOngoing(self) -> bool:
"""是否是持续通知"""
return self._is_ongoing
def setClearable(self, clearable: bool):
self._is_clearable = clearable
def setOngoing(self, ongoing: bool):
self._is_ongoing = ongoing
def getOverrideGroupKey(self) -> Optional[str]:
return self._override_group_key
def setOverrideGroupKey(self, override_group_key: Optional[str]):
self._override_group_key = override_group_key
def isGroup(self) -> bool:
return self._override_group_key is not None
def clone(self) -> "StatusBarNotification":
"""克隆通知"""
clone = StatusBarNotification(
pkg=self._pkg, op_pkg=self._op_pkg, id=self._id,
tag=self._tag, uid=self._uid, pid=self._pid,
user=self._user, notification=self._notification,
post_time=self._post_time,
)
clone._is_clearable = self._is_clearable
clone._is_ongoing = self._is_ongoing
clone._key = self._key
clone._override_group_key = self._override_group_key
return clone
def toString(self) -> str:
return (f"StatusBarNotification(pkg={self._pkg}, id={self._id}, "
f"tag={self._tag}, postTime={self._post_time})")
def hashCode(self) -> int:
return hash(self._key) & 0x7FFFFFFF
def equals(self, other) -> bool:
if not isinstance(other, StatusBarNotification):
return False
return self._key == other._key
# ═══════════════════════════════════════════════════════════════
# NotificationListenerService
# ═══════════════════════════════════════════════════════════════
class NotificationListenerService(Stub):
"""NotificationListenerService — 通知监听服务"""
# 取消原因
REASON_LISTENER_CANCEL = 1
REASON_CLICK = 2
REASON_CANCEL = 3
REASON_DELETE = 4
REASON_PACKAGE_CHANGED = 5
REASON_USER_STOPPED = 6
REASON_PACKACE_BANNED = 7
REASON_APP_CANCEL = 8
REASON_APP_CANCEL_ALL = 9
REASON_CHANNEL_BANNED = 10
REASON_SNOOZED = 11
REASON_TIMEOUT = 12
REASON_ERROR = 13
REASON_GROUP_SUMMARY_CANCELED = 14
REASON_ASSISTANT_CANCEL = 15
# 中断过滤器
INTERRUPTION_FILTER_NONE = 0
INTERRUPTION_FILTER_PRIORITY = 1
INTERRUPTION_FILTER_ALARMS = 2
INTERRUPTION_FILTER_ALL = 3
INTERRUPTION_FILTER_UNKNOWN = 4
# 提示
HINT_HOST_DISABLE_CALL_EFFECTS = 1
HINT_HOST_DISABLE_EFFECTS = 2
HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 3
# SUW 组件
TRAMPOLINE_ACTIVITY_CLASS = (
"com.android.settings.Settings$NotificationAppListActivity"
)
def __init__(self):
super().__init__()
self._active_notifications: Dict[str, StatusBarNotification] = {}
self._keys_list: List[str] = []
self._interruption_filter: int = (
NotificationListenerService.INTERRUPTION_FILTER_ALL
)
self._ranking_map: Optional["NotificationListenerService.RankingMap"] = None
self._is_listener_bound = False
@staticmethod
def requestRebind(component_name: Any):
"""请求重新绑定通知监听器"""
pass
@staticmethod
def requestUnbind(component_name: Any):
"""请求解绑通知监听器"""
pass
def onNotificationPosted(self, sbn: StatusBarNotification,
ranking_map: Optional["RankingMap"] = None):
"""通知被发送时回调"""
if sbn:
self._active_notifications[sbn.getKey()] = sbn
self._keys_list = list(self._active_notifications.keys())
if ranking_map:
self._ranking_map = ranking_map
def onNotificationRemoved(self, sbn: StatusBarNotification,
ranking_map: Optional["RankingMap"] = None,
reason: int = REASON_LISTENER_CANCEL):
"""通知被移除时回调"""
if sbn:
key = sbn.getKey()
self._active_notifications.pop(key, None)
if key in self._keys_list:
self._keys_list.remove(key)
def onListenerConnected(self):
"""监听器已连接"""
self._is_listener_bound = True
def onListenerDisconnected(self):
"""监听器已断开"""
self._is_listener_bound = False
def onNotificationRemovedReason(self, sbn: StatusBarNotification,
ranking_map: "RankingMap",
reason: int):
"""通知被移除 (带原因)"""
self.onNotificationRemoved(sbn, ranking_map, reason)
def onNotificationPostedWithRankingMap(self, sbn: StatusBarNotification,
ranking_map: "RankingMap"):
"""通知被发送 (带排名映射)"""
self.onNotificationPosted(sbn, ranking_map)
def getActiveNotifications(self) -> List[StatusBarNotification]:
"""获取活动通知列表"""
return list(self._active_notifications.values())
def getActiveNotificationsWithRankingMap(self) -> Tuple[
List[StatusBarNotification],
Optional["RankingMap"]]:
"""获取活动通知及排名映射"""
return list(self._active_notifications.values()), self._ranking_map
def getCurrentInterruptionFilter(self) -> int:
"""获取当前中断过滤器"""
return self._interruption_filter
def setInterruptionFilter(self, interruption_filter: int):
"""设置中断过滤器"""
self._interruption_filter = interruption_filter
def cancelNotification(self, key: str):
"""取消指定通知"""
self._active_notifications.pop(key, None)
if key in self._keys_list:
self._keys_list.remove(key)
def cancelAllNotifications(self):
"""取消所有通知"""
self._active_notifications.clear()
self._keys_list.clear()
def requestRebind(self, component_name: Any):
"""请求重新绑定"""
pass
def requestUnbind(self):
"""请求解绑"""
pass
def registerAsSystemService(self, context: Any, component_name: Any,
user: int):
pass
def unregisterAsSystemService(self):
pass
def isBound(self) -> bool:
return self._is_listener_bound
def toString(self) -> str:
return (f"NotificationListenerService("
f"notifications={len(self._active_notifications)}, "
f"filter={self._interruption_filter})")
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
def equals(self, other) -> bool:
return self is other
# ── RankingMap ──
class RankingMap(Stub):
"""RankingMap — 通知排名映射"""
def __init__(self):
super().__init__()
self._rankings: Dict[str, "NotificationListenerService.Ranking"] = {}
def getOrderedKeys(self) -> List[str]:
"""获取有序键列表"""
return list(self._rankings.keys())
def getRanking(self, key: str) -> Optional["NotificationListenerService.Ranking"]:
"""获取指定通知的排名信息"""
return self._rankings.get(key)
def getRawRanking(self, key: str) -> Optional[Any]:
return self._rankings.get(key)
def toString(self) -> str:
return f"RankingMap(keys={len(self._rankings)})"
def hashCode(self) -> int:
return hash(tuple(self._rankings.keys())) & 0x7FFFFFFF
def equals(self, other) -> bool:
if not isinstance(other, NotificationListenerService.RankingMap):
return False
return self._rankings == other._rankings
class Ranking(Stub):
"""Ranking — 通知排名"""
def __init__(self):
super().__init__()
self._key: str = ""
self._rank: int = 0
self._is_ambient: bool = False
self._matches_interruption_filter: bool = True
self._suppressed_visual_effects: int = 0
self._importance: int = 3 # IMPORTANCE_DEFAULT
self._importance_explanation: Optional[str] = None
self._group_key: Optional[str] = None
self._override_group_key: Optional[str] = None
self._channel: Optional[Any] = None
self._is_suspended: bool = False
def getKey(self) -> str:
return self._key
def getRank(self) -> int:
return self._rank
def isAmbient(self) -> bool:
return self._is_ambient
def matchesInterruptionFilter(self) -> bool:
return self._matches_interruption_filter
def getSuppressedVisualEffects(self) -> int:
return self._suppressed_visual_effects
def getImportance(self) -> int:
return self._importance
def getImportanceExplanation(self) -> Optional[str]:
return self._importance_explanation
def getGroupKey(self) -> Optional[str]:
return self._group_key
def getOverrideGroupKey(self) -> Optional[str]:
return self._override_group_key
def getChannel(self) -> Optional[Any]:
return self._channel
def isSuspended(self) -> bool:
return self._is_suspended
def toString(self) -> str:
return (f"Ranking(key={self._key}, rank={self._rank}, "
f"importance={self._importance})")
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"NotificationListenerService",
"StatusBarNotification",
]
@@ -0,0 +1,214 @@
"""
Android Service QuickSettings 组件 — TileService, Tile 等
方法签名完全对齐 Android SDK (android.service.quicksettings 包)
"""
import time
from typing import Any, Callable, Dict, List, Optional
from .._core import Stub, AndroidOnlyError
# ═══════════════════════════════════════════════════════════════
# Tile
# ═══════════════════════════════════════════════════════════════
class Tile(Stub):
"""Tile — 快捷设置磁贴"""
# 状态常量
STATE_ACTIVE = 1
STATE_INACTIVE = 2
STATE_UNAVAILABLE = 0
def __init__(self):
super().__init__()
self._state: int = Tile.STATE_INACTIVE
self._label: str = ""
self._subtitle: str = ""
self._content_description: str = ""
self._icon: Optional[Any] = None
self._icon_res_id: int = 0
self._meta_data: Optional[Dict[str, Any]] = None
self._last_updated: int = 0
def getState(self) -> int:
"""获取磁贴状态"""
return self._state
def setState(self, state: int):
"""设置磁贴状态"""
self._state = state
self._last_updated = int(time.time() * 1000)
def getLabel(self) -> str:
"""获取标签"""
return self._label
def setLabel(self, label: str):
"""设置标签"""
self._label = label
self._last_updated = int(time.time() * 1000)
def getSubtitle(self) -> str:
"""获取副标题"""
return self._subtitle
def setSubtitle(self, subtitle: str):
"""设置副标题"""
self._subtitle = subtitle
self._last_updated = int(time.time() * 1000)
def getContentDescription(self) -> str:
"""获取内容描述"""
return self._content_description
def setContentDescription(self, content_description: str):
"""设置内容描述"""
self._content_description = content_description
def getIcon(self) -> Optional[Any]:
"""获取图标"""
return self._icon
def setIcon(self, icon: Any):
"""设置图标"""
self._icon = icon
self._last_updated = int(time.time() * 1000)
def updateTile(self):
"""更新磁贴 (通知系统刷新)"""
self._last_updated = int(time.time() * 1000)
def getMetaData(self) -> Optional[Dict[str, Any]]:
return self._meta_data
def setMetaData(self, meta_data: Dict[str, Any]):
self._meta_data = meta_data
def toString(self) -> str:
state_names = {
Tile.STATE_ACTIVE: "ACTIVE",
Tile.STATE_INACTIVE: "INACTIVE",
Tile.STATE_UNAVAILABLE: "UNAVAILABLE",
}
return (f"Tile(label={self._label}, state="
f"{state_names.get(self._state, self._state)})")
def hashCode(self) -> int:
return hash((self._label, self._state)) & 0x7FFFFFFF
def equals(self, other) -> bool:
if not isinstance(other, Tile):
return False
return (self._label == other._label and self._state == other._state)
# ═══════════════════════════════════════════════════════════════
# TileService
# ═══════════════════════════════════════════════════════════════
class TileService(Stub):
"""TileService — 快捷设置磁贴服务"""
# 动作
ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE"
ACTION_REQUEST_LISTENING = (
"android.service.quicksettings.action.REQUEST_LISTENING"
)
META_DATA_ACTIVE_TILE = "android.service.quicksettings.ACTIVE_TILE"
def __init__(self):
super().__init__()
self._qs_tile: Optional[Tile] = None
self._locked: bool = False
self._secure: bool = False
self._listening: bool = False
self._tile_added: bool = False
self._tile_removed: bool = False
@staticmethod
def requestListeningState(context: Any, component: Any):
"""请求监听状态 (Android-only)"""
pass
def isLocked(self) -> bool:
"""检查设备是否锁定"""
return self._locked
def isSecure(self) -> bool:
"""检查设备是否安全锁定"""
return self._secure
def startActivity(self, intent: Any):
"""启动 Activity (Android-only)"""
raise AndroidOnlyError(
"TileService.startActivity() 仅在 Android 设备上可用"
)
def showDialog(self, dialog: Any):
"""显示对话框"""
pass
def getQsTile(self) -> Optional[Tile]:
"""获取快速设置磁贴"""
return self._qs_tile
def setQsTile(self, tile: Tile):
self._qs_tile = tile
def unlockAndRun(self, runnable: Callable):
"""解锁并运行"""
runnable()
def onTileAdded(self):
"""磁贴被添加时回调"""
self._tile_added = True
def onTileRemoved(self):
"""磁贴被移除时回调"""
self._tile_removed = True
def onStartListening(self):
"""开始监听时回调"""
self._listening = True
def onStopListening(self):
"""停止监听时回调"""
self._listening = False
def onClick(self):
"""被点击时回调"""
if self._qs_tile:
current_state = self._qs_tile.getState()
if current_state == Tile.STATE_ACTIVE:
self._qs_tile.setState(Tile.STATE_INACTIVE)
elif current_state == Tile.STATE_INACTIVE:
self._qs_tile.setState(Tile.STATE_ACTIVE)
def onDestroy(self):
"""销毁时回调"""
pass
def isListening(self) -> bool:
return self._listening
def toString(self) -> str:
return (f"TileService(locked={self._locked}, secure={self._secure}, "
f"listening={self._listening})")
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
def equals(self, other) -> bool:
return self is other
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"TileService",
"Tile",
]
+62
View File
@@ -0,0 +1,62 @@
"""
Android Service VR 组件 — VrListenerService 等
方法签名完全对齐 Android SDK (android.service.vr 包)
"""
from typing import Any, Callable, List, Optional
from .._core import Stub, AndroidOnlyError
# ═══════════════════════════════════════════════════════════════
# VrListenerService
# ═══════════════════════════════════════════════════════════════
class VrListenerService(Stub):
"""VrListenerService — VR 监听服务 (已弃用, 仅存根)"""
SERVICE_INTERFACE = "android.service.vr.VrListenerService"
def __init__(self):
super().__init__()
self._current_vr_activity: Optional[str] = None
self._is_listening: bool = False
def onCurrentVrActivityChanged(self, component: Any):
"""当前 VR Activity 变化时回调"""
if component:
self._current_vr_activity = str(component)
def getCurrentVrActivity(self) -> Optional[str]:
"""获取当前 VR Activity"""
return self._current_vr_activity
def onConnected(self):
"""服务连接回调"""
self._is_listening = True
def onDisconnected(self):
"""服务断开回调"""
self._is_listening = False
def isListening(self) -> bool:
return self._is_listening
def toString(self) -> str:
return (f"VrListenerService("
f"activity={self._current_vr_activity})")
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
def equals(self, other) -> bool:
return self is other
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"VrListenerService",
]
+264
View File
@@ -0,0 +1,264 @@
"""
Android Service Wallpaper 组件 — WallpaperService, Engine, SurfaceHolder 等
方法签名完全对齐 Android SDK (android.service.wallpaper 包)
"""
import time
from typing import Any, Callable, Dict, List, Optional, Tuple
from .._core import Stub, AndroidOnlyError
# ═══════════════════════════════════════════════════════════════
# SurfaceHolder
# ═══════════════════════════════════════════════════════════════
class SurfaceHolder(Stub):
"""SurfaceHolder — 表面持有者 (桌面存根)"""
# 表面类型
SURFACE_TYPE_NORMAL = 0
SURFACE_TYPE_HARDWARE = 1
SURFACE_TYPE_GPU = 2
SURFACE_TYPE_PUSH_BUFFERS = 3
def __init__(self):
super().__init__()
self._surface: Optional[Any] = None
self._surface_frame: Optional[Any] = None
self._surface_type: int = SurfaceHolder.SURFACE_TYPE_NORMAL
self._fixed_size: Optional[Tuple[int, int]] = None
self._format: int = 0 # PixelFormat.UNKNOWN
self._callbacks: List[Any] = []
def getSurface(self) -> Any:
"""获取 Surface"""
return self._surface
def getSurfaceFrame(self) -> Any:
return self._surface_frame
def isCreating(self) -> bool:
return False
def setType(self, surface_type: int):
self._surface_type = surface_type
def getType(self) -> int:
return self._surface_type
def setFormat(self, format: int):
self._format = format
def getFormat(self) -> int:
return self._format
def setFixedSize(self, width: int, height: int):
self._fixed_size = (width, height)
def setSizeFromLayout(self):
pass
def addCallback(self, callback: Any):
if callback not in self._callbacks:
self._callbacks.append(callback)
def removeCallback(self, callback: Any):
if callback in self._callbacks:
self._callbacks.remove(callback)
def notifySurfaceCreated(self):
for cb in self._callbacks:
if hasattr(cb, 'surfaceCreated'):
cb.surfaceCreated(self)
def notifySurfaceChanged(self, format: int, width: int, height: int):
for cb in self._callbacks:
if hasattr(cb, 'surfaceChanged'):
cb.surfaceChanged(self, format, width, height)
def notifySurfaceDestroyed(self):
for cb in self._callbacks:
if hasattr(cb, 'surfaceDestroyed'):
cb.surfaceDestroyed(self)
def lockCanvas(self) -> Any:
"""锁定画布"""
return None
def lockCanvas(self, dirty: Any) -> Any:
"""锁定画布 (指定脏区域)"""
return None
def unlockCanvasAndPost(self, canvas: Any):
"""解锁并提交画布"""
pass
def toString(self) -> str:
size_str = (f"{self._fixed_size[0]}x{self._fixed_size[1]}"
if self._fixed_size else "dynamic")
return f"SurfaceHolder(size={size_str}, callbacks={len(self._callbacks)})"
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
def equals(self, other) -> bool:
return self is other
# ═══════════════════════════════════════════════════════════════
# WallpaperService
# ═══════════════════════════════════════════════════════════════
class WallpaperService(Stub):
"""WallpaperService — 动态壁纸服务基类"""
# 服务接口
SERVICE_INTERFACE = "android.service.wallpaper.WallpaperService"
ACTION_LIVE_WALLPAPER_CHOOSER = (
"android.service.wallpaper.LIVE_WALLPAPER_CHOOSER"
)
# 元数据
META_DATA_WALLPAPER = "android.service.wallpaper"
def __init__(self):
super().__init__()
def onCreate(self):
"""创建壁纸服务"""
pass
def onCreateEngine(self) -> "WallpaperService.Engine":
"""创建壁纸引擎"""
return WallpaperService.Engine(self)
def onDestroy(self):
"""销毁壁纸服务"""
pass
def toString(self) -> str:
return f"WallpaperService@{hex(id(self))}"
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
def equals(self, other) -> bool:
return self is other
# ── Engine ──
class Engine(Stub):
"""Engine — 壁纸引擎"""
def __init__(self, service: "WallpaperService" = None):
super().__init__()
self._service = service
self._is_visible: bool = True
self._is_created: bool = False
self._surface_holder = SurfaceHolder()
self._surface_holder.addCallback(self)
self._offset: Tuple[float, float, float, float, float] = (0, 0, 0, 0, 0)
self._x_ppw: float = 1.0
self._y_ppw: float = 1.0
self._desired_width: int = 0
self._desired_height: int = 0
self._touch_events_enabled: bool = False
self._offset_notifications_enabled: bool = False
self._draw_count: int = 0
def onCreate(self, surface_holder: SurfaceHolder):
"""创建引擎"""
self._is_created = True
def onSurfaceCreated(self, holder: SurfaceHolder):
"""表面创建时回调"""
pass
def onSurfaceDestroyed(self, holder: SurfaceHolder):
"""表面销毁时回调"""
pass
def onSurfaceChanged(self, holder: SurfaceHolder,
format: int, width: int, height: int):
"""表面尺寸变化时回调"""
pass
def onDraw(self, canvas: Any):
"""绘制壁纸"""
self._draw_count += 1
def onOffsetsChanged(self, x_offset: float, y_offset: float,
x_step: float, y_step: float,
x_pixel_offset: int, y_pixel_offset: int):
"""偏移量变化回调"""
self._offset = (x_offset, y_offset, x_step, y_step)
def onTouchEvent(self, motion_event: Any):
"""触摸事件回调"""
pass
def onVisibilityChanged(self, visible: bool):
"""可见性变化回调"""
self._is_visible = visible
def setOffsetNotifications(self, x_ppw: float, y_ppw: float):
"""设置偏移通知"""
self._x_ppw = x_ppw
self._y_ppw = y_ppw
self._offset_notifications_enabled = True
def getSurfaceHolder(self) -> SurfaceHolder:
"""获取表面持有者"""
return self._surface_holder
def getDesiredMinimumWidth(self) -> int:
return self._desired_width
def getDesiredMinimumHeight(self) -> int:
return self._desired_height
def isVisible(self) -> bool:
return self._is_visible
def isPreview(self) -> bool:
return False
def setTouchEventsEnabled(self, enabled: bool):
self._touch_events_enabled = enabled
def setCreateSurfaceFromSurfaceControlResult(self, result: Any):
pass
def surfaceCreated(self, holder: SurfaceHolder):
"""SurfaceHolder.Callback — 表面创建"""
self.onSurfaceCreated(holder)
def surfaceChanged(self, holder: SurfaceHolder,
format: int, width: int, height: int):
"""SurfaceHolder.Callback — 表面变化"""
self.onSurfaceChanged(holder, format, width, height)
def surfaceDestroyed(self, holder: SurfaceHolder):
"""SurfaceHolder.Callback — 表面销毁"""
self.onSurfaceDestroyed(holder)
def toString(self) -> str:
return (f"Engine(visible={self._is_visible}, "
f"draws={self._draw_count})")
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
def equals(self, other) -> bool:
return self is other
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"WallpaperService",
"SurfaceHolder",
]
+519
View File
@@ -0,0 +1,519 @@
"""
Android SIP API — SipManager, SipProfile, SipSession, SipAudioCall
(已废弃但存在于 SDK 中)
方法签名完全对齐 Android SDK
"""
import sys
from typing import Any, Dict, List, Optional, Set, Tuple, Callable
from .._core import Stub, AndroidOnlyError
from ..java.lang import RuntimeException
IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {})
__all__ = [
"SipManager",
"SipProfile",
"SipSession",
"SipAudioCall",
]
# ═══════════════════════════════════════════════════════════════
# SipManager
# ═══════════════════════════════════════════════════════════════
class SipManager(Stub):
"""SipManager — SIP 会话管理器 (桌面存根, 已废弃)"""
EXTRA_CALL_ID = "android.sip.extra.CALL_ID"
EXTRA_OFFER_SD = "android.sip.extra.OFFER_SD"
EXTRA_PEER_PROFILE = "android.sip.extra.PEER_PROFILE"
def __init__(self, context: Any = None):
super().__init__()
self._context = context
self._opened = False
self._registered = False
self._session: Optional["SipSession"] = None
self._listener: Optional[Callable] = None
@staticmethod
def isSipWifiOnly(context: Any) -> bool:
"""检查 SIP 是否仅限 WiFi"""
return False
@staticmethod
def isVoipSupported(context: Any) -> bool:
"""检查是否支持 VoIP"""
return True
@staticmethod
def isApiSupported(context: Any) -> bool:
"""检查 SIP API 是否支持"""
return True
@staticmethod
def newInstance(context: Any) -> "SipManager":
"""创建新实例"""
return SipManager(context)
def open(self, localProfile: "SipProfile") -> "SipSession":
"""打开 SIP 会话"""
self._opened = True
self._session = SipSession(localProfile)
return self._session
def setRegistrationListener(
self, localProfileUri: str, listener: "SipSession.Listener"
):
"""设置注册监听器"""
if self._session:
self._session.setListener(listener)
def close(self, localProfileUri: str):
"""关闭 SIP 会话"""
self._opened = False
self._registered = False
self._session = None
def getSessionFor(self, intent: Any) -> Optional["SipSession"]:
"""获取 Intent 对应的 SIP 会话"""
return self._session
def register(
self, localProfile: "SipProfile", expiryTime: int, listener: "SipSession.Listener"
):
"""注册 SIP"""
raise AndroidOnlyError("SipManager.register() 仅在 Android 设备上可用")
def unregister(
self, localProfile: "SipProfile", listener: "SipSession.Listener"
):
"""注销 SIP"""
raise AndroidOnlyError("SipManager.unregister() 仅在 Android 设备上可用")
def isOpened(self, localProfileUri: str) -> bool:
"""检查是否已打开"""
return self._opened
def isRegistered(self, localProfileUri: str) -> bool:
"""检查是否已注册"""
return self._registered
def createSipSessionIfNeeded(self, localProfile: "SipProfile") -> "SipSession":
"""如有必要则创建 SIP 会话"""
if self._session is None:
self._session = SipSession(localProfile)
return self._session
def createSipProfile(self, intent: Any) -> Optional["SipProfile"]:
"""从 Intent 创建 SIP 配置文件"""
return None
def toString(self) -> str:
return f"SipManager(opened={self._opened}, registered={self._registered})"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# SipProfile
# ═══════════════════════════════════════════════════════════════
class SipProfile(Stub):
"""SipProfile — SIP 配置文件"""
# --- Protocol constants ---
PROTOCOL_UDP = "UDP"
PROTOCOL_TCP = "TCP"
PROTOCOL_TLS = "TLS"
class Builder(Stub):
"""SipProfile.Builder — SIP 配置文件构建器"""
def __init__(self, uri: str = ""):
super().__init__()
self._profile = SipProfile()
self._profile._uri = uri
def setProfileName(self, name: str) -> "SipProfile.Builder":
self._profile._profile_name = name
return self
def setUserName(self, userName: str) -> "SipProfile.Builder":
self._profile._user_name = userName
return self
def setDomain(self, domain: str) -> "SipProfile.Builder":
self._profile._domain = domain
return self
def setPassword(self, password: str) -> "SipProfile.Builder":
self._profile._password = password
return self
def setPort(self, port: int) -> "SipProfile.Builder":
self._profile._port = port
return self
def setProtocol(self, protocol: str) -> "SipProfile.Builder":
self._profile._protocol = protocol
return self
def setSendKeepAlive(self, enabled: bool) -> "SipProfile.Builder":
self._profile._send_keep_alive = enabled
return self
def setAutoRegistration(self, enabled: bool) -> "SipProfile.Builder":
self._profile._auto_registration = enabled
return self
def setDisplayName(self, displayName: str) -> "SipProfile.Builder":
self._profile._display_name = displayName
return self
def setAuthUserName(self, authUserName: str) -> "SipProfile.Builder":
self._profile._auth_user_name = authUserName
return self
def build(self) -> "SipProfile":
return self._profile
def toString(self) -> str:
return f"SipProfile.Builder(name={self._profile._profile_name})"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
def __init__(self):
super().__init__()
self._uri = ""
self._profile_name = ""
self._user_name = ""
self._domain = ""
self._display_name = ""
self._password = ""
self._protocol = self.PROTOCOL_UDP
self._port = 5060
self._send_keep_alive = False
self._auto_registration = True
self._auth_user_name = ""
def getProfileName(self) -> str:
return self._profile_name
def getUserName(self) -> str:
return self._user_name
def getDomain(self) -> str:
return self._domain
def getDisplayName(self) -> str:
return self._display_name
def getPassword(self) -> str:
return self._password
def getProtocol(self) -> str:
return self._protocol
def getPort(self) -> int:
return self._port
def getSendKeepAlive(self) -> bool:
return self._send_keep_alive
def isAutoRegistration(self) -> bool:
return self._auto_registration
def getUri(self) -> str:
return self._uri
def toString(self) -> str:
return (f"SipProfile(name={self._profile_name}, "
f"user={self._user_name}@{self._domain})")
def equals(self, other) -> bool:
if not isinstance(other, SipProfile):
return False
return self._uri == other._uri
def hashCode(self) -> int:
return hash(self._uri) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# SipSession
# ═══════════════════════════════════════════════════════════════
class SipSession(Stub):
"""SipSession — SIP 会话"""
# --- State constants ---
STATE_READY_TO_CALL = 0
STATE_INCOMING_CALL = 1
STATE_INCOMING_CALL_ANSWERING = 2
STATE_IN_CALL = 3
STATE_OUTGOING_CALL = 4
STATE_OUTGOING_CALL_RINGING = 5
STATE_OUTGOING_CALL_CANCELING = 6
STATE_PINGING = 7
STATE_DEREGISTERING = 8
STATE_CLOSED = 9
def __init__(self, profile: "SipProfile" = None):
super().__init__()
self._profile = profile
self._listener: Optional["SipSession.Listener"] = None
self._state = self.STATE_CLOSED
self._call_id = ""
self._peer_profile: Optional[SipProfile] = None
self._in_call = False
def setListener(self, listener: "SipSession.Listener"):
"""设置会话监听器"""
self._listener = listener
def register(self, duration: int):
"""注册 SIP"""
raise AndroidOnlyError("SipSession.register() 仅在 Android 设备上可用")
def unregister(self):
"""注销 SIP"""
raise AndroidOnlyError("SipSession.unregister() 仅在 Android 设备上可用")
def makeAudioCall(
self, localProfile: "SipProfile", peerProfile: "SipProfile"
):
"""发起音频呼叫"""
raise AndroidOnlyError("SipSession.makeAudioCall() 仅在 Android 设备上可用")
def answerCall(self, callId: str, sessionDescription: int):
"""接听呼叫"""
raise AndroidOnlyError("SipSession.answerCall() 仅在 Android 设备上可用")
def endCall(self):
"""结束呼叫"""
raise AndroidOnlyError("SipSession.endCall() 仅在 Android 设备上可用")
def changeCall(self, callId: str, sessionDescription: int):
"""更改呼叫"""
raise AndroidOnlyError("SipSession.changeCall() 仅在 Android 设备上可用")
def getCallId(self) -> str:
return self._call_id
def getPeerProfile(self) -> Optional["SipProfile"]:
return self._peer_profile
def getState(self) -> int:
return self._state
def isInCall(self) -> bool:
return self._in_call
def getLocalProfile(self) -> Optional["SipProfile"]:
return self._profile
# ═══════════════════════════════════════════════════════════
# Listener
# ═══════════════════════════════════════════════════════════
class Listener(Stub):
"""SipSession.Listener — SIP 会话监听器"""
def __init__(self):
super().__init__()
def onCalling(self, session: "SipSession"):
"""呼叫中回调"""
pass
def onRinging(self, session: "SipSession", caller: "SipProfile"):
"""响铃回调"""
pass
def onCallEstablished(self, session: "SipSession", sessionDescription: str):
"""呼叫建立回调"""
pass
def onCallEnded(self, session: "SipSession"):
"""呼叫结束回调"""
pass
def onCallBusy(self, session: "SipSession"):
"""呼叫忙碌回调"""
pass
def onError(self, session: "SipSession", errorCode: int, errorMessage: str):
"""错误回调"""
pass
def onCallChangeFailed(self, session: "SipSession", errorCode: int, errorMessage: str):
"""呼叫变更失败回调"""
pass
def onRegistrationDone(self, session: "SipSession", duration: int):
"""注册成功回调"""
pass
def onRegistrationFailed(
self, session: "SipSession", errorCode: int, errorMessage: str
):
"""注册失败回调"""
pass
def onRegistrationTimeout(self, session: "SipSession"):
"""注册超时回调"""
pass
def toString(self) -> str:
return f"SipSession.Listener@{hex(id(self))}"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
def toString(self) -> str:
return f"SipSession(state={self._state})"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# SipAudioCall
# ═══════════════════════════════════════════════════════════════
class SipAudioCall(Stub):
"""SipAudioCall — SIP 音频呼叫"""
def __init__(self, context: Any = None, localProfile: "SipProfile" = None):
super().__init__()
self._context = context
self._local_profile = localProfile
self._listener: Optional["SipAudioCall.Listener"] = None
self._muted = False
self._holding = False
self._in_call = False
def setListener(self, listener: "SipAudioCall.Listener"):
"""设置音频呼叫监听器"""
self._listener = listener
def startAudio(self):
"""开始音频"""
raise AndroidOnlyError("SipAudioCall.startAudio() 仅在 Android 设备上可用")
def stopAudio(self):
"""停止音频"""
raise AndroidOnlyError("SipAudioCall.stopAudio() 仅在 Android 设备上可用")
def close(self):
"""关闭音频呼叫"""
self._in_call = False
self._holding = False
def isMuted(self) -> bool:
return self._muted
def toggleMute(self):
"""切换静音"""
self._muted = not self._muted
def hold(self, hold: int):
"""保持呼叫"""
self._holding = (hold != 0)
def continueCall(self, sessionDescription: int):
"""继续呼叫"""
raise AndroidOnlyError("SipAudioCall.continueCall() 仅在 Android 设备上可用")
def makeCall(
self, localProfile: "SipProfile", peerProfile: "SipProfile", timeout: int
):
"""发起呼叫"""
raise AndroidOnlyError("SipAudioCall.makeCall() 仅在 Android 设备上可用")
def answerCall(self, sessionDescription: int):
"""接听呼叫"""
raise AndroidOnlyError("SipAudioCall.answerCall() 仅在 Android 设备上可用")
def sendDtmf(self, code: int):
"""发送 DTMF"""
pass
# ═══════════════════════════════════════════════════════════
# Listener
# ═══════════════════════════════════════════════════════════
class Listener(Stub):
"""SipAudioCall.Listener — 音频呼叫监听器"""
def __init__(self):
super().__init__()
def onReadyToCall(self, call: "SipAudioCall"):
"""准备呼叫回调"""
pass
def onCallEstablished(self, call: "SipAudioCall", sessionDescription: str):
"""呼叫建立回调"""
pass
def onCallEnded(self, call: "SipAudioCall"):
"""呼叫结束回调"""
pass
def onCallHeld(self, call: "SipAudioCall"):
"""呼叫保持回调"""
pass
def onCallChanged(self, call: "SipAudioCall"):
"""呼叫变更回调"""
pass
def onError(self, call: "SipAudioCall", errorCode: int, errorMessage: str):
"""错误回调"""
pass
def onRingingBack(self, call: "SipAudioCall"):
"""回铃回调"""
pass
def onRinging(self, call: "SipAudioCall", caller: "SipProfile"):
"""响铃回调"""
pass
def toString(self) -> str:
return f"SipAudioCall.Listener@{hex(id(self))}"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
def toString(self) -> str:
return f"SipAudioCall(inCall={self._in_call})"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
+370
View File
@@ -0,0 +1,370 @@
"""
Android Speech API — SpeechRecognizer, RecognitionListener, RecognitionService, RecognizerIntent
方法签名完全对齐 Android SDK
"""
import sys
import threading
import time
from typing import Any, Dict, List, Optional, Set, Tuple, Callable
from .._core import Stub, AndroidOnlyError
from ..java.lang import RuntimeException
IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {})
__all__ = [
"SpeechRecognizer",
"RecognitionListener",
"RecognitionService",
"RecognizerIntent",
"RecognizerResultsIntent",
]
# ═══════════════════════════════════════════════════════════════
# SpeechRecognizer
# ═══════════════════════════════════════════════════════════════
class SpeechRecognizer(Stub):
"""SpeechRecognizer — 语音识别器 (桌面麦克风模拟)"""
RESULTS_RECOGNITION = "results_recognition"
CONFIDENCE_SCORES = "confidence_scores"
ERROR_NETWORK_TIMEOUT = 1
ERROR_NETWORK = 2
ERROR_AUDIO = 3
ERROR_SERVER = 4
ERROR_CLIENT = 5
ERROR_SPEECH_TIMEOUT = 6
ERROR_NO_MATCH = 7
ERROR_RECOGNIZER_BUSY = 8
ERROR_INSUFFICIENT_PERMISSIONS = 9
ERROR_TOO_MANY_REQUESTS = 10
ERROR_LANGUAGE_NOT_SUPPORTED = 11
ERROR_LANGUAGE_UNAVAILABLE = 12
ERROR_SERVER_DISCONNECTED = 13
def __init__(self, context: Any = None):
super().__init__()
self._context = context
self._listener: Optional["RecognitionListener"] = None
self._is_listening = False
self._destroyed = False
self._simulate_timer: Optional[threading.Timer] = None
@staticmethod
def createSpeechRecognizer(context: Any) -> "SpeechRecognizer":
"""创建语音识别器"""
return SpeechRecognizer(context)
@staticmethod
def isRecognitionAvailable(context: Any) -> bool:
"""检查语音识别是否可用"""
try:
import speech_recognition as sr
return True
except ImportError:
try:
import whisper # noqa: F401
return True
except ImportError:
pass
return False
def setRecognitionListener(self, listener: "RecognitionListener"):
"""设置识别监听器"""
self._listener = listener
def startListening(self, intent: Any):
"""开始语音识别"""
if self._destroyed:
return
self._is_listening = True
# 使用简单识别模拟
if self._listener:
self._listener.onReadyForSpeech(None)
self._listener.onBeginningOfSpeech()
# 模拟麦克风音量变化
for i in range(3):
self._listener.onRmsChanged(5.0 + i * 2.0)
time.sleep(0.05)
self._listener.onBufferReceived(bytearray(1024))
self._listener.onEndOfSpeech()
# 模拟识别结果
results = {self.RESULTS_RECOGNITION: ["hello world"], self.CONFIDENCE_SCORES: [0.95]}
self._listener.onResults(results)
def stopListening(self):
"""停止语音识别"""
self._is_listening = False
if self._simulate_timer:
self._simulate_timer.cancel()
self._simulate_timer = None
def cancel(self):
"""取消语音识别"""
self._is_listening = False
if self._simulate_timer:
self._simulate_timer.cancel()
self._simulate_timer = None
if self._listener:
self._listener.onError(self.ERROR_NO_MATCH)
def destroy(self):
"""销毁语音识别器"""
self._destroyed = True
self._is_listening = False
if self._simulate_timer:
self._simulate_timer.cancel()
self._simulate_timer = None
self._listener = None
def toString(self) -> str:
return f"SpeechRecognizer(listening={self._is_listening})"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# RecognitionListener
# ═══════════════════════════════════════════════════════════════
class RecognitionListener(Stub):
"""RecognitionListener — 语音识别监听器"""
def __init__(self):
super().__init__()
def onReadyForSpeech(self, params: Any):
"""准备开始语音识别回调"""
pass
def onBeginningOfSpeech(self):
"""开始说话回调"""
pass
def onRmsChanged(self, rmsdB: float):
"""音量变化回调"""
pass
def onBufferReceived(self, buffer: bytearray):
"""接收音频缓冲区回调"""
pass
def onEndOfSpeech(self):
"""说话结束回调"""
pass
def onError(self, error: int):
"""错误回调"""
pass
def onResults(self, results: Dict[str, Any]):
"""识别结果回调"""
pass
def onPartialResults(self, partialResults: Dict[str, Any]):
"""部分识别结果回调"""
pass
def onSegmentResults(self, segmentResults: Dict[str, Any]):
"""分段结果回调"""
pass
def onEndOfSegmentedSession(self, sessionId: int):
"""分段会话结束回调"""
pass
def onLanguageDetection(self, language: str):
"""语言检测回调"""
pass
def onEvent(self, eventType: int, params: Any):
"""事件回调"""
pass
def toString(self) -> str:
return f"RecognitionListener@{hex(id(self))}"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# RecognitionService
# ═══════════════════════════════════════════════════════════════
class RecognitionService(Stub):
"""RecognitionService — 语音识别服务"""
SERVICE_INTERFACE = "android.speech.RecognitionService"
SERVICE_META_DATA = "android.speech"
def __init__(self):
super().__init__()
self._callback: Optional["RecognitionService.Callback"] = None
def onStartListening(self, intent: Any, listener: "RecognitionService.Callback"):
"""开始监听回调"""
pass
def onCancel(self, listener: "RecognitionService.Callback"):
"""取消回调"""
pass
def onStopListening(self, listener: "RecognitionService.Callback"):
"""停止监听回调"""
pass
def onDestroy(self):
"""销毁回调"""
pass
def oneShotListening(self, intent: Any, listener: "RecognitionService.Callback"):
"""一次性监听"""
if listener:
listener.readyForSpeech(None)
listener.beginningOfSpeech()
listener.endOfSpeech()
results = {SpeechRecognizer.RESULTS_RECOGNITION: ["hello"]}
listener.results(results)
def getCurrentCallback(self) -> Optional["RecognitionService.Callback"]:
return self._callback
# ═══════════════════════════════════════════════════════════
# Callback
# ═══════════════════════════════════════════════════════════
class Callback(Stub):
"""RecognitionService.Callback — 识别服务回调"""
def __init__(self):
super().__init__()
def results(self, results: Dict[str, Any]):
"""返回识别结果"""
pass
def partialResults(self, partialResults: Dict[str, Any]):
"""返回部分结果"""
pass
def error(self, error: int):
"""返回错误"""
pass
def beginningOfSpeech(self):
"""开始说话回调"""
pass
def bufferReceived(self, buffer: bytearray):
"""接收音频缓冲区回调"""
pass
def endOfSpeech(self):
"""说话结束回调"""
pass
def readyForSpeech(self, params: Any):
"""准备开始语音识别"""
pass
def rmsChanged(self, rmsdB: float):
"""音量变化回调"""
pass
def toString(self) -> str:
return f"RecognitionService.Callback@{hex(id(self))}"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
def toString(self) -> str:
return f"RecognitionService@{hex(id(self))}"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# RecognizerIntent
# ═══════════════════════════════════════════════════════════════
class RecognizerIntent(Stub):
"""RecognizerIntent — 识别器 Intent 常量"""
ACTION_RECOGNIZE_SPEECH = "android.speech.action.RECOGNIZE_SPEECH"
ACTION_WEB_SEARCH = "android.speech.action.WEB_SEARCH"
ACTION_VOICE_SEARCH_HANDS_FREE = "android.speech.action.VOICE_SEARCH_HANDS_FREE"
ACTION_GET_LANGUAGE_DETAILS = "android.speech.action.GET_LANGUAGE_DETAILS"
EXTRA_LANGUAGE_MODEL = "android.speech.extra.LANGUAGE_MODEL"
EXTRA_LANGUAGE = "android.speech.extra.LANGUAGE"
EXTRA_MAX_RESULTS = "android.speech.extra.MAX_RESULTS"
EXTRA_PARTIAL_RESULTS = "android.speech.extra.PARTIAL_RESULTS"
EXTRA_CALLING_PACKAGE = "android.speech.extra.CALLING_PACKAGE"
EXTRA_PROMPT = "android.speech.extra.PROMPT"
EXTRA_SPEECH_INPUT_MINIMUM_LENGTH_MILLIS = "android.speech.extra.SPEECH_INPUT_MINIMUM_LENGTH_MILLIS"
EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS = "android.speech.extra.SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS"
EXTRA_SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS = "android.speech.extra.SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS"
EXTRA_CONFIDENCE_SCORES = "android.speech.extra.CONFIDENCE_SCORES"
EXTRA_RESULTS = "android.speech.extra.RESULTS"
EXTRA_SECURE = "android.speech.extra.SECURE"
EXTRA_SUPPORTED_LANGUAGES = "android.speech.extra.SUPPORTED_LANGUAGES"
EXTRA_LANGUAGE_PREFERENCE = "android.speech.extra.LANGUAGE_PREFERENCE"
EXTRA_ONLY_RETURN_LANGUAGE_PREFERENCE = "android.speech.extra.ONLY_RETURN_LANGUAGE_PREFERENCE"
EXTRA_LANGUAGE_DETAILS = "android.speech.extra.LANGUAGE_DETAILS"
LANGUAGE_MODEL_FREE_FORM = "free_form"
LANGUAGE_MODEL_WEB_SEARCH = "web_search"
RESULT_AUDIO_ERROR = 1
RESULT_CLIENT_ERROR = 2
RESULT_NETWORK_ERROR = 3
RESULT_SERVER_ERROR = 4
RESULT_NO_MATCH = 5
DETAIL_META_DATA = "android.speech.DETAIL_META_DATA"
def __init__(self):
super().__init__()
# ═══════════════════════════════════════════════════════════════
# RecognizerResultsIntent
# ═══════════════════════════════════════════════════════════════
class RecognizerResultsIntent(Stub):
"""RecognizerResultsIntent — 识别结果 Intent 常量"""
ACTION_VOICE_SEARCH_RESULTS = "android.speech.action.VOICE_SEARCH_RESULTS"
EXTRA_VOICE_SEARCH_RESULT_STRINGS = "android.speech.extra.VOICE_SEARCH_RESULT_STRINGS"
EXTRA_VOICE_SEARCH_RESULT_URLS = "android.speech.extra.VOICE_SEARCH_RESULT_URLS"
EXTRA_VOICE_SEARCH_RESULT_HTML = "android.speech.extra.VOICE_SEARCH_RESULT_HTML"
EXTRA_VOICE_SEARCH_RESULT_HTML_BASE_URLS = "android.speech.extra.VOICE_SEARCH_RESULT_HTML_BASE_URLS"
URI_SCHEME_INLINE = "inline"
def __init__(self):
super().__init__()
+592
View File
@@ -0,0 +1,592 @@
"""
Android System 组件 — Os, StructStat, StructUtsname, OsConstants 等
方法签名完全对齐 Android SDK (android.system 包)
"""
import os as _os
import stat as _stat
import platform
import errno
from typing import Any, Dict, List, Optional, Tuple
from .._core import Stub, AndroidOnlyError
# ═══════════════════════════════════════════════════════════════
# OsConstants
# ═══════════════════════════════════════════════════════════════
class OsConstants(Stub):
"""OsConstants — 系统调用常量,映射自 os/stat 模块"""
# 文件访问常量
R_OK = _os.R_OK
W_OK = _os.W_OK
X_OK = _os.X_OK
F_OK = _os.F_OK
# 文件权限常量
S_IRWXU = _stat.S_IRWXU
S_IRUSR = _stat.S_IRUSR
S_IWUSR = _stat.S_IWUSR
S_IXUSR = _stat.S_IXUSR
S_IRWXG = _stat.S_IRWXG
S_IRGRP = _stat.S_IRGRP
S_IWGRP = _stat.S_IWGRP
S_IXGRP = _stat.S_IXGRP
S_IRWXO = _stat.S_IRWXO
S_IROTH = _stat.S_IROTH
S_IWOTH = _stat.S_IWOTH
S_IXOTH = _stat.S_IXOTH
S_ISUID = _stat.S_ISUID
S_ISGID = _stat.S_ISGID
S_ISVTX = _stat.S_ISVTX
# 文件类型常量
S_IFMT = _stat.S_IFMT
S_IFSOCK = _stat.S_IFSOCK
S_IFLNK = _stat.S_IFLNK
S_IFREG = _stat.S_IFREG
S_IFBLK = _stat.S_IFBLK
S_IFDIR = _stat.S_IFDIR
S_IFCHR = _stat.S_IFCHR
S_IFIFO = _stat.S_IFIFO
# 标准文件描述符
STDIN_FILENO = 0
STDOUT_FILENO = 1
STDERR_FILENO = 2
# 信号常量
SIGHUP = 1
SIGINT = 2
SIGQUIT = 3
SIGILL = 4
SIGTRAP = 5
SIGABRT = 6
SIGBUS = 7
SIGFPE = 8
SIGKILL = 9
SIGUSR1 = 10
SIGSEGV = 11
SIGUSR2 = 12
SIGPIPE = 13
SIGALRM = 14
SIGTERM = 15
SIGCHLD = 17
SIGCONT = 18
SIGSTOP = 19
SIGTSTP = 20
SIGTTIN = 21
SIGTTOU = 22
# errno 常量
EPERM = errno.EPERM
ENOENT = errno.ENOENT
ESRCH = errno.ESRCH
EINTR = errno.EINTR
EIO = errno.EIO
ENXIO = errno.ENXIO
E2BIG = errno.E2BIG
ENOEXEC = errno.ENOEXEC
EBADF = errno.EBADF
ECHILD = errno.ECHILD
EAGAIN = errno.EAGAIN
ENOMEM = errno.ENOMEM
EACCES = errno.EACCES
EFAULT = errno.EFAULT
ENOTBLK = errno.ENOTBLK
EBUSY = errno.EBUSY
EEXIST = errno.EEXIST
EXDEV = errno.EXDEV
ENODEV = errno.ENODEV
ENOTDIR = errno.ENOTDIR
EISDIR = errno.EISDIR
EINVAL = errno.EINVAL
ENFILE = errno.ENFILE
EMFILE = errno.EMFILE
ENOTTY = errno.ENOTTY
ETXTBSY = errno.ETXTBSY
EFBIG = errno.EFBIG
ENOSPC = errno.ENOSPC
ESPIPE = errno.ESPIPE
EROFS = errno.EROFS
EMLINK = errno.EMLINK
EPIPE = errno.EPIPE
EDOM = errno.EDOM
ERANGE = errno.ERANGE
EDEADLK = errno.EDEADLK
ENAMETOOLONG = errno.ENAMETOOLONG
ENOLCK = errno.ENOLCK
ENOSYS = errno.ENOSYS
ENOTEMPTY = errno.ENOTEMPTY
ELOOP = errno.ELOOP
ENOMSG = errno.ENOMSG
EIDRM = errno.EIDRM
ENOSTR = errno.ENOSTR
ENODATA = errno.ENODATA
ETIME = errno.ETIME
ENOSR = errno.ENOSR
EREMOTE = errno.EREMOTE
ENOLINK = errno.ENOLINK
EPROTO = errno.EPROTO
EMULTIHOP = errno.EMULTIHOP
EBADMSG = errno.EBADMSG
EOVERFLOW = errno.EOVERFLOW
EILSEQ = errno.EILSEQ
ENOTSOCK = errno.ENOTSOCK
EDESTADDRREQ = errno.EDESTADDRREQ
EMSGSIZE = errno.EMSGSIZE
EPROTOTYPE = errno.EPROTOTYPE
ENOPROTOOPT = errno.ENOPROTOOPT
EPROTONOSUPPORT = errno.EPROTONOSUPPORT
ESOCKTNOSUPPORT = errno.ESOCKTNOSUPPORT
EOPNOTSUPP = errno.EOPNOTSUPP
EPFNOSUPPORT = errno.EPFNOSUPPORT
EAFNOSUPPORT = errno.EAFNOSUPPORT
EADDRINUSE = errno.EADDRINUSE
EADDRNOTAVAIL = errno.EADDRNOTAVAIL
ENETDOWN = errno.ENETDOWN
ENETUNREACH = errno.ENETUNREACH
ENETRESET = errno.ENETRESET
ECONNABORTED = errno.ECONNABORTED
ECONNRESET = errno.ECONNRESET
ENOBUFS = errno.ENOBUFS
EISCONN = errno.EISCONN
ENOTCONN = errno.ENOTCONN
ESHUTDOWN = errno.ESHUTDOWN
ETOOMANYREFS = errno.ETOOMANYREFS
ETIMEDOUT = errno.ETIMEDOUT
ECONNREFUSED = errno.ECONNREFUSED
EHOSTDOWN = errno.EHOSTDOWN
EHOSTUNREACH = errno.EHOSTUNREACH
EALREADY = errno.EALREADY
EINPROGRESS = errno.EINPROGRESS
ESTALE = errno.ESTALE
EUCLEAN = errno.EUCLEAN
ENOTNAM = errno.ENOTNAM
ENAVAIL = errno.ENAVAIL
EISNAM = errno.EISNAM
EREMOTEIO = errno.EREMOTEIO
EDQUOT = errno.EDQUOT
ENOMEDIUM = errno.ENOMEDIUM
EMEDIUMTYPE = errno.EMEDIUMTYPE
ECANCELED = errno.ECANCELED
ENOKEY = errno.ENOKEY
EKEYEXPIRED = errno.EKEYEXPIRED
EKEYREVOKED = errno.EKEYREVOKED
EKEYREJECTED = errno.EKEYREJECTED
EOWNERDEAD = errno.EOWNERDEAD
ENOTRECOVERABLE = errno.ENOTRECOVERABLE
ERFKILL = errno.ERFKILL
EHWPOISON = getattr(errno, 'EHWPOISON', 1001)
def __init__(self):
super().__init__()
# ═══════════════════════════════════════════════════════════════
# StructStat
# ═══════════════════════════════════════════════════════════════
class StructStat(Stub):
"""StructStat — stat() 返回的文件状态结构体"""
def __init__(self, st_mode: int = 0, st_ino: int = 0, st_dev: int = 0,
st_nlink: int = 0, st_uid: int = 0, st_gid: int = 0,
st_size: int = 0, st_atime: int = 0, st_mtime: int = 0,
st_ctime: int = 0, st_blksize: int = 4096,
st_blocks: int = 0, st_rdev: int = 0):
super().__init__()
self.st_mode = st_mode
self.st_ino = st_ino
self.st_dev = st_dev
self.st_nlink = st_nlink
self.st_uid = st_uid
self.st_gid = st_gid
self.st_size = st_size
self.st_atime = st_atime
self.st_mtime = st_mtime
self.st_ctime = st_ctime
self.st_blksize = st_blksize
self.st_blocks = st_blocks
self.st_rdev = st_rdev
self.st_atime_ns = st_atime * 1_000_000_000
self.st_mtime_ns = st_mtime * 1_000_000_000
self.st_ctime_ns = st_ctime * 1_000_000_000
@staticmethod
def from_os_stat(st: _os.stat_result) -> "StructStat":
"""从 Python os.stat_result 创建 StructStat"""
return StructStat(
st_mode=st.st_mode,
st_ino=st.st_ino,
st_dev=st.st_dev,
st_nlink=st.st_nlink,
st_uid=st.st_uid,
st_gid=st.st_gid,
st_size=st.st_size,
st_atime=int(st.st_atime),
st_mtime=int(st.st_mtime),
st_ctime=int(st.st_ctime),
st_blksize=st.st_blksize,
st_blocks=st.st_blocks,
st_rdev=st.st_rdev,
)
def toString(self) -> str:
return (f"StructStat(st_mode={oct(self.st_mode)}, st_size={self.st_size}, "
f"st_mtime={self.st_mtime}, st_uid={self.st_uid}, st_gid={self.st_gid})")
def hashCode(self) -> int:
return hash((self.st_dev, self.st_ino, self.st_mode)) & 0x7FFFFFFF
def equals(self, other) -> bool:
if not isinstance(other, StructStat):
return False
return (self.st_dev == other.st_dev and self.st_ino == other.st_ino
and self.st_mode == other.st_mode and self.st_size == other.st_size)
# ═══════════════════════════════════════════════════════════════
# StructUtsname
# ═══════════════════════════════════════════════════════════════
class StructUtsname(Stub):
"""StructUtsname — uname() 返回的系统信息结构体"""
def __init__(self, sysname: str = "", nodename: str = "",
release: str = "", version: str = "", machine: str = ""):
super().__init__()
self.sysname = sysname or platform.system()
self.nodename = nodename or platform.node()
self.release = release or platform.release()
self.version = version or platform.version()
self.machine = machine or platform.machine()
@staticmethod
def from_platform() -> "StructUtsname":
"""从 Python platform 模块创建 StructUtsname"""
uname = platform.uname()
return StructUtsname(
sysname=uname.system,
nodename=uname.node,
release=uname.release,
version=uname.version,
machine=uname.machine,
)
def toString(self) -> str:
return (f"StructUtsname(sysname={self.sysname}, nodename={self.nodename}, "
f"release={self.release}, version={self.version}, machine={self.machine})")
def hashCode(self) -> int:
return hash((self.sysname, self.nodename, self.release)) & 0x7FFFFFFF
def equals(self, other) -> bool:
if not isinstance(other, StructUtsname):
return False
return (self.sysname == other.sysname and self.nodename == other.nodename
and self.release == other.release)
# ═══════════════════════════════════════════════════════════════
# Os
# ═══════════════════════════════════════════════════════════════
class Os(Stub):
"""Os — 系统调用包装器,代理到 Python os 模块"""
def __init__(self):
super().__init__()
# ── 文件操作 ──
@staticmethod
def stat(path: str) -> StructStat:
"""获取文件状态"""
st = _os.stat(path)
return StructStat.from_os_stat(st)
@staticmethod
def lstat(path: str) -> StructStat:
"""获取符号链接自身状态"""
st = _os.lstat(path)
return StructStat.from_os_stat(st)
@staticmethod
def chmod(path: str, mode: int):
"""修改文件权限"""
_os.chmod(path, mode)
@staticmethod
def chown(path: str, uid: int, gid: int):
"""修改文件所有者"""
_os.chown(path, uid, gid)
@staticmethod
def link(oldpath: str, newpath: str):
"""创建硬链接"""
_os.link(oldpath, newpath)
@staticmethod
def symlink(target: str, linkpath: str):
"""创建符号链接"""
_os.symlink(target, linkpath)
@staticmethod
def readlink(path: str) -> str:
"""读取符号链接目标"""
return _os.readlink(path)
@staticmethod
def unlink(pathname: str):
"""删除文件(解除链接)"""
_os.unlink(pathname)
@staticmethod
def remove(path: str):
"""删除文件"""
_os.remove(path)
@staticmethod
def rename(oldpath: str, newpath: str):
"""重命名文件"""
_os.rename(oldpath, newpath)
@staticmethod
def mkdir(path: str, mode: int = 0o777):
"""创建目录"""
_os.mkdir(path, mode)
@staticmethod
def rmdir(path: str):
"""删除空目录"""
_os.rmdir(path)
@staticmethod
def listdir(path: str) -> List[str]:
"""列出目录内容"""
return _os.listdir(path)
@staticmethod
def truncate(path: str, length: int):
"""截断文件到指定长度"""
with open(path, 'ab') as f:
f.truncate(length)
@staticmethod
def access(path: str, mode: int) -> bool:
"""检查文件访问权限"""
return _os.access(path, mode)
# ── 文件描述符操作 ──
@staticmethod
def dup(oldfd: int) -> int:
"""复制文件描述符"""
return _os.dup(oldfd)
@staticmethod
def dup2(oldfd: int, newfd: int):
"""复制文件描述符到指定值"""
_os.dup2(oldfd, newfd)
@staticmethod
def close(fd: int):
"""关闭文件描述符"""
_os.close(fd)
@staticmethod
def read(fd: int, count: int) -> bytes:
"""从文件描述符读取"""
return _os.read(fd, count)
@staticmethod
def write(fd: int, data: bytes) -> int:
"""写入文件描述符"""
return _os.write(fd, data)
@staticmethod
def lseek(fd: int, offset: int, whence: int) -> int:
"""移动文件读写位置"""
return _os.lseek(fd, offset, whence)
@staticmethod
def fsync(fd: int):
"""同步文件描述符"""
_os.fsync(fd)
@staticmethod
def fstat(fd: int) -> StructStat:
"""获取文件描述符对应文件状态"""
st = _os.fstat(fd)
return StructStat.from_os_stat(st)
@staticmethod
def isatty(fd: int) -> bool:
"""判断文件描述符是否为终端"""
return _os.isatty(fd)
# ── 进程相关 ──
@staticmethod
def getpid() -> int:
"""获取当前进程 ID"""
return _os.getpid()
@staticmethod
def getppid() -> int:
"""获取父进程 ID"""
return _os.getppid()
@staticmethod
def getuid() -> int:
"""获取当前用户 ID"""
return _os.getuid()
@staticmethod
def getgid() -> int:
"""获取当前组 ID"""
return _os.getgid()
@staticmethod
def geteuid() -> int:
"""获取有效用户 ID"""
return _os.geteuid()
@staticmethod
def getegid() -> int:
"""获取有效组 ID"""
return _os.getegid()
@staticmethod
def setuid(uid: int):
"""设置用户 ID"""
_os.setuid(uid)
@staticmethod
def setgid(gid: int):
"""设置组 ID"""
_os.setgid(gid)
@staticmethod
def setsid() -> int:
"""创建新会话"""
return _os.setsid()
@staticmethod
def umask(mask: int) -> int:
"""设置文件创建掩码"""
return _os.umask(mask)
@staticmethod
def kill(pid: int, sig: int):
"""发送信号给进程"""
_os.kill(pid, sig)
@staticmethod
def getenv(name: str, default: Optional[str] = None) -> Optional[str]:
"""获取环境变量"""
return _os.environ.get(name, default)
@staticmethod
def setenv(name: str, value: str, overwrite: bool = True):
"""设置环境变量"""
if overwrite or name not in _os.environ:
_os.environ[name] = value
@staticmethod
def unsetenv(name: str):
"""删除环境变量"""
_os.environ.pop(name, None)
@staticmethod
def environ() -> Dict[str, str]:
"""获取环境变量字典"""
return dict(_os.environ)
# ── 系统信息 ──
@staticmethod
def uname() -> StructUtsname:
"""获取系统信息"""
return StructUtsname.from_platform()
@staticmethod
def sysconf(name: str) -> int:
"""获取系统配置值"""
conf_names = {
"SC_CLK_TCK": 100,
"SC_PAGE_SIZE": 4096,
"SC_NPROCESSORS_CONF": _os.cpu_count() or 1,
"SC_NPROCESSORS_ONLN": _os.cpu_count() or 1,
}
return conf_names.get(name, -1)
@staticmethod
def getpagesize() -> int:
"""获取内存页大小"""
return 4096
@staticmethod
def getloadavg() -> List[float]:
"""获取系统负载平均值"""
try:
return list(_os.getloadavg())
except AttributeError:
return [0.0, 0.0, 0.0]
# ── 管道与 I/O ──
@staticmethod
def pipe() -> Tuple[int, int]:
"""创建管道"""
return _os.pipe()
@staticmethod
def mkfifo(path: str, mode: int = 0o666):
"""创建命名管道 (FIFO)"""
_os.mkfifo(path, mode)
@staticmethod
def tmpfile() -> str:
"""创建临时文件路径"""
import tempfile
return tempfile.mktemp()
# ── 时间 ──
@staticmethod
def time() -> int:
"""获取当前时间戳 (秒)"""
import time
return int(time.time())
# ── 其他 ──
@staticmethod
def strerror(errno: int) -> str:
"""将错误码转为错误描述"""
return _os.strerror(errno)
def toString(self) -> str:
return f"Os(sysname={platform.system()}, release={platform.release()})"
def hashCode(self) -> int:
return hash("android.system.Os") & 0x7FFFFFFF
def equals(self, other) -> bool:
return isinstance(other, Os)
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"Os",
"StructStat",
"StructUtsname",
"OsConstants",
]
+737
View File
@@ -0,0 +1,737 @@
"""
Android Telecom API — TelecomManager, Call, CallAudioState, DisconnectCause, PhoneAccount, Connection
方法签名完全对齐 Android SDK
"""
import sys
import time
from typing import Any, Dict, List, Optional, Set, Tuple, Callable
from .._core import Stub, AndroidOnlyError
from ..java.lang import RuntimeException
IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {})
__all__ = [
"TelecomManager",
"Call",
"CallAudioState",
"DisconnectCause",
"PhoneAccount",
"Connection",
]
# ═══════════════════════════════════════════════════════════════
# TelecomManager
# ═══════════════════════════════════════════════════════════════
class TelecomManager(Stub):
"""TelecomManager — 电信管理器 (桌面存根)"""
ACTION_CHANGE_DEFAULT_DIALER = "android.telecom.action.CHANGE_DEFAULT_DIALER"
ACTION_SHOW_CALL_SETTINGS = "android.telecom.action.SHOW_CALL_SETTINGS"
ACTION_SETTINGS = "android.telecom.action.SETTINGS"
ACTION_SHOW_MISSED_CALLS_NOTIFICATION = "android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION"
ACTION_CALL_FORWARDING = "android.telecom.action.CALL_FORWARDING"
ACTION_DEFAULT_CALL_SCREENING_APP = "android.telecom.action.DEFAULT_CALL_SCREENING_APP"
ACTION_DEFAULT_DIALER_CHANGED = "android.telecom.action.DEFAULT_DIALER_CHANGED"
ACTION_NEW_UNKNOWN_CALL = "android.telecom.action.NEW_UNKNOWN_CALL"
ACTION_TTY_PREFERRED = "android.telecom.action.TTY_PREFERRED"
ACTION_TTY_ENABLED = "android.telecom.action.TTY_ENABLED"
ACTION_TTY_DISABLED = "android.telecom.action.TTY_DISABLED"
ACTION_CONNECTION_SERVICE_CONFIGURE = "android.telecom.action.CONNECTION_SERVICE_CONFIGURE"
ACTION_CALL_DIAGNOSTIC_STATE_CHANGED = "android.telecom.action.CALL_DIAGNOSTIC_STATE_CHANGED"
DURATION_SHORT = "short"
DURATION_LONG = "long"
EXTRA_CALL_BACK_NUMBER = "android.telecom.extra.CALL_BACK_NUMBER"
EXTRA_CALL_CREATED_TIME_MILLIS = "android.telecom.extra.CALL_CREATED_TIME_MILLIS"
EXTRA_CALL_TECHNOLOGY_TYPE = "android.telecom.extra.CALL_TECHNOLOGY_TYPE"
EXTRA_CALL_NETWORK_TYPE = "android.telecom.extra.CALL_NETWORK_TYPE"
EXTRA_CALL_SUBSTATE = "android.telecom.extra.CALL_SUBSTATE"
EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME = "android.telecom.extra.CHANGE_DEFAULT_DIALER_PACKAGE_NAME"
EXTRA_CONNECTION_SERVICE = "android.telecom.extra.CONNECTION_SERVICE"
EXTRA_CALL_DISCONNECT_CAUSE = "android.telecom.extra.CALL_DISCONNECT_CAUSE"
EXTRA_DISCONNECT_CAUSE_DESCRIPTION = "android.telecom.extra.DISCONNECT_CAUSE_DESCRIPTION"
EXTRA_PHONE_ACCOUNT_HANDLE = "android.telecom.extra.PHONE_ACCOUNT_HANDLE"
EXTRA_PRIORITY = "android.telecom.extra.PRIORITY"
EXTRA_START_CALL_WITH_VIDEO_STATE = "android.telecom.extra.START_CALL_WITH_VIDEO_STATE"
EXTRA_HANDOVER_FROM = "android.telecom.extra.HANDOVER_FROM"
EXTRA_HANDOVER_TO = "android.telecom.extra.HANDOVER_TO"
EXTRA_IS_HANDOVER = "android.telecom.extra.IS_HANDOVER"
TTY_MODE_OFF = 0
TTY_MODE_FULL = 1
TTY_MODE_HCO = 2
TTY_MODE_VCO = 3
PRESENTATION_ALLOWED = 1
PRESENTATION_RESTRICTED = 2
PRESENTATION_UNKNOWN = 3
PRESENTATION_PAYPHONE = 4
def __init__(self, context: Any = None):
super().__init__()
self._context = context
self._default_dialer = "com.android.dialer"
self._phone_accounts: Dict[str, Any] = {}
def getDefaultDialerPackage(self) -> str:
"""获取默认拨号器包名"""
return self._default_dialer
def getCurrentTtyMode(self) -> int:
"""获取当前 TTY 模式"""
return self.TTY_MODE_OFF
def getCallCapablePhoneAccounts(self) -> List[Any]:
"""获取可通话的电话账户"""
return list(self._phone_accounts.values())
def getPhoneAccount(self, accountHandle: Any) -> Optional[Any]:
"""获取指定电话账户"""
return self._phone_accounts.get(str(accountHandle))
def getPhoneAccountList(self) -> List[Any]:
"""获取电话账户列表"""
return list(self._phone_accounts.values())
def getSimConfig(self, accountHandle: Any) -> Optional[Any]:
"""获取 SIM 配置"""
return None
def registerPhoneAccount(self, account: "PhoneAccount"):
"""注册电话账户"""
self._phone_accounts[str(account.getAccountHandle())] = account
def unregisterPhoneAccount(self, accountHandle: Any):
"""注销电话账户"""
self._phone_accounts.pop(str(accountHandle), None)
def clearPhoneAccounts(self):
"""清除所有电话账户"""
self._phone_accounts.clear()
def clearAccounts(self):
"""清除所有账户"""
self._phone_accounts.clear()
def showInCallScreen(self, showDialpad: bool):
"""显示通话界面"""
pass
def acceptRingingCall(self):
"""接听响铃呼叫"""
pass
def silenceRinger(self):
"""静音铃声"""
pass
def isInCall(self) -> bool:
"""检查是否正在通话"""
return False
def isInManagedCall(self) -> bool:
"""检查是否在托管通话中"""
return False
def addNewIncomingCall(self, phoneAccount: "PhoneAccount", extras: Any = None):
"""添加新的来电"""
pass
def addNewUnknownCall(self, phoneAccount: "PhoneAccount", extras: Any = None):
"""添加新的未知呼叫"""
pass
def handleCreateConferenceComplete(
self, callId: str, response: Any
):
"""处理创建会议完成"""
pass
def transferCall(self, callId: str, targetPhoneAccount: Any):
"""转移呼叫"""
pass
def setUserSelectedOutgoingPhoneAccount(
self, accountHandle: Any
):
"""设置用户选择的呼出电话账户"""
pass
def toString(self) -> str:
return f"TelecomManager@{hex(id(self))}"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# Call
# ═══════════════════════════════════════════════════════════════
class Call(Stub):
"""Call — 通话 (桌面存根)"""
STATE_ACTIVE = 1
STATE_HOLDING = 2
STATE_DIALING = 3
STATE_RINGING = 4
STATE_CONNECTING = 5
STATE_DISCONNECTED = 6
STATE_DISCONNECTING = 7
STATE_SELECT_PHONE_ACCOUNT = 8
STATE_NEW = 9
STATE_PULLING_CALL = 10
def __init__(self):
super().__init__()
self._state = self.STATE_NEW
self._details: Optional["Call.Details"] = None
self._conferenceable_calls: List["Call"] = []
self._callbacks: List[Callable] = []
self._listeners: List[Callable] = []
def getState(self) -> int:
return self._state
def getDetails(self) -> Optional["Call.Details"]:
return self._details
def getConferenceableCalls(self) -> List["Call"]:
return list(self._conferenceable_calls)
def registerCallback(self, callback: Callable):
if callback not in self._callbacks:
self._callbacks.append(callback)
def unregisterCallback(self, callback: Callable):
if callback in self._callbacks:
self._callbacks.remove(callback)
def addListener(self, listener: Callable):
if listener not in self._listeners:
self._listeners.append(listener)
def removeListener(self, listener: Callable):
if listener in self._listeners:
self._listeners.remove(listener)
def answer(self, videoState: int = 0):
"""接听通话"""
self._state = self.STATE_ACTIVE
def reject(self, rejectWithMessage: bool = False, textMessage: str = ""):
"""拒绝通话"""
self._state = self.STATE_DISCONNECTED
def disconnect(self):
"""挂断通话"""
self._state = self.STATE_DISCONNECTED
def hold(self):
"""保持通话"""
self._state = self.STATE_HOLDING
def unhold(self):
"""取消保持"""
self._state = self.STATE_ACTIVE
def playDtmfTone(self, digit: str):
"""播放 DTMF 音"""
pass
def stopDtmfTone(self):
"""停止 DTMF 音"""
pass
def postDialWait(self, remaining: str):
"""后拨号等待"""
pass
def mergeConference(self):
"""合并会议"""
pass
def splitFromConference(self):
"""从会议分离"""
pass
def sendCallEvent(self, event: str, extras: Any = None):
"""发送通话事件"""
pass
def putExtra(self, key: str, value: Any):
"""设置额外信息"""
pass
# ═══════════════════════════════════════════════════════════
# Details
# ═══════════════════════════════════════════════════════════
class Details(Stub):
"""Call.Details — 通话详细信息"""
DIRECTION_INCOMING = 1
DIRECTION_OUTGOING = 2
CAPABILITY_HOLD = 1
CAPABILITY_SUPPORT_HOLD = 2
CAPABILITY_MERGE_CONFERENCE = 4
CAPABILITY_SWAP_CONFERENCE = 8
CAPABILITY_RESPOND_VIA_TEXT = 16
CAPABILITY_MUTE = 32
CAPABILITY_MANAGE_CONFERENCE = 64
CAPABILITY_SUPPORTS_VT_LOCAL = 128
CAPABILITY_SUPPORTS_VT_REMOTE = 256
CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL = 512
CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL = 1024
CAPABILITY_SUPPORTS_DOWNGRADE_VT_TO_AUDIO = 2048
CAPABILITY_SUPPORTS_VIDEO_CALLING = 4096
CAPABILITY_SUPPORTS_VT_LOCAL_RX = 8192
CAPABILITY_SUPPORTS_VT_LOCAL_TX = 16384
CAPABILITY_SUPPORTS_VT_REMOTE_RX = 32768
CAPABILITY_SUPPORTS_VT_REMOTE_TX = 65536
CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 131072
CAPABILITY_CAN_PAUSE_VIDEO = 262144
CAPABILITY_SELF_MANAGED = 524288
CAPABILITY_SUPPORTS_VOIP = 1048576
CAPABILITY_ADD_PARTICIPANT = 2097152
CAPABILITY_CAN_UPGRADE_TO_VIDEO = 4194304
PROPERTY_HIGH_DEF_AUDIO = 1
PROPERTY_WIFI = 2
PROPERTY_EMERGENCY_CALLBACK = 4
PROPERTY_IS_EXTERNAL_CALL = 8
PROPERTY_HAS_CDMA_VOICE_PRIVACY = 16
PROPERTY_SELF_MANAGED = 32
PROPERTY_ASSISTED_DIALING = 64
PROPERTY_NETWORK_IDENTIFIED = 128
PROPERTY_CALL_AUTO_FORWARDED = 256
PROPERTY_CALL_TYPE_UNKNOWN = 512
PROPERTY_IS_VOIP_AUDIO_MODE = 1024
PROPERTY_IS_DOWNGRADED_CALL = 2048
PROPERTY_ENTITLEMENT_CHANNEL = 4096
PROPERTY_PULLED_CALL = 8192
PROPERTY_CROSS_SIM = 16384
PROPERTY_VIDEO_CALL = 32768
PROPERTY_RTT = 65536
def __init__(self):
super().__init__()
self._handle: Any = None
self._gateway_info: Any = None
self._caller_display_name: str = ""
self._call_direction = self.DIRECTION_INCOMING
self._account_handle: Any = None
self._call_capabilities = 0
self._call_properties = 0
self._caller_info: Any = None
self._creation_time_millis = 0
self._connect_time_millis = 0
self._disconnect_cause: Any = None
self._video_state = 0
self._intent_extras: Any = None
self._extras: Any = None
def getHandle(self) -> Optional[Any]:
return self._handle
def getGatewayInfo(self) -> Optional[Any]:
return self._gateway_info
def getCallerDisplayName(self) -> str:
return self._caller_display_name
def getCallDirection(self) -> int:
return self._call_direction
def getAccountHandle(self) -> Optional[Any]:
return self._account_handle
def getCallCapabilities(self) -> int:
return self._call_capabilities
def getCallProperties(self) -> int:
return self._call_properties
def getCallerInfo(self) -> Optional[Any]:
return self._caller_info
def getCreationTimeMillis(self) -> int:
return self._creation_time_millis
def getConnectTimeMillis(self) -> int:
return self._connect_time_millis
def getDisconnectCause(self) -> Optional["DisconnectCause"]:
return self._disconnect_cause
def getVideoState(self) -> int:
return self._video_state
def getIntentExtras(self) -> Optional[Any]:
return self._intent_extras
def getExtras(self) -> Optional[Any]:
return self._extras
def toString(self) -> str:
return f"Call.Details(handle={self._handle})"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
def toString(self) -> str:
return f"Call(state={self._state})"
def equals(self, other) -> bool:
if not isinstance(other, Call):
return False
return self._state == other._state and self._details == other._details
def hashCode(self) -> int:
return self._state & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# CallAudioState
# ═══════════════════════════════════════════════════════════════
class CallAudioState(Stub):
"""CallAudioState — 通话音频状态"""
ROUTE_EARPIECE = 1
ROUTE_SPEAKER = 2
ROUTE_BLUETOOTH = 4
ROUTE_WIRED_HEADSET = 8
ROUTE_WIRED_OR_EARPIECE = 9
def __init__(self):
super().__init__()
self._route = self.ROUTE_EARPIECE
self._supported_route_mask = (
self.ROUTE_EARPIECE | self.ROUTE_SPEAKER | self.ROUTE_WIRED_HEADSET
)
def getRoute(self) -> int:
return self._route
def getSupportedRouteMask(self) -> int:
return self._supported_route_mask
def toString(self) -> str:
return f"CallAudioState(route={self._route})"
def equals(self, other) -> bool:
if not isinstance(other, CallAudioState):
return False
return self._route == other._route
def hashCode(self) -> int:
return self._route & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# DisconnectCause
# ═══════════════════════════════════════════════════════════════
class DisconnectCause(Stub):
"""DisconnectCause — 断连原因"""
LOCAL = 1
REMOTE = 2
BUSY = 3
CANCELED = 4
NO_ANSWER = 5
MISSED = 6
ERROR = 7
REJECTED = 8
NUMBER_UNREACHABLE = 9
RESTRICTED = 10
OTHER = 11
ANSWERED_ELSEWHERE = 12
CALL_PULLED = 13
def __init__(self, causeCode: int = OTHER):
super().__init__()
self._cause_code = causeCode
self._reason: str = ""
self._description: str = ""
def getCode(self) -> int:
return self._cause_code
def getReason(self) -> str:
return self._reason
def getDescription(self) -> str:
return self._description
def toString(self) -> str:
return f"DisconnectCause(code={self._cause_code})"
def equals(self, other) -> bool:
if not isinstance(other, DisconnectCause):
return False
return self._cause_code == other._cause_code
def hashCode(self) -> int:
return self._cause_code & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# PhoneAccount
# ═══════════════════════════════════════════════════════════════
class PhoneAccount(Stub):
"""PhoneAccount — 电话账户"""
CAP_HOLD = 1
CAP_ADD_CALL = 2
CAP_MULTI_ENDPOINT = 4
CAP_CONFERENCE = 8
CAP_VIDEO_CALLING = 16
CAP_CALL_COMPOSER = 32
CAP_CONNECTION_MANAGER = 64
EXTRA_LOG_SELF_MANAGED_CALLS = "android.telecom.extra.LOG_SELF_MANAGED_CALLS"
EXTRA_ADD_SELF_MANAGED_CALLS = "android.telecom.extra.ADD_SELF_MANAGED_CALLS"
SCREENING_ALLOW = 0
SCREENING_STOP = 1
SCREENING_SILENCE = 2
SCREENING_DISALLOW = 3
SCHEME_SIP = "sip"
SCHEME_TEL = "tel"
SCHEME_VOICEMAIL = "voicemail"
class Builder(Stub):
"""PhoneAccount.Builder — 电话账户构建器"""
def __init__(self, accountHandle: Any, label: str):
super().__init__()
self._account = PhoneAccount()
self._account._account_handle = accountHandle
self._account._label = label
def setCapabilities(self, capabilities: int) -> "PhoneAccount.Builder":
self._account._capabilities = capabilities
return self
def setAddress(self, address: Any) -> "PhoneAccount.Builder":
self._account._address = address
return self
def setSubscriptionAddress(self, address: Any) -> "PhoneAccount.Builder":
self._account._subscription_address = address
return self
def setSupportedUriSchemes(
self, uriSchemes: List[str]
) -> "PhoneAccount.Builder":
self._account._supported_uri_schemes = list(uriSchemes)
return self
def setIcon(self, icon: Any) -> "PhoneAccount.Builder":
self._account._icon = icon
return self
def setHighlightColor(self, color: int) -> "PhoneAccount.Builder":
self._account._highlight_color = color
return self
def setShortDescription(self, description: str) -> "PhoneAccount.Builder":
self._account._short_description = description
return self
def addSupportedUriScheme(
self, uriScheme: str
) -> "PhoneAccount.Builder":
if uriScheme not in self._account._supported_uri_schemes:
self._account._supported_uri_schemes.append(uriScheme)
return self
def setExtras(self, extras: Any) -> "PhoneAccount.Builder":
self._account._extras = extras
return self
def build(self) -> "PhoneAccount":
return self._account
def toString(self) -> str:
return f"PhoneAccount.Builder(label={self._account._label})"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
def __init__(self):
super().__init__()
self._account_handle: Any = None
self._label: str = ""
self._capabilities: int = 0
self._address: Any = None
self._subscription_address: Any = None
self._supported_uri_schemes: List[str] = [self.SCHEME_TEL]
self._icon: Any = None
self._highlight_color: int = 0
self._short_description: str = ""
self._extras: Any = None
def getAccountHandle(self) -> Optional[Any]:
return self._account_handle
def getLabel(self) -> str:
return self._label
def getCapabilities(self) -> int:
return self._capabilities
def getAddress(self) -> Optional[Any]:
return self._address
def getSubscriptionAddress(self) -> Optional[Any]:
return self._subscription_address
def getSupportedUriSchemes(self) -> List[str]:
return list(self._supported_uri_schemes)
def getIcon(self) -> Optional[Any]:
return self._icon
def getHighlightColor(self) -> int:
return self._highlight_color
def getShortDescription(self) -> str:
return self._short_description
def getExtras(self) -> Optional[Any]:
return self._extras
def toString(self) -> str:
return f"PhoneAccount(label={self._label})"
def equals(self, other) -> bool:
if not isinstance(other, PhoneAccount):
return False
return self._account_handle == other._account_handle
def hashCode(self) -> int:
return hash(str(self._account_handle)) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# Connection
# ═══════════════════════════════════════════════════════════════
class Connection(Stub):
"""Connection — 通话连接 (桌面存根)"""
STATE_INITIALIZING = 0
STATE_ACTIVE = 1
STATE_DIALING = 2
STATE_RINGING = 3
STATE_HOLDING = 4
STATE_DISCONNECTED = 5
STATE_PULLING_CALL = 6
CAPABILITY_HOLD = 1
CAPABILITY_SUPPORT_HOLD = 2
CAPABILITY_MERGE_CONFERENCE = 4
CAPABILITY_SWAP_CONFERENCE = 8
CAPABILITY_RESPOND_VIA_TEXT = 16
CAPABILITY_MUTE = 32
CAPABILITY_MANAGE_CONFERENCE = 64
CAPABILITY_SUPPORTS_VT_LOCAL = 128
CAPABILITY_SUPPORTS_VT_REMOTE = 256
CAPABILITY_SUPPORTS_VIDEO_CALLING = 4096
CAPABILITY_SELF_MANAGED = 524288
CAPABILITY_SUPPORTS_VOIP = 1048576
CAPABILITY_ADD_PARTICIPANT = 2097152
PROPERTY_SELF_MANAGED = 32
PROPERTY_IS_VOIP_AUDIO_MODE = 1024
PROPERTY_HIGH_DEF_AUDIO = 1
PROPERTY_WIFI = 2
def __init__(self):
super().__init__()
self._state = self.STATE_INITIALIZING
self._video_state = 0
self._connection_capabilities = 0
self._connection_properties = 0
self._audio_mode_is_voip = False
self._conferenceable_connections: List["Connection"] = []
self._extras: Dict[str, Any] = {}
def setVideoState(self, videoState: int):
self._video_state = videoState
def setActive(self):
self._state = self.STATE_ACTIVE
def setRinging(self):
self._state = self.STATE_RINGING
def setDialing(self):
self._state = self.STATE_DIALING
def setHolding(self):
self._state = self.STATE_HOLDING
def setDisconnected(self, disconnectCause: "DisconnectCause"):
self._state = self.STATE_DISCONNECTED
def setPostDialWait(self, remaining: str):
pass
def setConferenceableConnections(
self, conferenceable: List["Connection"]
):
self._conferenceable_connections = list(conferenceable)
def setAudioModeIsVoip(self, isVoip: bool):
self._audio_mode_is_voip = isVoip
def setConnectionCapabilities(self, capabilities: int):
self._connection_capabilities = capabilities
def setConnectionProperties(self, properties: int):
self._connection_properties = properties
def putExtra(self, key: str, value: Any):
self._extras[key] = value
def onShowIncomingCallUi(self):
pass
def onCallAudioStateChanged(self, audioState: "CallAudioState"):
pass
def destroy(self):
self._state = self.STATE_DISCONNECTED
def onCallFilteringCompleted(self, completion: Any):
pass
def onSilence(self):
pass
def toString(self) -> str:
return f"Connection(state={self._state})"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
+485
View File
@@ -0,0 +1,485 @@
"""
Android Test 组件 — InstrumentationTestRunner, InstrumentationTestCase,
ActivityInstrumentationTestCase2, ActivityUnitTestCase, ServiceTestCase,
ProviderTestCase2, ApplicationTestCase, Instrumentation 等
方法签名完全对齐 Android SDK (android.test 包)
"""
import os
import sys
import time
import threading
from typing import Any, Callable, Dict, List, Optional, Tuple, Type
from .._core import Stub, AndroidOnlyError
# ═══════════════════════════════════════════════════════════════
# Instrumentation
# ═══════════════════════════════════════════════════════════════
class Instrumentation(Stub):
"""Instrumentation — 测试仪器 (桌面版)"""
# 报告标志
REPORT_KEY_IDENTIFIER = "id"
REPORT_KEY_STREAM_RESULTS = "stream"
def __init__(self):
super().__init__()
self._context: Optional[Any] = None
self._target_context: Optional[Any] = None
self._started = False
self._finished = False
self._automatic_performance_snapshots = False
self._instr_result: Dict[str, Any] = {}
def onCreate(self, arguments: Any):
"""创建仪器"""
self._started = True
def onStart(self):
"""启动仪器"""
pass
def onDestroy(self):
"""销毁仪器"""
self._finished = True
def getContext(self) -> Optional[Any]:
"""获取上下文"""
return self._context
def getTargetContext(self) -> Optional[Any]:
"""获取目标上下文"""
return self._target_context
def setContext(self, context: Any):
self._context = context
def setTargetContext(self, target_context: Any):
self._target_context = target_context
def startActivitySync(self, intent: Any) -> Any:
"""同步启动 Activity"""
return None
def sendKeySync(self, key_event: Any):
"""发送按键事件"""
pass
def sendCharacterSync(self, character: int):
"""发送字符事件"""
pass
def sendStringSync(self, text: str):
"""发送字符串"""
pass
def sendPointerSync(self, motion_event: Any):
"""发送指针事件"""
pass
def sendTrackballEventSync(self, motion_event: Any):
"""发送轨迹球事件"""
pass
def runOnMainSync(self, runner: Callable):
"""在主线程上同步运行"""
runner()
def waitForIdle(self):
"""等待空闲"""
pass
def waitForIdleSync(self):
"""同步等待空闲"""
pass
def callActivityOnCreate(self, activity: Any, icicle: Any):
"""调用 Activity.onCreate"""
if hasattr(activity, 'onCreate'):
activity.onCreate(icicle)
def callActivityOnStart(self, activity: Any):
"""调用 Activity.onStart"""
if hasattr(activity, 'onStart'):
activity.onStart()
def callActivityOnResume(self, activity: Any):
"""调用 Activity.onResume"""
if hasattr(activity, 'onResume'):
activity.onResume()
def callActivityOnPause(self, activity: Any):
"""调用 Activity.onPause"""
if hasattr(activity, 'onPause'):
activity.onPause()
def callActivityOnStop(self, activity: Any):
"""调用 Activity.onStop"""
if hasattr(activity, 'onStop'):
activity.onStop()
def callActivityOnDestroy(self, activity: Any):
"""调用 Activity.onDestroy"""
if hasattr(activity, 'onDestroy'):
activity.onDestroy()
def callActivityOnRestart(self, activity: Any):
"""调用 Activity.onRestart"""
if hasattr(activity, 'onRestart'):
activity.onRestart()
def isProfiling(self) -> bool:
return False
def startProfiling(self):
pass
def stopProfiling(self):
pass
def setInTouchMode(self, in_touch: bool):
pass
def finish(self, result_code: int, results: Any = None):
"""完成测试"""
self._finished = True
if results:
self._instr_result.update(results)
def getComponentName(self) -> Any:
return None
def getUiAutomation(self) -> Any:
return None
def toString(self) -> str:
return (f"Instrumentation(started={self._started}, "
f"finished={self._finished})")
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
def equals(self, other) -> bool:
return self is other
# ═══════════════════════════════════════════════════════════════
# InstrumentationTestRunner
# ═══════════════════════════════════════════════════════════════
class InstrumentationTestRunner(Stub):
"""InstrumentationTestRunner — 仪器化测试运行器"""
REPORT_KEY_NAME = "name"
REPORT_KEY_CLASS = "class"
REPORT_KEY_TEST = "test"
REPORT_KEY_NUM_TOTAL = "numtests"
REPORT_KEY_NUM_CURRENT = "current"
REPORT_KEY_NUM_ITERATIONS = "numiterations"
def __init__(self):
super().__init__()
self._instrumentation = Instrumentation()
self._test_results: List[Dict[str, Any]] = []
def onCreate(self, arguments: Any):
"""创建测试运行器"""
self._instrumentation.onCreate(arguments)
def onStart(self):
"""开始测试"""
self._instrumentation.onStart()
def onDestroy(self):
"""销毁"""
self._instrumentation.onDestroy()
def getInstrumentation(self) -> Instrumentation:
return self._instrumentation
def runTest(self, test_case: Any):
"""运行测试用例"""
try:
if hasattr(test_case, 'setUp'):
test_case.setUp()
if hasattr(test_case, 'runTest'):
test_case.runTest()
if hasattr(test_case, 'tearDown'):
test_case.tearDown()
self._test_results.append({"success": True, "test": str(test_case)})
except Exception as e:
self._test_results.append({
"success": False, "test": str(test_case), "error": str(e),
})
def getTestResults(self) -> List[Dict[str, Any]]:
return list(self._test_results)
def toString(self) -> str:
return (f"InstrumentationTestRunner("
f"results={len(self._test_results)})")
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
def equals(self, other) -> bool:
return self is other
# ═══════════════════════════════════════════════════════════════
# InstrumentationTestCase
# ═══════════════════════════════════════════════════════════════
class InstrumentationTestCase(Stub):
"""InstrumentationTestCase — 仪器化测试用例基类"""
def __init__(self):
super().__init__()
self._instrumentation: Optional[Instrumentation] = None
self._test_name: str = ""
def injectInstrumentation(self, instrumentation: Instrumentation):
"""注入仪器"""
self._instrumentation = instrumentation
def getInstrumentation(self) -> Optional[Instrumentation]:
return self._instrumentation
def setUp(self):
"""测试前准备"""
pass
def tearDown(self):
"""测试后清理"""
pass
def runTest(self):
"""执行测试"""
pass
def sendKeys(self, *keys: str):
"""发送按键"""
pass
def touchDown(self, view: Any, x: float, y: float):
"""触摸按下"""
pass
def touchUp(self, view: Any, x: float, y: float):
"""触摸抬起"""
pass
def touchAndHold(self, view: Any, x: float, y: float):
"""触摸并按住"""
pass
def click(self, view: Any):
"""点击"""
pass
def typeText(self, view: Any, text: str):
"""输入文本"""
pass
def assertActivityRequiresFeature(self, activity_class: Any,
feature: str) -> bool:
return True
def toString(self) -> str:
return f"InstrumentationTestCase(name={self._test_name})"
def hashCode(self) -> int:
return hash(self._test_name) & 0x7FFFFFFF
def equals(self, other) -> bool:
if not isinstance(other, InstrumentationTestCase):
return False
return self._test_name == other._test_name
# ═══════════════════════════════════════════════════════════════
# ActivityInstrumentationTestCase2
# ═══════════════════════════════════════════════════════════════
class ActivityInstrumentationTestCase2(InstrumentationTestCase):
"""ActivityInstrumentationTestCase2 — Activity 测试用例"""
def __init__(self, pkg: str, activity_class: Type):
super().__init__()
self._pkg = pkg
self._activity_class = activity_class
self._activity: Optional[Any] = None
def setActivityIntent(self, intent: Any):
"""设置 Activity Intent"""
pass
def getActivity(self) -> Optional[Any]:
"""获取 Activity 实例"""
return self._activity
def testActivityCreated(self) -> bool:
return self._activity is not None
def testActivityResumed(self) -> bool:
return self._activity is not None
def testActivityStarted(self) -> bool:
return self._activity is not None
def toString(self) -> str:
return (f"ActivityInstrumentationTestCase2("
f"pkg={self._pkg}, cls={self._activity_class})")
# ═══════════════════════════════════════════════════════════════
# ActivityUnitTestCase
# ═══════════════════════════════════════════════════════════════
class ActivityUnitTestCase(InstrumentationTestCase):
"""ActivityUnitTestCase — Activity 单元测试"""
def __init__(self, activity_class: Type):
super().__init__()
self._activity_class = activity_class
self._activity: Optional[Any] = None
def setActivityContext(self, context: Any):
"""设置 Activity 上下文"""
if self._activity and hasattr(self._activity, 'setContext'):
self._activity.setContext(context)
def setApplication(self, application: Any):
"""设置 Application"""
if self._activity and hasattr(self._activity, 'setApplication'):
self._activity.setApplication(application)
def getActivity(self) -> Optional[Any]:
return self._activity
def startActivity(self, intent: Any, bundle: Any = None,
last_non_config_instance: Any = None) -> Any:
"""启动 Activity"""
return self._activity
def toString(self) -> str:
return f"ActivityUnitTestCase(cls={self._activity_class})"
# ═══════════════════════════════════════════════════════════════
# ServiceTestCase
# ═══════════════════════════════════════════════════════════════
class ServiceTestCase(InstrumentationTestCase):
"""ServiceTestCase — Service 测试用"""
def __init__(self, service_class: Type):
super().__init__()
self._service_class = service_class
self._service: Optional[Any] = None
self._service_binder: Optional[Any] = None
self._service_created = False
def getService(self) -> Optional[Any]:
return self._service
def bindService(self, intent: Any) -> Any:
"""绑定 Service"""
return self._service_binder
def bindServiceNotNeeded(self):
"""Service 不需要绑定"""
pass
def setService(self, service: Any):
self._service = service
def testServiceCreated(self) -> bool:
return self._service_created
def testServiceBound(self) -> bool:
return self._service_binder is not None
def testServiceStarted(self) -> bool:
return self._service is not None
def testServiceStopped(self) -> bool:
return not self._service_created
def toString(self) -> str:
return f"ServiceTestCase(cls={self._service_class})"
# ═══════════════════════════════════════════════════════════════
# ProviderTestCase2
# ═══════════════════════════════════════════════════════════════
class ProviderTestCase2(InstrumentationTestCase):
"""ProviderTestCase2 — ContentProvider 测试用例"""
def __init__(self, provider_class: Type, provider_authority: str):
super().__init__()
self._provider_class = provider_class
self._provider_authority = provider_authority
self._provider: Optional[Any] = None
self._resolver: Optional[Any] = None
def getProvider(self) -> Optional[Any]:
return self._provider
def getMockContentResolver(self) -> Optional[Any]:
return self._resolver
def setProvider(self, provider: Any):
self._provider = provider
def setMockContentResolver(self, resolver: Any):
self._resolver = resolver
def toString(self) -> str:
return (f"ProviderTestCase2(cls={self._provider_class}, "
f"authority={self._provider_authority})")
# ═══════════════════════════════════════════════════════════════
# ApplicationTestCase
# ═══════════════════════════════════════════════════════════════
class ApplicationTestCase(InstrumentationTestCase):
"""ApplicationTestCase — Application 测试用例"""
def __init__(self, application_class: Type):
super().__init__()
self._application_class = application_class
self._application: Optional[Any] = None
def getApplication(self) -> Optional[Any]:
return self._application
def setApplication(self, application: Any):
self._application = application
def testApplicationCreated(self) -> bool:
return self._application is not None
def toString(self) -> str:
return f"ApplicationTestCase(cls={self._application_class})"
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"InstrumentationTestRunner",
"InstrumentationTestCase",
"ActivityInstrumentationTestCase2",
"ActivityUnitTestCase",
"ServiceTestCase",
"ProviderTestCase2",
"ApplicationTestCase",
"Instrumentation",
]
+504
View File
@@ -0,0 +1,504 @@
"""
Android Transition 组件 — Transition, TransitionManager, Scene, Fade, Slide, Explode 等
方法签名完全对齐 Android SDK (android.transition 包)
"""
import time
from typing import Any, Callable, Dict, List, Optional, Set, Tuple
from .._core import Stub, AndroidOnlyError
# ═══════════════════════════════════════════════════════════════
# Scene
# ═══════════════════════════════════════════════════════════════
class Scene(Stub):
"""Scene — 转场场景"""
def __init__(self, root: Any = None, layout: Any = None):
super().__init__()
self._root = root
self._layout = layout
self._entered = False
@staticmethod
def getSceneForLayout(root: Any, layout_id: int, context: Any) -> "Scene":
"""从布局资源创建场景"""
return Scene(root, layout_id)
def getRoot(self) -> Any:
return self._root
def enter(self):
"""进入场景"""
self._entered = True
def exit(self):
"""退出场景"""
self._entered = False
def isEntered(self) -> bool:
return self._entered
def setEnterAction(self, action: Callable):
"""设置进入动作"""
self._enter_action = action
def setExitAction(self, action: Callable):
"""设置退出动作"""
self._exit_action = action
def toString(self) -> str:
return f"Scene(root={self._root}, entered={self._entered})"
def hashCode(self) -> int:
return hash(id(self._root)) & 0x7FFFFFFF
def equals(self, other) -> bool:
if not isinstance(other, Scene):
return False
return self._root is other._root
# ═══════════════════════════════════════════════════════════════
# Transition
# ═══════════════════════════════════════════════════════════════
class Transition(Stub):
"""Transition — 转场动画基类"""
# 匹配模式
MATCH_VIEW_NAME = 0
MATCH_ID = 1
MATCH_INSTANCE = 2
MATCH_ITEM_ID = 3
def __init__(self):
super().__init__()
self._duration: int = 300
self._start_delay: int = 0
self._interpolator: Optional[Any] = None
self._targets: Set[Any] = set()
self._target_ids: Set[int] = set()
self._target_names: Set[str] = set()
self._target_types: Set[type] = set()
self._exclude_targets: Set[Any] = set()
self._exclude_ids: Set[int] = set()
self._exclude_names: Set[str] = set()
self._exclude_types: Set[type] = set()
self._listeners: List[Any] = []
self._name: str = ""
self._epicenter_callback: Optional[Callable] = None
self._path_motion: Optional[Any] = None
self._propagation: Optional[Any] = None
self._paused: bool = False
self._started: bool = False
self._ended: bool = False
def setDuration(self, duration_ms: int) -> "Transition":
"""设置持续时间 (毫秒)"""
self._duration = max(0, duration_ms)
return self
def getDuration(self) -> int:
return self._duration
def setStartDelay(self, start_delay: int) -> "Transition":
"""设置起始延迟 (毫秒)"""
self._start_delay = max(0, start_delay)
return self
def getStartDelay(self) -> int:
return self._start_delay
def setInterpolator(self, interpolator: Any) -> "Transition":
"""设置插值器"""
self._interpolator = interpolator
return self
def getInterpolator(self) -> Any:
return self._interpolator
def addTarget(self, target: Any) -> "Transition":
"""添加动画目标"""
if isinstance(target, int):
self._target_ids.add(target)
elif isinstance(target, str):
self._target_names.add(target)
elif isinstance(target, type):
self._target_types.add(target)
else:
self._targets.add(target)
return self
def removeTarget(self, target: Any) -> "Transition":
"""移除动画目标"""
if isinstance(target, int):
self._target_ids.discard(target)
elif isinstance(target, str):
self._target_names.discard(target)
elif isinstance(target, type):
self._target_types.discard(target)
else:
self._targets.discard(target)
return self
def getTargets(self) -> List[Any]:
return list(self._targets)
def excludeTarget(self, target: Any, exclude: bool = True) -> "Transition":
"""排除目标"""
if exclude:
if isinstance(target, int):
self._exclude_ids.add(target)
elif isinstance(target, str):
self._exclude_names.add(target)
elif isinstance(target, type):
self._exclude_types.add(target)
else:
self._exclude_targets.add(target)
else:
if isinstance(target, int):
self._exclude_ids.discard(target)
elif isinstance(target, str):
self._exclude_names.discard(target)
elif isinstance(target, type):
self._exclude_types.discard(target)
else:
self._exclude_targets.discard(target)
return self
def addListener(self, listener: Any) -> "Transition":
"""添加转场监听器"""
if listener not in self._listeners:
self._listeners.append(listener)
return self
def removeListener(self, listener: Any) -> "Transition":
"""移除转场监听器"""
if listener in self._listeners:
self._listeners.remove(listener)
return self
def getListeners(self) -> List[Any]:
return list(self._listeners)
def setName(self, name: str) -> "Transition":
self._name = name
return self
def getName(self) -> str:
return self._name
def setEpicenterCallback(self, callback: Callable) -> "Transition":
self._epicenter_callback = callback
return self
def getEpicenterCallback(self) -> Optional[Callable]:
return self._epicenter_callback
def setPathMotion(self, path_motion: Any) -> "Transition":
self._path_motion = path_motion
return self
def getPathMotion(self) -> Any:
return self._path_motion
def setPropagation(self, propagation: Any) -> "Transition":
self._propagation = propagation
return self
def getPropagation(self) -> Any:
return self._propagation
def isPaused(self) -> bool:
return self._paused
def pause(self):
"""暂停转场"""
self._paused = True
def resume(self):
"""恢复转场"""
self._paused = False
def cancel(self):
"""取消转场"""
self._started = False
self._ended = True
def end(self):
"""结束转场"""
self._ended = True
def toString(self) -> str:
return (f"{self.__class__.__name__}(duration={self._duration}, "
f"startDelay={self._start_delay}, targets={len(self._targets)})")
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
def equals(self, other) -> bool:
return self is other
def captureStartValues(self, view: Any):
"""捕获视图起始值"""
pass
def captureEndValues(self, view: Any):
"""捕获视图结束值"""
pass
def createAnimator(self, view: Any, start_values: Any,
end_values: Any) -> Optional[Any]:
"""创建动画器"""
return None
# ═══════════════════════════════════════════════════════════════
# TransitionSet
# ═══════════════════════════════════════════════════════════════
class TransitionSet(Transition):
"""TransitionSet — 转场合集"""
ORDERING_TOGETHER = 0
ORDERING_SEQUENTIAL = 1
def __init__(self):
super().__init__()
self._ordering = TransitionSet.ORDERING_TOGETHER
self._transitions: List[Transition] = []
def setOrdering(self, ordering: int) -> "TransitionSet":
"""设置播放顺序"""
self._ordering = ordering
return self
def getOrdering(self) -> int:
return self._ordering
def addTransition(self, transition: Transition) -> "TransitionSet":
"""添加子转场"""
self._transitions.append(transition)
return self
def removeTransition(self, transition: Transition) -> "TransitionSet":
"""移除子转场"""
if transition in self._transitions:
self._transitions.remove(transition)
return self
def getTransitionAt(self, index: int) -> Optional[Transition]:
"""获取指定索引的子转场"""
if 0 <= index < len(self._transitions):
return self._transitions[index]
return None
def getTransitionCount(self) -> int:
return len(self._transitions)
def toString(self) -> str:
return (f"TransitionSet(ordering={self._ordering}, "
f"count={len(self._transitions)})")
# ═══════════════════════════════════════════════════════════════
# AutoTransition
# ═══════════════════════════════════════════════════════════════
class AutoTransition(TransitionSet):
"""AutoTransition — 自动过渡转场"""
def __init__(self):
super().__init__()
self.setOrdering(TransitionSet.ORDERING_TOGETHER)
self._duration = 300
def toString(self) -> str:
return f"AutoTransition(duration={self._duration})"
# ═══════════════════════════════════════════════════════════════
# Fade
# ═══════════════════════════════════════════════════════════════
class Fade(Transition):
"""Fade — 淡入淡出转场"""
IN = 0x00000001
OUT = 0x00000002
def __init__(self, fading_mode: int = IN | OUT):
super().__init__()
self._fading_mode = fading_mode
def getFadingMode(self) -> int:
return self._fading_mode
def setFadingMode(self, fading_mode: int):
self._fading_mode = fading_mode
def toString(self) -> str:
return f"Fade(mode={self._fading_mode}, duration={self._duration})"
# ═══════════════════════════════════════════════════════════════
# ChangeBounds
# ═══════════════════════════════════════════════════════════════
class ChangeBounds(Transition):
"""ChangeBounds — 位置变化转场"""
def __init__(self):
super().__init__()
self._resize_clip: bool = False
self._reparent: bool = False
self._reparent_with_overlay: bool = True
def setResizeClip(self, resize_clip: bool):
self._resize_clip = resize_clip
def getResizeClip(self) -> bool:
return self._resize_clip
def setReparent(self, reparent: bool):
self._reparent = reparent
def getReparent(self) -> bool:
return self._reparent
def setReparentWithOverlay(self, reparent_with_overlay: bool):
self._reparent_with_overlay = reparent_with_overlay
def getReparentWithOverlay(self) -> bool:
return self._reparent_with_overlay
def toString(self) -> str:
return f"ChangeBounds(duration={self._duration}, resizeClip={self._resize_clip})"
# ═══════════════════════════════════════════════════════════════
# ChangeTransform
# ═══════════════════════════════════════════════════════════════
class ChangeTransform(Transition):
"""ChangeTransform — 变换属性转场"""
def __init__(self):
super().__init__()
self._reparent: bool = False
self._reparent_with_overlay: bool = True
def setReparent(self, reparent: bool):
self._reparent = reparent
def getReparent(self) -> bool:
return self._reparent
def setReparentWithOverlay(self, reparent_with_overlay: bool):
self._reparent_with_overlay = reparent_with_overlay
def getReparentWithOverlay(self) -> bool:
return self._reparent_with_overlay
def toString(self) -> str:
return f"ChangeTransform(duration={self._duration})"
# ═══════════════════════════════════════════════════════════════
# Slide
# ═══════════════════════════════════════════════════════════════
class Slide(Transition):
"""Slide — 滑动转场"""
TOP = 0x00000001
BOTTOM = 0x00000002
START = 0x00000003
END = 0x00000004
RIGHT = 0x00000005
LEFT = 0x00000006
def __init__(self, slide_edge: int = BOTTOM):
super().__init__()
self._slide_edge = slide_edge
def getSlideEdge(self) -> int:
return self._slide_edge
def setSlideEdge(self, slide_edge: int):
self._slide_edge = slide_edge
def toString(self) -> str:
return f"Slide(edge={self._slide_edge}, duration={self._duration})"
# ═══════════════════════════════════════════════════════════════
# Explode
# ═══════════════════════════════════════════════════════════════
class Explode(Transition):
"""Explode — 爆炸/散开转场"""
def __init__(self):
super().__init__()
def toString(self) -> str:
return f"Explode(duration={self._duration})"
# ═══════════════════════════════════════════════════════════════
# TransitionManager
# ═══════════════════════════════════════════════════════════════
class TransitionManager(Stub):
"""TransitionManager — 转场管理器"""
def __init__(self):
super().__init__()
@staticmethod
def beginDelayedTransition(scene_root: Any,
transition: Optional[Transition] = None):
"""开始延迟转场"""
pass
@staticmethod
def endTransitions(scene_root: Any):
"""结束转场"""
pass
@staticmethod
def go(scene: Scene, transition: Optional[Transition] = None):
"""执行场景转场"""
if scene:
scene.enter()
def toString(self) -> str:
return "TransitionManager(desktop-stub)"
def hashCode(self) -> int:
return hash("android.transition.TransitionManager") & 0x7FFFFFFF
def equals(self, other) -> bool:
return isinstance(other, TransitionManager)
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"Transition",
"TransitionManager",
"AutoTransition",
"Fade",
"ChangeBounds",
"ChangeTransform",
"Slide",
"Explode",
"TransitionSet",
"Scene",
]
+420
View File
@@ -0,0 +1,420 @@
"""
Android uwb 组件 — UwbManager, RangingResult, RangingParameters, UwbAddress 等
方法签名完全对齐 Android SDK (UWB API)
"""
from typing import Any, Callable, Dict, List, Optional
from .._core import Stub, AndroidOnlyError
# ═══════════════════════════════════════════════════════════════
# UwbConstants
# ═══════════════════════════════════════════════════════════════
class UwbConstants(Stub):
"""UwbConstants — UWB 常量"""
STATUS_OK = 0
STATUS_FAILURE = 1
STATUS_INVALID_PARAMETERS = 2
STATUS_SESSION_DOES_NOT_EXIST = 3
STATUS_MAX_RANGING_ROUND_RETRY = 4
STATUS_RANGING_TX_FAILED = 5
STATUS_SESSION_STATE_CHANGE_EXCEPTION = 6
STATUS_UNKNOWN_ERROR = 255
CONFIG_ID_0 = 0
CONFIG_ID_1 = 1
CONFIG_ID_2 = 2
CONFIG_ID_3 = 3
MEASUREMENT_REPORT_TYPE_DEFERRED = 0
MEASUREMENT_REPORT_TYPE_NTF = 1
RANGING_UPDATE_RATE_AUTO = 0
RANGING_UPDATE_RATE_FREQUENCY_1_HZ = 1
RANGING_UPDATE_RATE_FREQUENCY_2_HZ = 2
RANGING_UPDATE_RATE_FREQUENCY_5_HZ = 5
RANGING_UPDATE_RATE_FREQUENCY_10_HZ = 10
RANGING_UPDATE_RATE_FREQUENCY_20_HZ = 20
RANGING_UPDATE_RATE_FREQUENCY_100_HZ = 100
RANGING_UPDATE_RATE_FREQUENCY_250_HZ = 250
# ═══════════════════════════════════════════════════════════════
# UwbAddress
# ═══════════════════════════════════════════════════════════════
class UwbAddress(Stub):
"""UwbAddress — UWB 地址"""
SHORT_MAC_ADDRESS = 0
EXTENDED_MAC_ADDRESS = 1
def __init__(self, address: bytes = b"", type_: int = SHORT_MAC_ADDRESS):
super().__init__()
self._address = address
self._type = type_
def getAddress(self) -> bytes:
return self._address
def getType(self) -> int:
return self._type
def toString(self) -> str:
return f"UwbAddress(type={self._type}, addr={self._address.hex()})"
# ═══════════════════════════════════════════════════════════════
# AngleOfArrival
# ═══════════════════════════════════════════════════════════════
class AngleOfArrival(Stub):
"""AngleOfArrival — 到达角"""
STATUS_SUCCESS = 0
STATUS_FAILURE = 1
def __init__(self, azimuth: float = 0.0, elevation: float = 0.0,
azimuthConfidence: float = 0.0, elevationConfidence: float = 0.0,
status: int = STATUS_FAILURE):
super().__init__()
self._azimuth = azimuth
self._elevation = elevation
self._azimuth_confidence = azimuthConfidence
self._elevation_confidence = elevationConfidence
self._status = status
def getAzimuth(self) -> float:
return self._azimuth
def getElevation(self) -> float:
return self._elevation
def getAzimuthConfidence(self) -> float:
return self._azimuth_confidence
def getElevationConfidence(self) -> float:
return self._elevation_confidence
# ═══════════════════════════════════════════════════════════════
# DistanceMeasurement
# ═══════════════════════════════════════════════════════════════
class DistanceMeasurement(Stub):
"""DistanceMeasurement — 距离测量"""
STATUS_SUCCESS = 0
STATUS_FAILURE = 1
def __init__(self, meters: float = 0.0, confidence: float = 0.0,
standardDeviation: float = 0.0, status: int = STATUS_FAILURE):
super().__init__()
self._meters = meters
self._confidence = confidence
self._standard_deviation = standardDeviation
self._status = status
def getMeters(self) -> float:
return self._meters
def getConfidence(self) -> float:
return self._confidence
def getStandardDeviation(self) -> float:
return self._standard_deviation
# ═══════════════════════════════════════════════════════════════
# RangingResult
# ═══════════════════════════════════════════════════════════════
class RangingResult(Stub):
"""RangingResult — 测距结果"""
def __init__(self, uwbDevice: Any = None, remoteDevice: Any = None,
status: int = 0, nofRounds: int = 0, nofMeasurements: int = 0,
elapsedRealtimeNanos: int = 0,
distanceMeasurement: Optional[DistanceMeasurement] = None,
angleOfArrival: Optional[AngleOfArrival] = None,
rssi: float = -90.0):
super().__init__()
self._uwb_device = uwbDevice
self._remote_device = remoteDevice
self._status = status
self._nof_rounds = nofRounds
self._nof_measurements = nofMeasurements
self._elapsed_realtime_nanos = elapsedRealtimeNanos
self._distance_measurement = distanceMeasurement
self._angle_of_arrival = angleOfArrival
self._rssi = rssi
self._distance_reported = distanceMeasurement is not None
self._aoa_reported = angleOfArrival is not None
def getUwbDevice(self) -> Any:
return self._uwb_device
def getRemoteDevice(self) -> Any:
return self._remote_device
def getStatus(self) -> int:
return self._status
def getNofRounds(self) -> int:
return self._nof_rounds
def getNofMeasurements(self) -> int:
return self._nof_measurements
def getElapsedRealtimeNanos(self) -> int:
return self._elapsed_realtime_nanos
def getDistanceMeasurement(self) -> Optional[DistanceMeasurement]:
return self._distance_measurement
def getAngleOfArrival(self) -> Optional[AngleOfArrival]:
return self._angle_of_arrival
def getAngleOfArrivalAzimuth(self) -> float:
if self._angle_of_arrival:
return self._angle_of_arrival.getAzimuth()
return 0.0
def getAngleOfArrivalElevation(self) -> float:
if self._angle_of_arrival:
return self._angle_of_arrival.getElevation()
return 0.0
def getAngleOfArrivalAzimuthConfidence(self) -> float:
if self._angle_of_arrival:
return self._angle_of_arrival.getAzimuthConfidence()
return 0.0
def getAngleOfArrivalElevationConfidence(self) -> float:
if self._angle_of_arrival:
return self._angle_of_arrival.getElevationConfidence()
return 0.0
def getRssi(self) -> float:
return self._rssi
def isDistanceMeasurementReported(self) -> bool:
return self._distance_reported
def isAngleOfArrivalReported(self) -> bool:
return self._aoa_reported
# ═══════════════════════════════════════════════════════════════
# RangingParameters
# ═══════════════════════════════════════════════════════════════
class RangingParameters(Stub):
"""RangingParameters — 测距参数"""
class Builder(Stub):
"""RangingParameters.Builder — 测距参数构建器"""
def __init__(self):
super().__init__()
self._uwb_config_id = 0
self._session_id = 0
self._sub_session_id = 0
self._ranging_update_rate = 1
self._slot_duration = 0
self._measurement_report_type = 0
self._number_of_controlee = 1
self._destination_addresses: List[UwbAddress] = []
self._device_configs: List[Any] = []
self._vendor_specific_data: List[Any] = []
def setUwbConfigId(self, configId: int) -> "RangingParameters.Builder":
self._uwb_config_id = configId
return self
def setSessionId(self, sessionId: int) -> "RangingParameters.Builder":
self._session_id = sessionId
return self
def setSubSessionId(self, subSessionId: int) -> "RangingParameters.Builder":
self._sub_session_id = subSessionId
return self
def setRangingUpdateRate(self, rate: int) -> "RangingParameters.Builder":
self._ranging_update_rate = rate
return self
def setSlotDuration(self, duration: int) -> "RangingParameters.Builder":
self._slot_duration = duration
return self
def setMeasurementReportType(self, reportType: int) -> "RangingParameters.Builder":
self._measurement_report_type = reportType
return self
def setNumberOfControlee(self, count: int) -> "RangingParameters.Builder":
self._number_of_controlee = count
return self
def setDestinationAddresses(self, addrs: List[UwbAddress]) -> "RangingParameters.Builder":
self._destination_addresses = addrs
return self
def setDeviceConfigs(self, configs: List[Any]) -> "RangingParameters.Builder":
self._device_configs = configs
return self
def setVendorSpecificData(self, data: List[Any]) -> "RangingParameters.Builder":
self._vendor_specific_data = data
return self
def build(self) -> "RangingParameters":
rp = RangingParameters()
rp._uwb_config_id = self._uwb_config_id
rp._session_id = self._session_id
rp._sub_session_id = self._sub_session_id
rp._ranging_update_rate = self._ranging_update_rate
rp._slot_duration = self._slot_duration
rp._measurement_report_type = self._measurement_report_type
rp._number_of_controlee = self._number_of_controlee
rp._destination_addresses = self._destination_addresses
rp._device_configs = self._device_configs
rp._vendor_specific_data = self._vendor_specific_data
return rp
def __init__(self):
super().__init__()
# ═══════════════════════════════════════════════════════════════
# UwbConfiguration / RangingConfig (stubs)
# ═══════════════════════════════════════════════════════════════
class UwbConfiguration(Stub):
"""UwbConfiguration — UWB 配置"""
def __init__(self):
super().__init__()
class RangingConfig(Stub):
"""RangingConfig — 测距配置"""
def __init__(self):
super().__init__()
# ═══════════════════════════════════════════════════════════════
# RangingSession
# ═══════════════════════════════════════════════════════════════
class RangingSession(Stub):
"""RangingSession — 测距会话"""
def __init__(self):
super().__init__()
self._open = False
def open(self):
self._open = True
def close(self):
self._open = False
def reconfigure(self, params: RangingParameters):
pass
# ═══════════════════════════════════════════════════════════════
# UwbManager
# ═══════════════════════════════════════════════════════════════
class UwbManager(Stub):
"""UwbManager — UWB 管理器"""
# ═══════════════════════════════════════════════════════════
# UwbCallback
# ═══════════════════════════════════════════════════════════
class UwbCallback(Stub):
"""UwbManager.UwbCallback — UWB 回调"""
def onRangingResult(self, sessionId: int, result: RangingResult):
pass
def onRangingFailure(self, sessionId: int, reason: int):
pass
def onControleeResult(self, sessionId: int, status: int):
pass
_instance: Optional["UwbManager"] = None
def __init__(self):
super().__init__()
@staticmethod
def getInstance(context: Any = None) -> "UwbManager":
if UwbManager._instance is None:
UwbManager._instance = UwbManager()
return UwbManager._instance
def open(self, sessionId: int) -> RangingSession:
session = RangingSession()
session.open()
return session
def close(self, sessionId: int):
pass
def range(self, sessionId: int, params: RangingParameters,
callback: UwbCallback):
if callback:
result = RangingResult(
status=UwbConstants.STATUS_OK,
distanceMeasurement=DistanceMeasurement(
meters=1.5, confidence=0.8,
standardDeviation=0.1, status=DistanceMeasurement.STATUS_SUCCESS
),
rssi=-65.0
)
callback.onRangingResult(sessionId, result)
def stopRange(self, sessionId: int):
pass
def addControlee(self, sessionId: int, address: UwbAddress):
pass
def removeControlee(self, sessionId: int, address: UwbAddress):
pass
def setMeasurementData(self, sessionId: int, data: bytes):
pass
def sendMeasurementData(self, sessionId: int, data: bytes):
pass
def getUwbDevice(self) -> Any:
return None
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"UwbManager",
"UwbConstants",
"UwbAddress",
"AngleOfArrival",
"DistanceMeasurement",
"RangingResult",
"RangingParameters",
"UwbConfiguration",
"RangingConfig",
"RangingSession",
]
+313
View File
@@ -0,0 +1,313 @@
"""
Android view.autofill 组件 — AutofillManager, AutofillValue, AutofillId 等
方法签名完全对齐 Android SDK
"""
from typing import Any, Callable, Dict, List, Optional
from .._core import Stub, AndroidOnlyError
# ═══════════════════════════════════════════════════════════════
# AutofillManager
# ═══════════════════════════════════════════════════════════════
class AutofillManager(Stub):
"""AutofillManager — 自动填充管理器"""
# ═══════════════════════════════════════════════════════════
# AutofillCallback
# ═══════════════════════════════════════════════════════════
class AutofillCallback(Stub):
"""AutofillManager.AutofillCallback — 自动填充回调"""
def onAutofillEvent(self, view: Any, event: int):
pass
def onAutofillUnavailableEvent(self, event: int):
pass
def onAuthenticationShowing(self):
pass
def __init__(self):
super().__init__()
self._enabled = False
self._callbacks: List[AutofillManager.AutofillCallback] = []
def isEnabled(self) -> bool:
return self._enabled
def hasEnabledAutofillServices(self) -> bool:
return False
def isAutofillSupported(self) -> bool:
return False
def disableAutofillServices(self):
self._enabled = False
def notifyValueChanged(self, view: Any):
pass
def commit(self):
pass
def cancel(self):
pass
def requestAutofill(self, view: Any):
pass
def getAutofillServiceComponentName(self) -> Any:
return None
def getAutofillServiceLabel(self) -> str:
return ""
def getAvailableFieldClassificationAlgorithms(self) -> List[str]:
return []
def getDefaultFieldClassificationAlgorithm(self) -> Optional[str]:
return None
def getFillEventHistory(self) -> Optional["FillEventHistory"]:
return None
def onAuthenticationResult(self, authenticationResult: Any):
pass
def onAuthenticationFailed(self):
pass
# ═══════════════════════════════════════════════════════════════
# AutofillValue
# ═══════════════════════════════════════════════════════════════
class AutofillValue(Stub):
"""AutofillValue — 自动填充值"""
def __init__(self):
super().__init__()
self._text_value: Optional[str] = None
self._date_value: Optional[int] = None
self._toggle_value: Optional[bool] = None
self._list_value: Optional[int] = None
self._is_text = False
self._is_date = False
self._is_toggle = False
self._is_list = False
@staticmethod
def forText(value: str) -> "AutofillValue":
v = AutofillValue()
v._text_value = value
v._is_text = True
return v
@staticmethod
def forDate(value: int) -> "AutofillValue":
v = AutofillValue()
v._date_value = value
v._is_date = True
return v
@staticmethod
def forToggle(value: bool) -> "AutofillValue":
v = AutofillValue()
v._toggle_value = value
v._is_toggle = True
return v
@staticmethod
def forList(value: int) -> "AutofillValue":
v = AutofillValue()
v._list_value = value
v._is_list = True
return v
def getTextValue(self) -> Optional[str]:
return self._text_value
def getDateValue(self) -> Optional[int]:
return self._date_value
def getToggleValue(self) -> Optional[bool]:
return self._toggle_value
def getListValue(self) -> Optional[int]:
return self._list_value
def isText(self) -> bool:
return self._is_text
def isDate(self) -> bool:
return self._is_date
def isToggle(self) -> bool:
return self._is_toggle
def isList(self) -> bool:
return self._is_list
def equals(self, other: Any) -> bool:
if not isinstance(other, AutofillValue):
return False
return (self._text_value == other._text_value
and self._date_value == other._date_value
and self._toggle_value == other._toggle_value
and self._list_value == other._list_value)
def hashCode(self) -> int:
return hash((self._text_value, self._date_value,
self._toggle_value, self._list_value)) & 0x7FFFFFFF
def toString(self) -> str:
if self._is_text:
return f"AutofillValue(text={self._text_value})"
if self._is_date:
return f"AutofillValue(date={self._date_value})"
if self._is_toggle:
return f"AutofillValue(toggle={self._toggle_value})"
if self._is_list:
return f"AutofillValue(list={self._list_value})"
return "AutofillValue(empty)"
# ═══════════════════════════════════════════════════════════════
# AutofillId
# ═══════════════════════════════════════════════════════════════
class AutofillId(Stub):
"""AutofillId — 自动填充 ID"""
FLAG_AUTOFILL_SESSION_SUPPORTS_INDEXING = 1
FLAG_AUTOFILL_SESSION_SUPPORTS_AUGMENTED_AUTOFILL = 2
def __init__(self, viewId: int = 0, sessionId: int = 0,
virtualChildId: int = -1, virtualIntId: int = -1):
super().__init__()
self._view_id = viewId
self._session_id = sessionId
self._virtual_child_id = virtualChildId
self._virtual_int_id = virtualIntId
def getViewId(self) -> int:
return self._view_id
def getSessionId(self) -> int:
return self._session_id
def getVirtualChildId(self) -> int:
return self._virtual_child_id
def getVirtualIntId(self) -> int:
return self._virtual_int_id
def equals(self, other: Any) -> bool:
if not isinstance(other, AutofillId):
return False
return (self._view_id == other._view_id
and self._session_id == other._session_id
and self._virtual_child_id == other._virtual_child_id)
def hashCode(self) -> int:
return hash((self._view_id, self._session_id,
self._virtual_child_id)) & 0x7FFFFFFF
def toString(self) -> str:
return f"AutofillId(viewId={self._view_id}, sessionId={self._session_id})"
# ═══════════════════════════════════════════════════════════════
# FillEventHistory
# ═══════════════════════════════════════════════════════════════
class FillEventHistory(Stub):
"""FillEventHistory — 填充事件历史"""
def __init__(self):
super().__init__()
self._events: List[Any] = []
self._client_state: Any = None
def getEvents(self) -> List[Any]:
return self._events
def getClientState(self) -> Any:
return self._client_state
# ═══════════════════════════════════════════════════════════════
# FillContext
# ═══════════════════════════════════════════════════════════════
class FillContext(Stub):
"""FillContext — 填充上下文"""
def __init__(self, requestId: int = 0, autofillId: Optional[AutofillId] = None,
focusedId: Optional[AutofillId] = None,
focusedVirtualId: int = -1, extras: Any = None,
autofillValue: Optional[AutofillValue] = None,
visibility: Optional[int] = None,
options: Optional[List[Any]] = None):
super().__init__()
self._request_id = requestId
self._autofill_id = autofillId
self._focused_id = focusedId
self._focused_virtual_id = focusedVirtualId
self._extras = extras
self._autofill_value = autofillValue
self._visibility = visibility
self._options = options
def getRequestId(self) -> int:
return self._request_id
def getAutofillId(self) -> Optional[AutofillId]:
return self._autofill_id
def getFocusedId(self) -> Optional[AutofillId]:
return self._focused_id
def getFocusedVirtualId(self) -> int:
return self._focused_virtual_id
def getExtras(self) -> Any:
return self._extras
def getAutofillValue(self) -> Optional[AutofillValue]:
return self._autofill_value
def getVisibility(self) -> Optional[int]:
return self._visibility
def getOptions(self) -> Optional[List[Any]]:
return self._options
# ═══════════════════════════════════════════════════════════════
# AugmentedAutofillManager (stub)
# ═══════════════════════════════════════════════════════════════
class AugmentedAutofillManager(Stub):
"""AugmentedAutofillManager — 增强自动填充管理器"""
def __init__(self):
super().__init__()
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"AutofillManager",
"AutofillValue",
"AutofillId",
"FillEventHistory",
"FillContext",
"AugmentedAutofillManager",
]
@@ -0,0 +1,426 @@
"""
Android view.textclassifier 组件 — TextClassifier, TextClassification, TextLinks, TextSelection 等
方法签名完全对齐 Android SDK
"""
import re
from typing import Any, Callable, Dict, List, Optional, Set, Tuple
from .._core import Stub, AndroidOnlyError
# ═══════════════════════════════════════════════════════════════
# EntityConfig
# ═══════════════════════════════════════════════════════════════
class EntityConfig(Stub):
"""EntityConfig — 实体配置"""
class Builder(Stub):
"""EntityConfig.Builder — 实体配置构建器"""
def __init__(self):
super().__init__()
self._include_types: Optional[Set[str]] = None
self._exclude_types: Optional[Set[str]] = None
self._include_from_textclassifier = True
def includeTypesFromTextClassifier(self) -> "EntityConfig.Builder":
self._include_from_textclassifier = True
return self
def excludeTypes(self, varargs: List[str]) -> "EntityConfig.Builder":
self._exclude_types = set(varargs)
return self
def includeTypes(self, varargs: List[str]) -> "EntityConfig.Builder":
self._include_types = set(varargs)
return self
def build(self) -> "EntityConfig":
config = EntityConfig()
config._include_types = self._include_types
config._exclude_types = self._exclude_types
config._include_from_textclassifier = self._include_from_textclassifier
return config
def getEntityTypes(self) -> Optional[List[str]]:
if self._include_types:
return list(self._include_types)
return None
def __init__(self):
super().__init__()
self._include_types: Optional[Set[str]] = None
self._exclude_types: Optional[Set[str]] = None
self._include_from_textclassifier = True
def getEntityTypes(self) -> Optional[List[str]]:
if self._include_types:
return list(self._include_types)
return None
# ═══════════════════════════════════════════════════════════════
# TextClassifier
# ═══════════════════════════════════════════════════════════════
class TextClassifier(Stub):
"""TextClassifier — 文本分类器"""
TYPE_UNKNOWN = "unknown"
TYPE_ADDRESS = "address"
TYPE_DATE = "date"
TYPE_DATE_TIME = "datetime"
TYPE_DURATION = "duration"
TYPE_EMAIL = "email"
TYPE_FLIGHT_NUMBER = "flight"
TYPE_IDENTIFIER = "identifier"
TYPE_LINK = "link"
TYPE_LIST = "list"
TYPE_LOCATION = "location"
TYPE_MEASUREMENT = "measurement"
TYPE_MONEY = "money"
TYPE_OTHER = "other"
TYPE_PERIODIC = "periodic"
TYPE_PHONE = "phone"
TYPE_PRODUCT = "product"
TYPE_RECIPE = "recipe"
TYPE_TRACK_AIRLINE = "track_airline"
TYPE_TRACK_FLIGHT = "track_flight"
TYPE_TRACK_FOOD = "track_food"
TYPE_TRACK_SPORT = "track_sport"
TYPE_TRACK_WEATHER = "track_weather"
TYPE_URL = "url"
TYPE_VEHICLE = "vehicle"
WIDGET_TYPE_CLIPBOARD = "clipboard"
WIDGET_TYPE_CUT_COPY = "cutcopy"
WIDGET_TYPE_EDIT_TEXT = "edittext"
WIDGET_TYPE_LINKIFY = "linkify"
WIDGET_TYPE_NOTIFICATION = "notification"
WIDGET_TYPE_OTHER = "other"
WIDGET_TYPE_SELECTION = "selection"
WIDGET_TYPE_TEXT = "text"
WIDGET_TYPE_UNSELECTED_TEXT = "unselected_text"
# ═══════════════════════════════════════════════════════════
# Request
# ═══════════════════════════════════════════════════════════
class Request(Stub):
"""TextClassifier.Request — 分类请求"""
def __init__(self):
super().__init__()
self._calling_package_name: Optional[str] = None
self._text: str = ""
self._start_index: int = 0
self._end_index: int = 0
self._extras: Any = None
self._default_locales: Any = None
self._dark_launch_allowed = False
def getCallingPackageName(self) -> Optional[str]:
return self._calling_package_name
def getText(self) -> str:
return self._text
def getStartIndex(self) -> int:
return self._start_index
def getEndIndex(self) -> int:
return self._end_index
def getExtras(self) -> Any:
return self._extras
def getDefaultLocales(self) -> Any:
return self._default_locales
def isDarkLaunchAllowed(self) -> bool:
return self._dark_launch_allowed
def __init__(self):
super().__init__()
def classifyText(self, request: Request) -> "TextClassification":
text = request.getText()
start = request.getStartIndex()
end = request.getEndIndex()
if end > len(text):
end = len(text)
selected = text[start:end] if start < end else text
entity_type = TextClassifier.TYPE_UNKNOWN
confidence = 0.0
# Basic regex-based classification
if re.match(r'^https?://', selected):
entity_type = TextClassifier.TYPE_URL
confidence = 0.95
elif re.match(r'^[\w.+-]+@[\w-]+\.[\w.-]+$', selected):
entity_type = TextClassifier.TYPE_EMAIL
confidence = 0.95
elif re.match(r'^\+?[\d\s\-\(\)]{7,15}$', selected):
entity_type = TextClassifier.TYPE_PHONE
confidence = 0.85
elif re.match(r'^\d{1,4}[-/]\d{1,2}[-/]\d{1,4}$', selected):
entity_type = TextClassifier.TYPE_DATE
confidence = 0.80
elif re.match(r'^\d{1,2}:\d{2}(\s?[AP]M)?$', selected, re.IGNORECASE):
entity_type = TextClassifier.TYPE_DATE_TIME
confidence = 0.80
tc = TextClassification()
tc._text = selected
tc._entity_type = entity_type
tc._confidence_score = confidence
tc._entity_count = 1
return tc
def suggestConversationActions(self, request: Request) -> List["ConversationAction"]:
return []
def detectLanguage(self, text: str) -> "TextClassification":
tc = TextClassification()
tc._text = text
tc._entity_type = TextClassifier.TYPE_UNKNOWN
return tc
def onSelectionEvent(self, event: Any):
pass
def onTextChanged(self, text: Any):
pass
def onTextSelectionAction(self, action: Any):
pass
# ═══════════════════════════════════════════════════════════════
# TextClassification
# ═══════════════════════════════════════════════════════════════
class TextClassification(Stub):
"""TextClassification — 文本分类结果"""
def __init__(self):
super().__init__()
self._text: str = ""
self._entity_type: str = TextClassifier.TYPE_UNKNOWN
self._confidence_score: float = 0.0
self._entity_count: int = 0
self._icon: Any = None
self._label: str = ""
self._actions: List[Any] = []
self._extras: Any = None
self._id: Optional[str] = None
def getText(self) -> str:
return self._text
def getEntityCount(self) -> int:
return self._entity_count
def getEntity(self, index: int) -> Optional[str]:
if index == 0:
return self._entity_type
return None
def getEntityType(self) -> str:
return self._entity_type
def getConfidenceScore(self, entityType: str) -> float:
if entityType == self._entity_type:
return self._confidence_score
return 0.0
def getIcon(self) -> Any:
return self._icon
def getLabel(self) -> str:
return self._label
def getActions(self) -> List[Any]:
return self._actions
def getExtras(self) -> Any:
return self._extras
def getId(self) -> Optional[str]:
return self._id
def toString(self) -> str:
return f"TextClassification(type={self._entity_type}," \
f" confidence={self._confidence_score:.2f})"
# ═══════════════════════════════════════════════════════════════
# TextLinks
# ═══════════════════════════════════════════════════════════════
class TextLinks(Stub):
"""TextLinks — 文本链接"""
def __init__(self):
super().__init__()
self._links: List[Any] = []
self._extras: Any = None
def getLinks(self) -> List[Any]:
return self._links
def getEntityCount(self) -> int:
return len(self._links)
def getEntity(self, index: int) -> Optional[str]:
if 0 <= index < len(self._links):
return self._links[index]
return None
def getEntityType(self) -> str:
return TextClassifier.TYPE_LINK
def getConfidenceScore(self, entityType: str) -> float:
return 0.0
def getExtras(self) -> Any:
return self._extras
# ═══════════════════════════════════════════════════════════════
# TextSelection
# ═══════════════════════════════════════════════════════════════
class TextSelection(Stub):
"""TextSelection — 文本选择结果"""
def __init__(self):
super().__init__()
self._start_index: int = 0
self._end_index: int = 0
self._entity_type: str = TextClassifier.TYPE_UNKNOWN
self._text: str = ""
self._entity_count: int = 0
self._confidence_score: float = 0.0
self._extras: Any = None
self._id: Optional[str] = None
def getSelectionStartIndex(self) -> int:
return self._start_index
def getSelectionEndIndex(self) -> int:
return self._end_index
def getEntityType(self) -> str:
return self._entity_type
def getText(self) -> str:
return self._text
def getEntityCount(self) -> int:
return self._entity_count
def getEntity(self, index: int) -> Optional[str]:
if index == 0:
return self._entity_type
return None
def getConfidenceScore(self, entityType: str) -> float:
if entityType == self._entity_type:
return self._confidence_score
return 0.0
def getExtras(self) -> Any:
return self._extras
def getId(self) -> Optional[str]:
return self._id
# ═══════════════════════════════════════════════════════════════
# ConversationAction
# ═══════════════════════════════════════════════════════════════
class ConversationAction(Stub):
"""ConversationAction — 对话操作"""
TYPE_CALL = "call"
TYPE_CREATE_REMINDER = "create_reminder"
TYPE_OPEN_URL = "open_url"
TYPE_SEND_EMAIL = "send_email"
TYPE_SEND_SMS = "send_sms"
TYPE_SHARE_LOCATION = "share_location"
TYPE_TEXT_REPLY = "text_reply"
TYPE_VIEW_CALENDAR = "view_calendar"
TYPE_VIEW_MAP = "view_map"
def __init__(self, text: str = "", confidenceScore: float = 0.0,
action: str = "", extras: Any = None,
icon: Any = None, label: str = ""):
super().__init__()
self._text = text
self._confidence_score = confidenceScore
self._action = action
self._extras = extras
self._icon = icon
self._label = label
def getText(self) -> str:
return self._text
def getConfidenceScore(self) -> float:
return self._confidence_score
def getAction(self) -> str:
return self._action
def getExtras(self) -> Any:
return self._extras
def getIcon(self) -> Any:
return self._icon
def getLabel(self) -> str:
return self._label
# ═══════════════════════════════════════════════════════════════
# TextClassificationContext
# ═══════════════════════════════════════════════════════════════
class TextClassificationContext(Stub):
"""TextClassificationContext — 文本分类上下文"""
def __init__(self, packageName: str = "", widgetType: str = "",
widgetVersion: str = ""):
super().__init__()
self._package_name = packageName
self._widget_type = widgetType
self._widget_version = widgetVersion
def getPackageName(self) -> str:
return self._package_name
def getWidgetType(self) -> str:
return self._widget_type
def getWidgetVersion(self) -> str:
return self._widget_version
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"TextClassifier",
"TextClassification",
"TextLinks",
"TextSelection",
"ConversationAction",
"TextClassificationContext",
"EntityConfig",
]
+435
View File
@@ -0,0 +1,435 @@
"""
Android view.translation 组件 — TranslationManager, TranslationRequest, TranslationResponse 等
方法签名完全对齐 Android SDK
"""
from typing import Any, Callable, Dict, List, Optional
from .._core import Stub, AndroidOnlyError
# ═══════════════════════════════════════════════════════════════
# TranslationSpec
# ═══════════════════════════════════════════════════════════════
class TranslationSpec(Stub):
"""TranslationSpec — 翻译规格"""
DATA_TYPE_TEXT = 0
DATA_TYPE_UI = 1
def __init__(self):
super().__init__()
self._locale: Optional[str] = None
self._data_type = TranslationSpec.DATA_TYPE_TEXT
def setLocale(self, locale: str) -> "TranslationSpec":
self._locale = locale
return self
def setDataType(self, dataType: int) -> "TranslationSpec":
self._data_type = dataType
return self
def getLocale(self) -> Optional[str]:
return self._locale
def getDataType(self) -> int:
return self._data_type
# ═══════════════════════════════════════════════════════════════
# TranslationContext
# ═══════════════════════════════════════════════════════════════
class TranslationContext(Stub):
"""TranslationContext — 翻译上下文"""
def __init__(self, sourceLocale: str = "", targetLocale: str = "",
translationFlags: int = 0):
super().__init__()
self._source_locale = sourceLocale
self._target_locale = targetLocale
self._translation_flags = translationFlags
def getSourceLocale(self) -> str:
return self._source_locale
def getTargetLocale(self) -> str:
return self._target_locale
def getTranslationFlags(self) -> int:
return self._translation_flags
# ═══════════════════════════════════════════════════════════════
# TranslationCapability
# ═══════════════════════════════════════════════════════════════
class TranslationCapability(Stub):
"""TranslationCapability — 翻译能力"""
STATE_AVAILABLE = 0
STATE_UNAVAILABLE = 1
STATE_CAPABLE = 2
STATE_NOT_CAPABLE = 3
STATE_DOWNLOADING = 4
STATE_NOT_ALLOWED = 5
def __init__(self, state: int = STATE_UNAVAILABLE,
sourceLocale: str = "", targetLocale: str = "",
uiTranslationEnabled: bool = False,
textTranslationEnabled: bool = False,
translationService: str = "desktop"):
super().__init__()
self._state = state
self._source_locale = sourceLocale
self._target_locale = targetLocale
self._ui_translation_enabled = uiTranslationEnabled
self._text_translation_enabled = textTranslationEnabled
self._translation_service = translationService
def getState(self) -> int:
return self._state
def getSourceLocale(self) -> str:
return self._source_locale
def getTargetLocale(self) -> str:
return self._target_locale
def isUiTranslationEnabled(self) -> bool:
return self._ui_translation_enabled
def isTextTranslationEnabled(self) -> bool:
return self._text_translation_enabled
def getTranslationService(self) -> str:
return self._translation_service
# ═══════════════════════════════════════════════════════════════
# TranslationResult
# ═══════════════════════════════════════════════════════════════
class TranslationResult(Stub):
"""TranslationResult — 翻译结果"""
def __init__(self, translatedText: str = "", sourceText: str = "",
translationStatus: int = 0):
super().__init__()
self._translated_text = translatedText
self._source_text = sourceText
self._translation_status = translationStatus
def getTranslatedText(self) -> str:
return self._translated_text
def getSourceText(self) -> str:
return self._source_text
def getTranslationStatus(self) -> int:
return self._translation_status
# ═══════════════════════════════════════════════════════════════
# TranslationRequest
# ═══════════════════════════════════════════════════════════════
class TranslationRequest(Stub):
"""TranslationRequest — 翻译请求"""
class Builder(Stub):
"""TranslationRequest.Builder — 翻译请求构建器"""
def __init__(self):
super().__init__()
self._source_text: str = ""
self._source_locale: str = ""
self._target_locale: str = ""
self._translation_flags: int = 0
self._ui_translation_result: Any = None
self._request_id: int = 0
self._translation_config: Any = None
def setSourceText(self, text: str) -> "TranslationRequest.Builder":
self._source_text = text
return self
def setSourceLocale(self, locale: str) -> "TranslationRequest.Builder":
self._source_locale = locale
return self
def setTargetLocale(self, locale: str) -> "TranslationRequest.Builder":
self._target_locale = locale
return self
def setTranslationFlags(self, flags: int) -> "TranslationRequest.Builder":
self._translation_flags = flags
return self
def setUiTranslationResult(self, result: Any) -> "TranslationRequest.Builder":
self._ui_translation_result = result
return self
def setRequestId(self, id: int) -> "TranslationRequest.Builder":
self._request_id = id
return self
def setTranslationConfig(self, config: Any) -> "TranslationRequest.Builder":
self._translation_config = config
return self
def build(self) -> "TranslationRequest":
req = TranslationRequest()
req._source_text = self._source_text
req._source_locale = self._source_locale
req._target_locale = self._target_locale
req._translation_flags = self._translation_flags
req._ui_translation_result = self._ui_translation_result
req._request_id = self._request_id
req._translation_config = self._translation_config
return req
def __init__(self):
super().__init__()
self._source_text: str = ""
self._source_locale: str = ""
self._target_locale: str = ""
self._request_id: int = 0
self._translation_flags: int = 0
self._ui_translation_result: Any = None
self._translation_config: Any = None
def getSourceText(self) -> str:
return self._source_text
def getSourceLocale(self) -> str:
return self._source_locale
def getTargetLocale(self) -> str:
return self._target_locale
def getRequestId(self) -> int:
return self._request_id
def getTranslationFlags(self) -> int:
return self._translation_flags
# ═══════════════════════════════════════════════════════════════
# TranslationResponse
# ═══════════════════════════════════════════════════════════════
class TranslationResponse(Stub):
"""TranslationResponse — 翻译响应"""
class Builder(Stub):
"""TranslationResponse.Builder — 翻译响应构建器"""
def __init__(self):
super().__init__()
self._translation_result: Optional[TranslationResult] = None
self._status: int = 0
def setTranslationResult(self, result: TranslationResult) -> "TranslationResponse.Builder":
self._translation_result = result
return self
def setStatus(self, status: int) -> "TranslationResponse.Builder":
self._status = status
return self
def build(self) -> "TranslationResponse":
resp = TranslationResponse()
resp._translation_result = self._translation_result
resp._status = self._status
return resp
def __init__(self):
super().__init__()
self._translation_result: Optional[TranslationResult] = None
self._status: int = 0
def getTranslationResult(self) -> Optional[TranslationResult]:
return self._translation_result
def getStatus(self) -> int:
return self._status
# ═══════════════════════════════════════════════════════════════
# Translator
# ═══════════════════════════════════════════════════════════════
class Translator(Stub):
"""Translator — 翻译器"""
def __init__(self):
super().__init__()
self._supported_locales: List[str] = ["en", "zh", "ja", "ko", "fr", "de", "es"]
def translate(self, text: str, sourceLocale: str,
targetLocale: str) -> str:
# Basic stub: return original text with a prefix
if sourceLocale == targetLocale:
return text
return f"[{sourceLocale}->{targetLocale}] {text}"
def destroy(self):
pass
def getSupportedLocales(self) -> List[str]:
return list(self._supported_locales)
# ═══════════════════════════════════════════════════════════════
# TranslationManager
# ═══════════════════════════════════════════════════════════════
class TranslationManager(Stub):
"""TranslationManager — 翻译管理器"""
EXTRA_CAPABILITIES_FROM_FRAMEWORK = "android.view.translation.extra.CAPABILITIES_FROM_FRAMEWORK"
EXTRA_TRANSLATION_REQUEST_RESULT = "android.view.translation.extra.TRANSLATION_REQUEST_RESULT"
def __init__(self):
super().__init__()
self._translators: Dict[str, Translator] = {}
def startTranslation(self, request: TranslationRequest) -> TranslationResponse:
builder = TranslationResponse.Builder()
try:
source = request.getSourceLocale()
target = request.getTargetLocale()
text = request.getSourceText()
translator = self._get_or_create_translator()
translated = translator.translate(text, source, target)
result = TranslationResult(
translatedText=translated,
sourceText=text,
translationStatus=0
)
builder.setTranslationResult(result)
builder.setStatus(0)
except Exception as e:
builder.setStatus(1)
return builder.build()
def createOnDeviceTranslation(self) -> Translator:
translator = Translator()
return translator
def getTranslationCapabilities(self, sourceLocale: str,
targetLocale: str) -> List[TranslationCapability]:
return [
TranslationCapability(
state=TranslationCapability.STATE_AVAILABLE,
sourceLocale=sourceLocale,
targetLocale=targetLocale,
textTranslationEnabled=True
)
]
def onTranslationRequest(self, request: TranslationRequest):
pass
def isTranslationSupported(self) -> bool:
return True
def isOnDeviceTranslationSupported(self) -> bool:
return True
def isULTRADeviceSupported(self) -> bool:
return False
def _get_or_create_translator(self) -> Translator:
return Translator()
# ═══════════════════════════════════════════════════════════════
# ViewTranslation / ViewTranslationRequest / ViewTranslationResponse (stubs)
# ═══════════════════════════════════════════════════════════════
class ViewTranslation(Stub):
"""ViewTranslation — 视图翻译"""
def __init__(self, autofillId: Any = None, value: str = "",
translationResult: Optional[TranslationResult] = None):
super().__init__()
self._autofill_id = autofillId
self._value = value
self._translation_result = translationResult
def getAutofillId(self) -> Any:
return self._autofill_id
def getValue(self) -> str:
return self._value
def getTranslationResult(self) -> Optional[TranslationResult]:
return self._translation_result
def setTranslationResult(self, result: TranslationResult):
self._translation_result = result
class ViewTranslationRequest(Stub):
"""ViewTranslationRequest — 视图翻译请求"""
def __init__(self, autofillId: Any = None, value: str = "",
requestId: int = 0):
super().__init__()
self._autofill_id = autofillId
self._value = value
self._request_id = requestId
def getAutofillId(self) -> Any:
return self._autofill_id
def getValue(self) -> str:
return self._value
def getRequestId(self) -> int:
return self._request_id
class ViewTranslationResponse(Stub):
"""ViewTranslationResponse — 视图翻译响应"""
def __init__(self, autofillId: Any = None, value: str = "",
extras: Any = None):
super().__init__()
self._autofill_id = autofillId
self._value = value
self._extras = extras
def getAutofillId(self) -> Any:
return self._autofill_id
def getValue(self) -> str:
return self._value
def getExtras(self) -> Any:
return self._extras
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"TranslationManager",
"TranslationRequest",
"TranslationResponse",
"TranslationResult",
"TranslationCapability",
"Translator",
"TranslationContext",
"TranslationSpec",
"ViewTranslation",
"ViewTranslationRequest",
"ViewTranslationResponse",
]