Files
Android-pyApi/android_pyapi/android/content_res.py
T
shuiyu1123 a51854455b feat: 大规模扩充至212个模块/175K行代码 — 覆盖android/java/javax/org/dalvik全包
- android/ 167个模块 (app/content/os/view/widget/graphics/media/hardware/net/telephony/provider等)
- java/ 35个模块 (lang/io/util/net/nio/security/crypto/sql/beans/rmi/awt等)
- javax/ 4个模块 (net/crypto/security/sql)
- org/ 5个模块 (json/w3c_dom/xml_sax/xml_pull/apache_http)
- dalvik/ 1个模块 (system)
- ~3053个类/接口,方法签名完全对齐Android SDK
- 桌面跨平台运行 (Windows/macOS/Linux)
- 修复所有导入错误和Python关键字冲突
2026-06-24 14:15:56 +00:00

564 lines
18 KiB
Python

"""
Android content.res 组件 — Resources, AssetManager, Configuration 等
方法签名完全对齐 Android SDK
"""
from __future__ import annotations
import io
import os
import struct
import sys
from pathlib import Path
from typing import Any, Dict, List, Optional, Set, Tuple, Union
from xml.etree import ElementTree
from .._core import Stub, AndroidOnlyError, android_context, resolve_android_path
from ..java.lang import RuntimeException
IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {})
# ═══════════════════════════════════════════════════════════════
# Resources
# ═══════════════════════════════════════════════════════════════
class Resources(Stub):
"""Resources — 资源访问"""
def __init__(self, assets: Optional["AssetManager"] = None):
super().__init__()
self._assets = assets or AssetManager()
self._strings: Dict[str, str] = {}
self._colors: Dict[str, int] = {}
self._dimens: Dict[str, float] = {}
self._drawables: Dict[str, Any] = {}
self._booleans: Dict[str, bool] = {}
self._integers: Dict[str, int] = {}
self._identifiers: Dict[str, int] = {}
self._next_id = 0x7f010000
self._configuration = Configuration()
self._display_metrics = DisplayMetrics()
# ── String ──
def getString(self, id: Union[int, str], *format_args: Any) -> Optional[str]:
if isinstance(id, str):
return self._strings.get(id)
for key, val in self._strings.items():
if self._identifiers.get(key) == id:
return val.format(*format_args) if format_args else val
return None
def getText(self, id: Union[int, str]) -> Optional[str]:
return self.getString(id)
def getQuantityString(self, id: Union[int, str], quantity: int, *format_args: Any) -> str:
base = self.getString(id) or ""
return base.format(quantity, *format_args)
# ── Color ──
def getColor(self, id: Union[int, str], theme: Optional["Resources.Theme"] = None) -> int:
if isinstance(id, str):
return self._colors.get(id, 0xFF000000)
for key, val in self._colors.items():
if self._identifiers.get(key) == id:
return val
return 0xFF000000
def getColorStateList(self, id: Union[int, str], theme: Optional["Resources.Theme"] = None) -> Any:
return self.getColor(id, theme)
# ── Dimension ──
def getDimension(self, id: Union[int, str]) -> float:
if isinstance(id, str):
return self._dimens.get(id, 0.0)
for key, val in self._dimens.items():
if self._identifiers.get(key) == id:
return val
return 0.0
def getDimensionPixelSize(self, id: Union[int, str]) -> int:
return int(self.getDimension(id))
def getDimensionPixelOffset(self, id: Union[int, str]) -> int:
return int(self.getDimension(id))
# ── Drawable ──
def getDrawable(self, id: Union[int, str], theme: Optional["Resources.Theme"] = None) -> Any:
if isinstance(id, str):
return self._drawables.get(id)
for key, val in self._drawables.items():
if self._identifiers.get(key) == id:
return val
return None
# ── Identifier ──
def getIdentifier(self, name: str, defType: Optional[str] = None,
defPackage: Optional[str] = None) -> int:
key = f"{defPackage}:{defType}/{name}" if defType else name
if key not in self._identifiers:
self._next_id += 1
self._identifiers[key] = self._next_id
return self._identifiers[key]
# ── Boolean / Integer ──
def getBoolean(self, id: Union[int, str]) -> bool:
if isinstance(id, str):
return self._booleans.get(id, False)
for key, val in self._booleans.items():
if self._identifiers.get(key) == id:
return val
return False
def getInteger(self, id: Union[int, str]) -> int:
if isinstance(id, str):
return self._integers.get(id, 0)
for key, val in self._integers.items():
if self._identifiers.get(key) == id:
return val
return 0
# ── Layout / Animation / Xml ──
def getLayout(self, id: Union[int, str]) -> Any:
return self.getDrawable(id)
def getAnimation(self, id: Union[int, str]) -> Any:
return self.getDrawable(id)
def getXml(self, id: Union[int, str]) -> "XmlResourceParser":
return XmlResourceParser()
# ── Assets ──
def getAssets(self) -> "AssetManager":
return self._assets
# ── Config / Metrics ──
def getConfiguration(self) -> "Configuration":
return self._configuration
def getDisplayMetrics(self) -> "DisplayMetrics":
return self._display_metrics
def getSystem(self) -> "Resources":
return self
# ── Resource names ──
def getResourceEntryName(self, resid: int) -> str:
for key, val in self._identifiers.items():
if val == resid:
return key.split("/")[-1] if "/" in key else key
return "unknown"
def getResourceName(self, resid: int) -> str:
for key, val in self._identifiers.items():
if val == resid:
return key
return "unknown"
def getResourcePackageName(self, resid: int) -> str:
for key, val in self._identifiers.items():
if val == resid:
parts = key.split(":")
return parts[0] if len(parts) > 1 else ""
return ""
def getResourceTypeName(self, resid: int) -> str:
for key, val in self._identifiers.items():
if val == resid:
parts = key.split("/")
if len(parts) > 1:
type_part = parts[0]
if ":" in type_part:
return type_part.split(":")[1]
return type_part
return ""
return ""
# ── Raw resources ──
def openRawResource(self, id: int) -> io.BytesIO:
return io.BytesIO(b"")
def openRawResourceFd(self, id: int) -> Any:
return None
# ── Mutation helpers ──
def _put_string(self, name: str, value: str):
self._strings[name] = value
def _put_color(self, name: str, value: int):
self._colors[name] = value
def _put_dimension(self, name: str, value: float):
self._dimens[name] = value
def _put_drawable(self, name: str, value: Any):
self._drawables[name] = value
def _put_boolean(self, name: str, value: bool):
self._booleans[name] = value
def _put_integer(self, name: str, value: int):
self._integers[name] = value
# ── Theme ──
class Theme(Stub):
"""Resources.Theme — 主题"""
def __init__(self, resources: "Resources"):
super().__init__()
self._resources = resources
self._styles: Dict[int, Any] = {}
def applyStyle(self, resid: int, force: bool = False):
pass
def setTo(self, other: "Resources.Theme"):
self._styles = dict(other._styles)
def getDrawable(self, id: int) -> Any:
return self._resources.getDrawable(id)
def getColor(self, id: int) -> int:
return self._resources.getColor(id)
def getResources(self) -> "Resources":
return self._resources
def toString(self) -> str:
return f"Resources.Theme@{hex(id(self))}"
def hashCode(self) -> int:
return hash(id(self)) & 0x7FFFFFFF
def equals(self, other) -> bool:
return self is other
# ═══════════════════════════════════════════════════════════════
# AssetManager
# ═══════════════════════════════════════════════════════════════
class AssetManager(Stub):
"""AssetManager — 资源资产管理"""
ACCESS_UNKNOWN = 0
ACCESS_STREAMING = 1
ACCESS_BUFFER = 2
ACCESS_RANDOM = 3
def __init__(self):
super().__init__()
self._assets_dir = os.path.join(
os.path.expanduser("~"), "Android-compat", "assets"
)
os.makedirs(self._assets_dir, exist_ok=True)
self._open: bool = True
def open(self, fileName: str, access: int = ACCESS_STREAMING) -> io.BytesIO:
path = os.path.join(self._assets_dir, fileName)
if os.path.isfile(path):
with open(path, "rb") as f:
return io.BytesIO(f.read())
return io.BytesIO(b"")
def openFd(self, fileName: str) -> Any:
path = os.path.join(self._assets_dir, fileName)
if os.path.isfile(path):
return open(path, "rb")
return None
def openNonAssetFd(self, fileName: str) -> Any:
return self.openFd(fileName)
def list(self, path: str = "") -> List[str]:
dir_path = os.path.join(self._assets_dir, path)
if os.path.isdir(dir_path):
return os.listdir(dir_path)
return []
def close(self):
self._open = False
def toString(self) -> str:
return f"AssetManager@{hex(id(self))}"
# ═══════════════════════════════════════════════════════════════
# Configuration
# ═══════════════════════════════════════════════════════════════
class Configuration(Stub):
"""Configuration — 设备配置"""
# Color mode constants
COLOR_MODE_DEFAULT = 0
COLOR_MODE_WIDE_COLOR_GAMUT = 1
COLOR_MODE_HDR = 2
# Density constants
DENSITY_DPI_UNDEFINED = 0
DENSITY_DPI_LOW = 120
DENSITY_DPI_MEDIUM = 160
DENSITY_DPI_HIGH = 240
DENSITY_DPI_XHIGH = 320
DENSITY_DPI_XXHIGH = 480
DENSITY_DPI_XXXHIGH = 640
DENSITY_DPI_TV = 213
# Screen layout size
SCREENLAYOUT_SIZE_SMALL = 1
SCREENLAYOUT_SIZE_NORMAL = 2
SCREENLAYOUT_SIZE_LARGE = 3
SCREENLAYOUT_SIZE_XLARGE = 4
# Orientation
ORIENTATION_LANDSCAPE = 2
ORIENTATION_PORTRAIT = 1
ORIENTATION_SQUARE = 3
ORIENTATION_UNDEFINED = 0
# UI mode
UI_MODE_TYPE_NORMAL = 1
UI_MODE_TYPE_DESK = 2
UI_MODE_TYPE_CAR = 3
UI_MODE_TYPE_TELEVISION = 4
UI_MODE_TYPE_APPLIANCE = 5
UI_MODE_TYPE_WATCH = 6
UI_MODE_TYPE_VR_HEADSET = 7
UI_MODE_NIGHT_UNDEFINED = 0
UI_MODE_NIGHT_NO = 1
UI_MODE_NIGHT_YES = 2
# Touchscreen
TOUCHSCREEN_UNDEFINED = 0
TOUCHSCREEN_NOTOUCH = 1
TOUCHSCREEN_FINGER = 3
# Keyboard
KEYBOARD_UNDEFINED = 0
KEYBOARD_NOKEYS = 1
KEYBOARD_QWERTY = 2
KEYBOARD_12KEY = 3
# Keyboard hidden
KEYBOARDHIDDEN_UNDEFINED = 0
KEYBOARDHIDDEN_NO = 1
KEYBOARDHIDDEN_YES = 2
# Hard keyboard hidden
HARDKEYBOARDHIDDEN_UNDEFINED = 0
HARDKEYBOARDHIDDEN_NO = 1
HARDKEYBOARDHIDDEN_YES = 2
# Navigation
NAVIGATION_UNDEFINED = 0
NAVIGATION_NONAV = 1
NAVIGATION_DPAD = 2
NAVIGATION_TRACKBALL = 3
NAVIGATION_WHEEL = 4
# Navigation hidden
NAVIGATIONHIDDEN_UNDEFINED = 0
NAVIGATIONHIDDEN_NO = 1
NAVIGATIONHIDDEN_YES = 2
def __init__(self):
super().__init__()
self.fontScale: float = 1.0
self.mcc: int = 310
self.mnc: int = 260
self.locale: Optional[str] = "en-US"
self.screenLayout: int = self.SCREENLAYOUT_SIZE_NORMAL
self.touchscreen: int = self.TOUCHSCREEN_FINGER
self.keyboard: int = self.KEYBOARD_QWERTY
self.keyboardHidden: int = self.KEYBOARDHIDDEN_NO
self.hardKeyboardHidden: int = self.HARDKEYBOARDHIDDEN_NO
self.navigation: int = self.NAVIGATION_NONAV
self.navigationHidden: int = self.NAVIGATIONHIDDEN_NO
self.orientation: int = self.ORIENTATION_PORTRAIT
self.screenWidthDp: int = 360
self.screenHeightDp: int = 640
self.smallestScreenWidthDp: int = 360
self.densityDpi: int = self.DENSITY_DPI_MEDIUM
self.uiMode: int = self.UI_MODE_TYPE_NORMAL | self.UI_MODE_NIGHT_NO
def toString(self) -> str:
return (f"Configuration{{fontScale={self.fontScale}, mcc={self.mcc}, "
f"mnc={self.mnc}, locale={self.locale}, "
f"screenLayout={self.screenLayout}, orientation={self.orientation}, "
f"screenWidthDp={self.screenWidthDp}, screenHeightDp={self.screenHeightDp}}}")
# ═══════════════════════════════════════════════════════════════
# DisplayMetrics
# ═══════════════════════════════════════════════════════════════
class DisplayMetrics(Stub):
"""DisplayMetrics — 显示指标"""
DENSITY_DEFAULT = 160
DENSITY_LOW = 120
DENSITY_MEDIUM = 160
DENSITY_HIGH = 240
DENSITY_XHIGH = 320
DENSITY_XXHIGH = 480
DENSITY_XXXHIGH = 640
DENSITY_TV = 213
def __init__(self):
super().__init__()
self.widthPixels: int = 1080
self.heightPixels: int = 1920
self.densityDpi: int = self.DENSITY_DEFAULT
self.density: float = 1.0
self.scaledDensity: float = 1.0
self.xdpi: float = 160.0
self.ydpi: float = 160.0
self.noncompatWidthPixels: int = 1080
self.noncompatHeightPixels: int = 1920
self.noncompatDensityDpi: int = self.DENSITY_DEFAULT
self.noncompatDensity: float = 1.0
self.noncompatScaledDensity: float = 1.0
self.noncompatXdpi: float = 160.0
self.noncompatYdpi: float = 160.0
self.densityDpi: int = self.DENSITY_DEFAULT
def toString(self) -> str:
return (f"DisplayMetrics{{width={self.widthPixels}, height={self.heightPixels}, "
f"densityDpi={self.densityDpi}, density={self.density:.2f}}}")
# ═══════════════════════════════════════════════════════════════
# TypedArray
# ═══════════════════════════════════════════════════════════════
class TypedArray(Stub):
"""TypedArray — 类型化数组"""
def __init__(self):
super().__init__()
self._data: Dict[int, Any] = {}
def getString(self, index: int) -> Optional[str]:
val = self._data.get(index)
return str(val) if val is not None else None
def getInt(self, index: int, defValue: int = 0) -> int:
val = self._data.get(index)
return int(val) if val is not None else defValue
def getFloat(self, index: int, defValue: float = 0.0) -> float:
val = self._data.get(index)
return float(val) if val is not None else defValue
def getBoolean(self, index: int, defValue: bool = False) -> bool:
val = self._data.get(index)
return bool(val) if val is not None else defValue
def getColor(self, index: int, defValue: int = 0) -> int:
val = self._data.get(index)
return int(val) if val is not None else defValue
def getDimension(self, index: int, defValue: float = 0.0) -> float:
val = self._data.get(index)
return float(val) if val is not None else defValue
def getDrawable(self, index: int) -> Any:
return self._data.get(index)
def getResourceId(self, index: int, defValue: int = 0) -> int:
val = self._data.get(index)
return int(val) if val is not None else defValue
def getText(self, index: int) -> Optional[str]:
return self.getString(index)
def hasValue(self, index: int) -> bool:
return index in self._data
def length(self) -> int:
return len(self._data)
def recycle(self):
self._data.clear()
def toString(self) -> str:
return f"TypedArray@{hex(id(self))}"
# ═══════════════════════════════════════════════════════════════
# XmlResourceParser
# ═══════════════════════════════════════════════════════════════
class XmlResourceParser(Stub):
"""XmlResourceParser — XML 资源解析器"""
START_DOCUMENT = 0
END_DOCUMENT = 1
START_TAG = 2
END_TAG = 3
TEXT = 4
def __init__(self):
super().__init__()
self._root: Optional[ElementTree.Element] = None
self._event_type = self.START_DOCUMENT
def next(self) -> int:
return self.END_DOCUMENT
def getEventType(self) -> int:
return self._event_type
def getName(self) -> Optional[str]:
return None
def getText(self) -> Optional[str]:
return None
def getAttributeCount(self) -> int:
return 0
def getAttributeName(self, index: int) -> Optional[str]:
return None
def getAttributeValue(self, index: int) -> Optional[str]:
return None
def getAttributeValue(self, namespace: Optional[str], name: str) -> Optional[str]:
return None
def close(self):
pass
def toString(self) -> str:
return f"XmlResourceParser@{hex(id(self))}"
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"Resources",
"AssetManager",
"Configuration",
"DisplayMetrics",
"TypedArray",
"XmlResourceParser",
]