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",
"Account", "AccountManager",
"Activity", "ActivityManager",
"Fragment", "FragmentManager", "FragmentTransaction",
"Dialog", "AlertDialog", "ProgressDialog",
"DatePickerDialog", "TimePickerDialog",
"Service", "Application",
"PendingIntent", "ComponentName", "AlarmManager",
"NotificationChannel", "NotificationChannelGroup",
"NotificationCompat", "TaskStackBuilder",
"AlphaAnimation",
"Animation",
"Animator", "ValueAnimator", "ObjectAnimator", "AnimatorSet",
@@ -423,6 +430,7 @@ __all__ = [
"Html",
"ImageView",
"InputDevice",
"KeyEvent", "MotionEvent",
"InputManager",
"InputMethodManager",
"Intent",
@@ -456,6 +464,7 @@ __all__ = [
"ProgressBar",
"RadioManager", "RadioTuner",
"Rect", "RectF",
"RecyclerView",
"Resources", "AssetManager",
"RoleManager",
"ScrollView",
@@ -481,6 +490,7 @@ __all__ = [
"ThermalManager",
"TimeManager",
"Toast",
"TrafficStats",
"TranslateAnimation",
"TranslationManager",
"TvInputManager",
@@ -524,6 +534,14 @@ __all__ = [
"CloneNotSupportedException", "IllegalMonitorStateException",
"IllegalThreadStateException", "EnumConstantNotPresentException",
"TypeNotPresentException",
# Java Exception 体系 (扩展)
"ClassNotFoundException", "IllegalAccessException",
"InstantiationException", "InvocationTargetException",
"NoSuchElementException", "AssertionError",
"OutOfMemoryError", "StackOverflowError", "NoClassDefFoundError",
"UnsatisfiedLinkError", "LinkageError", "VerifyError",
"AbstractMethodError", "IncompatibleClassChangeError",
"ExceptionInInitializerError",
"SimpleDateFormat", "DecimalFormat",
"LocalDateTime", "LocalDate", "LocalTime", "Instant", "Duration", "Period",
"ChronoUnit", "ChronoField", "DayOfWeek", "Month", "ZoneId", "ZoneOffset",
+339
View File
@@ -18,6 +18,7 @@ from .._core import (
resolve_android_path, to_android_path,
_sdcard_root, _app_data_dir, _package_name,
)
from ..java.lang import IllegalStateException
try:
from .._linux_impl import IS_LINUX, LinuxNotify, LinuxShell, LinuxProcess, LinuxDisplay, LinuxUIAutomation, LinuxPower
@@ -247,6 +248,108 @@ class Intent(Stub):
def filterHashCode(self) -> int:
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)
@@ -738,6 +841,62 @@ class Notification(Stub):
n._extras = dict(self._extras)
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):
super().__init__()
self._channel_id = ""
@@ -1355,6 +1514,90 @@ class Fragment(Stub):
def unregisterForContextMenu(self, view):
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):
"""FragmentTransaction — 管理 Fragment 事务"""
@@ -1875,6 +2118,102 @@ class Activity(Stub):
return LinuxUIAutomation.press_key(key)
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
+1 -8
View File
@@ -11,9 +11,7 @@ from typing import Any, Dict, List, Optional, Set, Tuple, Callable
from enum import IntEnum
from .._core import Stub, AndroidOnlyError
from ..java.lang import RuntimeException
IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {})
from ..java.lang import RuntimeException, IllegalArgumentException, IllegalStateException
# ═══════════════════════════════════════════════════════════════
@@ -424,11 +422,6 @@ class Fragment(Stub):
return f"Fragment({self._mTag or hex(id(self))})"
class IllegalStateException(RuntimeException):
"""IllegalStateException — 非法状态异常"""
pass
# ═══════════════════════════════════════════════════════════════
# FragmentManager
# ═══════════════════════════════════════════════════════════════
+14 -2
View File
@@ -1116,10 +1116,16 @@ class SharedPreferences(Stub):
return SharedPreferencesEditor(self)
def registerOnSharedPreferenceChangeListener(self, listener):
pass
if not hasattr(self, '_listeners'):
self._listeners = []
self._listeners.append(listener)
def unregisterOnSharedPreferenceChangeListener(self, listener):
pass
if hasattr(self, '_listeners'):
try:
self._listeners.remove(listener)
except ValueError:
pass
def getSharedPrefsFile(self):
return self._file_path
@@ -1186,6 +1192,12 @@ class SharedPreferencesEditor(Stub):
self._prefs._data.pop(key, None)
self._prefs._data.update(self._modified)
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._removed.clear()
self._clear_all = False
+23 -3
View File
@@ -761,16 +761,36 @@ class Intent(Stub):
def getData(self) -> Optional[str]:
return self._data
def setData(self, data: str) -> "Intent":
def setData(self, data) -> "Intent":
self._data = data
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
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:
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 ──
+63
View File
@@ -2049,6 +2049,69 @@ class Paint(Stub):
def descent(self) -> float:
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
+22 -4
View File
@@ -1817,6 +1817,7 @@ class ExifInterface(Stub):
super().__init__()
self._filename = resolve_android_path(filename)
self._attributes: Dict[str, str] = {}
self._pil_image = None
self._load_exif()
def _load_exif(self):
@@ -1828,6 +1829,7 @@ class ExifInterface(Stub):
from PIL import ExifTags
try:
img = Image.open(self._filename)
self._pil_image = img
exif_data = img._getexif()
if exif_data:
for tag_id, value in exif_data.items():
@@ -1899,13 +1901,29 @@ class ExifInterface(Stub):
pass
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):
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):
pass
def rotate(self, degrees):
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
TAG_SYSTEM_UPDATE = 0
_cached_stats = None
_cache_time = 0
@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
@staticmethod
def getMobileRxBytes() -> int:
def getMobileRxPackets():
return 0
@staticmethod
def getTotalTxBytes() -> int:
def getMobileTxPackets():
return 0
@staticmethod
def getTotalRxBytes() -> int:
return 0
def getTotalRxBytes():
return TrafficStats._read_net_dev()["rx_bytes"]
@staticmethod
def getMobileTxPackets() -> int:
return 0
def getTotalTxBytes():
return TrafficStats._read_net_dev()["tx_bytes"]
@staticmethod
def getMobileRxPackets() -> int:
return 0
def getTotalRxPackets():
return TrafficStats._read_net_dev()["rx_packets"]
@staticmethod
def getTotalTxPackets() -> int:
return 0
def getTotalTxPackets():
return TrafficStats._read_net_dev()["tx_packets"]
@staticmethod
def getTotalRxPackets() -> int:
return 0
def getUidRxBytes(uid):
try:
with open(f"/proc/uid_stat/{uid}/tcp_rcv", "r") as f:
return int(f.read().strip())
except Exception:
return 0
@staticmethod
def getUidTxBytes(uid: int) -> int:
return 0
def getUidTxBytes(uid):
try:
with open(f"/proc/uid_stat/{uid}/tcp_snd", "r") as f:
return int(f.read().strip())
except Exception:
return 0
@staticmethod
def getUidRxBytes(uid: int) -> int:
return 0
def getUidRxPackets(uid):
return TrafficStats.getUidRxBytes(uid)
@staticmethod
def getUidTxPackets(uid: int) -> int:
return 0
def getUidTxPackets(uid):
return TrafficStats.getUidTxBytes(uid)
@staticmethod
def getUidRxPackets(uid: int) -> int:
return 0
def getThreadStatsTag():
return getattr(TrafficStats, '_thread_tag', 0)
@staticmethod
def setThreadStatsTag(tag):
TrafficStats._thread_tag = tag
@staticmethod
def clearThreadStatsTag():
TrafficStats._thread_tag = 0
@staticmethod
def getUidTcpTxBytes(uid: int) -> int:
@@ -1143,18 +1188,6 @@ class TrafficStats(Stub):
def getUidUdpRxBytes(uid: int) -> int:
return 0
@staticmethod
def setThreadStatsTag(tag: int):
pass
@staticmethod
def clearThreadStatsTag():
pass
@staticmethod
def getThreadStatsTag() -> int:
return 0
@staticmethod
def setThreadStatsUid(uid: int):
pass
+57 -14
View File
@@ -316,20 +316,30 @@ class Process(Stub):
return _os.getpid()
@staticmethod
def killProcess(pid: int):
pass
def killProcess(pid):
import signal
try:
_os.kill(pid, signal.SIGKILL)
except (OSError, ProcessLookupError):
pass
@staticmethod
def sendSignal(pid: int, signal: int):
pass
def sendSignal(pid, signal_num):
try:
_os.kill(pid, signal_num)
except (OSError, ProcessLookupError):
pass
@staticmethod
def setThreadPriority(priority: int):
pass
@staticmethod
def setThreadPriority(tid: int, priority: int):
pass
def setThreadPriority(tid, priority):
try:
_os.setpriority(_os.PRIO_PROCESS, tid, priority)
except (OSError, PermissionError):
pass
@staticmethod
def getElapsedCpuTime() -> int:
@@ -1925,20 +1935,53 @@ class FileUtils(Stub):
"""FileUtils — 文件工具"""
@staticmethod
def readFileToString(file: Any) -> str:
return ""
def readFileToString(path):
try:
with open(path, "r", encoding="utf-8") as f:
return f.read()
except Exception:
return ""
@staticmethod
def stringToFile(file: Any, string: str):
pass
def stringToFile(path, content):
try:
with open(path, "w", encoding="utf-8") as f:
f.write(content)
return True
except Exception:
return False
@staticmethod
def copyFile(src: Any, dest: Any) -> bool:
return True
def copyFile(src, dst):
try:
import shutil
shutil.copy2(src, dst)
return True
except Exception:
return False
@staticmethod
def deleteDirectory(dir_: Any) -> bool:
return True
def deleteDirectory(path):
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
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:
return "http://www.example.com/uaprof.xml"
def listen(self, listener: PhoneStateListener, events: int):
pass
def listen(self, listener, events):
if not hasattr(self, '_listeners'):
self._listeners = []
self._listeners.append((listener, events))
def getDataState(self) -> int:
return self.DATA_CONNECTED
+133 -7
View File
@@ -938,6 +938,106 @@ class View(Stub):
def onApplyWindowInsets(self, 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:
"""MeasureSpec — 测量规格包装"""
@@ -1993,6 +2093,28 @@ class KeyEvent(Stub):
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
@@ -2700,14 +2822,18 @@ class SurfaceHolder(Stub):
def setFormat(self, format: int):
self._format = format
def lockCanvas(self) -> Any:
return None
def lockCanvas(self, *args):
"""锁定画布并返回 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:
return None
def unlockCanvasAndPost(self, canvas: Any):
pass
def unlockCanvasAndPost(self, canvas):
"""解锁画布并提交"""
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 ..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'})"
class KeyEvent(Stub):
"""KeyEvent — 按键事件"""
class KeyEvent(_BaseKeyEvent):
"""KeyEvent — 按键事件 (扩展版,继承 view.KeyEvent 的所有常量)"""
ACTION_DOWN = 0
ACTION_UP = 1
+61 -29
View File
@@ -1260,28 +1260,48 @@ class RecyclerView(ViewGroup):
return False
def notifyDataSetChanged(self):
pass
for observer in getattr(self, '_observers', []):
observer.onChanged()
def notifyItemChanged(self, position: int):
pass
def notifyItemChanged(self, position, payload=None):
for observer in getattr(self, '_observers', []):
observer.onItemRangeChanged(position, 1, payload)
def notifyItemInserted(self, position: int):
pass
def notifyItemInserted(self, position):
for observer in getattr(self, '_observers', []):
observer.onItemRangeInserted(position, 1)
def notifyItemRemoved(self, position: int):
pass
def notifyItemRemoved(self, position):
for observer in getattr(self, '_observers', []):
observer.onItemRangeRemoved(position, 1)
def notifyItemMoved(self, from_position: int, to_position: int):
pass
def notifyItemMoved(self, fromPosition, toPosition):
for observer in getattr(self, '_observers', []):
observer.onItemRangeMoved(fromPosition, toPosition, 1)
def notifyItemRangeChanged(self, position_start: int, item_count: int):
pass
def notifyItemRangeChanged(self, positionStart, itemCount, payload=None):
for observer in getattr(self, '_observers', []):
observer.onItemRangeChanged(positionStart, itemCount, payload)
def notifyItemRangeInserted(self, position_start: int, item_count: int):
pass
def notifyItemRangeInserted(self, positionStart, itemCount):
for observer in getattr(self, '_observers', []):
observer.onItemRangeInserted(positionStart, itemCount)
def notifyItemRangeRemoved(self, position_start: int, item_count: int):
pass
def notifyItemRangeRemoved(self, positionStart, itemCount):
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:
return 0
@@ -2068,11 +2088,13 @@ class ScrollView(FrameLayout):
def arrowScroll(self, direction: int) -> bool:
return True
def smoothScrollTo(self, x: int, y: int):
self._scroll_position = y
def smoothScrollTo(self, x, y):
self._scroll_x = x
self._scroll_y = y
def smoothScrollBy(self, dx: int, dy: int):
self._scroll_position += dy
def smoothScrollBy(self, dx, 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:
return True
@@ -2106,11 +2128,13 @@ class HorizontalScrollView(FrameLayout):
def arrowScroll(self, direction: int) -> bool:
return True
def smoothScrollTo(self, x: int, y: int):
pass
def smoothScrollTo(self, x, y):
self._scroll_x = x
self._scroll_y = y
def smoothScrollBy(self, dx: int, dy: int):
pass
def smoothScrollBy(self, dx, 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:
return True
@@ -2795,16 +2819,24 @@ class BaseAdapter(Stub):
return self.getView(position, convert_view, parent)
def notifyDataSetChanged(self):
pass
for observer in getattr(self, '_observers', []):
observer.onChanged()
def notifyDataSetInvalidated(self):
pass
for observer in getattr(self, '_observers', []):
observer.onInvalidated()
def registerDataSetObserver(self, observer: Any):
pass
def registerDataSetObserver(self, observer):
if not hasattr(self, '_observers'):
self._observers = []
self._observers.append(observer)
def unregisterDataSetObserver(self, observer: Any):
pass
def unregisterDataSetObserver(self, observer):
if hasattr(self, '_observers'):
try:
self._observers.remove(observer)
except ValueError:
pass
class ArrayAdapter(BaseAdapter):
+41 -1
View File
@@ -237,7 +237,10 @@ class File(Stub):
return _os.path.abspath(self._path)
def getCanonicalPath(self):
return _os.path.realpath(self._path)
try:
return _os.path.realpath(self._path)
except Exception:
return self.getAbsolutePath()
def getParent(self):
p = _os.path.dirname(self._path)
@@ -439,6 +442,43 @@ class File(Stub):
def toPath(self):
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):
return self._path
+344 -2
View File
@@ -8,6 +8,7 @@ import re as _re
import math as _py_math
import sys as _sys
import os as _os
import platform
import time as _time
import threading as _threading
import inspect as _inspect
@@ -28,6 +29,12 @@ __all__ = [
"CloneNotSupportedException", "IllegalMonitorStateException",
"IllegalThreadStateException", "EnumConstantNotPresentException",
"TypeNotPresentException",
"ClassNotFoundException", "IllegalAccessException", "InstantiationException",
"InvocationTargetException", "NoSuchElementException",
"AssertionError", "OutOfMemoryError", "StackOverflowError",
"NoClassDefFoundError", "UnsatisfiedLinkError", "LinkageError",
"VerifyError", "AbstractMethodError", "IncompatibleClassChangeError",
"ExceptionInInitializerError",
"String", "Integer", "Long", "Short", "Byte", "Float", "Double",
"Boolean", "Character", "Math", "StrictMath", "System",
"Thread", "ThreadLocal", "StringBuilder", "StringBuffer",
@@ -227,6 +234,80 @@ class TypeNotPresentException(RuntimeException):
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
# ============================================================
@@ -753,6 +834,62 @@ class Integer(Number, Comparable):
return Integer(int(env_val))
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):
return String(str(self._value))
@@ -1418,9 +1555,92 @@ class Math:
@staticmethod
def copySign(m, s): return abs(m) * (1 if s >= 0 else -1)
@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
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):
@@ -1529,6 +1749,39 @@ class System:
System.err = err_stream
@staticmethod
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
out = _sys.stdout
err = _sys.stderr
@@ -1683,6 +1936,66 @@ class Thread(Runnable):
def getDefaultUncaughtExceptionHandler():
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):
"""java.lang.ThreadLocal"""
@@ -1744,6 +2057,35 @@ class StringBuilder(Stub):
def lastIndexOf(self, s, f=None):
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):
"""java.lang.StringBuffer — 线程安全的可变字符序列"""
+85
View File
@@ -1848,6 +1848,60 @@ class Collections:
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
@@ -2334,6 +2388,37 @@ class Calendar(Stub):
def getInstance() -> '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):
"""java.util.GregorianCalendar"""