- 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关键字冲突
812 lines
27 KiB
Python
812 lines
27 KiB
Python
"""
|
|
Android Media Service 组件 — MediaBrowserService, MediaSessionService, BrowserRoot, Result 等
|
|
方法签名完全对齐 Android SDK (android.service.media 包)
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import time
|
|
from typing import Any, Callable, Dict, List, Optional, Set, Tuple
|
|
|
|
from .._core import Stub, AndroidOnlyError
|
|
from ..java.lang import RuntimeException
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# Result
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class Result(Stub):
|
|
"""Result — 异步操作结果"""
|
|
|
|
FLAG_OPTION_NOT_KNOWN = 0
|
|
FLAG_OPTION_FOUND = 1
|
|
FLAG_OPTION_NOT_FOUND = 2
|
|
FLAG_OPTION_ERROR = 4
|
|
|
|
def __init__(self, result_type: Any = None):
|
|
super().__init__()
|
|
self._result_type = result_type
|
|
self._result: Any = None
|
|
self._flags: int = Result.FLAG_OPTION_NOT_KNOWN
|
|
self._is_done: bool = False
|
|
self._is_sent: bool = False
|
|
self._error: Optional[str] = None
|
|
|
|
def getResult(self) -> Any:
|
|
return self._result
|
|
|
|
def setResult(self, result: Any):
|
|
self._result = result
|
|
|
|
def getFlags(self) -> int:
|
|
return self._flags
|
|
|
|
def setFlags(self, flags: int):
|
|
self._flags = flags
|
|
|
|
def isDone(self) -> bool:
|
|
return self._is_done
|
|
|
|
def isSent(self) -> bool:
|
|
return self._is_sent
|
|
|
|
def getError(self) -> Optional[str]:
|
|
return self._error
|
|
|
|
def sendResult(self, result: Any):
|
|
"""发送结果"""
|
|
self._result = result
|
|
self._is_done = True
|
|
self._is_sent = True
|
|
|
|
def sendError(self, error: str):
|
|
"""发送错误"""
|
|
self._error = error
|
|
self._is_done = True
|
|
self._is_sent = True
|
|
self._flags = Result.FLAG_OPTION_ERROR
|
|
|
|
def onResultSent(self, result: Any, flags: int = 0):
|
|
"""结果已发送回调"""
|
|
self._result = result
|
|
self._flags = flags
|
|
self._is_done = True
|
|
|
|
def toString(self) -> str:
|
|
return f"Result(done={self._is_done}, sent={self._is_sent})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# BrowserRoot
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class BrowserRoot(Stub):
|
|
"""BrowserRoot — 媒体浏览根节点"""
|
|
|
|
EXTRA_RECENT = "android.service.media.extra.RECENT"
|
|
EXTRA_OFFLINE = "android.service.media.extra.OFFLINE"
|
|
EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED"
|
|
EXTRA_EMPTY_ROOT_ALLOWED = "android.service.media.extra.EMPTY_ROOT_ALLOWED"
|
|
EXTRA_RECYCLERVIEW = "android.service.media.extra.RECYCLERVIEW"
|
|
|
|
def __init__(self, root_id: str = "", extras: Any = None):
|
|
super().__init__()
|
|
self._root_id = root_id
|
|
self._extras = extras
|
|
self._title: str = ""
|
|
self._is_visible: bool = True
|
|
self._is_playable: bool = False
|
|
self._is_browsable: bool = True
|
|
self._children: List[MediaBrowserService.BrowserRoot] = []
|
|
self._icon_uri: str = ""
|
|
self._subtitle: str = ""
|
|
self._description: str = ""
|
|
|
|
def getRootId(self) -> str:
|
|
return self._root_id
|
|
|
|
def setRootId(self, root_id: str):
|
|
self._root_id = root_id
|
|
|
|
def getExtras(self) -> Any:
|
|
return self._extras
|
|
|
|
def setExtras(self, extras: Any):
|
|
self._extras = extras
|
|
|
|
def getTitle(self) -> str:
|
|
return self._title
|
|
|
|
def setTitle(self, title: str):
|
|
self._title = title
|
|
|
|
def isVisible(self) -> bool:
|
|
return self._is_visible
|
|
|
|
def setVisible(self, visible: bool):
|
|
self._is_visible = visible
|
|
|
|
def isPlayable(self) -> bool:
|
|
return self._is_playable
|
|
|
|
def setPlayable(self, playable: bool):
|
|
self._is_playable = playable
|
|
|
|
def isBrowsable(self) -> bool:
|
|
return self._is_browsable
|
|
|
|
def setBrowsable(self, browsable: bool):
|
|
self._is_browsable = browsable
|
|
|
|
def getIconUri(self) -> str:
|
|
return self._icon_uri
|
|
|
|
def setIconUri(self, uri: str):
|
|
self._icon_uri = uri
|
|
|
|
def getSubtitle(self) -> str:
|
|
return self._subtitle
|
|
|
|
def setSubtitle(self, subtitle: str):
|
|
self._subtitle = subtitle
|
|
|
|
def getDescription(self) -> str:
|
|
return self._description
|
|
|
|
def setDescription(self, desc: str):
|
|
self._description = desc
|
|
|
|
def toString(self) -> str:
|
|
return f"BrowserRoot(rootId={self._root_id})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# MediaBrowserService
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class MediaBrowserService(Stub):
|
|
"""MediaBrowserService — 媒体浏览服务"""
|
|
|
|
SERVICE_INTERFACE = "android.media.browse.MediaBrowserService"
|
|
SERVICE_META_DATA = "android.media.browse"
|
|
|
|
KEY_MEDIA_ITEM = "media_item"
|
|
KEY_SEARCH_RESULTS = "search_results"
|
|
KEY_ERROR = "error"
|
|
KEY_MEDIA_ROOT = "media_root"
|
|
KEY_RECENT = "recent"
|
|
KEY_OFFLINE = "offline"
|
|
KEY_SUGGESTED = "suggested"
|
|
KEY_EMPTY_ROOT_ALLOWED = "empty_root_allowed"
|
|
KEY_PAGE = "page"
|
|
KEY_PAGE_SIZE = "page_size"
|
|
|
|
# ═══════════════════════════════════════════════════════
|
|
# BrowserRoot (inner class)
|
|
# ═══════════════════════════════════════════════════════
|
|
|
|
class BrowserRoot(Stub):
|
|
"""MediaBrowserService.BrowserRoot — 浏览根节点"""
|
|
|
|
EXTRA_RECENT = "android.service.media.extra.RECENT"
|
|
EXTRA_OFFLINE = "android.service.media.extra.OFFLINE"
|
|
EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED"
|
|
EXTRA_EMPTY_ROOT_ALLOWED = "android.service.media.extra.EMPTY_ROOT_ALLOWED"
|
|
|
|
def __init__(self, root_id: str = "", extras: Any = None):
|
|
super().__init__()
|
|
self._root_id = root_id
|
|
self._extras = extras
|
|
|
|
def getRootId(self) -> str:
|
|
return self._root_id
|
|
|
|
def getExtras(self) -> Any:
|
|
return self._extras
|
|
|
|
# ═══════════════════════════════════════════════════════
|
|
# Result (inner class)
|
|
# ═══════════════════════════════════════════════════════
|
|
|
|
class Result(Stub):
|
|
"""MediaBrowserService.Result — 服务结果"""
|
|
|
|
FLAG_OPTION_NOT_KNOWN = 0
|
|
FLAG_OPTION_FOUND = 1
|
|
FLAG_OPTION_NOT_FOUND = 2
|
|
FLAG_OPTION_ERROR = 4
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._result: Any = None
|
|
self._flags: int = MediaBrowserService.Result.FLAG_OPTION_NOT_KNOWN
|
|
self._is_done: bool = False
|
|
self._error: Optional[str] = None
|
|
|
|
def getResult(self) -> Any:
|
|
return self._result
|
|
|
|
def getFlags(self) -> int:
|
|
return self._flags
|
|
|
|
def isDone(self) -> bool:
|
|
return self._is_done
|
|
|
|
def sendResult(self, result: Any, flags: int = 0):
|
|
self._result = result
|
|
self._flags = flags
|
|
self._is_done = True
|
|
|
|
def sendError(self, error: str):
|
|
self._error = error
|
|
self._flags = MediaBrowserService.Result.FLAG_OPTION_ERROR
|
|
self._is_done = True
|
|
|
|
def onResultSent(self, result: Any, flags: int = 0):
|
|
self._result = result
|
|
self._flags = flags
|
|
self._is_done = True
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._root: Optional[MediaBrowserService.BrowserRoot] = None
|
|
self._session: Optional[MediaSessionService.MediaSession] = None
|
|
self._is_connected: bool = False
|
|
self._media_items: Dict[str, Any] = {}
|
|
self._children_cache: Dict[str, List[Any]] = {}
|
|
self._search_results: Dict[str, List[Any]] = {}
|
|
self._package_name: str = ""
|
|
self._on_load_children_callbacks: Dict[str, Callable] = {}
|
|
self._on_load_item_callbacks: Dict[str, Callable] = {}
|
|
|
|
def onGetRoot(self, client_package_name: str, client_uid: int,
|
|
root_hints: Any = None) -> Optional[BrowserRoot]:
|
|
"""获取根节点回调"""
|
|
return self._root
|
|
|
|
def onLoadChildren(self, parent_id: str, result: Result):
|
|
"""加载子节点回调"""
|
|
children = self._children_cache.get(parent_id, [])
|
|
result.sendResult(children)
|
|
|
|
def onLoadChildrenWithOptions(self, parent_id: str, result: Result,
|
|
options: Any = None):
|
|
"""加载子节点 (带选项) 回调"""
|
|
self.onLoadChildren(parent_id, result)
|
|
|
|
def onLoadItem(self, item_id: str, result: Result):
|
|
"""加载媒体项回调"""
|
|
item = self._media_items.get(item_id)
|
|
if item:
|
|
result.sendResult(item)
|
|
else:
|
|
result.sendError(f"Item not found: {item_id}")
|
|
|
|
def onSearch(self, query: str, extras: Any, result: Result):
|
|
"""搜索回调"""
|
|
results = self._search_results.get(query, [])
|
|
result.sendResult(results)
|
|
|
|
def onCustomAction(self, action: str, extras: Any, result: Result):
|
|
"""自定义动作回调"""
|
|
result.sendResult(None)
|
|
|
|
def setRoot(self, root: BrowserRoot):
|
|
self._root = root
|
|
|
|
def getRoot(self) -> Optional[BrowserRoot]:
|
|
return self._root
|
|
|
|
def setSession(self, session: MediaSessionService.MediaSession):
|
|
self._session = session
|
|
|
|
def getSession(self) -> Optional[MediaSessionService.MediaSession]:
|
|
return self._session
|
|
|
|
def setMediaItems(self, items: Dict[str, Any]):
|
|
self._media_items = dict(items)
|
|
|
|
def addMediaItem(self, item_id: str, item: Any):
|
|
self._media_items[item_id] = item
|
|
|
|
def setChildren(self, parent_id: str, children: List[Any]):
|
|
self._children_cache[parent_id] = list(children)
|
|
|
|
def addChild(self, parent_id: str, child: Any):
|
|
if parent_id not in self._children_cache:
|
|
self._children_cache[parent_id] = []
|
|
self._children_cache[parent_id].append(child)
|
|
|
|
def setSearchResults(self, query: str, results: List[Any]):
|
|
self._search_results[query] = list(results)
|
|
|
|
def isConnected(self) -> bool:
|
|
return self._is_connected
|
|
|
|
def setConnected(self, connected: bool):
|
|
self._is_connected = connected
|
|
|
|
def getPackageName(self) -> str:
|
|
return self._package_name
|
|
|
|
def setPackageName(self, pkg: str):
|
|
self._package_name = pkg
|
|
|
|
def notifyChildrenChanged(self, parent_id: str):
|
|
"""通知子节点已变更"""
|
|
pass
|
|
|
|
def notifyChildrenChangedWithOptions(self, parent_id: str, options: Any = None):
|
|
"""通知子节点已变更 (带选项)"""
|
|
pass
|
|
|
|
def toString(self) -> str:
|
|
return f"MediaBrowserService(root={self._root})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# MediaSessionService
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class MediaSessionService(Stub):
|
|
"""MediaSessionService — 媒体会话服务"""
|
|
|
|
SERVICE_INTERFACE = "android.media.session.MediaSessionService"
|
|
SERVICE_META_DATA = "android.media.session"
|
|
|
|
KEY_MEDIA_SESSION = "media_session"
|
|
KEY_MEDIA_SESSION_TOKEN = "media_session_token"
|
|
KEY_MEDIA_CONTROLLER = "media_controller"
|
|
KEY_PLAYBACK_STATE = "playback_state"
|
|
KEY_METADATA = "metadata"
|
|
KEY_QUEUE = "queue"
|
|
KEY_QUEUE_TITLE = "queue_title"
|
|
KEY_ACTIONS = "actions"
|
|
KEY_ERROR = "error"
|
|
KEY_EXTRAS = "extras"
|
|
KEY_RATING = "rating"
|
|
KEY_REPEAT_MODE = "repeat_mode"
|
|
KEY_SHUFFLE_MODE = "shuffle_mode"
|
|
|
|
# ═══════════════════════════════════════════════════════
|
|
# MediaSession
|
|
# ═══════════════════════════════════════════════════════
|
|
|
|
class MediaSession(Stub):
|
|
"""MediaSessionService.MediaSession — 媒体会话"""
|
|
|
|
FLAG_HANDLES_MEDIA_BUTTONS = 1
|
|
FLAG_HANDLES_TRANSPORT_CONTROLS = 2
|
|
FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 4
|
|
|
|
PLAYBACK_STATE_NONE = 0
|
|
PLAYBACK_STATE_STOPPED = 1
|
|
PLAYBACK_STATE_PAUSED = 2
|
|
PLAYBACK_STATE_PLAYING = 3
|
|
PLAYBACK_STATE_FAST_FORWARDING = 4
|
|
PLAYBACK_STATE_REWINDING = 5
|
|
PLAYBACK_STATE_BUFFERING = 6
|
|
PLAYBACK_STATE_ERROR = 7
|
|
PLAYBACK_STATE_SKIPPING_TO_PREVIOUS = 8
|
|
PLAYBACK_STATE_SKIPPING_TO_NEXT = 9
|
|
PLAYBACK_STATE_SKIPPING_TO_QUEUE_ITEM = 10
|
|
PLAYBACK_STATE_CONNECTING = 11
|
|
|
|
REPEAT_MODE_NONE = 0
|
|
REPEAT_MODE_ONE = 1
|
|
REPEAT_MODE_ALL = 2
|
|
REPEAT_MODE_GROUP = 3
|
|
|
|
SHUFFLE_MODE_NONE = 0
|
|
SHUFFLE_MODE_ALL = 1
|
|
SHUFFLE_MODE_GROUP = 2
|
|
|
|
ACTION_PLAY = 1
|
|
ACTION_PAUSE = 2
|
|
ACTION_PLAY_PAUSE = 4
|
|
ACTION_STOP = 8
|
|
ACTION_SEEK_TO = 16
|
|
ACTION_FAST_FORWARD = 32
|
|
ACTION_REWIND = 64
|
|
ACTION_SKIP_TO_NEXT = 128
|
|
ACTION_SKIP_TO_PREVIOUS = 256
|
|
ACTION_SKIP_TO_QUEUE_ITEM = 512
|
|
ACTION_SET_RATING = 1024
|
|
ACTION_SET_REPEAT_MODE = 2048
|
|
ACTION_SET_SHUFFLE_MODE = 4096
|
|
ACTION_SET_CAPTIONING_ENABLED = 8192
|
|
ACTION_PLAY_FROM_MEDIA_ID = 16384
|
|
ACTION_PLAY_FROM_SEARCH = 32768
|
|
ACTION_PLAY_FROM_URI = 65536
|
|
ACTION_PREPARE = 131072
|
|
ACTION_PREPARE_FROM_MEDIA_ID = 262144
|
|
ACTION_PREPARE_FROM_SEARCH = 524288
|
|
ACTION_PREPARE_FROM_URI = 1048576
|
|
|
|
class Callback(Stub):
|
|
"""MediaSessionService.MediaSession.Callback — 会话回调"""
|
|
|
|
def onPlay(self):
|
|
pass
|
|
|
|
def onPause(self):
|
|
pass
|
|
|
|
def onStop(self):
|
|
pass
|
|
|
|
def onSeekTo(self, pos: int):
|
|
pass
|
|
|
|
def onFastForward(self):
|
|
pass
|
|
|
|
def onRewind(self):
|
|
pass
|
|
|
|
def onSkipToNext(self):
|
|
pass
|
|
|
|
def onSkipToPrevious(self):
|
|
pass
|
|
|
|
def onSkipToQueueItem(self, id: int):
|
|
pass
|
|
|
|
def onSetRating(self, rating: Any):
|
|
pass
|
|
|
|
def onSetRepeatMode(self, repeat_mode: int):
|
|
pass
|
|
|
|
def onSetShuffleMode(self, shuffle_mode: int):
|
|
pass
|
|
|
|
def onPlayFromMediaId(self, media_id: str, extras: Any):
|
|
pass
|
|
|
|
def onPlayFromSearch(self, query: str, extras: Any):
|
|
pass
|
|
|
|
def onPlayFromUri(self, uri: str, extras: Any):
|
|
pass
|
|
|
|
def onPrepare(self):
|
|
pass
|
|
|
|
def onPrepareFromMediaId(self, media_id: str, extras: Any):
|
|
pass
|
|
|
|
def onPrepareFromSearch(self, query: str, extras: Any):
|
|
pass
|
|
|
|
def onPrepareFromUri(self, uri: str, extras: Any):
|
|
pass
|
|
|
|
def onCommand(self, command: str, extras: Any, cb: Any):
|
|
pass
|
|
|
|
def onCustomAction(self, action: str, extras: Any):
|
|
pass
|
|
|
|
def onMediaButtonEvent(self, intent: Any) -> bool:
|
|
return False
|
|
|
|
def __init__(self, tag: str = ""):
|
|
super().__init__()
|
|
self._tag = tag
|
|
self._callback: Optional[MediaSessionService.MediaSession.Callback] = None
|
|
self._playback_state: int = MediaSessionService.MediaSession.PLAYBACK_STATE_NONE
|
|
self._metadata: Any = None
|
|
self._queue: List[Any] = []
|
|
self._queue_title: str = ""
|
|
self._actions: int = 0
|
|
self._repeat_mode: int = MediaSessionService.MediaSession.REPEAT_MODE_NONE
|
|
self._shuffle_mode: int = MediaSessionService.MediaSession.SHUFFLE_MODE_NONE
|
|
self._active: bool = False
|
|
self._flags: int = 0
|
|
self._position: int = 0
|
|
self._duration: int = 0
|
|
self._error: Optional[str] = None
|
|
self._extras: Dict[str, Any] = {}
|
|
|
|
def setCallback(self, callback: Callback):
|
|
self._callback = callback
|
|
|
|
def getCallback(self) -> Optional[Callback]:
|
|
return self._callback
|
|
|
|
def setActive(self, active: bool):
|
|
self._active = active
|
|
|
|
def isActive(self) -> bool:
|
|
return self._active
|
|
|
|
def setFlags(self, flags: int):
|
|
self._flags = flags
|
|
|
|
def getFlags(self) -> int:
|
|
return self._flags
|
|
|
|
def setPlaybackState(self, state: int):
|
|
self._playback_state = state
|
|
|
|
def getPlaybackState(self) -> int:
|
|
return self._playback_state
|
|
|
|
def setMetadata(self, metadata: Any):
|
|
self._metadata = metadata
|
|
|
|
def getMetadata(self) -> Any:
|
|
return self._metadata
|
|
|
|
def setQueue(self, queue: List[Any]):
|
|
self._queue = list(queue)
|
|
|
|
def getQueue(self) -> List[Any]:
|
|
return list(self._queue)
|
|
|
|
def setQueueTitle(self, title: str):
|
|
self._queue_title = title
|
|
|
|
def getQueueTitle(self) -> str:
|
|
return self._queue_title
|
|
|
|
def setActions(self, actions: int):
|
|
self._actions = actions
|
|
|
|
def getActions(self) -> int:
|
|
return self._actions
|
|
|
|
def setRepeatMode(self, mode: int):
|
|
self._repeat_mode = mode
|
|
|
|
def getRepeatMode(self) -> int:
|
|
return self._repeat_mode
|
|
|
|
def setShuffleMode(self, mode: int):
|
|
self._shuffle_mode = mode
|
|
|
|
def getShuffleMode(self) -> int:
|
|
return self._shuffle_mode
|
|
|
|
def getTag(self) -> str:
|
|
return self._tag
|
|
|
|
def setPosition(self, pos: int):
|
|
self._position = pos
|
|
|
|
def getPosition(self) -> int:
|
|
return self._position
|
|
|
|
def setDuration(self, duration: int):
|
|
self._duration = duration
|
|
|
|
def getDuration(self) -> int:
|
|
return self._duration
|
|
|
|
def setError(self, error: str):
|
|
self._error = error
|
|
|
|
def getError(self) -> Optional[str]:
|
|
return self._error
|
|
|
|
def getExtras(self) -> Dict[str, Any]:
|
|
return dict(self._extras)
|
|
|
|
def setExtras(self, extras: Dict[str, Any]):
|
|
self._extras = dict(extras)
|
|
|
|
def isPlaying(self) -> bool:
|
|
return self._playback_state == MediaSessionService.MediaSession.PLAYBACK_STATE_PLAYING
|
|
|
|
def toString(self) -> str:
|
|
return f"MediaSession(tag={self._tag}, state={self._playback_state})"
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._sessions: Dict[str, MediaSessionService.MediaSession] = {}
|
|
self._browser_service: Optional[MediaBrowserService] = None
|
|
self._is_running: bool = False
|
|
|
|
def onCreate(self):
|
|
"""创建服务"""
|
|
self._is_running = True
|
|
|
|
def onDestroy(self):
|
|
"""销毁服务"""
|
|
self._is_running = False
|
|
for session in self._sessions.values():
|
|
session.setActive(False)
|
|
self._sessions.clear()
|
|
|
|
def onGetSession(self, controller_info: Any) -> Optional[MediaSession]:
|
|
"""获取会话回调"""
|
|
if self._sessions:
|
|
return next(iter(self._sessions.values()))
|
|
return None
|
|
|
|
def getSessions(self) -> List[MediaSession]:
|
|
return list(self._sessions.values())
|
|
|
|
def addSession(self, tag: str, session: MediaSession):
|
|
self._sessions[tag] = session
|
|
|
|
def removeSession(self, tag: str):
|
|
self._sessions.pop(tag, None)
|
|
|
|
def getSession(self, tag: str) -> Optional[MediaSession]:
|
|
return self._sessions.get(tag)
|
|
|
|
def setBrowserService(self, service: MediaBrowserService):
|
|
self._browser_service = service
|
|
|
|
def getBrowserService(self) -> Optional[MediaBrowserService]:
|
|
return self._browser_service
|
|
|
|
def isRunning(self) -> bool:
|
|
return self._is_running
|
|
|
|
def toString(self) -> str:
|
|
return f"MediaSessionService(sessions={len(self._sessions)})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# MediaSessionServiceCallback
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class MediaSessionServiceCallback(Stub):
|
|
"""MediaSessionServiceCallback — 媒体会话服务回调"""
|
|
|
|
def onSessionCreated(self, session: MediaSessionService.MediaSession):
|
|
pass
|
|
|
|
def onSessionDestroyed(self, session: MediaSessionService.MediaSession):
|
|
pass
|
|
|
|
def onSessionActive(self, session: MediaSessionService.MediaSession):
|
|
pass
|
|
|
|
def onSessionInactive(self, session: MediaSessionService.MediaSession):
|
|
pass
|
|
|
|
def onPlaybackStateChanged(self, session: MediaSessionService.MediaSession, state: int):
|
|
pass
|
|
|
|
def onMetadataChanged(self, session: MediaSessionService.MediaSession, metadata: Any):
|
|
pass
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# MediaSessionUtils
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class MediaSessionUtils(Stub):
|
|
"""MediaSessionUtils — 媒体会话工具类"""
|
|
|
|
@staticmethod
|
|
def createMediaSession(tag: str) -> MediaSessionService.MediaSession:
|
|
return MediaSessionService.MediaSession(tag)
|
|
|
|
@staticmethod
|
|
def createBrowserRoot(root_id: str, extras: Any = None) -> BrowserRoot:
|
|
return BrowserRoot(root_id, extras)
|
|
|
|
@staticmethod
|
|
def createServiceResult() -> MediaBrowserService.Result:
|
|
return MediaBrowserService.Result()
|
|
|
|
@staticmethod
|
|
def getPlaybackStateName(state: int) -> str:
|
|
names = {
|
|
MediaSessionService.MediaSession.PLAYBACK_STATE_NONE: "NONE",
|
|
MediaSessionService.MediaSession.PLAYBACK_STATE_STOPPED: "STOPPED",
|
|
MediaSessionService.MediaSession.PLAYBACK_STATE_PAUSED: "PAUSED",
|
|
MediaSessionService.MediaSession.PLAYBACK_STATE_PLAYING: "PLAYING",
|
|
MediaSessionService.MediaSession.PLAYBACK_STATE_FAST_FORWARDING: "FAST_FORWARDING",
|
|
MediaSessionService.MediaSession.PLAYBACK_STATE_REWINDING: "REWINDING",
|
|
MediaSessionService.MediaSession.PLAYBACK_STATE_BUFFERING: "BUFFERING",
|
|
MediaSessionService.MediaSession.PLAYBACK_STATE_ERROR: "ERROR",
|
|
}
|
|
return names.get(state, f"UNKNOWN({state})")
|
|
|
|
@staticmethod
|
|
def isActiveSession(session: MediaSessionService.MediaSession) -> bool:
|
|
return session is not None and session.isActive()
|
|
|
|
@staticmethod
|
|
def getSupportedActions(session: MediaSessionService.MediaSession) -> int:
|
|
return session.getActions() if session else 0
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# MediaSessionServiceConfig
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class MediaSessionServiceConfig(Stub):
|
|
"""MediaSessionServiceConfig — 媒体会话服务配置"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._max_sessions: int = 10
|
|
self._allow_multiple_sessions: bool = True
|
|
self._notification_enabled: bool = True
|
|
self._auto_play_on_start: bool = False
|
|
self._enable_media_buttons: bool = True
|
|
self._enable_queue: bool = True
|
|
|
|
def getMaxSessions(self) -> int:
|
|
return self._max_sessions
|
|
|
|
def setMaxSessions(self, max_sessions: int):
|
|
self._max_sessions = max_sessions
|
|
|
|
def isAllowMultipleSessions(self) -> bool:
|
|
return self._allow_multiple_sessions
|
|
|
|
def setAllowMultipleSessions(self, allow: bool):
|
|
self._allow_multiple_sessions = allow
|
|
|
|
def isNotificationEnabled(self) -> bool:
|
|
return self._notification_enabled
|
|
|
|
def setNotificationEnabled(self, enabled: bool):
|
|
self._notification_enabled = enabled
|
|
|
|
def isAutoPlayOnStart(self) -> bool:
|
|
return self._auto_play_on_start
|
|
|
|
def setAutoPlayOnStart(self, auto_play: bool):
|
|
self._auto_play_on_start = auto_play
|
|
|
|
def isMediaButtonsEnabled(self) -> bool:
|
|
return self._enable_media_buttons
|
|
|
|
def setMediaButtonsEnabled(self, enabled: bool):
|
|
self._enable_media_buttons = enabled
|
|
|
|
def isQueueEnabled(self) -> bool:
|
|
return self._enable_queue
|
|
|
|
def setQueueEnabled(self, enabled: bool):
|
|
self._enable_queue = enabled
|
|
|
|
def toString(self) -> str:
|
|
return f"MediaSessionServiceConfig(maxSessions={self._max_sessions})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# MediaSessionServiceHelper
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class MediaSessionServiceHelper(Stub):
|
|
"""MediaSessionServiceHelper — 媒体会话服务辅助类"""
|
|
|
|
@staticmethod
|
|
def requestMediaSessionService(context: Any, service: MediaSessionService):
|
|
"""请求媒体会话服务"""
|
|
service.onCreate()
|
|
|
|
@staticmethod
|
|
def releaseMediaSessionService(service: MediaSessionService):
|
|
"""释放媒体会话服务"""
|
|
service.onDestroy()
|
|
|
|
@staticmethod
|
|
def sendMediaButton(context: Any, session: MediaSessionService.MediaSession,
|
|
key_code: int):
|
|
"""发送媒体按键"""
|
|
cb = session.getCallback()
|
|
if cb:
|
|
cb.onMediaButtonEvent(None)
|
|
|
|
@staticmethod
|
|
def createDefaultSession(tag: str = "default") -> MediaSessionService.MediaSession:
|
|
session = MediaSessionService.MediaSession(tag)
|
|
session.setCallback(MediaSessionService.MediaSession.Callback())
|
|
return session
|
|
|
|
@staticmethod
|
|
def playbackStateToString(state: int) -> str:
|
|
return MediaSessionUtils.getPlaybackStateName(state) |