""" Android Service Notification 组件 — NotificationListenerService, StatusBarNotification, RankingMap 等 方法签名完全对齐 Android SDK (android.service.notification 包) """ import time from typing import Any, Callable, Dict, List, Optional, Set, Tuple from .._core import Stub, AndroidOnlyError # ═══════════════════════════════════════════════════════════════ # StatusBarNotification # ═══════════════════════════════════════════════════════════════ class StatusBarNotification(Stub): """StatusBarNotification — 状态栏通知""" def __init__(self, pkg: str = "", op_pkg: str = "", id: int = 0, tag: Optional[str] = None, uid: int = 10001, pid: int = 1000, user: Any = None, notification: Any = None, post_time: int = 0): super().__init__() self._pkg = pkg self._op_pkg = op_pkg or pkg self._id = id self._tag = tag self._uid = uid self._pid = pid self._user = user self._notification = notification self._post_time = post_time or int(time.time() * 1000) self._is_clearable = True self._is_ongoing = False self._key = f"{pkg}|{id}|{tag or ''}" self._override_group_key: Optional[str] = None self._override_public = False self._initial_source_id = 0 self._initial_source_uid = uid self._initial_source_pkg = pkg def getPackageName(self) -> str: """获取通知所属包名""" return self._pkg def getOpPkg(self) -> str: return self._op_pkg def getNotification(self) -> Any: """获取 Notification 对象""" return self._notification def getId(self) -> int: """获取通知 ID""" return self._id def getTag(self) -> Optional[str]: """获取通知标签""" return self._tag def getPostTime(self) -> int: """获取发送时间 (毫秒)""" return self._post_time def getUid(self) -> int: return self._uid def getPid(self) -> int: return self._pid def getUser(self) -> Any: return self._user def getKey(self) -> str: return self._key def isClearable(self) -> bool: """是否可以清除""" return self._is_clearable def isOngoing(self) -> bool: """是否是持续通知""" return self._is_ongoing def setClearable(self, clearable: bool): self._is_clearable = clearable def setOngoing(self, ongoing: bool): self._is_ongoing = ongoing def getOverrideGroupKey(self) -> Optional[str]: return self._override_group_key def setOverrideGroupKey(self, override_group_key: Optional[str]): self._override_group_key = override_group_key def isGroup(self) -> bool: return self._override_group_key is not None def clone(self) -> "StatusBarNotification": """克隆通知""" clone = StatusBarNotification( pkg=self._pkg, op_pkg=self._op_pkg, id=self._id, tag=self._tag, uid=self._uid, pid=self._pid, user=self._user, notification=self._notification, post_time=self._post_time, ) clone._is_clearable = self._is_clearable clone._is_ongoing = self._is_ongoing clone._key = self._key clone._override_group_key = self._override_group_key return clone def toString(self) -> str: return (f"StatusBarNotification(pkg={self._pkg}, id={self._id}, " f"tag={self._tag}, postTime={self._post_time})") def hashCode(self) -> int: return hash(self._key) & 0x7FFFFFFF def equals(self, other) -> bool: if not isinstance(other, StatusBarNotification): return False return self._key == other._key # ═══════════════════════════════════════════════════════════════ # NotificationListenerService # ═══════════════════════════════════════════════════════════════ class NotificationListenerService(Stub): """NotificationListenerService — 通知监听服务""" # 取消原因 REASON_LISTENER_CANCEL = 1 REASON_CLICK = 2 REASON_CANCEL = 3 REASON_DELETE = 4 REASON_PACKAGE_CHANGED = 5 REASON_USER_STOPPED = 6 REASON_PACKACE_BANNED = 7 REASON_APP_CANCEL = 8 REASON_APP_CANCEL_ALL = 9 REASON_CHANNEL_BANNED = 10 REASON_SNOOZED = 11 REASON_TIMEOUT = 12 REASON_ERROR = 13 REASON_GROUP_SUMMARY_CANCELED = 14 REASON_ASSISTANT_CANCEL = 15 # 中断过滤器 INTERRUPTION_FILTER_NONE = 0 INTERRUPTION_FILTER_PRIORITY = 1 INTERRUPTION_FILTER_ALARMS = 2 INTERRUPTION_FILTER_ALL = 3 INTERRUPTION_FILTER_UNKNOWN = 4 # 提示 HINT_HOST_DISABLE_CALL_EFFECTS = 1 HINT_HOST_DISABLE_EFFECTS = 2 HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 3 # SUW 组件 TRAMPOLINE_ACTIVITY_CLASS = ( "com.android.settings.Settings$NotificationAppListActivity" ) def __init__(self): super().__init__() self._active_notifications: Dict[str, StatusBarNotification] = {} self._keys_list: List[str] = [] self._interruption_filter: int = ( NotificationListenerService.INTERRUPTION_FILTER_ALL ) self._ranking_map: Optional["NotificationListenerService.RankingMap"] = None self._is_listener_bound = False @staticmethod def requestRebind(component_name: Any): """请求重新绑定通知监听器""" pass @staticmethod def requestUnbind(component_name: Any): """请求解绑通知监听器""" pass def onNotificationPosted(self, sbn: StatusBarNotification, ranking_map: Optional["RankingMap"] = None): """通知被发送时回调""" if sbn: self._active_notifications[sbn.getKey()] = sbn self._keys_list = list(self._active_notifications.keys()) if ranking_map: self._ranking_map = ranking_map def onNotificationRemoved(self, sbn: StatusBarNotification, ranking_map: Optional["RankingMap"] = None, reason: int = REASON_LISTENER_CANCEL): """通知被移除时回调""" if sbn: key = sbn.getKey() self._active_notifications.pop(key, None) if key in self._keys_list: self._keys_list.remove(key) def onListenerConnected(self): """监听器已连接""" self._is_listener_bound = True def onListenerDisconnected(self): """监听器已断开""" self._is_listener_bound = False def onNotificationRemovedReason(self, sbn: StatusBarNotification, ranking_map: "RankingMap", reason: int): """通知被移除 (带原因)""" self.onNotificationRemoved(sbn, ranking_map, reason) def onNotificationPostedWithRankingMap(self, sbn: StatusBarNotification, ranking_map: "RankingMap"): """通知被发送 (带排名映射)""" self.onNotificationPosted(sbn, ranking_map) def getActiveNotifications(self) -> List[StatusBarNotification]: """获取活动通知列表""" return list(self._active_notifications.values()) def getActiveNotificationsWithRankingMap(self) -> Tuple[ List[StatusBarNotification], Optional["RankingMap"]]: """获取活动通知及排名映射""" return list(self._active_notifications.values()), self._ranking_map def getCurrentInterruptionFilter(self) -> int: """获取当前中断过滤器""" return self._interruption_filter def setInterruptionFilter(self, interruption_filter: int): """设置中断过滤器""" self._interruption_filter = interruption_filter def cancelNotification(self, key: str): """取消指定通知""" self._active_notifications.pop(key, None) if key in self._keys_list: self._keys_list.remove(key) def cancelAllNotifications(self): """取消所有通知""" self._active_notifications.clear() self._keys_list.clear() def requestRebind(self, component_name: Any): """请求重新绑定""" pass def requestUnbind(self): """请求解绑""" pass def registerAsSystemService(self, context: Any, component_name: Any, user: int): pass def unregisterAsSystemService(self): pass def isBound(self) -> bool: return self._is_listener_bound def toString(self) -> str: return (f"NotificationListenerService(" f"notifications={len(self._active_notifications)}, " f"filter={self._interruption_filter})") def hashCode(self) -> int: return id(self) & 0x7FFFFFFF def equals(self, other) -> bool: return self is other # ── RankingMap ── class RankingMap(Stub): """RankingMap — 通知排名映射""" def __init__(self): super().__init__() self._rankings: Dict[str, "NotificationListenerService.Ranking"] = {} def getOrderedKeys(self) -> List[str]: """获取有序键列表""" return list(self._rankings.keys()) def getRanking(self, key: str) -> Optional["NotificationListenerService.Ranking"]: """获取指定通知的排名信息""" return self._rankings.get(key) def getRawRanking(self, key: str) -> Optional[Any]: return self._rankings.get(key) def toString(self) -> str: return f"RankingMap(keys={len(self._rankings)})" def hashCode(self) -> int: return hash(tuple(self._rankings.keys())) & 0x7FFFFFFF def equals(self, other) -> bool: if not isinstance(other, NotificationListenerService.RankingMap): return False return self._rankings == other._rankings class Ranking(Stub): """Ranking — 通知排名""" def __init__(self): super().__init__() self._key: str = "" self._rank: int = 0 self._is_ambient: bool = False self._matches_interruption_filter: bool = True self._suppressed_visual_effects: int = 0 self._importance: int = 3 # IMPORTANCE_DEFAULT self._importance_explanation: Optional[str] = None self._group_key: Optional[str] = None self._override_group_key: Optional[str] = None self._channel: Optional[Any] = None self._is_suspended: bool = False def getKey(self) -> str: return self._key def getRank(self) -> int: return self._rank def isAmbient(self) -> bool: return self._is_ambient def matchesInterruptionFilter(self) -> bool: return self._matches_interruption_filter def getSuppressedVisualEffects(self) -> int: return self._suppressed_visual_effects def getImportance(self) -> int: return self._importance def getImportanceExplanation(self) -> Optional[str]: return self._importance_explanation def getGroupKey(self) -> Optional[str]: return self._group_key def getOverrideGroupKey(self) -> Optional[str]: return self._override_group_key def getChannel(self) -> Optional[Any]: return self._channel def isSuspended(self) -> bool: return self._is_suspended def toString(self) -> str: return (f"Ranking(key={self._key}, rank={self._rank}, " f"importance={self._importance})") # ═══════════════════════════════════════════════════════════════ # __all__ # ═══════════════════════════════════════════════════════════════ __all__ = [ "NotificationListenerService", "StatusBarNotification", ]