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

+144 -23
View File
@@ -59,6 +59,48 @@ from .android.webkit import *
from .android.nfc import * from .android.nfc import *
from .android.speech_tts import * from .android.speech_tts import *
from .android.inputmethod import * from .android.inputmethod import *
from .android.app_admin import *
from .android.app_backup import *
from .android.app_job import *
from .android.app_people import *
from .android.app_role import *
from .android.app_usage import *
from .android.appwidget import *
from .android.companion import *
from .android.credentials import *
from .android.hardware_display import *
from .android.hardware_fingerprint import *
from .android.hardware_input import *
from .android.hardware_usb import *
from .android.media_audiofx import *
from .android.media_browse import *
from .android.media_midi import *
from .android.media_projection import *
from .android.media_session import *
from .android.media_tv import *
from .android.mtp import *
from .android.opengl import *
from .android.print import *
from .android.renderscript import *
from .android.se import *
from .android.security import *
from .android.service_autofill import *
from .android.service_controls import *
from .android.service_dreams import *
from .android.service_notification import *
from .android.service_quicksettings import *
from .android.service_vr import *
from .android.service_wallpaper import *
from .android.sip import *
from .android.speech import *
from .android.system import *
from .android.telecom import *
from .android.test import *
from .android.transition import *
from .android.uwb import *
from .android.view_autofill import *
from .android.view_textclassifier import *
from .android.view_translation import *
# java.* 包 # java.* 包
from .java.io import * from .java.io import *
@@ -206,32 +248,111 @@ class TimerTask:
__version__ = "1.0.0" __version__ = "1.0.0"
__all__ = [ __all__ = [
# android # android
"Intent", "Context", "Uri", "ContentValues", "ContentResolver",
"BroadcastReceiver", "IntentFilter", "SharedPreferences",
"PackageManager", "PackageInfo", "ActivityManager",
"Build", "Environment", "PowerManager", "BatteryManager",
"Bundle", "Handler", "Looper", "Message",
"View", "ViewGroup", "TextView", "EditText", "Button",
"ImageView", "ScrollView", "ListView", "Spinner",
"CheckBox", "Switch", "ProgressBar", "Toast",
"Bitmap", "BitmapFactory", "Canvas", "Color", "Paint",
"Rect", "RectF", "Matrix", "Typeface",
"PdfRenderer", "Drawable", "BitmapDrawable",
"ConnectivityManager", "NetworkInfo", "WifiManager",
"MediaPlayer", "AudioManager", "MediaRecorder",
"SensorManager", "CameraManager", "BiometricPrompt",
"LocationManager", "Location", "Geocoder",
"SQLiteDatabase", "SQLiteOpenHelper", "Cursor",
"Settings", "MediaStore", "ContactsContract", "CalendarContract",
"TelephonyManager", "SmsManager",
"BluetoothAdapter", "BluetoothDevice",
"TextUtils", "Html", "SpannableString",
"Log", "Base64", "TypedValue",
"AccessibilityNodeInfo", "AccessibilityService", "AccessibilityNodeInfo", "AccessibilityService",
"Animation", "AlphaAnimation", "TranslateAnimation", "ActivityManager",
"AlphaAnimation",
"Animation",
"AppWidgetManager", "AppWidgetProvider", "AppWidgetProviderInfo",
"AudioManager",
"AutofillManager", "AutofillValue",
"BackupAgent", "BackupManager",
"Base64",
"BatteryManager",
"BiometricPrompt",
"Bitmap", "BitmapDrawable", "BitmapFactory",
"BluetoothAdapter", "BluetoothDevice",
"BroadcastReceiver",
"Build",
"Bundle",
"Button",
"CalendarContract",
"Call",
"CameraManager",
"Canvas",
"CheckBox",
"Color",
"CompanionDeviceManager",
"ConnectivityManager",
"ContentResolver",
"ContentValues",
"ContactsContract",
"Context",
"CredentialManager",
"Cursor",
"DevicePolicyManager",
"DisplayManager",
"Drawable",
"EditText",
"Environment",
"FingerprintManager",
"Geocoder",
"Handler",
"Html",
"ImageView",
"InputDevice",
"InputManager",
"InputMethodManager", "InputMethodManager",
"WebView", "Intent",
"IntentFilter",
"JobInfo", "JobScheduler", "JobService",
"KeyChain",
"ListView",
"Location",
"LocationManager",
"Log",
"Looper",
"Matrix",
"MediaBrowser", "MediaController",
"MediaPlayer",
"MediaProjectionManager",
"MediaRecorder",
"MediaSession",
"MediaStore",
"Message",
"MidiDevice", "MidiManager",
"MtpDevice",
"NetworkInfo",
"Os", "OsConstants",
"PackageInfo", "PackageManager",
"Paint",
"PdfRenderer",
"PeopleManager",
"PowerManager",
"PrintAttributes", "PrintDocumentAdapter", "PrintManager",
"ProgressBar",
"Rect", "RectF",
"RoleManager",
"ScrollView",
"SensorManager",
"Settings",
"SharedPreferences",
"SmsManager",
"SpannableString",
"SpeechRecognizer",
"Spinner",
"SQLiteDatabase", "SQLiteOpenHelper",
"StructStat",
"Switch",
"TelecomManager",
"TelephonyManager",
"TextClassifier",
"TextToSpeech", "TextToSpeech",
"TextUtils",
"TextView",
"Toast",
"TranslateAnimation",
"TranslationManager",
"TvInputManager",
"Typeface",
"TypedValue",
"Uri",
"UsageStatsManager",
"UsbDevice", "UsbManager",
"UwbManager",
"View", "ViewGroup",
"VirtualDisplay",
"WebView",
"WifiManager",
# java # java
"File", "FileInputStream", "FileOutputStream", "File", "FileInputStream", "FileOutputStream",
"BufferedReader", "BufferedWriter", "BufferedReader", "BufferedWriter",
+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",
]