mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Cleanup some dead code and lint warnings in session compat code
PiperOrigin-RevId: 736861977
This commit is contained in:
parent
df489e2f94
commit
6b1a2aff98
@ -104,10 +104,6 @@ import java.util.concurrent.TimeoutException;
|
||||
|
||||
private static final String TAG = "LegacyConversions";
|
||||
|
||||
// Stub BrowserRoot for accepting any connection here.
|
||||
public static final BrowserRoot defaultBrowserRoot =
|
||||
new BrowserRoot(MediaLibraryService.SERVICE_INTERFACE, null);
|
||||
|
||||
public static final ImmutableSet<String> KNOWN_METADATA_COMPAT_KEYS =
|
||||
ImmutableSet.of(
|
||||
MediaMetadataCompat.METADATA_KEY_TITLE,
|
||||
@ -1476,13 +1472,11 @@ import java.util.concurrent.TimeoutException;
|
||||
|
||||
if (state != null) {
|
||||
List<PlaybackStateCompat.CustomAction> customActions = state.getCustomActions();
|
||||
if (customActions != null) {
|
||||
for (CustomAction customAction : customActions) {
|
||||
String action = customAction.getAction();
|
||||
@Nullable Bundle extras = customAction.getExtras();
|
||||
sessionCommandsBuilder.add(
|
||||
new SessionCommand(action, extras == null ? Bundle.EMPTY : extras));
|
||||
}
|
||||
for (CustomAction customAction : customActions) {
|
||||
String action = customAction.getAction();
|
||||
@Nullable Bundle extras = customAction.getExtras();
|
||||
sessionCommandsBuilder.add(
|
||||
new SessionCommand(action, extras == null ? Bundle.EMPTY : extras));
|
||||
}
|
||||
}
|
||||
return sessionCommandsBuilder.build();
|
||||
@ -1504,9 +1498,6 @@ import java.util.concurrent.TimeoutException;
|
||||
return ImmutableList.of();
|
||||
}
|
||||
List<PlaybackStateCompat.CustomAction> customActions = state.getCustomActions();
|
||||
if (customActions == null) {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
ImmutableList.Builder<CommandButton> customLayout = new ImmutableList.Builder<>();
|
||||
for (CustomAction customAction : customActions) {
|
||||
String action = customAction.getAction();
|
||||
|
@ -422,6 +422,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
||||
return options;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Bundle getExtras(@Nullable LibraryParams params) {
|
||||
return params != null ? params.extras : null;
|
||||
}
|
||||
|
@ -450,7 +450,7 @@ public final class SessionToken {
|
||||
private static MediaSessionCompat.Token createCompatToken(
|
||||
Parcelable platformOrLegacyCompatToken) {
|
||||
if (platformOrLegacyCompatToken instanceof Token) {
|
||||
return MediaSessionCompat.Token.fromToken(platformOrLegacyCompatToken);
|
||||
return MediaSessionCompat.Token.fromToken((Token) platformOrLegacyCompatToken);
|
||||
}
|
||||
// Assume this is an android.support.v4.media.session.MediaSessionCompat.Token.
|
||||
return LegacyParcelableUtil.convert(
|
||||
|
@ -27,12 +27,9 @@ import androidx.annotation.IntDef;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.annotation.RestrictTo;
|
||||
import androidx.media3.common.util.Log;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
@ -68,7 +65,6 @@ import java.util.Objects;
|
||||
@UnstableApi
|
||||
@RestrictTo(LIBRARY)
|
||||
public class AudioAttributesCompat {
|
||||
static final String TAG = "AudioAttributesCompat";
|
||||
|
||||
/** Content type value to use when the content type is unknown, or other than the ones defined. */
|
||||
public static final int CONTENT_TYPE_UNKNOWN = AudioAttributes.CONTENT_TYPE_UNKNOWN;
|
||||
@ -183,10 +179,6 @@ public class AudioAttributesCompat {
|
||||
private static final int SUPPRESSIBLE_CALL = 2;
|
||||
private static final SparseIntArray SUPPRESSIBLE_USAGES;
|
||||
|
||||
// used by tests
|
||||
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||
static boolean sForceLegacyBehavior;
|
||||
|
||||
static {
|
||||
SUPPRESSIBLE_USAGES = new SparseIntArray();
|
||||
SUPPRESSIBLE_USAGES.put(USAGE_NOTIFICATION, SUPPRESSIBLE_NOTIFICATION);
|
||||
@ -220,58 +212,15 @@ public class AudioAttributesCompat {
|
||||
/** Flag defining a behavior where the audibility of the sound will be ensured by the system. */
|
||||
public static final int FLAG_AUDIBILITY_ENFORCED = 0x1 << 0;
|
||||
|
||||
static final int FLAG_SECURE = 0x1 << 1;
|
||||
static final int FLAG_SCO = 0x1 << 2;
|
||||
static final int FLAG_BEACON = 0x1 << 3;
|
||||
|
||||
/** Flag requesting the use of an output stream supporting hardware A/V synchronization. */
|
||||
public static final int FLAG_HW_AV_SYNC = 0x1 << 4;
|
||||
|
||||
static final int FLAG_HW_HOTWORD = 0x1 << 5;
|
||||
static final int FLAG_BYPASS_INTERRUPTION_POLICY = 0x1 << 6;
|
||||
static final int FLAG_BYPASS_MUTE = 0x1 << 7;
|
||||
static final int FLAG_LOW_LATENCY = 0x1 << 8;
|
||||
static final int FLAG_DEEP_BUFFER = 0x1 << 9;
|
||||
|
||||
static final int FLAG_ALL =
|
||||
(FLAG_AUDIBILITY_ENFORCED
|
||||
| FLAG_SECURE
|
||||
| FLAG_SCO
|
||||
| FLAG_BEACON
|
||||
| FLAG_HW_AV_SYNC
|
||||
| FLAG_HW_HOTWORD
|
||||
| FLAG_BYPASS_INTERRUPTION_POLICY
|
||||
| FLAG_BYPASS_MUTE
|
||||
| FLAG_LOW_LATENCY
|
||||
| FLAG_DEEP_BUFFER);
|
||||
static final int FLAG_ALL_PUBLIC =
|
||||
(FLAG_AUDIBILITY_ENFORCED | FLAG_HW_AV_SYNC | FLAG_LOW_LATENCY);
|
||||
|
||||
static final int INVALID_STREAM_TYPE = -1; // AudioSystem.STREAM_DEFAULT
|
||||
|
||||
public final AudioAttributesImpl mImpl;
|
||||
private final AudioAttributesImpl mImpl;
|
||||
|
||||
AudioAttributesCompat(AudioAttributesImpl impl) {
|
||||
mImpl = impl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the stream type matching the given attributes for volume control. Use this method to
|
||||
* derive the stream type needed to configure the volume control slider in an {@link
|
||||
* android.app.Activity} with {@link android.app.Activity#setVolumeControlStream(int)}. <br>
|
||||
* Do not use this method to set the stream type on an audio player object (e.g. {@link
|
||||
* android.media.AudioTrack}, {@link android.media.MediaPlayer}) as this is deprecated; use <code>
|
||||
* AudioAttributes</code> instead.
|
||||
*
|
||||
* @return a valid stream type for <code>Activity</code> or stream volume control that matches the
|
||||
* attributes, or {@link AudioManager#USE_DEFAULT_STREAM_TYPE} if there isn't a direct match.
|
||||
* Note that <code>USE_DEFAULT_STREAM_TYPE</code> is not a valid value for {@link
|
||||
* AudioManager#setStreamVolume(int, int, int)}.
|
||||
*/
|
||||
public int getVolumeControlStream() {
|
||||
return mImpl.getVolumeControlStream();
|
||||
}
|
||||
|
||||
// public API unique to AudioAttributesCompat
|
||||
|
||||
/**
|
||||
@ -301,17 +250,12 @@ public class AudioAttributesCompat {
|
||||
* @param aa an instance of {@link AudioAttributes}.
|
||||
* @return the new <code>AudioAttributesCompat</code>, or <code>null</code> on API < 21
|
||||
*/
|
||||
@Nullable
|
||||
public static AudioAttributesCompat wrap(Object aa) {
|
||||
if (sForceLegacyBehavior) {
|
||||
return null;
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
return new AudioAttributesCompat(new AudioAttributesImplApi26((AudioAttributes) aa));
|
||||
} else if (Build.VERSION.SDK_INT >= 21) {
|
||||
} else {
|
||||
return new AudioAttributesCompat(new AudioAttributesImplApi21((AudioAttributes) aa));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// The rest of this file implements an approximation to AudioAttributes using old stream types
|
||||
@ -373,14 +317,10 @@ public class AudioAttributesCompat {
|
||||
* default playback behavior in terms of routing and volume management.
|
||||
*/
|
||||
public Builder() {
|
||||
if (sForceLegacyBehavior) {
|
||||
mBuilderImpl = new AudioAttributesImplBase.Builder();
|
||||
} else if (Build.VERSION.SDK_INT >= 26) {
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
mBuilderImpl = new AudioAttributesImplApi26.Builder();
|
||||
} else if (Build.VERSION.SDK_INT >= 21) {
|
||||
mBuilderImpl = new AudioAttributesImplApi21.Builder();
|
||||
} else {
|
||||
mBuilderImpl = new AudioAttributesImplBase.Builder();
|
||||
mBuilderImpl = new AudioAttributesImplApi21.Builder();
|
||||
}
|
||||
}
|
||||
|
||||
@ -390,14 +330,10 @@ public class AudioAttributesCompat {
|
||||
* @param aa the AudioAttributesCompat object whose data will be reused in the new Builder.
|
||||
*/
|
||||
public Builder(AudioAttributesCompat aa) {
|
||||
if (sForceLegacyBehavior) {
|
||||
mBuilderImpl = new AudioAttributesImplBase.Builder(aa);
|
||||
} else if (Build.VERSION.SDK_INT >= 26) {
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
mBuilderImpl = new AudioAttributesImplApi26.Builder(checkNotNull(aa.unwrap()));
|
||||
} else if (Build.VERSION.SDK_INT >= 21) {
|
||||
mBuilderImpl = new AudioAttributesImplApi21.Builder(checkNotNull(aa.unwrap()));
|
||||
} else {
|
||||
mBuilderImpl = new AudioAttributesImplBase.Builder(aa);
|
||||
mBuilderImpl = new AudioAttributesImplApi21.Builder(checkNotNull(aa.unwrap()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -458,9 +394,8 @@ public class AudioAttributesCompat {
|
||||
*
|
||||
* <p>This is a bitwise OR with the existing flags.
|
||||
*
|
||||
* @param flags a combination of {@link AudioAttributesCompat#FLAG_AUDIBILITY_ENFORCED}, {@link
|
||||
* AudioAttributesCompat#FLAG_HW_AV_SYNC}.
|
||||
* @return the same Builder instance.
|
||||
* @param flags The optional flag of {@link AudioAttributesCompat#FLAG_AUDIBILITY_ENFORCED}.
|
||||
* @return The same Builder instance.
|
||||
*/
|
||||
public Builder setFlags(int flags) {
|
||||
mBuilderImpl.setFlags(flags);
|
||||
@ -498,92 +433,31 @@ public class AudioAttributesCompat {
|
||||
return mImpl.toString();
|
||||
}
|
||||
|
||||
static String usageToString(int usage) {
|
||||
switch (usage) {
|
||||
case USAGE_UNKNOWN:
|
||||
return "USAGE_UNKNOWN";
|
||||
case USAGE_MEDIA:
|
||||
return "USAGE_MEDIA";
|
||||
case USAGE_VOICE_COMMUNICATION:
|
||||
return "USAGE_VOICE_COMMUNICATION";
|
||||
case USAGE_VOICE_COMMUNICATION_SIGNALLING:
|
||||
return "USAGE_VOICE_COMMUNICATION_SIGNALLING";
|
||||
case USAGE_ALARM:
|
||||
return "USAGE_ALARM";
|
||||
case USAGE_NOTIFICATION:
|
||||
return "USAGE_NOTIFICATION";
|
||||
case USAGE_NOTIFICATION_RINGTONE:
|
||||
return "USAGE_NOTIFICATION_RINGTONE";
|
||||
case USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
|
||||
return "USAGE_NOTIFICATION_COMMUNICATION_REQUEST";
|
||||
case USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
|
||||
return "USAGE_NOTIFICATION_COMMUNICATION_INSTANT";
|
||||
case USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
|
||||
return "USAGE_NOTIFICATION_COMMUNICATION_DELAYED";
|
||||
case USAGE_NOTIFICATION_EVENT:
|
||||
return "USAGE_NOTIFICATION_EVENT";
|
||||
case USAGE_ASSISTANCE_ACCESSIBILITY:
|
||||
return "USAGE_ASSISTANCE_ACCESSIBILITY";
|
||||
case USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
|
||||
return "USAGE_ASSISTANCE_NAVIGATION_GUIDANCE";
|
||||
case USAGE_ASSISTANCE_SONIFICATION:
|
||||
return "USAGE_ASSISTANCE_SONIFICATION";
|
||||
case USAGE_GAME:
|
||||
return "USAGE_GAME";
|
||||
case USAGE_ASSISTANT:
|
||||
return "USAGE_ASSISTANT";
|
||||
default:
|
||||
return "unknown usage " + usage;
|
||||
}
|
||||
}
|
||||
|
||||
abstract static class AudioManagerHidden {
|
||||
public static final int STREAM_BLUETOOTH_SCO = 6;
|
||||
public static final int STREAM_SYSTEM_ENFORCED = 7;
|
||||
public static final int STREAM_TTS = 9;
|
||||
public static final int STREAM_ACCESSIBILITY = 10;
|
||||
|
||||
private AudioManagerHidden() {}
|
||||
}
|
||||
|
||||
/** Prevent AudioAttributes from being used even on platforms that support it. */
|
||||
public static void setForceLegacyBehavior(boolean force) {
|
||||
sForceLegacyBehavior = force;
|
||||
}
|
||||
|
||||
int getRawLegacyStreamType() {
|
||||
return mImpl.getRawLegacyStreamType();
|
||||
}
|
||||
|
||||
static int toVolumeStreamType(
|
||||
boolean fromGetVolumeControlStream, int flags, @AttributeUsage int usage) {
|
||||
static int toVolumeStreamType(int flags, @AttributeUsage int usage) {
|
||||
// flags to stream type mapping
|
||||
if ((flags & FLAG_AUDIBILITY_ENFORCED) == FLAG_AUDIBILITY_ENFORCED) {
|
||||
return fromGetVolumeControlStream
|
||||
? AudioManager.STREAM_SYSTEM
|
||||
: AudioManagerHidden.STREAM_SYSTEM_ENFORCED;
|
||||
return AudioManagerHidden.STREAM_SYSTEM_ENFORCED;
|
||||
}
|
||||
if ((flags & FLAG_SCO) == FLAG_SCO) {
|
||||
return fromGetVolumeControlStream
|
||||
? AudioManager.STREAM_VOICE_CALL
|
||||
: AudioManagerHidden.STREAM_BLUETOOTH_SCO;
|
||||
return AudioManagerHidden.STREAM_BLUETOOTH_SCO;
|
||||
}
|
||||
|
||||
// usage to stream type mapping
|
||||
switch (usage) {
|
||||
case USAGE_MEDIA:
|
||||
case USAGE_GAME:
|
||||
case USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
|
||||
case USAGE_ASSISTANT:
|
||||
return AudioManager.STREAM_MUSIC;
|
||||
case USAGE_ASSISTANCE_SONIFICATION:
|
||||
return AudioManager.STREAM_SYSTEM;
|
||||
case USAGE_VOICE_COMMUNICATION:
|
||||
return AudioManager.STREAM_VOICE_CALL;
|
||||
case USAGE_VOICE_COMMUNICATION_SIGNALLING:
|
||||
return fromGetVolumeControlStream
|
||||
? AudioManager.STREAM_VOICE_CALL
|
||||
: AudioManager.STREAM_DTMF;
|
||||
return AudioManager.STREAM_DTMF;
|
||||
case USAGE_ALARM:
|
||||
return AudioManager.STREAM_ALARM;
|
||||
case USAGE_NOTIFICATION_RINGTONE:
|
||||
@ -596,15 +470,8 @@ public class AudioAttributesCompat {
|
||||
return AudioManager.STREAM_NOTIFICATION;
|
||||
case USAGE_ASSISTANCE_ACCESSIBILITY:
|
||||
return AudioManagerHidden.STREAM_ACCESSIBILITY;
|
||||
case USAGE_UNKNOWN:
|
||||
return AudioManager.STREAM_MUSIC;
|
||||
default:
|
||||
if (fromGetVolumeControlStream) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unknown usage value " + usage + " in audio attributes");
|
||||
} else {
|
||||
return AudioManager.STREAM_MUSIC;
|
||||
}
|
||||
return AudioManager.STREAM_MUSIC;
|
||||
}
|
||||
}
|
||||
|
||||
@ -613,10 +480,7 @@ public class AudioAttributesCompat {
|
||||
if (!(o instanceof AudioAttributesCompat)) {
|
||||
return false;
|
||||
}
|
||||
final AudioAttributesCompat that = (AudioAttributesCompat) o;
|
||||
if (this.mImpl == null) {
|
||||
return that.mImpl == null;
|
||||
}
|
||||
AudioAttributesCompat that = (AudioAttributesCompat) o;
|
||||
return this.mImpl.equals(that.mImpl);
|
||||
}
|
||||
|
||||
@ -640,7 +504,7 @@ public class AudioAttributesCompat {
|
||||
USAGE_VIRTUAL_SOURCE
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface AttributeUsage {}
|
||||
private @interface AttributeUsage {}
|
||||
|
||||
@IntDef({
|
||||
CONTENT_TYPE_UNKNOWN,
|
||||
@ -650,20 +514,15 @@ public class AudioAttributesCompat {
|
||||
CONTENT_TYPE_SONIFICATION
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface AttributeContentType {}
|
||||
private @interface AttributeContentType {}
|
||||
|
||||
public interface AudioAttributesImpl {
|
||||
private interface AudioAttributesImpl {
|
||||
/** Gets framework {@link android.media.AudioAttributes}. */
|
||||
@Nullable
|
||||
Object getAudioAttributes();
|
||||
|
||||
int getVolumeControlStream();
|
||||
|
||||
int getLegacyStreamType();
|
||||
|
||||
// Returns explicitly set legacy stream type.
|
||||
int getRawLegacyStreamType();
|
||||
|
||||
int getContentType();
|
||||
|
||||
@AudioAttributesCompat.AttributeUsage
|
||||
@ -685,272 +544,11 @@ public class AudioAttributesCompat {
|
||||
}
|
||||
}
|
||||
|
||||
public static class AudioAttributesImplBase implements AudioAttributesImpl {
|
||||
public int mUsage = USAGE_UNKNOWN;
|
||||
|
||||
public int mContentType = CONTENT_TYPE_UNKNOWN;
|
||||
|
||||
public int mFlags = 0x0;
|
||||
|
||||
public int mLegacyStream = INVALID_STREAM_TYPE;
|
||||
|
||||
public AudioAttributesImplBase() {}
|
||||
|
||||
AudioAttributesImplBase(int contentType, int flags, int usage, int legacyStream) {
|
||||
mContentType = contentType;
|
||||
mFlags = flags;
|
||||
mUsage = usage;
|
||||
mLegacyStream = legacyStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object getAudioAttributes() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVolumeControlStream() {
|
||||
return AudioAttributesCompat.toVolumeStreamType(true, mFlags, mUsage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLegacyStreamType() {
|
||||
if (mLegacyStream != INVALID_STREAM_TYPE) {
|
||||
return mLegacyStream;
|
||||
}
|
||||
return AudioAttributesCompat.toVolumeStreamType(false, mFlags, mUsage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRawLegacyStreamType() {
|
||||
return mLegacyStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContentType() {
|
||||
return mContentType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @AudioAttributesCompat.AttributeUsage int getUsage() {
|
||||
return mUsage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFlags() {
|
||||
int flags = mFlags;
|
||||
int legacyStream = getLegacyStreamType();
|
||||
if (legacyStream == AudioManagerHidden.STREAM_BLUETOOTH_SCO) {
|
||||
flags |= AudioAttributesCompat.FLAG_SCO;
|
||||
} else if (legacyStream == AudioManagerHidden.STREAM_SYSTEM_ENFORCED) {
|
||||
flags |= AudioAttributesCompat.FLAG_AUDIBILITY_ENFORCED;
|
||||
}
|
||||
return flags & AudioAttributesCompat.FLAG_ALL_PUBLIC;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Override Object methods
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(new Object[] {mContentType, mFlags, mUsage, mLegacyStream});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if (!(o instanceof AudioAttributesImplBase)) {
|
||||
return false;
|
||||
}
|
||||
final AudioAttributesImplBase that = (AudioAttributesImplBase) o;
|
||||
return ((mContentType == that.getContentType())
|
||||
&& (mFlags == that.getFlags())
|
||||
&& (mUsage == that.getUsage())
|
||||
&& (mLegacyStream == that.mLegacyStream)); // query the slot directly, don't guess
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder("AudioAttributesCompat:");
|
||||
if (mLegacyStream != INVALID_STREAM_TYPE) {
|
||||
sb.append(" stream=").append(mLegacyStream);
|
||||
sb.append(" derived");
|
||||
}
|
||||
sb.append(" usage=")
|
||||
.append(AudioAttributesCompat.usageToString(mUsage))
|
||||
.append(" content=")
|
||||
.append(mContentType)
|
||||
.append(" flags=0x")
|
||||
.append(Integer.toHexString(mFlags).toUpperCase(Locale.ROOT));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
static class Builder implements AudioAttributesImpl.Builder {
|
||||
private int mUsage = USAGE_UNKNOWN;
|
||||
private int mContentType = CONTENT_TYPE_UNKNOWN;
|
||||
private int mFlags = 0x0;
|
||||
private int mLegacyStream = INVALID_STREAM_TYPE;
|
||||
|
||||
Builder() {}
|
||||
|
||||
Builder(AudioAttributesCompat aa) {
|
||||
mUsage = aa.getUsage();
|
||||
mContentType = aa.getContentType();
|
||||
mFlags = aa.getFlags();
|
||||
mLegacyStream = aa.getRawLegacyStreamType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AudioAttributesImpl build() {
|
||||
return new AudioAttributesImplBase(mContentType, mFlags, mUsage, mLegacyStream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder setUsage(@AudioAttributesCompat.AttributeUsage int usage) {
|
||||
switch (usage) {
|
||||
case USAGE_UNKNOWN:
|
||||
case USAGE_MEDIA:
|
||||
case USAGE_VOICE_COMMUNICATION:
|
||||
case USAGE_VOICE_COMMUNICATION_SIGNALLING:
|
||||
case USAGE_ALARM:
|
||||
case USAGE_NOTIFICATION:
|
||||
case USAGE_NOTIFICATION_RINGTONE:
|
||||
case USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
|
||||
case USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
|
||||
case USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
|
||||
case USAGE_NOTIFICATION_EVENT:
|
||||
case USAGE_ASSISTANCE_ACCESSIBILITY:
|
||||
case USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
|
||||
case USAGE_ASSISTANCE_SONIFICATION:
|
||||
case USAGE_GAME:
|
||||
case USAGE_VIRTUAL_SOURCE:
|
||||
mUsage = usage;
|
||||
break;
|
||||
// TODO: shouldn't it be USAGE_ASSISTANT?
|
||||
case USAGE_ASSISTANT:
|
||||
mUsage = USAGE_ASSISTANCE_NAVIGATION_GUIDANCE;
|
||||
break;
|
||||
default:
|
||||
mUsage = USAGE_UNKNOWN;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder setContentType(@AudioAttributesCompat.AttributeContentType int contentType) {
|
||||
switch (contentType) {
|
||||
case CONTENT_TYPE_UNKNOWN:
|
||||
case CONTENT_TYPE_MOVIE:
|
||||
case CONTENT_TYPE_MUSIC:
|
||||
case CONTENT_TYPE_SONIFICATION:
|
||||
case CONTENT_TYPE_SPEECH:
|
||||
mContentType = contentType;
|
||||
break;
|
||||
default:
|
||||
mContentType = CONTENT_TYPE_UNKNOWN;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder setFlags(int flags) {
|
||||
flags &= AudioAttributesCompat.FLAG_ALL;
|
||||
mFlags |= flags;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder setLegacyStreamType(int streamType) {
|
||||
if (streamType == AudioManagerHidden.STREAM_ACCESSIBILITY) {
|
||||
throw new IllegalArgumentException(
|
||||
"STREAM_ACCESSIBILITY is not a legacy stream "
|
||||
+ "type that was used for audio playback");
|
||||
}
|
||||
mLegacyStream = streamType;
|
||||
return setInternalLegacyStreamType(streamType);
|
||||
}
|
||||
|
||||
private Builder setInternalLegacyStreamType(int streamType) {
|
||||
switch (streamType) {
|
||||
case AudioManager.STREAM_VOICE_CALL:
|
||||
mContentType = CONTENT_TYPE_SPEECH;
|
||||
break;
|
||||
case AudioManagerHidden.STREAM_SYSTEM_ENFORCED:
|
||||
mFlags |= AudioAttributesCompat.FLAG_AUDIBILITY_ENFORCED;
|
||||
// intended fall through, attributes in common with STREAM_SYSTEM
|
||||
case AudioManager.STREAM_SYSTEM:
|
||||
mContentType = CONTENT_TYPE_SONIFICATION;
|
||||
break;
|
||||
case AudioManager.STREAM_RING:
|
||||
mContentType = CONTENT_TYPE_SONIFICATION;
|
||||
break;
|
||||
case AudioManager.STREAM_MUSIC:
|
||||
mContentType = CONTENT_TYPE_MUSIC;
|
||||
break;
|
||||
case AudioManager.STREAM_ALARM:
|
||||
mContentType = CONTENT_TYPE_SONIFICATION;
|
||||
break;
|
||||
case AudioManager.STREAM_NOTIFICATION:
|
||||
mContentType = CONTENT_TYPE_SONIFICATION;
|
||||
break;
|
||||
case AudioManagerHidden.STREAM_BLUETOOTH_SCO:
|
||||
mContentType = CONTENT_TYPE_SPEECH;
|
||||
mFlags |= AudioAttributesCompat.FLAG_SCO;
|
||||
break;
|
||||
case AudioManager.STREAM_DTMF:
|
||||
mContentType = CONTENT_TYPE_SONIFICATION;
|
||||
break;
|
||||
case AudioManagerHidden.STREAM_TTS:
|
||||
mContentType = CONTENT_TYPE_SONIFICATION;
|
||||
break;
|
||||
case AudioManager.STREAM_ACCESSIBILITY:
|
||||
mContentType = CONTENT_TYPE_SPEECH;
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "Invalid stream type " + streamType + " for AudioAttributesCompat");
|
||||
}
|
||||
mUsage = usageForStreamType(streamType);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||
static int usageForStreamType(int streamType) {
|
||||
switch (streamType) {
|
||||
case AudioManager.STREAM_VOICE_CALL:
|
||||
return USAGE_VOICE_COMMUNICATION;
|
||||
case AudioManagerHidden.STREAM_SYSTEM_ENFORCED:
|
||||
case AudioManager.STREAM_SYSTEM:
|
||||
return USAGE_ASSISTANCE_SONIFICATION;
|
||||
case AudioManager.STREAM_RING:
|
||||
return USAGE_NOTIFICATION_RINGTONE;
|
||||
case AudioManager.STREAM_MUSIC:
|
||||
return USAGE_MEDIA;
|
||||
case AudioManager.STREAM_ALARM:
|
||||
return USAGE_ALARM;
|
||||
case AudioManager.STREAM_NOTIFICATION:
|
||||
return USAGE_NOTIFICATION;
|
||||
case AudioManagerHidden.STREAM_BLUETOOTH_SCO:
|
||||
return USAGE_VOICE_COMMUNICATION;
|
||||
case AudioManager.STREAM_DTMF:
|
||||
return USAGE_VOICE_COMMUNICATION_SIGNALLING;
|
||||
case AudioManager.STREAM_ACCESSIBILITY:
|
||||
return USAGE_ASSISTANCE_ACCESSIBILITY;
|
||||
case AudioManagerHidden.STREAM_TTS:
|
||||
default:
|
||||
return USAGE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(21)
|
||||
public static class AudioAttributesImplApi21 implements AudioAttributesImpl {
|
||||
private static class AudioAttributesImplApi21 implements AudioAttributesImpl {
|
||||
|
||||
@Nullable public AudioAttributes mAudioAttributes;
|
||||
|
||||
public int mLegacyStreamType = INVALID_STREAM_TYPE;
|
||||
|
||||
public AudioAttributesImplApi21() {}
|
||||
public final int mLegacyStreamType;
|
||||
|
||||
AudioAttributesImplApi21(AudioAttributes audioAttributes) {
|
||||
this(audioAttributes, INVALID_STREAM_TYPE);
|
||||
@ -967,23 +565,12 @@ public class AudioAttributesCompat {
|
||||
return mAudioAttributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVolumeControlStream() {
|
||||
// TODO: address the framework change ag/4995785.
|
||||
return AudioAttributesCompat.toVolumeStreamType(true, getFlags(), getUsage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLegacyStreamType() {
|
||||
if (mLegacyStreamType != INVALID_STREAM_TYPE) {
|
||||
return mLegacyStreamType;
|
||||
}
|
||||
return AudioAttributesCompat.toVolumeStreamType(false, getFlags(), getUsage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRawLegacyStreamType() {
|
||||
return mLegacyStreamType;
|
||||
return AudioAttributesCompat.toVolumeStreamType(getFlags(), getUsage());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1020,7 +607,6 @@ public class AudioAttributesCompat {
|
||||
return "AudioAttributesCompat: audioattributes=" + mAudioAttributes;
|
||||
}
|
||||
|
||||
@RequiresApi(21)
|
||||
static class Builder implements AudioAttributesImpl.Builder {
|
||||
final AudioAttributes.Builder mFwkBuilder;
|
||||
|
||||
@ -1069,19 +655,12 @@ public class AudioAttributesCompat {
|
||||
}
|
||||
|
||||
@RequiresApi(26)
|
||||
public static class AudioAttributesImplApi26 extends AudioAttributesImplApi21 {
|
||||
|
||||
public AudioAttributesImplApi26() {}
|
||||
private static class AudioAttributesImplApi26 extends AudioAttributesImplApi21 {
|
||||
|
||||
AudioAttributesImplApi26(AudioAttributes audioAttributes) {
|
||||
super(audioAttributes, INVALID_STREAM_TYPE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVolumeControlStream() {
|
||||
return checkNotNull(mAudioAttributes).getVolumeControlStream();
|
||||
}
|
||||
|
||||
@RequiresApi(26)
|
||||
static class Builder extends AudioAttributesImplApi21.Builder {
|
||||
Builder() {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -49,13 +49,6 @@ public class MediaBrowserProtocol {
|
||||
public static final String EXTRA_MESSENGER_BINDER = "extra_messenger";
|
||||
public static final String EXTRA_SESSION_BINDER = "extra_session_binder";
|
||||
|
||||
/**
|
||||
* MediaBrowserCompat will check the version of the connected MediaBrowserServiceCompat, and it
|
||||
* will not send messages if they are introduced in the higher version of the
|
||||
* MediaBrowserServiceCompat.
|
||||
*/
|
||||
public static final int SERVICE_VERSION_1 = 1;
|
||||
|
||||
/**
|
||||
* To prevent ClassNotFoundException from Parcelables, MediaBrowser(Service)Compat tries to avoid
|
||||
* using framework code as much as possible (b/62648808). For backward compatibility, service v2
|
||||
|
@ -60,7 +60,6 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.media.browse.MediaBrowser;
|
||||
import android.media.session.MediaSession;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
@ -130,8 +129,6 @@ public abstract class MediaBrowserServiceCompat extends Service {
|
||||
static final String TAG = "MBServiceCompat";
|
||||
static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||
|
||||
private static final float EPSILON = 0.00001f;
|
||||
|
||||
private @MonotonicNonNull MediaBrowserServiceImpl mImpl;
|
||||
|
||||
/** The {@link Intent} that must be declared as handled by the service. */
|
||||
@ -176,7 +173,7 @@ public abstract class MediaBrowserServiceCompat extends Service {
|
||||
@SuppressWarnings({
|
||||
"argument.type.incompatible",
|
||||
"assignment.type.incompatible"
|
||||
}) // Using this before construtor completes
|
||||
}) // Using this before constructor completes
|
||||
final ServiceHandler mHandler = new ServiceHandler(/* service= */ this);
|
||||
|
||||
@Nullable MediaSessionCompat.Token mSession;
|
||||
@ -199,115 +196,6 @@ public abstract class MediaBrowserServiceCompat extends Service {
|
||||
RemoteUserInfo getCurrentBrowserInfo();
|
||||
}
|
||||
|
||||
class MediaBrowserServiceImplBase implements MediaBrowserServiceImpl {
|
||||
private @MonotonicNonNull Messenger mMessenger;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
mMessenger = new Messenger(mHandler);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
if (SERVICE_INTERFACE.equals(intent.getAction())) {
|
||||
return checkNotNull(mMessenger).getBinder();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSessionToken(final MediaSessionCompat.Token token) {
|
||||
mHandler.post(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Iterator<ConnectionRecord> iter = mConnections.values().iterator();
|
||||
while (iter.hasNext()) {
|
||||
ConnectionRecord connection = iter.next();
|
||||
try {
|
||||
BrowserRoot root = checkNotNull(connection.root);
|
||||
checkNotNull(connection.callbacks)
|
||||
.onConnect(root.getRootId(), token, root.getExtras());
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Connection for " + connection.pkg + " is no longer valid.");
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyChildrenChanged(String parentId, @Nullable Bundle options) {
|
||||
mHandler.post(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (IBinder binder : mConnections.keySet()) {
|
||||
ConnectionRecord connection = checkNotNull(mConnections.get(binder));
|
||||
notifyChildrenChangedOnHandler(connection, parentId, options);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyChildrenChanged(
|
||||
final RemoteUserInfo remoteUserInfo, final String parentId, final Bundle options) {
|
||||
mHandler.post(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (int i = 0; i < mConnections.size(); i++) {
|
||||
ConnectionRecord connection = mConnections.valueAt(i);
|
||||
if (connection.browserInfo.equals(remoteUserInfo)) {
|
||||
notifyChildrenChangedOnHandler(connection, parentId, options);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||
void notifyChildrenChangedOnHandler(
|
||||
ConnectionRecord connection, String parentId, @Nullable Bundle options) {
|
||||
List<Pair<@NullableType IBinder, @NullableType Bundle>> callbackList =
|
||||
connection.subscriptions.get(parentId);
|
||||
if (callbackList != null) {
|
||||
for (Pair<@NullableType IBinder, @NullableType Bundle> callback : callbackList) {
|
||||
if (MediaBrowserCompatUtils.hasDuplicatedItems(options, callback.second)) {
|
||||
performLoadChildren(parentId, connection, callback.second, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Don't break, because multiple remoteUserInfo may match.
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Bundle getBrowserRootHints() {
|
||||
if (mCurConnection == null) {
|
||||
throw new IllegalStateException(
|
||||
"This should be called inside of onLoadChildren,"
|
||||
+ " onLoadItem, onSearch, or onCustomAction methods");
|
||||
}
|
||||
return mCurConnection.rootHints == null ? null : new Bundle(mCurConnection.rootHints);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoteUserInfo getCurrentBrowserInfo() {
|
||||
if (mCurConnection == null) {
|
||||
throw new IllegalStateException(
|
||||
"This should be called inside of onLoadChildren,"
|
||||
+ " onLoadItem, onSearch, or onCustomAction methods");
|
||||
}
|
||||
return mCurConnection.browserInfo;
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(21)
|
||||
class MediaBrowserServiceImplApi21 implements MediaBrowserServiceImpl {
|
||||
final List<Bundle> mRootExtrasList = new ArrayList<>();
|
||||
@MonotonicNonNull MediaBrowserService mServiceFwk;
|
||||
@ -345,8 +233,7 @@ public abstract class MediaBrowserServiceCompat extends Service {
|
||||
}
|
||||
mRootExtrasList.clear();
|
||||
}
|
||||
checkNotNull(mServiceFwk)
|
||||
.setSessionToken(checkNotNull((MediaSession.Token) token.getToken()));
|
||||
checkNotNull(mServiceFwk).setSessionToken(token.getToken());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -520,7 +407,6 @@ public abstract class MediaBrowserServiceCompat extends Service {
|
||||
return mCurConnection.browserInfo;
|
||||
}
|
||||
|
||||
@RequiresApi(21)
|
||||
class MediaBrowserServiceApi21 extends MediaBrowserService {
|
||||
@SuppressWarnings("method.invocation.invalid") // Calling base method from constructor
|
||||
MediaBrowserServiceApi21(Context context) {
|
||||
@ -543,8 +429,7 @@ public abstract class MediaBrowserServiceCompat extends Service {
|
||||
|
||||
@Override
|
||||
public void onLoadChildren(String parentId, Result<List<MediaBrowser.MediaItem>> result) {
|
||||
MediaBrowserServiceImplApi21.this.onLoadChildren(
|
||||
parentId, new ResultWrapper<List<Parcel>>(result));
|
||||
MediaBrowserServiceImplApi21.this.onLoadChildren(parentId, new ResultWrapper<>(result));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -588,7 +473,7 @@ public abstract class MediaBrowserServiceCompat extends Service {
|
||||
|
||||
@Override
|
||||
public void onLoadItem(String itemId, Result<MediaBrowser.MediaItem> result) {
|
||||
MediaBrowserServiceImplApi23.this.onLoadItem(itemId, new ResultWrapper<Parcel>(result));
|
||||
MediaBrowserServiceImplApi23.this.onLoadItem(itemId, new ResultWrapper<>(result));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -672,7 +557,7 @@ public abstract class MediaBrowserServiceCompat extends Service {
|
||||
MediaSessionCompat.ensureClassLoader(options);
|
||||
mCurConnection = mConnectionFromFwk;
|
||||
MediaBrowserServiceImplApi26.this.onLoadChildren(
|
||||
parentId, new ResultWrapper<List<Parcel>>(result), options);
|
||||
parentId, new ResultWrapper<>(result), options);
|
||||
mCurConnection = null;
|
||||
}
|
||||
}
|
||||
@ -794,10 +679,6 @@ public abstract class MediaBrowserServiceCompat extends Service {
|
||||
* #sendError} when they are done. If {@link #sendResult}, {@link #sendError}, or {@link #detach}
|
||||
* is called twice, an exception will be thrown.
|
||||
*
|
||||
* <p>Those functions might also want to call {@link #sendProgressUpdate} to send interim updates
|
||||
* to the caller. If it is called after calling {@link #sendResult} or {@link #sendError}, an
|
||||
* exception will be thrown.
|
||||
*
|
||||
* @see MediaBrowserServiceCompat#onLoadChildren
|
||||
* @see MediaBrowserServiceCompat#onLoadItem
|
||||
* @see MediaBrowserServiceCompat#onSearch
|
||||
@ -826,23 +707,6 @@ public abstract class MediaBrowserServiceCompat extends Service {
|
||||
onResultSent(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an interim update to the caller. This method is supported only when it is used in {@link
|
||||
* #onCustomAction}.
|
||||
*
|
||||
* @param extras A bundle that contains extra data.
|
||||
*/
|
||||
public void sendProgressUpdate(@Nullable Bundle extras) {
|
||||
if (mSendResultCalled || mSendErrorCalled) {
|
||||
throw new IllegalStateException(
|
||||
"sendProgressUpdate() called when either "
|
||||
+ "sendResult() or sendError() had already been called for: "
|
||||
+ mDebug);
|
||||
}
|
||||
checkExtraFields(extras);
|
||||
onProgressUpdateSent(extras);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the caller of a failure. This is supported only when it is used in {@link
|
||||
* #onCustomAction}.
|
||||
@ -897,32 +761,12 @@ public abstract class MediaBrowserServiceCompat extends Service {
|
||||
*/
|
||||
void onResultSent(@Nullable T result) {}
|
||||
|
||||
/** Called when an interim update is sent. */
|
||||
void onProgressUpdateSent(@Nullable Bundle extras) {
|
||||
throw new UnsupportedOperationException(
|
||||
"It is not supported to send an interim update " + "for " + mDebug);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an error is sent, after assertions about not being called twice have happened.
|
||||
*/
|
||||
void onErrorSent(@Nullable Bundle extras) {
|
||||
throw new UnsupportedOperationException("It is not supported to send an error for " + mDebug);
|
||||
}
|
||||
|
||||
private void checkExtraFields(@Nullable Bundle extras) {
|
||||
if (extras == null) {
|
||||
return;
|
||||
}
|
||||
if (extras.containsKey(MediaBrowserCompat.EXTRA_DOWNLOAD_PROGRESS)) {
|
||||
float value = extras.getFloat(MediaBrowserCompat.EXTRA_DOWNLOAD_PROGRESS);
|
||||
if (value < -EPSILON || value > 1.0f + EPSILON) {
|
||||
throw new IllegalArgumentException(
|
||||
"The value of the EXTRA_DOWNLOAD_PROGRESS "
|
||||
+ "field must be a float number within [0.0, 1.0]");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ServiceBinderImpl {
|
||||
@ -968,7 +812,7 @@ public abstract class MediaBrowserServiceCompat extends Service {
|
||||
mConnections.put(b, connection);
|
||||
b.linkToDeath(connection, 0);
|
||||
if (mSession != null) {
|
||||
callbacks.onConnect(root.getRootId(), mSession, root.getExtras());
|
||||
callbacks.onConnect(root.getRootId(), checkNotNull(mSession), root.getExtras());
|
||||
}
|
||||
} catch (RemoteException ex) {
|
||||
Log.w(TAG, "Calling onConnect() failed. Dropping client. " + "pkg=" + pkg);
|
||||
@ -1188,7 +1032,7 @@ public abstract class MediaBrowserServiceCompat extends Service {
|
||||
private interface ServiceCallbacks {
|
||||
IBinder asBinder();
|
||||
|
||||
void onConnect(String root, @Nullable MediaSessionCompat.Token session, @Nullable Bundle extras)
|
||||
void onConnect(String root, MediaSessionCompat.Token session, @Nullable Bundle extras)
|
||||
throws RemoteException;
|
||||
|
||||
void onConnectFailed() throws RemoteException;
|
||||
@ -1214,8 +1058,7 @@ public abstract class MediaBrowserServiceCompat extends Service {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnect(
|
||||
String root, @Nullable MediaSessionCompat.Token session, @Nullable Bundle extras)
|
||||
public void onConnect(String root, MediaSessionCompat.Token session, @Nullable Bundle extras)
|
||||
throws RemoteException {
|
||||
if (extras == null) {
|
||||
extras = new Bundle();
|
||||
@ -1236,7 +1079,6 @@ public abstract class MediaBrowserServiceCompat extends Service {
|
||||
sendRequest(SERVICE_MSG_ON_CONNECT_FAILED, null);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@Override
|
||||
public void onLoadChildren(
|
||||
@Nullable String mediaId,
|
||||
@ -1268,7 +1110,6 @@ public abstract class MediaBrowserServiceCompat extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(21)
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
static class ResultWrapper<T> {
|
||||
MediaBrowserService.Result mResultFwk;
|
||||
@ -1331,10 +1172,8 @@ public abstract class MediaBrowserServiceCompat extends Service {
|
||||
mImpl = new MediaBrowserServiceImplApi26();
|
||||
} else if (Build.VERSION.SDK_INT >= 23) {
|
||||
mImpl = new MediaBrowserServiceImplApi23();
|
||||
} else if (Build.VERSION.SDK_INT >= 21) {
|
||||
mImpl = new MediaBrowserServiceImplApi21();
|
||||
} else {
|
||||
mImpl = new MediaBrowserServiceImplBase();
|
||||
mImpl = new MediaBrowserServiceImplApi21();
|
||||
}
|
||||
mImpl.onCreate();
|
||||
}
|
||||
@ -1437,7 +1276,7 @@ public abstract class MediaBrowserServiceCompat extends Service {
|
||||
/**
|
||||
* Called when a {@link MediaBrowserCompat#unsubscribe} is called.
|
||||
*
|
||||
* @param id
|
||||
* @param id The id to unsubscribe.
|
||||
*/
|
||||
public void onUnsubscribe(@Nullable String id) {}
|
||||
|
||||
@ -1494,8 +1333,7 @@ public abstract class MediaBrowserServiceCompat extends Service {
|
||||
* <p>Implementations must call either {@link Result#sendResult} or {@link Result#sendError}. If
|
||||
* the requested custom action will be an expensive operation {@link Result#detach} may be called
|
||||
* before returning from this function, and then the service can send the result later when the
|
||||
* custom action is completed. Implementation can also call {@link Result#sendProgressUpdate} to
|
||||
* send an interim update to the requester.
|
||||
* custom action is completed.
|
||||
*
|
||||
* <p>If the requested custom action is not supported by this service, call {@link
|
||||
* Result#sendError}. The default implementation will invoke {@link Result#sendError}.
|
||||
@ -1961,11 +1799,6 @@ public abstract class MediaBrowserServiceCompat extends Service {
|
||||
receiver.send(RESULT_OK, result);
|
||||
}
|
||||
|
||||
@Override
|
||||
void onProgressUpdateSent(@Nullable Bundle data) {
|
||||
receiver.send(RESULT_PROGRESS_UPDATE, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
void onErrorSent(@Nullable Bundle data) {
|
||||
receiver.send(RESULT_ERROR, data);
|
||||
|
@ -19,7 +19,6 @@ import static androidx.annotation.RestrictTo.Scope.LIBRARY;
|
||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||
|
||||
import android.app.ForegroundServiceStartNotAllowedException;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
@ -35,7 +34,6 @@ import androidx.annotation.RequiresApi;
|
||||
import androidx.annotation.RestrictTo;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.session.legacy.PlaybackStateCompat.MediaKeyAction;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -74,22 +72,6 @@ import java.util.List;
|
||||
* </service>
|
||||
* </pre>
|
||||
*
|
||||
* Events can then be handled in {@link Service#onStartCommand(Intent, int, int)} by calling {@link
|
||||
* MediaButtonReceiver#handleIntent(MediaSessionCompat, Intent)}, passing in your current {@link
|
||||
* MediaSessionCompat}:
|
||||
*
|
||||
* <pre>
|
||||
* private MediaSessionCompat mMediaSessionCompat = ...;
|
||||
*
|
||||
* public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
* MediaButtonReceiver.handleIntent(mMediaSessionCompat, intent);
|
||||
* return super.onStartCommand(intent, flags, startId);
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* This ensures that the correct callbacks to {@link MediaSessionCompat.Callback} will be triggered
|
||||
* based on the incoming {@link KeyEvent}.
|
||||
*
|
||||
* <p class="note"><strong>Note:</strong> Once the service is started, it must start to run in the
|
||||
* foreground.
|
||||
*
|
||||
@ -122,7 +104,7 @@ public class MediaButtonReceiver extends BroadcastReceiver {
|
||||
if (Build.VERSION.SDK_INT >= 31
|
||||
&& Api31.instanceOfForegroundServiceStartNotAllowedException(e)) {
|
||||
onForegroundServiceStartNotAllowedException(
|
||||
intent, Api31.castToForegroundServiceStartNotAllowedException(e));
|
||||
Api31.castToForegroundServiceStartNotAllowedException(e));
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
@ -171,13 +153,11 @@ public class MediaButtonReceiver extends BroadcastReceiver {
|
||||
* <p>In all other cases, apps should use a {@linkplain MediaBrowserCompat media browser} to bind
|
||||
* to and start the service instead of broadcasting an intent.
|
||||
*
|
||||
* @param intent The intent that was used {@linkplain Context#startForegroundService(Intent) for
|
||||
* starting the foreground service}.
|
||||
* @param e The exception thrown by the system and caught by this broadcast receiver.
|
||||
*/
|
||||
@RequiresApi(31)
|
||||
protected void onForegroundServiceStartNotAllowedException(
|
||||
Intent intent, ForegroundServiceStartNotAllowedException e) {
|
||||
ForegroundServiceStartNotAllowedException e) {
|
||||
Log.e(
|
||||
TAG,
|
||||
"caught exception when trying to start a foreground service from the "
|
||||
@ -202,7 +182,6 @@ public class MediaButtonReceiver extends BroadcastReceiver {
|
||||
mMediaBrowser = mediaBrowser;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void onConnected() {
|
||||
MediaControllerCompat mediaController =
|
||||
@ -227,112 +206,6 @@ public class MediaButtonReceiver extends BroadcastReceiver {
|
||||
mPendingResult.finish();
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
/**
|
||||
* Extracts any available {@link KeyEvent} from an {@link Intent#ACTION_MEDIA_BUTTON} intent,
|
||||
* passing it onto the {@link MediaSessionCompat} using {@link
|
||||
* MediaControllerCompat#dispatchMediaButtonEvent(KeyEvent)}, which in turn will trigger callbacks
|
||||
* to the {@link MediaSessionCompat.Callback} registered via {@link
|
||||
* MediaSessionCompat#setCallback(MediaSessionCompat.Callback)}.
|
||||
*
|
||||
* @param mediaSessionCompat A {@link MediaSessionCompat} that has a {@link
|
||||
* MediaSessionCompat.Callback} set.
|
||||
* @param intent The intent to parse.
|
||||
* @return The extracted {@link KeyEvent} if found, or null.
|
||||
*/
|
||||
@Nullable
|
||||
@SuppressWarnings("deprecation")
|
||||
public static KeyEvent handleIntent(MediaSessionCompat mediaSessionCompat, Intent intent) {
|
||||
if (mediaSessionCompat == null
|
||||
|| intent == null
|
||||
|| !Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())
|
||||
|| !intent.hasExtra(Intent.EXTRA_KEY_EVENT)) {
|
||||
return null;
|
||||
}
|
||||
KeyEvent ke = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
|
||||
MediaControllerCompat mediaController = mediaSessionCompat.getController();
|
||||
mediaController.dispatchMediaButtonEvent(ke);
|
||||
return ke;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a broadcast pending intent that will send a media button event. The {@code action} will
|
||||
* be translated to the appropriate {@link KeyEvent}, and it will be sent to the registered media
|
||||
* button receiver in the given context. The {@code action} should be one of the following:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link PlaybackStateCompat#ACTION_PLAY}
|
||||
* <li>{@link PlaybackStateCompat#ACTION_PAUSE}
|
||||
* <li>{@link PlaybackStateCompat#ACTION_SKIP_TO_NEXT}
|
||||
* <li>{@link PlaybackStateCompat#ACTION_SKIP_TO_PREVIOUS}
|
||||
* <li>{@link PlaybackStateCompat#ACTION_STOP}
|
||||
* <li>{@link PlaybackStateCompat#ACTION_FAST_FORWARD}
|
||||
* <li>{@link PlaybackStateCompat#ACTION_REWIND}
|
||||
* <li>{@link PlaybackStateCompat#ACTION_PLAY_PAUSE}
|
||||
* </ul>
|
||||
*
|
||||
* @param context The context of the application.
|
||||
* @param action The action to be sent via the pending intent.
|
||||
* @return Created pending intent, or null if cannot find a unique registered media button
|
||||
* receiver or if the {@code action} is unsupported/invalid.
|
||||
*/
|
||||
@Nullable
|
||||
public static PendingIntent buildMediaButtonPendingIntent(
|
||||
Context context, @MediaKeyAction long action) {
|
||||
ComponentName mbrComponent = getMediaButtonReceiverComponent(context);
|
||||
if (mbrComponent == null) {
|
||||
Log.w(
|
||||
TAG,
|
||||
"A unique media button receiver could not be found in the given context, so "
|
||||
+ "couldn't build a pending intent.");
|
||||
return null;
|
||||
}
|
||||
return buildMediaButtonPendingIntent(context, mbrComponent, action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a broadcast pending intent that will send a media button event. The {@code action} will
|
||||
* be translated to the appropriate {@link KeyEvent}, and sent to the provided media button
|
||||
* receiver via the pending intent. The {@code action} should be one of the following:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link PlaybackStateCompat#ACTION_PLAY}
|
||||
* <li>{@link PlaybackStateCompat#ACTION_PAUSE}
|
||||
* <li>{@link PlaybackStateCompat#ACTION_SKIP_TO_NEXT}
|
||||
* <li>{@link PlaybackStateCompat#ACTION_SKIP_TO_PREVIOUS}
|
||||
* <li>{@link PlaybackStateCompat#ACTION_STOP}
|
||||
* <li>{@link PlaybackStateCompat#ACTION_FAST_FORWARD}
|
||||
* <li>{@link PlaybackStateCompat#ACTION_REWIND}
|
||||
* <li>{@link PlaybackStateCompat#ACTION_PLAY_PAUSE}
|
||||
* </ul>
|
||||
*
|
||||
* @param context The context of the application.
|
||||
* @param mbrComponent The full component name of a media button receiver where you want to send
|
||||
* this intent.
|
||||
* @param action The action to be sent via the pending intent.
|
||||
* @return Created pending intent, or null if the given component name is null or the {@code
|
||||
* action} is unsupported/invalid.
|
||||
*/
|
||||
@Nullable
|
||||
public static PendingIntent buildMediaButtonPendingIntent(
|
||||
Context context, ComponentName mbrComponent, @MediaKeyAction long action) {
|
||||
if (mbrComponent == null) {
|
||||
Log.w(TAG, "The component name of media button receiver should be provided.");
|
||||
return null;
|
||||
}
|
||||
int keyCode = PlaybackStateCompat.toKeyCode(action);
|
||||
if (keyCode == KeyEvent.KEYCODE_UNKNOWN) {
|
||||
Log.w(TAG, "Cannot build a media button pending intent with the given action: " + action);
|
||||
return null;
|
||||
}
|
||||
Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
|
||||
intent.setComponent(mbrComponent);
|
||||
intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
|
||||
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
|
||||
return PendingIntent.getBroadcast(
|
||||
context, keyCode, intent, Build.VERSION.SDK_INT >= 31 ? PendingIntent.FLAG_MUTABLE : 0);
|
||||
}
|
||||
|
||||
/** */
|
||||
@Nullable
|
||||
|
@ -26,7 +26,6 @@ import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.service.media.MediaBrowserService;
|
||||
import androidx.annotation.RestrictTo;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.session.legacy.MediaBrowserCompat.ConnectionCallback;
|
||||
@ -36,43 +35,6 @@ import java.util.ArrayList;
|
||||
@UnstableApi
|
||||
@RestrictTo(LIBRARY)
|
||||
public final class MediaConstants {
|
||||
/**
|
||||
* Bundle key used for the account name in {@link MediaSessionCompat session} extras.
|
||||
*
|
||||
* <p>TYPE: String
|
||||
*
|
||||
* @see MediaControllerCompat#getExtras
|
||||
* @see MediaSessionCompat#setExtras
|
||||
*/
|
||||
@SuppressLint("IntentName")
|
||||
public static final String SESSION_EXTRAS_KEY_ACCOUNT_NAME =
|
||||
"androidx.media.MediaSessionCompat.Extras.KEY_ACCOUNT_NAME";
|
||||
|
||||
/**
|
||||
* Bundle key used for the account type in {@link MediaSessionCompat session} extras. The value
|
||||
* would vary across media applications.
|
||||
*
|
||||
* <p>TYPE: String
|
||||
*
|
||||
* @see MediaControllerCompat#getExtras
|
||||
* @see MediaSessionCompat#setExtras
|
||||
*/
|
||||
@SuppressLint("IntentName")
|
||||
public static final String SESSION_EXTRAS_KEY_ACCOUNT_TYPE =
|
||||
"androidx.media.MediaSessionCompat.Extras.KEY_ACCOUNT_TYPE";
|
||||
|
||||
/**
|
||||
* Bundle key used for the account auth token value in {@link MediaSessionCompat session} extras.
|
||||
* The value would vary across media applications.
|
||||
*
|
||||
* <p>TYPE: byte[]
|
||||
*
|
||||
* @see MediaControllerCompat#getExtras
|
||||
* @see MediaSessionCompat#setExtras
|
||||
*/
|
||||
@SuppressLint("IntentName")
|
||||
public static final String SESSION_EXTRAS_KEY_AUTHTOKEN =
|
||||
"androidx.media.MediaSessionCompat.Extras.KEY_AUTHTOKEN";
|
||||
|
||||
/**
|
||||
* Bundle key passed from {@link MediaSessionCompat} to the hosting {@link MediaControllerCompat}
|
||||
@ -106,78 +68,12 @@ public final class MediaConstants {
|
||||
public static final String SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV =
|
||||
"android.media.playback.ALWAYS_RESERVE_SPACE_FOR.ACTION_SKIP_TO_PREVIOUS";
|
||||
|
||||
/**
|
||||
* Bundle key used for media content id in {@link MediaMetadataCompat metadata}, should contain
|
||||
* the same ID provided to <a href="https://developers.google.com/actions/media">Media Actions
|
||||
* Catalog</a> in reference to this title (e.g., episode, movie). This key can contain the content
|
||||
* ID of the currently playing episode or movie and can be used to help users continue watching
|
||||
* after this session is paused or stopped.
|
||||
*
|
||||
* <p>TYPE: String
|
||||
*
|
||||
* @see MediaMetadataCompat
|
||||
*/
|
||||
@SuppressLint("IntentName")
|
||||
public static final String METADATA_KEY_CONTENT_ID =
|
||||
"androidx.media.MediaMetadatCompat.METADATA_KEY_CONTENT_ID";
|
||||
|
||||
/**
|
||||
* Bundle key used for next episode's media content ID in {@link MediaMetadataCompat metadata},
|
||||
* following the same ID and format provided to <a
|
||||
* href="https://developers.google.com/actions/media">Media Actions Catalog</a> in reference to
|
||||
* the next episode of the current title episode. This key can contain the content ID of the
|
||||
* episode immediately following the currently playing episode and can be used to help users
|
||||
* continue watching after this episode is over. This value is only valid for TV Episode content
|
||||
* type and should be left blank for other content.
|
||||
*
|
||||
* <p>TYPE: String
|
||||
*
|
||||
* @see MediaMetadataCompat
|
||||
*/
|
||||
@SuppressLint("IntentName")
|
||||
public static final String METADATA_KEY_NEXT_EPISODE_CONTENT_ID =
|
||||
"androidx.media.MediaMetadatCompat.METADATA_KEY_NEXT_EPISODE_CONTENT_ID";
|
||||
|
||||
/**
|
||||
* Bundle key used for the TV series's media content ID in {@link MediaMetadataCompat metadata},
|
||||
* following the same ID and format provided to <a
|
||||
* href="https://developers.google.com/actions/media">Media Actions Catalog</a> in reference to
|
||||
* the TV series of the current title episode. This value is only valid for TV Episode content
|
||||
* type and should be left blank for other content.
|
||||
*
|
||||
* <p>TYPE: String
|
||||
*
|
||||
* @see MediaMetadataCompat
|
||||
*/
|
||||
@SuppressLint("IntentName")
|
||||
public static final String METADATA_KEY_SERIES_CONTENT_ID =
|
||||
"androidx.media.MediaMetadatCompat.METADATA_KEY_SERIES_CONTENT_ID";
|
||||
|
||||
/**
|
||||
* Key sent through a key-value mapping in {@link MediaMetadataCompat#getLong(String)} or in the
|
||||
* {@link MediaDescriptionCompat#getExtras()} bundle to the hosting {@link MediaBrowserCompat} to
|
||||
* indicate that the corresponding {@link MediaMetadataCompat} or {@link
|
||||
* MediaBrowserCompat.MediaItem} has explicit content (i.e. user discretion is advised when
|
||||
* viewing or listening to this content).
|
||||
*
|
||||
* <p>TYPE: long (to enable, use value {@link #METADATA_VALUE_ATTRIBUTE_PRESENT})
|
||||
*
|
||||
* @see MediaMetadataCompat#getLong(String)
|
||||
* @see MediaMetadataCompat.Builder#putLong(String, long)
|
||||
* @see MediaDescriptionCompat#getExtras()
|
||||
* @see MediaDescriptionCompat.Builder#setExtras(Bundle)
|
||||
*/
|
||||
@SuppressLint("IntentName")
|
||||
public static final String METADATA_KEY_IS_EXPLICIT = "android.media.IS_EXPLICIT";
|
||||
|
||||
/**
|
||||
* Key sent through a key-value mapping in {@link MediaMetadataCompat#getLong(String)} or in the
|
||||
* {@link MediaDescriptionCompat#getExtras()} bundle to the hosting {@link MediaBrowserCompat} to
|
||||
* indicate that the corresponding {@link MediaMetadataCompat} or {@link
|
||||
* MediaBrowserCompat.MediaItem} is an advertisement.
|
||||
*
|
||||
* <p>TYPE: long (to enable, use value {@link #METADATA_VALUE_ATTRIBUTE_PRESENT})
|
||||
*
|
||||
* @see MediaMetadataCompat#getLong(String)
|
||||
* @see MediaMetadataCompat.Builder#putLong(String, long)
|
||||
* @see MediaDescriptionCompat#getExtras()
|
||||
@ -186,16 +82,6 @@ public final class MediaConstants {
|
||||
@SuppressLint("IntentName")
|
||||
public static final String METADATA_KEY_IS_ADVERTISEMENT = "android.media.metadata.ADVERTISEMENT";
|
||||
|
||||
/**
|
||||
* Value sent through a key-value mapping of {@link MediaMetadataCompat}, or through {@link
|
||||
* Bundle} extras on a different data type, to indicate the presence of an attribute described by
|
||||
* its corresponding key.
|
||||
*
|
||||
* @see MediaMetadataCompat#getLong(String)
|
||||
* @see MediaMetadataCompat.Builder#putLong(String, long)
|
||||
*/
|
||||
public static final long METADATA_VALUE_ATTRIBUTE_PRESENT = 1L;
|
||||
|
||||
/**
|
||||
* Bundle key passed through root hints to the {@link MediaBrowserServiceCompat} to indicate the
|
||||
* maximum number of children of the root node that can be supported by the hosting {@link
|
||||
@ -288,28 +174,6 @@ public final class MediaConstants {
|
||||
public static final String BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED =
|
||||
"android.media.browse.SEARCH_SUPPORTED";
|
||||
|
||||
/**
|
||||
* Bundle key used to pass a browseable {@link android.media.browse.MediaBrowser.MediaItem} that
|
||||
* represents 'Favorite' content or some other notion of preset/pinned content.
|
||||
*
|
||||
* <p>Use this key to indicate to consumers (e.g. Auto and Automotive) that they can display
|
||||
* and/or subscribe to this item.
|
||||
*
|
||||
* <p>When this item is subscribed to, it is expected that the {@link MediaBrowserService} or
|
||||
* {@link MediaBrowserServiceCompat} loads content that the user has marked for easy or quick
|
||||
* access - e.g. favorite radio stations, pinned playlists, etc.
|
||||
*
|
||||
* <p>TYPE: MediaBrowser.MediaItem - note this should not be a {@link
|
||||
* MediaBrowserCompat.MediaItem}
|
||||
*
|
||||
* @see MediaBrowserCompat#getExtras()
|
||||
* @see MediaBrowserServiceCompat#onGetRoot(String, int, Bundle)
|
||||
* @see MediaBrowserServiceCompat.BrowserRoot#BrowserRoot(String, Bundle)
|
||||
*/
|
||||
@SuppressLint("IntentName")
|
||||
public static final String BROWSER_SERVICE_EXTRAS_KEY_FAVORITES_MEDIA_ITEM =
|
||||
"androidx.media.BrowserRoot.Extras.FAVORITES_MEDIA_ITEM";
|
||||
|
||||
/**
|
||||
* Bundle key passed from the {@link MediaBrowserServiceCompat} to the hosting {@link
|
||||
* MediaBrowserCompat} to indicate a preference about how playable instances of {@link
|
||||
@ -687,111 +551,6 @@ public final class MediaConstants {
|
||||
public static final String EXTRAS_KEY_CUSTOM_BROWSER_ACTION_MEDIA_ITEM_ID =
|
||||
"androidx.media.utils.extras.KEY_CUSTOM_BROWSER_ACTION_MEDIA_ITEM_ID";
|
||||
|
||||
/**
|
||||
* {@link Bundle} key set in {@link MediaBrowserServiceCompat.Result} to indicate which browse
|
||||
* node should be displayed next.
|
||||
*
|
||||
* <p>A {@link MediaBrowserServiceCompat} that supports custom browser actions can set this key in
|
||||
* the {@link MediaBrowserServiceCompat.Result} passed in {@link
|
||||
* MediaBrowserServiceCompat#onCustomAction(String, Bundle, MediaBrowserServiceCompat.Result)}.
|
||||
*
|
||||
* <p>If this key is present in a {@link MediaBrowserCompat.CustomActionCallback} data {@link
|
||||
* Bundle} the {@link MediaBrowserCompat} will update the current browse node when {@link
|
||||
* MediaBrowserCompat.CustomActionCallback#onResult(String, Bundle, Bundle)} is called by the
|
||||
* {@link MediaBrowserServiceCompat}. The new browse node will be fetched by {@link
|
||||
* MediaBrowserCompat#getItem(String, MediaBrowserCompat.ItemCallback)}.
|
||||
*
|
||||
* <p>A {@link MediaBrowserServiceCompat} that supports custom browser actions must implement
|
||||
* {@link MediaBrowserServiceCompat#onLoadItem(String, MediaBrowserServiceCompat.Result)} to use
|
||||
* this feature.
|
||||
*
|
||||
* <p>TYPE: string, string {@link MediaBrowserCompat.MediaItem} ID to set as new browse node.
|
||||
*
|
||||
* @see MediaBrowserCompat#sendCustomAction(String, Bundle,
|
||||
* MediaBrowserCompat.CustomActionCallback)
|
||||
* @see MediaBrowserCompat.CustomActionCallback
|
||||
* @see MediaBrowserServiceCompat#onCustomAction(String, Bundle, MediaBrowserServiceCompat.Result)
|
||||
*/
|
||||
public static final String EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_BROWSE_NODE =
|
||||
"androidx.media.utils.extras.KEY_CUSTOM_BROWSER_ACTION_RESULT_BROWSE_NODE";
|
||||
|
||||
/**
|
||||
* {@link Bundle} key set in {@link MediaBrowserServiceCompat.Result} to show the currently
|
||||
* playing item.
|
||||
*
|
||||
* <p>A {@link MediaBrowserServiceCompat} that supports custom browser actions can set this key in
|
||||
* the {@link MediaBrowserServiceCompat.Result} passed in {@link
|
||||
* MediaBrowserServiceCompat#onCustomAction(String, Bundle, MediaBrowserServiceCompat.Result)}.
|
||||
*
|
||||
* <p>If this key is present and the value is true in {@link
|
||||
* MediaBrowserCompat.CustomActionCallback} {@link MediaBrowserServiceCompat.Result}, the
|
||||
* currently playing item will be shown when {@link
|
||||
* MediaBrowserCompat.CustomActionCallback#onResult(String, Bundle, Bundle)} is called by the
|
||||
* {@link MediaBrowserServiceCompat}.
|
||||
*
|
||||
* <p>TYPE: boolean, boolean value of true will show currently playing item.
|
||||
*
|
||||
* @see MediaBrowserCompat#sendCustomAction(String, Bundle,
|
||||
* MediaBrowserCompat.CustomActionCallback)
|
||||
* @see MediaBrowserCompat.CustomActionCallback
|
||||
* @see MediaBrowserServiceCompat#onCustomAction(String, Bundle, MediaBrowserServiceCompat.Result)
|
||||
*/
|
||||
public static final String EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_SHOW_PLAYING_ITEM =
|
||||
"androidx.media.utils.extras.KEY_CUSTOM_BROWSER_ACTION_RESULT_SHOW_PLAYING_ITEM";
|
||||
|
||||
/**
|
||||
* {@link Bundle} key set in {@link MediaBrowserServiceCompat.Result} to refresh a {@link
|
||||
* MediaBrowserCompat.MediaItem} in the browse tree.
|
||||
*
|
||||
* <p>A {@link MediaBrowserServiceCompat} that supports custom browser actions can set this key in
|
||||
* the {@link MediaBrowserServiceCompat.Result} passed in {@link
|
||||
* MediaBrowserServiceCompat#onCustomAction(String, Bundle, MediaBrowserServiceCompat.Result)}.
|
||||
*
|
||||
* <p>If this key is present in {@link MediaBrowserCompat.CustomActionCallback} {@link
|
||||
* MediaBrowserServiceCompat.Result}, the item will be refreshed with {@link
|
||||
* MediaBrowserCompat#getItem(String, MediaBrowserCompat.ItemCallback)} when {@link
|
||||
* MediaBrowserCompat.CustomActionCallback#onProgressUpdate(String, Bundle, Bundle)} or {@link
|
||||
* MediaBrowserCompat.CustomActionCallback#onResult(String, Bundle, Bundle)} is called by the
|
||||
* {@link MediaBrowserServiceCompat}.
|
||||
*
|
||||
* <p>A {@link MediaBrowserServiceCompat} that supports custom browser actions must implement
|
||||
* {@link MediaBrowserServiceCompat#onLoadItem(String, MediaBrowserServiceCompat.Result)} in order
|
||||
* to update the state of the item.
|
||||
*
|
||||
* <p>TYPE: string, string {@link MediaBrowserCompat.MediaItem} ID to refresh.
|
||||
*
|
||||
* @see MediaBrowserCompat#sendCustomAction(String, Bundle,
|
||||
* MediaBrowserCompat.CustomActionCallback)
|
||||
* @see MediaBrowserCompat.CustomActionCallback
|
||||
* @see MediaBrowserServiceCompat#onCustomAction(String, Bundle, MediaBrowserServiceCompat.Result)
|
||||
*/
|
||||
public static final String EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM =
|
||||
"androidx.media.utils.extras.KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM";
|
||||
|
||||
/**
|
||||
* {@link Bundle} key set in {@link MediaBrowserServiceCompat.Result} to set a message for the
|
||||
* user.
|
||||
*
|
||||
* <p>A {@link MediaBrowserServiceCompat} that supports custom browser actions can set this key in
|
||||
* the {@link MediaBrowserServiceCompat.Result} passed in {@link
|
||||
* MediaBrowserServiceCompat#onCustomAction(String, Bundle, MediaBrowserServiceCompat.Result)}.
|
||||
*
|
||||
* <p>If this key is present in {@link MediaBrowserCompat.CustomActionCallback} {@link
|
||||
* MediaBrowserServiceCompat.Result}, the message will be shown to the user when {@link
|
||||
* MediaBrowserCompat.CustomActionCallback#onProgressUpdate(String, Bundle, Bundle)} or {@link
|
||||
* MediaBrowserCompat.CustomActionCallback#onResult(String, Bundle, Bundle)} is called by the
|
||||
* {@link MediaBrowserServiceCompat}.
|
||||
*
|
||||
* <p>TYPE: string, localized message string to show the user.
|
||||
*
|
||||
* @see MediaBrowserCompat#sendCustomAction(String, Bundle,
|
||||
* MediaBrowserCompat.CustomActionCallback)
|
||||
* @see MediaBrowserCompat.CustomActionCallback
|
||||
* @see MediaBrowserServiceCompat#onCustomAction(String, Bundle, MediaBrowserServiceCompat.Result)
|
||||
*/
|
||||
public static final String EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_MESSAGE =
|
||||
"androidx.media.utils.extras.KEY_CUSTOM_BROWSER_ACTION_RESULT_MESSAGE";
|
||||
|
||||
/**
|
||||
* Bundle key used for the media ID in {@link PlaybackStateCompat playback state} extras. It's for
|
||||
* associating the playback state with the media being played so the value is expected to be same
|
||||
@ -897,30 +656,5 @@ public final class MediaConstants {
|
||||
public static final String TRANSPORT_CONTROLS_EXTRAS_KEY_LEGACY_STREAM_TYPE =
|
||||
"android.media.session.extra.LEGACY_STREAM_TYPE";
|
||||
|
||||
/**
|
||||
* Bundle key passed through the {@code extras} of {@link
|
||||
* MediaControllerCompat.TransportControls#prepareFromMediaId(String, Bundle)}, {@link
|
||||
* MediaControllerCompat.TransportControls#prepareFromSearch(String, Bundle)}, {@link
|
||||
* MediaControllerCompat.TransportControls#prepareFromUri(Uri, Bundle)}, {@link
|
||||
* MediaControllerCompat.TransportControls#playFromMediaId(String, Bundle)}, {@link
|
||||
* MediaControllerCompat.TransportControls#playFromSearch(String, Bundle)}, or {@link
|
||||
* MediaControllerCompat.TransportControls#playFromUri(Uri, Bundle)} to indicate whether the
|
||||
* session should shuffle the media to be played or not. The extra parameter is limited to the
|
||||
* current request and doesn't affect the {@link MediaSessionCompat#setShuffleMode(int) shuffle
|
||||
* mode}.
|
||||
*
|
||||
* <p>TYPE: boolean
|
||||
*
|
||||
* @see MediaControllerCompat.TransportControls#prepareFromMediaId(String, Bundle)
|
||||
* @see MediaControllerCompat.TransportControls#prepareFromSearch(String, Bundle)
|
||||
* @see MediaControllerCompat.TransportControls#prepareFromUri(Uri, Bundle)
|
||||
* @see MediaControllerCompat.TransportControls#playFromMediaId(String, Bundle)
|
||||
* @see MediaControllerCompat.TransportControls#playFromSearch(String, Bundle)
|
||||
* @see MediaControllerCompat.TransportControls#playFromUri(Uri, Bundle)
|
||||
*/
|
||||
@SuppressLint("IntentName")
|
||||
public static final String TRANSPORT_CONTROLS_EXTRAS_KEY_SHUFFLE =
|
||||
"androidx.media.MediaControllerCompat.TransportControls.extras.KEY_SHUFFLE";
|
||||
|
||||
private MediaConstants() {}
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ package androidx.media3.session.legacy;
|
||||
import static androidx.annotation.RestrictTo.Scope.LIBRARY;
|
||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.media.AudioManager;
|
||||
@ -44,11 +43,9 @@ import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.annotation.RestrictTo;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.session.R;
|
||||
import androidx.media3.session.legacy.MediaSessionCompat.QueueItem;
|
||||
import androidx.media3.session.legacy.PlaybackStateCompat.CustomAction;
|
||||
import androidx.versionedparcelable.ParcelUtils;
|
||||
import androidx.versionedparcelable.VersionedParcelable;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@ -124,50 +121,6 @@ public final class MediaControllerCompat {
|
||||
public static final String COMMAND_ARGUMENT_INDEX =
|
||||
"android.support.v4.media.session.command.ARGUMENT_INDEX";
|
||||
|
||||
/**
|
||||
* Sets a {@link MediaControllerCompat} in the {@code activity} for later retrieval via {@link
|
||||
* #getMediaController(Activity)}.
|
||||
*
|
||||
* <p>On API 21 and later, {@link Activity#setMediaController(MediaController)} will also be
|
||||
* called.
|
||||
*
|
||||
* @param activity The activity to set the {@code mediaController} in, must not be null.
|
||||
* @param mediaController The controller for the session which should receive media keys and
|
||||
* volume changes on API 21 and later.
|
||||
* @see #getMediaController(Activity)
|
||||
* @see Activity#setMediaController(android.media.session.MediaController)
|
||||
*/
|
||||
public static void setMediaController(Activity activity, MediaControllerCompat mediaController) {
|
||||
activity
|
||||
.getWindow()
|
||||
.getDecorView()
|
||||
.setTag(R.id.media_controller_compat_view_tag, mediaController);
|
||||
if (android.os.Build.VERSION.SDK_INT >= 21) {
|
||||
MediaControllerImplApi21.setMediaController(activity, mediaController);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the {@link MediaControllerCompat} set in the activity by {@link
|
||||
* #setMediaController(Activity, MediaControllerCompat)} for sending media key and volume events.
|
||||
*
|
||||
* <p>This is compatible with {@link Activity#getMediaController()}.
|
||||
*
|
||||
* @param activity The activity to get the media controller from, must not be null.
|
||||
* @return The controller which should receive events.
|
||||
* @see #setMediaController(Activity, MediaControllerCompat)
|
||||
*/
|
||||
@Nullable
|
||||
public static MediaControllerCompat getMediaController(Activity activity) {
|
||||
Object tag = activity.getWindow().getDecorView().getTag(R.id.media_controller_compat_view_tag);
|
||||
if (tag instanceof MediaControllerCompat) {
|
||||
return (MediaControllerCompat) tag;
|
||||
} else if (android.os.Build.VERSION.SDK_INT >= 21) {
|
||||
return MediaControllerImplApi21.getMediaController(activity);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||
static void validateCustomAction(@Nullable String action, @Nullable Bundle args) {
|
||||
if (action == null) {
|
||||
@ -213,18 +166,13 @@ public final class MediaControllerCompat {
|
||||
* @param sessionToken The token of the session to be controlled.
|
||||
*/
|
||||
public MediaControllerCompat(Context context, MediaSessionCompat.Token sessionToken) {
|
||||
if (sessionToken == null) {
|
||||
throw new IllegalArgumentException("sessionToken must not be null");
|
||||
}
|
||||
mRegisteredCallbacks = Collections.synchronizedSet(new HashSet<>());
|
||||
mToken = sessionToken;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 29) {
|
||||
mImpl = new MediaControllerImplApi29(context, sessionToken);
|
||||
} else if (Build.VERSION.SDK_INT >= 21) {
|
||||
mImpl = new MediaControllerImplApi21(context, sessionToken);
|
||||
} else {
|
||||
mImpl = new MediaControllerImplBase(sessionToken);
|
||||
mImpl = new MediaControllerImplApi21(context, sessionToken);
|
||||
}
|
||||
}
|
||||
|
||||
@ -476,17 +424,6 @@ public final class MediaControllerCompat {
|
||||
return mToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the SessionToken in media2 as VersionedParcelable for the session that this controller is
|
||||
* connected to.
|
||||
*
|
||||
* @return The session's token as VersionedParcelable.
|
||||
*/
|
||||
@Nullable
|
||||
public VersionedParcelable getSession2Token() {
|
||||
return mToken.getSession2Token();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the volume of the output this session is playing on. The command will be ignored if it
|
||||
* does not support {@link VolumeProviderCompat#VOLUME_CONTROL_ABSOLUTE}. The flags in {@link
|
||||
@ -516,16 +453,6 @@ public final class MediaControllerCompat {
|
||||
mImpl.adjustVolume(direction, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a callback to receive updates from the Session. Updates will be posted on the caller's
|
||||
* thread.
|
||||
*
|
||||
* @param callback The callback object, must not be null.
|
||||
*/
|
||||
public void registerCallback(Callback callback) {
|
||||
registerCallback(callback, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a callback to receive updates from the session. Updates will be posted on the specified
|
||||
* handler's thread.
|
||||
@ -533,11 +460,7 @@ public final class MediaControllerCompat {
|
||||
* @param callback The callback object, must not be null.
|
||||
* @param handler The handler to post updates on. If null the callers thread will be used.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public void registerCallback(Callback callback, @Nullable Handler handler) {
|
||||
if (callback == null) {
|
||||
throw new IllegalArgumentException("callback must not be null");
|
||||
}
|
||||
if (!mRegisteredCallbacks.add(callback)) {
|
||||
Log.w(TAG, "the callback has already been registered");
|
||||
return;
|
||||
@ -556,9 +479,6 @@ public final class MediaControllerCompat {
|
||||
* @param callback The callback to remove
|
||||
*/
|
||||
public void unregisterCallback(Callback callback) {
|
||||
if (callback == null) {
|
||||
throw new IllegalArgumentException("callback must not be null");
|
||||
}
|
||||
if (!mRegisteredCallbacks.remove(callback)) {
|
||||
Log.w(TAG, "the callback has never been registered");
|
||||
return;
|
||||
@ -656,12 +576,7 @@ public final class MediaControllerCompat {
|
||||
// Sharing this in constructor
|
||||
@SuppressWarnings({"assignment.type.incompatible", "argument.type.incompatible"})
|
||||
public Callback() {
|
||||
if (android.os.Build.VERSION.SDK_INT >= 21) {
|
||||
mCallbackFwk = new MediaControllerCallbackApi21(this);
|
||||
} else {
|
||||
mCallbackFwk = null;
|
||||
mIControllerCallback = new StubCompat(this);
|
||||
}
|
||||
mCallbackFwk = new MediaControllerCallback(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -789,11 +704,10 @@ public final class MediaControllerCompat {
|
||||
}
|
||||
|
||||
// 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 MediaControllerCallback extends MediaController.Callback {
|
||||
private final WeakReference<MediaControllerCompat.Callback> mCallback;
|
||||
|
||||
MediaControllerCallbackApi21(MediaControllerCompat.Callback callback) {
|
||||
MediaControllerCallback(MediaControllerCompat.Callback callback) {
|
||||
mCallback = new WeakReference<>(callback);
|
||||
}
|
||||
|
||||
@ -870,7 +784,7 @@ public final class MediaControllerCompat {
|
||||
callback.onAudioInfoChanged(
|
||||
new PlaybackInfo(
|
||||
info.getPlaybackType(),
|
||||
checkNotNull(AudioAttributesCompat.wrap(info.getAudioAttributes())),
|
||||
AudioAttributesCompat.wrap(info.getAudioAttributes()),
|
||||
info.getVolumeControl(),
|
||||
info.getMaxVolume(),
|
||||
info.getCurrentVolume()));
|
||||
@ -886,7 +800,7 @@ public final class MediaControllerCompat {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(@Nullable String event, @Nullable Bundle extras) throws RemoteException {
|
||||
public void onEvent(@Nullable String event, @Nullable Bundle extras) {
|
||||
MediaControllerCompat.Callback callback = mCallback.get();
|
||||
if (callback != null) {
|
||||
callback.postToHandler(MessageHandler.MSG_EVENT, event, extras);
|
||||
@ -894,7 +808,7 @@ public final class MediaControllerCompat {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSessionDestroyed() throws RemoteException {
|
||||
public void onSessionDestroyed() {
|
||||
MediaControllerCompat.Callback callback = mCallback.get();
|
||||
if (callback != null) {
|
||||
callback.postToHandler(MessageHandler.MSG_DESTROYED, null, null);
|
||||
@ -902,8 +816,7 @@ public final class MediaControllerCompat {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaybackStateChanged(@Nullable PlaybackStateCompat state)
|
||||
throws RemoteException {
|
||||
public void onPlaybackStateChanged(@Nullable PlaybackStateCompat state) {
|
||||
MediaControllerCompat.Callback callback = mCallback.get();
|
||||
if (callback != null) {
|
||||
callback.postToHandler(MessageHandler.MSG_UPDATE_PLAYBACK_STATE, state, null);
|
||||
@ -911,7 +824,7 @@ public final class MediaControllerCompat {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMetadataChanged(@Nullable MediaMetadataCompat metadata) throws RemoteException {
|
||||
public void onMetadataChanged(@Nullable MediaMetadataCompat metadata) {
|
||||
MediaControllerCompat.Callback callback = mCallback.get();
|
||||
if (callback != null) {
|
||||
callback.postToHandler(MessageHandler.MSG_UPDATE_METADATA, metadata, null);
|
||||
@ -919,7 +832,7 @@ public final class MediaControllerCompat {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQueueChanged(@Nullable List<QueueItem> queue) throws RemoteException {
|
||||
public void onQueueChanged(@Nullable List<QueueItem> queue) {
|
||||
MediaControllerCompat.Callback callback = mCallback.get();
|
||||
if (callback != null) {
|
||||
callback.postToHandler(MessageHandler.MSG_UPDATE_QUEUE, queue, null);
|
||||
@ -927,7 +840,7 @@ public final class MediaControllerCompat {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQueueTitleChanged(@Nullable CharSequence title) throws RemoteException {
|
||||
public void onQueueTitleChanged(@Nullable CharSequence title) {
|
||||
MediaControllerCompat.Callback callback = mCallback.get();
|
||||
if (callback != null) {
|
||||
callback.postToHandler(MessageHandler.MSG_UPDATE_QUEUE_TITLE, title, null);
|
||||
@ -935,7 +848,7 @@ public final class MediaControllerCompat {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCaptioningEnabledChanged(boolean enabled) throws RemoteException {
|
||||
public void onCaptioningEnabledChanged(boolean enabled) {
|
||||
MediaControllerCompat.Callback callback = mCallback.get();
|
||||
if (callback != null) {
|
||||
callback.postToHandler(MessageHandler.MSG_UPDATE_CAPTIONING_ENABLED, enabled, null);
|
||||
@ -943,7 +856,7 @@ public final class MediaControllerCompat {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRepeatModeChanged(int repeatMode) throws RemoteException {
|
||||
public void onRepeatModeChanged(int repeatMode) {
|
||||
MediaControllerCompat.Callback callback = mCallback.get();
|
||||
if (callback != null) {
|
||||
callback.postToHandler(MessageHandler.MSG_UPDATE_REPEAT_MODE, repeatMode, null);
|
||||
@ -951,12 +864,12 @@ public final class MediaControllerCompat {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShuffleModeChangedRemoved(boolean enabled) throws RemoteException {
|
||||
public void onShuffleModeChangedRemoved(boolean enabled) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShuffleModeChanged(int shuffleMode) throws RemoteException {
|
||||
public void onShuffleModeChanged(int shuffleMode) {
|
||||
MediaControllerCompat.Callback callback = mCallback.get();
|
||||
if (callback != null) {
|
||||
callback.postToHandler(MessageHandler.MSG_UPDATE_SHUFFLE_MODE, shuffleMode, null);
|
||||
@ -964,7 +877,7 @@ public final class MediaControllerCompat {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExtrasChanged(@Nullable Bundle extras) throws RemoteException {
|
||||
public void onExtrasChanged(@Nullable Bundle extras) {
|
||||
MediaControllerCompat.Callback callback = mCallback.get();
|
||||
if (callback != null) {
|
||||
callback.postToHandler(MessageHandler.MSG_UPDATE_EXTRAS, extras, null);
|
||||
@ -972,7 +885,7 @@ public final class MediaControllerCompat {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVolumeInfoChanged(@Nullable ParcelableVolumeInfo info) throws RemoteException {
|
||||
public void onVolumeInfoChanged(@Nullable ParcelableVolumeInfo info) {
|
||||
MediaControllerCompat.Callback callback = mCallback.get();
|
||||
if (callback != null) {
|
||||
PlaybackInfo pi = null;
|
||||
@ -990,7 +903,7 @@ public final class MediaControllerCompat {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSessionReady() throws RemoteException {
|
||||
public void onSessionReady() {
|
||||
MediaControllerCompat.Callback callback = mCallback.get();
|
||||
if (callback != null) {
|
||||
callback.postToHandler(MessageHandler.MSG_SESSION_READY, null, null);
|
||||
@ -1231,13 +1144,6 @@ public final class MediaControllerCompat {
|
||||
*/
|
||||
public void setPlaybackSpeed(float speed) {}
|
||||
|
||||
/**
|
||||
* Enables/disables captioning for this session.
|
||||
*
|
||||
* @param enabled {@code true} to enable captioning, {@code false} to disable.
|
||||
*/
|
||||
public abstract void setCaptioningEnabled(boolean enabled);
|
||||
|
||||
/**
|
||||
* Sets the repeat mode for this session.
|
||||
*
|
||||
@ -1448,533 +1354,6 @@ public final class MediaControllerCompat {
|
||||
Object getMediaController();
|
||||
}
|
||||
|
||||
static class MediaControllerImplBase implements MediaControllerImpl {
|
||||
private IMediaSession mBinder;
|
||||
@Nullable private TransportControls mTransportControls;
|
||||
@Nullable private Bundle mSessionInfo;
|
||||
|
||||
MediaControllerImplBase(MediaSessionCompat.Token token) {
|
||||
mBinder = IMediaSession.Stub.asInterface((IBinder) token.getToken());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerCallback(Callback callback, Handler handler) {
|
||||
if (callback == null) {
|
||||
throw new IllegalArgumentException("callback may not be null.");
|
||||
}
|
||||
try {
|
||||
mBinder.asBinder().linkToDeath(callback, 0);
|
||||
mBinder.registerCallbackListener(checkNotNull(callback.mIControllerCallback));
|
||||
callback.postToHandler(Callback.MessageHandler.MSG_SESSION_READY, null, null);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in registerCallback.", e);
|
||||
callback.postToHandler(Callback.MessageHandler.MSG_DESTROYED, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterCallback(Callback callback) {
|
||||
if (callback == null) {
|
||||
throw new IllegalArgumentException("callback may not be null.");
|
||||
}
|
||||
try {
|
||||
mBinder.unregisterCallbackListener(checkNotNull(callback.mIControllerCallback));
|
||||
mBinder.asBinder().unlinkToDeath(callback, 0);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in unregisterCallback.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchMediaButtonEvent(KeyEvent event) {
|
||||
if (event == null) {
|
||||
throw new IllegalArgumentException("event may not be null.");
|
||||
}
|
||||
try {
|
||||
mBinder.sendMediaButton(event);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in dispatchMediaButtonEvent.", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportControls getTransportControls() {
|
||||
if (mTransportControls == null) {
|
||||
mTransportControls = new TransportControlsBase(mBinder);
|
||||
}
|
||||
|
||||
return mTransportControls;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PlaybackStateCompat getPlaybackState() {
|
||||
try {
|
||||
return mBinder.getPlaybackState();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in getPlaybackState.", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public MediaMetadataCompat getMetadata() {
|
||||
try {
|
||||
return mBinder.getMetadata();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in getMetadata.", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<QueueItem> getQueue() {
|
||||
try {
|
||||
return mBinder.getQueue();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in getQueue.", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addQueueItem(MediaDescriptionCompat description) {
|
||||
try {
|
||||
long flags = mBinder.getFlags();
|
||||
if ((flags & MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS) == 0) {
|
||||
throw new UnsupportedOperationException(
|
||||
"This session doesn't support queue management operations");
|
||||
}
|
||||
mBinder.addQueueItem(description);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in addQueueItem.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addQueueItem(MediaDescriptionCompat description, int index) {
|
||||
try {
|
||||
long flags = mBinder.getFlags();
|
||||
if ((flags & MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS) == 0) {
|
||||
throw new UnsupportedOperationException(
|
||||
"This session doesn't support queue management operations");
|
||||
}
|
||||
mBinder.addQueueItemAt(description, index);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in addQueueItemAt.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeQueueItem(MediaDescriptionCompat description) {
|
||||
try {
|
||||
long flags = mBinder.getFlags();
|
||||
if ((flags & MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS) == 0) {
|
||||
throw new UnsupportedOperationException(
|
||||
"This session doesn't support queue management operations");
|
||||
}
|
||||
mBinder.removeQueueItem(description);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in removeQueueItem.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public CharSequence getQueueTitle() {
|
||||
try {
|
||||
return mBinder.getQueueTitle();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in getQueueTitle.", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Bundle getExtras() {
|
||||
try {
|
||||
return mBinder.getExtras();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in getExtras.", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRatingType() {
|
||||
try {
|
||||
return mBinder.getRatingType();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in getRatingType.", e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCaptioningEnabled() {
|
||||
try {
|
||||
return mBinder.isCaptioningEnabled();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in isCaptioningEnabled.", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRepeatMode() {
|
||||
try {
|
||||
return mBinder.getRepeatMode();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in getRepeatMode.", e);
|
||||
}
|
||||
return PlaybackStateCompat.REPEAT_MODE_INVALID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getShuffleMode() {
|
||||
try {
|
||||
return mBinder.getShuffleMode();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in getShuffleMode.", e);
|
||||
}
|
||||
return PlaybackStateCompat.SHUFFLE_MODE_INVALID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getFlags() {
|
||||
try {
|
||||
return mBinder.getFlags();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in getFlags.", e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PlaybackInfo getPlaybackInfo() {
|
||||
try {
|
||||
ParcelableVolumeInfo info = mBinder.getVolumeAttributes();
|
||||
if (info == null) {
|
||||
return null;
|
||||
}
|
||||
PlaybackInfo pi =
|
||||
new PlaybackInfo(
|
||||
info.volumeType,
|
||||
info.audioStream,
|
||||
info.controlType,
|
||||
info.maxVolume,
|
||||
info.currentVolume);
|
||||
return pi;
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in getPlaybackInfo.", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PendingIntent getSessionActivity() {
|
||||
try {
|
||||
return mBinder.getLaunchPendingIntent();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in getSessionActivity.", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVolumeTo(int value, int flags) {
|
||||
try {
|
||||
mBinder.setVolumeTo(value, flags, null);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in setVolumeTo.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void adjustVolume(int direction, int flags) {
|
||||
try {
|
||||
mBinder.adjustVolume(direction, flags, null);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in adjustVolume.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendCommand(String command, @Nullable Bundle params, @Nullable ResultReceiver cb) {
|
||||
try {
|
||||
mBinder.sendCommand(
|
||||
command, params, cb == null ? null : new MediaSessionCompat.ResultReceiverWrapper(cb));
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in sendCommand.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSessionReady() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getPackageName() {
|
||||
try {
|
||||
return mBinder.getPackageName();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in getPackageName.", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle getSessionInfo() {
|
||||
try {
|
||||
mSessionInfo = mBinder.getSessionInfo();
|
||||
} catch (RemoteException e) {
|
||||
Log.d(TAG, "Dead object in getSessionInfo.", e);
|
||||
}
|
||||
|
||||
mSessionInfo = MediaSessionCompat.unparcelWithClassLoader(mSessionInfo);
|
||||
return mSessionInfo == null ? Bundle.EMPTY : new Bundle(mSessionInfo);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object getMediaController() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static class TransportControlsBase extends TransportControls {
|
||||
private IMediaSession mBinder;
|
||||
|
||||
public TransportControlsBase(IMediaSession binder) {
|
||||
mBinder = binder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepare() {
|
||||
try {
|
||||
mBinder.prepare();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in prepare.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareFromMediaId(String mediaId, @Nullable Bundle extras) {
|
||||
try {
|
||||
mBinder.prepareFromMediaId(mediaId, extras);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in prepareFromMediaId.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareFromSearch(String query, @Nullable Bundle extras) {
|
||||
try {
|
||||
mBinder.prepareFromSearch(query, extras);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in prepareFromSearch.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareFromUri(Uri uri, @Nullable Bundle extras) {
|
||||
try {
|
||||
mBinder.prepareFromUri(uri, extras);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in prepareFromUri.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void play() {
|
||||
try {
|
||||
mBinder.play();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in play.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playFromMediaId(String mediaId, @Nullable Bundle extras) {
|
||||
try {
|
||||
mBinder.playFromMediaId(mediaId, extras);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in playFromMediaId.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playFromSearch(String query, @Nullable Bundle extras) {
|
||||
try {
|
||||
mBinder.playFromSearch(query, extras);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in playFromSearch.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playFromUri(Uri uri, @Nullable Bundle extras) {
|
||||
try {
|
||||
mBinder.playFromUri(uri, extras);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in playFromUri.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skipToQueueItem(long id) {
|
||||
try {
|
||||
mBinder.skipToQueueItem(id);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in skipToQueueItem.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pause() {
|
||||
try {
|
||||
mBinder.pause();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in pause.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
try {
|
||||
mBinder.stop();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in stop.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seekTo(long pos) {
|
||||
try {
|
||||
mBinder.seekTo(pos);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in seekTo.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fastForward() {
|
||||
try {
|
||||
mBinder.fastForward();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in fastForward.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skipToNext() {
|
||||
try {
|
||||
mBinder.next();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in skipToNext.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rewind() {
|
||||
try {
|
||||
mBinder.rewind();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in rewind.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skipToPrevious() {
|
||||
try {
|
||||
mBinder.previous();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in skipToPrevious.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRating(RatingCompat rating) {
|
||||
try {
|
||||
mBinder.rate(rating);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in setRating.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRating(RatingCompat rating, @Nullable Bundle extras) {
|
||||
try {
|
||||
mBinder.rateWithExtras(rating, extras);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in setRating.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlaybackSpeed(float speed) {
|
||||
if (speed == 0.0f) {
|
||||
throw new IllegalArgumentException("speed must not be zero");
|
||||
}
|
||||
try {
|
||||
mBinder.setPlaybackSpeed(speed);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in setPlaybackSpeed.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCaptioningEnabled(boolean enabled) {
|
||||
try {
|
||||
mBinder.setCaptioningEnabled(enabled);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in setCaptioningEnabled.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRepeatMode(@PlaybackStateCompat.RepeatMode int repeatMode) {
|
||||
try {
|
||||
mBinder.setRepeatMode(repeatMode);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in setRepeatMode.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShuffleMode(@PlaybackStateCompat.ShuffleMode int shuffleMode) {
|
||||
try {
|
||||
mBinder.setShuffleMode(shuffleMode);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in setShuffleMode.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendCustomAction(CustomAction customAction, @Nullable Bundle args) {
|
||||
sendCustomAction(customAction.getAction(), args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendCustomAction(String action, @Nullable Bundle args) {
|
||||
validateCustomAction(action, args);
|
||||
try {
|
||||
mBinder.sendCustomAction(action, args);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in sendCustomAction.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(21)
|
||||
static class MediaControllerImplApi21 implements MediaControllerImpl {
|
||||
protected final MediaController mControllerFwk;
|
||||
|
||||
@ -1983,7 +1362,7 @@ public final class MediaControllerCompat {
|
||||
@GuardedBy("mLock")
|
||||
private final List<Callback> mPendingCallbacks = new ArrayList<>();
|
||||
|
||||
private HashMap<Callback, ExtraCallback> mCallbackMap = new HashMap<>();
|
||||
private final HashMap<Callback, ExtraCallback> mCallbackMap = new HashMap<>();
|
||||
|
||||
@Nullable protected Bundle mSessionInfo;
|
||||
|
||||
@ -1993,8 +1372,7 @@ public final class MediaControllerCompat {
|
||||
@SuppressWarnings({"assignment.type.incompatible", "method.invocation.invalid"})
|
||||
MediaControllerImplApi21(Context context, MediaSessionCompat.Token sessionToken) {
|
||||
mSessionToken = sessionToken;
|
||||
mControllerFwk =
|
||||
new MediaController(context, (MediaSession.Token) checkNotNull(mSessionToken.getToken()));
|
||||
mControllerFwk = new MediaController(context, mSessionToken.getToken());
|
||||
if (mSessionToken.getExtraBinder() == null) {
|
||||
requestExtraBinder();
|
||||
}
|
||||
@ -2215,7 +1593,7 @@ public final class MediaControllerCompat {
|
||||
return volumeInfoFwk != null
|
||||
? new PlaybackInfo(
|
||||
volumeInfoFwk.getPlaybackType(),
|
||||
checkNotNull(AudioAttributesCompat.wrap(volumeInfoFwk.getAudioAttributes())),
|
||||
AudioAttributesCompat.wrap(volumeInfoFwk.getAudioAttributes()),
|
||||
volumeInfoFwk.getVolumeControl(),
|
||||
volumeInfoFwk.getMaxVolume(),
|
||||
volumeInfoFwk.getCurrentVolume())
|
||||
@ -2304,29 +1682,8 @@ public final class MediaControllerCompat {
|
||||
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 WeakReference<MediaControllerImplApi21> mMediaControllerImpl;
|
||||
private final WeakReference<MediaControllerImplApi21> mMediaControllerImpl;
|
||||
|
||||
ExtraBinderRequestResultReceiver(MediaControllerImplApi21 mediaControllerImpl) {
|
||||
super(null /* handler */);
|
||||
@ -2357,37 +1714,37 @@ public final class MediaControllerCompat {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSessionDestroyed() throws RemoteException {
|
||||
public void onSessionDestroyed() {
|
||||
// Will not be called.
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMetadataChanged(@Nullable MediaMetadataCompat metadata) throws RemoteException {
|
||||
public void onMetadataChanged(@Nullable MediaMetadataCompat metadata) {
|
||||
// Will not be called.
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQueueChanged(@Nullable List<QueueItem> queue) throws RemoteException {
|
||||
public void onQueueChanged(@Nullable List<QueueItem> queue) {
|
||||
// Will not be called.
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQueueTitleChanged(@Nullable CharSequence title) throws RemoteException {
|
||||
public void onQueueTitleChanged(@Nullable CharSequence title) {
|
||||
// Will not be called.
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExtrasChanged(@Nullable Bundle extras) throws RemoteException {
|
||||
public void onExtrasChanged(@Nullable Bundle extras) {
|
||||
// Will not be called.
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVolumeInfoChanged(@Nullable ParcelableVolumeInfo info) throws RemoteException {
|
||||
public void onVolumeInfoChanged(@Nullable ParcelableVolumeInfo info) {
|
||||
// Will not be called.
|
||||
throw new AssertionError();
|
||||
}
|
||||
@ -2411,7 +1768,6 @@ public final class MediaControllerCompat {
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(21)
|
||||
static class TransportControlsApi21 extends TransportControls {
|
||||
protected final MediaController.TransportControls mControlsFwk;
|
||||
|
||||
@ -2491,7 +1847,7 @@ public final class MediaControllerCompat {
|
||||
@SuppressWarnings("argument.type.incompatible") // Platform controller accepts null rating
|
||||
@Override
|
||||
public void setRating(RatingCompat rating) {
|
||||
mControlsFwk.setRating(rating != null ? (Rating) rating.getRating() : null);
|
||||
mControlsFwk.setRating((Rating) rating.getRating());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -2514,13 +1870,6 @@ public final class MediaControllerCompat {
|
||||
sendCustomAction(MediaSessionCompat.ACTION_SET_PLAYBACK_SPEED, bundle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCaptioningEnabled(boolean enabled) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putBoolean(MediaSessionCompat.ACTION_ARGUMENT_CAPTIONING_ENABLED, enabled);
|
||||
sendCustomAction(MediaSessionCompat.ACTION_SET_CAPTIONING_ENABLED, bundle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRepeatMode(@PlaybackStateCompat.RepeatMode int repeatMode) {
|
||||
Bundle bundle = new Bundle();
|
||||
@ -2549,7 +1898,7 @@ public final class MediaControllerCompat {
|
||||
|
||||
@Override
|
||||
public void playFromUri(Uri uri, @Nullable Bundle extras) {
|
||||
if (uri == null || Uri.EMPTY.equals(uri)) {
|
||||
if (Uri.EMPTY.equals(uri)) {
|
||||
throw new IllegalArgumentException("You must specify a non-empty Uri for playFromUri.");
|
||||
}
|
||||
Bundle bundle = new Bundle();
|
||||
|
@ -16,7 +16,6 @@
|
||||
package androidx.media3.session.legacy;
|
||||
|
||||
import static androidx.annotation.RestrictTo.Scope.LIBRARY;
|
||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.Bitmap;
|
||||
@ -26,7 +25,6 @@ import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.text.TextUtils;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.annotation.RestrictTo;
|
||||
@ -34,13 +32,12 @@ import androidx.media3.common.util.UnstableApi;
|
||||
|
||||
/**
|
||||
* 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 MediaMetadataCompat#getDescription()}.
|
||||
* Builder.
|
||||
*/
|
||||
@UnstableApi
|
||||
@RestrictTo(LIBRARY)
|
||||
@SuppressLint("BanParcelableUsage")
|
||||
public final class MediaDescriptionCompat implements Parcelable {
|
||||
private static final String TAG = "MediaDescriptionCompat";
|
||||
|
||||
/**
|
||||
* Used as a long extra field to indicate the bluetooth folder type of the media item as specified
|
||||
@ -60,7 +57,8 @@ public final class MediaDescriptionCompat implements Parcelable {
|
||||
*
|
||||
* @see #getExtras()
|
||||
*/
|
||||
public static final String EXTRA_BT_FOLDER_TYPE = "android.media.extra.BT_FOLDER_TYPE";
|
||||
@SuppressLint("InlinedApi") // Inlined compile time constant
|
||||
public static final String EXTRA_BT_FOLDER_TYPE = MediaDescription.EXTRA_BT_FOLDER_TYPE;
|
||||
|
||||
/**
|
||||
* The type of folder that is unknown or contains media elements of mixed types as specified in
|
||||
@ -196,20 +194,6 @@ public final class MediaDescriptionCompat implements Parcelable {
|
||||
mMediaUri = mediaUri;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
MediaDescriptionCompat(Parcel in) {
|
||||
mMediaId = in.readString();
|
||||
mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
|
||||
mSubtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
|
||||
mDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
|
||||
|
||||
ClassLoader loader = getClass().getClassLoader();
|
||||
mIcon = in.readParcelable(loader);
|
||||
mIconUri = in.readParcelable(loader);
|
||||
mExtras = in.readBundle(loader);
|
||||
mMediaUri = in.readParcelable(loader);
|
||||
}
|
||||
|
||||
/** Returns the media id or null. See {@link MediaMetadataCompat#METADATA_KEY_MEDIA_ID}. */
|
||||
@Nullable
|
||||
public String getMediaId() {
|
||||
@ -293,18 +277,7 @@ public final class MediaDescriptionCompat implements Parcelable {
|
||||
|
||||
@Override
|
||||
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);
|
||||
}
|
||||
getMediaDescription().writeToParcel(dest, flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -315,22 +288,19 @@ public final class MediaDescriptionCompat implements Parcelable {
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@RequiresApi(21)
|
||||
public Object getMediaDescription() {
|
||||
public MediaDescription getMediaDescription() {
|
||||
if (mDescriptionFwk != null) {
|
||||
return mDescriptionFwk;
|
||||
}
|
||||
MediaDescription.Builder bob = Api21Impl.createBuilder();
|
||||
Api21Impl.setMediaId(bob, mMediaId);
|
||||
Api21Impl.setTitle(bob, mTitle);
|
||||
Api21Impl.setSubtitle(bob, mSubtitle);
|
||||
Api21Impl.setDescription(bob, mDescription);
|
||||
Api21Impl.setIconBitmap(bob, mIcon);
|
||||
Api21Impl.setIconUri(bob, mIconUri);
|
||||
MediaDescription.Builder bob = new MediaDescription.Builder();
|
||||
bob.setMediaId(mMediaId);
|
||||
bob.setTitle(mTitle);
|
||||
bob.setSubtitle(mSubtitle);
|
||||
bob.setDescription(mDescription);
|
||||
bob.setIconBitmap(mIcon);
|
||||
bob.setIconUri(mIconUri);
|
||||
// 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
|
||||
// fromMediaDescription(getMediaDescription(mediaDescriptionCompat)) returns
|
||||
@ -344,14 +314,14 @@ public final class MediaDescriptionCompat implements Parcelable {
|
||||
extras = new Bundle(mExtras);
|
||||
}
|
||||
extras.putParcelable(DESCRIPTION_KEY_MEDIA_URI, mMediaUri);
|
||||
Api21Impl.setExtras(bob, extras);
|
||||
bob.setExtras(extras);
|
||||
} else {
|
||||
Api21Impl.setExtras(bob, mExtras);
|
||||
bob.setExtras(mExtras);
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= 23) {
|
||||
Api23Impl.setMediaUri(bob, mMediaUri);
|
||||
}
|
||||
mDescriptionFwk = Api21Impl.build(bob);
|
||||
mDescriptionFwk = bob.build();
|
||||
|
||||
return mDescriptionFwk;
|
||||
}
|
||||
@ -359,71 +329,56 @@ public final class MediaDescriptionCompat implements Parcelable {
|
||||
/**
|
||||
* 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.
|
||||
* @return An equivalent {@link MediaMetadataCompat} object, or null if none.
|
||||
* @param description A {@link android.media.MediaDescription} object.
|
||||
* @return An equivalent {@link MediaMetadataCompat} object.
|
||||
*/
|
||||
@Nullable
|
||||
@SuppressWarnings("deprecation")
|
||||
public static MediaDescriptionCompat fromMediaDescription(Object descriptionObj) {
|
||||
if (descriptionObj != null && Build.VERSION.SDK_INT >= 21) {
|
||||
Builder bob = new Builder();
|
||||
MediaDescription description = (MediaDescription) descriptionObj;
|
||||
bob.setMediaId(Api21Impl.getMediaId(description));
|
||||
bob.setTitle(Api21Impl.getTitle(description));
|
||||
bob.setSubtitle(Api21Impl.getSubtitle(description));
|
||||
bob.setDescription(Api21Impl.getDescription(description));
|
||||
bob.setIconBitmap(Api21Impl.getIconBitmap(description));
|
||||
bob.setIconUri(Api21Impl.getIconUri(description));
|
||||
Bundle extras = Api21Impl.getExtras(description);
|
||||
extras = MediaSessionCompat.unparcelWithClassLoader(extras);
|
||||
if (extras != null) {
|
||||
extras = new Bundle(extras);
|
||||
}
|
||||
Uri mediaUri = null;
|
||||
if (extras != null) {
|
||||
mediaUri = extras.getParcelable(DESCRIPTION_KEY_MEDIA_URI);
|
||||
if (mediaUri != null) {
|
||||
if (extras.containsKey(DESCRIPTION_KEY_NULL_BUNDLE_FLAG) && extras.size() == 2) {
|
||||
// The extras were only created for the media URI, so we set it back to null to
|
||||
// ensure mediaDescriptionCompat.getExtras() equals
|
||||
// fromMediaDescription(getMediaDescription(mediaDescriptionCompat)).getExtras()
|
||||
extras = null;
|
||||
} else {
|
||||
// Remove media URI keys to ensure mediaDescriptionCompat.getExtras().keySet()
|
||||
// equals fromMediaDescription(getMediaDescription(mediaDescriptionCompat))
|
||||
// .getExtras().keySet()
|
||||
extras.remove(DESCRIPTION_KEY_MEDIA_URI);
|
||||
extras.remove(DESCRIPTION_KEY_NULL_BUNDLE_FLAG);
|
||||
}
|
||||
public static MediaDescriptionCompat fromMediaDescription(MediaDescription description) {
|
||||
Builder bob = new Builder();
|
||||
bob.setMediaId(description.getMediaId());
|
||||
bob.setTitle(description.getTitle());
|
||||
bob.setSubtitle(description.getSubtitle());
|
||||
bob.setDescription(description.getDescription());
|
||||
bob.setIconBitmap(description.getIconBitmap());
|
||||
bob.setIconUri(description.getIconUri());
|
||||
Bundle extras = description.getExtras();
|
||||
extras = MediaSessionCompat.unparcelWithClassLoader(extras);
|
||||
if (extras != null) {
|
||||
extras = new Bundle(extras);
|
||||
}
|
||||
Uri mediaUri = null;
|
||||
if (extras != null) {
|
||||
mediaUri = extras.getParcelable(DESCRIPTION_KEY_MEDIA_URI);
|
||||
if (mediaUri != null) {
|
||||
if (extras.containsKey(DESCRIPTION_KEY_NULL_BUNDLE_FLAG) && extras.size() == 2) {
|
||||
// The extras were only created for the media URI, so we set it back to null to
|
||||
// ensure mediaDescriptionCompat.getExtras() equals
|
||||
// fromMediaDescription(getMediaDescription(mediaDescriptionCompat)).getExtras()
|
||||
extras = null;
|
||||
} else {
|
||||
// Remove media URI keys to ensure mediaDescriptionCompat.getExtras().keySet()
|
||||
// equals fromMediaDescription(getMediaDescription(mediaDescriptionCompat))
|
||||
// .getExtras().keySet()
|
||||
extras.remove(DESCRIPTION_KEY_MEDIA_URI);
|
||||
extras.remove(DESCRIPTION_KEY_NULL_BUNDLE_FLAG);
|
||||
}
|
||||
}
|
||||
bob.setExtras(extras);
|
||||
if (mediaUri != null) {
|
||||
bob.setMediaUri(mediaUri);
|
||||
} else if (Build.VERSION.SDK_INT >= 23) {
|
||||
bob.setMediaUri(Api23Impl.getMediaUri(description));
|
||||
}
|
||||
MediaDescriptionCompat descriptionCompat = bob.build();
|
||||
descriptionCompat.mDescriptionFwk = description;
|
||||
|
||||
return descriptionCompat;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
bob.setExtras(extras);
|
||||
if (mediaUri != null) {
|
||||
bob.setMediaUri(mediaUri);
|
||||
} else if (Build.VERSION.SDK_INT >= 23) {
|
||||
bob.setMediaUri(Api23Impl.getMediaUri(description));
|
||||
}
|
||||
MediaDescriptionCompat descriptionCompat = bob.build();
|
||||
descriptionCompat.mDescriptionFwk = description;
|
||||
return descriptionCompat;
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<MediaDescriptionCompat> CREATOR =
|
||||
new Parcelable.Creator<MediaDescriptionCompat>() {
|
||||
@Override
|
||||
public MediaDescriptionCompat createFromParcel(Parcel in) {
|
||||
if (Build.VERSION.SDK_INT < 21) {
|
||||
return new MediaDescriptionCompat(in);
|
||||
} else {
|
||||
return checkNotNull(
|
||||
fromMediaDescription(MediaDescription.CREATOR.createFromParcel(in)));
|
||||
}
|
||||
return fromMediaDescription(MediaDescription.CREATOR.createFromParcel(in));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -545,83 +500,6 @@ public final class MediaDescriptionCompat implements Parcelable {
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(21)
|
||||
private static class Api21Impl {
|
||||
private Api21Impl() {}
|
||||
|
||||
static MediaDescription.Builder createBuilder() {
|
||||
return new MediaDescription.Builder();
|
||||
}
|
||||
|
||||
static void setMediaId(MediaDescription.Builder builder, @Nullable String mediaId) {
|
||||
builder.setMediaId(mediaId);
|
||||
}
|
||||
|
||||
static void setTitle(MediaDescription.Builder builder, @Nullable CharSequence title) {
|
||||
builder.setTitle(title);
|
||||
}
|
||||
|
||||
static void setSubtitle(MediaDescription.Builder builder, @Nullable CharSequence subtitle) {
|
||||
builder.setSubtitle(subtitle);
|
||||
}
|
||||
|
||||
static void setDescription(
|
||||
MediaDescription.Builder builder, @Nullable CharSequence description) {
|
||||
builder.setDescription(description);
|
||||
}
|
||||
|
||||
static void setIconBitmap(MediaDescription.Builder builder, @Nullable Bitmap icon) {
|
||||
builder.setIconBitmap(icon);
|
||||
}
|
||||
|
||||
static void setIconUri(MediaDescription.Builder builder, @Nullable Uri iconUri) {
|
||||
builder.setIconUri(iconUri);
|
||||
}
|
||||
|
||||
static void setExtras(MediaDescription.Builder builder, @Nullable Bundle extras) {
|
||||
builder.setExtras(extras);
|
||||
}
|
||||
|
||||
static MediaDescription build(MediaDescription.Builder builder) {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static String getMediaId(MediaDescription description) {
|
||||
return description.getMediaId();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static CharSequence getTitle(MediaDescription description) {
|
||||
return description.getTitle();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static CharSequence getSubtitle(MediaDescription description) {
|
||||
return description.getSubtitle();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static CharSequence getDescription(MediaDescription description) {
|
||||
return description.getDescription();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static Bitmap getIconBitmap(MediaDescription description) {
|
||||
return description.getIconBitmap();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static Uri getIconUri(MediaDescription description) {
|
||||
return description.getIconUri();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static Bundle getExtras(MediaDescription description) {
|
||||
return description.getExtras();
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(23)
|
||||
private static class Api23Impl {
|
||||
private Api23Impl() {}
|
||||
|
@ -22,23 +22,18 @@ import android.annotation.SuppressLint;
|
||||
import android.graphics.Bitmap;
|
||||
import android.media.MediaMetadata;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.annotation.RestrictTo;
|
||||
import androidx.annotation.StringDef;
|
||||
import androidx.collection.ArrayMap;
|
||||
import androidx.media3.common.util.NullableType;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.session.legacy.MediaControllerCompat.TransportControls;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Set;
|
||||
|
||||
/** Contains metadata about an item, such as the title, artist, etc. */
|
||||
@UnstableApi
|
||||
@ -48,55 +43,55 @@ public final class MediaMetadataCompat implements Parcelable {
|
||||
private static final String TAG = "MediaMetadata";
|
||||
|
||||
/** The title of the media. */
|
||||
public static final String METADATA_KEY_TITLE = "android.media.metadata.TITLE";
|
||||
public static final String METADATA_KEY_TITLE = MediaMetadata.METADATA_KEY_TITLE;
|
||||
|
||||
/** The artist of the media. */
|
||||
public static final String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST";
|
||||
public static final String METADATA_KEY_ARTIST = MediaMetadata.METADATA_KEY_ARTIST;
|
||||
|
||||
/**
|
||||
* The duration of the media in ms. A negative duration indicates that the duration is unknown (or
|
||||
* infinite).
|
||||
*/
|
||||
public static final String METADATA_KEY_DURATION = "android.media.metadata.DURATION";
|
||||
public static final String METADATA_KEY_DURATION = MediaMetadata.METADATA_KEY_DURATION;
|
||||
|
||||
/** The album title for the media. */
|
||||
public static final String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM";
|
||||
public static final String METADATA_KEY_ALBUM = MediaMetadata.METADATA_KEY_ALBUM;
|
||||
|
||||
/** The author of the media. */
|
||||
public static final String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR";
|
||||
public static final String METADATA_KEY_AUTHOR = MediaMetadata.METADATA_KEY_AUTHOR;
|
||||
|
||||
/** The writer of the media. */
|
||||
public static final String METADATA_KEY_WRITER = "android.media.metadata.WRITER";
|
||||
public static final String METADATA_KEY_WRITER = MediaMetadata.METADATA_KEY_WRITER;
|
||||
|
||||
/** The composer of the media. */
|
||||
public static final String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER";
|
||||
public static final String METADATA_KEY_COMPOSER = MediaMetadata.METADATA_KEY_COMPOSER;
|
||||
|
||||
/** The compilation status of the media. */
|
||||
public static final String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION";
|
||||
public static final String METADATA_KEY_COMPILATION = MediaMetadata.METADATA_KEY_COMPILATION;
|
||||
|
||||
/**
|
||||
* The date the media was created or published. The format is unspecified but RFC 3339 is
|
||||
* recommended.
|
||||
*/
|
||||
public static final String METADATA_KEY_DATE = "android.media.metadata.DATE";
|
||||
public static final String METADATA_KEY_DATE = MediaMetadata.METADATA_KEY_DATE;
|
||||
|
||||
/** The year the media was created or published as a long. */
|
||||
public static final String METADATA_KEY_YEAR = "android.media.metadata.YEAR";
|
||||
public static final String METADATA_KEY_YEAR = MediaMetadata.METADATA_KEY_YEAR;
|
||||
|
||||
/** The genre of the media. */
|
||||
public static final String METADATA_KEY_GENRE = "android.media.metadata.GENRE";
|
||||
public static final String METADATA_KEY_GENRE = MediaMetadata.METADATA_KEY_GENRE;
|
||||
|
||||
/** The track number for the media. */
|
||||
public static final String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
|
||||
public static final String METADATA_KEY_TRACK_NUMBER = MediaMetadata.METADATA_KEY_TRACK_NUMBER;
|
||||
|
||||
/** The number of tracks in the media's original source. */
|
||||
public static final String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS";
|
||||
public static final String METADATA_KEY_NUM_TRACKS = MediaMetadata.METADATA_KEY_NUM_TRACKS;
|
||||
|
||||
/** The disc number for the media's original source. */
|
||||
public static final String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
|
||||
public static final String METADATA_KEY_DISC_NUMBER = MediaMetadata.METADATA_KEY_DISC_NUMBER;
|
||||
|
||||
/** The artist for the album of the media's original source. */
|
||||
public static final String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
|
||||
public static final String METADATA_KEY_ALBUM_ARTIST = MediaMetadata.METADATA_KEY_ALBUM_ARTIST;
|
||||
|
||||
/**
|
||||
* The artwork for the media as a {@link Bitmap}.
|
||||
@ -104,55 +99,55 @@ public final class MediaMetadataCompat implements Parcelable {
|
||||
* <p>The artwork should be relatively small and may be scaled down if it is too large. For higher
|
||||
* resolution artwork {@link #METADATA_KEY_ART_URI} should be used instead.
|
||||
*/
|
||||
public static final String METADATA_KEY_ART = "android.media.metadata.ART";
|
||||
public static final String METADATA_KEY_ART = MediaMetadata.METADATA_KEY_ART;
|
||||
|
||||
/** The artwork for the media as a Uri style String. */
|
||||
public static final String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI";
|
||||
public static final String METADATA_KEY_ART_URI = MediaMetadata.METADATA_KEY_ART_URI;
|
||||
|
||||
/**
|
||||
* The artwork for the album of the media's original source as a {@link Bitmap}. The artwork
|
||||
* should be relatively small and may be scaled down if it is too large. For higher resolution
|
||||
* artwork {@link #METADATA_KEY_ALBUM_ART_URI} should be used instead.
|
||||
*/
|
||||
public static final String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART";
|
||||
public static final String METADATA_KEY_ALBUM_ART = MediaMetadata.METADATA_KEY_ALBUM_ART;
|
||||
|
||||
/** The artwork for the album of the media's original source as a Uri style String. */
|
||||
public static final String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI";
|
||||
public static final String METADATA_KEY_ALBUM_ART_URI = MediaMetadata.METADATA_KEY_ALBUM_ART_URI;
|
||||
|
||||
/**
|
||||
* The user's rating for the media.
|
||||
*
|
||||
* @see RatingCompat
|
||||
*/
|
||||
public static final String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING";
|
||||
public static final String METADATA_KEY_USER_RATING = MediaMetadata.METADATA_KEY_USER_RATING;
|
||||
|
||||
/**
|
||||
* The overall rating for the media.
|
||||
*
|
||||
* @see RatingCompat
|
||||
*/
|
||||
public static final String METADATA_KEY_RATING = "android.media.metadata.RATING";
|
||||
public static final String METADATA_KEY_RATING = MediaMetadata.METADATA_KEY_RATING;
|
||||
|
||||
/**
|
||||
* A title that is suitable for display to the user. This will generally be the same as {@link
|
||||
* #METADATA_KEY_TITLE} but may differ for some formats. When displaying media described by this
|
||||
* metadata this should be preferred if present.
|
||||
*/
|
||||
public static final String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE";
|
||||
public static final String METADATA_KEY_DISPLAY_TITLE = MediaMetadata.METADATA_KEY_DISPLAY_TITLE;
|
||||
|
||||
/**
|
||||
* A subtitle that is suitable for display to the user. When displaying a second line for media
|
||||
* described by this metadata this should be preferred to other fields if present.
|
||||
*/
|
||||
public static final String METADATA_KEY_DISPLAY_SUBTITLE =
|
||||
"android.media.metadata.DISPLAY_SUBTITLE";
|
||||
MediaMetadata.METADATA_KEY_DISPLAY_SUBTITLE;
|
||||
|
||||
/**
|
||||
* A description that is suitable for display to the user. When displaying more information for
|
||||
* media described by this metadata this should be preferred to other fields if present.
|
||||
*/
|
||||
public static final String METADATA_KEY_DISPLAY_DESCRIPTION =
|
||||
"android.media.metadata.DISPLAY_DESCRIPTION";
|
||||
MediaMetadata.METADATA_KEY_DISPLAY_DESCRIPTION;
|
||||
|
||||
/**
|
||||
* An icon or thumbnail that is suitable for display to the user. When displaying an icon for
|
||||
@ -162,7 +157,7 @@ public final class MediaMetadataCompat implements Parcelable {
|
||||
* <p>The icon should be relatively small and may be scaled down if it is too large. For higher
|
||||
* resolution artwork {@link #METADATA_KEY_DISPLAY_ICON_URI} should be used instead.
|
||||
*/
|
||||
public static final String METADATA_KEY_DISPLAY_ICON = "android.media.metadata.DISPLAY_ICON";
|
||||
public static final String METADATA_KEY_DISPLAY_ICON = MediaMetadata.METADATA_KEY_DISPLAY_ICON;
|
||||
|
||||
/**
|
||||
* An icon or thumbnail that is suitable for display to the user. When displaying more information
|
||||
@ -170,20 +165,21 @@ public final class MediaMetadataCompat implements Parcelable {
|
||||
* fields when present. This must be a Uri style String.
|
||||
*/
|
||||
public static final String METADATA_KEY_DISPLAY_ICON_URI =
|
||||
"android.media.metadata.DISPLAY_ICON_URI";
|
||||
MediaMetadata.METADATA_KEY_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.
|
||||
*/
|
||||
public static final String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID";
|
||||
public static final String METADATA_KEY_MEDIA_ID = MediaMetadata.METADATA_KEY_MEDIA_ID;
|
||||
|
||||
/**
|
||||
* A Uri formatted String representing the content. This value is specific to the service
|
||||
* providing the content. It may be used with {@link TransportControls#playFromUri(Uri, Bundle)}
|
||||
* to initiate playback when provided by a {@link MediaBrowserCompat} connected to the same app.
|
||||
*/
|
||||
public static final String METADATA_KEY_MEDIA_URI = "android.media.metadata.MEDIA_URI";
|
||||
@SuppressLint("InlinedApi") // Inlined compile time constant
|
||||
public static final String METADATA_KEY_MEDIA_URI = MediaMetadata.METADATA_KEY_MEDIA_URI;
|
||||
|
||||
/**
|
||||
* The bluetooth folder type of the media specified in the section 6.10.2.2 of the Bluetooth AVRCP
|
||||
@ -199,7 +195,9 @@ public final class MediaMetadataCompat implements Parcelable {
|
||||
* <li>{@link MediaDescriptionCompat#BT_FOLDER_TYPE_YEARS}
|
||||
* </ul>
|
||||
*/
|
||||
public static final String METADATA_KEY_BT_FOLDER_TYPE = "android.media.metadata.BT_FOLDER_TYPE";
|
||||
@SuppressLint("InlinedApi") // Inlined compile time constant
|
||||
public static final String METADATA_KEY_BT_FOLDER_TYPE =
|
||||
MediaMetadata.METADATA_KEY_BT_FOLDER_TYPE;
|
||||
|
||||
/**
|
||||
* Whether the media is an advertisement. A value of 0 indicates it is not an advertisement. A
|
||||
@ -276,7 +274,7 @@ public final class MediaMetadataCompat implements Parcelable {
|
||||
static final ArrayMap<String, Integer> METADATA_KEYS_TYPE;
|
||||
|
||||
static {
|
||||
METADATA_KEYS_TYPE = new ArrayMap<String, Integer>();
|
||||
METADATA_KEYS_TYPE = new ArrayMap<>();
|
||||
METADATA_KEYS_TYPE.put(METADATA_KEY_TITLE, METADATA_TYPE_TEXT);
|
||||
METADATA_KEYS_TYPE.put(METADATA_KEY_ARTIST, METADATA_TYPE_TEXT);
|
||||
METADATA_KEYS_TYPE.put(METADATA_KEY_DURATION, METADATA_TYPE_LONG);
|
||||
@ -320,17 +318,8 @@ public final class MediaMetadataCompat implements Parcelable {
|
||||
METADATA_KEY_COMPOSER
|
||||
};
|
||||
|
||||
private static final @BitmapKey String[] PREFERRED_BITMAP_ORDER = {
|
||||
METADATA_KEY_DISPLAY_ICON, METADATA_KEY_ART, METADATA_KEY_ALBUM_ART
|
||||
};
|
||||
|
||||
private static final @TextKey String[] PREFERRED_URI_ORDER = {
|
||||
METADATA_KEY_DISPLAY_ICON_URI, METADATA_KEY_ART_URI, METADATA_KEY_ALBUM_ART_URI
|
||||
};
|
||||
|
||||
final Bundle mBundle;
|
||||
@Nullable private MediaMetadata mMetadataFwk;
|
||||
@Nullable private MediaDescriptionCompat mDescription;
|
||||
|
||||
MediaMetadataCompat(Bundle bundle) {
|
||||
mBundle = new Bundle(bundle);
|
||||
@ -396,7 +385,6 @@ public final class MediaMetadataCompat implements Parcelable {
|
||||
* @return A {@link RatingCompat} or null
|
||||
*/
|
||||
@Nullable
|
||||
@SuppressWarnings("deprecation")
|
||||
public RatingCompat getRating(@RatingKey String key) {
|
||||
RatingCompat rating = null;
|
||||
try {
|
||||
@ -417,7 +405,6 @@ public final class MediaMetadataCompat implements Parcelable {
|
||||
* @return A {@link Bitmap} or null
|
||||
*/
|
||||
@Nullable
|
||||
@SuppressWarnings("deprecation")
|
||||
public Bitmap getBitmap(@BitmapKey String key) {
|
||||
Bitmap bmp = null;
|
||||
try {
|
||||
@ -429,93 +416,6 @@ public final class MediaMetadataCompat implements Parcelable {
|
||||
return bmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a simple description of this metadata for display purposes.
|
||||
*
|
||||
* @return A simple description of this metadata.
|
||||
*/
|
||||
public MediaDescriptionCompat getDescription() {
|
||||
if (mDescription != null) {
|
||||
return mDescription;
|
||||
}
|
||||
|
||||
String mediaId = getString(METADATA_KEY_MEDIA_ID);
|
||||
|
||||
@NullableType CharSequence[] text = new CharSequence[3];
|
||||
Bitmap icon = null;
|
||||
Uri iconUri = null;
|
||||
|
||||
// First handle the case where display data is set already
|
||||
CharSequence displayText = getText(METADATA_KEY_DISPLAY_TITLE);
|
||||
if (!TextUtils.isEmpty(displayText)) {
|
||||
// If they have a display title use only display data, otherwise use
|
||||
// our best bets
|
||||
text[0] = displayText;
|
||||
text[1] = getText(METADATA_KEY_DISPLAY_SUBTITLE);
|
||||
text[2] = getText(METADATA_KEY_DISPLAY_DESCRIPTION);
|
||||
} else {
|
||||
// Use whatever fields we can
|
||||
int textIndex = 0;
|
||||
int keyIndex = 0;
|
||||
while (textIndex < text.length && keyIndex < PREFERRED_DESCRIPTION_ORDER.length) {
|
||||
CharSequence next = getText(PREFERRED_DESCRIPTION_ORDER[keyIndex++]);
|
||||
if (!TextUtils.isEmpty(next)) {
|
||||
// Fill in the next empty bit of text
|
||||
text[textIndex++] = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the best art bitmap we can find
|
||||
for (int i = 0; i < PREFERRED_BITMAP_ORDER.length; i++) {
|
||||
Bitmap next = getBitmap(PREFERRED_BITMAP_ORDER[i]);
|
||||
if (next != null) {
|
||||
icon = next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the best Uri we can find
|
||||
for (int i = 0; i < PREFERRED_URI_ORDER.length; i++) {
|
||||
String next = getString(PREFERRED_URI_ORDER[i]);
|
||||
if (!TextUtils.isEmpty(next)) {
|
||||
iconUri = Uri.parse(next);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Uri mediaUri = null;
|
||||
String mediaUriStr = getString(METADATA_KEY_MEDIA_URI);
|
||||
if (!TextUtils.isEmpty(mediaUriStr)) {
|
||||
mediaUri = Uri.parse(mediaUriStr);
|
||||
}
|
||||
|
||||
MediaDescriptionCompat.Builder bob = new MediaDescriptionCompat.Builder();
|
||||
bob.setMediaId(mediaId);
|
||||
bob.setTitle(text[0]);
|
||||
bob.setSubtitle(text[1]);
|
||||
bob.setDescription(text[2]);
|
||||
bob.setIconBitmap(icon);
|
||||
bob.setIconUri(iconUri);
|
||||
bob.setMediaUri(mediaUri);
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
if (mBundle.containsKey(METADATA_KEY_BT_FOLDER_TYPE)) {
|
||||
bundle.putLong(
|
||||
MediaDescriptionCompat.EXTRA_BT_FOLDER_TYPE, getLong(METADATA_KEY_BT_FOLDER_TYPE));
|
||||
}
|
||||
if (mBundle.containsKey(METADATA_KEY_DOWNLOAD_STATUS)) {
|
||||
bundle.putLong(
|
||||
MediaDescriptionCompat.EXTRA_DOWNLOAD_STATUS, getLong(METADATA_KEY_DOWNLOAD_STATUS));
|
||||
}
|
||||
if (!bundle.isEmpty()) {
|
||||
bob.setExtras(bundle);
|
||||
}
|
||||
mDescription = bob.build();
|
||||
|
||||
return mDescription;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
@ -535,15 +435,6 @@ public final class MediaMetadataCompat implements Parcelable {
|
||||
return mBundle.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Set containing the Strings used as keys in this metadata.
|
||||
*
|
||||
* @return a Set of String keys
|
||||
*/
|
||||
public Set<String> keySet() {
|
||||
return mBundle.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a copy of the bundle for this metadata object. This is available to support backwards
|
||||
* compatibility.
|
||||
@ -564,7 +455,7 @@ public final class MediaMetadataCompat implements Parcelable {
|
||||
*/
|
||||
@Nullable
|
||||
public static MediaMetadataCompat fromMediaMetadata(@Nullable Object metadataObj) {
|
||||
if (metadataObj != null && Build.VERSION.SDK_INT >= 21) {
|
||||
if (metadataObj != null) {
|
||||
Parcel p = Parcel.obtain();
|
||||
((MediaMetadata) metadataObj).writeToParcel(p, 0);
|
||||
p.setDataPosition(0);
|
||||
@ -580,12 +471,9 @@ public final class MediaMetadataCompat implements Parcelable {
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@RequiresApi(21)
|
||||
public Object getMediaMetadata() {
|
||||
public MediaMetadata getMediaMetadata() {
|
||||
if (mMetadataFwk == null) {
|
||||
Parcel p = Parcel.obtain();
|
||||
try {
|
||||
@ -628,39 +516,6 @@ public final class MediaMetadataCompat implements Parcelable {
|
||||
mBundle = new Bundle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Builder using a {@link MediaMetadataCompat} instance to set the initial values. All
|
||||
* fields in the source metadata will be included in the new metadata. Fields can be overwritten
|
||||
* by adding the same key.
|
||||
*
|
||||
* @param source
|
||||
*/
|
||||
public Builder(MediaMetadataCompat source) {
|
||||
mBundle = new Bundle(source.mBundle);
|
||||
MediaSessionCompat.ensureClassLoader(mBundle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Builder using a {@link MediaMetadataCompat} instance to set initial values, but
|
||||
* replace bitmaps with a scaled down copy if they are larger than maxBitmapSize.
|
||||
*
|
||||
* @param source The original metadata to copy.
|
||||
* @param maxBitmapSize The maximum height/width for bitmaps contained in the metadata.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public Builder(MediaMetadataCompat source, int maxBitmapSize) {
|
||||
this(source);
|
||||
for (String key : mBundle.keySet()) {
|
||||
Object value = mBundle.get(key);
|
||||
if (value instanceof Bitmap) {
|
||||
Bitmap bmp = (Bitmap) value;
|
||||
if (bmp.getHeight() > maxBitmapSize || bmp.getWidth() > maxBitmapSize) {
|
||||
putBitmap(key, scaleBitmap(bmp, maxBitmapSize));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a CharSequence value into the metadata. Custom keys may be used, but if the METADATA_KEYs
|
||||
* defined in this class are used they may only be one of the following:
|
||||
@ -819,15 +674,5 @@ public final class MediaMetadataCompat implements Parcelable {
|
||||
public MediaMetadataCompat build() {
|
||||
return new MediaMetadataCompat(mBundle);
|
||||
}
|
||||
|
||||
private Bitmap scaleBitmap(Bitmap bmp, int maxSize) {
|
||||
float maxSizeF = maxSize;
|
||||
float widthScale = maxSizeF / bmp.getWidth();
|
||||
float heightScale = maxSizeF / bmp.getHeight();
|
||||
float scale = Math.min(widthScale, heightScale);
|
||||
int height = (int) (bmp.getHeight() * scale);
|
||||
int width = (int) (bmp.getWidth() * scale);
|
||||
return Bitmap.createScaledBitmap(bmp, width, height, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -57,9 +57,6 @@ public final class MediaSessionManager {
|
||||
* @return The MediaSessionManager instance for this context.
|
||||
*/
|
||||
public static MediaSessionManager getSessionManager(Context context) {
|
||||
if (context == null) {
|
||||
throw new IllegalArgumentException("context cannot be null");
|
||||
}
|
||||
synchronized (sLock) {
|
||||
if (sSessionManager == null) {
|
||||
sSessionManager = new MediaSessionManager(context.getApplicationContext());
|
||||
@ -69,13 +66,7 @@ public final class MediaSessionManager {
|
||||
}
|
||||
|
||||
private MediaSessionManager(Context context) {
|
||||
if (Build.VERSION.SDK_INT >= 28) {
|
||||
mImpl = new MediaSessionManagerImplApi28(context);
|
||||
} else if (Build.VERSION.SDK_INT >= 21) {
|
||||
mImpl = new MediaSessionManagerImplApi21(context);
|
||||
} else {
|
||||
mImpl = new MediaSessionManagerImplBase(context);
|
||||
}
|
||||
mImpl = new MediaSessionManagerImpl(context);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -91,22 +82,9 @@ public final class MediaSessionManager {
|
||||
* {@code false} otherwise.
|
||||
*/
|
||||
public boolean isTrustedForMediaControl(RemoteUserInfo userInfo) {
|
||||
if (userInfo == null) {
|
||||
throw new IllegalArgumentException("userInfo should not be null");
|
||||
}
|
||||
return mImpl.isTrustedForMediaControl(userInfo.mImpl);
|
||||
}
|
||||
|
||||
Context getContext() {
|
||||
return mImpl.getContext();
|
||||
}
|
||||
|
||||
interface MediaSessionManagerImpl {
|
||||
Context getContext();
|
||||
|
||||
boolean isTrustedForMediaControl(RemoteUserInfoImpl userInfo);
|
||||
}
|
||||
|
||||
interface RemoteUserInfoImpl {
|
||||
String getPackageName();
|
||||
|
||||
@ -157,10 +135,10 @@ public final class MediaSessionManager {
|
||||
throw new IllegalArgumentException("packageName should be nonempty");
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= 28) {
|
||||
mImpl = new MediaSessionManagerImplApi28.RemoteUserInfoImplApi28(packageName, pid, uid);
|
||||
mImpl = new RemoteUserInfoImplApi28(packageName, pid, uid);
|
||||
} else {
|
||||
// Note: We need to include IBinder to distinguish controllers in a process.
|
||||
mImpl = new MediaSessionManagerImplBase.RemoteUserInfoImplBase(packageName, pid, uid);
|
||||
mImpl = new RemoteUserInfoImplBase(packageName, pid, uid);
|
||||
}
|
||||
}
|
||||
|
||||
@ -177,14 +155,13 @@ public final class MediaSessionManager {
|
||||
public RemoteUserInfo(android.media.session.MediaSessionManager.RemoteUserInfo remoteUserInfo) {
|
||||
// Framework RemoteUserInfo doesn't ensure non-null nor non-empty package name,
|
||||
// so ensure package name here instead.
|
||||
String packageName =
|
||||
MediaSessionManagerImplApi28.RemoteUserInfoImplApi28.getPackageName(remoteUserInfo);
|
||||
String packageName = RemoteUserInfoImplApi28.getPackageName(remoteUserInfo);
|
||||
if (packageName == null) {
|
||||
throw new NullPointerException("package shouldn't be null");
|
||||
} else if (TextUtils.isEmpty(packageName)) {
|
||||
throw new IllegalArgumentException("packageName should be nonempty");
|
||||
}
|
||||
mImpl = new MediaSessionManagerImplApi28.RemoteUserInfoImplApi28(remoteUserInfo);
|
||||
mImpl = new RemoteUserInfoImplApi28(remoteUserInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -241,8 +218,7 @@ public final class MediaSessionManager {
|
||||
}
|
||||
}
|
||||
|
||||
private static class MediaSessionManagerImplBase
|
||||
implements MediaSessionManager.MediaSessionManagerImpl {
|
||||
private static class MediaSessionManagerImpl {
|
||||
private static final String TAG = MediaSessionManager.TAG;
|
||||
private static final boolean DEBUG = MediaSessionManager.DEBUG;
|
||||
|
||||
@ -255,19 +231,24 @@ public final class MediaSessionManager {
|
||||
Context mContext;
|
||||
ContentResolver mContentResolver;
|
||||
|
||||
MediaSessionManagerImplBase(Context context) {
|
||||
MediaSessionManagerImpl(Context context) {
|
||||
mContext = context;
|
||||
mContentResolver = mContext.getContentResolver();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context getContext() {
|
||||
return mContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public boolean isTrustedForMediaControl(MediaSessionManager.RemoteUserInfoImpl userInfo) {
|
||||
// Don't use framework's isTrustedForMediaControl().
|
||||
// In P, framework's isTrustedForMediaControl() checks whether the UID, PID,
|
||||
// and package name match. In MediaSession/MediaController, Context#getPackageName() is
|
||||
// used by MediaController to tell MediaSession the package name.
|
||||
// However, UID, PID and Context#getPackageName() may not match if a activity/service runs
|
||||
// on the another app's process by specifying android:process in the AndroidManifest.xml.
|
||||
// In that case, this check will always fail.
|
||||
// Alternative way is to use Context#getOpPackageName() for sending the package name,
|
||||
// but it's hidden so we cannot use it.
|
||||
if (hasMediaControlPermission(userInfo)) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
ApplicationInfo applicationInfo =
|
||||
mContext.getPackageManager().getApplicationInfo(userInfo.getPackageName(), 0);
|
||||
@ -286,6 +267,15 @@ public final class MediaSessionManager {
|
||||
|| isEnabledNotificationListener(userInfo);
|
||||
}
|
||||
|
||||
/** Checks the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL permission. */
|
||||
private boolean hasMediaControlPermission(MediaSessionManager.RemoteUserInfoImpl userInfo) {
|
||||
return mContext.checkPermission(
|
||||
android.Manifest.permission.MEDIA_CONTENT_CONTROL,
|
||||
userInfo.getPid(),
|
||||
userInfo.getUid())
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
private boolean isPermissionGranted(
|
||||
MediaSessionManager.RemoteUserInfoImpl userInfo, String permission) {
|
||||
if (userInfo.getPid() < 0) {
|
||||
@ -320,142 +310,88 @@ public final class MediaSessionManager {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static class RemoteUserInfoImplBase implements MediaSessionManager.RemoteUserInfoImpl {
|
||||
private String mPackageName;
|
||||
private int mPid;
|
||||
private int mUid;
|
||||
private static class RemoteUserInfoImplBase implements MediaSessionManager.RemoteUserInfoImpl {
|
||||
private final String mPackageName;
|
||||
private final int mPid;
|
||||
private final int mUid;
|
||||
|
||||
RemoteUserInfoImplBase(String packageName, int pid, int uid) {
|
||||
mPackageName = packageName;
|
||||
mPid = pid;
|
||||
mUid = uid;
|
||||
RemoteUserInfoImplBase(String packageName, int pid, int uid) {
|
||||
mPackageName = packageName;
|
||||
mPid = pid;
|
||||
mUid = uid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPackageName() {
|
||||
return mPackageName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPid() {
|
||||
return mPid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUid() {
|
||||
return mUid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPackageName() {
|
||||
return mPackageName;
|
||||
if (!(obj instanceof RemoteUserInfoImplBase)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPid() {
|
||||
return mPid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUid() {
|
||||
return mUid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof RemoteUserInfoImplBase)) {
|
||||
return false;
|
||||
}
|
||||
RemoteUserInfoImplBase otherUserInfo = (RemoteUserInfoImplBase) obj;
|
||||
if (mPid < 0 || otherUserInfo.mPid < 0) {
|
||||
// Only compare package name and UID when PID is unknown.
|
||||
return TextUtils.equals(mPackageName, otherUserInfo.mPackageName)
|
||||
&& mUid == otherUserInfo.mUid;
|
||||
}
|
||||
RemoteUserInfoImplBase otherUserInfo = (RemoteUserInfoImplBase) obj;
|
||||
if (mPid < 0 || otherUserInfo.mPid < 0) {
|
||||
// Only compare package name and UID when PID is unknown.
|
||||
return TextUtils.equals(mPackageName, otherUserInfo.mPackageName)
|
||||
&& mPid == otherUserInfo.mPid
|
||||
&& mUid == otherUserInfo.mUid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return ObjectsCompat.hash(mPackageName, mUid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(21)
|
||||
private static class MediaSessionManagerImplApi21 extends MediaSessionManagerImplBase {
|
||||
MediaSessionManagerImplApi21(Context context) {
|
||||
super(context);
|
||||
mContext = context;
|
||||
return TextUtils.equals(mPackageName, otherUserInfo.mPackageName)
|
||||
&& mPid == otherUserInfo.mPid
|
||||
&& mUid == otherUserInfo.mUid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTrustedForMediaControl(MediaSessionManager.RemoteUserInfoImpl userInfo) {
|
||||
|
||||
return hasMediaControlPermission(userInfo) || super.isTrustedForMediaControl(userInfo);
|
||||
}
|
||||
|
||||
/** Checks the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL permission. */
|
||||
private boolean hasMediaControlPermission(MediaSessionManager.RemoteUserInfoImpl userInfo) {
|
||||
return getContext()
|
||||
.checkPermission(
|
||||
android.Manifest.permission.MEDIA_CONTENT_CONTROL,
|
||||
userInfo.getPid(),
|
||||
userInfo.getUid())
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
public int hashCode() {
|
||||
return ObjectsCompat.hash(mPackageName, mUid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This extends {@link RemoteUserInfoImplBase} on purpose not to use frameworks' equals() and
|
||||
* hashCode() implementation for two reasons:
|
||||
*
|
||||
* <p>1. To override PID checks when one of them are unknown. PID can be unknown between
|
||||
* MediaBrowserCompat / MediaBrowserServiceCompat 2. To skip checking hidden binder. Framework's
|
||||
* {@link android.media.session.MediaSessionManager.RemoteUserInfo} also checks internal binder to
|
||||
* distinguish multiple {@link android.media.session.MediaController} and {@link
|
||||
* android.media.browse.MediaBrowser} in a process. However, when the binders in both
|
||||
* RemoteUserInfos are {@code null}, framework's equal() specially handles the case and returns
|
||||
* {@code false}. This causes two issues that we need to workaround. Issue a) RemoteUserInfos
|
||||
* created by key events are considered as all different. Issue b) RemoteUserInfos created with
|
||||
* public constructors are considered as all different.
|
||||
*/
|
||||
@RequiresApi(28)
|
||||
private static final class MediaSessionManagerImplApi28 extends MediaSessionManagerImplApi21 {
|
||||
@Nullable android.media.session.MediaSessionManager mObject;
|
||||
private static final class RemoteUserInfoImplApi28 extends RemoteUserInfoImplBase {
|
||||
|
||||
MediaSessionManagerImplApi28(Context context) {
|
||||
super(context);
|
||||
mObject =
|
||||
(android.media.session.MediaSessionManager)
|
||||
context.getSystemService(Context.MEDIA_SESSION_SERVICE);
|
||||
RemoteUserInfoImplApi28(String packageName, int pid, int uid) {
|
||||
super(packageName, pid, uid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTrustedForMediaControl(MediaSessionManager.RemoteUserInfoImpl userInfo) {
|
||||
// Don't use framework's isTrustedForMediaControl().
|
||||
// In P, framework's isTrustedForMediaControl() checks whether the UID, PID,
|
||||
// and package name match. In MediaSession/MediaController, Context#getPackageName() is
|
||||
// used by MediaController to tell MediaSession the package name.
|
||||
// However, UID, PID and Context#getPackageName() may not match if a activity/service runs
|
||||
// on the another app's process by specifying android:process in the AndroidManifest.xml.
|
||||
// In that case, this check will always fail.
|
||||
// Alternative way is to use Context#getOpPackageName() for sending the package name,
|
||||
// but it's hidden so we cannot use it.
|
||||
return super.isTrustedForMediaControl(userInfo);
|
||||
RemoteUserInfoImplApi28(
|
||||
android.media.session.MediaSessionManager.RemoteUserInfo remoteUserInfo) {
|
||||
super(remoteUserInfo.getPackageName(), remoteUserInfo.getPid(), remoteUserInfo.getUid());
|
||||
}
|
||||
|
||||
/**
|
||||
* This extends {@link RemoteUserInfoImplBase} on purpose not to use frameworks' equals() and
|
||||
* hashCode() implementation for two reasons:
|
||||
*
|
||||
* <p>1. To override PID checks when one of them are unknown. PID can be unknown between
|
||||
* MediaBrowserCompat / MediaBrowserServiceCompat 2. To skip checking hidden binder. Framework's
|
||||
* {@link android.media.session.MediaSessionManager.RemoteUserInfo} also checks internal binder
|
||||
* to distinguish multiple {@link android.media.session.MediaController} and {@link
|
||||
* android.media.browse.MediaBrowser} in a process. However, when the binders in both
|
||||
* RemoteUserInfos are {@link null}, framework's equal() specially handles the case and returns
|
||||
* {@code false}. This cause two issues that we need to workaround. Issue a) RemoteUserInfos
|
||||
* created by key events are considered as all different. issue b) RemoteUserInfos created with
|
||||
* public constructors are considers as all different.
|
||||
*/
|
||||
@RequiresApi(28)
|
||||
private static final class RemoteUserInfoImplApi28 extends RemoteUserInfoImplBase {
|
||||
final android.media.session.MediaSessionManager.RemoteUserInfo mObject;
|
||||
|
||||
RemoteUserInfoImplApi28(String packageName, int pid, int uid) {
|
||||
super(packageName, pid, uid);
|
||||
mObject =
|
||||
new android.media.session.MediaSessionManager.RemoteUserInfo(packageName, pid, uid);
|
||||
}
|
||||
|
||||
RemoteUserInfoImplApi28(
|
||||
android.media.session.MediaSessionManager.RemoteUserInfo remoteUserInfo) {
|
||||
super(remoteUserInfo.getPackageName(), remoteUserInfo.getPid(), remoteUserInfo.getUid());
|
||||
mObject = remoteUserInfo;
|
||||
}
|
||||
|
||||
static String getPackageName(
|
||||
android.media.session.MediaSessionManager.RemoteUserInfo remoteUserInfo) {
|
||||
return remoteUserInfo.getPackageName();
|
||||
}
|
||||
static String getPackageName(
|
||||
android.media.session.MediaSessionManager.RemoteUserInfo remoteUserInfo) {
|
||||
return remoteUserInfo.getPackageName();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -37,15 +37,6 @@ public class ParcelableVolumeInfo implements Parcelable {
|
||||
public int maxVolume;
|
||||
public int currentVolume;
|
||||
|
||||
public ParcelableVolumeInfo(
|
||||
int volumeType, int audioStream, int controlType, int maxVolume, int currentVolume) {
|
||||
this.volumeType = volumeType;
|
||||
this.audioStream = audioStream;
|
||||
this.controlType = controlType;
|
||||
this.maxVolume = maxVolume;
|
||||
this.currentVolume = currentVolume;
|
||||
}
|
||||
|
||||
public ParcelableVolumeInfo(Parcel from) {
|
||||
volumeType = from.readInt();
|
||||
controlType = from.readInt();
|
||||
|
@ -26,7 +26,6 @@ import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.SystemClock;
|
||||
import android.text.TextUtils;
|
||||
import android.view.KeyEvent;
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.LongDef;
|
||||
import androidx.annotation.Nullable;
|
||||
@ -532,49 +531,6 @@ public final class PlaybackStateCompat implements Parcelable {
|
||||
*/
|
||||
public static final int ERROR_CODE_END_OF_QUEUE = 11;
|
||||
|
||||
// KeyEvent constants only available on API 11+
|
||||
private static final int KEYCODE_MEDIA_PAUSE = 127;
|
||||
private static final int KEYCODE_MEDIA_PLAY = 126;
|
||||
|
||||
/**
|
||||
* Translates a given action into a matched key code defined in {@link KeyEvent}. The given action
|
||||
* should be one of the following:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link PlaybackStateCompat#ACTION_PLAY}
|
||||
* <li>{@link PlaybackStateCompat#ACTION_PAUSE}
|
||||
* <li>{@link PlaybackStateCompat#ACTION_SKIP_TO_NEXT}
|
||||
* <li>{@link PlaybackStateCompat#ACTION_SKIP_TO_PREVIOUS}
|
||||
* <li>{@link PlaybackStateCompat#ACTION_STOP}
|
||||
* <li>{@link PlaybackStateCompat#ACTION_FAST_FORWARD}
|
||||
* <li>{@link PlaybackStateCompat#ACTION_REWIND}
|
||||
* <li>{@link PlaybackStateCompat#ACTION_PLAY_PAUSE}
|
||||
* </ul>
|
||||
*
|
||||
* @param action The action to be translated.
|
||||
* @return the key code matched to the given action.
|
||||
*/
|
||||
public static int toKeyCode(@MediaKeyAction long action) {
|
||||
if (action == ACTION_PLAY) {
|
||||
return KEYCODE_MEDIA_PLAY;
|
||||
} else if (action == ACTION_PAUSE) {
|
||||
return KEYCODE_MEDIA_PAUSE;
|
||||
} else if (action == ACTION_SKIP_TO_NEXT) {
|
||||
return KeyEvent.KEYCODE_MEDIA_NEXT;
|
||||
} else if (action == ACTION_SKIP_TO_PREVIOUS) {
|
||||
return KeyEvent.KEYCODE_MEDIA_PREVIOUS;
|
||||
} else if (action == ACTION_STOP) {
|
||||
return KeyEvent.KEYCODE_MEDIA_STOP;
|
||||
} else if (action == ACTION_FAST_FORWARD) {
|
||||
return KeyEvent.KEYCODE_MEDIA_FAST_FORWARD;
|
||||
} else if (action == ACTION_REWIND) {
|
||||
return KeyEvent.KEYCODE_MEDIA_REWIND;
|
||||
} else if (action == ACTION_PLAY_PAUSE) {
|
||||
return KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE;
|
||||
}
|
||||
return KeyEvent.KEYCODE_UNKNOWN;
|
||||
}
|
||||
|
||||
final int mState;
|
||||
final long mPosition;
|
||||
final long mBufferedPosition;
|
||||
@ -712,7 +668,7 @@ public final class PlaybackStateCompat implements Parcelable {
|
||||
* @param timeDiff Only used for testing, otherwise it should be null.
|
||||
* @return The current playback position in ms
|
||||
*/
|
||||
public long getCurrentPosition(Long timeDiff) {
|
||||
public long getCurrentPosition(@Nullable Long timeDiff) {
|
||||
long expectedPosition =
|
||||
mPosition
|
||||
+ (long)
|
||||
@ -776,7 +732,6 @@ public final class PlaybackStateCompat implements Parcelable {
|
||||
}
|
||||
|
||||
/** Get the list of custom actions. */
|
||||
@Nullable
|
||||
public List<PlaybackStateCompat.CustomAction> getCustomActions() {
|
||||
return mCustomActions;
|
||||
}
|
||||
@ -839,20 +794,17 @@ public final class PlaybackStateCompat implements Parcelable {
|
||||
/**
|
||||
* 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 stateFwk A {@link android.media.session.PlaybackState} object, or null if none.
|
||||
* @return An equivalent {@link PlaybackStateCompat} object, or null if none.
|
||||
*/
|
||||
@Nullable
|
||||
public static PlaybackStateCompat fromPlaybackState(@Nullable Object stateObj) {
|
||||
if (stateObj != null && Build.VERSION.SDK_INT >= 21) {
|
||||
PlaybackState stateFwk = (PlaybackState) stateObj;
|
||||
List<PlaybackState.CustomAction> customActionFwks = Api21Impl.getCustomActions(stateFwk);
|
||||
public static PlaybackStateCompat fromPlaybackState(@Nullable PlaybackState stateFwk) {
|
||||
if (stateFwk != null) {
|
||||
List<PlaybackState.CustomAction> customActionFwks = stateFwk.getCustomActions();
|
||||
List<PlaybackStateCompat.CustomAction> customActions = null;
|
||||
if (customActionFwks != null) {
|
||||
customActions = new ArrayList<>(customActionFwks.size());
|
||||
for (Object customActionFwk : customActionFwks) {
|
||||
for (PlaybackState.CustomAction customActionFwk : customActionFwks) {
|
||||
if (customActionFwk == null) {
|
||||
continue;
|
||||
}
|
||||
@ -868,16 +820,16 @@ public final class PlaybackStateCompat implements Parcelable {
|
||||
}
|
||||
PlaybackStateCompat stateCompat =
|
||||
new PlaybackStateCompat(
|
||||
Api21Impl.getState(stateFwk),
|
||||
Api21Impl.getPosition(stateFwk),
|
||||
Api21Impl.getBufferedPosition(stateFwk),
|
||||
Api21Impl.getPlaybackSpeed(stateFwk),
|
||||
Api21Impl.getActions(stateFwk),
|
||||
stateFwk.getState(),
|
||||
stateFwk.getPosition(),
|
||||
stateFwk.getBufferedPosition(),
|
||||
stateFwk.getPlaybackSpeed(),
|
||||
stateFwk.getActions(),
|
||||
ERROR_CODE_UNKNOWN_ERROR,
|
||||
Api21Impl.getErrorMessage(stateFwk),
|
||||
Api21Impl.getLastPositionUpdateTime(stateFwk),
|
||||
stateFwk.getErrorMessage(),
|
||||
stateFwk.getLastPositionUpdateTime(),
|
||||
customActions,
|
||||
Api21Impl.getActiveQueueItemId(stateFwk),
|
||||
stateFwk.getActiveQueueItemId(),
|
||||
extras);
|
||||
stateCompat.mStateFwk = stateFwk;
|
||||
return stateCompat;
|
||||
@ -889,30 +841,28 @@ public final class PlaybackStateCompat implements Parcelable {
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@Nullable
|
||||
public Object getPlaybackState() {
|
||||
if (mStateFwk == null && Build.VERSION.SDK_INT >= 21) {
|
||||
PlaybackState.Builder builder = Api21Impl.createBuilder();
|
||||
Api21Impl.setState(builder, mState, mPosition, mSpeed, mUpdateTime);
|
||||
Api21Impl.setBufferedPosition(builder, mBufferedPosition);
|
||||
Api21Impl.setActions(builder, mActions);
|
||||
Api21Impl.setErrorMessage(builder, mErrorMessage);
|
||||
@SuppressWarnings("argument.type.incompatible") // setErrorMessage not annotated as nullable
|
||||
public PlaybackState getPlaybackState() {
|
||||
if (mStateFwk == null) {
|
||||
PlaybackState.Builder builder = new PlaybackState.Builder();
|
||||
builder.setState(mState, mPosition, mSpeed, mUpdateTime);
|
||||
builder.setBufferedPosition(mBufferedPosition);
|
||||
builder.setActions(mActions);
|
||||
builder.setErrorMessage(mErrorMessage);
|
||||
for (PlaybackStateCompat.CustomAction customAction : mCustomActions) {
|
||||
PlaybackState.CustomAction action =
|
||||
(PlaybackState.CustomAction) customAction.getCustomAction();
|
||||
if (action != null) {
|
||||
Api21Impl.addCustomAction(builder, action);
|
||||
builder.addCustomAction(action);
|
||||
}
|
||||
}
|
||||
Api21Impl.setActiveQueueItemId(builder, mActiveItemId);
|
||||
builder.setActiveQueueItemId(mActiveItemId);
|
||||
if (Build.VERSION.SDK_INT >= 22) {
|
||||
Api22Impl.setExtras(builder, mExtras);
|
||||
}
|
||||
mStateFwk = Api21Impl.build(builder);
|
||||
mStateFwk = builder.build();
|
||||
}
|
||||
return mStateFwk;
|
||||
}
|
||||
@ -975,22 +925,19 @@ public final class PlaybackStateCompat implements Parcelable {
|
||||
* Creates an instance from a framework {@link android.media.session.PlaybackState.CustomAction}
|
||||
* object.
|
||||
*
|
||||
* <p>This method is only supported on API 21+.
|
||||
*
|
||||
* @param customActionObj A {@link android.media.session.PlaybackState.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) {
|
||||
PlaybackState.CustomAction customActionFwk = (PlaybackState.CustomAction) customActionObj;
|
||||
Bundle extras = Api21Impl.getExtras(customActionFwk);
|
||||
Bundle extras = customActionFwk.getExtras();
|
||||
MediaSessionCompat.ensureClassLoader(extras);
|
||||
PlaybackStateCompat.CustomAction customActionCompat =
|
||||
new PlaybackStateCompat.CustomAction(
|
||||
Api21Impl.getAction(customActionFwk),
|
||||
Api21Impl.getName(customActionFwk),
|
||||
Api21Impl.getIcon(customActionFwk),
|
||||
customActionFwk.getAction(),
|
||||
customActionFwk.getName(),
|
||||
customActionFwk.getIcon(),
|
||||
extras);
|
||||
customActionCompat.mCustomActionFwk = customActionFwk;
|
||||
return customActionCompat;
|
||||
@ -1000,21 +947,20 @@ public final class PlaybackStateCompat implements Parcelable {
|
||||
* Gets the underlying framework {@link android.media.session.PlaybackState.CustomAction}
|
||||
* object.
|
||||
*
|
||||
* <p>This method is only supported on API 21+.
|
||||
*
|
||||
* @return An equivalent {@link android.media.session.PlaybackState.CustomAction} object, or
|
||||
* null if none.
|
||||
*/
|
||||
@SuppressWarnings("argument.type.incompatible") // setExtras not annotated as nullable
|
||||
@Nullable
|
||||
public Object getCustomAction() {
|
||||
if (mCustomActionFwk != null || Build.VERSION.SDK_INT < 21) {
|
||||
if (mCustomActionFwk != null) {
|
||||
return mCustomActionFwk;
|
||||
}
|
||||
|
||||
PlaybackState.CustomAction.Builder builder =
|
||||
Api21Impl.createCustomActionBuilder(mAction, mName, mIcon);
|
||||
Api21Impl.setExtras(builder, mExtras);
|
||||
return Api21Impl.build(builder);
|
||||
new PlaybackState.CustomAction.Builder(mAction, mName, mIcon);
|
||||
builder.setExtras(mExtras);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<PlaybackStateCompat.CustomAction> CREATOR =
|
||||
@ -1325,10 +1271,6 @@ public final class PlaybackStateCompat implements Parcelable {
|
||||
* @return this
|
||||
*/
|
||||
public Builder addCustomAction(PlaybackStateCompat.CustomAction customAction) {
|
||||
if (customAction == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"You may not add a null CustomAction to PlaybackStateCompat");
|
||||
}
|
||||
mCustomActions.add(customAction);
|
||||
return this;
|
||||
}
|
||||
@ -1400,118 +1342,6 @@ public final class PlaybackStateCompat implements Parcelable {
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(21)
|
||||
private static class Api21Impl {
|
||||
private Api21Impl() {}
|
||||
|
||||
static PlaybackState.Builder createBuilder() {
|
||||
return new PlaybackState.Builder();
|
||||
}
|
||||
|
||||
static void setState(
|
||||
PlaybackState.Builder builder,
|
||||
int state,
|
||||
long position,
|
||||
float playbackSpeed,
|
||||
long updateTime) {
|
||||
builder.setState(state, position, playbackSpeed, updateTime);
|
||||
}
|
||||
|
||||
static void setBufferedPosition(PlaybackState.Builder builder, long bufferedPosition) {
|
||||
builder.setBufferedPosition(bufferedPosition);
|
||||
}
|
||||
|
||||
static void setActions(PlaybackState.Builder builder, long actions) {
|
||||
builder.setActions(actions);
|
||||
}
|
||||
|
||||
@SuppressWarnings("argument.type.incompatible") // Platform class not annotated as nullable
|
||||
static void setErrorMessage(PlaybackState.Builder builder, @Nullable CharSequence error) {
|
||||
builder.setErrorMessage(error);
|
||||
}
|
||||
|
||||
static void addCustomAction(
|
||||
PlaybackState.Builder builder, PlaybackState.CustomAction customAction) {
|
||||
builder.addCustomAction(customAction);
|
||||
}
|
||||
|
||||
static void setActiveQueueItemId(PlaybackState.Builder builder, long id) {
|
||||
builder.setActiveQueueItemId(id);
|
||||
}
|
||||
|
||||
static List<PlaybackState.CustomAction> getCustomActions(PlaybackState state) {
|
||||
return state.getCustomActions();
|
||||
}
|
||||
|
||||
static PlaybackState build(PlaybackState.Builder builder) {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
static int getState(PlaybackState state) {
|
||||
return state.getState();
|
||||
}
|
||||
|
||||
static long getPosition(PlaybackState state) {
|
||||
return state.getPosition();
|
||||
}
|
||||
|
||||
static long getBufferedPosition(PlaybackState state) {
|
||||
return state.getBufferedPosition();
|
||||
}
|
||||
|
||||
static float getPlaybackSpeed(PlaybackState state) {
|
||||
return state.getPlaybackSpeed();
|
||||
}
|
||||
|
||||
static long getActions(PlaybackState state) {
|
||||
return state.getActions();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static CharSequence getErrorMessage(PlaybackState state) {
|
||||
return state.getErrorMessage();
|
||||
}
|
||||
|
||||
static long getLastPositionUpdateTime(PlaybackState state) {
|
||||
return state.getLastPositionUpdateTime();
|
||||
}
|
||||
|
||||
static long getActiveQueueItemId(PlaybackState state) {
|
||||
return state.getActiveQueueItemId();
|
||||
}
|
||||
|
||||
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
|
||||
static void setExtras(PlaybackState.CustomAction.Builder builder, @Nullable Bundle extras) {
|
||||
builder.setExtras(extras);
|
||||
}
|
||||
|
||||
static PlaybackState.CustomAction build(PlaybackState.CustomAction.Builder builder) {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static Bundle getExtras(PlaybackState.CustomAction customAction) {
|
||||
return customAction.getExtras();
|
||||
}
|
||||
|
||||
static String getAction(PlaybackState.CustomAction customAction) {
|
||||
return customAction.getAction();
|
||||
}
|
||||
|
||||
static CharSequence getName(PlaybackState.CustomAction customAction) {
|
||||
return customAction.getName();
|
||||
}
|
||||
|
||||
static int getIcon(PlaybackState.CustomAction customAction) {
|
||||
return customAction.getIcon();
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(22)
|
||||
private static class Api22Impl {
|
||||
private Api22Impl() {}
|
||||
|
@ -21,7 +21,6 @@ import android.media.VolumeProvider;
|
||||
import android.os.Build;
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.annotation.RestrictTo;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import java.lang.annotation.Retention;
|
||||
@ -96,25 +95,6 @@ public abstract class VolumeProviderCompat {
|
||||
mControlId = volumeControlId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current volume of the provider.
|
||||
*
|
||||
* @return The current volume.
|
||||
*/
|
||||
public final int getCurrentVolume() {
|
||||
return mCurrentVolume;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the volume control type that this volume provider uses.
|
||||
*
|
||||
* @return The volume control type for this volume provider
|
||||
*/
|
||||
@ControlType
|
||||
public final int getVolumeControl() {
|
||||
return mControlType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum volume this provider allows.
|
||||
*
|
||||
@ -131,26 +111,13 @@ public abstract class VolumeProviderCompat {
|
||||
*/
|
||||
public final void setCurrentVolume(int currentVolume) {
|
||||
mCurrentVolume = currentVolume;
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
VolumeProvider volumeProviderFwk = (VolumeProvider) getVolumeProvider();
|
||||
Api21Impl.setCurrentVolume(volumeProviderFwk, currentVolume);
|
||||
}
|
||||
VolumeProvider volumeProviderFwk = (VolumeProvider) getVolumeProvider();
|
||||
volumeProviderFwk.setCurrentVolume(currentVolume);
|
||||
if (mCallback != null) {
|
||||
mCallback.onVolumeChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the volume control ID. It can be used to identify which volume provider is used by the
|
||||
* session.
|
||||
*
|
||||
* @return the volume control ID or {@code null} if it isn't set.
|
||||
*/
|
||||
@Nullable
|
||||
public final String getVolumeControlId() {
|
||||
return mControlId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override to handle requests to set the volume of the current output.
|
||||
*
|
||||
@ -181,7 +148,6 @@ public abstract class VolumeProviderCompat {
|
||||
*
|
||||
* @return An equivalent {@link android.media.VolumeProvider} object, or null if none.
|
||||
*/
|
||||
@RequiresApi(21)
|
||||
public Object getVolumeProvider() {
|
||||
if (mVolumeProviderFwk == null) {
|
||||
if (Build.VERSION.SDK_INT >= 30) {
|
||||
@ -219,13 +185,4 @@ public abstract class VolumeProviderCompat {
|
||||
public abstract static class Callback {
|
||||
public abstract void onVolumeChanged(VolumeProviderCompat volumeProvider);
|
||||
}
|
||||
|
||||
@RequiresApi(21)
|
||||
private static class Api21Impl {
|
||||
private Api21Impl() {}
|
||||
|
||||
static void setCurrentVolume(VolumeProvider volumeProvider, int currentVolume) {
|
||||
volumeProvider.setCurrentVolume(currentVolume);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -266,77 +266,6 @@ public final class LegacyConversionsTest {
|
||||
assertThat(convertedMediaItemWithDisplayTitleAndTitle.mediaMetadata.albumTitle).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertToMediaMetadataCompat_displayTitleAndTitleHandledCorrectly() {
|
||||
MediaMetadata mediaMetadataWithTitleOnly =
|
||||
new MediaMetadata.Builder()
|
||||
.setTitle("title")
|
||||
.setSubtitle("subtitle")
|
||||
.setDescription("description")
|
||||
.setArtist("artist")
|
||||
.setAlbumArtist("albumArtist")
|
||||
.build();
|
||||
MediaMetadata mediaMetadataWithDisplayTitleOnly =
|
||||
new MediaMetadata.Builder()
|
||||
.setDisplayTitle("displayTitle")
|
||||
.setSubtitle("subtitle")
|
||||
.setDescription("description")
|
||||
.setArtist("artist")
|
||||
.setAlbumArtist("albumArtist")
|
||||
.build();
|
||||
MediaMetadata mediaMetadataWithDisplayTitleAndTitle =
|
||||
new MediaMetadata.Builder()
|
||||
.setTitle("title")
|
||||
.setDisplayTitle("displayTitle")
|
||||
.setSubtitle("subtitle")
|
||||
.setDescription("description")
|
||||
.setArtist("artist")
|
||||
.setAlbumArtist("albumArtist")
|
||||
.build();
|
||||
|
||||
MediaDescriptionCompat mediaDescriptionCompatFromDisplayTitleAndTitle =
|
||||
LegacyConversions.convertToMediaMetadataCompat(
|
||||
mediaMetadataWithDisplayTitleAndTitle,
|
||||
"mediaId",
|
||||
/* mediaUri= */ null,
|
||||
/* durationMs= */ 10_000L,
|
||||
/* artworkBitmap= */ null)
|
||||
.getDescription();
|
||||
MediaDescriptionCompat mediaDescriptionCompatFromDisplayTitleOnly =
|
||||
LegacyConversions.convertToMediaMetadataCompat(
|
||||
mediaMetadataWithDisplayTitleOnly,
|
||||
"mediaId",
|
||||
/* mediaUri= */ null,
|
||||
/* durationMs= */ 10_000L,
|
||||
/* artworkBitmap= */ null)
|
||||
.getDescription();
|
||||
MediaDescriptionCompat mediaDescriptionCompatFromTitleOnly =
|
||||
LegacyConversions.convertToMediaMetadataCompat(
|
||||
mediaMetadataWithTitleOnly,
|
||||
"mediaId",
|
||||
/* mediaUri= */ null,
|
||||
/* durationMs= */ 10_000L,
|
||||
/* artworkBitmap= */ null)
|
||||
.getDescription();
|
||||
|
||||
assertThat(mediaDescriptionCompatFromDisplayTitleAndTitle.getTitle().toString())
|
||||
.isEqualTo("displayTitle");
|
||||
assertThat(mediaDescriptionCompatFromDisplayTitleAndTitle.getSubtitle().toString())
|
||||
.isEqualTo("subtitle");
|
||||
assertThat(mediaDescriptionCompatFromDisplayTitleAndTitle.getDescription().toString())
|
||||
.isEqualTo("description");
|
||||
assertThat(mediaDescriptionCompatFromDisplayTitleOnly.getTitle().toString())
|
||||
.isEqualTo("displayTitle");
|
||||
assertThat(mediaDescriptionCompatFromDisplayTitleOnly.getSubtitle().toString())
|
||||
.isEqualTo("subtitle");
|
||||
assertThat(mediaDescriptionCompatFromDisplayTitleOnly.getDescription().toString())
|
||||
.isEqualTo("description");
|
||||
assertThat(mediaDescriptionCompatFromTitleOnly.getTitle().toString()).isEqualTo("title");
|
||||
assertThat(mediaDescriptionCompatFromTitleOnly.getSubtitle().toString()).isEqualTo("artist");
|
||||
assertThat(mediaDescriptionCompatFromTitleOnly.getDescription().toString())
|
||||
.isEqualTo("albumArtist");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertToQueueItemId() {
|
||||
assertThat(LegacyConversions.convertToQueueItemId(C.INDEX_UNSET))
|
||||
|
@ -21,10 +21,7 @@ import static org.hamcrest.core.IsNot.not;
|
||||
|
||||
import android.media.AudioAttributes;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.SdkSuppress;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@ -32,55 +29,45 @@ import org.junit.runner.RunWith;
|
||||
/** Test {@link AudioAttributesCompat}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class AudioAttributesCompatTest {
|
||||
// some macros for conciseness
|
||||
static AudioAttributesCompat.Builder mkBuilder(
|
||||
@AudioAttributesCompat.AttributeContentType int type,
|
||||
@AudioAttributesCompat.AttributeUsage int usage) {
|
||||
return new AudioAttributesCompat.Builder().setContentType(type).setUsage(usage);
|
||||
}
|
||||
|
||||
static AudioAttributesCompat.Builder mkBuilder(int legacyStream) {
|
||||
return new AudioAttributesCompat.Builder().setLegacyStreamType(legacyStream);
|
||||
}
|
||||
|
||||
// some objects we'll toss around
|
||||
Object mMediaAA;
|
||||
AudioAttributesCompat mMediaAAC;
|
||||
AudioAttributesCompat mMediaLegacyAAC;
|
||||
AudioAttributesCompat mMediaAACFromAA;
|
||||
AudioAttributesCompat mNotificationAAC;
|
||||
AudioAttributesCompat mNotificationLegacyAAC;
|
||||
private Object mMediaAA;
|
||||
private AudioAttributesCompat mMediaAAC;
|
||||
private AudioAttributesCompat mMediaLegacyAAC;
|
||||
private AudioAttributesCompat mMediaAACFromAA;
|
||||
private AudioAttributesCompat mNotificationAAC;
|
||||
private AudioAttributesCompat mNotificationLegacyAAC;
|
||||
|
||||
@Before
|
||||
@SdkSuppress(minSdkVersion = 21)
|
||||
public void setUpApi21() {
|
||||
if (Build.VERSION.SDK_INT < 21) {
|
||||
return;
|
||||
}
|
||||
mMediaAA =
|
||||
new AudioAttributes.Builder()
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
|
||||
.setUsage(AudioAttributes.USAGE_MEDIA)
|
||||
.build();
|
||||
mMediaAACFromAA = AudioAttributesCompat.wrap((AudioAttributes) mMediaAA);
|
||||
mMediaAACFromAA = AudioAttributesCompat.wrap(mMediaAA);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mMediaAAC =
|
||||
mkBuilder(AudioAttributesCompat.CONTENT_TYPE_MUSIC, AudioAttributesCompat.USAGE_MEDIA)
|
||||
new AudioAttributesCompat.Builder()
|
||||
.setContentType(AudioAttributesCompat.CONTENT_TYPE_MUSIC)
|
||||
.setUsage(AudioAttributesCompat.USAGE_MEDIA)
|
||||
.build();
|
||||
mMediaLegacyAAC = mkBuilder(AudioManager.STREAM_MUSIC).build();
|
||||
mMediaLegacyAAC =
|
||||
new AudioAttributesCompat.Builder().setLegacyStreamType(AudioManager.STREAM_MUSIC).build();
|
||||
mNotificationAAC =
|
||||
mkBuilder(
|
||||
AudioAttributesCompat.CONTENT_TYPE_SONIFICATION,
|
||||
AudioAttributesCompat.USAGE_NOTIFICATION)
|
||||
new AudioAttributesCompat.Builder()
|
||||
.setContentType(AudioAttributesCompat.CONTENT_TYPE_SONIFICATION)
|
||||
.setUsage(AudioAttributesCompat.USAGE_NOTIFICATION)
|
||||
.build();
|
||||
mNotificationLegacyAAC =
|
||||
new AudioAttributesCompat.Builder()
|
||||
.setLegacyStreamType(AudioManager.STREAM_NOTIFICATION)
|
||||
.build();
|
||||
mNotificationLegacyAAC = mkBuilder(AudioManager.STREAM_NOTIFICATION).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SdkSuppress(minSdkVersion = 21)
|
||||
public void testCreateWithAudioAttributesApi21() {
|
||||
assertThat(mMediaAACFromAA, not(equalTo(null)));
|
||||
assertThat((AudioAttributes) mMediaAACFromAA.unwrap(), equalTo(mMediaAA));
|
||||
@ -90,7 +77,6 @@ public class AudioAttributesCompatTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SdkSuppress(minSdkVersion = 21)
|
||||
public void testEqualityApi21() {
|
||||
assertThat("self equality", mMediaAACFromAA, equalTo(mMediaAACFromAA));
|
||||
assertThat("different things", mMediaAACFromAA, not(equalTo(mNotificationAAC)));
|
||||
@ -126,59 +112,35 @@ public class AudioAttributesCompatTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SdkSuppress(minSdkVersion = 21)
|
||||
public void testLegacyStreamTypeInferenceApi21() {
|
||||
assertThat(mMediaAACFromAA.getLegacyStreamType(), equalTo(AudioManager.STREAM_MUSIC));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLegacyStreamTypeInferenceInLegacyMode() {
|
||||
// the builders behave differently based on the value of this only-for-testing global
|
||||
// so we need our very own objects inside this method
|
||||
AudioAttributesCompat.setForceLegacyBehavior(true);
|
||||
|
||||
AudioAttributesCompat mediaAAC =
|
||||
mkBuilder(AudioAttributesCompat.CONTENT_TYPE_MUSIC, AudioAttributesCompat.USAGE_MEDIA)
|
||||
.build();
|
||||
AudioAttributesCompat mediaLegacyAAC = mkBuilder(AudioManager.STREAM_MUSIC).build();
|
||||
|
||||
AudioAttributesCompat notificationAAC =
|
||||
mkBuilder(
|
||||
AudioAttributesCompat.CONTENT_TYPE_SONIFICATION,
|
||||
AudioAttributesCompat.USAGE_NOTIFICATION)
|
||||
.build();
|
||||
AudioAttributesCompat notificationLegacyAAC =
|
||||
mkBuilder(AudioManager.STREAM_NOTIFICATION).build();
|
||||
|
||||
assertThat(mediaAAC.getLegacyStreamType(), equalTo(AudioManager.STREAM_MUSIC));
|
||||
assertThat(mediaLegacyAAC.getLegacyStreamType(), equalTo(AudioManager.STREAM_MUSIC));
|
||||
assertThat(notificationAAC.getLegacyStreamType(), equalTo(AudioManager.STREAM_NOTIFICATION));
|
||||
assertThat(
|
||||
notificationLegacyAAC.getLegacyStreamType(), equalTo(AudioManager.STREAM_NOTIFICATION));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsageAndContentTypeInferredFromLegacyStreamType() {
|
||||
AudioAttributesCompat alarmAAC = mkBuilder(AudioManager.STREAM_ALARM).build();
|
||||
AudioAttributesCompat alarmAAC =
|
||||
new AudioAttributesCompat.Builder().setLegacyStreamType(AudioManager.STREAM_ALARM).build();
|
||||
assertThat(alarmAAC.getUsage(), equalTo(AudioAttributesCompat.USAGE_ALARM));
|
||||
assertThat(alarmAAC.getContentType(), equalTo(AudioAttributesCompat.CONTENT_TYPE_SONIFICATION));
|
||||
|
||||
AudioAttributesCompat musicAAC = mkBuilder(AudioManager.STREAM_MUSIC).build();
|
||||
AudioAttributesCompat musicAAC =
|
||||
new AudioAttributesCompat.Builder().setLegacyStreamType(AudioManager.STREAM_MUSIC).build();
|
||||
assertThat(musicAAC.getUsage(), equalTo(AudioAttributesCompat.USAGE_MEDIA));
|
||||
assertThat(musicAAC.getContentType(), equalTo(AudioAttributesCompat.CONTENT_TYPE_MUSIC));
|
||||
|
||||
AudioAttributesCompat notificationAAC = mkBuilder(AudioManager.STREAM_NOTIFICATION).build();
|
||||
AudioAttributesCompat notificationAAC =
|
||||
new AudioAttributesCompat.Builder()
|
||||
.setLegacyStreamType(AudioManager.STREAM_NOTIFICATION)
|
||||
.build();
|
||||
assertThat(notificationAAC.getUsage(), equalTo(AudioAttributesCompat.USAGE_NOTIFICATION));
|
||||
assertThat(
|
||||
notificationAAC.getContentType(), equalTo(AudioAttributesCompat.CONTENT_TYPE_SONIFICATION));
|
||||
|
||||
AudioAttributesCompat voiceCallAAC = mkBuilder(AudioManager.STREAM_VOICE_CALL).build();
|
||||
AudioAttributesCompat voiceCallAAC =
|
||||
new AudioAttributesCompat.Builder()
|
||||
.setLegacyStreamType(AudioManager.STREAM_VOICE_CALL)
|
||||
.build();
|
||||
assertThat(voiceCallAAC.getUsage(), equalTo(AudioAttributesCompat.USAGE_VOICE_COMMUNICATION));
|
||||
assertThat(voiceCallAAC.getContentType(), equalTo(AudioAttributesCompat.CONTENT_TYPE_SPEECH));
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanUp() {
|
||||
AudioAttributesCompat.setForceLegacyBehavior(false);
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,6 @@ import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import androidx.media3.test.session.common.TestUtils;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.SdkSuppress;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@ -30,7 +29,6 @@ import org.junit.runner.RunWith;
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class MediaDescriptionCompatTest {
|
||||
|
||||
@SdkSuppress(minSdkVersion = 21)
|
||||
@Test
|
||||
public void roundTripViaFrameworkObject_returnsEqualMediaUriAndExtras() {
|
||||
Uri mediaUri = Uri.parse("androidx://media/uri");
|
||||
@ -54,7 +52,6 @@ public class MediaDescriptionCompatTest {
|
||||
TestUtils.equals(createExtras(), restoredDescription2.getExtras());
|
||||
}
|
||||
|
||||
@SdkSuppress(minSdkVersion = 21)
|
||||
@Test
|
||||
public void getMediaDescription_withMediaUri_doesNotTouchExtras() {
|
||||
MediaDescriptionCompat originalDescription =
|
||||
|
Loading…
x
Reference in New Issue
Block a user