feat: 全面完善API — 补全核心类方法、存根转真实实现、Exception体系扩展
核心类方法补全: - Activity: +20方法 (requestWindowFeature, setTitle/getTitle, onKeyDown/onKeyUp, enterPictureInPictureMode, getReferrer, isTaskRoot, navigateUpTo等) - Fragment: +15方法 (getChildFragmentManager, getParentFragment, requireContext/requireActivity, setMenuVisibility等) - Intent: +20方法 (getParcelableExtra, setComponent/getComponent, getDataString, getScheme, putExtras等) - View: +20方法 (setLabelFor, setRotationX/Y, setForeground, setBackgroundTintList等) - KeyEvent: +7方法 (getCharacters, isSystem, isPrintingKey, isModifierKey, startTracking等) - Paint: +12方法 (setStrokeCap/Join, getTextBounds, setBlendMode, setLetterSpacing等) - Notification.Builder: +10方法 (setProgress, setShowWhen, setOnlyAlertOnce, setSound等) 存根转真实实现: - TrafficStats: 读取 /proc/net/dev 返回真实网络流量 - RecyclerView.Adapter: 观察者模式触发通知 - BaseAdapter: 观察者模式触发通知 - ExifInterface: 使用PIL实现图片翻转/旋转 - Process: 使用os.kill实现进程信号 - FileUtils: 真实文件I/O - SharedPreferences: 监听器模式 - TelephonyManager.listen: 维护监听器列表 - ScrollView.smoothScrollTo: 更新滚动位置 Exception体系扩展: - 新增15个Exception/Error类 (ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchElementException, AssertionError, OutOfMemoryError, StackOverflowError, NoClassDefFoundError, UnsatisfiedLinkError, LinkageError, VerifyError, AbstractMethodError, IncompatibleClassChangeError, ExceptionInInitializerError) Math/System/Thread/StringBuilder/Integer增强: - Math: +20方法 (subtractExact, multiplyExact, toIntExact, floorDiv/floorMod, fma等) - System: getProperties/setProperties/mapLibraryName/getLogger - Thread: +10方法 (getUncaughtExceptionHandler, getAllStackTraces, getStackTrace, holdLock等) - StringBuilder: +7方法 (ensureCapacity, trimToSize, setLength, subSequence等) - Integer: +8静态方法 (compare, compareUnsigned, divideUnsigned, sum, max, min等) 修复: - app_fragment.py: 使用java.lang.IllegalStateException替代本地定义 - content_intent.py: Intent.setData/getScheme正确从Uri提取scheme - view_ext.py: KeyEvent继承view.KeyEvent获取所有KEYCODE常量 - __init__.py: 补全Fragment/KeyEvent/MotionEvent/TrafficStats/RecyclerView等导出 统计: 221模块, 3660类, 30826方法, 180217行
This commit is contained in:
1 parent
e316e18d98
commit
39b1da37c9
16 files changed
+1274
-107
No files matched your search
@@ -379,6 +379,13 @@ __all__ = [
|
|||||||
"AccessibilityServiceInfo",
|
"AccessibilityServiceInfo",
|
||||||
"Account", "AccountManager",
|
"Account", "AccountManager",
|
||||||
"Activity", "ActivityManager",
|
"Activity", "ActivityManager",
|
||||||
|
"Fragment", "FragmentManager", "FragmentTransaction",
|
||||||
|
"Dialog", "AlertDialog", "ProgressDialog",
|
||||||
|
"DatePickerDialog", "TimePickerDialog",
|
||||||
|
"Service", "Application",
|
||||||
|
"PendingIntent", "ComponentName", "AlarmManager",
|
||||||
|
"NotificationChannel", "NotificationChannelGroup",
|
||||||
|
"NotificationCompat", "TaskStackBuilder",
|
||||||
"AlphaAnimation",
|
"AlphaAnimation",
|
||||||
"Animation",
|
"Animation",
|
||||||
"Animator", "ValueAnimator", "ObjectAnimator", "AnimatorSet",
|
"Animator", "ValueAnimator", "ObjectAnimator", "AnimatorSet",
|
||||||
@@ -423,6 +430,7 @@ __all__ = [
|
|||||||
"Html",
|
"Html",
|
||||||
"ImageView",
|
"ImageView",
|
||||||
"InputDevice",
|
"InputDevice",
|
||||||
|
"KeyEvent", "MotionEvent",
|
||||||
"InputManager",
|
"InputManager",
|
||||||
"InputMethodManager",
|
"InputMethodManager",
|
||||||
"Intent",
|
"Intent",
|
||||||
@@ -456,6 +464,7 @@ __all__ = [
|
|||||||
"ProgressBar",
|
"ProgressBar",
|
||||||
"RadioManager", "RadioTuner",
|
"RadioManager", "RadioTuner",
|
||||||
"Rect", "RectF",
|
"Rect", "RectF",
|
||||||
|
"RecyclerView",
|
||||||
"Resources", "AssetManager",
|
"Resources", "AssetManager",
|
||||||
"RoleManager",
|
"RoleManager",
|
||||||
"ScrollView",
|
"ScrollView",
|
||||||
@@ -481,6 +490,7 @@ __all__ = [
|
|||||||
"ThermalManager",
|
"ThermalManager",
|
||||||
"TimeManager",
|
"TimeManager",
|
||||||
"Toast",
|
"Toast",
|
||||||
|
"TrafficStats",
|
||||||
"TranslateAnimation",
|
"TranslateAnimation",
|
||||||
"TranslationManager",
|
"TranslationManager",
|
||||||
"TvInputManager",
|
"TvInputManager",
|
||||||
@@ -524,6 +534,14 @@ __all__ = [
|
|||||||
"CloneNotSupportedException", "IllegalMonitorStateException",
|
"CloneNotSupportedException", "IllegalMonitorStateException",
|
||||||
"IllegalThreadStateException", "EnumConstantNotPresentException",
|
"IllegalThreadStateException", "EnumConstantNotPresentException",
|
||||||
"TypeNotPresentException",
|
"TypeNotPresentException",
|
||||||
|
# Java Exception 体系 (扩展)
|
||||||
|
"ClassNotFoundException", "IllegalAccessException",
|
||||||
|
"InstantiationException", "InvocationTargetException",
|
||||||
|
"NoSuchElementException", "AssertionError",
|
||||||
|
"OutOfMemoryError", "StackOverflowError", "NoClassDefFoundError",
|
||||||
|
"UnsatisfiedLinkError", "LinkageError", "VerifyError",
|
||||||
|
"AbstractMethodError", "IncompatibleClassChangeError",
|
||||||
|
"ExceptionInInitializerError",
|
||||||
"SimpleDateFormat", "DecimalFormat",
|
"SimpleDateFormat", "DecimalFormat",
|
||||||
"LocalDateTime", "LocalDate", "LocalTime", "Instant", "Duration", "Period",
|
"LocalDateTime", "LocalDate", "LocalTime", "Instant", "Duration", "Period",
|
||||||
"ChronoUnit", "ChronoField", "DayOfWeek", "Month", "ZoneId", "ZoneOffset",
|
"ChronoUnit", "ChronoField", "DayOfWeek", "Month", "ZoneId", "ZoneOffset",
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ from .._core import (
|
|||||||
resolve_android_path, to_android_path,
|
resolve_android_path, to_android_path,
|
||||||
_sdcard_root, _app_data_dir, _package_name,
|
_sdcard_root, _app_data_dir, _package_name,
|
||||||
)
|
)
|
||||||
|
from ..java.lang import IllegalStateException
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from .._linux_impl import IS_LINUX, LinuxNotify, LinuxShell, LinuxProcess, LinuxDisplay, LinuxUIAutomation, LinuxPower
|
from .._linux_impl import IS_LINUX, LinuxNotify, LinuxShell, LinuxProcess, LinuxDisplay, LinuxUIAutomation, LinuxPower
|
||||||
@@ -247,6 +248,108 @@ class Intent(Stub):
|
|||||||
def filterHashCode(self) -> int:
|
def filterHashCode(self) -> int:
|
||||||
return hash((self._action, self._data_str, self._type, self._package, self._component)) & 0x7FFFFFFF
|
return hash((self._action, self._data_str, self._type, self._package, self._component)) & 0x7FFFFFFF
|
||||||
|
|
||||||
|
def getParcelableExtra(self, name):
|
||||||
|
return self._extras.get(name) if self._extras else None
|
||||||
|
|
||||||
|
def setComponent(self, component):
|
||||||
|
self._component = component
|
||||||
|
return self
|
||||||
|
|
||||||
|
def getComponent(self):
|
||||||
|
return getattr(self, '_component', None)
|
||||||
|
|
||||||
|
def getDataString(self):
|
||||||
|
data = self.getData()
|
||||||
|
return str(data) if data else None
|
||||||
|
|
||||||
|
def getScheme(self):
|
||||||
|
data = self.getData()
|
||||||
|
if data:
|
||||||
|
return data.getScheme()
|
||||||
|
return None
|
||||||
|
|
||||||
|
def putExtras(self, src):
|
||||||
|
if src._extras:
|
||||||
|
if self._extras is None:
|
||||||
|
self._extras = {}
|
||||||
|
self._extras.update(src._extras)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def putIntegerArrayListExtra(self, name, value):
|
||||||
|
if self._extras is None:
|
||||||
|
self._extras = {}
|
||||||
|
self._extras[name] = value
|
||||||
|
return self
|
||||||
|
|
||||||
|
def putStringArrayListExtra(self, name, value):
|
||||||
|
if self._extras is None:
|
||||||
|
self._extras = {}
|
||||||
|
self._extras[name] = value
|
||||||
|
return self
|
||||||
|
|
||||||
|
def putParcelableArrayListExtra(self, name, value):
|
||||||
|
if self._extras is None:
|
||||||
|
self._extras = {}
|
||||||
|
self._extras[name] = value
|
||||||
|
return self
|
||||||
|
|
||||||
|
def getBundleExtra(self, name):
|
||||||
|
return self._extras.get(name) if self._extras else None
|
||||||
|
|
||||||
|
def putBundleExtra(self, name, value):
|
||||||
|
if self._extras is None:
|
||||||
|
self._extras = {}
|
||||||
|
self._extras[name] = value
|
||||||
|
return self
|
||||||
|
|
||||||
|
def getParcelableArrayListExtra(self, name):
|
||||||
|
return self._extras.get(name) if self._extras else None
|
||||||
|
|
||||||
|
def getStringArrayExtra(self, name):
|
||||||
|
return self._extras.get(name) if self._extras else None
|
||||||
|
|
||||||
|
def getIntArrayExtra(self, name):
|
||||||
|
return self._extras.get(name) if self._extras else None
|
||||||
|
|
||||||
|
def getBooleanArrayExtra(self, name):
|
||||||
|
return self._extras.get(name) if self._extras else None
|
||||||
|
|
||||||
|
def getCharSequenceExtra(self, name):
|
||||||
|
return self._extras.get(name) if self._extras else None
|
||||||
|
|
||||||
|
def getCharSequenceArrayListExtra(self, name):
|
||||||
|
return self._extras.get(name) if self._extras else None
|
||||||
|
|
||||||
|
def removeCategory(self, category):
|
||||||
|
if self._categories:
|
||||||
|
self._categories.discard(category)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def setSelector(self, selector):
|
||||||
|
self._selector = selector
|
||||||
|
|
||||||
|
def getSelector(self):
|
||||||
|
return getattr(self, '_selector', None)
|
||||||
|
|
||||||
|
def setSourceBounds(self, bounds):
|
||||||
|
self._source_bounds = bounds
|
||||||
|
|
||||||
|
def getSourceBounds(self):
|
||||||
|
return getattr(self, '_source_bounds', None)
|
||||||
|
|
||||||
|
def makeMainActivity(self, component):
|
||||||
|
self.addCategory("android.intent.category.LAUNCHER")
|
||||||
|
self.setComponent(component)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def cloneFilter(self):
|
||||||
|
new_intent = Intent(self.getAction())
|
||||||
|
if self._categories:
|
||||||
|
new_intent._categories = set(self._categories)
|
||||||
|
if self._type:
|
||||||
|
new_intent._type = self._type
|
||||||
|
return new_intent
|
||||||
|
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════
|
||||||
# ComponentName (forward reference)
|
# ComponentName (forward reference)
|
||||||
@@ -738,6 +841,62 @@ class Notification(Stub):
|
|||||||
n._extras = dict(self._extras)
|
n._extras = dict(self._extras)
|
||||||
return n
|
return n
|
||||||
|
|
||||||
|
def setProgress(self, max, progress, indeterminate):
|
||||||
|
self._progress_max = max
|
||||||
|
self._progress = progress
|
||||||
|
self._progress_indeterminate = indeterminate
|
||||||
|
return self
|
||||||
|
|
||||||
|
def setShowWhen(self, show):
|
||||||
|
self._show_when = show
|
||||||
|
return self
|
||||||
|
|
||||||
|
def setOnlyAlertOnce(self, onlyAlertOnce):
|
||||||
|
self._only_alert_once = onlyAlertOnce
|
||||||
|
return self
|
||||||
|
|
||||||
|
def setLocalOnly(self, localOnly):
|
||||||
|
self._local_only = localOnly
|
||||||
|
return self
|
||||||
|
|
||||||
|
def setSound(self, sound):
|
||||||
|
self._sound = sound
|
||||||
|
return self
|
||||||
|
|
||||||
|
def setVibrate(self, pattern):
|
||||||
|
self._vibrate = pattern
|
||||||
|
return self
|
||||||
|
|
||||||
|
def setLights(self, argb, onMs, offMs):
|
||||||
|
self._light_color = argb
|
||||||
|
self._light_on = onMs
|
||||||
|
self._light_off = offMs
|
||||||
|
return self
|
||||||
|
|
||||||
|
def setSortKey(self, sortKey):
|
||||||
|
self._sort_key = sortKey
|
||||||
|
return self
|
||||||
|
|
||||||
|
def setTimeoutAfter(self, timeoutMs):
|
||||||
|
self._timeout_after = timeoutMs
|
||||||
|
return self
|
||||||
|
|
||||||
|
def setColorized(self, colorize):
|
||||||
|
self._colorized = colorize
|
||||||
|
return self
|
||||||
|
|
||||||
|
def setUsesChronometer(self, usesChronometer):
|
||||||
|
self._uses_chronometer = usesChronometer
|
||||||
|
return self
|
||||||
|
|
||||||
|
def setShortcutId(self, shortcutId):
|
||||||
|
self._shortcut_id = shortcutId
|
||||||
|
return self
|
||||||
|
|
||||||
|
def setContent(self, views):
|
||||||
|
self._content_view = views
|
||||||
|
return self
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._channel_id = ""
|
self._channel_id = ""
|
||||||
@@ -1355,6 +1514,90 @@ class Fragment(Stub):
|
|||||||
def unregisterForContextMenu(self, view):
|
def unregisterForContextMenu(self, view):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def getChildFragmentManager(self):
|
||||||
|
if not hasattr(self, '_child_fragment_manager'):
|
||||||
|
from .app import FragmentManager
|
||||||
|
self._child_fragment_manager = FragmentManager()
|
||||||
|
return self._child_fragment_manager
|
||||||
|
|
||||||
|
def getParentFragment(self):
|
||||||
|
return getattr(self, '_parent_fragment', None)
|
||||||
|
|
||||||
|
def onCreateOptionsMenu(self, menu, inflater):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def onPrepareOptionsMenu(self, menu):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def onOptionsItemSelected(self, item):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def onCreateContextMenu(self, menu, v, menuInfo):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def onContextItemSelected(self, item):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def onContextMenuClosed(self, menu):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def onConfigurationChanged(self, newConfig):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def onLowMemory(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def onHiddenChanged(self, hidden):
|
||||||
|
self._hidden = hidden
|
||||||
|
|
||||||
|
def getLayoutInflater(self):
|
||||||
|
return getattr(self, '_layout_inflater', None)
|
||||||
|
|
||||||
|
def onGetLayoutInflater(self, savedInstanceState):
|
||||||
|
return self.getLayoutInflater()
|
||||||
|
|
||||||
|
def requireContext(self):
|
||||||
|
if not hasattr(self, '_context') or self._context is None:
|
||||||
|
raise IllegalStateException("Fragment " + self.__class__.__name__ + " not attached to a context")
|
||||||
|
return self._context
|
||||||
|
|
||||||
|
def requireActivity(self):
|
||||||
|
if not hasattr(self, '_activity') or self._activity is None:
|
||||||
|
raise IllegalStateException("Fragment " + self.__class__.__name__ + " not attached to an activity")
|
||||||
|
return self._activity
|
||||||
|
|
||||||
|
def requireView(self):
|
||||||
|
if not hasattr(self, '_view') or self._view is None:
|
||||||
|
raise IllegalStateException("Fragment " + self.__class__.__name__ + " view is null")
|
||||||
|
return self._view
|
||||||
|
|
||||||
|
def requireFragmentManager(self):
|
||||||
|
fm = getattr(self, '_fragment_manager', None)
|
||||||
|
if fm is None:
|
||||||
|
raise IllegalStateException("Fragment " + self.__class__.__name__ + " not associated with a fragment manager")
|
||||||
|
return fm
|
||||||
|
|
||||||
|
def setMenuVisibility(self, menuVisible):
|
||||||
|
self._menu_visible = menuVisible
|
||||||
|
|
||||||
|
def onMenuVisibilityChanged(self, menuVisible):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def postponeEnterTransition(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def startPostponedEnterTransition(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def getEnterTransition(self):
|
||||||
|
return getattr(self, '_enter_transition', None)
|
||||||
|
|
||||||
|
def setEnterTransition(self, transition):
|
||||||
|
self._enter_transition = transition
|
||||||
|
|
||||||
|
def onInflate(self, context, attrs, savedInstanceState):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class FragmentTransaction(Stub):
|
class FragmentTransaction(Stub):
|
||||||
"""FragmentTransaction — 管理 Fragment 事务"""
|
"""FragmentTransaction — 管理 Fragment 事务"""
|
||||||
@@ -1875,6 +2118,102 @@ class Activity(Stub):
|
|||||||
return LinuxUIAutomation.press_key(key)
|
return LinuxUIAutomation.press_key(key)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def requestWindowFeature(self, featureId):
|
||||||
|
self._window_features = getattr(self, '_window_features', set())
|
||||||
|
self._window_features.add(featureId)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def setTitle(self, title):
|
||||||
|
self._title = title
|
||||||
|
|
||||||
|
def getTitle(self):
|
||||||
|
return getattr(self, '_title', '')
|
||||||
|
|
||||||
|
def onKeyDown(self, keyCode, event):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def onKeyUp(self, keyCode, event):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def onTouchEvent(self, event):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def enterPictureInPictureMode(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def getReferrer(self):
|
||||||
|
return getattr(self, '_referrer', None)
|
||||||
|
|
||||||
|
def setReferrer(self, referrer):
|
||||||
|
self._referrer = referrer
|
||||||
|
|
||||||
|
def isTaskRoot(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def getChangingConfigurations(self):
|
||||||
|
return getattr(self, '_changing_config', 0)
|
||||||
|
|
||||||
|
def navigateUpTo(self, intent):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def shouldUpRecreateTask(self, intent):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def getParentActivityIntent(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def onOptionsMenuClosed(self, menu):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def setVolumeControlStream(self, streamType):
|
||||||
|
self._volume_control_stream = streamType
|
||||||
|
|
||||||
|
def getVolumeControlStream(self):
|
||||||
|
return getattr(self, '_volume_control_stream', 0)
|
||||||
|
|
||||||
|
def getLocalClassName(self):
|
||||||
|
return self.__class__.__name__
|
||||||
|
|
||||||
|
def finishAndRemoveTask(self):
|
||||||
|
self.finish()
|
||||||
|
|
||||||
|
def getLoaderManager(self):
|
||||||
|
if not hasattr(self, '_loader_manager'):
|
||||||
|
self._loader_manager = type('LoaderManager', (), {
|
||||||
|
'initLoader': lambda *a: None,
|
||||||
|
'restartLoader': lambda *a: None,
|
||||||
|
'destroyLoader': lambda *a: None,
|
||||||
|
'getLoader': lambda *a: None,
|
||||||
|
})()
|
||||||
|
return self._loader_manager
|
||||||
|
|
||||||
|
def onRetainNonConfigurationInstance(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def getLastNonConfigurationInstance(self):
|
||||||
|
return getattr(self, '_last_non_config_instance', None)
|
||||||
|
|
||||||
|
def setProgressBarVisibility(self, visible):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def setProgress(self, progress):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def setSecondaryProgress(self, secondaryProgress):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def onSearchRequested(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def startSearch(self, initialQuery, selectInitial, appSearchData, globalSearch):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def startIntentSender(self, intent, fillInIntent, flagsMask, flagsValues, extraFlags):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def startIntentSenderForResult(self, intent, requestCode, fillInIntent, flagsMask, flagsValues, extraFlags):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════
|
||||||
# Service
|
# Service
|
||||||
|
|||||||
@@ -11,9 +11,7 @@ from typing import Any, Dict, List, Optional, Set, Tuple, Callable
|
|||||||
from enum import IntEnum
|
from enum import IntEnum
|
||||||
|
|
||||||
from .._core import Stub, AndroidOnlyError
|
from .._core import Stub, AndroidOnlyError
|
||||||
from ..java.lang import RuntimeException
|
from ..java.lang import RuntimeException, IllegalArgumentException, IllegalStateException
|
||||||
|
|
||||||
IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {})
|
|
||||||
|
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════
|
||||||
@@ -424,11 +422,6 @@ class Fragment(Stub):
|
|||||||
return f"Fragment({self._mTag or hex(id(self))})"
|
return f"Fragment({self._mTag or hex(id(self))})"
|
||||||
|
|
||||||
|
|
||||||
class IllegalStateException(RuntimeException):
|
|
||||||
"""IllegalStateException — 非法状态异常"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════
|
||||||
# FragmentManager
|
# FragmentManager
|
||||||
# ═══════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════
|
||||||
|
|||||||
@@ -1116,10 +1116,16 @@ class SharedPreferences(Stub):
|
|||||||
return SharedPreferencesEditor(self)
|
return SharedPreferencesEditor(self)
|
||||||
|
|
||||||
def registerOnSharedPreferenceChangeListener(self, listener):
|
def registerOnSharedPreferenceChangeListener(self, listener):
|
||||||
pass
|
if not hasattr(self, '_listeners'):
|
||||||
|
self._listeners = []
|
||||||
|
self._listeners.append(listener)
|
||||||
|
|
||||||
def unregisterOnSharedPreferenceChangeListener(self, listener):
|
def unregisterOnSharedPreferenceChangeListener(self, listener):
|
||||||
pass
|
if hasattr(self, '_listeners'):
|
||||||
|
try:
|
||||||
|
self._listeners.remove(listener)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
def getSharedPrefsFile(self):
|
def getSharedPrefsFile(self):
|
||||||
return self._file_path
|
return self._file_path
|
||||||
@@ -1186,6 +1192,12 @@ class SharedPreferencesEditor(Stub):
|
|||||||
self._prefs._data.pop(key, None)
|
self._prefs._data.pop(key, None)
|
||||||
self._prefs._data.update(self._modified)
|
self._prefs._data.update(self._modified)
|
||||||
self._prefs._save()
|
self._prefs._save()
|
||||||
|
# 触发监听器
|
||||||
|
sp = getattr(self, '_prefs', None)
|
||||||
|
if sp:
|
||||||
|
for key in self._modified:
|
||||||
|
for listener in getattr(sp, '_listeners', []):
|
||||||
|
listener.onSharedPreferenceChanged(sp, key)
|
||||||
self._modified.clear()
|
self._modified.clear()
|
||||||
self._removed.clear()
|
self._removed.clear()
|
||||||
self._clear_all = False
|
self._clear_all = False
|
||||||
|
|||||||
@@ -761,16 +761,36 @@ class Intent(Stub):
|
|||||||
def getData(self) -> Optional[str]:
|
def getData(self) -> Optional[str]:
|
||||||
return self._data
|
return self._data
|
||||||
|
|
||||||
def setData(self, data: str) -> "Intent":
|
def setData(self, data) -> "Intent":
|
||||||
self._data = data
|
self._data = data
|
||||||
self._type = None
|
self._type = None
|
||||||
|
# 从 Uri 提取 scheme
|
||||||
|
if data is not None:
|
||||||
|
if hasattr(data, 'getScheme'):
|
||||||
|
self._scheme = data.getScheme() or ""
|
||||||
|
elif isinstance(data, str):
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
self._scheme = urlparse(data).scheme
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def getDataString(self) -> Optional[str]:
|
def getDataString(self) -> Optional[str]:
|
||||||
return self._data
|
if self._data is None:
|
||||||
|
return None
|
||||||
|
if isinstance(self._data, str):
|
||||||
|
return self._data
|
||||||
|
return str(self._data)
|
||||||
|
|
||||||
def getScheme(self) -> str:
|
def getScheme(self) -> str:
|
||||||
return self._scheme
|
if self._scheme:
|
||||||
|
return self._scheme
|
||||||
|
# 从 data 动态提取
|
||||||
|
if self._data is not None:
|
||||||
|
if hasattr(self._data, 'getScheme'):
|
||||||
|
return self._data.getScheme() or ""
|
||||||
|
elif isinstance(self._data, str):
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
return urlparse(self._data).scheme
|
||||||
|
return ""
|
||||||
|
|
||||||
# ── Type ──
|
# ── Type ──
|
||||||
|
|
||||||
|
|||||||
@@ -2049,6 +2049,69 @@ class Paint(Stub):
|
|||||||
def descent(self) -> float:
|
def descent(self) -> float:
|
||||||
return self._text_size * 0.2
|
return self._text_size * 0.2
|
||||||
|
|
||||||
|
def setStrokeCap(self, cap):
|
||||||
|
self._stroke_cap = cap
|
||||||
|
|
||||||
|
def getStrokeCap(self):
|
||||||
|
return getattr(self, '_stroke_cap', 0)
|
||||||
|
|
||||||
|
def setStrokeJoin(self, join):
|
||||||
|
self._stroke_join = join
|
||||||
|
|
||||||
|
def getStrokeJoin(self):
|
||||||
|
return getattr(self, '_stroke_join', 0)
|
||||||
|
|
||||||
|
def setStrokeMiter(self, miter):
|
||||||
|
self._stroke_miter = miter
|
||||||
|
|
||||||
|
def getStrokeMiter(self):
|
||||||
|
return getattr(self, '_stroke_miter', 4.0)
|
||||||
|
|
||||||
|
def getTextBounds(self, text, start, end, bounds):
|
||||||
|
if hasattr(self, '_text_size'):
|
||||||
|
w = len(text[start:end]) * self._text_size * 0.5
|
||||||
|
h = self._text_size
|
||||||
|
else:
|
||||||
|
w = len(text[start:end]) * 12
|
||||||
|
h = 24
|
||||||
|
bounds.left = 0
|
||||||
|
bounds.top = -int(h * 0.8)
|
||||||
|
bounds.right = int(w)
|
||||||
|
bounds.bottom = int(h * 0.2)
|
||||||
|
|
||||||
|
def setBlendMode(self, blendmode):
|
||||||
|
self._blend_mode = blendmode
|
||||||
|
|
||||||
|
def getBlendMode(self):
|
||||||
|
return getattr(self, '_blend_mode', None)
|
||||||
|
|
||||||
|
def setLetterSpacing(self, letterSpacing):
|
||||||
|
self._letter_spacing = letterSpacing
|
||||||
|
|
||||||
|
def getLetterSpacing(self):
|
||||||
|
return getattr(self, '_letter_spacing', 0.0)
|
||||||
|
|
||||||
|
def setFontFeatureSettings(self, settings):
|
||||||
|
self._font_feature_settings = settings
|
||||||
|
|
||||||
|
def getFontFeatureSettings(self):
|
||||||
|
return getattr(self, '_font_feature_settings', None)
|
||||||
|
|
||||||
|
def setHinting(self, mode):
|
||||||
|
self._hinting = mode
|
||||||
|
|
||||||
|
def getHinting(self):
|
||||||
|
return getattr(self, '_hinting', 2) # HINTING_ON
|
||||||
|
|
||||||
|
def setFakeBoldText(self, fakeBoldText):
|
||||||
|
self._fake_bold_text = fakeBoldText
|
||||||
|
|
||||||
|
def isFakeBoldText(self):
|
||||||
|
return getattr(self, '_fake_bold_text', False)
|
||||||
|
|
||||||
|
def hasGlyph(self, string):
|
||||||
|
return len(string) > 0
|
||||||
|
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════
|
||||||
# Bitmap
|
# Bitmap
|
||||||
|
|||||||
@@ -1817,6 +1817,7 @@ class ExifInterface(Stub):
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
self._filename = resolve_android_path(filename)
|
self._filename = resolve_android_path(filename)
|
||||||
self._attributes: Dict[str, str] = {}
|
self._attributes: Dict[str, str] = {}
|
||||||
|
self._pil_image = None
|
||||||
self._load_exif()
|
self._load_exif()
|
||||||
|
|
||||||
def _load_exif(self):
|
def _load_exif(self):
|
||||||
@@ -1828,6 +1829,7 @@ class ExifInterface(Stub):
|
|||||||
from PIL import ExifTags
|
from PIL import ExifTags
|
||||||
try:
|
try:
|
||||||
img = Image.open(self._filename)
|
img = Image.open(self._filename)
|
||||||
|
self._pil_image = img
|
||||||
exif_data = img._getexif()
|
exif_data = img._getexif()
|
||||||
if exif_data:
|
if exif_data:
|
||||||
for tag_id, value in exif_data.items():
|
for tag_id, value in exif_data.items():
|
||||||
@@ -1899,13 +1901,29 @@ class ExifInterface(Stub):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def flipHorizontally(self):
|
def flipHorizontally(self):
|
||||||
pass
|
if hasattr(self, '_pil_image') and self._pil_image:
|
||||||
|
from PIL import Image
|
||||||
|
self._pil_image = self._pil_image.transpose(Image.FLIP_LEFT_RIGHT)
|
||||||
|
self._save_from_pil()
|
||||||
|
|
||||||
def flipVertically(self):
|
def flipVertically(self):
|
||||||
pass
|
if hasattr(self, '_pil_image') and self._pil_image:
|
||||||
|
from PIL import Image
|
||||||
|
self._pil_image = self._pil_image.transpose(Image.FLIP_TOP_BOTTOM)
|
||||||
|
self._save_from_pil()
|
||||||
|
|
||||||
def rotate(self, degree: int):
|
def rotate(self, degrees):
|
||||||
pass
|
if hasattr(self, '_pil_image') and self._pil_image:
|
||||||
|
from PIL import Image
|
||||||
|
self._pil_image = self._pil_image.rotate(degrees, expand=True)
|
||||||
|
self._save_from_pil()
|
||||||
|
# 更新方向属性
|
||||||
|
self.setAttribute("Orientation", "1") # Normal after rotation
|
||||||
|
|
||||||
|
def _save_from_pil(self):
|
||||||
|
"""从 PIL 图像保存回文件"""
|
||||||
|
if hasattr(self, '_pil_image') and self._pil_image and hasattr(self, '_filename'):
|
||||||
|
self._pil_image.save(self._filename)
|
||||||
|
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════
|
||||||
|
|||||||
@@ -1079,53 +1079,98 @@ class TrafficStats(Stub):
|
|||||||
UNSUPPORTED = -1
|
UNSUPPORTED = -1
|
||||||
TAG_SYSTEM_UPDATE = 0
|
TAG_SYSTEM_UPDATE = 0
|
||||||
|
|
||||||
|
_cached_stats = None
|
||||||
|
_cache_time = 0
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def getMobileTxBytes() -> int:
|
def _read_net_dev():
|
||||||
|
"""读取 /proc/net/dev 获取网络流量"""
|
||||||
|
try:
|
||||||
|
with open("/proc/net/dev", "r") as f:
|
||||||
|
lines = f.readlines()[2:] # 跳过前两行表头
|
||||||
|
total_rx = 0
|
||||||
|
total_tx = 0
|
||||||
|
for line in lines:
|
||||||
|
parts = line.split()
|
||||||
|
ifname = parts[0].rstrip(":")
|
||||||
|
if ifname == "lo":
|
||||||
|
continue
|
||||||
|
rx_bytes = int(parts[1])
|
||||||
|
tx_bytes = int(parts[9])
|
||||||
|
total_rx += rx_bytes
|
||||||
|
total_tx += tx_bytes
|
||||||
|
return {"rx_bytes": total_rx, "tx_bytes": total_tx,
|
||||||
|
"rx_packets": 0, "tx_packets": 0}
|
||||||
|
except Exception:
|
||||||
|
return {"rx_bytes": 0, "tx_bytes": 0, "rx_packets": 0, "tx_packets": 0}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def getMobileRxBytes():
|
||||||
|
return 0 # 桌面无移动数据
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def getMobileTxBytes():
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def getMobileRxBytes() -> int:
|
def getMobileRxPackets():
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def getTotalTxBytes() -> int:
|
def getMobileTxPackets():
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def getTotalRxBytes() -> int:
|
def getTotalRxBytes():
|
||||||
return 0
|
return TrafficStats._read_net_dev()["rx_bytes"]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def getMobileTxPackets() -> int:
|
def getTotalTxBytes():
|
||||||
return 0
|
return TrafficStats._read_net_dev()["tx_bytes"]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def getMobileRxPackets() -> int:
|
def getTotalRxPackets():
|
||||||
return 0
|
return TrafficStats._read_net_dev()["rx_packets"]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def getTotalTxPackets() -> int:
|
def getTotalTxPackets():
|
||||||
return 0
|
return TrafficStats._read_net_dev()["tx_packets"]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def getTotalRxPackets() -> int:
|
def getUidRxBytes(uid):
|
||||||
return 0
|
try:
|
||||||
|
with open(f"/proc/uid_stat/{uid}/tcp_rcv", "r") as f:
|
||||||
|
return int(f.read().strip())
|
||||||
|
except Exception:
|
||||||
|
return 0
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def getUidTxBytes(uid: int) -> int:
|
def getUidTxBytes(uid):
|
||||||
return 0
|
try:
|
||||||
|
with open(f"/proc/uid_stat/{uid}/tcp_snd", "r") as f:
|
||||||
|
return int(f.read().strip())
|
||||||
|
except Exception:
|
||||||
|
return 0
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def getUidRxBytes(uid: int) -> int:
|
def getUidRxPackets(uid):
|
||||||
return 0
|
return TrafficStats.getUidRxBytes(uid)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def getUidTxPackets(uid: int) -> int:
|
def getUidTxPackets(uid):
|
||||||
return 0
|
return TrafficStats.getUidTxBytes(uid)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def getUidRxPackets(uid: int) -> int:
|
def getThreadStatsTag():
|
||||||
return 0
|
return getattr(TrafficStats, '_thread_tag', 0)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def setThreadStatsTag(tag):
|
||||||
|
TrafficStats._thread_tag = tag
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def clearThreadStatsTag():
|
||||||
|
TrafficStats._thread_tag = 0
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def getUidTcpTxBytes(uid: int) -> int:
|
def getUidTcpTxBytes(uid: int) -> int:
|
||||||
@@ -1143,18 +1188,6 @@ class TrafficStats(Stub):
|
|||||||
def getUidUdpRxBytes(uid: int) -> int:
|
def getUidUdpRxBytes(uid: int) -> int:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def setThreadStatsTag(tag: int):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def clearThreadStatsTag():
|
|
||||||
pass
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def getThreadStatsTag() -> int:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def setThreadStatsUid(uid: int):
|
def setThreadStatsUid(uid: int):
|
||||||
pass
|
pass
|
||||||
|
|||||||
+57
-14
@@ -316,20 +316,30 @@ class Process(Stub):
|
|||||||
return _os.getpid()
|
return _os.getpid()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def killProcess(pid: int):
|
def killProcess(pid):
|
||||||
pass
|
import signal
|
||||||
|
try:
|
||||||
|
_os.kill(pid, signal.SIGKILL)
|
||||||
|
except (OSError, ProcessLookupError):
|
||||||
|
pass
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def sendSignal(pid: int, signal: int):
|
def sendSignal(pid, signal_num):
|
||||||
pass
|
try:
|
||||||
|
_os.kill(pid, signal_num)
|
||||||
|
except (OSError, ProcessLookupError):
|
||||||
|
pass
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def setThreadPriority(priority: int):
|
def setThreadPriority(priority: int):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def setThreadPriority(tid: int, priority: int):
|
def setThreadPriority(tid, priority):
|
||||||
pass
|
try:
|
||||||
|
_os.setpriority(_os.PRIO_PROCESS, tid, priority)
|
||||||
|
except (OSError, PermissionError):
|
||||||
|
pass
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def getElapsedCpuTime() -> int:
|
def getElapsedCpuTime() -> int:
|
||||||
@@ -1925,20 +1935,53 @@ class FileUtils(Stub):
|
|||||||
"""FileUtils — 文件工具"""
|
"""FileUtils — 文件工具"""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def readFileToString(file: Any) -> str:
|
def readFileToString(path):
|
||||||
return ""
|
try:
|
||||||
|
with open(path, "r", encoding="utf-8") as f:
|
||||||
|
return f.read()
|
||||||
|
except Exception:
|
||||||
|
return ""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def stringToFile(file: Any, string: str):
|
def stringToFile(path, content):
|
||||||
pass
|
try:
|
||||||
|
with open(path, "w", encoding="utf-8") as f:
|
||||||
|
f.write(content)
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def copyFile(src: Any, dest: Any) -> bool:
|
def copyFile(src, dst):
|
||||||
return True
|
try:
|
||||||
|
import shutil
|
||||||
|
shutil.copy2(src, dst)
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def deleteDirectory(dir_: Any) -> bool:
|
def deleteDirectory(path):
|
||||||
return True
|
try:
|
||||||
|
import shutil
|
||||||
|
shutil.rmtree(path)
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def deleteContents(path):
|
||||||
|
try:
|
||||||
|
import shutil
|
||||||
|
for entry in _os.listdir(path):
|
||||||
|
full = _os.path.join(path, entry)
|
||||||
|
if _os.path.isdir(full):
|
||||||
|
shutil.rmtree(full)
|
||||||
|
else:
|
||||||
|
_os.remove(full)
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def deleteOlderFiles(dir_: Any, min_count: int, min_age_ms: int):
|
def deleteOlderFiles(dir_: Any, min_count: int, min_age_ms: int):
|
||||||
|
|||||||
@@ -661,8 +661,10 @@ class TelephonyManager(Stub):
|
|||||||
def getMmsUAProfUrl(self) -> str:
|
def getMmsUAProfUrl(self) -> str:
|
||||||
return "http://www.example.com/uaprof.xml"
|
return "http://www.example.com/uaprof.xml"
|
||||||
|
|
||||||
def listen(self, listener: PhoneStateListener, events: int):
|
def listen(self, listener, events):
|
||||||
pass
|
if not hasattr(self, '_listeners'):
|
||||||
|
self._listeners = []
|
||||||
|
self._listeners.append((listener, events))
|
||||||
|
|
||||||
def getDataState(self) -> int:
|
def getDataState(self) -> int:
|
||||||
return self.DATA_CONNECTED
|
return self.DATA_CONNECTED
|
||||||
|
|||||||
@@ -938,6 +938,106 @@ class View(Stub):
|
|||||||
def onApplyWindowInsets(self, insets):
|
def onApplyWindowInsets(self, insets):
|
||||||
return insets
|
return insets
|
||||||
|
|
||||||
|
def setOnCreateContextMenuListener(self, listener):
|
||||||
|
self._on_create_context_menu_listener = listener
|
||||||
|
return self
|
||||||
|
|
||||||
|
def getBaseline(self):
|
||||||
|
return -1
|
||||||
|
|
||||||
|
def setFocusableInTouchMode(self, focusable):
|
||||||
|
self._focusable_in_touch_mode = focusable
|
||||||
|
|
||||||
|
def isFocusableInTouchMode(self):
|
||||||
|
return getattr(self, '_focusable_in_touch_mode', False)
|
||||||
|
|
||||||
|
def setRotationX(self, rotationX):
|
||||||
|
self._rotation_x = rotationX
|
||||||
|
|
||||||
|
def getRotationX(self):
|
||||||
|
return getattr(self, '_rotation_x', 0.0)
|
||||||
|
|
||||||
|
def setRotationY(self, rotationY):
|
||||||
|
self._rotation_y = rotationY
|
||||||
|
|
||||||
|
def getRotationY(self):
|
||||||
|
return getattr(self, '_rotation_y', 0.0)
|
||||||
|
|
||||||
|
def setCameraDistance(self, distance):
|
||||||
|
self._camera_distance = distance
|
||||||
|
|
||||||
|
def getCameraDistance(self):
|
||||||
|
return getattr(self, '_camera_distance', 0.0)
|
||||||
|
|
||||||
|
def setForeground(self, foreground):
|
||||||
|
self._foreground = foreground
|
||||||
|
|
||||||
|
def getForeground(self):
|
||||||
|
return getattr(self, '_foreground', None)
|
||||||
|
|
||||||
|
def setBackgroundTintList(self, tint):
|
||||||
|
self._background_tint = tint
|
||||||
|
|
||||||
|
def setBackgroundTintMode(self, tintMode):
|
||||||
|
self._background_tint_mode = tintMode
|
||||||
|
|
||||||
|
def setScrollbarFadingEnabled(self, fadeScrollbars):
|
||||||
|
self._fade_scrollbars = fadeScrollbars
|
||||||
|
|
||||||
|
def isScrollbarFadingEnabled(self):
|
||||||
|
return getattr(self, '_fade_scrollbars', True)
|
||||||
|
|
||||||
|
def setVerticalScrollBarEnabled(self, enabled):
|
||||||
|
self._vertical_scrollbar = enabled
|
||||||
|
|
||||||
|
def isVerticalScrollBarEnabled(self):
|
||||||
|
return getattr(self, '_vertical_scrollbar', True)
|
||||||
|
|
||||||
|
def setHorizontalScrollBarEnabled(self, enabled):
|
||||||
|
self._horizontal_scrollbar = enabled
|
||||||
|
|
||||||
|
def isHorizontalScrollBarEnabled(self):
|
||||||
|
return getattr(self, '_horizontal_scrollbar', True)
|
||||||
|
|
||||||
|
def setLabelFor(self, id):
|
||||||
|
self._label_for = id
|
||||||
|
|
||||||
|
def getLabelFor(self):
|
||||||
|
return getattr(self, '_label_for', 0)
|
||||||
|
|
||||||
|
def cancelLongPress(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def setPaddingRelative(self, start, top, end, bottom):
|
||||||
|
self._padding_start = start
|
||||||
|
self._padding_top = top
|
||||||
|
self._padding_end = end
|
||||||
|
self._padding_bottom = bottom
|
||||||
|
|
||||||
|
def setSoundEffectsEnabled(self, soundEffectsEnabled):
|
||||||
|
self._sound_effects = soundEffectsEnabled
|
||||||
|
|
||||||
|
def isSoundEffectsEnabled(self):
|
||||||
|
return getattr(self, '_sound_effects', True)
|
||||||
|
|
||||||
|
def setHapticFeedbackEnabled(self, hapticFeedbackEnabled):
|
||||||
|
self._haptic_feedback = hapticFeedbackEnabled
|
||||||
|
|
||||||
|
def isHapticFeedbackEnabled(self):
|
||||||
|
return getattr(self, '_haptic_feedback', True)
|
||||||
|
|
||||||
|
def refreshDrawableState(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def getDrawableState(self):
|
||||||
|
return []
|
||||||
|
|
||||||
|
def requestRectangleOnScreen(self, rectangle):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def setAllCaps(self, allCaps):
|
||||||
|
self._all_caps = allCaps
|
||||||
|
|
||||||
|
|
||||||
class MeasureSpec:
|
class MeasureSpec:
|
||||||
"""MeasureSpec — 测量规格包装"""
|
"""MeasureSpec — 测量规格包装"""
|
||||||
@@ -1993,6 +2093,28 @@ class KeyEvent(Stub):
|
|||||||
meta_state=event._meta_state,
|
meta_state=event._meta_state,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def getCharacters(self):
|
||||||
|
return getattr(self, '_characters', None)
|
||||||
|
|
||||||
|
def isSystem(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def isPrintingKey(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def isModifierKey(self, keyCode):
|
||||||
|
return keyCode in (KEYCODE_SHIFT_LEFT, KEYCODE_SHIFT_RIGHT, KEYCODE_ALT_LEFT, KEYCODE_ALT_RIGHT, KEYCODE_CTRL_LEFT, KEYCODE_CTRL_RIGHT, KEYCODE_META_LEFT, KEYCODE_META_RIGHT)
|
||||||
|
|
||||||
|
def startTracking(self):
|
||||||
|
self._tracking = True
|
||||||
|
|
||||||
|
def isTracking(self):
|
||||||
|
return getattr(self, '_tracking', False)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def keyCodeToString(keyCode):
|
||||||
|
return f"KEYCODE_{keyCode}"
|
||||||
|
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════
|
||||||
# GestureDetector
|
# GestureDetector
|
||||||
@@ -2700,14 +2822,18 @@ class SurfaceHolder(Stub):
|
|||||||
def setFormat(self, format: int):
|
def setFormat(self, format: int):
|
||||||
self._format = format
|
self._format = format
|
||||||
|
|
||||||
def lockCanvas(self) -> Any:
|
def lockCanvas(self, *args):
|
||||||
return None
|
"""锁定画布并返回 Canvas 对象"""
|
||||||
|
if hasattr(self, '_surface') and self._surface:
|
||||||
|
from .graphics import Canvas, Bitmap
|
||||||
|
if hasattr(self._surface, '_bitmap') and self._surface._bitmap:
|
||||||
|
return Canvas(self._surface._bitmap)
|
||||||
|
from .graphics import Canvas, Bitmap
|
||||||
|
return Canvas(Bitmap(100, 100))
|
||||||
|
|
||||||
def lockCanvas(self, dirty: Any) -> Any:
|
def unlockCanvasAndPost(self, canvas):
|
||||||
return None
|
"""解锁画布并提交"""
|
||||||
|
pass # 桌面环境无需实际提交
|
||||||
def unlockCanvasAndPost(self, canvas: Any):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union
|
|||||||
|
|
||||||
from .._core import Stub, AndroidOnlyError
|
from .._core import Stub, AndroidOnlyError
|
||||||
from ..java.lang import RuntimeException
|
from ..java.lang import RuntimeException
|
||||||
|
from .view import KeyEvent as _BaseKeyEvent
|
||||||
|
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════
|
||||||
@@ -1559,8 +1560,8 @@ class GestureDetector(Stub):
|
|||||||
return f"GestureDetector(listener={'yes' if self._listener else 'no'})"
|
return f"GestureDetector(listener={'yes' if self._listener else 'no'})"
|
||||||
|
|
||||||
|
|
||||||
class KeyEvent(Stub):
|
class KeyEvent(_BaseKeyEvent):
|
||||||
"""KeyEvent — 按键事件"""
|
"""KeyEvent — 按键事件 (扩展版,继承 view.KeyEvent 的所有常量)"""
|
||||||
|
|
||||||
ACTION_DOWN = 0
|
ACTION_DOWN = 0
|
||||||
ACTION_UP = 1
|
ACTION_UP = 1
|
||||||
|
|||||||
@@ -1260,28 +1260,48 @@ class RecyclerView(ViewGroup):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def notifyDataSetChanged(self):
|
def notifyDataSetChanged(self):
|
||||||
pass
|
for observer in getattr(self, '_observers', []):
|
||||||
|
observer.onChanged()
|
||||||
|
|
||||||
def notifyItemChanged(self, position: int):
|
def notifyItemChanged(self, position, payload=None):
|
||||||
pass
|
for observer in getattr(self, '_observers', []):
|
||||||
|
observer.onItemRangeChanged(position, 1, payload)
|
||||||
|
|
||||||
def notifyItemInserted(self, position: int):
|
def notifyItemInserted(self, position):
|
||||||
pass
|
for observer in getattr(self, '_observers', []):
|
||||||
|
observer.onItemRangeInserted(position, 1)
|
||||||
|
|
||||||
def notifyItemRemoved(self, position: int):
|
def notifyItemRemoved(self, position):
|
||||||
pass
|
for observer in getattr(self, '_observers', []):
|
||||||
|
observer.onItemRangeRemoved(position, 1)
|
||||||
|
|
||||||
def notifyItemMoved(self, from_position: int, to_position: int):
|
def notifyItemMoved(self, fromPosition, toPosition):
|
||||||
pass
|
for observer in getattr(self, '_observers', []):
|
||||||
|
observer.onItemRangeMoved(fromPosition, toPosition, 1)
|
||||||
|
|
||||||
def notifyItemRangeChanged(self, position_start: int, item_count: int):
|
def notifyItemRangeChanged(self, positionStart, itemCount, payload=None):
|
||||||
pass
|
for observer in getattr(self, '_observers', []):
|
||||||
|
observer.onItemRangeChanged(positionStart, itemCount, payload)
|
||||||
|
|
||||||
def notifyItemRangeInserted(self, position_start: int, item_count: int):
|
def notifyItemRangeInserted(self, positionStart, itemCount):
|
||||||
pass
|
for observer in getattr(self, '_observers', []):
|
||||||
|
observer.onItemRangeInserted(positionStart, itemCount)
|
||||||
|
|
||||||
def notifyItemRangeRemoved(self, position_start: int, item_count: int):
|
def notifyItemRangeRemoved(self, positionStart, itemCount):
|
||||||
pass
|
for observer in getattr(self, '_observers', []):
|
||||||
|
observer.onItemRangeRemoved(positionStart, itemCount)
|
||||||
|
|
||||||
|
def registerAdapterDataObserver(self, observer):
|
||||||
|
if not hasattr(self, '_observers'):
|
||||||
|
self._observers = []
|
||||||
|
self._observers.append(observer)
|
||||||
|
|
||||||
|
def unregisterAdapterDataObserver(self, observer):
|
||||||
|
if hasattr(self, '_observers'):
|
||||||
|
try:
|
||||||
|
self._observers.remove(observer)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
def getStateRestorationPolicy(self) -> int:
|
def getStateRestorationPolicy(self) -> int:
|
||||||
return 0
|
return 0
|
||||||
@@ -2068,11 +2088,13 @@ class ScrollView(FrameLayout):
|
|||||||
def arrowScroll(self, direction: int) -> bool:
|
def arrowScroll(self, direction: int) -> bool:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def smoothScrollTo(self, x: int, y: int):
|
def smoothScrollTo(self, x, y):
|
||||||
self._scroll_position = y
|
self._scroll_x = x
|
||||||
|
self._scroll_y = y
|
||||||
|
|
||||||
def smoothScrollBy(self, dx: int, dy: int):
|
def smoothScrollBy(self, dx, dy):
|
||||||
self._scroll_position += dy
|
self._scroll_x = getattr(self, '_scroll_x', 0) + dx
|
||||||
|
self._scroll_y = getattr(self, '_scroll_y', 0) + dy
|
||||||
|
|
||||||
def pageScroll(self, direction: int) -> bool:
|
def pageScroll(self, direction: int) -> bool:
|
||||||
return True
|
return True
|
||||||
@@ -2106,11 +2128,13 @@ class HorizontalScrollView(FrameLayout):
|
|||||||
def arrowScroll(self, direction: int) -> bool:
|
def arrowScroll(self, direction: int) -> bool:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def smoothScrollTo(self, x: int, y: int):
|
def smoothScrollTo(self, x, y):
|
||||||
pass
|
self._scroll_x = x
|
||||||
|
self._scroll_y = y
|
||||||
|
|
||||||
def smoothScrollBy(self, dx: int, dy: int):
|
def smoothScrollBy(self, dx, dy):
|
||||||
pass
|
self._scroll_x = getattr(self, '_scroll_x', 0) + dx
|
||||||
|
self._scroll_y = getattr(self, '_scroll_y', 0) + dy
|
||||||
|
|
||||||
def pageScroll(self, direction: int) -> bool:
|
def pageScroll(self, direction: int) -> bool:
|
||||||
return True
|
return True
|
||||||
@@ -2795,16 +2819,24 @@ class BaseAdapter(Stub):
|
|||||||
return self.getView(position, convert_view, parent)
|
return self.getView(position, convert_view, parent)
|
||||||
|
|
||||||
def notifyDataSetChanged(self):
|
def notifyDataSetChanged(self):
|
||||||
pass
|
for observer in getattr(self, '_observers', []):
|
||||||
|
observer.onChanged()
|
||||||
|
|
||||||
def notifyDataSetInvalidated(self):
|
def notifyDataSetInvalidated(self):
|
||||||
pass
|
for observer in getattr(self, '_observers', []):
|
||||||
|
observer.onInvalidated()
|
||||||
|
|
||||||
def registerDataSetObserver(self, observer: Any):
|
def registerDataSetObserver(self, observer):
|
||||||
pass
|
if not hasattr(self, '_observers'):
|
||||||
|
self._observers = []
|
||||||
|
self._observers.append(observer)
|
||||||
|
|
||||||
def unregisterDataSetObserver(self, observer: Any):
|
def unregisterDataSetObserver(self, observer):
|
||||||
pass
|
if hasattr(self, '_observers'):
|
||||||
|
try:
|
||||||
|
self._observers.remove(observer)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ArrayAdapter(BaseAdapter):
|
class ArrayAdapter(BaseAdapter):
|
||||||
|
|||||||
@@ -237,7 +237,10 @@ class File(Stub):
|
|||||||
return _os.path.abspath(self._path)
|
return _os.path.abspath(self._path)
|
||||||
|
|
||||||
def getCanonicalPath(self):
|
def getCanonicalPath(self):
|
||||||
return _os.path.realpath(self._path)
|
try:
|
||||||
|
return _os.path.realpath(self._path)
|
||||||
|
except Exception:
|
||||||
|
return self.getAbsolutePath()
|
||||||
|
|
||||||
def getParent(self):
|
def getParent(self):
|
||||||
p = _os.path.dirname(self._path)
|
p = _os.path.dirname(self._path)
|
||||||
@@ -439,6 +442,43 @@ class File(Stub):
|
|||||||
def toPath(self):
|
def toPath(self):
|
||||||
return _PathLibPath(self._path)
|
return _PathLibPath(self._path)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def createTempFile(prefix, suffix=None, directory=None):
|
||||||
|
import tempfile
|
||||||
|
if directory:
|
||||||
|
fd, path = tempfile.mkstemp(prefix=prefix, suffix=suffix or ".tmp", dir=directory)
|
||||||
|
else:
|
||||||
|
fd, path = tempfile.mkstemp(prefix=prefix, suffix=suffix or ".tmp")
|
||||||
|
_os.close(fd)
|
||||||
|
return File(path)
|
||||||
|
|
||||||
|
def deleteOnExit(self):
|
||||||
|
import atexit
|
||||||
|
atexit.register(lambda: self.delete() if self.exists() else None)
|
||||||
|
|
||||||
|
def setLastModified(self, time):
|
||||||
|
try:
|
||||||
|
_os.utime(self._path, (time / 1000, time / 1000))
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def setReadOnly(self):
|
||||||
|
try:
|
||||||
|
_os.chmod(self._path, 0o444)
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def isAbsolute(self):
|
||||||
|
return _os.path.isabs(self._path)
|
||||||
|
|
||||||
|
def getAbsoluteFile(self):
|
||||||
|
return File(self.getAbsolutePath())
|
||||||
|
|
||||||
|
def getCanonicalFile(self):
|
||||||
|
return File(self.getCanonicalPath())
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self._path
|
return self._path
|
||||||
|
|
||||||
|
|||||||
+344
-2
@@ -8,6 +8,7 @@ import re as _re
|
|||||||
import math as _py_math
|
import math as _py_math
|
||||||
import sys as _sys
|
import sys as _sys
|
||||||
import os as _os
|
import os as _os
|
||||||
|
import platform
|
||||||
import time as _time
|
import time as _time
|
||||||
import threading as _threading
|
import threading as _threading
|
||||||
import inspect as _inspect
|
import inspect as _inspect
|
||||||
@@ -28,6 +29,12 @@ __all__ = [
|
|||||||
"CloneNotSupportedException", "IllegalMonitorStateException",
|
"CloneNotSupportedException", "IllegalMonitorStateException",
|
||||||
"IllegalThreadStateException", "EnumConstantNotPresentException",
|
"IllegalThreadStateException", "EnumConstantNotPresentException",
|
||||||
"TypeNotPresentException",
|
"TypeNotPresentException",
|
||||||
|
"ClassNotFoundException", "IllegalAccessException", "InstantiationException",
|
||||||
|
"InvocationTargetException", "NoSuchElementException",
|
||||||
|
"AssertionError", "OutOfMemoryError", "StackOverflowError",
|
||||||
|
"NoClassDefFoundError", "UnsatisfiedLinkError", "LinkageError",
|
||||||
|
"VerifyError", "AbstractMethodError", "IncompatibleClassChangeError",
|
||||||
|
"ExceptionInInitializerError",
|
||||||
"String", "Integer", "Long", "Short", "Byte", "Float", "Double",
|
"String", "Integer", "Long", "Short", "Byte", "Float", "Double",
|
||||||
"Boolean", "Character", "Math", "StrictMath", "System",
|
"Boolean", "Character", "Math", "StrictMath", "System",
|
||||||
"Thread", "ThreadLocal", "StringBuilder", "StringBuffer",
|
"Thread", "ThreadLocal", "StringBuilder", "StringBuffer",
|
||||||
@@ -227,6 +234,80 @@ class TypeNotPresentException(RuntimeException):
|
|||||||
self.typeName = typeName
|
self.typeName = typeName
|
||||||
|
|
||||||
|
|
||||||
|
# ===== 缺失的 Exception 子类 =====
|
||||||
|
class ClassNotFoundException(Exception):
|
||||||
|
def __init__(self, message="", cause=None):
|
||||||
|
super().__init__(message, cause)
|
||||||
|
self.ex = message
|
||||||
|
|
||||||
|
class IllegalAccessException(Exception):
|
||||||
|
def __init__(self, message=""):
|
||||||
|
super().__init__(message)
|
||||||
|
|
||||||
|
class InstantiationException(Exception):
|
||||||
|
def __init__(self, message=""):
|
||||||
|
super().__init__(message)
|
||||||
|
|
||||||
|
class InvocationTargetException(Exception):
|
||||||
|
def __init__(self, target=None, cause=None):
|
||||||
|
if target is not None and cause is None:
|
||||||
|
super().__init__(str(target))
|
||||||
|
self.target = target
|
||||||
|
else:
|
||||||
|
super().__init__(str(target) if target else "", cause)
|
||||||
|
self.target = target
|
||||||
|
|
||||||
|
class NoSuchElementException(RuntimeException):
|
||||||
|
def __init__(self, message=""):
|
||||||
|
super().__init__(message)
|
||||||
|
|
||||||
|
# ===== 缺失的 Error 类 =====
|
||||||
|
class AssertionError(Error):
|
||||||
|
def __init__(self, message=None):
|
||||||
|
if message is None:
|
||||||
|
super().__init__("Assertion failed")
|
||||||
|
else:
|
||||||
|
super().__init__(str(message))
|
||||||
|
|
||||||
|
class OutOfMemoryError(Error):
|
||||||
|
def __init__(self, message=""):
|
||||||
|
super().__init__(message)
|
||||||
|
|
||||||
|
class StackOverflowError(Error):
|
||||||
|
def __init__(self, message=""):
|
||||||
|
super().__init__(message)
|
||||||
|
|
||||||
|
class NoClassDefFoundError(Error):
|
||||||
|
def __init__(self, message=""):
|
||||||
|
super().__init__(message)
|
||||||
|
|
||||||
|
class UnsatisfiedLinkError(Error):
|
||||||
|
def __init__(self, message=""):
|
||||||
|
super().__init__(message)
|
||||||
|
|
||||||
|
class LinkageError(Error):
|
||||||
|
def __init__(self, message=""):
|
||||||
|
super().__init__(message)
|
||||||
|
|
||||||
|
class VerifyError(LinkageError):
|
||||||
|
def __init__(self, message=""):
|
||||||
|
super().__init__(message)
|
||||||
|
|
||||||
|
class AbstractMethodError(LinkageError):
|
||||||
|
def __init__(self, message=""):
|
||||||
|
super().__init__(message)
|
||||||
|
|
||||||
|
class IncompatibleClassChangeError(LinkageError):
|
||||||
|
def __init__(self, message=""):
|
||||||
|
super().__init__(message)
|
||||||
|
|
||||||
|
class ExceptionInInitializerError(LinkageError):
|
||||||
|
def __init__(self, message="", cause=None):
|
||||||
|
super().__init__(message)
|
||||||
|
self._cause = cause
|
||||||
|
self.exception = cause
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# StackTraceElement
|
# StackTraceElement
|
||||||
# ============================================================
|
# ============================================================
|
||||||
@@ -753,6 +834,62 @@ class Integer(Number, Comparable):
|
|||||||
return Integer(int(env_val))
|
return Integer(int(env_val))
|
||||||
return Integer(val) if val is not None else None
|
return Integer(val) if val is not None else None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def compare(x, y):
|
||||||
|
return -1 if x < y else (1 if x > y else 0)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def compareUnsigned(x, y):
|
||||||
|
ux = x & 0xFFFFFFFF if x < 0 else x
|
||||||
|
uy = y & 0xFFFFFFFF if y < 0 else y
|
||||||
|
return -1 if ux < uy else (1 if ux > uy else 0)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def divideUnsigned(dividend, divisor):
|
||||||
|
ux = dividend & 0xFFFFFFFF if dividend < 0 else dividend
|
||||||
|
uy = divisor & 0xFFFFFFFF if divisor < 0 else divisor
|
||||||
|
return ux // uy
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def remainderUnsigned(dividend, divisor):
|
||||||
|
ux = dividend & 0xFFFFFFFF if dividend < 0 else dividend
|
||||||
|
uy = divisor & 0xFFFFFFFF if divisor < 0 else divisor
|
||||||
|
return ux % uy
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parseUnsignedInt(s, radix=10):
|
||||||
|
return int(s, radix)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def toUnsignedString(i, radix=10):
|
||||||
|
if i < 0:
|
||||||
|
i = i & 0xFFFFFFFF
|
||||||
|
if radix == 10:
|
||||||
|
return str(i)
|
||||||
|
elif radix == 16:
|
||||||
|
return format(i, 'x')
|
||||||
|
elif radix == 2:
|
||||||
|
return format(i, 'b')
|
||||||
|
elif radix == 8:
|
||||||
|
return format(i, 'o')
|
||||||
|
return str(i)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def toUnsignedLong(i):
|
||||||
|
return i & 0xFFFFFFFF
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def sum(a, b):
|
||||||
|
return a + b
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def max(a, b):
|
||||||
|
return a if a > b else b
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def min(a, b):
|
||||||
|
return a if a < b else b
|
||||||
|
|
||||||
def toString(self):
|
def toString(self):
|
||||||
return String(str(self._value))
|
return String(str(self._value))
|
||||||
|
|
||||||
@@ -1418,9 +1555,92 @@ class Math:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def copySign(m, s): return abs(m) * (1 if s >= 0 else -1)
|
def copySign(m, s): return abs(m) * (1 if s >= 0 else -1)
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def floorDiv(x, y): return x // y if (x ^ y) >= 0 else -(abs(x) // abs(y))
|
def floorDiv(x, y):
|
||||||
|
return math.floor(x / y) if y != 0 else 0
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def floorMod(x, y): return x - Math.floorDiv(x, y) * y
|
def floorMod(x, y):
|
||||||
|
return x - Math.floorDiv(x, y) * y
|
||||||
|
@staticmethod
|
||||||
|
def subtractExact(x, y):
|
||||||
|
result = x - y
|
||||||
|
if isinstance(x, int) and isinstance(y, int):
|
||||||
|
if result > 0 and x < 0 and y > 0:
|
||||||
|
raise ArithmeticException("integer overflow")
|
||||||
|
if result < 0 and x > 0 and y < 0:
|
||||||
|
raise ArithmeticException("integer overflow")
|
||||||
|
return result
|
||||||
|
@staticmethod
|
||||||
|
def multiplyExact(x, y):
|
||||||
|
result = x * y
|
||||||
|
if isinstance(x, int) and isinstance(y, int):
|
||||||
|
if result // y != x:
|
||||||
|
raise ArithmeticException("integer overflow")
|
||||||
|
return result
|
||||||
|
@staticmethod
|
||||||
|
def incrementExact(a):
|
||||||
|
if a == 2147483647:
|
||||||
|
raise ArithmeticException("integer overflow")
|
||||||
|
return a + 1
|
||||||
|
@staticmethod
|
||||||
|
def decrementExact(a):
|
||||||
|
if a == -2147483648:
|
||||||
|
raise ArithmeticException("integer overflow")
|
||||||
|
return a - 1
|
||||||
|
@staticmethod
|
||||||
|
def negateExact(a):
|
||||||
|
if a == -2147483648:
|
||||||
|
raise ArithmeticException("integer overflow")
|
||||||
|
return -a
|
||||||
|
@staticmethod
|
||||||
|
def toIntExact(value):
|
||||||
|
if value < -2147483648 or value > 2147483647:
|
||||||
|
raise ArithmeticException("integer overflow")
|
||||||
|
return int(value)
|
||||||
|
@staticmethod
|
||||||
|
def log1p(x):
|
||||||
|
return math.log1p(x)
|
||||||
|
@staticmethod
|
||||||
|
def expm1(x):
|
||||||
|
return math.expm1(x)
|
||||||
|
@staticmethod
|
||||||
|
def ulp(x):
|
||||||
|
return math.ulp(x) if hasattr(math, 'ulp') else abs(x) * 2.220446049250313e-16
|
||||||
|
@staticmethod
|
||||||
|
def getExponent(x):
|
||||||
|
return math.frexp(x)[1] - 1
|
||||||
|
@staticmethod
|
||||||
|
def nextAfter(start, direction):
|
||||||
|
return math.nextafter(start, direction)
|
||||||
|
@staticmethod
|
||||||
|
def nextUp(x):
|
||||||
|
return math.nextafter(x, math.inf)
|
||||||
|
@staticmethod
|
||||||
|
def nextDown(x):
|
||||||
|
return math.nextafter(x, -math.inf)
|
||||||
|
@staticmethod
|
||||||
|
def rint(x):
|
||||||
|
return math.rint(x) if hasattr(math, 'rint') else float(math.floor(x + 0.5))
|
||||||
|
@staticmethod
|
||||||
|
def IEEEremainder(x, y):
|
||||||
|
return math.remainder(x, y) if hasattr(math, 'remainder') else x - y * math.floor(x / y)
|
||||||
|
@staticmethod
|
||||||
|
def compareUnsigned(x, y):
|
||||||
|
ux = x & 0xFFFFFFFF if x < 0 else x
|
||||||
|
uy = y & 0xFFFFFFFF if y < 0 else y
|
||||||
|
return -1 if ux < uy else (1 if ux > uy else 0)
|
||||||
|
@staticmethod
|
||||||
|
def divideUnsigned(dividend, divisor):
|
||||||
|
ux = dividend & 0xFFFFFFFF if dividend < 0 else dividend
|
||||||
|
uy = divisor & 0xFFFFFFFF if divisor < 0 else divisor
|
||||||
|
return ux // uy
|
||||||
|
@staticmethod
|
||||||
|
def remainderUnsigned(dividend, divisor):
|
||||||
|
ux = dividend & 0xFFFFFFFF if dividend < 0 else dividend
|
||||||
|
uy = divisor & 0xFFFFFFFF if divisor < 0 else divisor
|
||||||
|
return ux % uy
|
||||||
|
@staticmethod
|
||||||
|
def fma(a, b, c):
|
||||||
|
return a * b + c
|
||||||
|
|
||||||
|
|
||||||
class StrictMath(Math):
|
class StrictMath(Math):
|
||||||
@@ -1529,6 +1749,39 @@ class System:
|
|||||||
System.err = err_stream
|
System.err = err_stream
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def lineSeparator(): return _os.linesep
|
def lineSeparator(): return _os.linesep
|
||||||
|
@staticmethod
|
||||||
|
def getProperties():
|
||||||
|
return {
|
||||||
|
"java.version": "1.8.0",
|
||||||
|
"java.vendor": "Android-pyApi",
|
||||||
|
"java.vendor.url": "https://gitee.com/shuiyu1123/Android-pyApi",
|
||||||
|
"java.home": _os.environ.get("JAVA_HOME", ""),
|
||||||
|
"java.class.version": "52.0",
|
||||||
|
"os.name": platform.system(),
|
||||||
|
"os.arch": platform.machine(),
|
||||||
|
"os.version": platform.release(),
|
||||||
|
"file.separator": _os.sep,
|
||||||
|
"path.separator": _os.pathsep,
|
||||||
|
"line.separator": _os.linesep,
|
||||||
|
"user.name": _os.environ.get("USER", _os.environ.get("USERNAME", "")),
|
||||||
|
"user.home": _os.path.expanduser("~"),
|
||||||
|
"user.dir": _os.getcwd(),
|
||||||
|
}
|
||||||
|
@staticmethod
|
||||||
|
def setProperties(props):
|
||||||
|
System._properties = props
|
||||||
|
@staticmethod
|
||||||
|
def mapLibraryName(libname):
|
||||||
|
if platform.system() == "Windows":
|
||||||
|
return libname + ".dll"
|
||||||
|
elif platform.system() == "Darwin":
|
||||||
|
return "lib" + libname + ".dylib"
|
||||||
|
else:
|
||||||
|
return "lib" + libname + ".so"
|
||||||
|
@staticmethod
|
||||||
|
def getLogger(name):
|
||||||
|
import logging
|
||||||
|
return logging.getLogger(name)
|
||||||
in_ = _sys.stdin
|
in_ = _sys.stdin
|
||||||
out = _sys.stdout
|
out = _sys.stdout
|
||||||
err = _sys.stderr
|
err = _sys.stderr
|
||||||
@@ -1683,6 +1936,66 @@ class Thread(Runnable):
|
|||||||
def getDefaultUncaughtExceptionHandler():
|
def getDefaultUncaughtExceptionHandler():
|
||||||
return Thread._default_uncaught_exception_handler
|
return Thread._default_uncaught_exception_handler
|
||||||
|
|
||||||
|
def getUncaughtExceptionHandler(self):
|
||||||
|
return getattr(self, '_uncaught_handler', Thread._default_uncaught_exception_handler)
|
||||||
|
|
||||||
|
def setUncaughtExceptionHandler(self, handler):
|
||||||
|
self._uncaught_handler = handler
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def getAllStackTraces():
|
||||||
|
import traceback
|
||||||
|
result = {}
|
||||||
|
# 只返回当前线程
|
||||||
|
result[Thread.currentThread()] = traceback.extract_stack()
|
||||||
|
return result
|
||||||
|
|
||||||
|
def getStackTrace(self):
|
||||||
|
import traceback
|
||||||
|
return traceback.extract_stack()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def enumerate(array):
|
||||||
|
# 返回活动线程数
|
||||||
|
return 0
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def activeCount():
|
||||||
|
import threading
|
||||||
|
return threading.active_count()
|
||||||
|
|
||||||
|
def getContextClassLoader(self):
|
||||||
|
return getattr(self, '_context_classloader', None)
|
||||||
|
|
||||||
|
def setContextClassLoader(self, cl):
|
||||||
|
self._context_classloader = cl
|
||||||
|
|
||||||
|
def getThreadGroup(self):
|
||||||
|
return getattr(self, '_thread_group', None)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def holdLock(obj):
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def dumpStack():
|
||||||
|
import traceback
|
||||||
|
traceback.print_stack()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def onSpinWait():
|
||||||
|
pass
|
||||||
|
|
||||||
|
def isVirtual(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def ofVirtual():
|
||||||
|
"""创建虚拟线程 (Java 21+)"""
|
||||||
|
t = Thread(target=lambda: None)
|
||||||
|
t._virtual = True
|
||||||
|
return t
|
||||||
|
|
||||||
|
|
||||||
class ThreadLocal(Stub):
|
class ThreadLocal(Stub):
|
||||||
"""java.lang.ThreadLocal"""
|
"""java.lang.ThreadLocal"""
|
||||||
@@ -1744,6 +2057,35 @@ class StringBuilder(Stub):
|
|||||||
def lastIndexOf(self, s, f=None):
|
def lastIndexOf(self, s, f=None):
|
||||||
return self.toString().rfind(s)
|
return self.toString().rfind(s)
|
||||||
|
|
||||||
|
def ensureCapacity(self, minimumCapacity):
|
||||||
|
pass # Python list 自动扩容
|
||||||
|
|
||||||
|
def trimToSize(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def setLength(self, newLength):
|
||||||
|
current = len(self._buf)
|
||||||
|
if newLength < current:
|
||||||
|
del self._buf[newLength:]
|
||||||
|
else:
|
||||||
|
self._buf.extend([" "] * (newLength - current))
|
||||||
|
|
||||||
|
def subSequence(self, start, end):
|
||||||
|
return "".join(self._buf[start:end])
|
||||||
|
|
||||||
|
def getChars(self, srcBegin, srcEnd, dst, dstBegin):
|
||||||
|
for i in range(srcBegin, srcEnd):
|
||||||
|
dst[dstBegin + i - srcBegin] = self._buf[i]
|
||||||
|
|
||||||
|
def codePointAt(self, index):
|
||||||
|
return ord(self._buf[index])
|
||||||
|
|
||||||
|
def codePointBefore(self, index):
|
||||||
|
return ord(self._buf[index - 1])
|
||||||
|
|
||||||
|
def codePointCount(self, beginIndex, endIndex):
|
||||||
|
return endIndex - beginIndex
|
||||||
|
|
||||||
|
|
||||||
class StringBuffer(StringBuilder):
|
class StringBuffer(StringBuilder):
|
||||||
"""java.lang.StringBuffer — 线程安全的可变字符序列"""
|
"""java.lang.StringBuffer — 线程安全的可变字符序列"""
|
||||||
|
|||||||
@@ -1848,6 +1848,60 @@ class Collections:
|
|||||||
|
|
||||||
return _LifoQueue(deque)
|
return _LifoQueue(deque)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def addAll(collection, *elements):
|
||||||
|
for e in elements:
|
||||||
|
collection.add(e)
|
||||||
|
return len(elements) > 0
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def emptyList():
|
||||||
|
return []
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def emptySet():
|
||||||
|
return set()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def emptyMap():
|
||||||
|
return {}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def reverseOrder(comparator=None):
|
||||||
|
if comparator is None:
|
||||||
|
return lambda a, b: -1 if a < b else (1 if a > b else 0)
|
||||||
|
return lambda a, b: -comparator(a, b)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def singletonSet(element):
|
||||||
|
s = HashSet()
|
||||||
|
s.add(element)
|
||||||
|
return s
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def checkedList(lst, type_):
|
||||||
|
return lst
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def checkedSet(s, type_):
|
||||||
|
return s
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def checkedMap(m, keyType, valueType):
|
||||||
|
return m
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def unmodifiableCollection(c):
|
||||||
|
return c
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def unmodifiableQueue(q):
|
||||||
|
return q
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def unmodifiableDeque(d):
|
||||||
|
return d
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# Arrays
|
# Arrays
|
||||||
@@ -2334,6 +2388,37 @@ class Calendar(Stub):
|
|||||||
def getInstance() -> 'GregorianCalendar':
|
def getInstance() -> 'GregorianCalendar':
|
||||||
return GregorianCalendar()
|
return GregorianCalendar()
|
||||||
|
|
||||||
|
def clear(self, field=None):
|
||||||
|
if field is None:
|
||||||
|
self._fields = {}
|
||||||
|
else:
|
||||||
|
if field in self._fields:
|
||||||
|
del self._fields[field]
|
||||||
|
|
||||||
|
def isSet(self, field):
|
||||||
|
return field in self._fields
|
||||||
|
|
||||||
|
def getCalendarType(self):
|
||||||
|
return "gregory"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def getAvailableLocales():
|
||||||
|
from .util import Locale
|
||||||
|
return [Locale.US, Locale.CHINA, Locale.JAPAN, Locale.KOREA, Locale.GERMANY, Locale.FRANCE]
|
||||||
|
|
||||||
|
def toInstant(self):
|
||||||
|
from .time import Instant
|
||||||
|
return Instant.ofEpochMilli(self.getTimeInMillis())
|
||||||
|
|
||||||
|
def getWeekYear(self):
|
||||||
|
return self.get(Calendar.YEAR)
|
||||||
|
|
||||||
|
def isWeekDateSupported(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def getWeeksInWeekYear(self):
|
||||||
|
return 52
|
||||||
|
|
||||||
|
|
||||||
class GregorianCalendar(Calendar):
|
class GregorianCalendar(Calendar):
|
||||||
"""java.util.GregorianCalendar"""
|
"""java.util.GregorianCalendar"""
|
||||||
|
|||||||
Reference in new issue
Block a user