feat: 大幅增强原生行为 — 修复P0 Bug + 补全核心API
P0 Bug 修复: - Canvas.drawBitmap 重复定义导致重载丢失 → 合并为*args重载 - String.hashCode() 不可达的第二return → 修复为单return - File.compareTo() 调用不存在的__cmp__ → 改为显式比较 - HashMap.remove() 逻辑错误 → 区分单参(返回旧值)/双参(返回boolean) P1 核心行为增强: - Handler: sendMessage/sendMessageDelayed 现在真正入队异步执行 - Looper: loop() 实现真正的消息循环(按when时间取出到期消息) - AsyncTask: execute() 在后台线程池执行,完成后主线程回调 - Bundle: 新增12个数组类型put/get方法 - Math.round: 改为Java语义(floor(a+0.5))而非Python银行家舍入 - Thread: currentThread()缓存对象, 新增join/getState/getId/getPriority - String: 新增strip/repeat/isBlank/lines/subSequence/getChars/chars/formatted/translateEscapes P2 缺失API补全: - 新增21个Java标准Exception类 (NullPointerException等) - View: 新增39个事件处理/监听器/位置尺寸方法 - Activity: 新增33个生命周期/权限/UI方法 - Context: 新增22个文件流/权限/资源/主题方法 - Canvas: 支持 Canvas(Bitmap) 构造函数重载 - __init__.py: __all__ 补全Exception体系和Activity类
This commit is contained in:
1 parent
4645015004
commit
8c3bd3c415
9 files changed
+1017
-52
No files matched your search
@@ -1698,6 +1698,129 @@ class Activity(Stub):
|
||||
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)
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# Service
|
||||
|
||||
@@ -743,6 +743,86 @@ class Context(Stub):
|
||||
def toString(self) -> str:
|
||||
return f"Context{{{self._package_name}}}"
|
||||
|
||||
# ─── 文件流 / 权限 (扩展) ─────────────────────────────────
|
||||
|
||||
def openFileInput(self, name):
|
||||
path = os.path.join(self.getFilesDir(), name)
|
||||
return open(path, 'rb')
|
||||
|
||||
def openFileOutput(self, name, mode):
|
||||
path = os.path.join(self.getFilesDir(), name)
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
return open(path, 'wb')
|
||||
|
||||
def getFileStreamPath(self, name):
|
||||
from ..java.io import File
|
||||
return File(self.getFilesDir(), name)
|
||||
|
||||
def grantUriPermission(self, toPackage, uri, modeFlags):
|
||||
pass
|
||||
|
||||
def revokeUriPermission(self, uri, modeFlags):
|
||||
pass
|
||||
|
||||
def checkUriPermission(self, uri, pid, uid, modeFlags):
|
||||
return 0 # PERMISSION_GRANTED
|
||||
|
||||
def checkPermission(self, permission, pid, uid):
|
||||
return 0 # PERMISSION_GRANTED
|
||||
|
||||
def enforceUriPermission(self, uri, pid, uid, modeFlags, message):
|
||||
pass
|
||||
|
||||
# ─── 资源 / 主题 (扩展) ───────────────────────────────────
|
||||
|
||||
def getApplicationInfo(self):
|
||||
from .content_pm import ApplicationInfo
|
||||
return ApplicationInfo()
|
||||
|
||||
def getTheme(self):
|
||||
return getattr(self, '_theme', 0)
|
||||
|
||||
def setTheme(self, resid):
|
||||
self._theme = resid
|
||||
|
||||
def getText(self, resId):
|
||||
return str(resId)
|
||||
|
||||
def getQuantityString(self, resId, quantity, *formatArgs):
|
||||
return str(resId)
|
||||
|
||||
def getStringArray(self, resId):
|
||||
return []
|
||||
|
||||
def getTextArray(self, resId):
|
||||
return []
|
||||
|
||||
def getIntArray(self, resId):
|
||||
return []
|
||||
|
||||
# ─── Context 派生 / 回调 (扩展) ───────────────────────────
|
||||
|
||||
def createConfigurationContext(self, overrideConfiguration):
|
||||
return Context()
|
||||
|
||||
def createDisplayContext(self, display):
|
||||
return Context()
|
||||
|
||||
def registerComponentCallbacks(self, callback):
|
||||
pass
|
||||
|
||||
def unregisterComponentCallbacks(self, callback):
|
||||
pass
|
||||
|
||||
def getExternalStoragePublicDirectory(self, type_):
|
||||
from ..java.io import File
|
||||
from .os import Environment
|
||||
return Environment.getExternalStoragePublicDirectory(type_)
|
||||
|
||||
def getDisplayMetrics(self):
|
||||
from .util import DisplayMetrics
|
||||
return DisplayMetrics()
|
||||
|
||||
|
||||
class ContextWrapper(Context):
|
||||
"""ContextWrapper — Context 的包装器"""
|
||||
|
||||
@@ -2404,8 +2404,25 @@ class Canvas(Stub):
|
||||
|
||||
ALL_SAVE_FLAG = 0x1F
|
||||
|
||||
def __init__(self, width: int = 0, height: int = 0):
|
||||
def __init__(self, width=0, height=0):
|
||||
super().__init__()
|
||||
# 支持 Canvas(Bitmap) 重载
|
||||
if isinstance(width, Bitmap):
|
||||
bitmap = width
|
||||
self._bitmap = bitmap
|
||||
self._width = bitmap.getWidth()
|
||||
self._height = bitmap.getHeight()
|
||||
if HAS_PIL:
|
||||
from PIL import Image, ImageDraw
|
||||
self._image = bitmap._image if hasattr(bitmap, '_image') and bitmap._image else None
|
||||
self._draw = ImageDraw.Draw(self._image) if self._image else None
|
||||
else:
|
||||
self._pixels = None
|
||||
self._save_count = 0
|
||||
self._matrix_stack = [Matrix()]
|
||||
self._clip_stack = [Rect(0, 0, self._width, self._height)]
|
||||
self._matrix = Matrix()
|
||||
return
|
||||
if HAS_PIL:
|
||||
from PIL import Image, ImageDraw
|
||||
self._width = width
|
||||
@@ -2685,21 +2702,43 @@ class Canvas(Stub):
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def drawBitmap(self, bitmap: Bitmap, left: float, top: float, paint: Paint = None):
|
||||
if HAS_PIL and self._draw is not None and hasattr(bitmap, "_image") and bitmap._image:
|
||||
self._image.paste(bitmap._image, (int(left), int(top)))
|
||||
elif HAS_PIL and self._image is not None:
|
||||
pass
|
||||
|
||||
def drawBitmap(self, bitmap: Bitmap, src: Rect, dst: RectF, paint: Paint = None):
|
||||
if HAS_PIL and self._image is not None and hasattr(bitmap, "_image") and bitmap._image:
|
||||
if src:
|
||||
cropped = bitmap._image.crop((src.left, src.top, src.right, src.bottom))
|
||||
resized = cropped.resize((int(dst.width()), int(dst.height())))
|
||||
self._image.paste(resized, (int(dst.left), int(dst.top)))
|
||||
def drawBitmap(self, *args):
|
||||
"""drawBitmap 重载: (bitmap,left,top,paint) / (bitmap,src,dst,paint) / (bitmap,matrix,paint)"""
|
||||
if len(args) == 4:
|
||||
bitmap, a, b, paint = args
|
||||
if isinstance(a, (int, float)) and isinstance(b, (int, float)):
|
||||
if HAS_PIL and self._draw is not None and hasattr(bitmap, "_image") and bitmap._image:
|
||||
self._image.paste(bitmap._image, (int(a), int(b)))
|
||||
elif HAS_PIL and self._image is not None:
|
||||
pass
|
||||
else:
|
||||
resized = bitmap._image.resize((int(dst.width()), int(dst.height())))
|
||||
self._image.paste(resized, (int(dst.left), int(dst.top)))
|
||||
src, dst = a, b
|
||||
if HAS_PIL and self._image is not None and hasattr(bitmap, "_image") and bitmap._image:
|
||||
if src:
|
||||
cropped = bitmap._image.crop((src.left, src.top, src.right, src.bottom))
|
||||
resized = cropped.resize((int(dst.width()), int(dst.height())))
|
||||
self._image.paste(resized, (int(dst.left), int(dst.top)))
|
||||
else:
|
||||
resized = bitmap._image.resize((int(dst.width()), int(dst.height())))
|
||||
self._image.paste(resized, (int(dst.left), int(dst.top)))
|
||||
elif len(args) == 3:
|
||||
bitmap, matrix, paint = args
|
||||
if HAS_PIL and self._image is not None and hasattr(bitmap, "_image") and bitmap._image:
|
||||
try:
|
||||
from PIL import Image
|
||||
inv = Matrix()
|
||||
if hasattr(matrix, "invert") and matrix.invert(inv):
|
||||
v = inv.getValues()
|
||||
elif hasattr(matrix, "getValues"):
|
||||
v = matrix.getValues()
|
||||
else:
|
||||
v = [1, 0, 0, 0, 1, 0, 0, 0, 1]
|
||||
transformed = bitmap._image.transform(
|
||||
bitmap._image.size, Image.AFFINE,
|
||||
(v[0], v[1], v[2], v[3], v[4], v[5]))
|
||||
self._image.paste(transformed, (0, 0))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def drawText(self, text: str, x: float, y: float, paint: Paint):
|
||||
if HAS_PIL and self._draw is not None:
|
||||
|
||||
+214
-15
@@ -466,6 +466,61 @@ class Bundle(BaseBundle):
|
||||
def putIntegerArrayList(self, key: str, value: Optional[List[int]]):
|
||||
self._map[key] = value
|
||||
|
||||
# ---- 数组类型 ----
|
||||
def putIntArray(self, key: str, value: Optional[List[int]]):
|
||||
self._map[key] = list(value) if value is not None else None
|
||||
|
||||
def getIntArray(self, key: str) -> Optional[List[int]]:
|
||||
v = self._map.get(key)
|
||||
if isinstance(v, list):
|
||||
return [int(x) for x in v]
|
||||
return None
|
||||
|
||||
def putStringArray(self, key: str, value: Optional[List[str]]):
|
||||
self._map[key] = list(value) if value is not None else None
|
||||
|
||||
def getStringArray(self, key: str) -> Optional[List[str]]:
|
||||
v = self._map.get(key)
|
||||
if isinstance(v, list):
|
||||
return [str(x) for x in v]
|
||||
return None
|
||||
|
||||
def putLongArray(self, key: str, value: Optional[List[int]]):
|
||||
self._map[key] = list(value) if value is not None else None
|
||||
|
||||
def getLongArray(self, key: str) -> Optional[List[int]]:
|
||||
v = self._map.get(key)
|
||||
if isinstance(v, list):
|
||||
return [int(x) for x in v]
|
||||
return None
|
||||
|
||||
def putBooleanArray(self, key: str, value: Optional[List[bool]]):
|
||||
self._map[key] = list(value) if value is not None else None
|
||||
|
||||
def getBooleanArray(self, key: str) -> Optional[List[bool]]:
|
||||
v = self._map.get(key)
|
||||
if isinstance(v, list):
|
||||
return [bool(x) for x in v]
|
||||
return None
|
||||
|
||||
def putDoubleArray(self, key: str, value: Optional[List[float]]):
|
||||
self._map[key] = list(value) if value is not None else None
|
||||
|
||||
def getDoubleArray(self, key: str) -> Optional[List[float]]:
|
||||
v = self._map.get(key)
|
||||
if isinstance(v, list):
|
||||
return [float(x) for x in v]
|
||||
return None
|
||||
|
||||
def putByteArray(self, key: str, value: Optional[List[int]]):
|
||||
self._map[key] = list(value) if value is not None else None
|
||||
|
||||
def getByteArray(self, key: str) -> Optional[List[int]]:
|
||||
v = self._map.get(key)
|
||||
if isinstance(v, list):
|
||||
return [int(x) & 0xFF for x in v]
|
||||
return None
|
||||
|
||||
def putAll(self, bundle: "Bundle"):
|
||||
if bundle:
|
||||
self._map.update(bundle._map)
|
||||
@@ -729,6 +784,14 @@ class Message(Stub):
|
||||
def setTarget(self, handler: "Handler"):
|
||||
self._target = handler
|
||||
|
||||
@property
|
||||
def target(self) -> Optional["Handler"]:
|
||||
return self._target
|
||||
|
||||
@target.setter
|
||||
def target(self, handler: "Handler"):
|
||||
self._target = handler
|
||||
|
||||
def getCallback(self) -> Optional[Callable]:
|
||||
return self._callback
|
||||
|
||||
@@ -769,15 +832,17 @@ class Looper(Stub):
|
||||
_main_looper: Optional["Looper"] = None
|
||||
_thread_local = threading.local()
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, quit_allowed: bool = True):
|
||||
super().__init__()
|
||||
self._thread = threading.current_thread()
|
||||
self._running = False
|
||||
self._message_queue: List[Message] = []
|
||||
self._quit_allowed = True
|
||||
self._queue_lock = threading.Lock()
|
||||
self._quit_allowed = quit_allowed
|
||||
|
||||
@staticmethod
|
||||
def prepare():
|
||||
"""为当前线程准备一个 Looper"""
|
||||
looper = Looper()
|
||||
Looper._thread_local.looper = looper
|
||||
return looper
|
||||
@@ -799,16 +864,76 @@ class Looper(Stub):
|
||||
|
||||
@staticmethod
|
||||
def loop():
|
||||
"""运行消息循环,处理到期的消息"""
|
||||
looper = Looper.myLooper()
|
||||
if looper is None:
|
||||
raise RuntimeError("No Looper prepared")
|
||||
looper._running = True
|
||||
while looper._running:
|
||||
msg = looper._next()
|
||||
if msg is not None:
|
||||
target = msg._target
|
||||
if target is not None:
|
||||
target.dispatchMessage(msg)
|
||||
msg.recycle()
|
||||
else:
|
||||
# 无到期消息,短暂 sleep 避免忙等
|
||||
_time.sleep(0.001)
|
||||
|
||||
def _next(self) -> Optional[Message]:
|
||||
"""非阻塞地取出一条到期消息(when <= now)"""
|
||||
with self._queue_lock:
|
||||
now = SystemClock.uptimeMillis()
|
||||
for i, msg in enumerate(self._message_queue):
|
||||
if msg.when <= now:
|
||||
return self._message_queue.pop(i)
|
||||
return None
|
||||
|
||||
def _enqueue(self, msg: Message):
|
||||
"""将消息按 when 时间顺序入队"""
|
||||
with self._queue_lock:
|
||||
inserted = False
|
||||
for i, m in enumerate(self._message_queue):
|
||||
if msg.when < m.when:
|
||||
self._message_queue.insert(i, msg)
|
||||
inserted = True
|
||||
break
|
||||
if not inserted:
|
||||
self._message_queue.append(msg)
|
||||
|
||||
def _remove_callbacks(self, callback):
|
||||
"""从队列中移除匹配 callback 的消息"""
|
||||
with self._queue_lock:
|
||||
self._message_queue = [
|
||||
m for m in self._message_queue
|
||||
if m._callback is not callback
|
||||
]
|
||||
|
||||
def _remove_messages(self, what: int):
|
||||
"""从队列中移除匹配 what 的消息"""
|
||||
with self._queue_lock:
|
||||
self._message_queue = [
|
||||
m for m in self._message_queue
|
||||
if m.what != what
|
||||
]
|
||||
|
||||
def _has_messages(self, what: int) -> bool:
|
||||
"""检查队列中是否有匹配 what 的消息"""
|
||||
with self._queue_lock:
|
||||
return any(m.what == what for m in self._message_queue)
|
||||
|
||||
def quit(self):
|
||||
self._running = False
|
||||
"""退出消息循环"""
|
||||
if not self._quit_allowed:
|
||||
raise RuntimeError("Main Looper is not allowed to quit")
|
||||
with self._queue_lock:
|
||||
self._running = False
|
||||
self._message_queue.clear()
|
||||
|
||||
def quitSafely(self):
|
||||
self._running = False
|
||||
"""安全退出,仅停止循环(已入队消息不再处理)"""
|
||||
with self._queue_lock:
|
||||
self._running = False
|
||||
|
||||
def isCurrentThread(self) -> bool:
|
||||
return threading.current_thread() == self._thread
|
||||
@@ -858,7 +983,6 @@ class Handler(Stub):
|
||||
def postDelayed(self, r: Callable, delay_ms: int) -> bool:
|
||||
msg = Message.obtain(self)
|
||||
msg._callback = r
|
||||
msg.when = int(_time.time() * 1000) + delay_ms
|
||||
return self.sendMessageDelayed(msg, delay_ms)
|
||||
|
||||
def postAtTime(self, r: Callable, uptime_ms: int) -> bool:
|
||||
@@ -869,6 +993,11 @@ class Handler(Stub):
|
||||
|
||||
def sendMessage(self, msg: Message) -> bool:
|
||||
msg._target = self
|
||||
looper = self._looper
|
||||
if looper is not None and looper._running:
|
||||
looper._enqueue(msg)
|
||||
return True
|
||||
# 当前线程无运行中的 Looper,回退到同步执行(保持兼容性)
|
||||
self.dispatchMessage(msg)
|
||||
return True
|
||||
|
||||
@@ -878,31 +1007,53 @@ class Handler(Stub):
|
||||
|
||||
def sendMessageDelayed(self, msg: Message, delay_ms: int) -> bool:
|
||||
msg._target = self
|
||||
msg.when = int(_time.time() * 1000) + delay_ms
|
||||
msg.when = SystemClock.uptimeMillis() + delay_ms
|
||||
looper = self._looper
|
||||
if looper is not None and looper._running:
|
||||
looper._enqueue(msg)
|
||||
return True
|
||||
# 回退到同步执行
|
||||
self.dispatchMessage(msg)
|
||||
return True
|
||||
|
||||
def sendMessageAtTime(self, msg: Message, uptime_ms: int) -> bool:
|
||||
msg._target = self
|
||||
msg.when = uptime_ms
|
||||
looper = self._looper
|
||||
if looper is not None and looper._running:
|
||||
looper._enqueue(msg)
|
||||
return True
|
||||
self.dispatchMessage(msg)
|
||||
return True
|
||||
|
||||
def sendMessageAtFrontOfQueue(self, msg: Message) -> bool:
|
||||
msg._target = self
|
||||
msg.when = 0
|
||||
looper = self._looper
|
||||
if looper is not None and looper._running:
|
||||
with looper._queue_lock:
|
||||
looper._message_queue.insert(0, msg)
|
||||
return True
|
||||
self.dispatchMessage(msg)
|
||||
return True
|
||||
|
||||
def removeCallbacks(self, r: Callable):
|
||||
pass
|
||||
looper = self._looper
|
||||
if looper is not None:
|
||||
looper._remove_callbacks(r)
|
||||
|
||||
def removeMessages(self, what: int):
|
||||
pass
|
||||
looper = self._looper
|
||||
if looper is not None:
|
||||
looper._remove_messages(what)
|
||||
|
||||
def obtainMessage(self, what: int = 0) -> Message:
|
||||
return Message.obtain(self, what)
|
||||
|
||||
def hasMessages(self, what: int) -> bool:
|
||||
looper = self._looper
|
||||
if looper is not None:
|
||||
return looper._has_messages(what)
|
||||
return False
|
||||
|
||||
def getLooper(self) -> Looper:
|
||||
@@ -950,6 +1101,42 @@ class HandlerThread(threading.Thread, Stub):
|
||||
return self.quit()
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# AsyncTask Executor (守护线程池)
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
class _AsyncTaskExecutor:
|
||||
"""守护线程池执行器 — 用于 AsyncTask 后台执行,不阻塞程序退出。
|
||||
|
||||
使用 BoundedSemaphore 限制并发数,每个任务在独立的守护线程中运行。
|
||||
"""
|
||||
|
||||
def __init__(self, max_workers: int = 4):
|
||||
self._max_workers = max_workers
|
||||
self._semaphore = threading.BoundedSemaphore(max_workers)
|
||||
self._shutdown_flag = False
|
||||
|
||||
def submit(self, fn, *args, **kwargs):
|
||||
if self._shutdown_flag:
|
||||
raise RuntimeError("Executor has been shut down")
|
||||
self._semaphore.acquire()
|
||||
|
||||
def _run():
|
||||
try:
|
||||
fn(*args, **kwargs)
|
||||
finally:
|
||||
self._semaphore.release()
|
||||
|
||||
t = threading.Thread(target=_run, daemon=True)
|
||||
t.start()
|
||||
|
||||
def execute(self, command):
|
||||
self.submit(command)
|
||||
|
||||
def shutdown(self, wait: bool = True):
|
||||
self._shutdown_flag = True
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# AsyncTask
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
@@ -967,6 +1154,7 @@ class AsyncTask(Stub):
|
||||
self._status = AsyncTask.Status.PENDING
|
||||
self._result: Any = None
|
||||
self._cancelled = False
|
||||
self._handler = Handler(Looper.getMainLooper())
|
||||
|
||||
def doInBackground(self, *params) -> Any:
|
||||
raise NotImplementedError
|
||||
@@ -984,19 +1172,25 @@ class AsyncTask(Stub):
|
||||
pass
|
||||
|
||||
def execute(self, *params) -> "AsyncTask":
|
||||
return self.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, *params)
|
||||
|
||||
def executeOnExecutor(self, exec: Any, *params) -> "AsyncTask":
|
||||
self._status = AsyncTask.Status.RUNNING
|
||||
self.onPreExecute()
|
||||
exec.submit(self._run_background, params)
|
||||
return self
|
||||
|
||||
def _run_background(self, params):
|
||||
"""在后台线程中执行 doInBackground,完成后通过 Handler 回调主线程"""
|
||||
try:
|
||||
self._result = self.doInBackground(*params)
|
||||
except Exception as e:
|
||||
self._result = e
|
||||
self._status = AsyncTask.Status.FINISHED
|
||||
if not self._cancelled:
|
||||
self.onPostExecute(self._result)
|
||||
return self
|
||||
|
||||
def executeOnExecutor(self, exec: Any, *params) -> "AsyncTask":
|
||||
return self.execute(*params)
|
||||
if self._cancelled:
|
||||
self._handler.post(lambda: self.onCancelled(self._result))
|
||||
else:
|
||||
self._handler.post(lambda: self.onPostExecute(self._result))
|
||||
|
||||
def get(self) -> Any:
|
||||
return self._result
|
||||
@@ -1012,7 +1206,12 @@ class AsyncTask(Stub):
|
||||
return True
|
||||
|
||||
def publishProgress(self, *values):
|
||||
self.onProgressUpdate(*values)
|
||||
self._handler.post(lambda: self.onProgressUpdate(*values))
|
||||
|
||||
|
||||
# 初始化默认执行器
|
||||
AsyncTask.THREAD_POOL_EXECUTOR = _AsyncTaskExecutor(max_workers=4)
|
||||
AsyncTask.SERIAL_EXECUTOR = _AsyncTaskExecutor(max_workers=1)
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
@@ -777,6 +777,155 @@ class View(Stub):
|
||||
def inflate(context, resource, root):
|
||||
return View(context)
|
||||
|
||||
# ─── 事件处理 (扩展) ───────────────────────────────────────
|
||||
|
||||
def onGenericMotionEvent(self, event) -> bool:
|
||||
return False
|
||||
|
||||
def onHoverEvent(self, event) -> bool:
|
||||
return False
|
||||
|
||||
def onDragEvent(self, event) -> bool:
|
||||
return False
|
||||
|
||||
def onSizeChanged(self, w, h, oldw, oldh):
|
||||
pass
|
||||
|
||||
def onFocusChanged(self, gainFocus, direction, previouslyFocusedRect):
|
||||
pass
|
||||
|
||||
def onScrollChanged(self, l, t, oldl, oldt):
|
||||
pass
|
||||
|
||||
def onVisibilityChanged(self, changedView, visibility):
|
||||
pass
|
||||
|
||||
# ─── 监听器设置 (扩展) ─────────────────────────────────────
|
||||
|
||||
def setOnGenericMotionListener(self, listener):
|
||||
self._on_generic_motion_listener = listener
|
||||
return self
|
||||
|
||||
def setOnHoverListener(self, listener):
|
||||
self._on_hover_listener = listener
|
||||
return self
|
||||
|
||||
def setOnDragListener(self, listener):
|
||||
self._on_drag_listener = listener
|
||||
return self
|
||||
|
||||
def setOnFocusChangeListener(self, listener):
|
||||
self._on_focus_change_listener = listener
|
||||
return self
|
||||
|
||||
def setOnContextClickListener(self, listener):
|
||||
self._on_context_click_listener = listener
|
||||
return self
|
||||
|
||||
def addOnLayoutChangeListener(self, listener):
|
||||
if not hasattr(self, '_layout_change_listeners'):
|
||||
self._layout_change_listeners = []
|
||||
self._layout_change_listeners.append(listener)
|
||||
|
||||
def removeOnLayoutChangeListener(self, listener):
|
||||
if hasattr(self, '_layout_change_listeners'):
|
||||
try:
|
||||
self._layout_change_listeners.remove(listener)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
def setOnApplyWindowInsetsListener(self, listener):
|
||||
self._on_apply_window_insets_listener = listener
|
||||
return self
|
||||
|
||||
# ─── 位置和尺寸 (扩展) ─────────────────────────────────────
|
||||
|
||||
def getLocationOnScreen(self, location):
|
||||
location[0] = self._left if hasattr(self, '_left') else 0
|
||||
location[1] = self._top if hasattr(self, '_top') else 0
|
||||
|
||||
def getLocationInWindow(self, location):
|
||||
self.getLocationOnScreen(location)
|
||||
|
||||
def offsetLeftAndRight(self, offset):
|
||||
if hasattr(self, '_left'):
|
||||
self._left += offset
|
||||
|
||||
def offsetTopAndBottom(self, offset):
|
||||
if hasattr(self, '_top'):
|
||||
self._top += offset
|
||||
|
||||
def getHitRect(self, out_rect):
|
||||
out_rect.left = self._left if hasattr(self, '_left') else 0
|
||||
out_rect.top = self._top if hasattr(self, '_top') else 0
|
||||
out_rect.right = out_rect.left + (self._width if hasattr(self, '_width') else 0)
|
||||
out_rect.bottom = out_rect.top + (self._height if hasattr(self, '_height') else 0)
|
||||
|
||||
def getDrawingRect(self, out_rect):
|
||||
out_rect.left = 0
|
||||
out_rect.top = 0
|
||||
out_rect.right = self._width if hasattr(self, '_width') else 0
|
||||
out_rect.bottom = self._height if hasattr(self, '_height') else 0
|
||||
|
||||
# ─── 其他扩展方法 ──────────────────────────────────────────
|
||||
|
||||
def setTooltipText(self, text):
|
||||
self._tooltip_text = text
|
||||
|
||||
def getTooltipText(self):
|
||||
return getattr(self, '_tooltip_text', None)
|
||||
|
||||
def setTranslationZ(self, translationZ):
|
||||
self._translation_z = translationZ
|
||||
|
||||
def getTranslationZ(self):
|
||||
return getattr(self, '_translation_z', 0.0)
|
||||
|
||||
def setZ(self, z):
|
||||
self._z = z
|
||||
|
||||
def getZ(self):
|
||||
return getattr(self, '_z', 0.0)
|
||||
|
||||
def setTransitionName(self, name):
|
||||
self._transition_name = name
|
||||
|
||||
def getTransitionName(self):
|
||||
return getattr(self, '_transition_name', None)
|
||||
|
||||
def setClipToOutline(self, clipToOutline):
|
||||
self._clip_to_outline = clipToOutline
|
||||
|
||||
def getClipToOutline(self):
|
||||
return getattr(self, '_clip_to_outline', False)
|
||||
|
||||
def setFitsSystemWindows(self, fitSystemWindows):
|
||||
self._fits_system_windows = fitSystemWindows
|
||||
|
||||
def getFitsSystemWindows(self):
|
||||
return getattr(self, '_fits_system_windows', False)
|
||||
|
||||
def playSoundEffect(self, soundConstant):
|
||||
pass
|
||||
|
||||
def setSaveEnabled(self, enabled):
|
||||
self._save_enabled = enabled
|
||||
|
||||
def isSaveEnabled(self):
|
||||
return getattr(self, '_save_enabled', True)
|
||||
|
||||
def setSystemUiVisibility(self, visibility):
|
||||
self._system_ui_visibility = visibility
|
||||
|
||||
def getSystemUiVisibility(self):
|
||||
return getattr(self, '_system_ui_visibility', 0)
|
||||
|
||||
def requestApplyInsets(self):
|
||||
pass
|
||||
|
||||
def onApplyWindowInsets(self, insets):
|
||||
return insets
|
||||
|
||||
|
||||
class MeasureSpec:
|
||||
"""MeasureSpec — 测量规格包装"""
|
||||
@@ -2646,6 +2795,24 @@ class SurfaceTexture(Stub):
|
||||
mtx[i] = 1.0 if i % 5 == 0 else 0.0
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# MenuInflater
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
class MenuInflater(Stub):
|
||||
"""MenuInflater — 菜单填充器(桌面环境桩实现)"""
|
||||
|
||||
def __init__(self, context=None):
|
||||
super().__init__()
|
||||
self._context = context
|
||||
|
||||
def inflate(self, menu_res: int, menu: Any):
|
||||
return menu
|
||||
|
||||
def toString(self) -> str:
|
||||
return f"MenuInflater@{hex(id(self))}"
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# Window
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
Reference in new issue
Block a user