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:
shuiyu1123 committed 2026-06-24 23:46:36 +00:00
1 parent e316e18d98
commit 39b1da37c9
16 files changed
+1274 -107

No files matched your search

+18
View File
@@ -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",
+339
View File
@@ -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
+1 -8
View File
@@ -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
# ═══════════════════════════════════════════════════════════════ # ═══════════════════════════════════════════════════════════════
+14 -2
View File
@@ -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
+23 -3
View File
@@ -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 ──
+63
View File
@@ -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
+22 -4
View File
@@ -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)
# ═══════════════════════════════════════════════════════════════ # ═══════════════════════════════════════════════════════════════
+66 -33
View File
@@ -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
View File
@@ -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):
+4 -2
View File
@@ -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
+133 -7
View File
@@ -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
# ═══════════════════════════════════════════════════════════════ # ═══════════════════════════════════════════════════════════════
+3 -2
View File
@@ -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
+61 -29
View File
@@ -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):
+41 -1
View File
@@ -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
View File
@@ -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 — 线程安全的可变字符序列"""
+85
View File
@@ -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"""