Revert changes to androidx.media3.session.legacy

This commit is contained in:
Gaëtan Muller 2024-07-17 16:18:07 +02:00 committed by Ian Baker
parent b90f00c774
commit ed15ab012f
14 changed files with 2206 additions and 126 deletions

View File

@ -308,9 +308,10 @@ public class AudioAttributesCompat {
} }
if (Build.VERSION.SDK_INT >= 26) { if (Build.VERSION.SDK_INT >= 26) {
return new AudioAttributesCompat(new AudioAttributesImplApi26((AudioAttributes) aa)); return new AudioAttributesCompat(new AudioAttributesImplApi26((AudioAttributes) aa));
} else { } else if (Build.VERSION.SDK_INT >= 21) {
return new AudioAttributesCompat(new AudioAttributesImplApi21((AudioAttributes) aa)); return new AudioAttributesCompat(new AudioAttributesImplApi21((AudioAttributes) aa));
} }
return null;
} }
// The rest of this file implements an approximation to AudioAttributes using old stream types // The rest of this file implements an approximation to AudioAttributes using old stream types
@ -376,8 +377,10 @@ public class AudioAttributesCompat {
mBuilderImpl = new AudioAttributesImplBase.Builder(); mBuilderImpl = new AudioAttributesImplBase.Builder();
} else if (Build.VERSION.SDK_INT >= 26) { } else if (Build.VERSION.SDK_INT >= 26) {
mBuilderImpl = new AudioAttributesImplApi26.Builder(); mBuilderImpl = new AudioAttributesImplApi26.Builder();
} else { } else if (Build.VERSION.SDK_INT >= 21) {
mBuilderImpl = new AudioAttributesImplApi21.Builder(); mBuilderImpl = new AudioAttributesImplApi21.Builder();
} else {
mBuilderImpl = new AudioAttributesImplBase.Builder();
} }
} }
@ -391,8 +394,10 @@ public class AudioAttributesCompat {
mBuilderImpl = new AudioAttributesImplBase.Builder(aa); mBuilderImpl = new AudioAttributesImplBase.Builder(aa);
} else if (Build.VERSION.SDK_INT >= 26) { } else if (Build.VERSION.SDK_INT >= 26) {
mBuilderImpl = new AudioAttributesImplApi26.Builder(checkNotNull(aa.unwrap())); mBuilderImpl = new AudioAttributesImplApi26.Builder(checkNotNull(aa.unwrap()));
} else { } else if (Build.VERSION.SDK_INT >= 21) {
mBuilderImpl = new AudioAttributesImplApi21.Builder(checkNotNull(aa.unwrap())); mBuilderImpl = new AudioAttributesImplApi21.Builder(checkNotNull(aa.unwrap()));
} else {
mBuilderImpl = new AudioAttributesImplBase.Builder(aa);
} }
} }
@ -938,6 +943,7 @@ public class AudioAttributesCompat {
} }
} }
@RequiresApi(21)
public static class AudioAttributesImplApi21 implements AudioAttributesImpl { public static class AudioAttributesImplApi21 implements AudioAttributesImpl {
@Nullable public AudioAttributes mAudioAttributes; @Nullable public AudioAttributes mAudioAttributes;
@ -1014,6 +1020,7 @@ public class AudioAttributesCompat {
return "AudioAttributesCompat: audioattributes=" + mAudioAttributes; return "AudioAttributesCompat: audioattributes=" + mAudioAttributes;
} }
@RequiresApi(21)
static class Builder implements AudioAttributesImpl.Builder { static class Builder implements AudioAttributesImpl.Builder {
final AudioAttributes.Builder mFwkBuilder; final AudioAttributes.Builder mFwkBuilder;

View File

@ -88,7 +88,7 @@ public final class LegacyParcelableUtil {
// TODO: b/335804969 - Remove this workaround once the bug fix is in the androidx.media dependency // TODO: b/335804969 - Remove this workaround once the bug fix is in the androidx.media dependency
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private static <T> T maybeApplyMediaDescriptionParcelableBugWorkaround(T value) { private static <T> T maybeApplyMediaDescriptionParcelableBugWorkaround(T value) {
if (Util.SDK_INT >= 23) { if (Util.SDK_INT < 21 || Util.SDK_INT >= 23) {
return value; return value;
} }
if (value instanceof android.support.v4.media.MediaBrowserCompat.MediaItem) { if (value instanceof android.support.v4.media.MediaBrowserCompat.MediaItem) {

View File

@ -56,6 +56,7 @@ import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.ServiceConnection; import android.content.ServiceConnection;
import android.media.MediaDescription;
import android.media.browse.MediaBrowser; import android.media.browse.MediaBrowser;
import android.os.BadParcelableException; import android.os.BadParcelableException;
import android.os.Binder; import android.os.Binder;
@ -72,8 +73,8 @@ import android.os.RemoteException;
import android.support.v4.os.ResultReceiver; import android.support.v4.os.ResultReceiver;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import androidx.annotation.DoNotInline;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo; import androidx.annotation.RestrictTo;
@ -204,8 +205,10 @@ public final class MediaBrowserCompat {
mImpl = new MediaBrowserImplApi26(context, serviceComponent, callback, rootHints); mImpl = new MediaBrowserImplApi26(context, serviceComponent, callback, rootHints);
} else if (Build.VERSION.SDK_INT >= 23) { } else if (Build.VERSION.SDK_INT >= 23) {
mImpl = new MediaBrowserImplApi23(context, serviceComponent, callback, rootHints); mImpl = new MediaBrowserImplApi23(context, serviceComponent, callback, rootHints);
} else { } else if (Build.VERSION.SDK_INT >= 21) {
mImpl = new MediaBrowserImplApi21(context, serviceComponent, callback, rootHints); mImpl = new MediaBrowserImplApi21(context, serviceComponent, callback, rootHints);
} else {
mImpl = new MediaBrowserImplBase(context, serviceComponent, callback, rootHints);
} }
} }
@ -455,18 +458,20 @@ public final class MediaBrowserCompat {
* Creates an instance from a framework {@link android.media.browse.MediaBrowser.MediaItem} * Creates an instance from a framework {@link android.media.browse.MediaBrowser.MediaItem}
* object. * object.
* *
* <p>This method is only supported on API 21+. On API 20 and below, it returns null.
*
* @param itemObj A {@link android.media.browse.MediaBrowser.MediaItem} object. * @param itemObj A {@link android.media.browse.MediaBrowser.MediaItem} object.
* @return An equivalent {@link MediaItem} object, or null if none. * @return An equivalent {@link MediaItem} object, or null if none.
*/ */
@Nullable @Nullable
public static MediaItem fromMediaItem(@Nullable Object itemObj) { public static MediaItem fromMediaItem(@Nullable Object itemObj) {
if (itemObj == null) { if (itemObj == null || Build.VERSION.SDK_INT < 21) {
return null; return null;
} }
MediaBrowser.MediaItem itemFwk = (MediaBrowser.MediaItem) itemObj; MediaBrowser.MediaItem itemFwk = (MediaBrowser.MediaItem) itemObj;
int flags = itemFwk.getFlags(); int flags = Api21Impl.getFlags(itemFwk);
MediaDescriptionCompat descriptionCompat = MediaDescriptionCompat descriptionCompat =
MediaDescriptionCompat.fromMediaDescription(itemFwk.getDescription()); MediaDescriptionCompat.fromMediaDescription(Api21Impl.getDescription(itemFwk));
return new MediaItem(descriptionCompat, flags); return new MediaItem(descriptionCompat, flags);
} }
@ -474,12 +479,14 @@ public final class MediaBrowserCompat {
* Creates a list of {@link MediaItem} objects from a framework {@link * Creates a list of {@link MediaItem} objects from a framework {@link
* android.media.browse.MediaBrowser.MediaItem} object list. * android.media.browse.MediaBrowser.MediaItem} object list.
* *
* <p>This method is only supported on API 21+. On API 20 and below, it returns null.
*
* @param itemList A list of {@link android.media.browse.MediaBrowser.MediaItem} objects. * @param itemList A list of {@link android.media.browse.MediaBrowser.MediaItem} objects.
* @return An equivalent list of {@link MediaItem} objects, or null if none. * @return An equivalent list of {@link MediaItem} objects, or null if none.
*/ */
@Nullable @Nullable
public static List<MediaItem> fromMediaItemList(@Nullable List<?> itemList) { public static List<MediaItem> fromMediaItemList(@Nullable List<?> itemList) {
if (itemList == null) { if (itemList == null || Build.VERSION.SDK_INT < 21) {
return null; return null;
} }
List<MediaItem> items = new ArrayList<>(itemList.size()); List<MediaItem> items = new ArrayList<>(itemList.size());
@ -594,7 +601,11 @@ public final class MediaBrowserCompat {
@Nullable ConnectionCallbackInternal mConnectionCallbackInternal; @Nullable ConnectionCallbackInternal mConnectionCallbackInternal;
public ConnectionCallback() { public ConnectionCallback() {
if (Build.VERSION.SDK_INT >= 21) {
mConnectionCallbackFwk = new ConnectionCallbackApi21(); mConnectionCallbackFwk = new ConnectionCallbackApi21();
} else {
mConnectionCallbackFwk = null;
}
} }
/** /**
@ -636,6 +647,7 @@ public final class MediaBrowserCompat {
void onConnectionFailed(); void onConnectionFailed();
} }
@RequiresApi(21)
private class ConnectionCallbackApi21 extends MediaBrowser.ConnectionCallback { private class ConnectionCallbackApi21 extends MediaBrowser.ConnectionCallback {
ConnectionCallbackApi21() {} ConnectionCallbackApi21() {}
@ -667,7 +679,7 @@ public final class MediaBrowserCompat {
/** Callbacks for subscription related events. */ /** Callbacks for subscription related events. */
public abstract static class SubscriptionCallback { public abstract static class SubscriptionCallback {
@NonNull final MediaBrowser.SubscriptionCallback mSubscriptionCallbackFwk; @Nullable final MediaBrowser.SubscriptionCallback mSubscriptionCallbackFwk;
final IBinder mToken; final IBinder mToken;
@Nullable WeakReference<Subscription> mSubscriptionRef; @Nullable WeakReference<Subscription> mSubscriptionRef;
@ -675,8 +687,10 @@ public final class MediaBrowserCompat {
mToken = new Binder(); mToken = new Binder();
if (Build.VERSION.SDK_INT >= 26) { if (Build.VERSION.SDK_INT >= 26) {
mSubscriptionCallbackFwk = new SubscriptionCallbackApi26(); mSubscriptionCallbackFwk = new SubscriptionCallbackApi26();
} else { } else if (Build.VERSION.SDK_INT >= 21) {
mSubscriptionCallbackFwk = new SubscriptionCallbackApi21(); mSubscriptionCallbackFwk = new SubscriptionCallbackApi21();
} else {
mSubscriptionCallbackFwk = null;
} }
} }
@ -724,6 +738,7 @@ public final class MediaBrowserCompat {
mSubscriptionRef = new WeakReference<>(subscription); mSubscriptionRef = new WeakReference<>(subscription);
} }
@RequiresApi(21)
private class SubscriptionCallbackApi21 extends MediaBrowser.SubscriptionCallback { private class SubscriptionCallbackApi21 extends MediaBrowser.SubscriptionCallback {
SubscriptionCallbackApi21() {} SubscriptionCallbackApi21() {}
@ -1628,6 +1643,7 @@ public final class MediaBrowserCompat {
} }
} }
@RequiresApi(21)
static class MediaBrowserImplApi21 static class MediaBrowserImplApi21
implements MediaBrowserImpl, implements MediaBrowserImpl,
MediaBrowserServiceCallbackImpl, MediaBrowserServiceCallbackImpl,
@ -1728,7 +1744,7 @@ public final class MediaBrowserCompat {
if (mServiceBinderWrapper == null) { if (mServiceBinderWrapper == null) {
// TODO: When MediaBrowser is connected to framework's MediaBrowserService, // TODO: When MediaBrowser is connected to framework's MediaBrowserService,
// subscribe with options won't work properly. // subscribe with options won't work properly.
mBrowserFwk.subscribe(parentId, callback.mSubscriptionCallbackFwk); mBrowserFwk.subscribe(parentId, checkNotNull(callback.mSubscriptionCallbackFwk));
} else { } else {
try { try {
mServiceBinderWrapper.addSubscription( mServiceBinderWrapper.addSubscription(
@ -2076,9 +2092,9 @@ public final class MediaBrowserCompat {
// This is to prevent ClassNotFoundException when options has Parcelable in it. // This is to prevent ClassNotFoundException when options has Parcelable in it.
if (mServiceBinderWrapper == null || mServiceVersion < SERVICE_VERSION_2) { if (mServiceBinderWrapper == null || mServiceVersion < SERVICE_VERSION_2) {
if (options == null) { if (options == null) {
mBrowserFwk.subscribe(parentId, callback.mSubscriptionCallbackFwk); mBrowserFwk.subscribe(parentId, checkNotNull(callback.mSubscriptionCallbackFwk));
} else { } else {
mBrowserFwk.subscribe(parentId, options, callback.mSubscriptionCallbackFwk); mBrowserFwk.subscribe(parentId, options, checkNotNull(callback.mSubscriptionCallbackFwk));
} }
} else { } else {
super.subscribe(parentId, options, callback); super.subscribe(parentId, options, callback);
@ -2093,7 +2109,7 @@ public final class MediaBrowserCompat {
if (callback == null) { if (callback == null) {
mBrowserFwk.unsubscribe(parentId); mBrowserFwk.unsubscribe(parentId);
} else { } else {
mBrowserFwk.unsubscribe(parentId, callback.mSubscriptionCallbackFwk); mBrowserFwk.unsubscribe(parentId, checkNotNull(callback.mSubscriptionCallbackFwk));
} }
} else { } else {
super.unsubscribe(parentId, callback); super.unsubscribe(parentId, callback);
@ -2450,4 +2466,19 @@ public final class MediaBrowserCompat {
} }
} }
} }
@RequiresApi(21)
private static class Api21Impl {
private Api21Impl() {}
@DoNotInline
static MediaDescription getDescription(MediaBrowser.MediaItem item) {
return item.getDescription();
}
@DoNotInline
static int getFlags(MediaBrowser.MediaItem item) {
return item.getFlags();
}
}
} }

View File

@ -307,6 +307,7 @@ public abstract class MediaBrowserServiceCompat extends Service {
} }
} }
@RequiresApi(21)
class MediaBrowserServiceImplApi21 implements MediaBrowserServiceImpl { class MediaBrowserServiceImplApi21 implements MediaBrowserServiceImpl {
final List<Bundle> mRootExtrasList = new ArrayList<>(); final List<Bundle> mRootExtrasList = new ArrayList<>();
@MonotonicNonNull MediaBrowserService mServiceFwk; @MonotonicNonNull MediaBrowserService mServiceFwk;
@ -519,6 +520,7 @@ public abstract class MediaBrowserServiceCompat extends Service {
return mCurConnection.browserInfo; return mCurConnection.browserInfo;
} }
@RequiresApi(21)
class MediaBrowserServiceApi21 extends MediaBrowserService { class MediaBrowserServiceApi21 extends MediaBrowserService {
@SuppressWarnings("method.invocation.invalid") // Calling base method from constructor @SuppressWarnings("method.invocation.invalid") // Calling base method from constructor
MediaBrowserServiceApi21(Context context) { MediaBrowserServiceApi21(Context context) {
@ -1266,6 +1268,7 @@ public abstract class MediaBrowserServiceCompat extends Service {
} }
} }
@RequiresApi(21)
@SuppressWarnings({"rawtypes", "unchecked"}) @SuppressWarnings({"rawtypes", "unchecked"})
static class ResultWrapper<T> { static class ResultWrapper<T> {
MediaBrowserService.Result mResultFwk; MediaBrowserService.Result mResultFwk;
@ -1328,8 +1331,10 @@ public abstract class MediaBrowserServiceCompat extends Service {
mImpl = new MediaBrowserServiceImplApi26(); mImpl = new MediaBrowserServiceImplApi26();
} else if (Build.VERSION.SDK_INT >= 23) { } else if (Build.VERSION.SDK_INT >= 23) {
mImpl = new MediaBrowserServiceImplApi23(); mImpl = new MediaBrowserServiceImplApi23();
} else { } else if (Build.VERSION.SDK_INT >= 21) {
mImpl = new MediaBrowserServiceImplApi21(); mImpl = new MediaBrowserServiceImplApi21();
} else {
mImpl = new MediaBrowserServiceImplBase();
} }
mImpl.onCreate(); mImpl.onCreate();
} }

View File

@ -40,7 +40,6 @@ import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.view.KeyEvent; import android.view.KeyEvent;
import androidx.annotation.GuardedBy; import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo; import androidx.annotation.RestrictTo;
@ -143,12 +142,9 @@ public final class MediaControllerCompat {
.getWindow() .getWindow()
.getDecorView() .getDecorView()
.setTag(R.id.media_controller_compat_view_tag, mediaController); .setTag(R.id.media_controller_compat_view_tag, mediaController);
MediaController controllerFwk = null; if (android.os.Build.VERSION.SDK_INT >= 21) {
if (mediaController != null) { MediaControllerImplApi21.setMediaController(activity, mediaController);
Object sessionTokenObj = mediaController.getSessionToken().getToken();
controllerFwk = new MediaController(activity, (MediaSession.Token) sessionTokenObj);
} }
activity.setMediaController(controllerFwk);
} }
/** /**
@ -166,16 +162,11 @@ public final class MediaControllerCompat {
Object tag = activity.getWindow().getDecorView().getTag(R.id.media_controller_compat_view_tag); Object tag = activity.getWindow().getDecorView().getTag(R.id.media_controller_compat_view_tag);
if (tag instanceof MediaControllerCompat) { if (tag instanceof MediaControllerCompat) {
return (MediaControllerCompat) tag; return (MediaControllerCompat) tag;
} else { } else if (android.os.Build.VERSION.SDK_INT >= 21) {
MediaController controllerFwk = activity.getMediaController(); return MediaControllerImplApi21.getMediaController(activity);
if (controllerFwk == null) { }
return null; return null;
} }
MediaSession.Token sessionTokenFwk = controllerFwk.getSessionToken();
return new MediaControllerCompat(
activity, MediaSessionCompat.Token.fromToken(sessionTokenFwk));
}
}
@SuppressWarnings("WeakerAccess") /* synthetic access */ @SuppressWarnings("WeakerAccess") /* synthetic access */
static void validateCustomAction(@Nullable String action, @Nullable Bundle args) { static void validateCustomAction(@Nullable String action, @Nullable Bundle args) {
@ -230,8 +221,10 @@ public final class MediaControllerCompat {
if (Build.VERSION.SDK_INT >= 29) { if (Build.VERSION.SDK_INT >= 29) {
mImpl = new MediaControllerImplApi29(context, sessionToken); mImpl = new MediaControllerImplApi29(context, sessionToken);
} else { } else if (Build.VERSION.SDK_INT >= 21) {
mImpl = new MediaControllerImplApi21(context, sessionToken); mImpl = new MediaControllerImplApi21(context, sessionToken);
} else {
mImpl = new MediaControllerImplBase(sessionToken);
} }
} }
@ -642,6 +635,8 @@ public final class MediaControllerCompat {
/** /**
* Gets the underlying framework {@link android.media.session.MediaController} object. * Gets the underlying framework {@link android.media.session.MediaController} object.
* *
* <p>This method is only supported on API 21+.
*
* @return The underlying {@link android.media.session.MediaController} object, or null if none. * @return The underlying {@link android.media.session.MediaController} object, or null if none.
*/ */
@Nullable @Nullable
@ -654,14 +649,19 @@ public final class MediaControllerCompat {
* #registerCallback} * #registerCallback}
*/ */
public abstract static class Callback implements IBinder.DeathRecipient { public abstract static class Callback implements IBinder.DeathRecipient {
@NonNull final MediaController.Callback mCallbackFwk; @Nullable final MediaController.Callback mCallbackFwk;
@Nullable MessageHandler mHandler; @Nullable MessageHandler mHandler;
@Nullable IMediaControllerCallback mIControllerCallback; @Nullable IMediaControllerCallback mIControllerCallback;
// Sharing this in constructor // Sharing this in constructor
@SuppressWarnings({"assignment.type.incompatible", "argument.type.incompatible"}) @SuppressWarnings({"assignment.type.incompatible", "argument.type.incompatible"})
public Callback() { public Callback() {
if (android.os.Build.VERSION.SDK_INT >= 21) {
mCallbackFwk = new MediaControllerCallbackApi21(this); mCallbackFwk = new MediaControllerCallbackApi21(this);
} else {
mCallbackFwk = null;
mIControllerCallback = new StubCompat(this);
}
} }
/** /**
@ -789,6 +789,7 @@ public final class MediaControllerCompat {
} }
// Callback methods in this class are run on handler which was given to registerCallback(). // Callback methods in this class are run on handler which was given to registerCallback().
@RequiresApi(21)
private static class MediaControllerCallbackApi21 extends MediaController.Callback { private static class MediaControllerCallbackApi21 extends MediaController.Callback {
private final WeakReference<MediaControllerCompat.Callback> mCallback; private final WeakReference<MediaControllerCompat.Callback> mCallback;
@ -1973,6 +1974,7 @@ public final class MediaControllerCompat {
} }
} }
@RequiresApi(21)
static class MediaControllerImplApi21 implements MediaControllerImpl { static class MediaControllerImplApi21 implements MediaControllerImpl {
protected final MediaController mControllerFwk; protected final MediaController mControllerFwk;
@ -2000,7 +2002,7 @@ public final class MediaControllerCompat {
@Override @Override
public final void registerCallback(Callback callback, Handler handler) { public final void registerCallback(Callback callback, Handler handler) {
mControllerFwk.registerCallback(callback.mCallbackFwk, handler); mControllerFwk.registerCallback(checkNotNull(callback.mCallbackFwk), handler);
synchronized (mLock) { synchronized (mLock) {
IMediaSession extraBinder = mSessionToken.getExtraBinder(); IMediaSession extraBinder = mSessionToken.getExtraBinder();
if (extraBinder != null) { if (extraBinder != null) {
@ -2022,7 +2024,7 @@ public final class MediaControllerCompat {
@Override @Override
public final void unregisterCallback(Callback callback) { public final void unregisterCallback(Callback callback) {
mControllerFwk.unregisterCallback(callback.mCallbackFwk); mControllerFwk.unregisterCallback(checkNotNull(callback.mCallbackFwk));
synchronized (mLock) { synchronized (mLock) {
IMediaSession extraBinder = mSessionToken.getExtraBinder(); IMediaSession extraBinder = mSessionToken.getExtraBinder();
if (extraBinder != null) { if (extraBinder != null) {
@ -2149,7 +2151,7 @@ public final class MediaControllerCompat {
@Override @Override
public int getRatingType() { public int getRatingType() {
if (android.os.Build.VERSION.SDK_INT == 21) { if (android.os.Build.VERSION.SDK_INT < 22) {
try { try {
IMediaSession extraBinder = mSessionToken.getExtraBinder(); IMediaSession extraBinder = mSessionToken.getExtraBinder();
if (extraBinder != null) { if (extraBinder != null) {
@ -2302,6 +2304,27 @@ public final class MediaControllerCompat {
mPendingCallbacks.clear(); mPendingCallbacks.clear();
} }
@SuppressWarnings("argument.type.incompatible") // Activity.setMediaController is not annotated
static void setMediaController(Activity activity, MediaControllerCompat mediaControllerCompat) {
MediaController controllerFwk = null;
if (mediaControllerCompat != null) {
Object sessionTokenObj = mediaControllerCompat.getSessionToken().getToken();
controllerFwk = new MediaController(activity, (MediaSession.Token) sessionTokenObj);
}
activity.setMediaController(controllerFwk);
}
@Nullable
static MediaControllerCompat getMediaController(Activity activity) {
MediaController controllerFwk = activity.getMediaController();
if (controllerFwk == null) {
return null;
}
MediaSession.Token sessionTokenFwk = controllerFwk.getSessionToken();
return new MediaControllerCompat(
activity, MediaSessionCompat.Token.fromToken(sessionTokenFwk));
}
private static class ExtraBinderRequestResultReceiver extends ResultReceiver { private static class ExtraBinderRequestResultReceiver extends ResultReceiver {
private WeakReference<MediaControllerImplApi21> mMediaControllerImpl; private WeakReference<MediaControllerImplApi21> mMediaControllerImpl;
@ -2388,6 +2411,7 @@ public final class MediaControllerCompat {
} }
} }
@RequiresApi(21)
static class TransportControlsApi21 extends TransportControls { static class TransportControlsApi21 extends TransportControls {
protected final MediaController.TransportControls mControlsFwk; protected final MediaController.TransportControls mControlsFwk;

View File

@ -294,8 +294,19 @@ public final class MediaDescriptionCompat implements Parcelable {
@Override @Override
public void writeToParcel(Parcel dest, int flags) { public void writeToParcel(Parcel dest, int flags) {
if (Build.VERSION.SDK_INT < 21) {
dest.writeString(mMediaId);
TextUtils.writeToParcel(mTitle, dest, flags);
TextUtils.writeToParcel(mSubtitle, dest, flags);
TextUtils.writeToParcel(mDescription, dest, flags);
dest.writeParcelable(mIcon, flags);
dest.writeParcelable(mIconUri, flags);
dest.writeBundle(mExtras);
dest.writeParcelable(mMediaUri, flags);
} else {
((MediaDescription) getMediaDescription()).writeToParcel(dest, flags); ((MediaDescription) getMediaDescription()).writeToParcel(dest, flags);
} }
}
@Override @Override
public String toString() { public String toString() {
@ -305,19 +316,22 @@ public final class MediaDescriptionCompat implements Parcelable {
/** /**
* Gets the underlying framework {@link android.media.MediaDescription} object. * Gets the underlying framework {@link android.media.MediaDescription} object.
* *
* <p>This method is only supported on {@link android.os.Build.VERSION_CODES#LOLLIPOP} and later.
*
* @return An equivalent {@link android.media.MediaDescription} object, or null if none. * @return An equivalent {@link android.media.MediaDescription} object, or null if none.
*/ */
@RequiresApi(21)
public Object getMediaDescription() { public Object getMediaDescription() {
if (mDescriptionFwk != null) { if (mDescriptionFwk != null) {
return mDescriptionFwk; return mDescriptionFwk;
} }
MediaDescription.Builder bob = new MediaDescription.Builder() MediaDescription.Builder bob = Api21Impl.createBuilder();
.setMediaId(mMediaId) Api21Impl.setMediaId(bob, mMediaId);
.setTitle(mTitle) Api21Impl.setTitle(bob, mTitle);
.setSubtitle(mSubtitle) Api21Impl.setSubtitle(bob, mSubtitle);
.setDescription(mDescription) Api21Impl.setDescription(bob, mDescription);
.setIconBitmap(mIcon) Api21Impl.setIconBitmap(bob, mIcon);
.setIconUri(mIconUri); Api21Impl.setIconUri(bob, mIconUri);
// Media URI was not added until API 23, so add it to the Bundle of extras to // Media URI was not added until API 23, so add it to the Bundle of extras to
// ensure the data is not lost - this ensures that // ensure the data is not lost - this ensures that
// fromMediaDescription(getMediaDescription(mediaDescriptionCompat)) returns // fromMediaDescription(getMediaDescription(mediaDescriptionCompat)) returns
@ -331,14 +345,14 @@ public final class MediaDescriptionCompat implements Parcelable {
extras = new Bundle(mExtras); extras = new Bundle(mExtras);
} }
extras.putParcelable(DESCRIPTION_KEY_MEDIA_URI, mMediaUri); extras.putParcelable(DESCRIPTION_KEY_MEDIA_URI, mMediaUri);
bob.setExtras(extras); Api21Impl.setExtras(bob, extras);
} else { } else {
bob.setExtras(mExtras); Api21Impl.setExtras(bob, mExtras);
} }
if (Build.VERSION.SDK_INT >= 23) { if (Build.VERSION.SDK_INT >= 23) {
Api23Impl.setMediaUri(bob, mMediaUri); Api23Impl.setMediaUri(bob, mMediaUri);
} }
mDescriptionFwk = bob.build(); mDescriptionFwk = Api21Impl.build(bob);
return mDescriptionFwk; return mDescriptionFwk;
} }
@ -346,22 +360,24 @@ public final class MediaDescriptionCompat implements Parcelable {
/** /**
* Creates an instance from a framework {@link android.media.MediaDescription} object. * Creates an instance from a framework {@link android.media.MediaDescription} object.
* *
* <p>This method is only supported on API 21+.
*
* @param descriptionObj A {@link android.media.MediaDescription} object, or null if none. * @param descriptionObj A {@link android.media.MediaDescription} object, or null if none.
* @return An equivalent {@link MediaMetadataCompat} object, or null if none. * @return An equivalent {@link MediaMetadataCompat} object, or null if none.
*/ */
@Nullable @Nullable
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public static MediaDescriptionCompat fromMediaDescription(Object descriptionObj) { public static MediaDescriptionCompat fromMediaDescription(Object descriptionObj) {
if (descriptionObj != null) { if (descriptionObj != null && Build.VERSION.SDK_INT >= 21) {
Builder bob = new Builder(); Builder bob = new Builder();
MediaDescription description = (MediaDescription) descriptionObj; MediaDescription description = (MediaDescription) descriptionObj;
bob.setMediaId(description.getMediaId()); bob.setMediaId(Api21Impl.getMediaId(description));
bob.setTitle(description.getTitle()); bob.setTitle(Api21Impl.getTitle(description));
bob.setSubtitle(description.getSubtitle()); bob.setSubtitle(Api21Impl.getSubtitle(description));
bob.setDescription(description.getDescription()); bob.setDescription(Api21Impl.getDescription(description));
bob.setIconBitmap(description.getIconBitmap()); bob.setIconBitmap(Api21Impl.getIconBitmap(description));
bob.setIconUri(description.getIconUri()); bob.setIconUri(Api21Impl.getIconUri(description));
Bundle extras = description.getExtras(); Bundle extras = Api21Impl.getExtras(description);
extras = MediaSessionCompat.unparcelWithClassLoader(extras); extras = MediaSessionCompat.unparcelWithClassLoader(extras);
if (extras != null) { if (extras != null) {
extras = new Bundle(extras); extras = new Bundle(extras);
@ -403,9 +419,13 @@ public final class MediaDescriptionCompat implements Parcelable {
new Parcelable.Creator<MediaDescriptionCompat>() { new Parcelable.Creator<MediaDescriptionCompat>() {
@Override @Override
public MediaDescriptionCompat createFromParcel(Parcel in) { public MediaDescriptionCompat createFromParcel(Parcel in) {
if (Build.VERSION.SDK_INT < 21) {
return new MediaDescriptionCompat(in);
} else {
return checkNotNull( return checkNotNull(
fromMediaDescription(MediaDescription.CREATOR.createFromParcel(in))); fromMediaDescription(MediaDescription.CREATOR.createFromParcel(in)));
} }
}
@Override @Override
public MediaDescriptionCompat[] newArray(int size) { public MediaDescriptionCompat[] newArray(int size) {
@ -526,6 +546,99 @@ public final class MediaDescriptionCompat implements Parcelable {
} }
} }
@RequiresApi(21)
private static class Api21Impl {
private Api21Impl() {}
@DoNotInline
static MediaDescription.Builder createBuilder() {
return new MediaDescription.Builder();
}
@DoNotInline
static void setMediaId(MediaDescription.Builder builder, @Nullable String mediaId) {
builder.setMediaId(mediaId);
}
@DoNotInline
static void setTitle(MediaDescription.Builder builder, @Nullable CharSequence title) {
builder.setTitle(title);
}
@DoNotInline
static void setSubtitle(MediaDescription.Builder builder, @Nullable CharSequence subtitle) {
builder.setSubtitle(subtitle);
}
@DoNotInline
static void setDescription(
MediaDescription.Builder builder, @Nullable CharSequence description) {
builder.setDescription(description);
}
@DoNotInline
static void setIconBitmap(MediaDescription.Builder builder, @Nullable Bitmap icon) {
builder.setIconBitmap(icon);
}
@DoNotInline
static void setIconUri(MediaDescription.Builder builder, @Nullable Uri iconUri) {
builder.setIconUri(iconUri);
}
@DoNotInline
static void setExtras(MediaDescription.Builder builder, @Nullable Bundle extras) {
builder.setExtras(extras);
}
@DoNotInline
static MediaDescription build(MediaDescription.Builder builder) {
return builder.build();
}
@DoNotInline
@Nullable
static String getMediaId(MediaDescription description) {
return description.getMediaId();
}
@DoNotInline
@Nullable
static CharSequence getTitle(MediaDescription description) {
return description.getTitle();
}
@DoNotInline
@Nullable
static CharSequence getSubtitle(MediaDescription description) {
return description.getSubtitle();
}
@DoNotInline
@Nullable
static CharSequence getDescription(MediaDescription description) {
return description.getDescription();
}
@DoNotInline
@Nullable
static Bitmap getIconBitmap(MediaDescription description) {
return description.getIconBitmap();
}
@DoNotInline
@Nullable
static Uri getIconUri(MediaDescription description) {
return description.getIconUri();
}
@DoNotInline
@Nullable
static Bundle getExtras(MediaDescription description) {
return description.getExtras();
}
}
@RequiresApi(23) @RequiresApi(23)
private static class Api23Impl { private static class Api23Impl {
private Api23Impl() {} private Api23Impl() {}

View File

@ -22,12 +22,14 @@ import android.annotation.SuppressLint;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.media.MediaMetadata; import android.media.MediaMetadata;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo; import androidx.annotation.RestrictTo;
import androidx.annotation.StringDef; import androidx.annotation.StringDef;
import androidx.collection.ArrayMap; import androidx.collection.ArrayMap;
@ -555,12 +557,14 @@ public final class MediaMetadataCompat implements Parcelable {
/** /**
* Creates an instance from a framework {@link android.media.MediaMetadata} object. * Creates an instance from a framework {@link android.media.MediaMetadata} object.
* *
* <p>This method is only supported on {@link android.os.Build.VERSION_CODES#LOLLIPOP} and later.
*
* @param metadataObj A {@link android.media.MediaMetadata} object, or null if none. * @param metadataObj A {@link android.media.MediaMetadata} object, or null if none.
* @return An equivalent {@link MediaMetadataCompat} object, or null if none. * @return An equivalent {@link MediaMetadataCompat} object, or null if none.
*/ */
@Nullable @Nullable
public static MediaMetadataCompat fromMediaMetadata(@Nullable Object metadataObj) { public static MediaMetadataCompat fromMediaMetadata(@Nullable Object metadataObj) {
if (metadataObj != null) { if (metadataObj != null && Build.VERSION.SDK_INT >= 21) {
Parcel p = Parcel.obtain(); Parcel p = Parcel.obtain();
((MediaMetadata) metadataObj).writeToParcel(p, 0); ((MediaMetadata) metadataObj).writeToParcel(p, 0);
p.setDataPosition(0); p.setDataPosition(0);
@ -576,8 +580,11 @@ public final class MediaMetadataCompat implements Parcelable {
/** /**
* Gets the underlying framework {@link android.media.MediaMetadata} object. * Gets the underlying framework {@link android.media.MediaMetadata} object.
* *
* <p>This method is only supported on {@link android.os.Build.VERSION_CODES#LOLLIPOP} and later.
*
* @return An equivalent {@link android.media.MediaMetadata} object, or null if none. * @return An equivalent {@link android.media.MediaMetadata} object, or null if none.
*/ */
@RequiresApi(21)
public Object getMediaMetadata() { public Object getMediaMetadata() {
if (mMetadataFwk == null) { if (mMetadataFwk == null) {
Parcel p = Parcel.obtain(); Parcel p = Parcel.obtain();

View File

@ -71,8 +71,10 @@ public final class MediaSessionManager {
private MediaSessionManager(Context context) { private MediaSessionManager(Context context) {
if (Build.VERSION.SDK_INT >= 28) { if (Build.VERSION.SDK_INT >= 28) {
mImpl = new MediaSessionManagerImplApi28(context); mImpl = new MediaSessionManagerImplApi28(context);
} else { } else if (Build.VERSION.SDK_INT >= 21) {
mImpl = new MediaSessionManagerImplApi21(context); mImpl = new MediaSessionManagerImplApi21(context);
} else {
mImpl = new MediaSessionManagerImplBase(context);
} }
} }
@ -371,6 +373,7 @@ public final class MediaSessionManager {
} }
} }
@RequiresApi(21)
private static class MediaSessionManagerImplApi21 extends MediaSessionManagerImplBase { private static class MediaSessionManagerImplApi21 extends MediaSessionManagerImplBase {
MediaSessionManagerImplApi21(Context context) { MediaSessionManagerImplApi21(Context context) {
super(context); super(context);

View File

@ -840,14 +840,16 @@ public final class PlaybackStateCompat implements Parcelable {
/** /**
* Creates an instance from a framework {@link android.media.session.PlaybackState} object. * Creates an instance from a framework {@link android.media.session.PlaybackState} object.
* *
* <p>This method is only supported on API 21+.
*
* @param stateObj A {@link android.media.session.PlaybackState} object, or null if none. * @param stateObj A {@link android.media.session.PlaybackState} object, or null if none.
* @return An equivalent {@link PlaybackStateCompat} object, or null if none. * @return An equivalent {@link PlaybackStateCompat} object, or null if none.
*/ */
@Nullable @Nullable
public static PlaybackStateCompat fromPlaybackState(@Nullable Object stateObj) { public static PlaybackStateCompat fromPlaybackState(@Nullable Object stateObj) {
if (stateObj != null) { if (stateObj != null && Build.VERSION.SDK_INT >= 21) {
PlaybackState stateFwk = (PlaybackState) stateObj; PlaybackState stateFwk = (PlaybackState) stateObj;
List<PlaybackState.CustomAction> customActionFwks = stateFwk.getCustomActions(); List<PlaybackState.CustomAction> customActionFwks = Api21Impl.getCustomActions(stateFwk);
List<PlaybackStateCompat.CustomAction> customActions = null; List<PlaybackStateCompat.CustomAction> customActions = null;
if (customActionFwks != null) { if (customActionFwks != null) {
customActions = new ArrayList<>(customActionFwks.size()); customActions = new ArrayList<>(customActionFwks.size());
@ -867,16 +869,16 @@ public final class PlaybackStateCompat implements Parcelable {
} }
PlaybackStateCompat stateCompat = PlaybackStateCompat stateCompat =
new PlaybackStateCompat( new PlaybackStateCompat(
stateFwk.getState(), Api21Impl.getState(stateFwk),
stateFwk.getPosition(), Api21Impl.getPosition(stateFwk),
stateFwk.getBufferedPosition(), Api21Impl.getBufferedPosition(stateFwk),
stateFwk.getPlaybackSpeed(), Api21Impl.getPlaybackSpeed(stateFwk),
stateFwk.getActions(), Api21Impl.getActions(stateFwk),
ERROR_CODE_UNKNOWN_ERROR, ERROR_CODE_UNKNOWN_ERROR,
stateFwk.getErrorMessage(), Api21Impl.getErrorMessage(stateFwk),
stateFwk.getLastPositionUpdateTime(), Api21Impl.getLastPositionUpdateTime(stateFwk),
customActions, customActions,
stateFwk.getActiveQueueItemId(), Api21Impl.getActiveQueueItemId(stateFwk),
extras); extras);
stateCompat.mStateFwk = stateFwk; stateCompat.mStateFwk = stateFwk;
return stateCompat; return stateCompat;
@ -888,28 +890,30 @@ public final class PlaybackStateCompat implements Parcelable {
/** /**
* Gets the underlying framework {@link android.media.session.PlaybackState} object. * Gets the underlying framework {@link android.media.session.PlaybackState} object.
* *
* <p>This method is only supported on API 21+.
*
* @return An equivalent {@link android.media.session.PlaybackState} object, or null if none. * @return An equivalent {@link android.media.session.PlaybackState} object, or null if none.
*/ */
@Nullable @Nullable
public Object getPlaybackState() { public Object getPlaybackState() {
if (mStateFwk == null) { if (mStateFwk == null && Build.VERSION.SDK_INT >= 21) {
PlaybackState.Builder builder = new PlaybackState.Builder() PlaybackState.Builder builder = Api21Impl.createBuilder();
.setState(mState, mPosition, mSpeed, mUpdateTime) Api21Impl.setState(builder, mState, mPosition, mSpeed, mUpdateTime);
.setBufferedPosition(mBufferedPosition) Api21Impl.setBufferedPosition(builder, mBufferedPosition);
.setActions(mActions) Api21Impl.setActions(builder, mActions);
.setErrorMessage(mErrorMessage); Api21Impl.setErrorMessage(builder, mErrorMessage);
for (PlaybackStateCompat.CustomAction customAction : mCustomActions) { for (PlaybackStateCompat.CustomAction customAction : mCustomActions) {
PlaybackState.CustomAction action = PlaybackState.CustomAction action =
(PlaybackState.CustomAction) customAction.getCustomAction(); (PlaybackState.CustomAction) customAction.getCustomAction();
if (action != null) { if (action != null) {
builder.addCustomAction(action); Api21Impl.addCustomAction(builder, action);
} }
} }
builder.setActiveQueueItemId(mActiveItemId); Api21Impl.setActiveQueueItemId(builder, mActiveItemId);
if (Build.VERSION.SDK_INT >= 22) { if (Build.VERSION.SDK_INT >= 22) {
Api22Impl.setExtras(builder, mExtras); Api22Impl.setExtras(builder, mExtras);
} }
mStateFwk = builder.build(); mStateFwk = Api21Impl.build(builder);
} }
return mStateFwk; return mStateFwk;
} }
@ -972,19 +976,22 @@ public final class PlaybackStateCompat implements Parcelable {
* Creates an instance from a framework {@link android.media.session.PlaybackState.CustomAction} * Creates an instance from a framework {@link android.media.session.PlaybackState.CustomAction}
* object. * object.
* *
* <p>This method is only supported on API 21+.
*
* @param customActionObj A {@link android.media.session.PlaybackState.CustomAction} object, or * @param customActionObj A {@link android.media.session.PlaybackState.CustomAction} object, or
* null if none. * null if none.
* @return An equivalent {@link PlaybackStateCompat.CustomAction} object, or null if none. * @return An equivalent {@link PlaybackStateCompat.CustomAction} object, or null if none.
*/ */
@RequiresApi(21)
public static PlaybackStateCompat.CustomAction fromCustomAction(Object customActionObj) { public static PlaybackStateCompat.CustomAction fromCustomAction(Object customActionObj) {
PlaybackState.CustomAction customActionFwk = (PlaybackState.CustomAction) customActionObj; PlaybackState.CustomAction customActionFwk = (PlaybackState.CustomAction) customActionObj;
Bundle extras = customActionFwk.getExtras(); Bundle extras = Api21Impl.getExtras(customActionFwk);
MediaSessionCompat.ensureClassLoader(extras); MediaSessionCompat.ensureClassLoader(extras);
PlaybackStateCompat.CustomAction customActionCompat = PlaybackStateCompat.CustomAction customActionCompat =
new PlaybackStateCompat.CustomAction( new PlaybackStateCompat.CustomAction(
customActionFwk.getAction(), Api21Impl.getAction(customActionFwk),
customActionFwk.getName(), Api21Impl.getName(customActionFwk),
customActionFwk.getIcon(), Api21Impl.getIcon(customActionFwk),
extras); extras);
customActionCompat.mCustomActionFwk = customActionFwk; customActionCompat.mCustomActionFwk = customActionFwk;
return customActionCompat; return customActionCompat;
@ -994,18 +1001,21 @@ public final class PlaybackStateCompat implements Parcelable {
* Gets the underlying framework {@link android.media.session.PlaybackState.CustomAction} * Gets the underlying framework {@link android.media.session.PlaybackState.CustomAction}
* object. * object.
* *
* <p>This method is only supported on API 21+.
*
* @return An equivalent {@link android.media.session.PlaybackState.CustomAction} object, or * @return An equivalent {@link android.media.session.PlaybackState.CustomAction} object, or
* null if none. * null if none.
*/ */
@Nullable @Nullable
public Object getCustomAction() { public Object getCustomAction() {
if (mCustomActionFwk != null) { if (mCustomActionFwk != null || Build.VERSION.SDK_INT < 21) {
return mCustomActionFwk; return mCustomActionFwk;
} }
return new PlaybackState.CustomAction.Builder(mAction, mName, mIcon) PlaybackState.CustomAction.Builder builder =
.setExtras(mExtras) Api21Impl.createCustomActionBuilder(mAction, mName, mIcon);
.build(); Api21Impl.setExtras(builder, mExtras);
return Api21Impl.build(builder);
} }
public static final Parcelable.Creator<PlaybackStateCompat.CustomAction> CREATOR = public static final Parcelable.Creator<PlaybackStateCompat.CustomAction> CREATOR =
@ -1391,6 +1401,142 @@ public final class PlaybackStateCompat implements Parcelable {
} }
} }
@RequiresApi(21)
private static class Api21Impl {
private Api21Impl() {}
@DoNotInline
static PlaybackState.Builder createBuilder() {
return new PlaybackState.Builder();
}
@DoNotInline
static void setState(
PlaybackState.Builder builder,
int state,
long position,
float playbackSpeed,
long updateTime) {
builder.setState(state, position, playbackSpeed, updateTime);
}
@DoNotInline
static void setBufferedPosition(PlaybackState.Builder builder, long bufferedPosition) {
builder.setBufferedPosition(bufferedPosition);
}
@DoNotInline
static void setActions(PlaybackState.Builder builder, long actions) {
builder.setActions(actions);
}
@SuppressWarnings("argument.type.incompatible") // Platform class not annotated as nullable
@DoNotInline
static void setErrorMessage(PlaybackState.Builder builder, @Nullable CharSequence error) {
builder.setErrorMessage(error);
}
@DoNotInline
static void addCustomAction(
PlaybackState.Builder builder, PlaybackState.CustomAction customAction) {
builder.addCustomAction(customAction);
}
@DoNotInline
static void setActiveQueueItemId(PlaybackState.Builder builder, long id) {
builder.setActiveQueueItemId(id);
}
@DoNotInline
static List<PlaybackState.CustomAction> getCustomActions(PlaybackState state) {
return state.getCustomActions();
}
@DoNotInline
static PlaybackState build(PlaybackState.Builder builder) {
return builder.build();
}
@DoNotInline
static int getState(PlaybackState state) {
return state.getState();
}
@DoNotInline
static long getPosition(PlaybackState state) {
return state.getPosition();
}
@DoNotInline
static long getBufferedPosition(PlaybackState state) {
return state.getBufferedPosition();
}
@DoNotInline
static float getPlaybackSpeed(PlaybackState state) {
return state.getPlaybackSpeed();
}
@DoNotInline
static long getActions(PlaybackState state) {
return state.getActions();
}
@Nullable
@DoNotInline
static CharSequence getErrorMessage(PlaybackState state) {
return state.getErrorMessage();
}
@DoNotInline
static long getLastPositionUpdateTime(PlaybackState state) {
return state.getLastPositionUpdateTime();
}
@DoNotInline
static long getActiveQueueItemId(PlaybackState state) {
return state.getActiveQueueItemId();
}
@DoNotInline
static PlaybackState.CustomAction.Builder createCustomActionBuilder(
String action, CharSequence name, int icon) {
return new PlaybackState.CustomAction.Builder(action, name, icon);
}
@SuppressWarnings("argument.type.incompatible") // Platform class not annotated as nullable
@DoNotInline
static void setExtras(PlaybackState.CustomAction.Builder builder, @Nullable Bundle extras) {
builder.setExtras(extras);
}
@DoNotInline
static PlaybackState.CustomAction build(PlaybackState.CustomAction.Builder builder) {
return builder.build();
}
@Nullable
@DoNotInline
static Bundle getExtras(PlaybackState.CustomAction customAction) {
return customAction.getExtras();
}
@DoNotInline
static String getAction(PlaybackState.CustomAction customAction) {
return customAction.getAction();
}
@DoNotInline
static CharSequence getName(PlaybackState.CustomAction customAction) {
return customAction.getName();
}
@DoNotInline
static int getIcon(PlaybackState.CustomAction customAction) {
return customAction.getIcon();
}
}
@RequiresApi(22) @RequiresApi(22)
private static class Api22Impl { private static class Api22Impl {
private Api22Impl() {} private Api22Impl() {}

View File

@ -323,6 +323,8 @@ public final class RatingCompat implements Parcelable {
/** /**
* Creates an instance from a framework {@link android.media.Rating} object. * Creates an instance from a framework {@link android.media.Rating} object.
* *
* <p>This method is only supported on API 19+.
*
* @param ratingObj A {@link android.media.Rating} object, or null if none. * @param ratingObj A {@link android.media.Rating} object, or null if none.
* @return An equivalent {@link RatingCompat} object, or null if none. * @return An equivalent {@link RatingCompat} object, or null if none.
*/ */
@ -364,6 +366,8 @@ public final class RatingCompat implements Parcelable {
/** /**
* Gets the underlying framework {@link android.media.Rating} object. * Gets the underlying framework {@link android.media.Rating} object.
* *
* <p>This method is only supported on API 19+.
*
* @return An equivalent {@link android.media.Rating} object, or null if none. * @return An equivalent {@link android.media.Rating} object, or null if none.
*/ */
@Nullable @Nullable

View File

@ -19,8 +19,10 @@ import static androidx.annotation.RestrictTo.Scope.LIBRARY;
import android.media.VolumeProvider; import android.media.VolumeProvider;
import android.os.Build; import android.os.Build;
import androidx.annotation.DoNotInline;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo; import androidx.annotation.RestrictTo;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
@ -59,6 +61,7 @@ public abstract class VolumeProviderCompat {
private final int mMaxVolume; private final int mMaxVolume;
@Nullable private final String mControlId; @Nullable private final String mControlId;
private int mCurrentVolume; private int mCurrentVolume;
@Nullable private Callback mCallback;
@Nullable private VolumeProvider mVolumeProviderFwk; @Nullable private VolumeProvider mVolumeProviderFwk;
@ -129,7 +132,13 @@ public abstract class VolumeProviderCompat {
*/ */
public final void setCurrentVolume(int currentVolume) { public final void setCurrentVolume(int currentVolume) {
mCurrentVolume = currentVolume; mCurrentVolume = currentVolume;
getVolumeProvider().setCurrentVolume(currentVolume); if (Build.VERSION.SDK_INT >= 21) {
VolumeProvider volumeProviderFwk = (VolumeProvider) getVolumeProvider();
Api21Impl.setCurrentVolume(volumeProviderFwk, currentVolume);
}
if (mCallback != null) {
mCallback.onVolumeChanged(this);
}
} }
/** /**
@ -157,12 +166,24 @@ public abstract class VolumeProviderCompat {
*/ */
public void onAdjustVolume(int direction) {} public void onAdjustVolume(int direction) {}
/**
* Sets a callback to receive volume changes.
*
* <p>Used internally by the support library.
*/
public void setCallback(@Nullable Callback callback) {
mCallback = callback;
}
/** /**
* Gets the underlying framework {@link android.media.VolumeProvider} object. * Gets the underlying framework {@link android.media.VolumeProvider} object.
* *
* <p>This method is only supported on API 21+.
*
* @return An equivalent {@link android.media.VolumeProvider} object, or null if none. * @return An equivalent {@link android.media.VolumeProvider} object, or null if none.
*/ */
public VolumeProvider getVolumeProvider() { @RequiresApi(21)
public Object getVolumeProvider() {
if (mVolumeProviderFwk == null) { if (mVolumeProviderFwk == null) {
if (Build.VERSION.SDK_INT >= 30) { if (Build.VERSION.SDK_INT >= 30) {
mVolumeProviderFwk = mVolumeProviderFwk =
@ -194,4 +215,19 @@ public abstract class VolumeProviderCompat {
} }
return mVolumeProviderFwk; return mVolumeProviderFwk;
} }
/** Listens for changes to the volume. */
public abstract static class Callback {
public abstract void onVolumeChanged(VolumeProviderCompat volumeProvider);
}
@RequiresApi(21)
private static class Api21Impl {
private Api21Impl() {}
@DoNotInline
static void setCurrentVolume(VolumeProvider volumeProvider, int currentVolume) {
volumeProvider.setCurrentVolume(currentVolume);
}
}
} }

View File

@ -21,7 +21,9 @@ import static org.hamcrest.core.IsNot.not;
import android.media.AudioAttributes; import android.media.AudioAttributes;
import android.media.AudioManager; import android.media.AudioManager;
import android.os.Build;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SdkSuppress;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -50,7 +52,11 @@ public class AudioAttributesCompatTest {
AudioAttributesCompat mNotificationLegacyAAC; AudioAttributesCompat mNotificationLegacyAAC;
@Before @Before
@SdkSuppress(minSdkVersion = 21)
public void setUpApi21() { public void setUpApi21() {
if (Build.VERSION.SDK_INT < 21) {
return;
}
mMediaAA = mMediaAA =
new AudioAttributes.Builder() new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
@ -74,6 +80,7 @@ public class AudioAttributesCompatTest {
} }
@Test @Test
@SdkSuppress(minSdkVersion = 21)
public void testCreateWithAudioAttributesApi21() { public void testCreateWithAudioAttributesApi21() {
assertThat(mMediaAACFromAA, not(equalTo(null))); assertThat(mMediaAACFromAA, not(equalTo(null)));
assertThat((AudioAttributes) mMediaAACFromAA.unwrap(), equalTo(mMediaAA)); assertThat((AudioAttributes) mMediaAACFromAA.unwrap(), equalTo(mMediaAA));
@ -83,6 +90,7 @@ public class AudioAttributesCompatTest {
} }
@Test @Test
@SdkSuppress(minSdkVersion = 21)
public void testEqualityApi21() { public void testEqualityApi21() {
assertThat("self equality", mMediaAACFromAA, equalTo(mMediaAACFromAA)); assertThat("self equality", mMediaAACFromAA, equalTo(mMediaAACFromAA));
assertThat("different things", mMediaAACFromAA, not(equalTo(mNotificationAAC))); assertThat("different things", mMediaAACFromAA, not(equalTo(mNotificationAAC)));
@ -118,6 +126,7 @@ public class AudioAttributesCompatTest {
} }
@Test @Test
@SdkSuppress(minSdkVersion = 21)
public void testLegacyStreamTypeInferenceApi21() { public void testLegacyStreamTypeInferenceApi21() {
assertThat(mMediaAACFromAA.getLegacyStreamType(), equalTo(AudioManager.STREAM_MUSIC)); assertThat(mMediaAACFromAA.getLegacyStreamType(), equalTo(AudioManager.STREAM_MUSIC));
} }

View File

@ -22,6 +22,7 @@ import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import androidx.media3.test.session.common.TestUtils; import androidx.media3.test.session.common.TestUtils;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SdkSuppress;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -29,6 +30,7 @@ import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
public class MediaDescriptionCompatTest { public class MediaDescriptionCompatTest {
@SdkSuppress(minSdkVersion = 21)
@Test @Test
public void roundTripViaFrameworkObject_returnsEqualMediaUriAndExtras() { public void roundTripViaFrameworkObject_returnsEqualMediaUriAndExtras() {
Uri mediaUri = Uri.parse("androidx://media/uri"); Uri mediaUri = Uri.parse("androidx://media/uri");
@ -52,6 +54,7 @@ public class MediaDescriptionCompatTest {
TestUtils.equals(createExtras(), restoredDescription2.getExtras()); TestUtils.equals(createExtras(), restoredDescription2.getExtras());
} }
@SdkSuppress(minSdkVersion = 21)
@Test @Test
public void getMediaDescription_withMediaUri_doesNotTouchExtras() { public void getMediaDescription_withMediaUri_doesNotTouchExtras() {
MediaDescriptionCompat originalDescription = MediaDescriptionCompat originalDescription =