am 86e941e6: am a70def9e: Merge "Update to MediaBrowser APIs per council feedback" into lmp-dev

* commit '86e941e6b80481dba07731def3968f29c760c982':
  Update to MediaBrowser APIs per council feedback
This commit is contained in:
RoboErik
2014-08-28 22:57:25 +00:00
committed by Android Git Automerger
18 changed files with 679 additions and 915 deletions

View File

@@ -325,8 +325,6 @@ LOCAL_SRC_FILES += \
media/java/android/media/IRemoteVolumeObserver.aidl \
media/java/android/media/IRingtonePlayer.aidl \
media/java/android/media/IVolumeController.aidl \
media/java/android/media/browse/IMediaBrowserService.aidl \
media/java/android/media/browse/IMediaBrowserServiceCallbacks.aidl \
media/java/android/media/projection/IMediaProjection.aidl \
media/java/android/media/projection/IMediaProjectionCallback.aidl \
media/java/android/media/projection/IMediaProjectionManager.aidl \
@@ -346,6 +344,8 @@ LOCAL_SRC_FILES += \
media/java/android/media/tv/ITvInputServiceCallback.aidl \
media/java/android/media/tv/ITvInputSession.aidl \
media/java/android/media/tv/ITvInputSessionCallback.aidl \
media/java/android/service/media/IMediaBrowserService.aidl \
media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl \
telecomm/java/com/android/internal/telecomm/IVideoCallback.aidl \
telecomm/java/com/android/internal/telecomm/IVideoProvider.aidl \
telecomm/java/com/android/internal/telecomm/IConnectionService.aidl \

View File

@@ -14919,6 +14919,31 @@ package android.media {
ctor public MediaCryptoException(java.lang.String);
}
public class MediaDescription implements android.os.Parcelable {
method public int describeContents();
method public java.lang.CharSequence getDescription();
method public android.os.Bundle getExtras();
method public android.graphics.Bitmap getIconBitmap();
method public android.net.Uri getIconUri();
method public java.lang.String getMediaId();
method public java.lang.CharSequence getSubtitle();
method public java.lang.CharSequence getTitle();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
}
public static class MediaDescription.Builder {
ctor public MediaDescription.Builder();
method public android.media.MediaDescription build();
method public android.media.MediaDescription.Builder setDescription(java.lang.CharSequence);
method public android.media.MediaDescription.Builder setExtras(android.os.Bundle);
method public android.media.MediaDescription.Builder setIconBitmap(android.graphics.Bitmap);
method public android.media.MediaDescription.Builder setIconUri(android.net.Uri);
method public android.media.MediaDescription.Builder setMediaId(java.lang.String);
method public android.media.MediaDescription.Builder setSubtitle(java.lang.CharSequence);
method public android.media.MediaDescription.Builder setTitle(java.lang.CharSequence);
}
public final class MediaDrm {
ctor public MediaDrm(java.util.UUID) throws android.media.UnsupportedSchemeException;
method public void closeSession(byte[]);
@@ -15097,7 +15122,7 @@ package android.media {
method public boolean containsKey(java.lang.String);
method public int describeContents();
method public android.graphics.Bitmap getBitmap(java.lang.String);
method public android.media.MediaMetadata.Description getDescription();
method public android.media.MediaDescription getDescription();
method public long getLong(java.lang.String);
method public android.media.Rating getRating(java.lang.String);
method public java.lang.String getString(java.lang.String);
@@ -15125,6 +15150,7 @@ package android.media {
field public static final java.lang.String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE";
field public static final java.lang.String METADATA_KEY_DURATION = "android.media.metadata.DURATION";
field public static final java.lang.String METADATA_KEY_GENRE = "android.media.metadata.GENRE";
field public static final java.lang.String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID";
field public static final java.lang.String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS";
field public static final java.lang.String METADATA_KEY_RATING = "android.media.metadata.RATING";
field public static final java.lang.String METADATA_KEY_TITLE = "android.media.metadata.TITLE";
@@ -15145,14 +15171,6 @@ package android.media {
method public android.media.MediaMetadata.Builder putText(java.lang.String, java.lang.CharSequence);
}
public final class MediaMetadata.Description {
method public java.lang.CharSequence getDescription();
method public android.graphics.Bitmap getIcon();
method public android.net.Uri getIconUri();
method public java.lang.CharSequence getSubtitle();
method public java.lang.CharSequence getTitle();
}
public abstract deprecated class MediaMetadataEditor {
method public synchronized void addEditableKey(int);
method public abstract void apply();
@@ -16238,7 +16256,6 @@ package android.media.browse {
method public android.content.ComponentName getServiceComponent();
method public android.media.session.MediaSession.Token getSessionToken();
method public boolean isConnected();
method public void loadIcon(android.net.Uri, int, int, android.media.browse.MediaBrowser.IconCallback);
method public void subscribe(android.net.Uri, android.media.browse.MediaBrowser.SubscriptionCallback);
method public void unsubscribe(android.net.Uri);
}
@@ -16250,27 +16267,12 @@ package android.media.browse {
method public void onConnectionSuspended();
}
public static abstract class MediaBrowser.IconCallback {
ctor public MediaBrowser.IconCallback();
method public void onError(android.net.Uri);
method public void onIconLoaded(android.net.Uri, android.graphics.Bitmap);
}
public static abstract class MediaBrowser.SubscriptionCallback {
ctor public MediaBrowser.SubscriptionCallback();
method public void onChildrenLoaded(android.net.Uri, java.util.List<android.media.browse.MediaBrowserItem>);
method public void onError(android.net.Uri);
}
public final class MediaBrowserItem implements android.os.Parcelable {
public static class MediaBrowser.MediaItem implements android.os.Parcelable {
ctor public MediaBrowser.MediaItem(int, android.media.MediaDescription);
method public int describeContents();
method public android.os.Bundle getExtras();
method public android.media.MediaDescription getDescription();
method public int getFlags();
method public int getIconResourceId();
method public android.net.Uri getIconUri();
method public java.lang.CharSequence getSummary();
method public java.lang.CharSequence getTitle();
method public android.net.Uri getUri();
method public java.lang.String getMediaId();
method public boolean isBrowsable();
method public boolean isPlayable();
method public void writeToParcel(android.os.Parcel, int);
@@ -16279,37 +16281,10 @@ package android.media.browse {
field public static final int FLAG_PLAYABLE = 2; // 0x2
}
public static final class MediaBrowserItem.Builder {
ctor public MediaBrowserItem.Builder(android.net.Uri, int, java.lang.CharSequence);
method public android.media.browse.MediaBrowserItem build();
method public android.media.browse.MediaBrowserItem.Builder setExtras(android.os.Bundle);
method public android.media.browse.MediaBrowserItem.Builder setIconResourceId(int);
method public android.media.browse.MediaBrowserItem.Builder setIconUri(android.net.Uri);
method public android.media.browse.MediaBrowserItem.Builder setSummary(java.lang.CharSequence);
}
public abstract class MediaBrowserService extends android.app.Service {
ctor public MediaBrowserService();
method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
method public android.media.session.MediaSession.Token getSessionToken();
method public void notifyChildrenChanged(android.net.Uri);
method public android.os.IBinder onBind(android.content.Intent);
method public abstract android.media.browse.MediaBrowserService.BrowserRoot onGetRoot(java.lang.String, int, android.os.Bundle);
method public abstract void onLoadChildren(android.net.Uri, android.media.browse.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowserItem>>);
method public abstract void onLoadIcon(android.net.Uri, int, int, android.media.browse.MediaBrowserService.Result<android.graphics.Bitmap>);
method public void setSessionToken(android.media.session.MediaSession.Token);
field public static final java.lang.String SERVICE_ACTION = "android.media.browse.MediaBrowserService";
}
public static final class MediaBrowserService.BrowserRoot {
ctor public MediaBrowserService.BrowserRoot(android.net.Uri, android.os.Bundle);
method public android.os.Bundle getExtras();
method public android.net.Uri getRootUri();
}
public class MediaBrowserService.Result {
method public void detach();
method public void sendResult(T);
public static abstract class MediaBrowser.SubscriptionCallback {
ctor public MediaBrowser.SubscriptionCallback();
method public void onChildrenLoaded(android.net.Uri, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
method public void onError(android.net.Uri);
}
}
@@ -16404,7 +16379,7 @@ package android.media.session {
method public java.lang.String getPackageName();
method public android.media.session.MediaController.PlaybackInfo getPlaybackInfo();
method public android.media.session.PlaybackState getPlaybackState();
method public java.util.List<android.media.session.MediaSession.Item> getQueue();
method public java.util.List<android.media.session.MediaSession.QueueItem> getQueue();
method public java.lang.CharSequence getQueueTitle();
method public int getRatingType();
method public android.app.PendingIntent getSessionActivity();
@@ -16421,7 +16396,7 @@ package android.media.session {
method public void onExtrasChanged(android.os.Bundle);
method public void onMetadataChanged(android.media.MediaMetadata);
method public void onPlaybackStateChanged(android.media.session.PlaybackState);
method public void onQueueChanged(java.util.List<android.media.session.MediaSession.Item>);
method public void onQueueChanged(java.util.List<android.media.session.MediaSession.QueueItem>);
method public void onQueueTitleChanged(java.lang.CharSequence);
method public void onSessionDestroyed();
method public void onSessionEvent(java.lang.String, android.os.Bundle);
@@ -16441,16 +16416,16 @@ package android.media.session {
method public void fastForward();
method public void pause();
method public void play();
method public void playFromMediaId(java.lang.String, android.os.Bundle);
method public void playFromSearch(java.lang.String, android.os.Bundle);
method public void playUri(android.net.Uri, android.os.Bundle);
method public void rewind();
method public void seekTo(long);
method public void sendCustomAction(android.media.session.PlaybackState.CustomAction, android.os.Bundle);
method public void sendCustomAction(java.lang.String, android.os.Bundle);
method public void setRating(android.media.Rating);
method public void skipToItem(long);
method public void skipToNext();
method public void skipToPrevious();
method public void skipToQueueItem(long);
method public void stop();
}
@@ -16471,7 +16446,7 @@ package android.media.session {
method public void setPlaybackState(android.media.session.PlaybackState);
method public void setPlaybackToLocal(android.media.AudioAttributes);
method public void setPlaybackToRemote(android.media.VolumeProvider);
method public void setQueue(java.util.List<android.media.session.MediaSession.Item>);
method public void setQueue(java.util.List<android.media.session.MediaSession.QueueItem>);
method public void setQueueTitle(java.lang.CharSequence);
method public void setSessionActivity(android.app.PendingIntent);
field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
@@ -16486,34 +16461,27 @@ package android.media.session {
method public boolean onMediaButtonEvent(android.content.Intent);
method public void onPause();
method public void onPlay();
method public void onPlayFromMediaId(java.lang.String, android.os.Bundle);
method public void onPlayFromSearch(java.lang.String, android.os.Bundle);
method public void onPlayUri(android.net.Uri, android.os.Bundle);
method public void onRewind();
method public void onSeekTo(long);
method public void onSetRating(android.media.Rating);
method public void onSkipToItem(long);
method public void onSkipToNext();
method public void onSkipToPrevious();
method public void onSkipToQueueItem(long);
method public void onStop();
}
public static final class MediaSession.Item implements android.os.Parcelable {
public static final class MediaSession.QueueItem implements android.os.Parcelable {
ctor public MediaSession.QueueItem(android.media.MediaDescription, long);
method public int describeContents();
method public android.os.Bundle getExtras();
method public long getId();
method public android.media.MediaMetadata getMetadata();
method public android.net.Uri getUri();
method public android.media.MediaDescription getDescription();
method public long getQueueId();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
field public static final int UNKNOWN_ID = -1; // 0xffffffff
}
public static final class MediaSession.Item.Builder {
ctor public MediaSession.Item.Builder(android.media.MediaMetadata, long, android.net.Uri);
method public android.media.session.MediaSession.Item build();
method public android.media.session.MediaSession.Item.Builder setExtras(android.os.Bundle);
}
public static final class MediaSession.Token implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
@@ -16534,6 +16502,7 @@ package android.media.session {
public final class PlaybackState implements android.os.Parcelable {
method public int describeContents();
method public long getActions();
method public long getActiveQueueItemId();
method public long getBufferedPosition();
method public java.util.List<android.media.session.PlaybackState.CustomAction> getCustomActions();
method public java.lang.CharSequence getErrorMessage();
@@ -16545,15 +16514,15 @@ package android.media.session {
field public static final long ACTION_FAST_FORWARD = 64L; // 0x40L
field public static final long ACTION_PAUSE = 2L; // 0x2L
field public static final long ACTION_PLAY = 4L; // 0x4L
field public static final long ACTION_PLAY_FROM_MEDIA_ID = 1024L; // 0x400L
field public static final long ACTION_PLAY_FROM_SEARCH = 2048L; // 0x800L
field public static final long ACTION_PLAY_PAUSE = 512L; // 0x200L
field public static final long ACTION_PLAY_URI = 1024L; // 0x400L
field public static final long ACTION_REWIND = 8L; // 0x8L
field public static final long ACTION_SEEK_TO = 256L; // 0x100L
field public static final long ACTION_SET_RATING = 128L; // 0x80L
field public static final long ACTION_SKIP_TO_ITEM = 4096L; // 0x1000L
field public static final long ACTION_SKIP_TO_NEXT = 32L; // 0x20L
field public static final long ACTION_SKIP_TO_PREVIOUS = 16L; // 0x10L
field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L
field public static final long ACTION_STOP = 1L; // 0x1L
field public static final android.os.Parcelable.Creator CREATOR;
field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL
@@ -16567,6 +16536,7 @@ package android.media.session {
field public static final int STATE_REWINDING = 5; // 0x5
field public static final int STATE_SKIPPING_TO_NEXT = 10; // 0xa
field public static final int STATE_SKIPPING_TO_PREVIOUS = 9; // 0x9
field public static final int STATE_SKIPPING_TO_QUEUE_ITEM = 11; // 0xb
field public static final int STATE_STOPPED = 1; // 0x1
}
@@ -16577,7 +16547,7 @@ package android.media.session {
method public android.media.session.PlaybackState.Builder addCustomAction(android.media.session.PlaybackState.CustomAction);
method public android.media.session.PlaybackState build();
method public android.media.session.PlaybackState.Builder setActions(long);
method public android.media.session.PlaybackState.Builder setActiveItem(long);
method public android.media.session.PlaybackState.Builder setActiveQueueItemId(long);
method public android.media.session.PlaybackState.Builder setBufferedPosition(long);
method public android.media.session.PlaybackState.Builder setErrorMessage(java.lang.CharSequence);
method public android.media.session.PlaybackState.Builder setState(int, long, float, long);
@@ -27093,6 +27063,33 @@ package android.service.dreams {
}
package android.service.media {
public abstract class MediaBrowserService extends android.app.Service {
ctor public MediaBrowserService();
method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
method public android.media.session.MediaSession.Token getSessionToken();
method public void notifyChildrenChanged(android.net.Uri);
method public android.os.IBinder onBind(android.content.Intent);
method public abstract android.service.media.MediaBrowserService.BrowserRoot onGetRoot(java.lang.String, int, android.os.Bundle);
method public abstract void onLoadChildren(android.net.Uri, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>);
method public void setSessionToken(android.media.session.MediaSession.Token);
field public static final java.lang.String SERVICE_ACTION = "android.media.browse.MediaBrowserService";
}
public static final class MediaBrowserService.BrowserRoot {
ctor public MediaBrowserService.BrowserRoot(android.net.Uri, android.os.Bundle);
method public android.os.Bundle getExtras();
method public android.net.Uri getRootUri();
}
public class MediaBrowserService.Result {
method public void detach();
method public void sendResult(T);
}
}
package android.service.notification {
public abstract class NotificationListenerService extends android.app.Service {

View File

@@ -0,0 +1,18 @@
/* Copyright 2014, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
package android.media;
parcelable MediaDescription;

View File

@@ -0,0 +1,276 @@
package android.media;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Size;
/**
* A simple set of metadata for a media item suitable for display. This can be
* created using the Builder or retrieved from existing metadata using
* {@link MediaMetadata#getDescription()}.
*/
public class MediaDescription implements Parcelable {
/**
* A unique persistent id for the content or null.
*/
private final String mMediaId;
/**
* A primary title suitable for display or null.
*/
private final CharSequence mTitle;
/**
* A subtitle suitable for display or null.
*/
private final CharSequence mSubtitle;
/**
* A description suitable for display or null.
*/
private final CharSequence mDescription;
/**
* A bitmap icon suitable for display or null.
*/
private final Bitmap mIcon;
/**
* A Uri for an icon suitable for display or null.
*/
private final Uri mIconUri;
/**
* Extras for opaque use by apps/system.
*/
private final Bundle mExtras;
private MediaDescription(String mediaId, CharSequence title, CharSequence subtitle,
CharSequence description, Bitmap icon, Uri iconUri, Bundle extras) {
mMediaId = mediaId;
mTitle = title;
mSubtitle = subtitle;
mDescription = description;
mIcon = icon;
mIconUri = iconUri;
mExtras = extras;
}
private MediaDescription(Parcel in) {
mMediaId = in.readString();
mTitle = in.readCharSequence();
mSubtitle = in.readCharSequence();
mDescription = in.readCharSequence();
mIcon = in.readParcelable(null);
mIconUri = in.readParcelable(null);
mExtras = in.readBundle();
}
/**
* Returns the media id or null. See
* {@link MediaMetadata#METADATA_KEY_MEDIA_ID}.
*/
public @Nullable String getMediaId() {
return mMediaId;
}
/**
* Returns a title suitable for display or null.
*
* @return A title or null.
*/
public @Nullable CharSequence getTitle() {
return mTitle;
}
/**
* Returns a subtitle suitable for display or null.
*
* @return A subtitle or null.
*/
public @Nullable CharSequence getSubtitle() {
return mSubtitle;
}
/**
* Returns a description suitable for display or null.
*
* @return A description or null.
*/
public @Nullable CharSequence getDescription() {
return mDescription;
}
/**
* Returns a bitmap icon suitable for display or null.
*
* @return An icon or null.
*/
public @Nullable Bitmap getIconBitmap() {
return mIcon;
}
/**
* Returns a Uri for an icon suitable for display or null.
*
* @return An icon uri or null.
*/
public @Nullable Uri getIconUri() {
return mIconUri;
}
/**
* Returns any extras that were added to the description.
*
* @return A bundle of extras or null.
*/
public @Nullable Bundle getExtras() {
return mExtras;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mMediaId);
dest.writeCharSequence(mTitle);
dest.writeCharSequence(mSubtitle);
dest.writeCharSequence(mDescription);
dest.writeParcelable(mIcon, flags);
dest.writeParcelable(mIconUri, flags);
dest.writeBundle(mExtras);
}
@Override
public String toString() {
return mTitle + ", " + mSubtitle + ", " + mDescription;
}
public static final Parcelable.Creator<MediaDescription> CREATOR =
new Parcelable.Creator<MediaDescription>() {
@Override
public MediaDescription createFromParcel(Parcel in) {
return new MediaDescription(in);
}
@Override
public MediaDescription[] newArray(int size) {
return new MediaDescription[size];
}
};
/**
* Builder for {@link MediaDescription} objects.
*/
public static class Builder {
private String mMediaId;
private CharSequence mTitle;
private CharSequence mSubtitle;
private CharSequence mDescription;
private Bitmap mIcon;
private Uri mIconUri;
private Bundle mExtras;
/**
* Creates an initially empty builder.
*/
public Builder() {
}
/**
* Sets the media id.
*
* @param mediaId The unique id for the item or null.
* @return this
*/
public Builder setMediaId(@Nullable String mediaId) {
mMediaId = mediaId;
return this;
}
/**
* Sets the title.
*
* @param title A title suitable for display to the user or null.
* @return this
*/
public Builder setTitle(@Nullable CharSequence title) {
mTitle = title;
return this;
}
/**
* Sets the subtitle.
*
* @param subtitle A subtitle suitable for display to the user or null.
* @return this
*/
public Builder setSubtitle(@Nullable CharSequence subtitle) {
mSubtitle = subtitle;
return this;
}
/**
* Sets the description.
*
* @param description A description suitable for display to the user or
* null.
* @return this
*/
public Builder setDescription(@Nullable CharSequence description) {
mDescription = description;
return this;
}
/**
* Sets the icon.
*
* @param icon A {@link Bitmap} icon suitable for display to the user or
* null.
* @return this
*/
public Builder setIconBitmap(@Nullable Bitmap icon) {
mIcon = icon;
return this;
}
/**
* Sets the icon uri.
*
* @param iconUri A {@link Uri} for an icon suitable for display to the
* user or null.
* @return this
*/
public Builder setIconUri(@Nullable Uri iconUri) {
mIconUri = iconUri;
return this;
}
/**
* Sets a bundle of extras.
*
* @param extras The extras to include with this description or null.
* @return this
*/
public Builder setExtras(@Nullable Bundle extras) {
mExtras = extras;
return this;
}
public MediaDescription build() {
return new MediaDescription(mMediaId, mTitle, mSubtitle, mDescription, mIcon, mIconUri,
mExtras);
}
}
}

View File

@@ -17,15 +17,22 @@ package android.media;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.browse.MediaBrowser;
import android.media.session.MediaController;
import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.OperationCanceledException;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.text.format.Time;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Size;
import android.util.SparseArray;
import java.util.Set;
@@ -119,7 +126,9 @@ public final class MediaMetadata implements Parcelable {
public static final String METADATA_KEY_ART = "android.media.metadata.ART";
/**
* The artwork for the media as a Uri.
* The artwork for the media as a Uri formatted String. The artwork can be
* loaded using a combination of {@link ContentResolver#openInputStream} and
* {@link BitmapFactory#decodeStream}.
*/
public static final String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI";
@@ -130,7 +139,10 @@ public final class MediaMetadata implements Parcelable {
public static final String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART";
/**
* The artwork for the album of the media's original source as a Uri.
* The artwork for the album of the media's original source as a Uri
* formatted String. The artwork can be loaded using a combination of
* {@link ContentResolver#openInputStream} and
* {@link BitmapFactory#decodeStream}.
*/
public static final String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI";
@@ -181,13 +193,26 @@ public final class MediaMetadata implements Parcelable {
= "android.media.metadata.DISPLAY_ICON";
/**
* An icon or thumbnail that is suitable for display to the user. When
* displaying more information for media described by this metadata the
* display description should be preferred to other fields when present.
* A Uri formatted String for an icon or thumbnail that is suitable for
* display to the user. When displaying more information for media described
* by this metadata the display description should be preferred to other
* fields when present. The icon can be loaded using a combination of
* {@link ContentResolver#openInputStream} and
* {@link BitmapFactory#decodeStream}.
*/
public static final String METADATA_KEY_DISPLAY_ICON_URI
= "android.media.metadata.DISPLAY_ICON_URI";
/**
* A String key for identifying the content. This value is specific to the
* service providing the content. If used, this should be a persistent
* unique key for the underlying content. It may be used with
* {@link MediaController.TransportControls#playFromMediaId(String, Bundle)}
* to initiate playback when provided by a {@link MediaBrowser} connected to
* the same app.
*/
public static final String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID";
private static final String[] PREFERRED_DESCRIPTION_ORDER = {
METADATA_KEY_TITLE,
METADATA_KEY_ARTIST,
@@ -277,7 +302,7 @@ public final class MediaMetadata implements Parcelable {
}
private final Bundle mBundle;
private Description mDescription;
private MediaDescription mDescription;
private MediaMetadata(Bundle bundle) {
mBundle = new Bundle(bundle);
@@ -406,11 +431,13 @@ public final class MediaMetadata implements Parcelable {
*
* @return A simple description of this metadata.
*/
public @NonNull Description getDescription() {
public @NonNull MediaDescription getDescription() {
if (mDescription != null) {
return mDescription;
}
String mediaId = getString(METADATA_KEY_MEDIA_ID);
CharSequence[] text = new CharSequence[3];
Bitmap icon = null;
Uri iconUri = null;
@@ -454,7 +481,15 @@ public final class MediaMetadata implements Parcelable {
}
}
mDescription = new Description(text[0], text[1], text[2], icon, iconUri);
MediaDescription.Builder bob = new MediaDescription.Builder();
bob.setMediaId(mediaId);
bob.setTitle(text[0]);
bob.setSubtitle(text[1]);
bob.setDescription(text[2]);
bob.setIconBitmap(icon);
bob.setIconUri(iconUri);
mDescription = bob.build();
return mDescription;
}
@@ -668,90 +703,4 @@ public final class MediaMetadata implements Parcelable {
return new MediaMetadata(mBundle);
}
}
/**
* A simple form of the metadata that can be used for display.
*/
public final class Description {
/**
* A primary title suitable for display or null.
*/
private final CharSequence mTitle;
/**
* A subtitle suitable for display or null.
*/
private final CharSequence mSubtitle;
/**
* A description suitable for display or null.
*/
private final CharSequence mDescription;
/**
* A bitmap icon suitable for display or null.
*/
private final Bitmap mIcon;
/**
* A Uri for an icon suitable for display or null.
*/
private final Uri mIconUri;
/**
* Returns the best available title or null.
*
* @return A title or null.
*/
public @Nullable CharSequence getTitle() {
return mTitle;
}
/**
* Returns the best available subtitle or null.
*
* @return A subtitle or null.
*/
public @Nullable CharSequence getSubtitle() {
return mSubtitle;
}
/**
* Returns the best available description or null.
*
* @return A description or null.
*/
public @Nullable CharSequence getDescription() {
return mDescription;
}
/**
* Returns the best available icon or null.
*
* @return An icon or null.
*/
public @Nullable Bitmap getIcon() {
return mIcon;
}
/**
* Returns the best available icon Uri or null.
*
* @return An icon uri or null.
*/
public @Nullable Uri getIconUri() {
return mIconUri;
}
private Description(CharSequence title, CharSequence subtitle, CharSequence description,
Bitmap icon, Uri iconUri) {
mTitle = title;
mSubtitle = subtitle;
mDescription = description;
mIcon = icon;
mIconUri = iconUri;
}
@Override
public String toString() {
return mTitle + ", " + mSubtitle + ", " + mDescription;
}
}
}

View File

@@ -16,6 +16,7 @@
package android.media.browse;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
@@ -23,26 +24,27 @@ import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.ParceledListSlice;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.media.MediaDescription;
import android.media.session.MediaSession;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import android.service.media.MediaBrowserService;
import android.service.media.IMediaBrowserService;
import android.service.media.IMediaBrowserServiceCallbacks;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
/**
* Browses media content offered by a link MediaBrowserService.
@@ -67,8 +69,6 @@ public final class MediaBrowser {
private final Handler mHandler = new Handler();
private final ArrayMap<Uri,Subscription> mSubscriptions =
new ArrayMap<Uri, MediaBrowser.Subscription>();
private final SparseArray<IconRequest> mIconRequests =
new SparseArray<IconRequest>();
private int mState = CONNECT_STATE_DISCONNECTED;
private MediaServiceConnection mServiceConnection;
@@ -77,7 +77,6 @@ public final class MediaBrowser {
private Uri mRootUri;
private MediaSession.Token mMediaSessionToken;
private Bundle mExtras;
private int mNextSeq;
/**
* Creates a media browser for the specified media browse service.
@@ -362,49 +361,6 @@ public final class MediaBrowser {
}
}
/**
* Loads the icon of a media item.
*
* @param uri The uri of the Icon.
* @param width The preferred width of the icon in dp.
* @param height The preferred width of the icon in dp.
* @param callback The callback to receive the icon.
*/
public void loadIcon(final @NonNull Uri uri, final int width, final int height,
final @NonNull IconCallback callback) {
if (uri == null) {
throw new IllegalArgumentException("Icon uri cannot be null");
}
if (callback == null) {
throw new IllegalArgumentException("Icon callback cannot be null");
}
mHandler.post(new Runnable() {
@Override
public void run() {
for (int i = 0; i < mIconRequests.size(); i++) {
IconRequest existingRequest = mIconRequests.valueAt(i);
if (existingRequest.isSameRequest(uri, width, height)) {
existingRequest.addCallback(callback);
return;
}
}
final int seq = mNextSeq++;
IconRequest request = new IconRequest(seq, uri, width, height);
request.addCallback(callback);
mIconRequests.put(seq, request);
if (mState == CONNECT_STATE_CONNECTED) {
try {
mServiceBinder.loadIcon(seq, uri, width, height, mServiceCallbacks);
} catch (RemoteException e) {
// Process is crashing. We will disconnect, and upon reconnect we will
// automatically reload the icons. So nothing to do here.
Log.d(TAG, "loadIcon failed with RemoteException uri=" + uri);
}
}
}
});
}
/**
* For debugging.
*/
@@ -461,18 +417,6 @@ public final class MediaBrowser {
Log.d(TAG, "addSubscription failed with RemoteException parentUri=" + uri);
}
}
for (int i = 0; i < mIconRequests.size(); i++) {
IconRequest request = mIconRequests.valueAt(i);
try {
mServiceBinder.loadIcon(request.mSeq, request.mUri,
request.mWidth, request.mHeight, mServiceCallbacks);
} catch (RemoteException e) {
// Process is crashing. We will disconnect, and upon reconnect we will
// automatically reload. So nothing to do here.
Log.d(TAG, "loadIcon failed with RemoteException request=" + request);
}
}
}
});
}
@@ -515,7 +459,7 @@ public final class MediaBrowser {
return;
}
List<MediaBrowserItem> data = list.getList();
List<MediaItem> data = list.getList();
if (DBG) {
Log.d(TAG, "onLoadChildren for " + mServiceComponent + " uri=" + uri);
}
@@ -539,32 +483,6 @@ public final class MediaBrowser {
});
}
private final void onLoadIcon(final IMediaBrowserServiceCallbacks callback,
final int seqNum, final Bitmap bitmap) {
mHandler.post(new Runnable() {
@Override
public void run() {
// Check that there hasn't been a disconnect or a different
// ServiceConnection.
if (!isCurrent(callback, "onLoadIcon")) {
return;
}
IconRequest request = mIconRequests.get(seqNum);
if (request == null) {
Log.d(TAG, "onLoadIcon called for seqNum=" + seqNum + " request="
+ request + " but the request is not registered");
return;
}
mIconRequests.delete(seqNum);
for (IconCallback IconCallback : request.getCallbacks()) {
IconCallback.onIconLoaded(request.mUri, bitmap);
}
}
});
}
/**
* Return true if {@code callback} is the current ServiceCallbacks. Also logs if it's not.
*/
@@ -600,6 +518,126 @@ public final class MediaBrowser {
Log.d(TAG, " mMediaSessionToken=" + mMediaSessionToken);
}
public static class MediaItem implements Parcelable {
private final int mFlags;
private final MediaDescription mDescription;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag=true, value = { FLAG_BROWSABLE, FLAG_PLAYABLE })
public @interface Flags { }
/**
* Flag: Indicates that the item has children of its own.
*/
public static final int FLAG_BROWSABLE = 1 << 0;
/**
* Flag: Indicates that the item is playable.
* <p>
* The Uri of this item may be passed to link android.media.session.MediaController#play(Uri)
* to start playing it.
* </p>
*/
public static final int FLAG_PLAYABLE = 1 << 1;
/**
* Create a new MediaItem for use in browsing media.
*
* @param flags The flags for this item.
* @param description The description of the media, which must include a
* media id.
*/
public MediaItem(@Flags int flags, @NonNull MediaDescription description) {
if (description == null) {
throw new IllegalArgumentException("description cannot be null");
}
if (TextUtils.isEmpty(description.getMediaId())) {
throw new IllegalArgumentException("description must have a non-empty media id");
}
mFlags = flags;
mDescription = description;
}
/**
* Private constructor.
*/
private MediaItem(Parcel in) {
mFlags = in.readInt();
mDescription = MediaDescription.CREATOR.createFromParcel(in);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mFlags);
mDescription.writeToParcel(out, flags);
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("MediaItem{");
sb.append("mFlags=").append(mFlags);
sb.append(", mDescription=").append(mDescription);
sb.append('}');
return sb.toString();
}
public static final Parcelable.Creator<MediaItem> CREATOR =
new Parcelable.Creator<MediaItem>() {
@Override
public MediaItem createFromParcel(Parcel in) {
return new MediaItem(in);
}
@Override
public MediaItem[] newArray(int size) {
return new MediaItem[size];
}
};
/**
* Gets the flags of the item.
*/
public @Flags int getFlags() {
return mFlags;
}
/**
* Returns whether this item is browsable.
* @see #FLAG_BROWSABLE
*/
public boolean isBrowsable() {
return (mFlags & FLAG_BROWSABLE) != 0;
}
/**
* Returns whether this item is playable.
* @see #FLAG_PLAYABLE
*/
public boolean isPlayable() {
return (mFlags & FLAG_PLAYABLE) != 0;
}
/**
* Returns the description of the media.
*/
public @NonNull MediaDescription getDescription() {
return mDescription;
}
/**
* Returns the media id for this item.
*/
public @NonNull String getMediaId() {
return mDescription.getMediaId();
}
}
/**
* Callbacks for connection related events.
@@ -632,7 +670,7 @@ public final class MediaBrowser {
* Called when the list of children is loaded or updated.
*/
public void onChildrenLoaded(@NonNull Uri parentUri,
@NonNull List<MediaBrowserItem> children) {
@NonNull List<MediaItem> children) {
}
/**
@@ -646,87 +684,6 @@ public final class MediaBrowser {
}
}
/**
* Callbacks for icon loading.
*/
public static abstract class IconCallback {
/**
* Called when the icon is loaded.
*/
public void onIconLoaded(@NonNull Uri uri, @NonNull Bitmap bitmap) {
}
/**
* Called when the Uri doesnt exist or the bitmap cannot be loaded.
*/
public void onError(@NonNull Uri uri) {
}
}
private static class IconRequest {
final int mSeq;
final Uri mUri;
final int mWidth;
final int mHeight;
final List<IconCallback> mCallbacks;
/**
* Constructs an icon request.
* @param seq The unique sequence number assigned to the request by the media browser.
* @param uri The Uri for the icon.
* @param width The width for the icon.
* @param height The height for the icon.
*/
IconRequest(int seq, @NonNull Uri uri, int width, int height) {
if (uri == null) {
throw new IllegalArgumentException("Icon uri cannot be null");
}
this.mSeq = seq;
this.mUri = uri;
this.mWidth = width;
this.mHeight = height;
mCallbacks = new ArrayList<IconCallback>();
}
/**
* Adds a callback to the icon request.
* If the callback already exists, it will not be added again.
*/
public void addCallback(@NonNull IconCallback callback) {
if (callback == null) {
throw new IllegalArgumentException("callback cannot be null in IconRequest");
}
if (!mCallbacks.contains(callback)) {
mCallbacks.add(callback);
}
}
/**
* Checks if the icon request has the same uri, width, and height as the given values.
*/
public boolean isSameRequest(@Nullable Uri uri, int width, int height) {
return Objects.equals(mUri, uri) && mWidth == width && mHeight == height;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("IconRequest{");
sb.append("uri=").append(mUri);
sb.append(", width=").append(mWidth);
sb.append(", height=").append(mHeight);
sb.append(", seq=").append(mSeq);
sb.append('}');
return sb.toString();
}
/**
* Gets an unmodifiable view of the list of callbacks associated with the request.
*/
public List<IconCallback> getCallbacks() {
return Collections.unmodifiableList(mCallbacks);
}
}
/**
* ServiceConnection to the other app.
*/
@@ -809,7 +766,7 @@ public final class MediaBrowser {
}
return true;
}
};
}
/**
* Callbacks from the service.
@@ -852,14 +809,6 @@ public final class MediaBrowser {
mediaBrowser.onLoadChildren(this, uri, list);
}
}
@Override
public void onLoadIcon(final int seqNum, final Bitmap bitmap) {
MediaBrowser mediaBrowser = mMediaBrowser.get();
if (mediaBrowser != null) {
mediaBrowser.onLoadIcon(this, seqNum, bitmap);
}
}
}
private static class Subscription {

View File

@@ -1,313 +0,0 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.media.browse;
import android.annotation.DrawableRes;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.net.Uri;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Describes a media item in the list of items offered by a {@link MediaBrowserService}.
*/
public final class MediaBrowserItem implements Parcelable {
private final Uri mUri;
private final int mFlags;
private final CharSequence mTitle;
private final CharSequence mSummary;
private final Uri mIconUri;
private final int mIconResourceId;
private final Bundle mExtras;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag=true, value = { FLAG_BROWSABLE, FLAG_PLAYABLE })
public @interface Flags { }
/**
* Flag: Indicates that the item has children of its own.
*/
public static final int FLAG_BROWSABLE = 1 << 0;
/**
* Flag: Indicates that the item is playable.
* <p>
* The Uri of this item may be passed to link android.media.session.MediaController#play(Uri)
* to start playing it.
* </p>
*/
public static final int FLAG_PLAYABLE = 1 << 1;
/**
* Initialize a MediaBrowserItem object.
*/
private MediaBrowserItem(@NonNull Uri uri, int flags, @NonNull CharSequence title,
CharSequence summary, @Nullable Uri iconUri, int iconResourceId, Bundle extras) {
if (uri == null) {
throw new IllegalArgumentException("uri can not be null");
}
if (title == null) {
throw new IllegalArgumentException("title can not be null");
}
mUri = uri;
mFlags = flags;
mTitle = title;
mSummary = summary;
mIconUri = iconUri;
mIconResourceId = iconResourceId;
mExtras = extras;
}
/**
* Private constructor.
*/
private MediaBrowserItem(Parcel in) {
mUri = Uri.CREATOR.createFromParcel(in);
mFlags = in.readInt();
mTitle = in.readCharSequence();
if (in.readInt() != 0) {
mSummary = in.readCharSequence();
} else {
mSummary = null;
}
if (in.readInt() != 0) {
mIconUri = Uri.CREATOR.createFromParcel(in);
} else {
mIconUri = null;
}
mIconResourceId = in.readInt();
if (in.readInt() != 0) {
mExtras = Bundle.CREATOR.createFromParcel(in);
} else {
mExtras = null;
}
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel out, int flags) {
mUri.writeToParcel(out, flags);
out.writeInt(mFlags);
out.writeCharSequence(mTitle);
if (mSummary != null) {
out.writeInt(1);
out.writeCharSequence(mSummary);
} else {
out.writeInt(0);
}
if (mIconUri != null) {
out.writeInt(1);
mIconUri.writeToParcel(out, flags);
} else {
out.writeInt(0);
}
out.writeInt(mIconResourceId);
if (mExtras != null) {
out.writeInt(1);
mExtras.writeToParcel(out, flags);
} else {
out.writeInt(0);
}
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("MediaBrowserItem{");
sb.append("mUri=").append(mUri);
sb.append(", mFlags=").append(mFlags);
sb.append(", mTitle=").append(mTitle);
sb.append(", mSummary=").append(mSummary);
sb.append(", mIconUri=").append(mIconUri);
sb.append(", mIconResourceId=").append(mIconResourceId);
sb.append('}');
return sb.toString();
}
public static final Parcelable.Creator<MediaBrowserItem> CREATOR =
new Parcelable.Creator<MediaBrowserItem>() {
@Override
public MediaBrowserItem createFromParcel(Parcel in) {
return new MediaBrowserItem(in);
}
@Override
public MediaBrowserItem[] newArray(int size) {
return new MediaBrowserItem[size];
}
};
/**
* Gets the Uri of the item.
*/
public @NonNull Uri getUri() {
return mUri;
}
/**
* Gets the flags of the item.
*/
public @Flags int getFlags() {
return mFlags;
}
/**
* Returns whether this item is browsable.
* @see #FLAG_BROWSABLE
*/
public boolean isBrowsable() {
return (mFlags & FLAG_BROWSABLE) != 0;
}
/**
* Returns whether this item is playable.
* @see #FLAG_PLAYABLE
*/
public boolean isPlayable() {
return (mFlags & FLAG_PLAYABLE) != 0;
}
/**
* Gets the title of the item.
* @more
* The title will be shown as the first line of text when
* describing each item to the user.
*/
public @NonNull CharSequence getTitle() {
return mTitle;
}
/**
* Gets summary of the item, or null if none.
* @more
* The summary will be shown as the second line of text when
* describing each item to the user.
*/
public @Nullable CharSequence getSummary() {
return mSummary;
}
/**
* Gets the Uri of the icon.
*/
public @Nullable Uri getIconUri() {
return mIconUri;
}
/**
* Gets the resource id of the icon.
*/
public @DrawableRes int getIconResourceId() {
return mIconResourceId;
}
/**
* Gets additional service-specified extras about the
* item or its content, or null if none.
*/
public @Nullable Bundle getExtras() {
return mExtras;
}
/**
* Builder for {@link MediaBrowserItem} objects.
*/
public static final class Builder {
private final Uri mUri;
private final int mFlags;
private final CharSequence mTitle;
private CharSequence mSummary;
private Uri mIconUri;
private int mIconResourceId;
private Bundle mExtras;
/**
* Creates an item builder.
*/
public Builder(@NonNull Uri uri, @Flags int flags, @NonNull CharSequence title) {
if (uri == null) {
throw new IllegalArgumentException("uri can not be null");
}
if (title == null) {
throw new IllegalArgumentException("title can not be null");
}
mUri = uri;
mFlags = flags;
mTitle = title;
}
/**
* Sets summary of the item, or null if none.
*/
public @NonNull Builder setSummary(@Nullable CharSequence summary) {
mSummary = summary;
return this;
}
/**
* Sets the uri of the icon.
* <p>
* Either {@link #setIconUri(Uri)} or {@link #setIconResourceId(int)} should be called.
* If both are specified, the resource id will be used to load the icon.
* </p>
*/
public @NonNull Builder setIconUri(@Nullable Uri iconUri) {
mIconUri = iconUri;
return this;
}
/**
* Sets the resource id of the icon.
* <p>
* Either {@link #setIconUri(Uri)} or {@link #setIconResourceId(int)} should be specified.
* If both are specified, the resource id will be used to load the icon.
* </p>
*/
public @NonNull Builder setIconResourceId(@DrawableRes int ResourceId) {
mIconResourceId = ResourceId;
return this;
}
/**
* Sets additional service-specified extras about the
* item or its content.
*/
public @NonNull Builder setExtras(@Nullable Bundle extras) {
mExtras = extras;
return this;
}
/**
* Builds the item.
*/
public @NonNull MediaBrowserItem build() {
return new MediaBrowserItem(mUri, mFlags, mTitle, mSummary, mIconUri,
mIconResourceId, mExtras);
}
}
}

View File

@@ -30,7 +30,7 @@ oneway interface ISessionCallback {
// These callbacks are for the TransportPerformer
void onPlay();
void onPlayUri(in Uri uri, in Bundle extras);
void onPlayFromMediaId(String uri, in Bundle extras);
void onPlayFromSearch(String query, in Bundle extras);
void onSkipToTrack(long id);
void onPause();

View File

@@ -51,9 +51,9 @@ interface ISessionController {
// These commands are for the TransportControls
void play();
void playUri(in Uri uri, in Bundle extras);
void playFromMediaId(String uri, in Bundle extras);
void playFromSearch(String string, in Bundle extras);
void skipToTrack(long id);
void skipToQueueItem(long id);
void pause();
void stop();
void next();

View File

@@ -173,7 +173,7 @@ public final class MediaController {
*
* @return The current play queue or null.
*/
public @Nullable List<MediaSession.Item> getQueue() {
public @Nullable List<MediaSession.QueueItem> getQueue() {
try {
ParceledListSlice queue = mSessionBinder.getQueue();
if (queue != null) {
@@ -538,9 +538,9 @@ public final class MediaController {
* @param queue A list of items in the current play queue. It should
* include the currently playing item as well as previous and
* upcoming items if applicable.
* @see MediaSession.Item
* @see MediaSession.QueueItem
*/
public void onQueueChanged(@Nullable List<MediaSession.Item> queue) {
public void onQueueChanged(@Nullable List<MediaSession.QueueItem> queue) {
}
/**
@@ -594,18 +594,19 @@ public final class MediaController {
/**
* Request that the player start playback for a specific {@link Uri}.
*
* @param uri The uri of the requested media.
* @param mediaId The uri of the requested media.
* @param extras Optional extras that can include extra information about the media item
* to be played.
*/
public void playUri(Uri uri, Bundle extras) {
if (uri == null) {
throw new IllegalArgumentException("You must specify a non-null Uri for playUri.");
public void playFromMediaId(String mediaId, Bundle extras) {
if (TextUtils.isEmpty(mediaId)) {
throw new IllegalArgumentException(
"You must specify a non-empty String for playFromMediaId.");
}
try {
mSessionBinder.playUri(uri, extras);
mSessionBinder.playFromMediaId(mediaId, extras);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling play(" + uri + ").", e);
Log.wtf(TAG, "Error calling play(" + mediaId + ").", e);
}
}
@@ -631,9 +632,9 @@ public final class MediaController {
* Play an item with a specific id in the play queue. If you specify an
* id that is not in the play queue, the behavior is undefined.
*/
public void skipToItem(long id) {
public void skipToQueueItem(long id) {
try {
mSessionBinder.skipToTrack(id);
mSessionBinder.skipToQueueItem(id);
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling skipToItem(" + id + ").", e);
}
@@ -904,7 +905,7 @@ public final class MediaController {
@Override
public void onQueueChanged(ParceledListSlice parceledQueue) {
List<MediaSession.Item> queue = parceledQueue.getList();
List<MediaSession.QueueItem> queue = parceledQueue.getList();
MediaController controller = mController.get();
if (controller != null) {
controller.postMessage(MSG_UPDATE_QUEUE, queue, null);
@@ -960,7 +961,7 @@ public final class MediaController {
mCallback.onMetadataChanged((MediaMetadata) msg.obj);
break;
case MSG_UPDATE_QUEUE:
mCallback.onQueueChanged((List<MediaSession.Item>) msg.obj);
mCallback.onQueueChanged((List<MediaSession.QueueItem>) msg.obj);
break;
case MSG_UPDATE_QUEUE_TITLE:
mCallback.onQueueTitleChanged((CharSequence) msg.obj);

View File

@@ -16,4 +16,4 @@
package android.media.session;
parcelable MediaSession.Token;
parcelable MediaSession.Track;
parcelable MediaSession.QueueItem;

View File

@@ -25,6 +25,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ParceledListSlice;
import android.media.AudioAttributes;
import android.media.MediaDescription;
import android.media.MediaMetadata;
import android.media.Rating;
import android.media.VolumeProvider;
@@ -38,6 +39,7 @@ import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.UserHandle;
import android.service.media.MediaBrowserService;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
@@ -426,9 +428,9 @@ public final class MediaSession {
*
* @param queue A list of items in the play queue.
*/
public void setQueue(@Nullable List<Item> queue) {
public void setQueue(@Nullable List<QueueItem> queue) {
try {
mBinder.setQueue(new ParceledListSlice<Item>(queue));
mBinder.setQueue(new ParceledListSlice<QueueItem>(queue));
} catch (RemoteException e) {
Log.wtf("Dead object in setQueue.", e);
}
@@ -486,8 +488,8 @@ public final class MediaSession {
postToCallback(CallbackMessageHandler.MSG_PLAY);
}
private void dispatchPlayUri(Uri uri, Bundle extras) {
postToCallback(CallbackMessageHandler.MSG_PLAY_URI, uri, extras);
private void dispatchPlayFromMediaId(String mediaId, Bundle extras) {
postToCallback(CallbackMessageHandler.MSG_PLAY_MEDIA_ID, mediaId, extras);
}
private void dispatchPlayFromSearch(String query, Bundle extras) {
@@ -752,9 +754,10 @@ public final class MediaSession {
}
/**
* Override to handle requests to play a specific {@link Uri}.
* Override to handle requests to play a specific mediaId that was
* provided by your app's {@link MediaBrowserService}.
*/
public void onPlayUri(Uri uri, Bundle extras) {
public void onPlayFromMediaId(String mediaId, Bundle extras) {
}
/**
@@ -767,7 +770,7 @@ public final class MediaSession {
* Override to handle requests to play an item with a given id from the
* play queue.
*/
public void onSkipToItem(long id) {
public void onSkipToQueueItem(long id) {
}
/**
@@ -876,10 +879,10 @@ public final class MediaSession {
}
@Override
public void onPlayUri(Uri uri, Bundle extras) {
public void onPlayFromMediaId(String mediaId, Bundle extras) {
MediaSession session = mMediaSession.get();
if (session != null) {
session.dispatchPlayUri(uri, extras);
session.dispatchPlayFromMediaId(mediaId, extras);
}
}
@@ -994,125 +997,59 @@ public final class MediaSession {
}
/**
* A single item that is part of the play queue. It contains information
* necessary to display a single item in the queue.
* A single item that is part of the play queue. It contains a description
* of the item and its id in the queue.
*/
public static final class Item implements Parcelable {
public static final class QueueItem implements Parcelable {
/**
* This id is reserved. No items can be explicitly asigned this id.
*/
public static final int UNKNOWN_ID = -1;
private final MediaMetadata mMetadata;
private final MediaDescription mDescription;
private final long mId;
private final Uri mUri;
private final Bundle mExtras;
/**
* Create a new {@link MediaSession.Item}.
* Create a new {@link MediaSession.QueueItem}.
*
* @param metadata The metadata for this item.
* @param description The {@link MediaDescription} for this item.
* @param id An identifier for this item. It must be unique within the
* play queue.
* @param uri The uri for this item.
* @param extras A bundle of extras that can be used to add extra
* information about this item.
* play queue and cannot be {@link #UNKNOWN_ID}.
*/
private Item(MediaMetadata metadata, long id, Uri uri, Bundle extras) {
mMetadata = metadata;
public QueueItem(MediaDescription description, long id) {
if (description == null) {
throw new IllegalArgumentException("Description cannot be null.");
}
if (id == UNKNOWN_ID) {
throw new IllegalArgumentException("Id cannot be QueueItem.UNKNOWN_ID");
}
mDescription = description;
mId = id;
mUri = uri;
mExtras = extras;
}
private Item(Parcel in) {
mMetadata = MediaMetadata.CREATOR.createFromParcel(in);
private QueueItem(Parcel in) {
mDescription = MediaDescription.CREATOR.createFromParcel(in);
mId = in.readLong();
mUri = Uri.CREATOR.createFromParcel(in);
mExtras = in.readBundle();
}
/**
* Get the metadata for this item.
* Get the description for this item.
*/
public MediaMetadata getMetadata() {
return mMetadata;
public MediaDescription getDescription() {
return mDescription;
}
/**
* Get the id for this item.
* Get the queue id for this item.
*/
public long getId() {
public long getQueueId() {
return mId;
}
/**
* Get the Uri for this item.
*/
public Uri getUri() {
return mUri;
}
/**
* Get the extras for this item.
*/
public Bundle getExtras() {
return mExtras;
}
/**
* Builder for {@link MediaSession.Item} objects.
*/
public static final class Builder {
private final MediaMetadata mMetadata;
private final long mId;
private final Uri mUri;
private Bundle mExtras;
/**
* Create a builder with the metadata, id, and uri already set.
*/
public Builder(MediaMetadata metadata, long id, Uri uri) {
if (metadata == null) {
throw new IllegalArgumentException(
"You must specify a non-null MediaMetadata to build an Item.");
}
if (uri == null) {
throw new IllegalArgumentException(
"You must specify a non-null Uri to build an Item.");
}
if (id == UNKNOWN_ID) {
throw new IllegalArgumentException(
"You must specify an id other than UNKNOWN_ID to build an Item.");
}
mMetadata = metadata;
mId = id;
mUri = uri;
}
/**
* Set optional extras for the item.
*/
public MediaSession.Item.Builder setExtras(Bundle extras) {
mExtras = extras;
return this;
}
/**
* Create the {@link Item}.
*/
public MediaSession.Item build() {
return new MediaSession.Item(mMetadata, mId, mUri, mExtras);
}
}
@Override
public void writeToParcel(Parcel dest, int flags) {
mMetadata.writeToParcel(dest, flags);
mDescription.writeToParcel(dest, flags);
dest.writeLong(mId);
mUri.writeToParcel(dest, flags);
dest.writeBundle(mExtras);
}
@Override
@@ -1120,28 +1057,24 @@ public final class MediaSession {
return 0;
}
public static final Creator<MediaSession.Item> CREATOR
= new Creator<MediaSession.Item>() {
public static final Creator<MediaSession.QueueItem> CREATOR = new Creator<MediaSession.QueueItem>() {
@Override
public MediaSession.Item createFromParcel(Parcel p) {
return new MediaSession.Item(p);
public MediaSession.QueueItem createFromParcel(Parcel p) {
return new MediaSession.QueueItem(p);
}
@Override
public MediaSession.Item[] newArray(int size) {
return new MediaSession.Item[size];
public MediaSession.QueueItem[] newArray(int size) {
return new MediaSession.QueueItem[size];
}
};
@Override
public String toString() {
return "MediaSession.Item {" +
"Metadata=" + mMetadata +
", Id=" + mId +
", Uri=" + mUri +
", Extras=" + mExtras +
" }";
return "MediaSession.QueueItem {" +
"Description=" + mDescription +
", Id=" + mId + " }";
}
}
@@ -1160,7 +1093,7 @@ public final class MediaSession {
private class CallbackMessageHandler extends Handler {
private static final int MSG_PLAY = 1;
private static final int MSG_PLAY_URI = 2;
private static final int MSG_PLAY_MEDIA_ID = 2;
private static final int MSG_PLAY_SEARCH = 3;
private static final int MSG_SKIP_TO_ITEM = 4;
private static final int MSG_PAUSE = 5;
@@ -1206,14 +1139,14 @@ public final class MediaSession {
case MSG_PLAY:
mCallback.onPlay();
break;
case MSG_PLAY_URI:
mCallback.onPlayUri((Uri) msg.obj, msg.getData());
case MSG_PLAY_MEDIA_ID:
mCallback.onPlayFromMediaId((String) msg.obj, msg.getData());
break;
case MSG_PLAY_SEARCH:
mCallback.onPlayFromSearch((String) msg.obj, msg.getData());
break;
case MSG_SKIP_TO_ITEM:
mCallback.onSkipToItem((Long) msg.obj);
mCallback.onSkipToQueueItem((Long) msg.obj);
case MSG_PAUSE:
mCallback.onPause();
break;

View File

@@ -36,95 +36,95 @@ public final class PlaybackState implements Parcelable {
private static final String TAG = "PlaybackState";
/**
* Indicates this performer supports the stop command.
* Indicates this session supports the stop command.
*
* @see Builder#setActions(long)
*/
public static final long ACTION_STOP = 1 << 0;
/**
* Indicates this performer supports the pause command.
* Indicates this session supports the pause command.
*
* @see Builder#setActions(long)
*/
public static final long ACTION_PAUSE = 1 << 1;
/**
* Indicates this performer supports the play command.
* Indicates this session supports the play command.
*
* @see Builder#setActions(long)
*/
public static final long ACTION_PLAY = 1 << 2;
/**
* Indicates this performer supports the rewind command.
* Indicates this session supports the rewind command.
*
* @see Builder#setActions(long)
*/
public static final long ACTION_REWIND = 1 << 3;
/**
* Indicates this performer supports the previous command.
* Indicates this session supports the previous command.
*
* @see Builder#setActions(long)
*/
public static final long ACTION_SKIP_TO_PREVIOUS = 1 << 4;
/**
* Indicates this performer supports the next command.
* Indicates this session supports the next command.
*
* @see Builder#setActions(long)
*/
public static final long ACTION_SKIP_TO_NEXT = 1 << 5;
/**
* Indicates this performer supports the fast forward command.
* Indicates this session supports the fast forward command.
*
* @see Builder#setActions(long)
*/
public static final long ACTION_FAST_FORWARD = 1 << 6;
/**
* Indicates this performer supports the set rating command.
* Indicates this session supports the set rating command.
*
* @see Builder#setActions(long)
*/
public static final long ACTION_SET_RATING = 1 << 7;
/**
* Indicates this performer supports the seek to command.
* Indicates this session supports the seek to command.
*
* @see Builder#setActions(long)
*/
public static final long ACTION_SEEK_TO = 1 << 8;
/**
* Indicates this performer supports the play/pause toggle command.
* Indicates this session supports the play/pause toggle command.
*
* @see Builder#setActions(long)
*/
public static final long ACTION_PLAY_PAUSE = 1 << 9;
/**
* Indicates this performer supports the play from uri command.
* Indicates this session supports the play from media id command.
*
* @see Builder#setActions(long)
*/
public static final long ACTION_PLAY_URI = 1 << 10;
public static final long ACTION_PLAY_FROM_MEDIA_ID = 1 << 10;
/**
* Indicates this performer supports the play from search command.
* Indicates this session supports the play from search command.
*
* @see Builder#setActions(long)
*/
public static final long ACTION_PLAY_FROM_SEARCH = 1 << 11;
/**
* Indicates this performer supports the skip to item command.
* Indicates this session supports the skip to queue item command.
*
* @see Builder#setActions(long)
*/
public static final long ACTION_SKIP_TO_ITEM = 1 << 12;
public static final long ACTION_SKIP_TO_QUEUE_ITEM = 1 << 12;
/**
* This is the default playback state and indicates that no media has been
@@ -210,6 +210,14 @@ public final class PlaybackState implements Parcelable {
*/
public final static int STATE_SKIPPING_TO_NEXT = 10;
/**
* State indicating the player is currently skipping to a specific item in
* the queue.
*
* @see Builder#setState
*/
public final static int STATE_SKIPPING_TO_QUEUE_ITEM = 11;
/**
* Use this value for the position to indicate the position is not known.
*/
@@ -373,6 +381,18 @@ public final class PlaybackState implements Parcelable {
return mUpdateTime;
}
/**
* Get the id of the currently active item in the queue. If there is no
* queue or a queue is not supported by the session this will be
* {@link MediaSession.QueueItem#UNKNOWN_ID}.
*
* @return The id of the currently active item in the queue or
* {@link MediaSession.QueueItem#UNKNOWN_ID}.
*/
public long getActiveQueueItemId() {
return mActiveItemId;
}
/**
* Get the {@link PlaybackState} state for the given
* {@link RemoteControlClient} state.
@@ -716,7 +736,7 @@ public final class PlaybackState implements Parcelable {
private long mActions;
private CharSequence mErrorMessage;
private long mUpdateTime;
private long mActiveItemId = MediaSession.Item.UNKNOWN_ID;
private long mActiveItemId = MediaSession.QueueItem.UNKNOWN_ID;
/**
* Creates an initially empty state builder.
@@ -904,12 +924,12 @@ public final class PlaybackState implements Parcelable {
/**
* Set the active item in the play queue by specifying its id. The
* default value is {@link MediaSession.Item#UNKNOWN_ID}
* default value is {@link MediaSession.QueueItem#UNKNOWN_ID}
*
* @param id The id of the active item.
* @return this
*/
public Builder setActiveItem(long id) {
public Builder setActiveQueueItemId(long id) {
mActiveItemId = id;
return this;
}

View File

@@ -1,9 +1,9 @@
// Copyright 2014 Google Inc. All Rights Reserved.
package android.media.browse;
package android.service.media;
import android.content.res.Configuration;
import android.media.browse.IMediaBrowserServiceCallbacks;
import android.service.media.IMediaBrowserServiceCallbacks;
import android.net.Uri;
import android.os.Bundle;
@@ -18,6 +18,4 @@ oneway interface IMediaBrowserService {
void addSubscription(in Uri uri, IMediaBrowserServiceCallbacks callbacks);
void removeSubscription(in Uri uri, IMediaBrowserServiceCallbacks callbacks);
void loadIcon(in int seqNum, in Uri uri, int width, int height,
IMediaBrowserServiceCallbacks callbacks);
}

View File

@@ -1,6 +1,6 @@
// Copyright 2014 Google Inc. All Rights Reserved.
package android.media.browse;
package android.service.media;
import android.content.pm.ParceledListSlice;
import android.graphics.Bitmap;
@@ -24,5 +24,4 @@ oneway interface IMediaBrowserServiceCallbacks {
void onConnect(in Uri root, in MediaSession.Token session, in Bundle extras);
void onConnectFailed();
void onLoadChildren(in Uri uri, in ParceledListSlice list);
void onLoadIcon(int seqNum, in Bitmap bitmap);
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package android.media.browse;
package android.service.media;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -27,6 +27,8 @@ import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.media.browse.MediaBrowser;
import android.media.browse.MediaBrowser.MediaItem;
import android.media.session.MediaSession;
import android.net.Uri;
import android.os.Binder;
@@ -34,6 +36,9 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.Handler;
import android.os.RemoteException;
import android.service.media.IMediaBrowserService;
import android.service.media.IMediaBrowserServiceCallbacks;
import android.service.media.IMediaBrowserService.Stub;
import android.util.ArrayMap;
import android.util.Log;
@@ -264,59 +269,6 @@ public abstract class MediaBrowserService extends Service {
}
});
}
@Override
public void loadIcon(final int seq, final Uri uri, final int width, final int height,
final IMediaBrowserServiceCallbacks callbacks) {
if (uri == null) {
throw new IllegalStateException("loadIcon sent null list for uri " + uri);
}
mHandler.post(new Runnable() {
@Override
public void run() {
// In theory we could return a result to a new connection, but in practice
// the other side in MediaBrowser uses a new IMediaBrowserServiceCallbacks
// object every time it calls connect(), so as long as it does that we won't
// see results sent for the wrong connection.
final ConnectionRecord connection = mConnections.get(callbacks.asBinder());
if (connection == null) {
if (DBG) {
Log.d(TAG, "Not loading bitmap for invalid connection. uri=" + uri);
}
return;
}
final Result<Bitmap> result = new Result<Bitmap>(uri) {
@Override
void onResultSent(Bitmap bitmap) {
if (mConnections.get(connection.callbacks.asBinder()) != connection) {
if (DBG) {
Log.d(TAG, "Not sending onLoadIcon result for connection"
+ " that has been disconnected. pkg=" + connection.pkg
+ " uri=" + uri);
}
return;
}
try {
callbacks.onLoadIcon(seq, bitmap);
} catch (RemoteException e) {
// The other side is in the process of crashing.
Log.w(TAG, "RemoteException in calling onLoadIcon", e);
}
}
};
onLoadIcon(uri, width, height, result);
if (!result.isDone()) {
throw new IllegalStateException("onLoadIcon must call detach() or"
+ " sendResult() before returning for package=" + connection.pkg
+ " uri=" + uri);
}
}
});
}
}
@Override
@@ -372,26 +324,7 @@ public abstract class MediaBrowserService extends Service {
* @return The list of children, or null if the uri is invalid.
*/
public abstract void onLoadChildren(@NonNull Uri parentUri,
@NonNull Result<List<MediaBrowserItem>> result);
/**
* Called to get the icon of a particular media item.
* <p>
* Implementations must call result.{@link Result#sendResult result.sendResult} with the bitmap.
* If loading the bitmap will be an expensive operation that should be performed
* on another thread, result.{@link Result#detach result.detach} may be called before returning
* from this function, and then {@link Result#sendResult result.sendResult} called when
* the loading is complete.
*
* @param uri The uri of the media item.
* @param width The requested width of the icon in dp.
* @param height The requested height of the icon in dp.
*
* @return The file descriptor of the icon, which may then be loaded
* using a bitmap factory, or null if the item does not have an icon.
*/
public abstract void onLoadIcon(@NonNull Uri uri, int width, int height,
@NonNull Result<Bitmap> result);
@NonNull Result<List<MediaBrowser.MediaItem>> result);
/**
* Call to set the media session.
@@ -478,9 +411,11 @@ public abstract class MediaBrowserService extends Service {
* Callers must make sure that this connection is still connected.
*/
private void performLoadChildren(final Uri uri, final ConnectionRecord connection) {
final Result<List<MediaBrowserItem>> result = new Result<List<MediaBrowserItem>>(uri) {
final Result<List<MediaBrowser.MediaItem>> result
= new Result<List<MediaBrowser.MediaItem>>(
uri) {
@Override
void onResultSent(List<MediaBrowserItem> list) {
void onResultSent(List<MediaBrowser.MediaItem> list) {
if (list == null) {
throw new IllegalStateException("onLoadChildren sent null list for uri " + uri);
}
@@ -492,7 +427,7 @@ public abstract class MediaBrowserService extends Service {
return;
}
final ParceledListSlice<MediaBrowserItem> pls = new ParceledListSlice(list);
final ParceledListSlice<MediaBrowser.MediaItem> pls = new ParceledListSlice(list);
try {
connection.callbacks.onLoadChildren(uri, pls);
} catch (RemoteException ex) {

View File

@@ -23,6 +23,7 @@ import android.content.Intent;
import android.content.pm.ParceledListSlice;
import android.media.AudioManager;
import android.media.AudioManagerInternal;
import android.media.MediaDescription;
import android.media.MediaMetadata;
import android.media.Rating;
import android.media.VolumeProvider;
@@ -441,7 +442,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
private String getShortMetadataString() {
int fields = mMetadata == null ? 0 : mMetadata.size();
MediaMetadata.Description description = mMetadata == null ? null : mMetadata
MediaDescription description = mMetadata == null ? null : mMetadata
.getDescription();
return "size=" + fields + ", description=" + description;
}
@@ -820,9 +821,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
}
}
public void playUri(Uri uri, Bundle extras) {
public void playFromMediaId(String mediaId, Bundle extras) {
try {
mCb.onPlayUri(uri, extras);
mCb.onPlayFromMediaId(mediaId, extras);
} catch (RemoteException e) {
Slog.e(TAG, "Remote failure in playUri.", e);
}
@@ -1042,8 +1043,8 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
}
@Override
public void playUri(Uri uri, Bundle extras) throws RemoteException {
mSessionCb.playUri(uri, extras);
public void playFromMediaId(String mediaId, Bundle extras) throws RemoteException {
mSessionCb.playFromMediaId(mediaId, extras);
}
@Override
@@ -1052,7 +1053,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
}
@Override
public void skipToTrack(long id) {
public void skipToQueueItem(long id) {
mSessionCb.skipToTrack(id);
}

View File

@@ -12,6 +12,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.media.MediaDescription;
import android.media.MediaMetadata;
import android.media.session.MediaController;
import android.media.session.MediaSession;
@@ -185,10 +186,10 @@ public class NotificationHelper extends BroadcastReceiver {
text = "Empty metadata!";
art = null;
} else {
MediaMetadata.Description description = mMetadata.getDescription();
MediaDescription description = mMetadata.getDescription();
title = description.getTitle();
text = description.getSubtitle();
art = description.getIcon();
art = description.getIconBitmap();
}
String playPauseLabel = "";