""" Android accounts 组件 — AccountManager, Account, AuthenticatorDescription 等 方法签名完全对齐 Android SDK """ from __future__ import annotations import threading import uuid from typing import Any, Callable, Dict, List, Optional, Set from .._core import Stub, AndroidOnlyError from ..java.lang import RuntimeException IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) # ═══════════════════════════════════════════════════════════════ # Account # ═══════════════════════════════════════════════════════════════ class Account(Stub): """Account — 账户""" def __init__(self, name: str, type_: str): super().__init__() self.name: str = name self.type: str = type_ self._extras: Dict[str, Any] = {} def toString(self) -> str: return f"Account{{name={self.name}, type={self.type}}}" def describeContents(self) -> int: return 0 def hashCode(self) -> int: return hash((self.name, self.type)) & 0x7FFFFFFF def equals(self, other) -> bool: if not isinstance(other, Account): return False return self.name == other.name and self.type == other.type # ═══════════════════════════════════════════════════════════════ # AccountManagerFuture # ═══════════════════════════════════════════════════════════════ class AccountManagerFuture(Stub): """AccountManagerFuture — 异步结果""" def __init__(self): super().__init__() self._result: Any = None self._exception: Optional[Exception] = None self._done: bool = False self._event = threading.Event() def getResult(self) -> Any: self._event.wait() if self._exception: raise self._exception return self._result def getException(self) -> Optional[Exception]: return self._exception def isDone(self) -> bool: return self._done def isCancelled(self) -> bool: return False def cancel(self, mayInterruptIfRunning: bool) -> bool: return False def _set_result(self, result: Any): self._result = result self._done = True self._event.set() def _set_exception(self, exc: Exception): self._exception = exc self._done = True self._event.set() # ═══════════════════════════════════════════════════════════════ # AccountManagerCallback # ═══════════════════════════════════════════════════════════════ class AccountManagerCallback(Stub): """AccountManagerCallback — 回调接口""" def run(self, future: AccountManagerFuture): pass # ═══════════════════════════════════════════════════════════════ # AuthenticatorDescription # ═══════════════════════════════════════════════════════════════ class AuthenticatorDescription(Stub): """AuthenticatorDescription — 认证器描述""" KEY_ACCOUNT_MANAGER_RESPONSE = "accountManagerResponse" def __init__(self, type_: str, packageName: str = "", labelId: int = 0, iconId: int = 0, smallIconId: int = 0, accountPreferencesId: int = 0, customTokens: bool = False, prefMinId: int = 0): super().__init__() self.type: str = type_ self.packageName: str = packageName self.labelId: int = labelId self.iconId: int = iconId self.smallIconId: int = smallIconId self.accountPreferencesId: int = accountPreferencesId self.customTokens: bool = customTokens self.prefMinId: int = prefMinId def toString(self) -> str: return f"AuthenticatorDescription{{type={self.type}}}" def hashCode(self) -> int: return hash(self.type) & 0x7FFFFFFF def equals(self, other) -> bool: if not isinstance(other, AuthenticatorDescription): return False return self.type == other.type # ═══════════════════════════════════════════════════════════════ # OnAccountsUpdateListener # ═══════════════════════════════════════════════════════════════ class OnAccountsUpdateListener(Stub): """OnAccountsUpdateListener — 账户更新监听器""" def onAccountsUpdated(self, accounts: Optional[List[Account]]): pass # ═══════════════════════════════════════════════════════════════ # AccountManager # ═══════════════════════════════════════════════════════════════ class AccountManager(Stub): """AccountManager — 账户管理器""" ACTION_ACCOUNT_REMOVED = "android.accounts.action.ACCOUNT_REMOVED" ACTION_VISIBLE_ACCOUNTS_CHANGED = "android.accounts.action.VISIBLE_ACCOUNTS_CHANGED" KEY_ACCOUNTS = "accounts" KEY_ACCOUNT_MANAGER_RESPONSE = "accountManagerResponse" KEY_ACCOUNT_TYPE = "accountType" KEY_AUTHTOKEN = "authtoken" KEY_AUTH_FAILED_MESSAGE = "authFailedMessage" KEY_AUTH_TOKEN_LABEL = "authTokenLabelKey" KEY_BOOLEAN_RESULT = "booleanResult" KEY_ERROR_CODE = "errorCode" KEY_ERROR_MESSAGE = "errorMessage" KEY_INTENT = "intent" KEY_LAST_SEEN_PIN = "lastSeenPin" KEY_PASSWORD = "password" KEY_USERDATA = "userdata" LOGIN_ACCOUNTS_CHANGED_ACTION = "android.accounts.LOGIN_ACCOUNTS_CHANGED" PACKAGE_NAME_KEY_LEGACY_NOT_VISIBLE = "android.accounts.legacy_not_visible" PACKAGE_NAME_KEY_LEGACY_VISIBLE = "android.accounts.legacy_visible" PACKAGE_NAME_KEY_LEGACY_NOT_VISIBLE = "android.accounts.legacy_not_visible" ERROR_CODE_REMOTE_EXCEPTION = 1 ERROR_CODE_NETWORK_ERROR = 2 ERROR_CODE_CANCELED = 3 ERROR_CODE_INVALID_RESPONSE = 4 ERROR_CODE_UNSUPPORTED_OPERATION = 5 ERROR_CODE_BAD_ARGUMENTS = 6 ERROR_CODE_BAD_AUTHENTICATION = 7 ERROR_CODE_BAD_REQUEST = 8 ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE = 101 def __init__(self): super().__init__() self._lock = threading.Lock() self._accounts: Dict[str, Account] = {} self._passwords: Dict[str, str] = {} self._userdata: Dict[str, Dict[str, str]] = {} self._auth_tokens: Dict[str, Dict[str, str]] = {} self._listeners: List[OnAccountsUpdateListener] = [] self._listener_handler: Dict[OnAccountsUpdateListener, Any] = {} @staticmethod def get(context: Any = None) -> "AccountManager": return AccountManager() # ── Account listing ── def getAccounts(self) -> List[Account]: with self._lock: return list(self._accounts.values()) def getAccountsByType(self, type_: str) -> List[Account]: with self._lock: return [a for a in self._accounts.values() if a.type == type_] def getAccountsByTypeAndFeatures(self, type_: str, features: List[str], callback: Optional[AccountManagerCallback] = None, handler: Any = None) -> AccountManagerFuture: future = AccountManagerFuture() accounts = self.getAccountsByType(type_) if callback: callback.run(future) future._set_result(accounts) return future def getAccountsForPackage(self, packageName: str, uid: int = -1) -> List[Account]: return self.getAccounts() def getSharedAccounts(self, userId: int) -> List[Account]: return self.getAccounts() # ── CRUD ── def addAccount(self, accountType: str, authTokenType: Optional[str] = None, requiredFeatures: Optional[List[str]] = None, addAccountOptions: Optional[Dict[str, Any]] = None, activity: Any = None, callback: Optional[AccountManagerCallback] = None, handler: Any = None) -> AccountManagerFuture: future = AccountManagerFuture() name = f"account_{uuid.uuid4().hex[:8]}" account = Account(name, accountType) with self._lock: self._accounts[f"{name}:{accountType}"] = account future._set_result(None) if callback: callback.run(future) return future def addAccountExplicitly(self, account: Account, password: Optional[str] = None, userdata: Optional[Dict[str, Any]] = None) -> bool: key = f"{account.name}:{account.type}" with self._lock: if key in self._accounts: return False self._accounts[key] = account if password is not None: self._passwords[key] = password if userdata: self._userdata[key] = {k: str(v) for k, v in userdata.items()} return True def removeAccount(self, account: Account, callback: Optional[AccountManagerCallback] = None, handler: Any = None) -> AccountManagerFuture: future = AccountManagerFuture() key = f"{account.name}:{account.type}" with self._lock: self._accounts.pop(key, None) self._passwords.pop(key, None) self._userdata.pop(key, None) self._auth_tokens.pop(key, None) future._set_result(True) if callback: callback.run(future) return future def renameAccount(self, account: Account, newName: str) -> AccountManagerFuture: future = AccountManagerFuture() key = f"{account.name}:{account.type}" with self._lock: if key in self._accounts: new_key = f"{newName}:{account.type}" self._accounts[new_key] = Account(newName, account.type) if key in self._passwords: self._passwords[new_key] = self._passwords.pop(key) if key in self._userdata: self._userdata[new_key] = self._userdata.pop(key) if key in self._auth_tokens: self._auth_tokens[new_key] = self._auth_tokens.pop(key) del self._accounts[key] future._set_result(account) return future # ── Password ── def getPassword(self, account: Account) -> Optional[str]: key = f"{account.name}:{account.type}" with self._lock: return self._passwords.get(key) def setPassword(self, account: Account, password: str): key = f"{account.name}:{account.type}" with self._lock: if password: self._passwords[key] = password else: self._passwords.pop(key, None) def clearPassword(self, account: Account): self.setPassword(account, "") # ── User data ── def getUserData(self, account: Account, key: str) -> Optional[str]: ak = f"{account.name}:{account.type}" with self._lock: userdata = self._userdata.get(ak, {}) return userdata.get(key) def setUserData(self, account: Account, key: str, value: str): ak = f"{account.name}:{account.type}" with self._lock: if ak not in self._userdata: self._userdata[ak] = {} self._userdata[ak][key] = value # ── Auth tokens ── def getAuthToken(self, account: Account, authTokenType: str, notifyAuthFailure: bool = True, callback: Optional[AccountManagerCallback] = None, handler: Any = None) -> AccountManagerFuture: future = AccountManagerFuture() key = f"{account.name}:{account.type}" with self._lock: tokens = self._auth_tokens.get(key, {}) token = tokens.get(authTokenType) future._set_result(token) if callback: callback.run(future) return future def peekAuthToken(self, account: Account, authTokenType: str) -> Optional[str]: key = f"{account.name}:{account.type}" with self._lock: tokens = self._auth_tokens.get(key, {}) return tokens.get(authTokenType) def addAuthToken(self, account: Account, authTokenType: str, authToken: str): key = f"{account.name}:{account.type}" with self._lock: if key not in self._auth_tokens: self._auth_tokens[key] = {} self._auth_tokens[key][authTokenType] = authToken def setAuthToken(self, account: Account, authTokenType: str, authToken: str): self.addAuthToken(account, authTokenType, authToken) def invalidateAuthToken(self, accountType: str, authToken: str): with self._lock: for key in list(self._auth_tokens.keys()): if key.endswith(f":{accountType}"): tokens = self._auth_tokens[key] for ttype, token in list(tokens.items()): if token == authToken: del tokens[ttype] # ── Features ── def hasFeatures(self, account: Account, features: List[str], callback: AccountManagerCallback, handler: Any = None) -> AccountManagerFuture: future = AccountManagerFuture() future._set_result(True) if callback: callback.run(future) return future # ── Create account ── def createAccount(self, accountType: str, authTokenType: Optional[str] = None, requiredFeatures: Optional[List[str]] = None, options: Optional[Dict[str, Any]] = None, activity: Any = None, callback: Optional[AccountManagerCallback] = None, handler: Any = None) -> AccountManagerFuture: return self.addAccount(accountType, authTokenType, requiredFeatures, options, activity, callback, handler) # ── Shared accounts ── def removeSharedAccount(self, account: Account, callback: Optional[AccountManagerCallback] = None, handler: Any = None) -> AccountManagerFuture: return self.removeAccount(account, callback, handler) def updateAppPermission(self, account: Account, authTokenType: str, uid: int, value: bool): pass def setAuthTokenType(self, account: Account, authTokenType: str): pass # ── Confirm / Edit ── def confirmCredentials(self, account: Account, options: Optional[Dict[str, Any]] = None, activity: Any = None, callback: Optional[AccountManagerCallback] = None, handler: Any = None) -> AccountManagerFuture: future = AccountManagerFuture() future._set_result(None) if callback: callback.run(future) return future def editProperties(self, accountType: str, activity: Any = None, callback: Optional[AccountManagerCallback] = None, handler: Any = None) -> AccountManagerFuture: future = AccountManagerFuture() future._set_result(None) if callback: callback.run(future) return future # ── Listeners ── def addOnAccountsUpdatedListener(self, listener: OnAccountsUpdateListener, handler: Any = None, updateImmediately: bool = False): with self._lock: self._listeners.append(listener) self._listener_handler[listener] = handler def removeOnAccountsUpdatedListener(self, listener: OnAccountsUpdateListener): with self._lock: if listener in self._listeners: self._listeners.remove(listener) self._listener_handler.pop(listener, None) def _notify_listeners(self): with self._lock: accounts = list(self._accounts.values()) for listener in self._listeners: listener.onAccountsUpdated(accounts) def toString(self) -> str: return f"AccountManager@{hex(id(self))}" # ═══════════════════════════════════════════════════════════════ # __all__ # ═══════════════════════════════════════════════════════════════ __all__ = [ "Account", "AccountManager", "AccountManagerFuture", "AccountManagerCallback", "AuthenticatorDescription", "OnAccountsUpdateListener", ]