""" Android media.browse 组件 — MediaBrowser, MediaItem, MediaDescription 等 方法签名完全对齐 Android SDK """ from typing import Any, Callable, Dict, List, Optional, Set from .._core import Stub, AndroidOnlyError # ═══════════════════════════════════════════════════════════════ # MediaBrowser # ═══════════════════════════════════════════════════════════════ class MediaBrowser(Stub): """MediaBrowser — 媒体浏览器""" EXTRA_PAGE = "android.media.browse.extra.PAGE" EXTRA_PAGE_SIZE = "android.media.browse.extra.PAGE_SIZE" # ═══════════════════════════════════════════════════════════ # ConnectionCallback # ═══════════════════════════════════════════════════════════ class ConnectionCallback(Stub): """MediaBrowser.ConnectionCallback — 连接回调""" def onConnected(self): pass def onConnectionSuspended(self): pass def onConnectionFailed(self): pass # ═══════════════════════════════════════════════════════════ # SubscriptionCallback # ═══════════════════════════════════════════════════════════ class SubscriptionCallback(Stub): """MediaBrowser.SubscriptionCallback — 订阅回调""" def onChildrenLoaded(self, parentId: str, children: List["MediaBrowser.MediaItem"]): pass def onError(self, parentId: str): pass def onChildrenLoadedWithOptions(self, parentId: str, children: List["MediaBrowser.MediaItem"], options: Any): pass # ═══════════════════════════════════════════════════════════ # ItemCallback # ═══════════════════════════════════════════════════════════ class ItemCallback(Stub): """MediaBrowser.ItemCallback — 媒体项回调""" def onItemLoaded(self, item: "MediaBrowser.MediaItem"): pass def onError(self, mediaId: str): pass # ═══════════════════════════════════════════════════════════ # MediaItem # ═══════════════════════════════════════════════════════════ class MediaItem(Stub): """MediaBrowser.MediaItem — 媒体项""" FLAG_BROWSABLE = 1 FLAG_PLAYABLE = 2 def __init__(self, description: "MediaDescription" = None, flags: int = FLAG_PLAYABLE): super().__init__() self._description = description or MediaDescription() self._flags = flags def getMediaId(self) -> Optional[str]: return self._description.getMediaId() def getDescription(self) -> "MediaDescription": return self._description def getFlags(self) -> int: return self._flags def isBrowsable(self) -> bool: return bool(self._flags & MediaBrowser.MediaItem.FLAG_BROWSABLE) def isPlayable(self) -> bool: return bool(self._flags & MediaBrowser.MediaItem.FLAG_PLAYABLE) def toString(self) -> str: return f"MediaItem(mediaId={self.getMediaId()}, flags={self._flags})" def __init__(self, context: Any = None, serviceComponent: Any = None, connectionCallback: Optional[ConnectionCallback] = None, rootHints: Any = None): super().__init__() self._connected = False self._service_component = serviceComponent self._root: str = "" self._session_token: Any = None self._connection_callback = connectionCallback self._subscriptions: Dict[str, List[MediaBrowser.SubscriptionCallback]] = {} self._media_tree: Dict[str, List[MediaBrowser.MediaItem]] = {} self._item_cache: Dict[str, MediaBrowser.MediaItem] = {} def connect(self): self._connected = True self._root = "__ROOT__" # Build a simple in-memory media tree self._media_tree["__ROOT__"] = [ MediaBrowser.MediaItem( MediaDescription.Builder() .setMediaId("__RECENT__") .setTitle("Recently Played") .setSubtitle("Your recently played media") .build(), flags=MediaBrowser.MediaItem.FLAG_BROWSABLE ), MediaBrowser.MediaItem( MediaDescription.Builder() .setMediaId("__ALL__") .setTitle("All Music") .setSubtitle("All your music library") .build(), flags=MediaBrowser.MediaItem.FLAG_BROWSABLE ), ] self._media_tree["__RECENT__"] = [ MediaBrowser.MediaItem( MediaDescription.Builder() .setMediaId("song_1") .setTitle("Sample Song 1") .setSubtitle("Artist 1") .build(), flags=MediaBrowser.MediaItem.FLAG_PLAYABLE ), ] self._item_cache["song_1"] = self._media_tree["__RECENT__"][0] if self._connection_callback: self._connection_callback.onConnected() def disconnect(self): self._connected = False self._subscriptions.clear() if self._connection_callback: self._connection_callback.onConnectionSuspended() def getConnected(self) -> bool: return self._connected def getServiceComponent(self) -> Any: return self._service_component def getRoot(self) -> str: return self._root def isConnected(self) -> bool: return self._connected def getSessionToken(self) -> Any: return self._session_token def getItem(self, mediaId: str) -> Optional[MediaItem]: return self._item_cache.get(mediaId) def subscribe(self, parentId: str, callback: SubscriptionCallback): if parentId not in self._subscriptions: self._subscriptions[parentId] = [] self._subscriptions[parentId].append(callback) children = self._media_tree.get(parentId, []) if children: callback.onChildrenLoaded(parentId, children) else: callback.onError(parentId) def unsubscribe(self, parentId: str, callback: Optional[SubscriptionCallback] = None): if parentId in self._subscriptions: if callback: self._subscriptions[parentId] = [ cb for cb in self._subscriptions[parentId] if cb is not callback ] if not self._subscriptions[parentId]: del self._subscriptions[parentId] else: del self._subscriptions[parentId] # ═══════════════════════════════════════════════════════════════ # MediaDescription # ═══════════════════════════════════════════════════════════════ class MediaDescription(Stub): """MediaDescription — 媒体描述""" class Builder(Stub): """MediaDescription.Builder — 媒体描述构建器""" def __init__(self): super().__init__() self._media_id: Optional[str] = None self._title: Optional[str] = None self._subtitle: Optional[str] = None self._description: Optional[str] = None self._icon_bitmap: Any = None self._icon_uri: Optional[str] = None self._extras: Any = None self._media_uri: Optional[str] = None def setMediaId(self, mediaId: str) -> "MediaDescription.Builder": self._media_id = mediaId return self def setTitle(self, title: str) -> "MediaDescription.Builder": self._title = title return self def setSubtitle(self, subtitle: str) -> "MediaDescription.Builder": self._subtitle = subtitle return self def setDescription(self, description: str) -> "MediaDescription.Builder": self._description = description return self def setIconBitmap(self, icon: Any) -> "MediaDescription.Builder": self._icon_bitmap = icon return self def setIconUri(self, iconUri: str) -> "MediaDescription.Builder": self._icon_uri = iconUri return self def setExtras(self, extras: Any) -> "MediaDescription.Builder": self._extras = extras return self def setMediaUri(self, mediaUri: str) -> "MediaDescription.Builder": self._media_uri = mediaUri return self def build(self) -> "MediaDescription": md = MediaDescription() md._media_id = self._media_id md._title = self._title md._subtitle = self._subtitle md._description = self._description md._icon_bitmap = self._icon_bitmap md._icon_uri = self._icon_uri md._extras = self._extras md._media_uri = self._media_uri return md def __init__(self): super().__init__() self._media_id: Optional[str] = None self._title: Optional[str] = None self._subtitle: Optional[str] = None self._description: Optional[str] = None self._icon_bitmap: Any = None self._icon_uri: Optional[str] = None self._extras: Any = None self._media_uri: Optional[str] = None def getMediaId(self) -> Optional[str]: return self._media_id def getTitle(self) -> Optional[str]: return self._title def getSubtitle(self) -> Optional[str]: return self._subtitle def getDescription(self) -> Optional[str]: return self._description def getIconBitmap(self) -> Any: return self._icon_bitmap def getIconUri(self) -> Optional[str]: return self._icon_uri def getExtras(self) -> Any: return self._extras def getMediaUri(self) -> Optional[str]: return self._media_uri def toString(self) -> str: return f"MediaDescription(id={self._media_id}, title={self._title})" # ═══════════════════════════════════════════════════════════════ # __all__ # ═══════════════════════════════════════════════════════════════ __all__ = [ "MediaBrowser", "MediaDescription", ]