From 99c1f9f453b3b589b3f8f2c32ea4f947e309c089 Mon Sep 17 00:00:00 2001 From: Sungsoo Date: Wed, 30 Nov 2016 16:29:34 +0900 Subject: [PATCH] MediaSession: Add methods for queue managements Bug: 30203831 Test: Passed MediaControllerTest Change-Id: Ibc33572d21bd83d47660be8f41173b9cea206015 --- api/current.txt | 9 + api/system-current.txt | 9 + api/test-current.txt | 9 + .../media/session/ISessionCallback.aidl | 5 + .../media/session/ISessionController.aidl | 6 + .../media/session/MediaController.java | 176 +++++++++++++----- .../android/media/session/MediaSession.java | 130 ++++++++++++- .../server/media/MediaSessionRecord.java | 56 ++++++ 8 files changed, 355 insertions(+), 45 deletions(-) diff --git a/api/current.txt b/api/current.txt index adc32ee95247b..3ea56f882122f 100644 --- a/api/current.txt +++ b/api/current.txt @@ -23590,6 +23590,8 @@ package android.media.session { public final class MediaController { ctor public MediaController(android.content.Context, android.media.session.MediaSession.Token); + method public void addQueueItem(android.media.MediaDescription); + method public void addQueueItem(android.media.MediaDescription, int); method public void adjustVolume(int, int); method public boolean dispatchMediaButtonEvent(android.view.KeyEvent); method public android.os.Bundle getExtras(); @@ -23608,6 +23610,8 @@ package android.media.session { method public boolean isShuffleModeEnabled(); method public void registerCallback(android.media.session.MediaController.Callback); method public void registerCallback(android.media.session.MediaController.Callback, android.os.Handler); + method public void removeQueueItem(android.media.MediaDescription); + method public void removeQueueItemAt(int); method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver); method public void setVolumeTo(int, int); method public void unregisterCallback(android.media.session.MediaController.Callback); @@ -23685,11 +23689,14 @@ package android.media.session { method public void setSessionActivity(android.app.PendingIntent); method public void setShuffleModeEnabled(boolean); field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1 + field public static final int FLAG_HANDLES_QUEUE_COMMANDS = 4; // 0x4 field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2 } public static abstract class MediaSession.Callback { ctor public MediaSession.Callback(); + method public void onAddQueueItem(android.media.MediaDescription); + method public void onAddQueueItem(android.media.MediaDescription, int); method public void onCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver); method public void onCustomAction(java.lang.String, android.os.Bundle); method public void onFastForward(); @@ -23703,6 +23710,8 @@ package android.media.session { method public void onPrepareFromMediaId(java.lang.String, android.os.Bundle); method public void onPrepareFromSearch(java.lang.String, android.os.Bundle); method public void onPrepareFromUri(android.net.Uri, android.os.Bundle); + method public void onRemoveQueueItem(android.media.MediaDescription); + method public void onRemoveQueueItemAt(int); method public void onRewind(); method public void onSeekTo(long); method public void onSetRating(android.media.Rating); diff --git a/api/system-current.txt b/api/system-current.txt index 4a16b839b934a..860993ac75c56 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -25284,6 +25284,8 @@ package android.media.session { public final class MediaController { ctor public MediaController(android.content.Context, android.media.session.MediaSession.Token); + method public void addQueueItem(android.media.MediaDescription); + method public void addQueueItem(android.media.MediaDescription, int); method public void adjustVolume(int, int); method public boolean dispatchMediaButtonEvent(android.view.KeyEvent); method public android.os.Bundle getExtras(); @@ -25302,6 +25304,8 @@ package android.media.session { method public boolean isShuffleModeEnabled(); method public void registerCallback(android.media.session.MediaController.Callback); method public void registerCallback(android.media.session.MediaController.Callback, android.os.Handler); + method public void removeQueueItem(android.media.MediaDescription); + method public void removeQueueItemAt(int); method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver); method public void setVolumeTo(int, int); method public void unregisterCallback(android.media.session.MediaController.Callback); @@ -25379,11 +25383,14 @@ package android.media.session { method public void setSessionActivity(android.app.PendingIntent); method public void setShuffleModeEnabled(boolean); field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1 + field public static final int FLAG_HANDLES_QUEUE_COMMANDS = 4; // 0x4 field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2 } public static abstract class MediaSession.Callback { ctor public MediaSession.Callback(); + method public void onAddQueueItem(android.media.MediaDescription); + method public void onAddQueueItem(android.media.MediaDescription, int); method public void onCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver); method public void onCustomAction(java.lang.String, android.os.Bundle); method public void onFastForward(); @@ -25397,6 +25404,8 @@ package android.media.session { method public void onPrepareFromMediaId(java.lang.String, android.os.Bundle); method public void onPrepareFromSearch(java.lang.String, android.os.Bundle); method public void onPrepareFromUri(android.net.Uri, android.os.Bundle); + method public void onRemoveQueueItem(android.media.MediaDescription); + method public void onRemoveQueueItemAt(int); method public void onRewind(); method public void onSeekTo(long); method public void onSetRating(android.media.Rating); diff --git a/api/test-current.txt b/api/test-current.txt index d36b8de463dc6..a02a4339e919a 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -23682,6 +23682,8 @@ package android.media.session { public final class MediaController { ctor public MediaController(android.content.Context, android.media.session.MediaSession.Token); + method public void addQueueItem(android.media.MediaDescription); + method public void addQueueItem(android.media.MediaDescription, int); method public void adjustVolume(int, int); method public boolean dispatchMediaButtonEvent(android.view.KeyEvent); method public android.os.Bundle getExtras(); @@ -23700,6 +23702,8 @@ package android.media.session { method public boolean isShuffleModeEnabled(); method public void registerCallback(android.media.session.MediaController.Callback); method public void registerCallback(android.media.session.MediaController.Callback, android.os.Handler); + method public void removeQueueItem(android.media.MediaDescription); + method public void removeQueueItemAt(int); method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver); method public void setVolumeTo(int, int); method public void unregisterCallback(android.media.session.MediaController.Callback); @@ -23777,11 +23781,14 @@ package android.media.session { method public void setSessionActivity(android.app.PendingIntent); method public void setShuffleModeEnabled(boolean); field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1 + field public static final int FLAG_HANDLES_QUEUE_COMMANDS = 4; // 0x4 field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2 } public static abstract class MediaSession.Callback { ctor public MediaSession.Callback(); + method public void onAddQueueItem(android.media.MediaDescription); + method public void onAddQueueItem(android.media.MediaDescription, int); method public void onCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver); method public void onCustomAction(java.lang.String, android.os.Bundle); method public void onFastForward(); @@ -23795,6 +23802,8 @@ package android.media.session { method public void onPrepareFromMediaId(java.lang.String, android.os.Bundle); method public void onPrepareFromSearch(java.lang.String, android.os.Bundle); method public void onPrepareFromUri(android.net.Uri, android.os.Bundle); + method public void onRemoveQueueItem(android.media.MediaDescription); + method public void onRemoveQueueItemAt(int); method public void onRewind(); method public void onSeekTo(long); method public void onSetRating(android.media.Rating); diff --git a/media/java/android/media/session/ISessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl index 2f6e260741c45..a146c6226171b 100644 --- a/media/java/android/media/session/ISessionCallback.aidl +++ b/media/java/android/media/session/ISessionCallback.aidl @@ -16,6 +16,7 @@ package android.media.session; import android.content.Intent; +import android.media.MediaDescription; import android.media.Rating; import android.net.Uri; import android.os.Bundle; @@ -49,6 +50,10 @@ oneway interface ISessionCallback { void onRepeatMode(int repeatMode); void onShuffleMode(boolean enabled); void onCustomAction(String action, in Bundle args); + void onAddQueueItem(in MediaDescription description); + void onAddQueueItemAt(in MediaDescription description, int index); + void onRemoveQueueItem(in MediaDescription description); + void onRemoveQueueItemAt(int index); // These callbacks are for volume handling void onAdjustVolume(int direction); diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl index e92758c950af7..7b5233ac07045 100644 --- a/media/java/android/media/session/ISessionController.aidl +++ b/media/java/android/media/session/ISessionController.aidl @@ -18,6 +18,7 @@ package android.media.session; import android.app.PendingIntent; import android.content.Intent; import android.content.pm.ParceledListSlice; +import android.media.MediaDescription; import android.media.MediaMetadata; import android.media.Rating; import android.media.session.ISessionControllerCallback; @@ -51,6 +52,11 @@ interface ISessionController { MediaMetadata getMetadata(); PlaybackState getPlaybackState(); ParceledListSlice getQueue(); + void addQueueItem(in MediaDescription description); + void addQueueItemAt(in MediaDescription description, int index); + void removeQueueItem(in MediaDescription description); + void removeQueueItemAt(int index); + CharSequence getQueueTitle(); Bundle getExtras(); int getRatingType(); diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java index 8cbf8e1eccdfb..bab2af25dde5e 100644 --- a/media/java/android/media/session/MediaController.java +++ b/media/java/android/media/session/MediaController.java @@ -23,6 +23,7 @@ import android.content.Context; import android.content.pm.ParceledListSlice; import android.media.AudioAttributes; import android.media.AudioManager; +import android.media.MediaDescription; import android.media.MediaMetadata; import android.media.Rating; import android.media.VolumeProvider; @@ -38,6 +39,7 @@ import android.util.Log; import android.view.KeyEvent; import java.lang.ref.WeakReference; +import java.lang.UnsupportedOperationException; import java.util.ArrayList; import java.util.List; @@ -111,8 +113,7 @@ public final class MediaController { } /** - * Get a {@link TransportControls} instance to send transport actions to - * the associated session. + * Get a {@link TransportControls} instance to send transport actions to this session. * * @return A transport controls instance. */ @@ -151,7 +152,7 @@ public final class MediaController { try { return mSessionBinder.getPlaybackState(); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getPlaybackState.", e); + Log.wtf(TAG, "Error calling getPlaybackState", e); return null; } } @@ -165,7 +166,7 @@ public final class MediaController { try { return mSessionBinder.getMetadata(); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getMetadata.", e); + Log.wtf(TAG, "Error calling getMetadata", e); return null; } } @@ -183,11 +184,102 @@ public final class MediaController { return queue.getList(); } } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getQueue.", e); + Log.wtf(TAG, "Error calling getQueue", e); } return null; } + /** + * Add a queue item from the given {@code description} at the end of the play queue + * of this session. Not all sessions may support this. + * + * @param description The {@link MediaDescription} for creating the + * {@link MediaSession.QueueItem} to be inserted. + * @throws UnsupportedOperationException If this session doesn't support this. + * @see MediaSession#FLAG_HANDLES_QUEUE_COMMANDS + */ + public void addQueueItem(MediaDescription description) { + try { + long flags = mSessionBinder.getFlags(); + if ((flags & MediaSession.FLAG_HANDLES_QUEUE_COMMANDS) == 0) { + throw new UnsupportedOperationException( + "This session doesn't support queue management operations"); + } + mSessionBinder.addQueueItem(description); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling addQueueItem", e); + } + } + + /** + * Add a queue item from the given {@code description} at the specified position + * in the play queue of this session. Shifts the queue item currently at that position + * (if any) and any subsequent queue items to the right (adds one to their indices). + * Not all sessions may support this. + * + * @param description The {@link MediaDescription} for creating the + * {@link MediaSession.QueueItem} to be inserted. + * @param index The index at which the created {@link MediaSession.QueueItem} is to be inserted. + * @throws UnsupportedOperationException If this session doesn't support this. + * @see MediaSession#FLAG_HANDLES_QUEUE_COMMANDS + */ + public void addQueueItem(MediaDescription description, int index) { + try { + long flags = mSessionBinder.getFlags(); + if ((flags & MediaSession.FLAG_HANDLES_QUEUE_COMMANDS) == 0) { + throw new UnsupportedOperationException( + "This session doesn't support queue management operations"); + } + mSessionBinder.addQueueItemAt(description, index); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling addQueueItemAt", e); + } + } + + /** + * Remove the first occurrence of the specified {@link MediaSession.QueueItem} + * with the given {@link MediaDescription description} in the play queue of the associated + * session. Not all sessions may support this. + * + * @param description The {@link MediaDescription} for denoting the + * {@link MediaSession.QueueItem} to be removed. + * @throws UnsupportedOperationException If this session doesn't support this. + * @see MediaSession#FLAG_HANDLES_QUEUE_COMMANDS + */ + public void removeQueueItem(MediaDescription description) { + try { + long flags = mSessionBinder.getFlags(); + if ((flags & MediaSession.FLAG_HANDLES_QUEUE_COMMANDS) == 0) { + throw new UnsupportedOperationException( + "This session doesn't support queue management operations"); + } + mSessionBinder.removeQueueItem(description); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling removeQueueItem", e); + } + } + + /** + * Remove an queue item at the specified position in the play queue + * of this session. Not all sessions may support this. + * + * @param index The index of the element to be removed. + * @throws UnsupportedOperationException If this session doesn't support this. + * @see MediaSession#FLAG_HANDLES_QUEUE_COMMANDS + */ + public void removeQueueItemAt(int index) { + try { + long flags = mSessionBinder.getFlags(); + if ((flags & MediaSession.FLAG_HANDLES_QUEUE_COMMANDS) == 0) { + throw new UnsupportedOperationException( + "This session doesn't support queue management operations"); + } + mSessionBinder.removeQueueItemAt(index); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling removeQueueItemAt", e); + } + } + /** * Get the queue title for this session. */ @@ -230,7 +322,7 @@ public final class MediaController { try { return mSessionBinder.getRatingType(); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getRatingType.", e); + Log.wtf(TAG, "Error calling getRatingType", e); return Rating.RATING_NONE; } } @@ -245,7 +337,7 @@ public final class MediaController { try { return mSessionBinder.getRepeatMode(); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getRepeatMode.", e); + Log.wtf(TAG, "Error calling getRepeatMode", e); return PlaybackState.REPEAT_MODE_NONE; } } @@ -259,7 +351,7 @@ public final class MediaController { try { return mSessionBinder.isShuffleModeEnabled(); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling isShuffleModeEnabled.", e); + Log.wtf(TAG, "Error calling isShuffleModeEnabled", e); return false; } } @@ -273,7 +365,7 @@ public final class MediaController { try { return mSessionBinder.getFlags(); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getFlags.", e); + Log.wtf(TAG, "Error calling getFlags", e); } return 0; } @@ -290,7 +382,7 @@ public final class MediaController { result.maxVolume, result.currentVolume); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getAudioInfo.", e); + Log.wtf(TAG, "Error calling getAudioInfo", e); } return null; } @@ -305,7 +397,7 @@ public final class MediaController { try { return mSessionBinder.getLaunchPendingIntent(); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getPendingIntent.", e); + Log.wtf(TAG, "Error calling getPendingIntent", e); } return null; } @@ -334,7 +426,7 @@ public final class MediaController { try { mSessionBinder.setVolumeTo(value, flags, mContext.getPackageName()); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling setVolumeTo.", e); + Log.wtf(TAG, "Error calling setVolumeTo", e); } } @@ -355,7 +447,7 @@ public final class MediaController { try { mSessionBinder.adjustVolume(direction, flags, mContext.getPackageName()); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling adjustVolumeBy.", e); + Log.wtf(TAG, "Error calling adjustVolumeBy", e); } } @@ -421,7 +513,7 @@ public final class MediaController { try { mSessionBinder.sendCommand(command, args, cb); } catch (RemoteException e) { - Log.d(TAG, "Dead object in sendCommand.", e); + Log.d(TAG, "Dead object in sendCommand", e); } } @@ -435,7 +527,7 @@ public final class MediaController { try { mPackageName = mSessionBinder.getPackageName(); } catch (RemoteException e) { - Log.d(TAG, "Dead object in getPackageName.", e); + Log.d(TAG, "Dead object in getPackageName", e); } } return mPackageName; @@ -452,7 +544,7 @@ public final class MediaController { try { mTag = mSessionBinder.getTag(); } catch (RemoteException e) { - Log.d(TAG, "Dead object in getTag.", e); + Log.d(TAG, "Dead object in getTag", e); } } return mTag; @@ -652,7 +744,7 @@ public final class MediaController { try { mSessionBinder.prepare(); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling prepare.", e); + Log.wtf(TAG, "Error calling prepare", e); } } @@ -671,12 +763,12 @@ public final class MediaController { public void prepareFromMediaId(String mediaId, Bundle extras) { if (TextUtils.isEmpty(mediaId)) { throw new IllegalArgumentException( - "You must specify a non-empty String for prepareFromMediaId."); + "You must specify a non-empty String for prepareFromMediaId"); } try { mSessionBinder.prepareFromMediaId(mediaId, extras); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling prepare(" + mediaId + ").", e); + Log.wtf(TAG, "Error calling prepare(" + mediaId + ")", e); } } @@ -702,7 +794,7 @@ public final class MediaController { try { mSessionBinder.prepareFromSearch(query, extras); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling prepare(" + query + ").", e); + Log.wtf(TAG, "Error calling prepare(" + query + ")", e); } } @@ -721,12 +813,12 @@ public final class MediaController { public void prepareFromUri(Uri uri, Bundle extras) { if (uri == null || Uri.EMPTY.equals(uri)) { throw new IllegalArgumentException( - "You must specify a non-empty Uri for prepareFromUri."); + "You must specify a non-empty Uri for prepareFromUri"); } try { mSessionBinder.prepareFromUri(uri, extras); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling prepare(" + uri + ").", e); + Log.wtf(TAG, "Error calling prepare(" + uri + ")", e); } } @@ -737,7 +829,7 @@ public final class MediaController { try { mSessionBinder.play(); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling play.", e); + Log.wtf(TAG, "Error calling play", e); } } @@ -751,12 +843,12 @@ public final class MediaController { public void playFromMediaId(String mediaId, Bundle extras) { if (TextUtils.isEmpty(mediaId)) { throw new IllegalArgumentException( - "You must specify a non-empty String for playFromMediaId."); + "You must specify a non-empty String for playFromMediaId"); } try { mSessionBinder.playFromMediaId(mediaId, extras); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling play(" + mediaId + ").", e); + Log.wtf(TAG, "Error calling play(" + mediaId + ")", e); } } @@ -778,7 +870,7 @@ public final class MediaController { try { mSessionBinder.playFromSearch(query, extras); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling play(" + query + ").", e); + Log.wtf(TAG, "Error calling play(" + query + ")", e); } } @@ -792,12 +884,12 @@ public final class MediaController { public void playFromUri(Uri uri, Bundle extras) { if (uri == null || Uri.EMPTY.equals(uri)) { throw new IllegalArgumentException( - "You must specify a non-empty Uri for playFromUri."); + "You must specify a non-empty Uri for playFromUri"); } try { mSessionBinder.playFromUri(uri, extras); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling play(" + uri + ").", e); + Log.wtf(TAG, "Error calling play(" + uri + ")", e); } } @@ -809,7 +901,7 @@ public final class MediaController { try { mSessionBinder.skipToQueueItem(id); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling skipToItem(" + id + ").", e); + Log.wtf(TAG, "Error calling skipToItem(" + id + ")", e); } } @@ -821,7 +913,7 @@ public final class MediaController { try { mSessionBinder.pause(); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling pause.", e); + Log.wtf(TAG, "Error calling pause", e); } } @@ -833,7 +925,7 @@ public final class MediaController { try { mSessionBinder.stop(); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling stop.", e); + Log.wtf(TAG, "Error calling stop", e); } } @@ -846,7 +938,7 @@ public final class MediaController { try { mSessionBinder.seekTo(pos); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling seekTo.", e); + Log.wtf(TAG, "Error calling seekTo", e); } } @@ -858,7 +950,7 @@ public final class MediaController { try { mSessionBinder.fastForward(); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling fastForward.", e); + Log.wtf(TAG, "Error calling fastForward", e); } } @@ -869,7 +961,7 @@ public final class MediaController { try { mSessionBinder.next(); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling next.", e); + Log.wtf(TAG, "Error calling next", e); } } @@ -881,7 +973,7 @@ public final class MediaController { try { mSessionBinder.rewind(); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling rewind.", e); + Log.wtf(TAG, "Error calling rewind", e); } } @@ -892,7 +984,7 @@ public final class MediaController { try { mSessionBinder.previous(); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling previous.", e); + Log.wtf(TAG, "Error calling previous", e); } } @@ -907,7 +999,7 @@ public final class MediaController { try { mSessionBinder.rate(rating); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling rate.", e); + Log.wtf(TAG, "Error calling rate", e); } } @@ -923,7 +1015,7 @@ public final class MediaController { try { mSessionBinder.repeatMode(repeatMode); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling setRepeatMode.", e); + Log.wtf(TAG, "Error calling setRepeatMode", e); } } @@ -936,7 +1028,7 @@ public final class MediaController { try { mSessionBinder.shuffleMode(enabled); } catch (RemoteException e) { - Log.wtf(TAG, "Error calling shuffleMode.", e); + Log.wtf(TAG, "Error calling shuffleMode", e); } } @@ -950,7 +1042,7 @@ public final class MediaController { public void sendCustomAction(@NonNull PlaybackState.CustomAction customAction, @Nullable Bundle args) { if (customAction == null) { - throw new IllegalArgumentException("CustomAction cannot be null."); + throw new IllegalArgumentException("CustomAction cannot be null"); } sendCustomAction(customAction.getAction(), args); } @@ -966,12 +1058,12 @@ public final class MediaController { */ public void sendCustomAction(@NonNull String action, @Nullable Bundle args) { if (TextUtils.isEmpty(action)) { - throw new IllegalArgumentException("CustomAction cannot be null."); + throw new IllegalArgumentException("CustomAction cannot be null"); } try { mSessionBinder.sendCustomAction(action, args); } catch (RemoteException e) { - Log.d(TAG, "Dead object in sendCustomAction.", e); + Log.d(TAG, "Dead object in sendCustomAction", e); } } } diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java index 84dc93ad4347a..bee3f52c9b900 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -86,6 +86,12 @@ public final class MediaSession { */ public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 1 << 1; + /** + * Set this flag on the session to indicate that it handles queue + * management commands through its {@link Callback}. + */ + public static final int FLAG_HANDLES_QUEUE_COMMANDS = 1 << 2; + /** * System only flag for a session that needs to have priority over all other * sessions. This flag ensures this session will receive media button events @@ -100,6 +106,7 @@ public final class MediaSession { @IntDef(flag = true, value = { FLAG_HANDLES_MEDIA_BUTTONS, FLAG_HANDLES_TRANSPORT_CONTROLS, + FLAG_HANDLES_QUEUE_COMMANDS, FLAG_EXCLUSIVE_GLOBAL_PRIORITY }) public @interface SessionFlags { } @@ -645,6 +652,22 @@ public final class MediaSession { postToCallback(CallbackMessageHandler.MSG_CUSTOM_ACTION, action, args); } + private void dispatchAddQueueItem(MediaDescription description) { + postToCallback(CallbackMessageHandler.MSG_ADD_QUEUE_ITEM, description); + } + + private void dispatchAddQueueItem(MediaDescription description, int index) { + postToCallback(CallbackMessageHandler.MSG_ADD_QUEUE_ITEM_AT, description, index); + } + + private void dispatchRemoveQueueItem(MediaDescription description) { + postToCallback(CallbackMessageHandler.MSG_REMOVE_QUEUE_ITEM, description); + } + + private void dispatchRemoveQueueItemAt(int index) { + postToCallback(CallbackMessageHandler.MSG_REMOVE_QUEUE_ITEM_AT, index); + } + private void dispatchMediaButton(Intent mediaButtonIntent) { postToCallback(CallbackMessageHandler.MSG_MEDIA_BUTTON, mediaButtonIntent); } @@ -666,10 +689,22 @@ public final class MediaSession { postToCallback(CallbackMessageHandler.MSG_COMMAND, cmd); } + private void postToCallback(int what, int arg1) { + postToCallback(what, null, arg1); + } + private void postToCallback(int what, Object obj) { postToCallback(what, obj, null); } + private void postToCallback(int what, Object obj, int arg1) { + synchronized (mLock) { + if (mCallback != null) { + mCallback.post(what, obj, arg1); + } + } + } + private void postToCallback(int what, Object obj, Bundle extras) { synchronized (mLock) { if (mCallback != null) { @@ -1043,6 +1078,47 @@ public final class MediaSession { */ public void onCustomAction(@NonNull String action, @Nullable Bundle extras) { } + + /** + * Called when a {@link MediaController} wants to add a {@link QueueItem} with the given + * {@link MediaDescription description} at the end of the play queue. + * + * @param description The {@link MediaDescription} for creating the {@link QueueItem} to be + * inserted. + */ + public void onAddQueueItem(MediaDescription description) { + } + + /** + * Called when a {@link MediaController} wants to add a {@link QueueItem} with the given + * {@link MediaDescription description} at the specified position in the play queue. + * + * @param description The {@link MediaDescription} for creating the {@link QueueItem} to be + * inserted. + * @param index The index at which the created {@link QueueItem} is to be inserted. + */ + public void onAddQueueItem(MediaDescription description, int index) { + } + + /** + * Called when a {@link MediaController} wants to remove the first occurrence of the + * specified {@link QueueItem} with the given {@link MediaDescription description} + * in the play queue. + * + * @param description The {@link MediaDescription} for denoting the {@link QueueItem} to be + * removed. + */ + public void onRemoveQueueItem(MediaDescription description) { + } + + /** + * Called when a {@link MediaController} wants to remove a {@link QueueItem} at the + * specified position in the play queue. + * + * @param index The index of the element to be removed. + */ + public void onRemoveQueueItemAt(int index) { + } } /** @@ -1238,6 +1314,38 @@ public final class MediaSession { } } + @Override + public void onAddQueueItem(MediaDescription description) { + MediaSession session = mMediaSession.get(); + if (session != null) { + session.dispatchAddQueueItem(description); + } + } + + @Override + public void onAddQueueItemAt(MediaDescription description, int index) { + MediaSession session = mMediaSession.get(); + if (session != null) { + session.dispatchAddQueueItem(description, index); + } + } + + @Override + public void onRemoveQueueItem(MediaDescription description) { + MediaSession session = mMediaSession.get(); + if (session != null) { + session.dispatchRemoveQueueItem(description); + } + } + + @Override + public void onRemoveQueueItemAt(int index) { + MediaSession session = mMediaSession.get(); + if (session != null) { + session.dispatchRemoveQueueItemAt(index); + } + } + @Override public void onAdjustVolume(int direction) { MediaSession session = mMediaSession.get(); @@ -1376,6 +1484,10 @@ public final class MediaSession { private static final int MSG_CUSTOM_ACTION = 22; private static final int MSG_ADJUST_VOLUME = 23; private static final int MSG_SET_VOLUME = 24; + private static final int MSG_ADD_QUEUE_ITEM = 25; + private static final int MSG_ADD_QUEUE_ITEM_AT = 26; + private static final int MSG_REMOVE_QUEUE_ITEM = 27; + private static final int MSG_REMOVE_QUEUE_ITEM_AT = 28; private MediaSession.Callback mCallback; @@ -1465,7 +1577,7 @@ public final class MediaSession { mCallback.onSetRating((Rating) msg.obj); break; case MSG_REPEAT_MODE: - mCallback.onSetRepeatMode((int) msg.obj); + mCallback.onSetRepeatMode(msg.arg1); break; case MSG_SHUFFLE_MODE: mCallback.onSetShuffleModeEnabled((boolean) msg.obj); @@ -1473,12 +1585,24 @@ public final class MediaSession { case MSG_CUSTOM_ACTION: mCallback.onCustomAction((String) msg.obj, msg.getData()); break; + case MSG_ADD_QUEUE_ITEM: + mCallback.onAddQueueItem((MediaDescription) msg.obj); + break; + case MSG_ADD_QUEUE_ITEM_AT: + mCallback.onAddQueueItem((MediaDescription) msg.obj, msg.arg1); + break; + case MSG_REMOVE_QUEUE_ITEM: + mCallback.onRemoveQueueItem((MediaDescription) msg.obj); + break; + case MSG_REMOVE_QUEUE_ITEM_AT: + mCallback.onRemoveQueueItemAt(msg.arg1); + break; case MSG_ADJUST_VOLUME: synchronized (mLock) { vp = mVolumeProvider; } if (vp != null) { - vp.onAdjustVolume((int) msg.obj); + vp.onAdjustVolume(msg.arg1); } break; case MSG_SET_VOLUME: @@ -1486,7 +1610,7 @@ public final class MediaSession { vp = mVolumeProvider; } if (vp != null) { - vp.onSetVolumeTo((int) msg.obj); + vp.onSetVolumeTo(msg.arg1); } break; } diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index ede6e30becd34..10ecb867e8f0c 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -1123,6 +1123,38 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } } + public void addQueueItem(MediaDescription description) { + try { + mCb.onAddQueueItem(description); + } catch (RemoteException e) { + Slog.e(TAG, "Remote failure in addQueueItem.", e); + } + } + + public void addQueueItemAt(MediaDescription description, int index) { + try { + mCb.onAddQueueItemAt(description, index); + } catch (RemoteException e) { + Slog.e(TAG, "Remote failure in addQueueItemAt.", e); + } + } + + public void removeQueueItem(MediaDescription description) { + try { + mCb.onRemoveQueueItem(description); + } catch (RemoteException e) { + Slog.e(TAG, "Remote failure in removeQueueItem.", e); + } + } + + public void removeQueueItemAt(int index) { + try { + mCb.onRemoveQueueItemAt(index); + } catch (RemoteException e) { + Slog.e(TAG, "Remote failure in removeQueueItem.", e); + } + } + public void adjustVolume(int direction) { try { mCb.onAdjustVolume(direction); @@ -1396,6 +1428,30 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } } + @Override + public void addQueueItem(MediaDescription description) { + updateCallingPackage(); + mSessionCb.addQueueItem(description); + } + + @Override + public void addQueueItemAt(MediaDescription description, int index) { + updateCallingPackage(); + mSessionCb.addQueueItemAt(description, index); + } + + @Override + public void removeQueueItem(MediaDescription description) { + updateCallingPackage(); + mSessionCb.removeQueueItem(description); + } + + @Override + public void removeQueueItemAt(int index) { + updateCallingPackage(); + mSessionCb.removeQueueItemAt(index); + } + @Override public CharSequence getQueueTitle() { return mQueueTitle;