""" Android Preference 组件 — Preference, PreferenceScreen, PreferenceManager 等 方法签名完全对齐 Android SDK """ import os import sys import json from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union from .._core import ( Stub, AndroidOnlyError, android_context, resolve_android_path, to_android_path, _sdcard_root, _app_data_dir, _package_name, ) # ═══════════════════════════════════════════════════════════════ # PreferenceChangeListener # ═══════════════════════════════════════════════════════════════ class PreferenceChangeListener(Stub): """OnPreferenceChangeListener — 偏好变化监听器""" def onPreferenceChange(self, preference: "Preference", new_value: Any) -> bool: return True class PreferenceOnPreferenceClickListener(Stub): """OnPreferenceClickListener — 偏好点击监听器""" def onPreferenceClick(self, preference: "Preference") -> bool: return True # ═══════════════════════════════════════════════════════════════ # PreferenceManager # ═══════════════════════════════════════════════════════════════ class PreferenceManager(Stub): """PreferenceManager — 偏好管理器""" KEY_HAS_SET_DEFAULT_VALUES = "_has_set_default_values" def __init__(self, context: Any): super().__init__() self._context = context self._shared_prefs_name: str = f"{_package_name}_preferences" self._shared_prefs_mode: int = 0 self._prefs_data: Dict[str, Any] = {} self._prefs_path = os.path.join(_app_data_dir, "shared_prefs", f"{self._shared_prefs_name}.json") self._load() def _load(self): os.makedirs(os.path.dirname(self._prefs_path), exist_ok=True) if os.path.exists(self._prefs_path): try: with open(self._prefs_path, "r") as f: self._prefs_data = json.load(f) except (json.JSONDecodeError, OSError): self._prefs_data = {} else: self._prefs_data = {} def _save(self): os.makedirs(os.path.dirname(self._prefs_path), exist_ok=True) with open(self._prefs_path, "w") as f: json.dump(self._prefs_data, f) def getSharedPreferences(self) -> Any: return self._prefs_data def setSharedPreferencesName(self, name: str): self._shared_prefs_name = name self._prefs_path = os.path.join(_app_data_dir, "shared_prefs", f"{name}.json") self._load() def setSharedPreferencesMode(self, mode: int): self._shared_prefs_mode = mode @staticmethod def getDefaultSharedPreferences(context: Any) -> Any: mgr = PreferenceManager(context) return mgr.getSharedPreferences() @staticmethod def setDefaultValues(context: Any, res_id: int, read_again: bool): pass @staticmethod def setDefaultValues(context: Any, shared_prefs_name: str, shared_prefs_mode: int, res_id: int, read_again: bool): pass def getPreference(self, key: str, default: Any = None) -> Any: return self._prefs_data.get(key, default) def putPreference(self, key: str, value: Any): self._prefs_data[key] = value self._save() def removePreference(self, key: str): if key in self._prefs_data: del self._prefs_data[key] self._save() # ═══════════════════════════════════════════════════════════════ # Preference # ═══════════════════════════════════════════════════════════════ class Preference(Stub): """Preference — 偏好设置项基类""" def __init__(self, context: Any = None, attrs: Any = None, def_style_attr: int = 0): super().__init__() self._context = context self._title: str = "" self._summary: str = "" self._key: Optional[str] = None self._enabled: bool = True self._default_value: Any = None self._order: int = 0 self._persistent: bool = True self._icon: Any = None self._dependents: List["Preference"] = [] self._dependency_key: Optional[str] = None self._preference_manager: Optional[PreferenceManager] = None self._parent: Optional["PreferenceGroup"] = None self._on_change_listener: Optional[PreferenceChangeListener] = None self._on_click_listener: Optional[PreferenceOnPreferenceClickListener] = None def setTitle(self, title: str): self._title = title def getTitle(self) -> str: return self._title def setSummary(self, summary: str): self._summary = summary def getSummary(self) -> str: return self._summary def setKey(self, key: str): self._key = key def getKey(self) -> Optional[str]: return self._key def setEnabled(self, enabled: bool): self._enabled = enabled def isEnabled(self) -> bool: return self._enabled def setDefaultValue(self, value: Any): self._default_value = value def setOnPreferenceClickListener(self, listener: PreferenceOnPreferenceClickListener): self._on_click_listener = listener def setOnPreferenceChangeListener(self, listener: PreferenceChangeListener): self._on_change_listener = listener def getSharedPreferences(self) -> Any: if self._preference_manager: return self._preference_manager.getSharedPreferences() return {} def getEditor(self) -> Any: if self._preference_manager: return self._preference_manager return None def shouldCommit(self) -> bool: return True def shouldPersist(self) -> bool: return self._persistent and self._key is not None def persistString(self, value: str) -> bool: if self.shouldPersist() and self._preference_manager: self._preference_manager.putPreference(self._key, value) return True return False def persistInt(self, value: int) -> bool: if self.shouldPersist() and self._preference_manager: self._preference_manager.putPreference(self._key, value) return True return False def persistBoolean(self, value: bool) -> bool: if self.shouldPersist() and self._preference_manager: self._preference_manager.putPreference(self._key, value) return True return False def persistFloat(self, value: float) -> bool: if self.shouldPersist() and self._preference_manager: self._preference_manager.putPreference(self._key, value) return True return False def persistLong(self, value: int) -> bool: return self.persistInt(value) def getPersistedString(self, default_return: Optional[str] = None) -> Optional[str]: if self.shouldPersist() and self._preference_manager: val = self._preference_manager.getPreference(self._key, default_return) return str(val) if val is not None else default_return return default_return def getPersistedInt(self, default_return: int = 0) -> int: if self.shouldPersist() and self._preference_manager: return int(self._preference_manager.getPreference(self._key, default_return)) return default_return def getPersistedBoolean(self, default_return: bool = False) -> bool: if self.shouldPersist() and self._preference_manager: val = self._preference_manager.getPreference(self._key, default_return) if isinstance(val, bool): return val return str(val).lower() in ("true", "1", "yes") return default_return def getPersistedFloat(self, default_return: float = 0.0) -> float: if self.shouldPersist() and self._preference_manager: return float(self._preference_manager.getPreference(self._key, default_return)) return default_return def getPersistedLong(self, default_return: int = 0) -> int: return self.getPersistedInt(default_return) def notifyChanged(self): pass def notifyHierarchyChanged(self): pass def onBindView(self, view: Any): pass def onClick(self): if self._on_click_listener: self._on_click_listener.onPreferenceClick(self) def setOrder(self, order: int): self._order = order def getOrder(self) -> int: return self._order def setPersistent(self, persistent: bool): self._persistent = persistent def isPersistent(self) -> bool: return self._persistent def setDependency(self, dependency_key: str): self._dependency_key = dependency_key def getDependency(self) -> Optional[str]: return self._dependency_key def toString(self) -> str: return f"Preference(key={self._key}, title={self._title})" def hashCode(self) -> int: return hash(self._key) & 0x7FFFFFFF if self._key else 0 # ═══════════════════════════════════════════════════════════════ # PreferenceGroup # ═══════════════════════════════════════════════════════════════ class PreferenceGroup(Preference): """PreferenceGroup — 偏好组""" def __init__(self, context: Any = None, attrs: Any = None): super().__init__(context, attrs) self._preferences: List[Preference] = [] def addPreference(self, preference: Preference) -> bool: if preference not in self._preferences: self._preferences.append(preference) preference._parent = self return True return False def removePreference(self, preference: Preference) -> bool: if preference in self._preferences: self._preferences.remove(preference) preference._parent = None return True return False def removeAll(self): for p in self._preferences: p._parent = None self._preferences.clear() def getPreference(self, index: int) -> Optional[Preference]: if 0 <= index < len(self._preferences): return self._preferences[index] return None def getPreferenceCount(self) -> int: return len(self._preferences) def findPreference(self, key: str) -> Optional[Preference]: for p in self._preferences: if p.getKey() == key: return p if isinstance(p, PreferenceGroup): result = p.findPreference(key) if result: return result return None def toString(self) -> str: return f"PreferenceGroup(count={len(self._preferences)})" # ═══════════════════════════════════════════════════════════════ # PreferenceScreen # ═══════════════════════════════════════════════════════════════ class PreferenceScreen(PreferenceGroup): """PreferenceScreen — 根偏好屏幕""" def __init__(self, context: Any = None, attrs: Any = None): super().__init__(context, attrs) self._root: bool = True # ═══════════════════════════════════════════════════════════════ # PreferenceCategory # ═══════════════════════════════════════════════════════════════ class PreferenceCategory(PreferenceGroup): """PreferenceCategory — 偏好分类标题""" def __init__(self, context: Any = None, attrs: Any = None): super().__init__(context, attrs) # ═══════════════════════════════════════════════════════════════ # TwoStatePreference # ═══════════════════════════════════════════════════════════════ class TwoStatePreference(Preference): """TwoStatePreference — 双状态偏好基类""" def __init__(self, context: Any = None, attrs: Any = None): super().__init__(context, attrs) self._checked: bool = False self._summary_on: str = "" self._summary_off: str = "" self._disable_dependents_state: bool = False def isChecked(self) -> bool: return self._checked def setChecked(self, checked: bool): self._checked = checked self.persistBoolean(checked) def setSummaryOn(self, summary: str): self._summary_on = summary def setSummaryOff(self, summary: str): self._summary_off = summary def setDisableDependentsState(self, state: bool): self._disable_dependents_state = state # ═══════════════════════════════════════════════════════════════ # CheckBoxPreference # ═══════════════════════════════════════════════════════════════ class CheckBoxPreference(TwoStatePreference): """CheckBoxPreference — 复选框偏好""" def __init__(self, context: Any = None, attrs: Any = None): super().__init__(context, attrs) # ═══════════════════════════════════════════════════════════════ # SwitchPreference # ═══════════════════════════════════════════════════════════════ class SwitchPreference(TwoStatePreference): """SwitchPreference — 开关偏好""" def __init__(self, context: Any = None, attrs: Any = None): super().__init__(context, attrs) self._switch_text_on: str = "" self._switch_text_off: str = "" def setSwitchTextOn(self, text: str): self._switch_text_on = text def setSwitchTextOff(self, text: str): self._switch_text_off = text def getSwitchTextOn(self) -> str: return self._switch_text_on def getSwitchTextOff(self) -> str: return self._switch_text_off # ═══════════════════════════════════════════════════════════════ # DialogPreference # ═══════════════════════════════════════════════════════════════ class DialogPreference(Preference): """DialogPreference — 对话框偏好基类""" def __init__(self, context: Any = None, attrs: Any = None): super().__init__(context, attrs) self._dialog_title: str = "" self._dialog_message: str = "" self._dialog_icon: Any = None self._positive_button_text: str = "" self._negative_button_text: str = "" # ═══════════════════════════════════════════════════════════════ # EditTextPreference # ═══════════════════════════════════════════════════════════════ class EditTextPreference(DialogPreference): """EditTextPreference — 文本编辑偏好""" def __init__(self, context: Any = None, attrs: Any = None): super().__init__(context, attrs) self._text: str = "" def getText(self) -> str: return self._text def setText(self, text: str): self._text = text self.persistString(text) # ═══════════════════════════════════════════════════════════════ # ListPreference # ═══════════════════════════════════════════════════════════════ class ListPreference(DialogPreference): """ListPreference — 列表偏好""" def __init__(self, context: Any = None, attrs: Any = None): super().__init__(context, attrs) self._entries: List[str] = [] self._entry_values: List[str] = [] self._value: str = "" def getEntries(self) -> List[str]: return list(self._entries) def setEntries(self, entries: List[str]): self._entries = list(entries) def getEntryValues(self) -> List[str]: return list(self._entry_values) def setEntryValues(self, values: List[str]): self._entry_values = list(values) def getValue(self) -> str: return self._value def setValue(self, value: str): self._value = value self.persistString(value) def getEntry(self) -> Optional[str]: try: idx = self._entry_values.index(self._value) if 0 <= idx < len(self._entries): return self._entries[idx] except ValueError: pass return None def getSummary(self) -> str: entry = self.getEntry() if entry: return entry return self._summary def setDefaultValue(self, value: Any): super().setDefaultValue(value) # ═══════════════════════════════════════════════════════════════ # MultiSelectListPreference # ═══════════════════════════════════════════════════════════════ class MultiSelectListPreference(DialogPreference): """MultiSelectListPreference — 多选列表偏好""" def __init__(self, context: Any = None, attrs: Any = None): super().__init__(context, attrs) self._entries: List[str] = [] self._entry_values: List[str] = [] self._values: Set[str] = set() def getEntries(self) -> List[str]: return list(self._entries) def setEntries(self, entries: List[str]): self._entries = list(entries) def getEntryValues(self) -> List[str]: return list(self._entry_values) def setEntryValues(self, values: List[str]): self._entry_values = list(values) def getValues(self) -> Set[str]: return set(self._values) def setValues(self, values: Set[str]): self._values = set(values) # ═══════════════════════════════════════════════════════════════ # RingtonePreference / SeekBarPreference / DropDownPreference # ═══════════════════════════════════════════════════════════════ class RingtonePreference(Preference): """RingtonePreference — 铃声偏好""" def __init__(self, context: Any = None, attrs: Any = None): super().__init__(context, attrs) self._ringtone_type: int = 1 self._show_default: bool = True self._show_silent: bool = True class SeekBarPreference(Preference): """SeekBarPreference — 滑块偏好""" def __init__(self, context: Any = None, attrs: Any = None): super().__init__(context, attrs) self._min: int = 0 self._max: int = 100 self._current: int = 50 self._show_progress: bool = True self._adjustable: bool = True def getMin(self) -> int: return self._min def setMin(self, min_val: int): self._min = min_val def getMax(self) -> int: return self._max def setMax(self, max_val: int): self._max = max_val def getValue(self) -> int: return self._current def setValue(self, value: int): self._current = value self.persistInt(value) class DropDownPreference(ListPreference): """DropDownPreference — 下拉列表偏好""" def __init__(self, context: Any = None, attrs: Any = None): super().__init__(context, attrs) # ═══════════════════════════════════════════════════════════════ # PreferenceFragment / PreferenceFragmentCompat # ═══════════════════════════════════════════════════════════════ class PreferenceFragment(Stub): """PreferenceFragment — 偏好片段""" def __init__(self): super().__init__() self._preference_manager: Optional[PreferenceManager] = None def onCreate(self, savedInstanceState: Any): pass def onCreatePreferences(self, savedInstanceState: Any, root_key: Optional[str]): pass def addPreferencesFromResource(self, res_id: int): pass def setPreferenceScreen(self, screen: PreferenceScreen): pass def getPreferenceScreen(self) -> Optional[PreferenceScreen]: return None def findPreference(self, key: str) -> Optional[Preference]: screen = self.getPreferenceScreen() if screen: return screen.findPreference(key) return None class PreferenceFragmentCompat(PreferenceFragment): """PreferenceFragmentCompat — 兼容偏好片段""" def __init__(self): super().__init__() # ═══════════════════════════════════════════════════════════════ # __all__ # ═══════════════════════════════════════════════════════════════ __all__ = [ "Preference", "PreferenceScreen", "PreferenceCategory", "PreferenceManager", "CheckBoxPreference", "SwitchPreference", "ListPreference", "EditTextPreference", "MultiSelectListPreference", "PreferenceFragment", "PreferenceFragmentCompat", "PreferenceGroup", "PreferenceChangeListener", "PreferenceOnPreferenceClickListener", "TwoStatePreference", "DialogPreference", "RingtonePreference", "SeekBarPreference", "DropDownPreference", ]