新增 _linux_impl.py (800+行): - LinuxNetwork: 真实IP/接口/DNS/网关/MAC (ip命令+/proc/net) - LinuxBluetooth: 真实蓝牙状态/扫描/配对/连接 (bluetoothctl) - LinuxSensors: 真实温度/CPU频率/风扇 (/sys/class/hwmon) - LinuxAudio: 真实音量控制/静音 (amixer) - LinuxClipboard: 真实剪贴板读写 (xclip+tkinter回退) - LinuxPower: 真实CPU/内存/电池 (psutil+/sys/class/power_supply) - LinuxLocation: 真实IP地理定位 (ip-api.com) - LinuxNotify: 真实桌面通知 (notify-send) - LinuxDisplay: 真实屏幕尺寸/截屏/鼠标位置 (xdotool+X11) - LinuxUIAutomation: 真实点击/输入/按键/滚动 (xdotool) - LinuxCamera: 真实摄像头拍照 (opencv/V4L2) - LinuxProcess: 真实进程列表/系统信息/应用管理 (psutil+/proc) - LinuxShell: 真实命令执行 (subprocess) 改造10+核心类使用Linux真实实现: - BluetoothAdapter/SensorManager/AudioManager - ConnectivityManager/WifiManager/TelephonyManager - LocationManager/PowerManager/BatteryManager/Build - ActivityManager/Activity/Toast/Display/ClipboardManager - NotificationManager 15项真实行为测试全部通过: - 真实IP: 10.60.218.244 - 真实位置: 北京 39.9042, 116.407 - 真实主机名: work-41-vci-g427c - 真实内核: 5.15.120.byteatom-ck.14 - 真实内存: 1.8GB/3.8GB - 真实进程列表
2182 lines
69 KiB
Python
2182 lines
69 KiB
Python
"""
|
|
Android App 组件 — Activity, Service, Intent, Notification 等
|
|
方法签名完全对齐 Android SDK
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
import time
|
|
import re
|
|
import sys
|
|
import threading
|
|
from datetime import datetime
|
|
from typing import Any, Dict, List, Optional, Tuple, Callable, Union
|
|
from urllib.parse import urlparse, urlunparse
|
|
|
|
from .._core import (
|
|
Stub, AndroidOnlyError, android_context,
|
|
resolve_android_path, to_android_path,
|
|
_sdcard_root, _app_data_dir, _package_name,
|
|
)
|
|
|
|
try:
|
|
from .._linux_impl import IS_LINUX, LinuxNotify, LinuxShell, LinuxProcess, LinuxDisplay, LinuxUIAutomation, LinuxPower
|
|
_USE_LINUX = IS_LINUX
|
|
except ImportError:
|
|
_USE_LINUX = False
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# Intent
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class Intent(Stub):
|
|
"""Android Intent — 组件间消息传递"""
|
|
|
|
# Action 常量
|
|
ACTION_MAIN = "android.intent.action.MAIN"
|
|
ACTION_VIEW = "android.intent.action.VIEW"
|
|
ACTION_EDIT = "android.intent.action.EDIT"
|
|
ACTION_SEND = "android.intent.action.SEND"
|
|
ACTION_CALL = "android.intent.action.CALL"
|
|
ACTION_DIAL = "android.intent.action.DIAL"
|
|
ACTION_WEB_SEARCH = "android.intent.action.WEB_SEARCH"
|
|
ACTION_PICK = "android.intent.action.PICK"
|
|
ACTION_GET_CONTENT = "android.intent.action.GET_CONTENT"
|
|
ACTION_INSTALL_PACKAGE = "android.intent.action.INSTALL_PACKAGE"
|
|
ACTION_BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED"
|
|
ACTION_POWER_CONNECTED = "android.intent.action.ACTION_POWER_CONNECTED"
|
|
ACTION_POWER_DISCONNECTED = "android.intent.action.ACTION_POWER_DISCONNECTED"
|
|
ACTION_BATTERY_LOW = "android.intent.action.BATTERY_LOW"
|
|
ACTION_BATTERY_OKAY = "android.intent.action.BATTERY_OKAY"
|
|
ACTION_TIME_TICK = "android.intent.action.TIME_TICK"
|
|
ACTION_DATE_CHANGED = "android.intent.action.DATE_CHANGED"
|
|
ACTION_SCREEN_ON = "android.intent.action.SCREEN_ON"
|
|
ACTION_SCREEN_OFF = "android.intent.action.SCREEN_OFF"
|
|
ACTION_AIRPLANE_MODE_CHANGED = "android.intent.action.AIRPLANE_MODE_CHANGED"
|
|
|
|
# Flag 常量
|
|
FLAG_ACTIVITY_NEW_TASK = 0x10000000
|
|
FLAG_ACTIVITY_CLEAR_TOP = 0x04000000
|
|
FLAG_ACTIVITY_SINGLE_TOP = 0x20000000
|
|
FLAG_ACTIVITY_NO_HISTORY = 0x40000000
|
|
FLAG_FROM_BACKGROUND = 0x00004000
|
|
FLAG_RECEIVER_FOREGROUND = 0x10000000
|
|
FLAG_INCLUDE_STOPPED_PACKAGES = 0x00000020
|
|
|
|
# Category 常量
|
|
CATEGORY_LAUNCHER = "android.intent.category.LAUNCHER"
|
|
CATEGORY_DEFAULT = "android.intent.category.DEFAULT"
|
|
CATEGORY_HOME = "android.intent.category.HOME"
|
|
CATEGORY_BROWSABLE = "android.intent.category.BROWSABLE"
|
|
CATEGORY_OPENABLE = "android.intent.category.OPENABLE"
|
|
|
|
def __init__(self, action: Optional[str] = None, uri: Optional[str] = None):
|
|
super().__init__()
|
|
self._action = action
|
|
self._uri = uri
|
|
self._data_str = None
|
|
self._type = None
|
|
self._extras: Dict[str, Any] = {}
|
|
self._flags = 0
|
|
self._categories: List[str] = []
|
|
self._package = None
|
|
self._component: Optional[Tuple[str, str]] = None # (pkg, cls)
|
|
self._clip_data = None
|
|
|
|
def setClass(self, package_context: Any, cls: type) -> "Intent":
|
|
self._package = package_context.packageName if hasattr(package_context, 'packageName') else _package_name
|
|
self._component = (self._package, cls.__name__ if isinstance(cls, type) else str(cls))
|
|
return self
|
|
|
|
def setAction(self, action: str) -> "Intent":
|
|
self._action = action
|
|
return self
|
|
|
|
def getAction(self) -> Optional[str]:
|
|
return self._action
|
|
|
|
def setData(self, uri: str) -> "Intent":
|
|
self._data_str = uri
|
|
return self
|
|
|
|
def getData(self) -> Optional[str]:
|
|
return self._data_str
|
|
|
|
def setType(self, type_: str) -> "Intent":
|
|
self._type = type_
|
|
return self
|
|
|
|
def getType(self) -> Optional[str]:
|
|
return self._type
|
|
|
|
def setDataAndType(self, uri: str, type_: str) -> "Intent":
|
|
self._data_str = uri
|
|
self._type = type_
|
|
return self
|
|
|
|
def putExtra(self, name: str, value: Any) -> "Intent":
|
|
self._extras[name] = value
|
|
return self
|
|
|
|
def getStringExtra(self, name: str) -> Optional[str]:
|
|
return self._extras.get(name)
|
|
|
|
def getIntExtra(self, name: str, default: int = 0) -> int:
|
|
return self._extras.get(name, default)
|
|
|
|
def getBooleanExtra(self, name: str, default: bool = False) -> bool:
|
|
return self._extras.get(name, default)
|
|
|
|
def getLongExtra(self, name: str, default: int = 0) -> int:
|
|
return self._extras.get(name, default)
|
|
|
|
def getFloatExtra(self, name: str, default: float = 0.0) -> float:
|
|
return self._extras.get(name, default)
|
|
|
|
def getDoubleExtra(self, name: str, default: float = 0.0) -> float:
|
|
return self._extras.get(name, default)
|
|
|
|
def getSerializableExtra(self, name: str):
|
|
return self._extras.get(name)
|
|
|
|
def getIntegerArrayListExtra(self, name: str) -> Optional[List[int]]:
|
|
return self._extras.get(name)
|
|
|
|
def getStringArrayListExtra(self, name: str) -> Optional[List[str]]:
|
|
return self._extras.get(name)
|
|
|
|
def getExtras(self) -> Dict[str, Any]:
|
|
return self._extras
|
|
|
|
def hasExtra(self, name: str) -> bool:
|
|
return name in self._extras
|
|
|
|
def removeExtra(self, name: str):
|
|
self._extras.pop(name, None)
|
|
|
|
def addCategory(self, category: str) -> "Intent":
|
|
if category not in self._categories:
|
|
self._categories.append(category)
|
|
return self
|
|
|
|
def getCategories(self) -> Optional[List[str]]:
|
|
return self._categories if self._categories else None
|
|
|
|
def hasCategory(self, category: str) -> bool:
|
|
return category in self._categories
|
|
|
|
def setFlags(self, flags: int) -> "Intent":
|
|
self._flags = flags
|
|
return self
|
|
|
|
def getFlags(self) -> int:
|
|
return self._flags
|
|
|
|
def addFlags(self, flags: int) -> "Intent":
|
|
self._flags |= flags
|
|
return self
|
|
|
|
def setPackage(self, package_name: str) -> "Intent":
|
|
self._package = package_name
|
|
return self
|
|
|
|
def getPackage(self) -> Optional[str]:
|
|
return self._package
|
|
|
|
def setClassName(self, package_name: str, class_name: str) -> "Intent":
|
|
self._component = (package_name, class_name)
|
|
return self
|
|
|
|
def resolveActivity(self, pm) -> Optional["ComponentName"]:
|
|
from .content import ComponentName
|
|
if self._component:
|
|
return ComponentName(self._component[0], self._component[1])
|
|
return None
|
|
|
|
def createChooser(self, title: str) -> "Intent":
|
|
"""Create a chooser intent"""
|
|
return self
|
|
|
|
def toURI(self) -> str:
|
|
return str(self._uri or self._data_str or "")
|
|
|
|
def toString(self) -> str:
|
|
return (
|
|
f"Intent {{ act={self._action} "
|
|
f"pkg={self._package} "
|
|
f"cmp={self._component} "
|
|
f"hasExtras={bool(self._extras)} }}"
|
|
)
|
|
|
|
def clone(self) -> "Intent":
|
|
cloned = Intent(self._action, self._uri)
|
|
cloned._data_str = self._data_str
|
|
cloned._type = self._type
|
|
cloned._extras = dict(self._extras)
|
|
cloned._flags = self._flags
|
|
cloned._categories = list(self._categories)
|
|
cloned._package = self._package
|
|
cloned._component = self._component
|
|
return cloned
|
|
|
|
def fillIn(self, other: "Intent", flags: int = 0) -> int:
|
|
filled = 0
|
|
if other._action and not self._action:
|
|
self._action = other._action; filled |= 1
|
|
if other._data_str and not self._data_str:
|
|
self._data_str = other._data_str; filled |= 2
|
|
if other._type and not self._type:
|
|
self._type = other._type; filled |= 4
|
|
if other._categories:
|
|
for c in other._categories:
|
|
if c not in self._categories:
|
|
self._categories.append(c)
|
|
filled |= 8
|
|
return filled
|
|
|
|
def filterEquals(self, other: "Intent") -> bool:
|
|
return (
|
|
self._action == other._action
|
|
and self._data_str == other._data_str
|
|
and self._type == other._type
|
|
and self._package == other._package
|
|
and self._component == other._component
|
|
)
|
|
|
|
def filterHashCode(self) -> int:
|
|
return hash((self._action, self._data_str, self._type, self._package, self._component)) & 0x7FFFFFFF
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# ComponentName (forward reference)
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class ComponentName(Stub):
|
|
"""Identifies a specific component class within a package"""
|
|
|
|
def __init__(self, pkg: str, cls: str):
|
|
super().__init__()
|
|
self._package = pkg
|
|
self._classname = cls
|
|
|
|
def getPackageName(self) -> str:
|
|
return self._package
|
|
|
|
def getClassName(self) -> str:
|
|
return self._classname
|
|
|
|
def getShortClassName(self) -> str:
|
|
return self._classname.split(".")[-1] if "." in self._classname else self._classname
|
|
|
|
def flattenToString(self) -> str:
|
|
return f"{self._package}/{self._classname}"
|
|
|
|
def flattenToShortString(self) -> str:
|
|
return f"{self._package}/{self.getShortClassName()}"
|
|
|
|
@staticmethod
|
|
def unflattenFromString(str_: str) -> Optional["ComponentName"]:
|
|
if "/" in str_:
|
|
parts = str_.split("/", 1)
|
|
return ComponentName(parts[0], parts[1])
|
|
return None
|
|
|
|
def toString(self) -> str:
|
|
return f"ComponentInfo{{{self._package}/{self._classname}}}"
|
|
|
|
def __repr__(self):
|
|
return self.toString()
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# PendingIntent
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class PendingIntent(Stub):
|
|
"""A description of an Intent and target action to perform with it"""
|
|
|
|
FLAG_ONE_SHOT = 1 << 30
|
|
FLAG_NO_CREATE = 1 << 29
|
|
FLAG_CANCEL_CURRENT = 1 << 28
|
|
FLAG_UPDATE_CURRENT = 1 << 27
|
|
FLAG_IMMUTABLE = 1 << 26
|
|
|
|
_pending_intents: Dict[int, "PendingIntent"] = {}
|
|
_next_id = 1000
|
|
|
|
def __init__(self, intent: Intent, request_code: int, flags: int):
|
|
super().__init__()
|
|
self._intent = intent
|
|
self._request_code = request_code
|
|
self._flags = flags
|
|
self._id = PendingIntent._next_id
|
|
PendingIntent._next_id += 1
|
|
PendingIntent._pending_intents[self._id] = self
|
|
|
|
@staticmethod
|
|
def getActivity(context, request_code: int, intent: Intent, flags: int) -> "PendingIntent":
|
|
pi = PendingIntent(intent, request_code, flags)
|
|
pi._type = "activity"
|
|
return pi
|
|
|
|
@staticmethod
|
|
def getBroadcast(context, request_code: int, intent: Intent, flags: int) -> "PendingIntent":
|
|
pi = PendingIntent(intent, request_code, flags)
|
|
pi._type = "broadcast"
|
|
return pi
|
|
|
|
@staticmethod
|
|
def getService(context, request_code: int, intent: Intent, flags: int) -> "PendingIntent":
|
|
pi = PendingIntent(intent, request_code, flags)
|
|
pi._type = "service"
|
|
return pi
|
|
|
|
@staticmethod
|
|
def getActivities(context, request_code: int, intents: List[Intent], flags: int) -> "PendingIntent":
|
|
pi = PendingIntent(intents[0] if intents else Intent(), request_code, flags)
|
|
pi._type = "activities"
|
|
pi._intents = intents
|
|
return pi
|
|
|
|
def send(self):
|
|
"""Simulate sending the pending intent"""
|
|
pass
|
|
|
|
def cancel(self):
|
|
PendingIntent._pending_intents.pop(self._id, None)
|
|
|
|
def getIntent(self) -> Intent:
|
|
return self._intent
|
|
|
|
def getTargetPackage(self) -> str:
|
|
return self._intent.getPackage() or ""
|
|
|
|
def toString(self) -> str:
|
|
return f"PendingIntent{{{hex(id(self))}}}"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# AlarmManager
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class AlarmManager(Stub):
|
|
"""AlarmManager — 调度定时操作"""
|
|
|
|
RTC_WAKEUP = 0
|
|
RTC = 1
|
|
ELAPSED_REALTIME = 3
|
|
ELAPSED_REALTIME_WAKEUP = 2
|
|
INTERVAL_FIFTEEN_MINUTES = 900000
|
|
INTERVAL_HALF_HOUR = 1800000
|
|
INTERVAL_HOUR = 3600000
|
|
INTERVAL_DAY = 86400000
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._alarms: Dict[int, Dict] = {}
|
|
|
|
def set(self, type_: int, trigger_at_ms: int, operation: PendingIntent):
|
|
self._alarms[id(operation)] = {
|
|
"type": type_,
|
|
"trigger_at": trigger_at_ms,
|
|
"operation": operation,
|
|
"repeating": False,
|
|
}
|
|
|
|
def setRepeating(self, type_: int, trigger_at_ms: int, interval_ms: int, operation: PendingIntent):
|
|
self._alarms[id(operation)] = {
|
|
"type": type_,
|
|
"trigger_at": trigger_at_ms,
|
|
"interval": interval_ms,
|
|
"operation": operation,
|
|
"repeating": True,
|
|
}
|
|
|
|
def setExact(self, type_: int, trigger_at_ms: int, operation: PendingIntent):
|
|
self.set(type_, trigger_at_ms, operation)
|
|
|
|
def setAndAllowWhileIdle(self, type_: int, trigger_at_ms: int, operation: PendingIntent):
|
|
self.set(type_, trigger_at_ms, operation)
|
|
|
|
def setExactAndAllowWhileIdle(self, type_: int, trigger_at_ms: int, operation: PendingIntent):
|
|
self.set(type_, trigger_at_ms, operation)
|
|
|
|
def cancel(self, operation: PendingIntent):
|
|
self._alarms.pop(id(operation), None)
|
|
|
|
def getNextAlarmClock(self):
|
|
return None
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# NotificationChannel
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class NotificationChannel(Stub):
|
|
"""Notification channel for Android O+"""
|
|
|
|
IMPORTANCE_NONE = 0
|
|
IMPORTANCE_MIN = 1
|
|
IMPORTANCE_LOW = 2
|
|
IMPORTANCE_DEFAULT = 3
|
|
IMPORTANCE_HIGH = 4
|
|
IMPORTANCE_MAX = 5
|
|
IMPORTANCE_UNSPECIFIED = -1000
|
|
|
|
def __init__(self, channel_id: str, name: str, importance: int):
|
|
super().__init__()
|
|
self._id = channel_id
|
|
self._name = name
|
|
self._importance = importance
|
|
self._description = ""
|
|
self._enabled = True
|
|
self._show_badge = True
|
|
self._sound = None
|
|
self._lights = False
|
|
self._vibration = True
|
|
self._bypass_dnd = False
|
|
self._lockscreen_visibility = 0
|
|
self._group = None
|
|
self._can_bubble = False
|
|
|
|
def getId(self) -> str:
|
|
return self._id
|
|
|
|
def getName(self) -> str:
|
|
return self._name
|
|
|
|
def getImportance(self) -> int:
|
|
return self._importance
|
|
|
|
def setDescription(self, description: str):
|
|
self._description = description
|
|
|
|
def getDescription(self) -> str:
|
|
return self._description
|
|
|
|
def setShowBadge(self, show_badge: bool):
|
|
self._show_badge = show_badge
|
|
|
|
def canShowBadge(self) -> bool:
|
|
return self._show_badge
|
|
|
|
def enableLights(self, lights: bool):
|
|
self._lights = lights
|
|
|
|
def shouldShowLights(self) -> bool:
|
|
return self._lights
|
|
|
|
def enableVibration(self, vibration: bool):
|
|
self._vibration = vibration
|
|
|
|
def shouldVibrate(self) -> bool:
|
|
return self._vibration
|
|
|
|
def setBypassDnd(self, bypass_dnd: bool):
|
|
self._bypass_dnd = bypass_dnd
|
|
|
|
def canBypassDnd(self) -> bool:
|
|
return self._bypass_dnd
|
|
|
|
def setSound(self, sound: Any, audio_attributes: Any = None):
|
|
self._sound = sound
|
|
|
|
def getSound(self):
|
|
return self._sound
|
|
|
|
def setGroup(self, group_id: str):
|
|
self._group = group_id
|
|
|
|
def getGroup(self) -> Optional[str]:
|
|
return self._group
|
|
|
|
def setLockScreenVisibility(self, visibility: int):
|
|
self._lockscreen_visibility = visibility
|
|
|
|
def getLockScreenVisibility(self) -> int:
|
|
return self._lockscreen_visibility
|
|
|
|
def toString(self) -> str:
|
|
return f"NotificationChannel(id={self._id}, name={self._name}, importance={self._importance})"
|
|
|
|
|
|
class NotificationChannelGroup(Stub):
|
|
"""Group of notification channels"""
|
|
|
|
def __init__(self, group_id: str, name: str):
|
|
super().__init__()
|
|
self._id = group_id
|
|
self._name = name
|
|
self._channels: List[NotificationChannel] = []
|
|
|
|
def getId(self) -> str:
|
|
return self._id
|
|
|
|
def getName(self) -> str:
|
|
return self._name
|
|
|
|
def getChannels(self) -> List[NotificationChannel]:
|
|
return list(self._channels)
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# Notification
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class Notification(Stub):
|
|
"""Android Notification"""
|
|
|
|
DEFAULT_ALL = -1
|
|
DEFAULT_SOUND = 1
|
|
DEFAULT_VIBRATE = 2
|
|
DEFAULT_LIGHTS = 4
|
|
EXTRA_TITLE = "android.title"
|
|
EXTRA_TEXT = "android.text"
|
|
EXTRA_SUB_TEXT = "android.subText"
|
|
EXTRA_INFO_TEXT = "android.infoText"
|
|
EXTRA_SUMMARY_TEXT = "android.summaryText"
|
|
EXTRA_CHANNEL_ID = "android.intent.extra.CHANNEL_ID"
|
|
EXTRA_BIG_TEXT = "android.bigText"
|
|
EXTRA_PICTURE = "android.picture"
|
|
PRIORITY_MIN = -2
|
|
PRIORITY_LOW = -1
|
|
PRIORITY_DEFAULT = 0
|
|
PRIORITY_HIGH = 1
|
|
PRIORITY_MAX = 2
|
|
VISIBILITY_PRIVATE = 0
|
|
VISIBILITY_PUBLIC = 1
|
|
VISIBILITY_SECRET = -1
|
|
|
|
class Builder(Stub):
|
|
"""Notification builder pattern"""
|
|
|
|
def __init__(self, context, channel_id: Optional[str] = None):
|
|
super().__init__()
|
|
self._context = context
|
|
self._channel_id = channel_id or ""
|
|
self._title = ""
|
|
self._text = ""
|
|
self._sub_text = ""
|
|
self._info_text = ""
|
|
self._summary_text = ""
|
|
self._big_text = ""
|
|
self._small_icon = 0
|
|
self._large_icon = None
|
|
self._color = 0
|
|
self._priority = Notification.PRIORITY_DEFAULT
|
|
self._defaults = 0
|
|
self._auto_cancel = False
|
|
self._ongoing = False
|
|
self._when = 0
|
|
self._number = 0
|
|
self._content_intent = None
|
|
self._delete_intent = None
|
|
self._full_screen_intent = None
|
|
self._ticker = ""
|
|
self._visibility = Notification.VISIBILITY_PRIVATE
|
|
self._category = ""
|
|
self._group = ""
|
|
self._group_summary = False
|
|
self._public_version = None
|
|
self._extras: Dict[str, Any] = {}
|
|
self._actions: List[Dict] = []
|
|
self._style = None
|
|
self._badge_icon_type = 0
|
|
self._settings_text = None
|
|
self._uses_channel_defaults = False
|
|
|
|
def setChannelId(self, channel_id: str) -> "Notification.Builder":
|
|
self._channel_id = channel_id
|
|
return self
|
|
|
|
def setContentTitle(self, title: str) -> "Notification.Builder":
|
|
self._title = title
|
|
return self
|
|
|
|
def setContentText(self, text: str) -> "Notification.Builder":
|
|
self._text = text
|
|
return self
|
|
|
|
def setSubText(self, text: str) -> "Notification.Builder":
|
|
self._sub_text = text
|
|
return self
|
|
|
|
def setContentInfo(self, info: str) -> "Notification.Builder":
|
|
self._info_text = info
|
|
return self
|
|
|
|
def setSummaryText(self, text: str) -> "Notification.Builder":
|
|
self._summary_text = text
|
|
return self
|
|
|
|
def setSmallIcon(self, icon: int) -> "Notification.Builder":
|
|
self._small_icon = icon
|
|
return self
|
|
|
|
def setLargeIcon(self, icon: Any) -> "Notification.Builder":
|
|
self._large_icon = icon
|
|
return self
|
|
|
|
def setColor(self, color: int) -> "Notification.Builder":
|
|
self._color = color
|
|
return self
|
|
|
|
def setPriority(self, priority: int) -> "Notification.Builder":
|
|
self._priority = priority
|
|
return self
|
|
|
|
def setDefaults(self, defaults: int) -> "Notification.Builder":
|
|
self._defaults = defaults
|
|
return self
|
|
|
|
def setAutoCancel(self, auto_cancel: bool) -> "Notification.Builder":
|
|
self._auto_cancel = auto_cancel
|
|
return self
|
|
|
|
def setOngoing(self, ongoing: bool) -> "Notification.Builder":
|
|
self._ongoing = ongoing
|
|
return self
|
|
|
|
def setWhen(self, when: int) -> "Notification.Builder":
|
|
self._when = when
|
|
return self
|
|
|
|
def setNumber(self, number: int) -> "Notification.Builder":
|
|
self._number = number
|
|
return self
|
|
|
|
def setContentIntent(self, intent: PendingIntent) -> "Notification.Builder":
|
|
self._content_intent = intent
|
|
return self
|
|
|
|
def setDeleteIntent(self, intent: PendingIntent) -> "Notification.Builder":
|
|
self._delete_intent = intent
|
|
return self
|
|
|
|
def setFullScreenIntent(self, intent: PendingIntent, high_priority: bool) -> "Notification.Builder":
|
|
self._full_screen_intent = intent
|
|
return self
|
|
|
|
def setTicker(self, ticker: str) -> "Notification.Builder":
|
|
self._ticker = ticker
|
|
return self
|
|
|
|
def setVisibility(self, visibility: int) -> "Notification.Builder":
|
|
self._visibility = visibility
|
|
return self
|
|
|
|
def setCategory(self, category: str) -> "Notification.Builder":
|
|
self._category = category
|
|
return self
|
|
|
|
def setGroup(self, group: str) -> "Notification.Builder":
|
|
self._group = group
|
|
return self
|
|
|
|
def setGroupSummary(self, is_group_summary: bool) -> "Notification.Builder":
|
|
self._group_summary = is_group_summary
|
|
return self
|
|
|
|
def setPublicVersion(self, notification: "Notification") -> "Notification.Builder":
|
|
self._public_version = notification
|
|
return self
|
|
|
|
def addAction(self, icon: int, title: str, intent: PendingIntent) -> "Notification.Builder":
|
|
self._actions.append({"icon": icon, "title": title, "intent": intent})
|
|
return self
|
|
|
|
def setStyle(self, style: Any) -> "Notification.Builder":
|
|
self._style = style
|
|
return self
|
|
|
|
def setBadgeIconType(self, icon_type: int) -> "Notification.Builder":
|
|
self._badge_icon_type = icon_type
|
|
return self
|
|
|
|
def setSettingsText(self, text: str) -> "Notification.Builder":
|
|
self._settings_text = text
|
|
return self
|
|
|
|
def addExtras(self, extras: Dict[str, Any]) -> "Notification.Builder":
|
|
if extras:
|
|
self._extras.update(extras)
|
|
return self
|
|
|
|
def extend(self, builder: Any) -> "Notification.Builder":
|
|
return self
|
|
|
|
def build(self) -> "Notification":
|
|
n = Notification()
|
|
n._channel_id = self._channel_id
|
|
n._title = self._title
|
|
n._text = self._text
|
|
n._sub_text = self._sub_text
|
|
n._info_text = self._info_text
|
|
n._summary_text = self._summary_text
|
|
n._big_text = self._big_text
|
|
n._small_icon = self._small_icon
|
|
n._large_icon = self._large_icon
|
|
n._color = self._color
|
|
n._priority = self._priority
|
|
n._defaults = self._defaults
|
|
n._auto_cancel = self._auto_cancel
|
|
n._ongoing = self._ongoing
|
|
n._when = self._when or int(time.time() * 1000)
|
|
n._number = self._number
|
|
n._content_intent = self._content_intent
|
|
n._delete_intent = self._delete_intent
|
|
n._full_screen_intent = self._full_screen_intent
|
|
n._ticker = self._ticker
|
|
n._visibility = self._visibility
|
|
n._category = self._category
|
|
n._group = self._group
|
|
n._group_summary = self._group_summary
|
|
n._public_version = self._public_version
|
|
n._actions = list(self._actions)
|
|
n._style = self._style
|
|
n._extras = dict(self._extras)
|
|
return n
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._channel_id = ""
|
|
self._title = ""
|
|
self._text = ""
|
|
self._sub_text = ""
|
|
self._info_text = ""
|
|
self._summary_text = ""
|
|
self._big_text = ""
|
|
self._small_icon = 0
|
|
self._large_icon = None
|
|
self._color = 0
|
|
self._priority = self.PRIORITY_DEFAULT
|
|
self._defaults = 0
|
|
self._auto_cancel = False
|
|
self._ongoing = False
|
|
self._when = 0
|
|
self._number = 0
|
|
self._content_intent = None
|
|
self._delete_intent = None
|
|
self._full_screen_intent = None
|
|
self._ticker = ""
|
|
self._visibility = self.VISIBILITY_PRIVATE
|
|
self._category = ""
|
|
self._group = ""
|
|
self._group_summary = False
|
|
self._public_version = None
|
|
self._extras: Dict[str, Any] = {}
|
|
self._actions: List[Dict] = []
|
|
self._style = None
|
|
|
|
def getChannelId(self) -> str:
|
|
return self._channel_id
|
|
|
|
def toString(self) -> str:
|
|
return f"Notification(channel={self._channel_id}, title={self._title})"
|
|
|
|
|
|
class NotificationCompat(Stub):
|
|
"""NotificationCompat — 兼容版本"""
|
|
|
|
class Builder(Stub):
|
|
def __init__(self, context, channel_id: Optional[str] = None):
|
|
super().__init__()
|
|
self._builder = Notification.Builder(context, channel_id)
|
|
|
|
def setContentTitle(self, title: str) -> "NotificationCompat.Builder":
|
|
self._builder.setContentTitle(title)
|
|
return self
|
|
|
|
def setContentText(self, text: str) -> "NotificationCompat.Builder":
|
|
self._builder.setContentText(text)
|
|
return self
|
|
|
|
def setSmallIcon(self, icon: int) -> "NotificationCompat.Builder":
|
|
self._builder.setSmallIcon(icon)
|
|
return self
|
|
|
|
def setPriority(self, priority: int) -> "NotificationCompat.Builder":
|
|
self._builder.setPriority(priority)
|
|
return self
|
|
|
|
def setAutoCancel(self, auto_cancel: bool) -> "NotificationCompat.Builder":
|
|
self._builder.setAutoCancel(auto_cancel)
|
|
return self
|
|
|
|
def setContentIntent(self, intent: PendingIntent) -> "NotificationCompat.Builder":
|
|
self._builder.setContentIntent(intent)
|
|
return self
|
|
|
|
def setDefaults(self, defaults: int) -> "NotificationCompat.Builder":
|
|
self._builder.setDefaults(defaults)
|
|
return self
|
|
|
|
def build(self) -> Notification:
|
|
return self._builder.build()
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# NotificationManager
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class NotificationManager(Stub):
|
|
"""NotificationManager — 发送通知"""
|
|
|
|
IMPORTANCE_NONE = 0
|
|
IMPORTANCE_MIN = 1
|
|
IMPORTANCE_LOW = 2
|
|
IMPORTANCE_DEFAULT = 3
|
|
IMPORTANCE_HIGH = 4
|
|
IMPORTANCE_MAX = 5
|
|
IMPORTANCE_UNSPECIFIED = -1000
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._notifications: Dict[int, Notification] = {}
|
|
self._channels: Dict[str, NotificationChannel] = {}
|
|
self._groups: Dict[str, NotificationChannelGroup] = {}
|
|
self._notification_id_counter = 0
|
|
|
|
def notify(self, id_: int, notification: Notification):
|
|
self._notifications[id_] = notification
|
|
if _USE_LINUX:
|
|
title = getattr(notification, "_title", "") or ""
|
|
text = getattr(notification, "_text", "") or ""
|
|
if title or text:
|
|
LinuxNotify.send(title, text)
|
|
else:
|
|
sys.stderr.write(
|
|
f"[NotificationManager] notify id={id_}: "
|
|
f"title={notification._title} text={notification._text}\n"
|
|
)
|
|
|
|
def notifyAsPackage(self, package_name: str, tag: Optional[str], id_: int, notification: Notification):
|
|
self.notify(id_, notification)
|
|
|
|
def cancel(self, id_: int):
|
|
self._notifications.pop(id_, None)
|
|
|
|
def cancelAll(self):
|
|
self._notifications.clear()
|
|
|
|
def cancelAsPackage(self, package_name: str, tag: Optional[str], id_: int):
|
|
self.cancel(id_)
|
|
|
|
def createNotificationChannel(self, channel: NotificationChannel):
|
|
self._channels[channel.getId()] = channel
|
|
sys.stderr.write(
|
|
f"[NotificationManager] create channel: {channel.getId()}\n"
|
|
)
|
|
|
|
def createNotificationChannels(self, channels: List[NotificationChannel]):
|
|
for c in channels:
|
|
self.createNotificationChannel(c)
|
|
|
|
def getNotificationChannel(self, channel_id: str) -> Optional[NotificationChannel]:
|
|
return self._channels.get(channel_id)
|
|
|
|
def getNotificationChannels(self) -> List[NotificationChannel]:
|
|
return list(self._channels.values())
|
|
|
|
def deleteNotificationChannel(self, channel_id: str):
|
|
self._channels.pop(channel_id, None)
|
|
|
|
def createNotificationChannelGroup(self, group: NotificationChannelGroup):
|
|
self._groups[group.getId()] = group
|
|
|
|
def getNotificationChannelGroups(self) -> List[NotificationChannelGroup]:
|
|
return list(self._groups.values())
|
|
|
|
def getActiveNotifications(self) -> List[Notification]:
|
|
return list(self._notifications.values())
|
|
|
|
def areNotificationsEnabled(self) -> bool:
|
|
return True
|
|
|
|
def isNotificationPolicyAccessGranted(self) -> bool:
|
|
return True
|
|
|
|
def matchesCallFilter(self, address: Any) -> bool:
|
|
return True
|
|
|
|
def toString(self) -> str:
|
|
return f"NotificationManager(notifications={len(self._notifications)})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# Dialog / AlertDialog / ProgressDialog / DatePickerDialog / TimePickerDialog
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class Dialog(Stub):
|
|
"""Base dialog class"""
|
|
|
|
def __init__(self, context, theme_res_id: int = 0):
|
|
super().__init__()
|
|
self._context = context
|
|
self._theme_res_id = theme_res_id
|
|
self._showing = False
|
|
self._cancelable = True
|
|
self._on_cancel_listener = None
|
|
self._on_dismiss_listener = None
|
|
self._on_show_listener = None
|
|
self._title = ""
|
|
self._window = None
|
|
|
|
def show(self):
|
|
self._showing = True
|
|
sys.stderr.write(f"[Dialog] show: {self.__class__.__name__}\n")
|
|
|
|
def dismiss(self):
|
|
self._showing = False
|
|
sys.stderr.write(f"[Dialog] dismiss: {self.__class__.__name__}\n")
|
|
|
|
def isShowing(self) -> bool:
|
|
return self._showing
|
|
|
|
def cancel(self):
|
|
self.dismiss()
|
|
if self._on_cancel_listener:
|
|
self._on_cancel_listener.onCancel(self)
|
|
|
|
def setCancelable(self, flag: bool):
|
|
self._cancelable = flag
|
|
|
|
def setCanceledOnTouchOutside(self, cancel: bool):
|
|
pass
|
|
|
|
def setTitle(self, title: str):
|
|
self._title = title
|
|
|
|
def getContext(self):
|
|
return self._context
|
|
|
|
def setOnCancelListener(self, listener):
|
|
self._on_cancel_listener = listener
|
|
|
|
def setOnDismissListener(self, listener):
|
|
self._on_dismiss_listener = listener
|
|
|
|
def setOnShowListener(self, listener):
|
|
self._on_show_listener = listener
|
|
|
|
def getWindow(self):
|
|
return self._window
|
|
|
|
def findViewById(self, id_: int):
|
|
return None
|
|
|
|
def onBackPressed(self):
|
|
if self._cancelable:
|
|
self.cancel()
|
|
|
|
|
|
class AlertDialog(Dialog):
|
|
"""Alert dialog with builder pattern"""
|
|
|
|
BUTTON_POSITIVE = -1
|
|
BUTTON_NEGATIVE = -2
|
|
BUTTON_NEUTRAL = -3
|
|
|
|
def __init__(self, context, theme_res_id: int = 0):
|
|
super().__init__(context, theme_res_id)
|
|
self._message = ""
|
|
self._buttons: Dict[int, Dict] = {}
|
|
self._items: List[str] = []
|
|
self._single_choice_items = None
|
|
self._multi_choice_items = None
|
|
self._selected_index = -1
|
|
self._selected_indices: List[int] = []
|
|
self._view = None
|
|
self._icon = 0
|
|
self._custom_title = None
|
|
|
|
class Builder(Stub):
|
|
"""AlertDialog builder"""
|
|
def __init__(self, context, theme_res_id: int = 0):
|
|
super().__init__()
|
|
self._context = context
|
|
self._theme_res_id = theme_res_id
|
|
self._title = ""
|
|
self._message = ""
|
|
self._positive_text = ""
|
|
self._positive_listener = None
|
|
self._negative_text = ""
|
|
self._negative_listener = None
|
|
self._neutral_text = ""
|
|
self._neutral_listener = None
|
|
self._cancelable = True
|
|
self._on_cancel_listener = None
|
|
self._items: List[str] = []
|
|
self._on_item_click_listener = None
|
|
self._single_choice_items = None
|
|
self._single_choice_selected = -1
|
|
self._single_choice_listener = None
|
|
self._multi_choice_items = None
|
|
self._multi_choice_selected = None
|
|
self._multi_choice_listener = None
|
|
self._icon = 0
|
|
self._view = None
|
|
self._custom_title_view = None
|
|
|
|
def setTitle(self, title: str) -> "AlertDialog.Builder":
|
|
self._title = title
|
|
return self
|
|
|
|
def setMessage(self, message: str) -> "AlertDialog.Builder":
|
|
self._message = message
|
|
return self
|
|
|
|
def setPositiveButton(self, text: str, listener=None) -> "AlertDialog.Builder":
|
|
self._positive_text = text
|
|
self._positive_listener = listener
|
|
return self
|
|
|
|
def setNegativeButton(self, text: str, listener=None) -> "AlertDialog.Builder":
|
|
self._negative_text = text
|
|
self._negative_listener = listener
|
|
return self
|
|
|
|
def setNeutralButton(self, text: str, listener=None) -> "AlertDialog.Builder":
|
|
self._neutral_text = text
|
|
self._neutral_listener = listener
|
|
return self
|
|
|
|
def setCancelable(self, cancelable: bool) -> "AlertDialog.Builder":
|
|
self._cancelable = cancelable
|
|
return self
|
|
|
|
def setOnCancelListener(self, listener) -> "AlertDialog.Builder":
|
|
self._on_cancel_listener = listener
|
|
return self
|
|
|
|
def setItems(self, items: List[str], listener=None) -> "AlertDialog.Builder":
|
|
self._items = list(items)
|
|
self._on_item_click_listener = listener
|
|
return self
|
|
|
|
def setSingleChoiceItems(self, items: List[str], checked_item: int, listener=None) -> "AlertDialog.Builder":
|
|
self._single_choice_items = list(items)
|
|
self._single_choice_selected = checked_item
|
|
self._single_choice_listener = listener
|
|
return self
|
|
|
|
def setMultiChoiceItems(self, items: List[str], checked_items: List[bool], listener=None) -> "AlertDialog.Builder":
|
|
self._multi_choice_items = list(items)
|
|
self._multi_choice_selected = checked_items or []
|
|
self._multi_choice_listener = listener
|
|
return self
|
|
|
|
def setIcon(self, icon: int) -> "AlertDialog.Builder":
|
|
self._icon = icon
|
|
return self
|
|
|
|
def setView(self, view: Any) -> "AlertDialog.Builder":
|
|
self._view = view
|
|
return self
|
|
|
|
def setCustomTitle(self, view: Any) -> "AlertDialog.Builder":
|
|
self._custom_title_view = view
|
|
return self
|
|
|
|
def create(self) -> "AlertDialog":
|
|
dialog = AlertDialog(self._context, self._theme_res_id)
|
|
dialog._title = self._title
|
|
dialog._message = self._message
|
|
dialog._cancelable = self._cancelable
|
|
dialog._icon = self._icon
|
|
dialog._view = self._view
|
|
if self._positive_text:
|
|
dialog._buttons[AlertDialog.BUTTON_POSITIVE] = {
|
|
"text": self._positive_text,
|
|
"listener": self._positive_listener,
|
|
}
|
|
if self._negative_text:
|
|
dialog._buttons[AlertDialog.BUTTON_NEGATIVE] = {
|
|
"text": self._negative_text,
|
|
"listener": self._negative_listener,
|
|
}
|
|
if self._neutral_text:
|
|
dialog._buttons[AlertDialog.BUTTON_NEUTRAL] = {
|
|
"text": self._neutral_text,
|
|
"listener": self._neutral_listener,
|
|
}
|
|
dialog._items = list(self._items)
|
|
dialog._single_choice_items = self._single_choice_items
|
|
dialog._multi_choice_items = self._multi_choice_items
|
|
return dialog
|
|
|
|
def show(self) -> "AlertDialog":
|
|
dialog = self.create()
|
|
dialog.show()
|
|
return dialog
|
|
|
|
|
|
class ProgressDialog(AlertDialog):
|
|
"""Progress dialog with a progress bar"""
|
|
|
|
STYLE_SPINNER = 0
|
|
STYLE_HORIZONTAL = 1
|
|
|
|
def __init__(self, context, theme_res_id: int = 0):
|
|
super().__init__(context, theme_res_id)
|
|
self._progress_style = ProgressDialog.STYLE_SPINNER
|
|
self._progress = 0
|
|
self._max = 100
|
|
self._indeterminate = True
|
|
self._progress_number_format = "%d/%d"
|
|
self._progress_percent_format = "%d%%"
|
|
|
|
def setProgressStyle(self, style: int):
|
|
self._progress_style = style
|
|
|
|
def setProgress(self, value: int):
|
|
self._progress = value
|
|
|
|
def getProgress(self) -> int:
|
|
return self._progress
|
|
|
|
def setMax(self, max_: int):
|
|
self._max = max_
|
|
|
|
def getMax(self) -> int:
|
|
return self._max
|
|
|
|
def setIndeterminate(self, indeterminate: bool):
|
|
self._indeterminate = indeterminate
|
|
|
|
def isIndeterminate(self) -> bool:
|
|
return self._indeterminate
|
|
|
|
def incrementProgressBy(self, diff: int):
|
|
self._progress = min(self._max, max(0, self._progress + diff))
|
|
|
|
@staticmethod
|
|
def show(context, title: str, message: str, indeterminate: bool = True, cancelable: bool = False) -> "ProgressDialog":
|
|
dialog = ProgressDialog(context)
|
|
dialog.setTitle(title)
|
|
dialog._message = message
|
|
dialog._indeterminate = indeterminate
|
|
dialog.setCancelable(cancelable)
|
|
dialog.show()
|
|
return dialog
|
|
|
|
|
|
class DatePickerDialog(AlertDialog):
|
|
"""Date picker dialog"""
|
|
|
|
def __init__(self, context, listener=None, year: int = 2024, month: int = 0, day_of_month: int = 1, theme_res_id: int = 0):
|
|
super().__init__(context, theme_res_id)
|
|
self._listener = listener
|
|
self._year = year
|
|
self._month = month
|
|
self._day_of_month = day_of_month
|
|
self._date_picker = None
|
|
|
|
def getDatePicker(self):
|
|
return self._date_picker
|
|
|
|
def updateDate(self, year: int, month: int, day_of_month: int):
|
|
self._year = year
|
|
self._month = month
|
|
self._day_of_month = day_of_month
|
|
|
|
def onDateChanged(self, view, year: int, month: int, day: int):
|
|
self._year = year
|
|
self._month = month
|
|
self._day_of_month = day
|
|
|
|
class OnDateSetListener:
|
|
def onDateSet(self, view, year: int, month: int, day_of_month: int):
|
|
pass
|
|
|
|
|
|
class TimePickerDialog(AlertDialog):
|
|
"""Time picker dialog"""
|
|
|
|
def __init__(self, context, listener=None, hour_of_day: int = 12, minute: int = 0, is24_hour_view: bool = True, theme_res_id: int = 0):
|
|
super().__init__(context, theme_res_id)
|
|
self._listener = listener
|
|
self._hour_of_day = hour_of_day
|
|
self._minute = minute
|
|
self._is24_hour_view = is24_hour_view
|
|
|
|
class OnTimeSetListener:
|
|
def onTimeSet(self, view, hour_of_day: int, minute: int):
|
|
pass
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# Fragment / FragmentManager / FragmentTransaction
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class Fragment(Stub):
|
|
"""Fragment — UI 片段"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._arguments: Optional[Bundle] = None
|
|
self._activity = None
|
|
self._view = None
|
|
self._fragment_manager = None
|
|
self._state = 0
|
|
self._tag = None
|
|
self._id = -1
|
|
self._container_id = -1
|
|
self._menu = True
|
|
self._retain_instance = False
|
|
self._target_fragment = None
|
|
self._target_request_code = -1
|
|
self._user_visible_hint = True
|
|
|
|
def setArguments(self, args: "Bundle"):
|
|
self._arguments = args
|
|
|
|
def getArguments(self) -> Optional["Bundle"]:
|
|
return self._arguments
|
|
|
|
def getActivity(self):
|
|
return self._activity
|
|
|
|
def getContext(self):
|
|
return self._activity
|
|
|
|
def getFragmentManager(self) -> Optional["FragmentManager"]:
|
|
return self._fragment_manager
|
|
|
|
def getView(self):
|
|
return self._view
|
|
|
|
def getResources(self):
|
|
if self._activity:
|
|
return getattr(self._activity, 'getResources', lambda: None)()
|
|
return None
|
|
|
|
def getString(self, res_id: int, *args) -> str:
|
|
return str(res_id)
|
|
|
|
def getTag(self) -> Optional[str]:
|
|
return self._tag
|
|
|
|
def setId(self, id_: int):
|
|
self._id = id_
|
|
|
|
def getId(self) -> int:
|
|
return self._id
|
|
|
|
def onAttach(self, context):
|
|
pass
|
|
|
|
def onCreate(self, savedInstanceState: Optional["Bundle"] = None):
|
|
pass
|
|
|
|
def onCreateView(self, inflater, container, savedInstanceState):
|
|
return None
|
|
|
|
def onViewCreated(self, view, savedInstanceState: Optional["Bundle"] = None):
|
|
pass
|
|
|
|
def onStart(self):
|
|
pass
|
|
|
|
def onResume(self):
|
|
pass
|
|
|
|
def onPause(self):
|
|
pass
|
|
|
|
def onStop(self):
|
|
pass
|
|
|
|
def onDestroyView(self):
|
|
pass
|
|
|
|
def onDestroy(self):
|
|
pass
|
|
|
|
def onDetach(self):
|
|
pass
|
|
|
|
def onActivityCreated(self, savedInstanceState: Optional["Bundle"] = None):
|
|
pass
|
|
|
|
def onSaveInstanceState(self, out_state: "Bundle"):
|
|
pass
|
|
|
|
def onViewStateRestored(self, savedInstanceState: Optional["Bundle"] = None):
|
|
pass
|
|
|
|
def isAdded(self) -> bool:
|
|
return self._activity is not None
|
|
|
|
def isDetached(self) -> bool:
|
|
return self._activity is None
|
|
|
|
def isVisible(self) -> bool:
|
|
return False
|
|
|
|
def isRemoving(self) -> bool:
|
|
return False
|
|
|
|
def isInLayout(self) -> bool:
|
|
return False
|
|
|
|
def isResumed(self) -> bool:
|
|
return self._state >= 7
|
|
|
|
def setHasOptionsMenu(self, has_menu: bool):
|
|
self._menu = has_menu
|
|
|
|
def setRetainInstance(self, retain: bool):
|
|
self._retain_instance = retain
|
|
|
|
def getRetainInstance(self) -> bool:
|
|
return self._retain_instance
|
|
|
|
def setTargetFragment(self, fragment: "Fragment", request_code: int):
|
|
self._target_fragment = fragment
|
|
self._target_request_code = request_code
|
|
|
|
def getTargetFragment(self) -> Optional["Fragment"]:
|
|
return self._target_fragment
|
|
|
|
def getTargetRequestCode(self) -> int:
|
|
return self._target_request_code
|
|
|
|
def setUserVisibleHint(self, is_visible_to_user: bool):
|
|
self._user_visible_hint = is_visible_to_user
|
|
|
|
def getUserVisibleHint(self) -> bool:
|
|
return self._user_visible_hint
|
|
|
|
def registerForContextMenu(self, view):
|
|
pass
|
|
|
|
def unregisterForContextMenu(self, view):
|
|
pass
|
|
|
|
|
|
class FragmentTransaction(Stub):
|
|
"""FragmentTransaction — 管理 Fragment 事务"""
|
|
|
|
TRANSIT_ENTER_MASK = 0x1000
|
|
TRANSIT_EXIT_MASK = 0x2000
|
|
TRANSIT_FRAGMENT_FADE = 0x1001
|
|
TRANSIT_FRAGMENT_OPEN = 0x1003
|
|
TRANSIT_FRAGMENT_CLOSE = 0x1004
|
|
TRANSIT_NONE = 0
|
|
TRANSIT_UNSET = -1
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._ops: List[Dict] = []
|
|
self._transition = FragmentTransaction.TRANSIT_NONE
|
|
self._transition_style = 0
|
|
self._allow_state_loss = False
|
|
self._commit_allowing_state_loss = False
|
|
|
|
def add(self, container_view_id: int, fragment: Fragment, tag: Optional[str] = None) -> "FragmentTransaction":
|
|
self._ops.append({"type": "add", "id": container_view_id, "fragment": fragment, "tag": tag})
|
|
return self
|
|
|
|
def replace(self, container_view_id: int, fragment: Fragment, tag: Optional[str] = None) -> "FragmentTransaction":
|
|
self._ops.append({"type": "replace", "id": container_view_id, "fragment": fragment, "tag": tag})
|
|
return self
|
|
|
|
def remove(self, fragment: Fragment) -> "FragmentTransaction":
|
|
self._ops.append({"type": "remove", "fragment": fragment})
|
|
return self
|
|
|
|
def hide(self, fragment: Fragment) -> "FragmentTransaction":
|
|
self._ops.append({"type": "hide", "fragment": fragment})
|
|
return self
|
|
|
|
def show(self, fragment: Fragment) -> "FragmentTransaction":
|
|
self._ops.append({"type": "show", "fragment": fragment})
|
|
return self
|
|
|
|
def detach(self, fragment: Fragment) -> "FragmentTransaction":
|
|
self._ops.append({"type": "detach", "fragment": fragment})
|
|
return self
|
|
|
|
def attach(self, fragment: Fragment) -> "FragmentTransaction":
|
|
self._ops.append({"type": "attach", "fragment": fragment})
|
|
return self
|
|
|
|
def addToBackStack(self, name: Optional[str] = None) -> "FragmentTransaction":
|
|
self._ops.append({"type": "addToBackStack", "name": name})
|
|
return self
|
|
|
|
def setTransition(self, transit: int) -> "FragmentTransaction":
|
|
self._transition = transit
|
|
return self
|
|
|
|
def setTransitionStyle(self, style: int) -> "FragmentTransaction":
|
|
self._transition_style = style
|
|
return self
|
|
|
|
def setCustomAnimations(self, enter: int, exit: int, pop_enter: int = 0, pop_exit: int = 0) -> "FragmentTransaction":
|
|
return self
|
|
|
|
def disallowAddToBackStack(self) -> "FragmentTransaction":
|
|
return self
|
|
|
|
def setBreadCrumbShortTitle(self, title: str) -> "FragmentTransaction":
|
|
return self
|
|
|
|
def setBreadCrumbTitle(self, title: str) -> "FragmentTransaction":
|
|
return self
|
|
|
|
def commit(self) -> int:
|
|
return 0
|
|
|
|
def commitAllowingStateLoss(self) -> int:
|
|
return 0
|
|
|
|
def commitNow(self) -> int:
|
|
return 0
|
|
|
|
def commitNowAllowingStateLoss(self) -> int:
|
|
return 0
|
|
|
|
def isAddToBackStackAllowed(self) -> bool:
|
|
return True
|
|
|
|
|
|
class FragmentManager(Stub):
|
|
"""FragmentManager — Fragment 管理器"""
|
|
|
|
POP_BACK_STACK_INCLUSIVE = 1
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._fragments: Dict[str, Fragment] = {}
|
|
self._back_stack: List[Dict] = []
|
|
|
|
def beginTransaction(self) -> FragmentTransaction:
|
|
return FragmentTransaction()
|
|
|
|
def findFragmentById(self, id_: int) -> Optional[Fragment]:
|
|
for f in self._fragments.values():
|
|
if f.getId() == id_:
|
|
return f
|
|
return None
|
|
|
|
def findFragmentByTag(self, tag: str) -> Optional[Fragment]:
|
|
return self._fragments.get(tag)
|
|
|
|
def popBackStack(self):
|
|
if self._back_stack:
|
|
self._back_stack.pop()
|
|
|
|
def popBackStackImmediate(self, name: str, flags: int) -> bool:
|
|
return True
|
|
|
|
def getBackStackEntryCount(self) -> int:
|
|
return len(self._back_stack)
|
|
|
|
def getFragment(self, bundle: "Bundle", key: str) -> Optional[Fragment]:
|
|
return None
|
|
|
|
def getFragments(self) -> List[Fragment]:
|
|
return list(self._fragments.values())
|
|
|
|
def saveFragmentInstanceState(self, fragment: Fragment):
|
|
return None
|
|
|
|
def isDestroyed(self) -> bool:
|
|
return False
|
|
|
|
def isStateSaved(self) -> bool:
|
|
return False
|
|
|
|
def putFragment(self, bundle: "Bundle", key: str, fragment: Fragment):
|
|
pass
|
|
|
|
def executePendingTransactions(self) -> bool:
|
|
return True
|
|
|
|
def clearFragmentResult(self, request_key: str):
|
|
pass
|
|
|
|
def setFragmentResult(self, request_key: str, result: "Bundle"):
|
|
pass
|
|
|
|
def setFragmentResultListener(self, lifecycle: Any, listener: Any):
|
|
pass
|
|
|
|
def registerFragmentLifecycleCallbacks(self, cb: Any, recursive: bool):
|
|
pass
|
|
|
|
def unregisterFragmentLifecycleCallbacks(self, cb: Any):
|
|
pass
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# Activity
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class Activity(Stub):
|
|
"""Activity — 应用界面入口"""
|
|
|
|
# 结果码
|
|
RESULT_OK = -1
|
|
RESULT_CANCELED = 0
|
|
RESULT_FIRST_USER = 1
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._created = False
|
|
self._started = False
|
|
self._resumed = False
|
|
self._destroyed = False
|
|
self._intent: Optional[Intent] = None
|
|
self._callbacks: Dict[int, Tuple[int, Intent]] = {}
|
|
self._fragment_manager = FragmentManager()
|
|
self._application = None
|
|
self._title = ""
|
|
self._request_code_counter = 0
|
|
|
|
def onCreate(self, savedInstanceState: Optional["Bundle"] = None):
|
|
self._created = True
|
|
sys.stderr.write(f"[Activity] onCreate: {self.__class__.__name__}\n")
|
|
|
|
def onStart(self):
|
|
self._started = True
|
|
sys.stderr.write(f"[Activity] onStart: {self.__class__.__name__}\n")
|
|
|
|
def onResume(self):
|
|
self._resumed = True
|
|
sys.stderr.write(f"[Activity] onResume: {self.__class__.__name__}\n")
|
|
|
|
def onPause(self):
|
|
self._resumed = False
|
|
sys.stderr.write(f"[Activity] onPause: {self.__class__.__name__}\n")
|
|
|
|
def onStop(self):
|
|
self._started = False
|
|
sys.stderr.write(f"[Activity] onStop: {self.__class__.__name__}\n")
|
|
|
|
def onDestroy(self):
|
|
self._destroyed = True
|
|
self._created = False
|
|
sys.stderr.write(f"[Activity] onDestroy: {self.__class__.__name__}\n")
|
|
|
|
def onRestart(self):
|
|
sys.stderr.write(f"[Activity] onRestart: {self.__class__.__name__}\n")
|
|
|
|
def onBackPressed(self):
|
|
sys.stderr.write("[Activity] onBackPressed\n")
|
|
|
|
def onSaveInstanceState(self, out_state: "Bundle"):
|
|
pass
|
|
|
|
def onRestoreInstanceState(self, savedInstanceState: "Bundle"):
|
|
pass
|
|
|
|
def onNewIntent(self, intent: Intent):
|
|
self._intent = intent
|
|
|
|
def onActivityResult(self, request_code: int, result_code: int, data: Optional[Intent]):
|
|
pass
|
|
|
|
def onRequestPermissionsResult(self, request_code: int, permissions: List[str], grant_results: List[int]):
|
|
pass
|
|
|
|
def setContentView(self, layout_res_id: int):
|
|
sys.stderr.write(f"[Activity] setContentView: {layout_res_id}\n")
|
|
|
|
def findViewById(self, id_: int) -> Any:
|
|
return None
|
|
|
|
def startActivity(self, intent: Intent):
|
|
sys.stderr.write(
|
|
f"[Activity] startActivity: action={intent.getAction()} "
|
|
f"pkg={intent.getPackage()}\n"
|
|
)
|
|
|
|
def startActivityForResult(self, intent: Intent, request_code: int, options: Optional["Bundle"] = None):
|
|
sys.stderr.write(
|
|
f"[Activity] startActivityForResult: code={request_code} "
|
|
f"action={intent.getAction()}\n"
|
|
)
|
|
|
|
def setResult(self, result_code: int, data: Optional[Intent] = None):
|
|
sys.stderr.write(f"[Activity] setResult: code={result_code}\n")
|
|
|
|
def finish(self):
|
|
sys.stderr.write(f"[Activity] finish: {self.__class__.__name__}\n")
|
|
self.onPause()
|
|
self.onStop()
|
|
self.onDestroy()
|
|
|
|
def finishActivity(self, request_code: int):
|
|
pass
|
|
|
|
def getIntent(self) -> Optional[Intent]:
|
|
return self._intent
|
|
|
|
def setIntent(self, intent: Intent):
|
|
self._intent = intent
|
|
|
|
def getString(self, res_id: int, *args) -> str:
|
|
return str(res_id)
|
|
|
|
def getResources(self):
|
|
return None
|
|
|
|
def getApplication(self):
|
|
return self._application
|
|
|
|
def getFragmentManager(self) -> FragmentManager:
|
|
return self._fragment_manager
|
|
|
|
def getSupportFragmentManager(self) -> FragmentManager:
|
|
return self._fragment_manager
|
|
|
|
def getPackageManager(self):
|
|
from .content_pm import PackageManager
|
|
return PackageManager()
|
|
|
|
def getPackageName(self) -> str:
|
|
return _package_name
|
|
|
|
def getContentResolver(self):
|
|
from .content import ContentResolver
|
|
return ContentResolver()
|
|
|
|
def getSystemService(self, name: str):
|
|
return android_context.getSystemService(name)
|
|
|
|
def isFinishing(self) -> bool:
|
|
return self._destroyed
|
|
|
|
def isDestroyed(self) -> bool:
|
|
return self._destroyed
|
|
|
|
def recreate(self):
|
|
self.onDestroy()
|
|
self.onCreate(None)
|
|
self.onStart()
|
|
self.onResume()
|
|
|
|
def getPreferences(self, mode: int):
|
|
from .content import SharedPreferences
|
|
return SharedPreferences(_package_name + "_preferences")
|
|
|
|
def getSharedPreferences(self, name: str, mode: int):
|
|
from .content import SharedPreferences
|
|
return SharedPreferences(name)
|
|
|
|
def getApplicationContext(self):
|
|
return android_context
|
|
|
|
def getBaseContext(self):
|
|
return android_context
|
|
|
|
def getCallingActivity(self) -> Optional[ComponentName]:
|
|
return None
|
|
|
|
def getCallingPackage(self) -> Optional[str]:
|
|
return None
|
|
|
|
def getTaskId(self) -> int:
|
|
return 1
|
|
|
|
def hasWindowFocus(self) -> bool:
|
|
return True
|
|
|
|
def isInMultiWindowMode(self) -> bool:
|
|
return False
|
|
|
|
def isInPictureInPictureMode(self) -> bool:
|
|
return False
|
|
|
|
def moveTaskToBack(self, non_root: bool) -> bool:
|
|
return True
|
|
|
|
def onLowMemory(self):
|
|
pass
|
|
|
|
def onTrimMemory(self, level: int):
|
|
pass
|
|
|
|
def onConfigurationChanged(self, config):
|
|
pass
|
|
|
|
def onMultiWindowModeChanged(self, is_in_multi_window_mode: bool, new_config):
|
|
pass
|
|
|
|
def onPictureInPictureModeChanged(self, is_in_pip_mode: bool, new_config):
|
|
pass
|
|
|
|
# ─── 生命周期 (扩展) ───────────────────────────────────────
|
|
|
|
def onPostCreate(self, savedInstanceState):
|
|
pass
|
|
|
|
def onPostResume(self):
|
|
pass
|
|
|
|
def onUserLeaveHint(self):
|
|
pass
|
|
|
|
def onUserInteraction(self):
|
|
pass
|
|
|
|
def onCreateContextMenu(self, menu, v, menuInfo):
|
|
pass
|
|
|
|
def onContextMenuClosed(self, menu):
|
|
pass
|
|
|
|
def onContextItemSelected(self, item):
|
|
return False
|
|
|
|
def onWindowFocusChanged(self, hasFocus):
|
|
pass
|
|
|
|
def onWindowAttributesChanged(self, params):
|
|
pass
|
|
|
|
def onContentChanged(self):
|
|
pass
|
|
|
|
def onAttachedToWindow(self):
|
|
pass
|
|
|
|
def onDetachedFromWindow(self):
|
|
pass
|
|
|
|
def onTitleChanged(self, title, color):
|
|
pass
|
|
|
|
# ─── 权限 (扩展) ───────────────────────────────────────────
|
|
|
|
def requestPermissions(self, permissions, requestCode):
|
|
pass
|
|
|
|
def checkSelfPermission(self, permission):
|
|
return 0 # PERMISSION_GRANTED
|
|
|
|
def shouldShowRequestPermissionRationale(self, permission):
|
|
return False
|
|
|
|
# ─── UI (扩展) ─────────────────────────────────────────────
|
|
|
|
def getWindow(self):
|
|
if not hasattr(self, '_window'):
|
|
from .view import Window
|
|
self._window = Window()
|
|
return self._window
|
|
|
|
def getWindowManager(self):
|
|
if not hasattr(self, '_window_manager'):
|
|
from .view import WindowManager
|
|
self._window_manager = WindowManager()
|
|
return self._window_manager
|
|
|
|
def setRequestedOrientation(self, requestedOrientation):
|
|
self._requested_orientation = requestedOrientation
|
|
|
|
def getRequestedOrientation(self):
|
|
return getattr(self, '_requested_orientation', 0)
|
|
|
|
def runOnUiThread(self, action):
|
|
import threading
|
|
# 在桌面环境直接执行
|
|
action()
|
|
|
|
def overridePendingTransition(self, enterAnim, exitAnim):
|
|
pass
|
|
|
|
def getActionBar(self):
|
|
return getattr(self, '_action_bar', None)
|
|
|
|
def setActionBar(self, toolbar):
|
|
self._action_bar = toolbar
|
|
|
|
def getMenuInflater(self):
|
|
from .view import MenuInflater
|
|
return MenuInflater()
|
|
|
|
def onCreateOptionsMenu(self, menu):
|
|
return True
|
|
|
|
def onPrepareOptionsMenu(self, menu):
|
|
return True
|
|
|
|
def onOptionsItemSelected(self, item):
|
|
return False
|
|
|
|
def registerForContextMenu(self, view):
|
|
pass
|
|
|
|
def unregisterForContextMenu(self, view):
|
|
pass
|
|
|
|
def openContextMenu(self, view):
|
|
pass
|
|
|
|
def finishAffinity(self):
|
|
self.finish()
|
|
|
|
def invalidateOptionsMenu(self):
|
|
pass
|
|
|
|
def setVisible(self, visible):
|
|
self._visible = visible
|
|
|
|
def setTheme(self, resid):
|
|
self._theme = resid
|
|
|
|
def getTheme(self):
|
|
return getattr(self, '_theme', 0)
|
|
|
|
def getDeviceInfo(self):
|
|
"""获取设备信息"""
|
|
if _USE_LINUX:
|
|
return LinuxProcess.get_system_info()
|
|
return {}
|
|
|
|
def executeShell(self, command):
|
|
"""执行 Shell 命令"""
|
|
if _USE_LINUX:
|
|
return LinuxShell.exec(command)
|
|
return {"success": False, "output": "Shell not available"}
|
|
|
|
def screenshot(self, path=None):
|
|
"""截屏"""
|
|
if _USE_LINUX:
|
|
return LinuxDisplay.screenshot(path)
|
|
return None
|
|
|
|
def getScreenSize(self):
|
|
"""获取屏幕尺寸"""
|
|
if _USE_LINUX:
|
|
return LinuxDisplay.get_screen_size()
|
|
return (1920, 1080)
|
|
|
|
def click(self, x, y):
|
|
"""点击屏幕坐标"""
|
|
if _USE_LINUX:
|
|
return LinuxUIAutomation.click(x, y)
|
|
return False
|
|
|
|
def typeText(self, text):
|
|
"""输入文本"""
|
|
if _USE_LINUX:
|
|
return LinuxUIAutomation.type_text(text)
|
|
return False
|
|
|
|
def pressKey(self, key):
|
|
"""按键"""
|
|
if _USE_LINUX:
|
|
return LinuxUIAutomation.press_key(key)
|
|
return False
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# Service
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class Service(Stub):
|
|
"""Service — 后台服务"""
|
|
|
|
START_CONTINUATION_MASK = 0xF
|
|
START_FLAG_REDELIVERY = 1
|
|
START_FLAG_RETRY = 2
|
|
START_NOT_STICKY = 2
|
|
START_REDELIVER_INTENT = 3
|
|
START_STICKY = 1
|
|
START_STICKY_COMPATIBILITY = 0
|
|
STOP_FOREGROUND_DETACH = 1
|
|
STOP_FOREGROUND_REMOVE = 2
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._started = False
|
|
|
|
def onCreate(self):
|
|
self._started = True
|
|
sys.stderr.write(f"[Service] onCreate: {self.__class__.__name__}\n")
|
|
|
|
def onStartCommand(self, intent: Optional[Intent], flags: int, start_id: int) -> int:
|
|
return Service.START_NOT_STICKY
|
|
|
|
def onBind(self, intent: Intent):
|
|
return None
|
|
|
|
def onUnbind(self, intent: Intent) -> bool:
|
|
return False
|
|
|
|
def onRebind(self, intent: Intent):
|
|
pass
|
|
|
|
def onDestroy(self):
|
|
self._started = False
|
|
sys.stderr.write(f"[Service] onDestroy: {self.__class__.__name__}\n")
|
|
|
|
def stopSelf(self):
|
|
self.onDestroy()
|
|
|
|
def stopSelfResult(self, start_id: int) -> bool:
|
|
self.onDestroy()
|
|
return True
|
|
|
|
def startForeground(self, id_: int, notification: Notification):
|
|
pass
|
|
|
|
def stopForeground(self, flags: int):
|
|
pass
|
|
|
|
def getApplication(self):
|
|
return None
|
|
|
|
def getClass(self):
|
|
return self.__class__
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# Application
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class Application(Stub):
|
|
"""Application — 应用级别上下文"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._activity_lifecycle_callbacks: List = []
|
|
self._on_low_memory_listeners: List = []
|
|
self._on_trim_memory_listeners: List = []
|
|
|
|
def onCreate(self):
|
|
sys.stderr.write(f"[Application] onCreate: {self.__class__.__name__}\n")
|
|
|
|
def onTerminate(self):
|
|
pass
|
|
|
|
def onConfigurationChanged(self, new_config):
|
|
pass
|
|
|
|
def onLowMemory(self):
|
|
pass
|
|
|
|
def onTrimMemory(self, level: int):
|
|
pass
|
|
|
|
def registerActivityLifecycleCallbacks(self, callback):
|
|
self._activity_lifecycle_callbacks.append(callback)
|
|
|
|
def unregisterActivityLifecycleCallbacks(self, callback):
|
|
try:
|
|
self._activity_lifecycle_callbacks.remove(callback)
|
|
except ValueError:
|
|
pass
|
|
|
|
def getApplicationContext(self):
|
|
return android_context
|
|
|
|
def getPackageManager(self):
|
|
from .content_pm import PackageManager
|
|
return PackageManager()
|
|
|
|
def getContentResolver(self):
|
|
from .content import ContentResolver
|
|
return ContentResolver()
|
|
|
|
def getSystemService(self, name: str):
|
|
return android_context.getSystemService(name)
|
|
|
|
def getSharedPreferences(self, name: str, mode: int):
|
|
from .content import SharedPreferences
|
|
return SharedPreferences(name)
|
|
|
|
def getFilesDir(self):
|
|
return os.path.join(_app_data_dir, "files")
|
|
|
|
def getCacheDir(self):
|
|
return os.path.join(_app_data_dir, "cache")
|
|
|
|
def getBaseContext(self):
|
|
return android_context
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# ActivityManager
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class ActivityManager(Stub):
|
|
"""ActivityManager — 管理 Activity 运行状态"""
|
|
|
|
RECENT_IGNORE_UNAVAILABLE = 0x02
|
|
RECENT_WITH_EXCLUDED = 0x01
|
|
MOVE_TASK_WITH_HOME = 0x01
|
|
MOVE_TASK_NO_USER_ACTION = 0x02
|
|
|
|
class RunningAppProcessInfo(Stub):
|
|
"""Information about a running process"""
|
|
IMPORTANCE_FOREGROUND = 100
|
|
IMPORTANCE_VISIBLE = 200
|
|
IMPORTANCE_SERVICE = 300
|
|
IMPORTANCE_BACKGROUND = 400
|
|
IMPORTANCE_EMPTY = 500
|
|
IMPORTANCE_FOREGROUND_SERVICE = 125
|
|
IMPORTANCE_TOP_SLEEPING = 150
|
|
IMPORTANCE_CACHED = 400
|
|
IMPORTANCE_GONE = 1000
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.processName = ""
|
|
self.pid = 0
|
|
self.uid = 0
|
|
self.importance = self.IMPORTANCE_FOREGROUND
|
|
self.lru = 0
|
|
self.importanceReasonCode = 0
|
|
self.importanceReasonPid = 0
|
|
self.importanceReasonComponent = None
|
|
self.pkgList: List[str] = []
|
|
|
|
class AppTask(Stub):
|
|
"""Represents a task managed by ActivityManager"""
|
|
def finishAndRemoveTask(self):
|
|
pass
|
|
def getTaskInfo(self):
|
|
return None
|
|
def moveToFront(self):
|
|
pass
|
|
def setExcludeFromRecents(self, exclude: bool):
|
|
pass
|
|
|
|
class MemoryInfo(Stub):
|
|
"""Memory information"""
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.availMem = 0
|
|
self.totalMem = 0
|
|
self.threshold = 0
|
|
self.lowMemory = False
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._memory_info = self.MemoryInfo()
|
|
|
|
def getMemoryInfo(self) -> "ActivityManager.MemoryInfo":
|
|
if _USE_LINUX:
|
|
return LinuxPower.get_memory_info()
|
|
return self._memory_info
|
|
|
|
def getRunningAppProcesses(self) -> Optional[List["ActivityManager.RunningAppProcessInfo"]]:
|
|
if _USE_LINUX:
|
|
return LinuxProcess.list_processes()
|
|
return []
|
|
|
|
def killBackgroundProcesses(self, package_name: str):
|
|
pass
|
|
|
|
def clearApplicationUserData(self) -> bool:
|
|
return True
|
|
|
|
def getRecentTasks(self, max_num: int, flags: int) -> List:
|
|
return []
|
|
|
|
def getAppTasks(self) -> List["ActivityManager.AppTask"]:
|
|
return []
|
|
|
|
def moveTaskToFront(self, task_id: int, flags: int):
|
|
pass
|
|
|
|
def isBackgroundRestricted(self) -> bool:
|
|
return False
|
|
|
|
def isLowRamDevice(self) -> bool:
|
|
return False
|
|
|
|
def getLauncherLargeIconSize(self) -> int:
|
|
return 48
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# TaskStackBuilder
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class TaskStackBuilder(Stub):
|
|
"""Builder for synthetic back stack"""
|
|
|
|
def __init__(self, context):
|
|
super().__init__()
|
|
self._context = context
|
|
self._intents: List[Intent] = []
|
|
|
|
@staticmethod
|
|
def create(context) -> "TaskStackBuilder":
|
|
return TaskStackBuilder(context)
|
|
|
|
def addNextIntent(self, intent: Intent) -> "TaskStackBuilder":
|
|
self._intents.append(intent)
|
|
return self
|
|
|
|
def addNextIntentWithParentStack(self, intent: Intent) -> "TaskStackBuilder":
|
|
self._intents.append(intent)
|
|
return self
|
|
|
|
def addParentStack(self, activity_cls: type) -> "TaskStackBuilder":
|
|
return self
|
|
|
|
def startActivities(self, options: Optional["Bundle"] = None):
|
|
for intent in self._intents:
|
|
sys.stderr.write(f"[TaskStackBuilder] start activity: {intent}\n")
|
|
|
|
def startActivitiesIfNeeded(self, options: Optional["Bundle"] = None):
|
|
self.startActivities(options)
|
|
|
|
def getIntent(self, request_code: int) -> Optional[Intent]:
|
|
if self._intents:
|
|
return self._intents[-1]
|
|
return None
|
|
|
|
def editIntentAt(self, index: int) -> Optional[Intent]:
|
|
if 0 <= index < len(self._intents):
|
|
return self._intents[index]
|
|
return None
|
|
|
|
def getIntentCount(self) -> int:
|
|
return len(self._intents)
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# Bundle (forward reference for Fragment uses)
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
from .os import Bundle
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# __all__
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
__all__ = [
|
|
"Intent",
|
|
"ComponentName",
|
|
"PendingIntent",
|
|
"AlarmManager",
|
|
"Notification",
|
|
"NotificationChannel",
|
|
"NotificationChannelGroup",
|
|
"NotificationManager",
|
|
"NotificationCompat",
|
|
"Dialog",
|
|
"AlertDialog",
|
|
"ProgressDialog",
|
|
"DatePickerDialog",
|
|
"TimePickerDialog",
|
|
"Fragment",
|
|
"FragmentManager",
|
|
"FragmentTransaction",
|
|
"Activity",
|
|
"Service",
|
|
"Application",
|
|
"ActivityManager",
|
|
"TaskStackBuilder",
|
|
] |