新增模块覆盖: 系统层 (14): system, security, print, opengl, renderscript, transition, test, service_notification, service_controls, service_quicksettings, service_wallpaper, service_autofill, service_dreams, service_vr 应用层 (13): app_admin, app_backup, app_job, app_usage, app_role, app_people, appwidget, companion, credentials, mtp, sip, telecom, speech 硬件媒体层 (15): hardware_display, hardware_fingerprint, hardware_usb, hardware_input, media_audiofx, media_browse, media_session, media_tv, media_midi, media_projection, uwb, se, view_autofill, view_textclassifier, view_translation 总计 94 个模块,75,342 行代码
854 lines
29 KiB
Python
854 lines
29 KiB
Python
"""
|
|
Android media.session 组件 — MediaSession, MediaController, PlaybackState, MediaMetadata 等
|
|
方法签名完全对齐 Android SDK
|
|
"""
|
|
|
|
import time
|
|
from typing import Any, Callable, Dict, List, Optional, Set, Union
|
|
|
|
from .._core import Stub, AndroidOnlyError
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# MediaSession
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class MediaSession(Stub):
|
|
"""MediaSession — 媒体会话"""
|
|
|
|
FLAG_HANDLES_MEDIA_BUTTONS = 1
|
|
FLAG_HANDLES_TRANSPORT_CONTROLS = 2
|
|
FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 4
|
|
|
|
# ═══════════════════════════════════════════════════════════
|
|
# Callback
|
|
# ═══════════════════════════════════════════════════════════
|
|
|
|
class Callback(Stub):
|
|
"""MediaSession.Callback — 会话回调"""
|
|
|
|
def onPlay(self):
|
|
pass
|
|
|
|
def onPause(self):
|
|
pass
|
|
|
|
def onSkipToNext(self):
|
|
pass
|
|
|
|
def onSkipToPrevious(self):
|
|
pass
|
|
|
|
def onFastForward(self):
|
|
pass
|
|
|
|
def onRewind(self):
|
|
pass
|
|
|
|
def onStop(self):
|
|
pass
|
|
|
|
def onSeekTo(self, pos: int):
|
|
pass
|
|
|
|
def onSetRating(self, rating: "Rating"):
|
|
pass
|
|
|
|
def onSetRepeatMode(self, repeatMode: int):
|
|
pass
|
|
|
|
def onSetShuffleMode(self, shuffleMode: int):
|
|
pass
|
|
|
|
def onCommand(self, command: str, extras: Any, cb: Any):
|
|
pass
|
|
|
|
def onCustomAction(self, action: str, extras: Any):
|
|
pass
|
|
|
|
def onMediaButtonEvent(self, mediaButtonIntent: Any) -> bool:
|
|
return False
|
|
|
|
def onPlayFromMediaId(self, mediaId: 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, mediaId: str, extras: Any):
|
|
pass
|
|
|
|
def onPrepareFromSearch(self, query: str, extras: Any):
|
|
pass
|
|
|
|
def onPrepareFromUri(self, uri: str, extras: Any):
|
|
pass
|
|
|
|
def __init__(self, context: Any = None, tag: str = "",
|
|
token: "SessionToken" = None):
|
|
super().__init__()
|
|
self._tag = tag
|
|
self._token = token or SessionToken()
|
|
self._active = False
|
|
self._flags = 0
|
|
self._session_activity: Any = None
|
|
self._callback: Optional[MediaSession.Callback] = None
|
|
self._metadata: Optional["MediaMetadata"] = None
|
|
self._playback_state: Optional["PlaybackState"] = None
|
|
self._queue: List["QueueItem"] = []
|
|
self._queue_title: Optional[str] = None
|
|
self._extras: Any = None
|
|
self._rating_type = Rating.RATING_NONE
|
|
self._repeat_mode = PlaybackState.REPEAT_MODE_NONE
|
|
self._shuffle_mode = PlaybackState.SHUFFLE_MODE_NONE
|
|
self._captioning_enabled = False
|
|
self._controllers: List[MediaController] = []
|
|
|
|
def setSessionActivity(self, sessionActivity: Any):
|
|
self._session_activity = sessionActivity
|
|
|
|
def sendSessionEvent(self, event: str, extras: Any):
|
|
for ctrl in self._controllers:
|
|
ctrl._callback.onSessionEvent(event, extras)
|
|
|
|
def release(self):
|
|
self._active = False
|
|
self._controllers.clear()
|
|
|
|
def getSessionToken(self) -> "SessionToken":
|
|
return self._token
|
|
|
|
def setFlags(self, flags: int):
|
|
self._flags = flags
|
|
|
|
def setPlaybackToLocal(self, volumeProvider: Any = None):
|
|
pass
|
|
|
|
def setPlaybackToRemote(self, volumeProvider: Any):
|
|
pass
|
|
|
|
def setActive(self, active: bool):
|
|
self._active = active
|
|
|
|
def isActive(self) -> bool:
|
|
return self._active
|
|
|
|
def setMediaMetadata(self, metadata: "MediaMetadata"):
|
|
self._metadata = metadata
|
|
for ctrl in self._controllers:
|
|
ctrl._callback.onMetadataChanged(metadata)
|
|
|
|
def setPlaybackState(self, state: "PlaybackState"):
|
|
self._playback_state = state
|
|
for ctrl in self._controllers:
|
|
ctrl._callback.onPlaybackStateChanged(state)
|
|
|
|
def setQueue(self, queue: List["QueueItem"]):
|
|
self._queue = queue
|
|
for ctrl in self._controllers:
|
|
ctrl._callback.onQueueChanged(queue)
|
|
|
|
def setQueueTitle(self, title: str):
|
|
self._queue_title = title
|
|
for ctrl in self._controllers:
|
|
ctrl._callback.onQueueTitleChanged(title)
|
|
|
|
def setExtras(self, extras: Any):
|
|
self._extras = extras
|
|
for ctrl in self._controllers:
|
|
ctrl._callback.onExtrasChanged(extras)
|
|
|
|
def setMetadata(self, metadata: "MediaMetadata"):
|
|
self.setMediaMetadata(metadata)
|
|
|
|
def setRatingType(self, ratingType: int):
|
|
self._rating_type = ratingType
|
|
|
|
def setRepeatMode(self, repeatMode: int):
|
|
self._repeat_mode = repeatMode
|
|
for ctrl in self._controllers:
|
|
ctrl._callback.onRepeatModeChanged(repeatMode)
|
|
|
|
def setShuffleMode(self, shuffleMode: int):
|
|
self._shuffle_mode = shuffleMode
|
|
for ctrl in self._controllers:
|
|
ctrl._callback.onShuffleModeChanged(shuffleMode)
|
|
|
|
def setCaptioningEnabled(self, enabled: bool):
|
|
self._captioning_enabled = enabled
|
|
for ctrl in self._controllers:
|
|
ctrl._callback.onCaptioningEnabledChanged(enabled)
|
|
|
|
def getController(self) -> "MediaController":
|
|
ctrl = MediaController(self)
|
|
self._controllers.append(ctrl)
|
|
return ctrl
|
|
|
|
def getCallerPackageName(self) -> str:
|
|
return "com.android.systemui"
|
|
|
|
def setCallback(self, callback: Callback, handler: Any = None):
|
|
self._callback = callback
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# MediaController
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class MediaController(Stub):
|
|
"""MediaController — 媒体控制器"""
|
|
|
|
# ═══════════════════════════════════════════════════════════
|
|
# Callback
|
|
# ═══════════════════════════════════════════════════════════
|
|
|
|
class Callback(Stub):
|
|
"""MediaController.Callback — 控制器回调"""
|
|
|
|
def onSessionReady(self):
|
|
pass
|
|
|
|
def onSessionDestroyed(self):
|
|
pass
|
|
|
|
def onSessionEvent(self, event: str, extras: Any):
|
|
pass
|
|
|
|
def onPlaybackStateChanged(self, state: "PlaybackState"):
|
|
pass
|
|
|
|
def onMetadataChanged(self, metadata: "MediaMetadata"):
|
|
pass
|
|
|
|
def onQueueChanged(self, queue: List["QueueItem"]):
|
|
pass
|
|
|
|
def onQueueTitleChanged(self, title: str):
|
|
pass
|
|
|
|
def onExtrasChanged(self, extras: Any):
|
|
pass
|
|
|
|
def onAudioInfoChanged(self, info: Any):
|
|
pass
|
|
|
|
def onCaptioningEnabledChanged(self, enabled: bool):
|
|
pass
|
|
|
|
def onRepeatModeChanged(self, repeatMode: int):
|
|
pass
|
|
|
|
def onShuffleModeChanged(self, shuffleMode: int):
|
|
pass
|
|
|
|
# ═══════════════════════════════════════════════════════════
|
|
# TransportControls
|
|
# ═══════════════════════════════════════════════════════════
|
|
|
|
class TransportControls(Stub):
|
|
"""MediaController.TransportControls — 传输控制"""
|
|
|
|
def __init__(self, session: "MediaSession"):
|
|
super().__init__()
|
|
self._session = session
|
|
|
|
def play(self):
|
|
if self._session._callback:
|
|
self._session._callback.onPlay()
|
|
|
|
def pause(self):
|
|
if self._session._callback:
|
|
self._session._callback.onPause()
|
|
|
|
def stop(self):
|
|
if self._session._callback:
|
|
self._session._callback.onStop()
|
|
|
|
def fastForward(self):
|
|
if self._session._callback:
|
|
self._session._callback.onFastForward()
|
|
|
|
def rewind(self):
|
|
if self._session._callback:
|
|
self._session._callback.onRewind()
|
|
|
|
def skipToNext(self):
|
|
if self._session._callback:
|
|
self._session._callback.onSkipToNext()
|
|
|
|
def skipToPrevious(self):
|
|
if self._session._callback:
|
|
self._session._callback.onSkipToPrevious()
|
|
|
|
def seekTo(self, pos: int):
|
|
if self._session._callback:
|
|
self._session._callback.onSeekTo(pos)
|
|
|
|
def setRating(self, rating: "Rating"):
|
|
if self._session._callback:
|
|
self._session._callback.onSetRating(rating)
|
|
|
|
def setRepeatMode(self, repeatMode: int):
|
|
if self._session._callback:
|
|
self._session._callback.onSetRepeatMode(repeatMode)
|
|
|
|
def setShuffleMode(self, shuffleMode: int):
|
|
if self._session._callback:
|
|
self._session._callback.onSetShuffleMode(shuffleMode)
|
|
|
|
def sendCustomAction(self, action: str, extras: Any):
|
|
if self._session._callback:
|
|
self._session._callback.onCustomAction(action, extras)
|
|
|
|
def playFromMediaId(self, mediaId: str, extras: Any):
|
|
if self._session._callback:
|
|
self._session._callback.onPlayFromMediaId(mediaId, extras)
|
|
|
|
def playFromSearch(self, query: str, extras: Any):
|
|
if self._session._callback:
|
|
self._session._callback.onPlayFromSearch(query, extras)
|
|
|
|
def playFromUri(self, uri: str, extras: Any):
|
|
if self._session._callback:
|
|
self._session._callback.onPlayFromUri(uri, extras)
|
|
|
|
def prepare(self):
|
|
if self._session._callback:
|
|
self._session._callback.onPrepare()
|
|
|
|
def prepareFromMediaId(self, mediaId: str, extras: Any):
|
|
if self._session._callback:
|
|
self._session._callback.onPrepareFromMediaId(mediaId, extras)
|
|
|
|
def prepareFromSearch(self, query: str, extras: Any):
|
|
if self._session._callback:
|
|
self._session._callback.onPrepareFromSearch(query, extras)
|
|
|
|
def prepareFromUri(self, uri: str, extras: Any):
|
|
if self._session._callback:
|
|
self._session._callback.onPrepareFromUri(uri, extras)
|
|
|
|
def __init__(self, session: "MediaSession" = None):
|
|
super().__init__()
|
|
self._session = session
|
|
self._callback = MediaController.Callback()
|
|
self._transport_controls = MediaController.TransportControls(session)
|
|
|
|
def getSessionToken(self) -> "SessionToken":
|
|
return self._session.getSessionToken() if self._session else SessionToken()
|
|
|
|
def getTransportControls(self) -> TransportControls:
|
|
return self._transport_controls
|
|
|
|
def getPlaybackState(self) -> Optional["PlaybackState"]:
|
|
return self._session._playback_state if self._session else None
|
|
|
|
def getMetadata(self) -> Optional["MediaMetadata"]:
|
|
return self._session._metadata if self._session else None
|
|
|
|
def getQueueTitle(self) -> Optional[str]:
|
|
return self._session._queue_title if self._session else None
|
|
|
|
def getQueue(self) -> List["QueueItem"]:
|
|
return self._session._queue if self._session else []
|
|
|
|
def getRepeatMode(self) -> int:
|
|
return self._session._repeat_mode if self._session else PlaybackState.REPEAT_MODE_NONE
|
|
|
|
def getShuffleMode(self) -> int:
|
|
return self._session._shuffle_mode if self._session else PlaybackState.SHUFFLE_MODE_NONE
|
|
|
|
def getRatingType(self) -> int:
|
|
return self._session._rating_type if self._session else Rating.RATING_NONE
|
|
|
|
def getExtras(self) -> Any:
|
|
return self._session._extras if self._session else None
|
|
|
|
def getFlags(self) -> int:
|
|
return self._session._flags if self._session else 0
|
|
|
|
def getPackageName(self) -> str:
|
|
return self._session.getCallerPackageName() if self._session else ""
|
|
|
|
def getSessionActivity(self) -> Any:
|
|
return self._session._session_activity if self._session else None
|
|
|
|
def registerCallback(self, callback: Callback, handler: Any = None):
|
|
self._callback = callback
|
|
|
|
def unregisterCallback(self, callback: Callback):
|
|
self._callback = MediaController.Callback()
|
|
|
|
def sendCommand(self, command: str, extras: Any, cb: Any):
|
|
if self._session and self._session._callback:
|
|
self._session._callback.onCommand(command, extras, cb)
|
|
|
|
def adjustVolume(self, direction: int, flags: int):
|
|
pass
|
|
|
|
def setVolumeTo(self, value: int, flags: int):
|
|
pass
|
|
|
|
def isSessionReady(self) -> bool:
|
|
return True
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# PlaybackState
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class PlaybackState(Stub):
|
|
"""PlaybackState — 播放状态"""
|
|
|
|
STATE_NONE = 0
|
|
STATE_STOPPED = 1
|
|
STATE_PAUSED = 2
|
|
STATE_PLAYING = 3
|
|
STATE_FAST_FORWARDING = 4
|
|
STATE_REWINDING = 5
|
|
STATE_BUFFERING = 6
|
|
STATE_ERROR = 7
|
|
STATE_CONNECTING = 8
|
|
STATE_SKIPPING_TO_PREVIOUS = 9
|
|
STATE_SKIPPING_TO_NEXT = 10
|
|
STATE_SKIPPING_TO_QUEUE_ITEM = 11
|
|
|
|
ACTION_PLAY = 1 << 0
|
|
ACTION_PAUSE = 1 << 1
|
|
ACTION_PLAY_PAUSE = 1 << 2
|
|
ACTION_STOP = 1 << 3
|
|
ACTION_SEEK_TO = 1 << 4
|
|
ACTION_FAST_FORWARD = 1 << 5
|
|
ACTION_REWIND = 1 << 6
|
|
ACTION_SKIP_TO_NEXT = 1 << 7
|
|
ACTION_SKIP_TO_PREVIOUS = 1 << 8
|
|
ACTION_SET_RATING = 1 << 9
|
|
ACTION_SET_REPEAT_MODE = 1 << 10
|
|
ACTION_SET_SHUFFLE_MODE = 1 << 11
|
|
ACTION_SET_CAPTIONING_ENABLED = 1 << 12
|
|
ACTION_SKIP_TO_QUEUE_ITEM = 1 << 13
|
|
ACTION_PLAY_FROM_MEDIA_ID = 1 << 14
|
|
ACTION_PLAY_FROM_SEARCH = 1 << 15
|
|
ACTION_PREPARE = 1 << 16
|
|
ACTION_PREPARE_FROM_MEDIA_ID = 1 << 17
|
|
ACTION_PREPARE_FROM_SEARCH = 1 << 18
|
|
ACTION_PREPARE_FROM_URI = 1 << 19
|
|
ACTION_PLAY_FROM_URI = 1 << 20
|
|
|
|
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
|
|
|
|
class Builder(Stub):
|
|
"""PlaybackState.Builder — 播放状态构建器"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._state = PlaybackState.STATE_NONE
|
|
self._position = 0
|
|
self._speed = 1.0
|
|
self._actions = 0
|
|
self._custom_actions: List[Any] = []
|
|
self._active_queue_item_id = -1
|
|
self._error_message: Optional[str] = None
|
|
self._extras: Any = None
|
|
self._buffered_position = 0
|
|
|
|
def setState(self, state: int, position: int, speed: float) -> "PlaybackState.Builder":
|
|
self._state = state
|
|
self._position = position
|
|
self._speed = speed
|
|
return self
|
|
|
|
def setActions(self, actions: int) -> "PlaybackState.Builder":
|
|
self._actions = actions
|
|
return self
|
|
|
|
def setCustomAction(self, action: str, name: str, icon: Any = None) -> "PlaybackState.Builder":
|
|
self._custom_actions.append((action, name, icon))
|
|
return self
|
|
|
|
def setActiveQueueItemId(self, id: int) -> "PlaybackState.Builder":
|
|
self._active_queue_item_id = id
|
|
return self
|
|
|
|
def setErrorMessage(self, error: str) -> "PlaybackState.Builder":
|
|
self._error_message = error
|
|
return self
|
|
|
|
def setExtras(self, extras: Any) -> "PlaybackState.Builder":
|
|
self._extras = extras
|
|
return self
|
|
|
|
def setBufferedPosition(self, bufferedPosition: int) -> "PlaybackState.Builder":
|
|
self._buffered_position = bufferedPosition
|
|
return self
|
|
|
|
def setPosition(self, position: int) -> "PlaybackState.Builder":
|
|
self._position = position
|
|
return self
|
|
|
|
def setSpeed(self, speed: float) -> "PlaybackState.Builder":
|
|
self._speed = speed
|
|
return self
|
|
|
|
def addCustomAction(self, action: "PlaybackState.CustomAction") -> "PlaybackState.Builder":
|
|
self._custom_actions.append(action)
|
|
return self
|
|
|
|
def build(self) -> "PlaybackState":
|
|
ps = PlaybackState()
|
|
ps._state = self._state
|
|
ps._position = self._position
|
|
ps._speed = self._speed
|
|
ps._actions = self._actions
|
|
ps._custom_actions = self._custom_actions
|
|
ps._active_queue_item_id = self._active_queue_item_id
|
|
ps._error_message = self._error_message
|
|
ps._extras = self._extras
|
|
ps._buffered_position = self._buffered_position
|
|
return ps
|
|
|
|
class CustomAction(Stub):
|
|
"""PlaybackState.CustomAction — 自定义操作"""
|
|
|
|
def __init__(self, action: str, name: str, icon: Any = None):
|
|
super().__init__()
|
|
self._action = action
|
|
self._name = name
|
|
self._icon = icon
|
|
|
|
def getAction(self) -> str:
|
|
return self._action
|
|
|
|
def getName(self) -> str:
|
|
return self._name
|
|
|
|
def getIcon(self) -> Any:
|
|
return self._icon
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._state = PlaybackState.STATE_NONE
|
|
self._position = 0
|
|
self._speed = 1.0
|
|
self._actions = 0
|
|
self._custom_actions: List[Any] = []
|
|
self._active_queue_item_id = -1
|
|
self._error_message: Optional[str] = None
|
|
self._extras: Any = None
|
|
self._buffered_position = 0
|
|
self._last_position_update_time = int(time.time() * 1000)
|
|
|
|
def getState(self) -> int:
|
|
return self._state
|
|
|
|
def getPosition(self) -> int:
|
|
return self._position
|
|
|
|
def getPlaybackSpeed(self) -> float:
|
|
return self._speed
|
|
|
|
def getActions(self) -> int:
|
|
return self._actions
|
|
|
|
def getCustomActions(self) -> List[Any]:
|
|
return self._custom_actions
|
|
|
|
def getActiveQueueItemId(self) -> int:
|
|
return self._active_queue_item_id
|
|
|
|
def getErrorMessage(self) -> Optional[str]:
|
|
return self._error_message
|
|
|
|
def getExtras(self) -> Any:
|
|
return self._extras
|
|
|
|
def getBufferedPosition(self) -> int:
|
|
return self._buffered_position
|
|
|
|
def getLastPositionUpdateTime(self) -> int:
|
|
return self._last_position_update_time
|
|
|
|
def toString(self) -> str:
|
|
state_names = {
|
|
PlaybackState.STATE_NONE: "NONE",
|
|
PlaybackState.STATE_STOPPED: "STOPPED",
|
|
PlaybackState.STATE_PAUSED: "PAUSED",
|
|
PlaybackState.STATE_PLAYING: "PLAYING",
|
|
}
|
|
return f"PlaybackState(state={state_names.get(self._state, str(self._state))}," \
|
|
f" pos={self._position})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# MediaMetadata
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class MediaMetadata(Stub):
|
|
"""MediaMetadata — 媒体元数据"""
|
|
|
|
METADATA_KEY_ALBUM = "android.media.metadata.ALBUM"
|
|
METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART"
|
|
METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST"
|
|
METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI"
|
|
METADATA_KEY_ART = "android.media.metadata.ART"
|
|
METADATA_KEY_ARTIST = "android.media.metadata.ARTIST"
|
|
METADATA_KEY_ART_URI = "android.media.metadata.ART_URI"
|
|
METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR"
|
|
METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION"
|
|
METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER"
|
|
METADATA_KEY_DATE = "android.media.metadata.DATE"
|
|
METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER"
|
|
METADATA_KEY_DISPLAY_DESCRIPTION = "android.media.metadata.DISPLAY_DESCRIPTION"
|
|
METADATA_KEY_DISPLAY_ICON = "android.media.metadata.DISPLAY_ICON"
|
|
METADATA_KEY_DISPLAY_ICON_URI = "android.media.metadata.DISPLAY_ICON_URI"
|
|
METADATA_KEY_DISPLAY_SUBTITLE = "android.media.metadata.DISPLAY_SUBTITLE"
|
|
METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE"
|
|
METADATA_KEY_DURATION = "android.media.metadata.DURATION"
|
|
METADATA_KEY_GENRE = "android.media.metadata.GENRE"
|
|
METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID"
|
|
METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS"
|
|
METADATA_KEY_RATING = "android.media.metadata.RATING"
|
|
METADATA_KEY_TITLE = "android.media.metadata.TITLE"
|
|
METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER"
|
|
METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING"
|
|
METADATA_KEY_WRITER = "android.media.metadata.WRITER"
|
|
METADATA_KEY_YEAR = "android.media.metadata.YEAR"
|
|
|
|
class Builder(Stub):
|
|
"""MediaMetadata.Builder — 元数据构建器"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._data: Dict[str, Any] = {}
|
|
|
|
def putString(self, key: str, value: str) -> "MediaMetadata.Builder":
|
|
self._data[key] = value
|
|
return self
|
|
|
|
def putLong(self, key: str, value: int) -> "MediaMetadata.Builder":
|
|
self._data[key] = value
|
|
return self
|
|
|
|
def putBitmap(self, key: str, value: Any) -> "MediaMetadata.Builder":
|
|
self._data[key] = value
|
|
return self
|
|
|
|
def putText(self, key: str, value: str) -> "MediaMetadata.Builder":
|
|
self._data[key] = value
|
|
return self
|
|
|
|
def putRating(self, key: str, value: "Rating") -> "MediaMetadata.Builder":
|
|
self._data[key] = value
|
|
return self
|
|
|
|
def build(self) -> "MediaMetadata":
|
|
mm = MediaMetadata()
|
|
mm._data = dict(self._data)
|
|
return mm
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._data: Dict[str, Any] = {}
|
|
|
|
def getString(self, key: str) -> Optional[str]:
|
|
v = self._data.get(key)
|
|
return str(v) if v is not None else None
|
|
|
|
def getLong(self, key: str) -> int:
|
|
v = self._data.get(key, 0)
|
|
try:
|
|
return int(v)
|
|
except (ValueError, TypeError):
|
|
return 0
|
|
|
|
def getBitmap(self, key: str) -> Any:
|
|
return self._data.get(key)
|
|
|
|
def getText(self, key: str) -> Optional[str]:
|
|
return self.getString(key)
|
|
|
|
def getDescription(self) -> "MediaDescription":
|
|
from .media_browse import MediaDescription
|
|
builder = MediaDescription.Builder()
|
|
title = self.getString(MediaMetadata.METADATA_KEY_TITLE)
|
|
if title:
|
|
builder.setTitle(title)
|
|
artist = self.getString(MediaMetadata.METADATA_KEY_ARTIST)
|
|
if artist:
|
|
builder.setSubtitle(artist)
|
|
media_id = self.getString(MediaMetadata.METADATA_KEY_MEDIA_ID)
|
|
if media_id:
|
|
builder.setMediaId(media_id)
|
|
return builder.build()
|
|
|
|
def getRating(self, key: str) -> Optional["Rating"]:
|
|
v = self._data.get(key)
|
|
return v if isinstance(v, Rating) else None
|
|
|
|
def getBitmapDescription(self) -> Any:
|
|
return None
|
|
|
|
def containsKey(self, key: str) -> bool:
|
|
return key in self._data
|
|
|
|
def keySet(self) -> Set[str]:
|
|
return set(self._data.keys())
|
|
|
|
def getMediaId(self) -> Optional[str]:
|
|
return self.getString(MediaMetadata.METADATA_KEY_MEDIA_ID)
|
|
|
|
def toString(self) -> str:
|
|
title = self.getString(MediaMetadata.METADATA_KEY_TITLE)
|
|
return f"MediaMetadata(title={title})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# QueueItem
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class QueueItem(Stub):
|
|
"""QueueItem — 队列项"""
|
|
|
|
def __init__(self, description: "MediaDescription" = None, queueId: int = 0):
|
|
super().__init__()
|
|
self._description = description
|
|
self._queue_id = queueId
|
|
|
|
def getDescription(self) -> "MediaDescription":
|
|
return self._description
|
|
|
|
def getQueueId(self) -> int:
|
|
return self._queue_id
|
|
|
|
def toString(self) -> str:
|
|
return f"QueueItem(id={self._queue_id}, desc={self._description})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# Rating
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class Rating(Stub):
|
|
"""Rating — 评分"""
|
|
|
|
RATING_NONE = 0
|
|
RATING_HEART = 1
|
|
RATING_THUMB_UP_DOWN = 2
|
|
RATING_3_STARS = 3
|
|
RATING_4_STARS = 4
|
|
RATING_5_STARS = 5
|
|
RATING_PERCENTAGE = 6
|
|
|
|
def __init__(self, ratingStyle: int = RATING_NONE):
|
|
super().__init__()
|
|
self._rating_style = ratingStyle
|
|
self._rated = False
|
|
self._heart = False
|
|
self._thumb_up = False
|
|
self._star_rating = 0.0
|
|
self._percent_rating = 0.0
|
|
|
|
@staticmethod
|
|
def newHeartRating(hasHeart: bool) -> "Rating":
|
|
r = Rating(Rating.RATING_HEART)
|
|
r._rated = True
|
|
r._heart = hasHeart
|
|
return r
|
|
|
|
@staticmethod
|
|
def newThumbRating(thumbUp: bool) -> "Rating":
|
|
r = Rating(Rating.RATING_THUMB_UP_DOWN)
|
|
r._rated = True
|
|
r._thumb_up = thumbUp
|
|
return r
|
|
|
|
@staticmethod
|
|
def newStarRating(starRatingStyle: int, starRating: float) -> "Rating":
|
|
r = Rating(starRatingStyle)
|
|
r._rated = True
|
|
r._star_rating = starRating
|
|
return r
|
|
|
|
@staticmethod
|
|
def newPercentageRating(percent: float) -> "Rating":
|
|
r = Rating(Rating.RATING_PERCENTAGE)
|
|
r._rated = True
|
|
r._percent_rating = percent
|
|
return r
|
|
|
|
@staticmethod
|
|
def newUnratedRating(ratingStyle: int) -> "Rating":
|
|
return Rating(ratingStyle)
|
|
|
|
def isRated(self) -> bool:
|
|
return self._rated
|
|
|
|
def getRatingStyle(self) -> int:
|
|
return self._rating_style
|
|
|
|
def hasHeart(self) -> bool:
|
|
return self._heart
|
|
|
|
def isThumbUp(self) -> bool:
|
|
return self._thumb_up
|
|
|
|
def getStarRating(self) -> float:
|
|
return self._star_rating
|
|
|
|
def getPercentRating(self) -> float:
|
|
return self._percent_rating
|
|
|
|
def toString(self) -> str:
|
|
return f"Rating(style={self._rating_style}, rated={self._rated})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# SessionToken
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class SessionToken(Stub):
|
|
"""SessionToken — 会话令牌"""
|
|
|
|
TYPE_SESSION = 0
|
|
|
|
def __init__(self, type_: int = TYPE_SESSION):
|
|
super().__init__()
|
|
self._type = type_
|
|
self._binder = None
|
|
|
|
def getType(self) -> int:
|
|
return self._type
|
|
|
|
def getSessionBinder(self) -> Any:
|
|
return self._binder
|
|
|
|
def toString(self) -> str:
|
|
return f"SessionToken(type={self._type})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# __all__
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
__all__ = [
|
|
"MediaSession",
|
|
"MediaController",
|
|
"PlaybackState",
|
|
"MediaMetadata",
|
|
"QueueItem",
|
|
"Rating",
|
|
"SessionToken",
|
|
] |