Files
Android-pyApi/android_pyapi/android/service_media.py
T
shuiyu1123 a51854455b feat: 大规模扩充至212个模块/175K行代码 — 覆盖android/java/javax/org/dalvik全包
- 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关键字冲突
2026-06-24 14:15:56 +00:00

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)