Bump minSdk to 21 and remove resulting simple dead code

All other AndroidX libraries have already increased their min SDK to
21.

This change renames private symbols to remove `V21` suffixes and
similar, but doesn't change public or protected symbols with similar
names, to avoid needless breakages/churn on users of the library.

Some of the dead code removal is more complex, so I've split it out
into follow-up changes to make it easier to review.

PiperOrigin-RevId: 651776556
This commit is contained in:
ibaker 2024-07-12 08:07:40 -07:00 committed by Copybara-Service
parent 5fa9985ce6
commit 6a9ff95bf0
60 changed files with 170 additions and 815 deletions

View File

@ -11,6 +11,8 @@
* Add override for `SimpleBasePlayer.State.Builder.setPlaylist()` to
directly specify a `Timeline` and current `Tracks` and `Metadata`
instead of building a playlist structure.
* Increase `minSdk` to 21 (Android Lollipop). This is aligned with all
other AndroidX libraries.
* ExoPlayer:
* `MediaCodecRenderer.onProcessedStreamChange()` can now be called for
every media item. Previously it was not called for the first one. Use

View File

@ -26,7 +26,7 @@ package androidx.media3.common {
}
public final class AudioAttributes {
method @RequiresApi(21) public androidx.media3.common.AudioAttributes.AudioAttributesV21 getAudioAttributesV21();
method public androidx.media3.common.AudioAttributes.AudioAttributesV21 getAudioAttributesV21();
field public static final androidx.media3.common.AudioAttributes DEFAULT;
field @androidx.media3.common.C.AudioAllowedCapturePolicy public final int allowedCapturePolicy;
field @androidx.media3.common.C.AudioContentType public final int contentType;
@ -35,7 +35,7 @@ package androidx.media3.common {
field @androidx.media3.common.C.AudioUsage public final int usage;
}
@RequiresApi(21) public static final class AudioAttributes.AudioAttributesV21 {
public static final class AudioAttributes.AudioAttributesV21 {
field public final android.media.AudioAttributes audioAttributes;
}

View File

@ -14,7 +14,7 @@
project.ext {
releaseVersion = '1.4.0-rc01'
releaseVersionCode = 1_004_000_2_01
minSdkVersion = 19
minSdkVersion = 21
// See https://developer.android.com/training/cars/media/automotive-os#automotive-module
automotiveMinSdkVersion = 28
appTargetSdkVersion = 34

View File

@ -63,7 +63,7 @@ public class DemoDownloadService extends DownloadService {
@Override
protected Scheduler getScheduler() {
return Util.SDK_INT >= 21 ? new PlatformScheduler(this, JOB_ID) : null;
return new PlatformScheduler(this, JOB_ID);
}
@Override

View File

@ -37,7 +37,6 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue;
public final class AudioAttributes {
/** A direct wrapper around {@link android.media.AudioAttributes}. */
@RequiresApi(21)
public static final class AudioAttributesV21 {
public final android.media.AudioAttributes audioAttributes;
@ -165,7 +164,6 @@ public final class AudioAttributes {
* <p>Some fields are ignored if the corresponding {@link android.media.AudioAttributes.Builder}
* setter is not available on the current API level.
*/
@RequiresApi(21)
public AudioAttributesV21 getAudioAttributesV21() {
if (audioAttributesV21 == null) {
audioAttributesV21 = new AudioAttributesV21(this);

View File

@ -32,7 +32,6 @@ import android.media.MediaFormat;
import android.net.Uri;
import android.view.Surface;
import androidx.annotation.IntDef;
import androidx.annotation.RequiresApi;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;
import com.google.errorprone.annotations.InlineMe;
@ -1698,7 +1697,6 @@ public final class C {
replacement = "Util.generateAudioSessionIdV21(context)",
imports = {"androidx.media3.common.util.Util"})
@Deprecated
@RequiresApi(21)
public static int generateAudioSessionIdV21(Context context) {
return Util.generateAudioSessionIdV21(context);
}

View File

@ -962,16 +962,14 @@ public final class Util {
/**
* Returns the language tag for a {@link Locale}.
*
* <p>For API levels &ge; 21, this tag is IETF BCP 47 compliant. Use {@link
* #normalizeLanguageCode(String)} to retrieve a normalized IETF BCP 47 language tag for all API
* levels if needed.
* <p>This tag is IETF BCP 47 compliant.
*
* @param locale A {@link Locale}.
* @return The language tag.
*/
@UnstableApi
public static String getLocaleLanguageTag(Locale locale) {
return SDK_INT >= 21 ? getLocaleLanguageTagV21(locale) : locale.toString();
return locale.toLanguageTag();
}
/**
@ -2253,7 +2251,6 @@ public final class Util {
/** Creates {@link AudioFormat} with given sampleRate, channelConfig, and encoding. */
@UnstableApi
@RequiresApi(21)
public static AudioFormat getAudioFormat(int sampleRate, int channelConfig, int encoding) {
return new AudioFormat.Builder()
.setSampleRate(sampleRate)
@ -2417,7 +2414,6 @@ public final class Util {
* @see AudioManager#generateAudioSessionId()
*/
@UnstableApi
@RequiresApi(21)
public static int generateAudioSessionIdV21(Context context) {
@Nullable
AudioManager audioManager = ((AudioManager) context.getSystemService(Context.AUDIO_SERVICE));
@ -3065,8 +3061,7 @@ public final class Util {
*/
@UnstableApi
public static boolean isWear(Context context) {
return SDK_INT >= 20
&& context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
}
/**
@ -3502,9 +3497,7 @@ public final class Util {
@UnstableApi
public static Drawable getDrawable(
Context context, Resources resources, @DrawableRes int drawableRes) {
return SDK_INT >= 21
? Api21.getDrawable(context, resources, drawableRes)
: resources.getDrawable(drawableRes);
return resources.getDrawable(drawableRes, context.getTheme());
}
/**
@ -3666,11 +3659,6 @@ public final class Util {
return split(config.getLocales().toLanguageTags(), ",");
}
@RequiresApi(21)
private static String getLocaleLanguageTagV21(Locale locale) {
return locale.toLanguageTag();
}
private static HashMap<String, String> createIsoLanguageReplacementMap() {
String[] iso2Languages = Locale.getISOLanguages();
HashMap<String, String> replacedLanguages =
@ -3890,14 +3878,6 @@ public final class Util {
0xF3
};
@RequiresApi(21)
private static final class Api21 {
@DoNotInline
public static Drawable getDrawable(Context context, Resources resources, @DrawableRes int res) {
return resources.getDrawable(res, context.getTheme());
}
}
@RequiresApi(29)
private static class Api29 {

View File

@ -40,7 +40,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.NoRouteToHostException;
@ -248,7 +247,6 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
private static final int MAX_REDIRECTS = 20; // Same limit as okhttp.
private static final int HTTP_STATUS_TEMPORARY_REDIRECT = 307;
private static final int HTTP_STATUS_PERMANENT_REDIRECT = 308;
private static final long MAX_BYTES_TO_DRAIN = 2048;
private final boolean allowCrossProtocolRedirects;
private final boolean crossProtocolRedirectsForceOriginal;
@ -482,9 +480,6 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
try {
@Nullable InputStream inputStream = this.inputStream;
if (inputStream != null) {
long bytesRemaining =
bytesToRead == C.LENGTH_UNSET ? C.LENGTH_UNSET : bytesToRead - bytesRead;
maybeTerminateInputStream(connection, bytesRemaining);
try {
inputStream.close();
} catch (IOException e) {
@ -784,52 +779,6 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
return read;
}
/**
* On platform API levels 19 and 20, okhttp's implementation of {@link InputStream#close} can
* block for a long time if the stream has a lot of data remaining. Call this method before
* closing the input stream to make a best effort to cause the input stream to encounter an
* unexpected end of input, working around this issue. On other platform API levels, the method
* does nothing.
*
* @param connection The connection whose {@link InputStream} should be terminated.
* @param bytesRemaining The number of bytes remaining to be read from the input stream if its
* length is known. {@link C#LENGTH_UNSET} otherwise.
*/
private static void maybeTerminateInputStream(
@Nullable HttpURLConnection connection, long bytesRemaining) {
if (connection == null || Util.SDK_INT > 20) {
return;
}
try {
InputStream inputStream = connection.getInputStream();
if (bytesRemaining == C.LENGTH_UNSET) {
// If the input stream has already ended, do nothing. The socket may be re-used.
if (inputStream.read() == -1) {
return;
}
} else if (bytesRemaining <= MAX_BYTES_TO_DRAIN) {
// There isn't much data left. Prefer to allow it to drain, which may allow the socket to be
// re-used.
return;
}
String className = inputStream.getClass().getName();
if ("com.android.okhttp.internal.http.HttpTransport$ChunkedInputStream".equals(className)
|| "com.android.okhttp.internal.http.HttpTransport$FixedLengthInputStream"
.equals(className)) {
Class<?> superclass = inputStream.getClass().getSuperclass();
Method unexpectedEndOfInput =
checkNotNull(superclass).getDeclaredMethod("unexpectedEndOfInput");
unexpectedEndOfInput.setAccessible(true);
unexpectedEndOfInput.invoke(inputStream);
}
} catch (Exception e) {
// If an IOException then the connection didn't ever have an input stream, or it was closed
// already. If another type of exception then something went wrong, most likely the device
// isn't using okhttp.
}
}
/** Closes the current connection quietly, if there is one. */
private void closeConnectionQuietly() {
if (connection != null) {

View File

@ -22,14 +22,11 @@ import android.net.Uri;
import android.system.ErrnoException;
import android.system.OsConstants;
import android.text.TextUtils;
import androidx.annotation.DoNotInline;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.media3.common.C;
import androidx.media3.common.PlaybackException;
import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.io.FileNotFoundException;
import java.io.IOException;
@ -200,7 +197,8 @@ public final class FileDataSource extends BaseDataSource {
// different SDK versions.
throw new FileDataSourceException(
e,
Util.SDK_INT >= 21 && Api21.isPermissionError(e.getCause())
e.getCause() instanceof ErrnoException
&& ((ErrnoException) e.getCause()).errno == OsConstants.EACCES
? PlaybackException.ERROR_CODE_IO_NO_PERMISSION
: PlaybackException.ERROR_CODE_IO_FILE_NOT_FOUND);
} catch (SecurityException e) {
@ -209,12 +207,4 @@ public final class FileDataSource extends BaseDataSource {
throw new FileDataSourceException(e, PlaybackException.ERROR_CODE_IO_UNSPECIFIED);
}
}
@RequiresApi(21)
private static final class Api21 {
@DoNotInline
private static boolean isPermissionError(@Nullable Throwable e) {
return e instanceof ErrnoException && ((ErrnoException) e).errno == OsConstants.EACCES;
}
}
}

View File

@ -24,7 +24,6 @@ import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.media3.common.C;
import androidx.media3.common.PlaybackException;
import androidx.media3.common.util.UnstableApi;
@ -48,7 +47,6 @@ import java.util.Set;
* #open(DataSpec)} is not actually used for reading data. Instead, the underlying {@link
* FileDescriptor} is used for all read operations.
*/
@RequiresApi(21)
@UnstableApi
public class FileDescriptorDataSource extends BaseDataSource {

View File

@ -748,7 +748,7 @@ public final class MediaMetricsListener
} else if (cause instanceof DrmSession.DrmSessionException) {
// Unpack DrmSessionException.
cause = checkNotNull(cause.getCause());
if (Util.SDK_INT >= 21 && cause instanceof MediaDrm.MediaDrmStateException) {
if (cause instanceof MediaDrm.MediaDrmStateException) {
String diagnosticsInfo = ((MediaDrm.MediaDrmStateException) cause).getDiagnosticInfo();
int subErrorCode = Util.getErrorCodeFromPlatformDiagnosticsInfo(diagnosticsInfo);
int errorCode = getDrmErrorCode(subErrorCode);
@ -771,8 +771,7 @@ public final class MediaMetricsListener
} else if (cause instanceof FileDataSource.FileDataSourceException
&& cause.getCause() instanceof FileNotFoundException) {
@Nullable Throwable notFoundCause = checkNotNull(cause.getCause()).getCause();
if (Util.SDK_INT >= 21
&& notFoundCause instanceof ErrnoException
if (notFoundCause instanceof ErrnoException
&& ((ErrnoException) notFoundCause).errno == OsConstants.EACCES) {
return new ErrorInfo(PlaybackErrorEvent.ERROR_IO_NO_PERMISSION, /* subErrorCode= */ 0);
} else {

View File

@ -57,7 +57,7 @@ public final class AudioCapabilitiesReceiver {
private final Listener listener;
private final Handler handler;
@Nullable private final AudioDeviceCallbackV23 audioDeviceCallback;
@Nullable private final BroadcastReceiver hdmiAudioPlugBroadcastReceiver;
private final BroadcastReceiver hdmiAudioPlugBroadcastReceiver;
@Nullable private final ExternalSurroundSoundSettingObserver externalSurroundSoundSettingObserver;
@Nullable private AudioCapabilities audioCapabilities;
@ -105,8 +105,7 @@ public final class AudioCapabilitiesReceiver {
this.routedDevice = routedDevice;
handler = Util.createHandlerForCurrentOrMainLooper();
audioDeviceCallback = Util.SDK_INT >= 23 ? new AudioDeviceCallbackV23() : null;
hdmiAudioPlugBroadcastReceiver =
Util.SDK_INT >= 21 ? new HdmiAudioPlugBroadcastReceiver() : null;
hdmiAudioPlugBroadcastReceiver = new HdmiAudioPlugBroadcastReceiver();
Uri externalSurroundSoundUri = AudioCapabilities.getExternalSurroundSoundGlobalSettingUri();
externalSurroundSoundSettingObserver =
externalSurroundSoundUri != null
@ -162,16 +161,12 @@ public final class AudioCapabilitiesReceiver {
if (Util.SDK_INT >= 23 && audioDeviceCallback != null) {
Api23.registerAudioDeviceCallback(context, audioDeviceCallback, handler);
}
@Nullable Intent stickyIntent = null;
if (hdmiAudioPlugBroadcastReceiver != null) {
IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_HDMI_AUDIO_PLUG);
stickyIntent =
context.registerReceiver(
hdmiAudioPlugBroadcastReceiver,
intentFilter,
/* broadcastPermission= */ null,
handler);
}
Intent stickyIntent =
context.registerReceiver(
hdmiAudioPlugBroadcastReceiver,
new IntentFilter(AudioManager.ACTION_HDMI_AUDIO_PLUG),
/* broadcastPermission= */ null,
handler);
audioCapabilities =
AudioCapabilities.getCapabilitiesInternal(
context, stickyIntent, audioAttributes, routedDevice);
@ -190,9 +185,7 @@ public final class AudioCapabilitiesReceiver {
if (Util.SDK_INT >= 23 && audioDeviceCallback != null) {
Api23.unregisterAudioDeviceCallback(context, audioDeviceCallback);
}
if (hdmiAudioPlugBroadcastReceiver != null) {
context.unregisterReceiver(hdmiAudioPlugBroadcastReceiver);
}
context.unregisterReceiver(hdmiAudioPlugBroadcastReceiver);
if (externalSurroundSoundSettingObserver != null) {
externalSurroundSoundSettingObserver.unregister();
}

View File

@ -244,9 +244,7 @@ public abstract class DecoderAudioRenderer<
if (formatSupport <= C.FORMAT_UNSUPPORTED_DRM) {
return RendererCapabilities.create(formatSupport);
}
@TunnelingSupport
int tunnelingSupport = Util.SDK_INT >= 21 ? TUNNELING_SUPPORTED : TUNNELING_NOT_SUPPORTED;
return RendererCapabilities.create(formatSupport, ADAPTIVE_NOT_SEAMLESS, tunnelingSupport);
return RendererCapabilities.create(formatSupport, ADAPTIVE_NOT_SEAMLESS, TUNNELING_SUPPORTED);
}
/**

View File

@ -337,8 +337,8 @@ public final class DefaultAudioSink implements AudioSink {
/**
* Sets whether to enable 32-bit float output or integer output. Where possible, 32-bit float
* output will be used if the input is 32-bit float, and also if the input is high resolution
* (24-bit or 32-bit) integer PCM. Float output is supported from API level 21. Audio processing
* (for example, speed adjustment) will not be available when float output is in use.
* (24-bit or 32-bit) integer PCM. Audio processing (for example, speed adjustment) will not be
* available when float output is in use.
*
* <p>The default value is {@code false}.
*/
@ -541,8 +541,6 @@ public final class DefaultAudioSink implements AudioSink {
@Nullable private ByteBuffer inputBuffer;
private int inputBufferAccessUnitCount;
@Nullable private ByteBuffer outputBuffer;
private byte @MonotonicNonNull [] preV21OutputBuffer;
private int preV21OutputBufferOffset;
private boolean handledEndOfStream;
private boolean stoppedAudioTrack;
private boolean handledOffloadOnPresentationEnded;
@ -571,7 +569,7 @@ public final class DefaultAudioSink implements AudioSink {
? getCapabilities(context, audioAttributes, /* routedDevice= */ null)
: builder.audioCapabilities;
audioProcessorChain = builder.audioProcessorChain;
enableFloatOutput = Util.SDK_INT >= 21 && builder.enableFloatOutput;
enableFloatOutput = builder.enableFloatOutput;
preferAudioTrackPlaybackParams = Util.SDK_INT >= 23 && builder.enableAudioTrackPlaybackParams;
offloadMode = OFFLOAD_MODE_DISABLED;
audioTrackBufferSizeProvider = builder.audioTrackBufferSizeProvider;
@ -700,14 +698,6 @@ public final class DefaultAudioSink implements AudioSink {
trimmingAudioProcessor.setTrimFrameCount(
inputFormat.encoderDelay, inputFormat.encoderPadding);
if (Util.SDK_INT < 21 && inputFormat.channelCount == 8 && outputChannels == null) {
// AudioTrack doesn't support 8 channel output before Android L. Discard the last two (side)
// channels to give a 6 channel stream that is supported.
outputChannels = new int[6];
for (int i = 0; i < outputChannels.length; i++) {
outputChannels[i] = i;
}
}
channelMappingAudioProcessor.setChannelMap(outputChannels);
AudioProcessor.AudioFormat outputFormat = new AudioProcessor.AudioFormat(inputFormat);
@ -1160,32 +1150,10 @@ public final class DefaultAudioSink implements AudioSink {
Assertions.checkArgument(outputBuffer == buffer);
} else {
outputBuffer = buffer;
if (Util.SDK_INT < 21) {
int bytesRemaining = buffer.remaining();
if (preV21OutputBuffer == null || preV21OutputBuffer.length < bytesRemaining) {
preV21OutputBuffer = new byte[bytesRemaining];
}
int originalPosition = buffer.position();
buffer.get(preV21OutputBuffer, 0, bytesRemaining);
buffer.position(originalPosition);
preV21OutputBufferOffset = 0;
}
}
int bytesRemaining = buffer.remaining();
int bytesWrittenOrError = 0; // Error if negative
if (Util.SDK_INT < 21) { // outputMode == OUTPUT_MODE_PCM.
// Work out how many bytes we can write without the risk of blocking.
int bytesToWrite = audioTrackPositionTracker.getAvailableBufferSize(writtenPcmBytes);
if (bytesToWrite > 0) {
bytesToWrite = min(bytesRemaining, bytesToWrite);
bytesWrittenOrError =
audioTrack.write(preV21OutputBuffer, preV21OutputBufferOffset, bytesToWrite);
if (bytesWrittenOrError > 0) { // No error
preV21OutputBufferOffset += bytesWrittenOrError;
buffer.position(buffer.position() + bytesWrittenOrError);
}
}
} else if (tunneling) {
if (tunneling) {
Assertions.checkState(avSyncPresentationTimeUs != C.TIME_UNSET);
if (avSyncPresentationTimeUs == C.TIME_END_OF_SOURCE) {
// Audio processors during tunneling are required to produce buffers immediately when
@ -1196,10 +1164,9 @@ public final class DefaultAudioSink implements AudioSink {
lastTunnelingAvSyncPresentationTimeUs = avSyncPresentationTimeUs;
}
bytesWrittenOrError =
writeNonBlockingWithAvSyncV21(
audioTrack, buffer, bytesRemaining, avSyncPresentationTimeUs);
writeNonBlockingWithAvSync(audioTrack, buffer, bytesRemaining, avSyncPresentationTimeUs);
} else {
bytesWrittenOrError = writeNonBlockingV21(audioTrack, buffer, bytesRemaining);
bytesWrittenOrError = writeNonBlocking(audioTrack, buffer, bytesRemaining);
}
lastFeedElapsedRealtimeMs = SystemClock.elapsedRealtime();
@ -1402,7 +1369,6 @@ public final class DefaultAudioSink implements AudioSink {
@Override
public void enableTunnelingV21() {
Assertions.checkState(Util.SDK_INT >= 21);
Assertions.checkState(externalAudioSessionIdProvided);
if (!tunneling) {
tunneling = true;
@ -1445,12 +1411,8 @@ public final class DefaultAudioSink implements AudioSink {
}
private void setVolumeInternal() {
if (!isAudioTrackInitialized()) {
// Do nothing.
} else if (Util.SDK_INT >= 21) {
setVolumeInternalV21(audioTrack, volume);
} else {
setVolumeInternalV3(audioTrack, volume);
if (isAudioTrackInitialized()) {
audioTrack.setVolume(volume);
}
}
@ -1474,14 +1436,6 @@ public final class DefaultAudioSink implements AudioSink {
if (isOffloadedPlayback(audioTrack)) {
checkNotNull(offloadStreamEventCallbackV29).unregister(audioTrack);
}
if (Util.SDK_INT < 21 && !externalAudioSessionIdProvided) {
// Prior to API level 21, audio sessions are not kept alive once there are no components
// associated with them. If we generated the session ID internally, the only component
// associated with the session is the audio track that's being released, and therefore
// the session will not be kept alive. As a result, we need to generate a new session when
// we next create an audio track.
audioSessionId = C.AUDIO_SESSION_ID_UNSET;
}
AudioTrackConfig oldAudioTrackConfig = configuration.buildAudioTrackConfig();
if (pendingConfiguration != null) {
configuration = pendingConfiguration;
@ -1822,13 +1776,11 @@ public final class DefaultAudioSink implements AudioSink {
}
}
@RequiresApi(21)
private static int writeNonBlockingV21(AudioTrack audioTrack, ByteBuffer buffer, int size) {
private static int writeNonBlocking(AudioTrack audioTrack, ByteBuffer buffer, int size) {
return audioTrack.write(buffer, size, AudioTrack.WRITE_NON_BLOCKING);
}
@RequiresApi(21)
private int writeNonBlockingWithAvSyncV21(
private int writeNonBlockingWithAvSync(
AudioTrack audioTrack, ByteBuffer buffer, int size, long presentationTimeUs) {
if (Util.SDK_INT >= 26) {
// The underlying platform AudioTrack writes AV sync headers directly.
@ -1858,7 +1810,7 @@ public final class DefaultAudioSink implements AudioSink {
return 0;
}
}
int result = writeNonBlockingV21(audioTrack, buffer, size);
int result = writeNonBlocking(audioTrack, buffer, size);
if (result < 0) {
bytesUntilNextAvSync = 0;
return result;
@ -1867,15 +1819,6 @@ public final class DefaultAudioSink implements AudioSink {
return result;
}
@RequiresApi(21)
private static void setVolumeInternalV21(AudioTrack audioTrack, float volume) {
audioTrack.setVolume(volume);
}
private static void setVolumeInternalV3(AudioTrack audioTrack, float volume) {
audioTrack.setStereoVolume(volume, volume);
}
private void playPendingData() {
if (!stoppedAudioTrack) {
stoppedAudioTrack = true;
@ -2253,10 +2196,8 @@ public final class DefaultAudioSink implements AudioSink {
private AudioTrack createAudioTrack(AudioAttributes audioAttributes, int audioSessionId) {
if (Util.SDK_INT >= 29) {
return createAudioTrackV29(audioAttributes, audioSessionId);
} else if (Util.SDK_INT >= 21) {
return createAudioTrackV21(audioAttributes, audioSessionId);
} else {
return createAudioTrackV9(audioAttributes, audioSessionId);
return createAudioTrackV21(audioAttributes, audioSessionId);
}
}
@ -2265,7 +2206,7 @@ public final class DefaultAudioSink implements AudioSink {
AudioFormat audioFormat =
Util.getAudioFormat(outputSampleRate, outputChannelConfig, outputEncoding);
android.media.AudioAttributes audioTrackAttributes =
getAudioTrackAttributesV21(audioAttributes, tunneling);
getAudioTrackAttributes(audioAttributes, tunneling);
return new AudioTrack.Builder()
.setAudioAttributes(audioTrackAttributes)
.setAudioFormat(audioFormat)
@ -2276,59 +2217,28 @@ public final class DefaultAudioSink implements AudioSink {
.build();
}
@RequiresApi(21)
private AudioTrack createAudioTrackV21(AudioAttributes audioAttributes, int audioSessionId) {
return new AudioTrack(
getAudioTrackAttributesV21(audioAttributes, tunneling),
getAudioTrackAttributes(audioAttributes, tunneling),
Util.getAudioFormat(outputSampleRate, outputChannelConfig, outputEncoding),
bufferSize,
AudioTrack.MODE_STREAM,
audioSessionId);
}
@SuppressWarnings("deprecation") // Using deprecated AudioTrack constructor.
private AudioTrack createAudioTrackV9(AudioAttributes audioAttributes, int audioSessionId) {
int streamType = Util.getStreamTypeForAudioUsage(audioAttributes.usage);
if (audioSessionId == C.AUDIO_SESSION_ID_UNSET) {
return new AudioTrack(
streamType,
outputSampleRate,
outputChannelConfig,
outputEncoding,
bufferSize,
AudioTrack.MODE_STREAM);
} else {
// Re-attach to the same audio session.
return new AudioTrack(
streamType,
outputSampleRate,
outputChannelConfig,
outputEncoding,
bufferSize,
AudioTrack.MODE_STREAM,
audioSessionId);
}
}
@RequiresApi(21)
private static android.media.AudioAttributes getAudioTrackAttributesV21(
private static android.media.AudioAttributes getAudioTrackAttributes(
AudioAttributes audioAttributes, boolean tunneling) {
if (tunneling) {
return getAudioTrackTunnelingAttributesV21();
return new android.media.AudioAttributes.Builder()
.setContentType(android.media.AudioAttributes.CONTENT_TYPE_MOVIE)
.setFlags(android.media.AudioAttributes.FLAG_HW_AV_SYNC)
.setUsage(android.media.AudioAttributes.USAGE_MEDIA)
.build();
} else {
return audioAttributes.getAudioAttributesV21().audioAttributes;
}
}
@RequiresApi(21)
private static android.media.AudioAttributes getAudioTrackTunnelingAttributesV21() {
return new android.media.AudioAttributes.Builder()
.setContentType(android.media.AudioAttributes.CONTENT_TYPE_MOVIE)
.setFlags(android.media.AudioAttributes.FLAG_HW_AV_SYNC)
.setUsage(android.media.AudioAttributes.USAGE_MEDIA)
.build();
}
public boolean outputModeIsOffload() {
return outputMode == OUTPUT_MODE_OFFLOAD;
}

View File

@ -278,8 +278,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
if (!MimeTypes.isAudio(format.sampleMimeType)) {
return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE);
}
@TunnelingSupport
int tunnelingSupport = Util.SDK_INT >= 21 ? TUNNELING_SUPPORTED : TUNNELING_NOT_SUPPORTED;
boolean formatHasDrm = format.cryptoType != C.CRYPTO_TYPE_NONE;
boolean supportsFormatDrm = supportsFormatDrm(format);
@ -291,7 +289,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
audioOffloadSupport = getAudioOffloadSupport(format);
if (audioSink.supportsFormat(format)) {
return RendererCapabilities.create(
C.FORMAT_HANDLED, ADAPTIVE_NOT_SEAMLESS, tunnelingSupport, audioOffloadSupport);
C.FORMAT_HANDLED, ADAPTIVE_NOT_SEAMLESS, TUNNELING_SUPPORTED, audioOffloadSupport);
}
}
// If the input is PCM then it will be passed directly to the sink. Hence the sink must support
@ -346,7 +344,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
return RendererCapabilities.create(
formatSupport,
adaptiveSupport,
tunnelingSupport,
TUNNELING_SUPPORTED,
hardwareAccelerationSupport,
decoderSupport,
audioOffloadSupport);

View File

@ -77,8 +77,11 @@ public final class DrmUtil {
*/
public static @PlaybackException.ErrorCode int getErrorCodeForMediaDrmException(
Throwable exception, @ErrorSource int errorSource) {
if (Util.SDK_INT >= 21 && Api21.isMediaDrmStateException(exception)) {
return Api21.mediaDrmStateExceptionToErrorCode(exception);
if (exception instanceof MediaDrm.MediaDrmStateException) {
@Nullable
String diagnosticsInfo = ((MediaDrm.MediaDrmStateException) exception).getDiagnosticInfo();
int drmErrorCode = Util.getErrorCodeFromPlatformDiagnosticsInfo(diagnosticsInfo);
return Util.getErrorCodeForMediaDrmErrorCode(drmErrorCode);
} else if (Util.SDK_INT >= 23 && Api23.isMediaDrmResetException(exception)) {
return PlaybackException.ERROR_CODE_DRM_SYSTEM_ERROR;
} else if (exception instanceof NotProvisionedException
@ -128,26 +131,6 @@ public final class DrmUtil {
&& e.getMessage().contains("Landroid/media/ResourceBusyException;.<init>(");
}
// Internal classes.
@RequiresApi(21)
private static final class Api21 {
@DoNotInline
public static boolean isMediaDrmStateException(@Nullable Throwable throwable) {
return throwable instanceof MediaDrm.MediaDrmStateException;
}
@DoNotInline
public static @PlaybackException.ErrorCode int mediaDrmStateExceptionToErrorCode(
Throwable throwable) {
@Nullable
String diagnosticsInfo = ((MediaDrm.MediaDrmStateException) throwable).getDiagnosticInfo();
int drmErrorCode = Util.getErrorCodeFromPlatformDiagnosticsInfo(diagnosticsInfo);
return Util.getErrorCodeForMediaDrmErrorCode(drmErrorCode);
}
}
@RequiresApi(23)
private static final class Api23 {

View File

@ -53,12 +53,22 @@ public final class FrameworkCryptoConfig implements CryptoConfig {
@Deprecated public final boolean forceAllowInsecureDecoderComponents;
/**
* Constructs an instance.
*
* @param uuid The DRM scheme UUID.
* @param sessionId The DRM session id.
* @param forceAllowInsecureDecoderComponents Whether to allow use of insecure decoder components
* even if the underlying platform says otherwise.
*/
@SuppressWarnings("deprecation") // Delegating to deprecated constructor
public FrameworkCryptoConfig(UUID uuid, byte[] sessionId) {
this(uuid, sessionId, /* forceAllowInsecureDecoderComponents= */ false);
}
/**
* @deprecated Use {@link FrameworkCryptoConfig#FrameworkCryptoConfig(UUID, byte[])} instead, and
* {@link ExoMediaDrm#requiresSecureDecoder} for the secure decoder handling logic.
*/
@SuppressWarnings("deprecation") // Setting deprecated field
@Deprecated
public FrameworkCryptoConfig(
UUID uuid, byte[] sessionId, boolean forceAllowInsecureDecoderComponents) {
this.uuid = uuid;

View File

@ -308,7 +308,7 @@ public final class FrameworkMediaDrm implements ExoMediaDrm {
}
}
}
return result && !shouldForceAllowInsecureDecoderComponents();
return result;
}
@UnstableApi
@ -389,17 +389,7 @@ public final class FrameworkMediaDrm implements ExoMediaDrm {
@UnstableApi
@Override
public FrameworkCryptoConfig createCryptoConfig(byte[] sessionId) throws MediaCryptoException {
boolean forceAllowInsecureDecoderComponents = shouldForceAllowInsecureDecoderComponents();
return new FrameworkCryptoConfig(
adjustUuid(uuid), sessionId, forceAllowInsecureDecoderComponents);
}
// Work around a bug prior to Lollipop where L1 Widevine forced into L3 mode would still
// indicate that it required secure video decoders [Internal ref: b/11428937].
private boolean shouldForceAllowInsecureDecoderComponents() {
return Util.SDK_INT < 21
&& C.WIDEVINE_UUID.equals(uuid)
&& "L3".equals(getPropertyString("securityLevel"));
return new FrameworkCryptoConfig(adjustUuid(uuid), sessionId);
}
@UnstableApi

View File

@ -252,7 +252,6 @@ public interface MediaCodecAdapter {
*
* @see MediaCodec#releaseOutputBuffer(int, long)
*/
@RequiresApi(21)
void releaseOutputBuffer(int index, long renderTimeStampNs);
/** Flushes the adapter and the underlying {@link MediaCodec}. */
@ -278,7 +277,6 @@ public interface MediaCodecAdapter {
* @see MediaCodec.Callback#onOutputBufferAvailable
* @return Whether listener was successfully registered.
*/
@RequiresApi(21)
default boolean registerOnBufferAvailableListener(
MediaCodecAdapter.OnBufferAvailableListener listener) {
return false;

View File

@ -38,22 +38,16 @@ public class MediaCodecDecoderException extends DecoderException {
public MediaCodecDecoderException(Throwable cause, @Nullable MediaCodecInfo codecInfo) {
super("Decoder failed: " + (codecInfo == null ? null : codecInfo.name), cause);
this.codecInfo = codecInfo;
diagnosticInfo = Util.SDK_INT >= 21 ? getDiagnosticInfoV21(cause) : null;
diagnosticInfo =
cause instanceof MediaCodec.CodecException
? ((MediaCodec.CodecException) cause).getDiagnosticInfo()
: null;
errorCode =
Util.SDK_INT >= 23
? getErrorCodeV23(cause)
: Util.getErrorCodeFromPlatformDiagnosticsInfo(diagnosticInfo);
}
@RequiresApi(21)
@Nullable
private static String getDiagnosticInfoV21(Throwable cause) {
if (cause instanceof MediaCodec.CodecException) {
return ((MediaCodec.CodecException) cause).getDiagnosticInfo();
}
return null;
}
@RequiresApi(23)
private static int getErrorCodeV23(Throwable cause) {
if (cause instanceof MediaCodec.CodecException) {

View File

@ -258,22 +258,12 @@ public final class MediaCodecInfo {
if (format.width <= 0 || format.height <= 0) {
return true;
}
if (Util.SDK_INT >= 21) {
return isVideoSizeAndRateSupportedV21(format.width, format.height, format.frameRate);
} else {
boolean isFormatSupported =
format.width * format.height <= MediaCodecUtil.maxH264DecodableFrameSize();
if (!isFormatSupported) {
logNoSupport("legacyFrameSize, " + format.width + "x" + format.height);
}
return isFormatSupported;
}
return isVideoSizeAndRateSupportedV21(format.width, format.height, format.frameRate);
} else { // Audio
return Util.SDK_INT < 21
|| ((format.sampleRate == Format.NO_VALUE
|| isAudioSampleRateSupportedV21(format.sampleRate))
&& (format.channelCount == Format.NO_VALUE
|| isAudioChannelCountSupportedV21(format.channelCount)));
return (format.sampleRate == Format.NO_VALUE
|| isAudioSampleRateSupportedV21(format.sampleRate))
&& (format.channelCount == Format.NO_VALUE
|| isAudioChannelCountSupportedV21(format.channelCount));
}
}
@ -482,7 +472,6 @@ public final class MediaCodecInfo {
* Format#NO_VALUE} or any value less than or equal to 0.
* @return Whether the decoder supports video with the given width, height and frame rate.
*/
@RequiresApi(21)
public boolean isVideoSizeAndRateSupportedV21(int width, int height, double frameRate) {
if (capabilities == null) {
logNoSupport("sizeAndRate.caps");
@ -506,13 +495,13 @@ public final class MediaCodecInfo {
return false;
}
// If COVERAGE_RESULT_NO_PERFORMANCE_POINTS_UNSUPPORTED then logic falls through
// to API 21+ code below.
// to code below.
}
if (!areSizeAndRateSupportedV21(videoCapabilities, width, height, frameRate)) {
if (!areSizeAndRateSupported(videoCapabilities, width, height, frameRate)) {
if (width >= height
|| !needsRotatedVerticalResolutionWorkaround(name)
|| !areSizeAndRateSupportedV21(videoCapabilities, height, width, frameRate)) {
|| !areSizeAndRateSupported(videoCapabilities, height, width, frameRate)) {
logNoSupport("sizeAndRate.support, " + width + "x" + height + "@" + frameRate);
return false;
}
@ -525,8 +514,6 @@ public final class MediaCodecInfo {
* Returns the smallest video size greater than or equal to a specified size that also satisfies
* the {@link MediaCodec}'s width and height alignment requirements.
*
* <p>Must not be called if the device SDK version is less than 21.
*
* @param width Width in pixels.
* @param height Height in pixels.
* @return The smallest video size greater than or equal to the specified size that also satisfies
@ -534,7 +521,6 @@ public final class MediaCodecInfo {
* codec.
*/
@Nullable
@RequiresApi(21)
public Point alignVideoSizeV21(int width, int height) {
if (capabilities == null) {
return null;
@ -543,18 +529,15 @@ public final class MediaCodecInfo {
if (videoCapabilities == null) {
return null;
}
return alignVideoSizeV21(videoCapabilities, width, height);
return alignVideoSize(videoCapabilities, width, height);
}
/**
* Whether the decoder supports audio with a given sample rate.
*
* <p>Must not be called if the device SDK version is less than 21.
*
* @param sampleRate The sample rate in Hz.
* @return Whether the decoder supports audio with the given sample rate.
*/
@RequiresApi(21)
public boolean isAudioSampleRateSupportedV21(int sampleRate) {
if (capabilities == null) {
logNoSupport("sampleRate.caps");
@ -575,12 +558,9 @@ public final class MediaCodecInfo {
/**
* Whether the decoder supports audio with a given channel count.
*
* <p>Must not be called if the device SDK version is less than 21.
*
* @param channelCount The channel count.
* @return Whether the decoder supports audio with the given channel count.
*/
@RequiresApi(21)
public boolean isAudioChannelCountSupportedV21(int channelCount) {
if (capabilities == null) {
logNoSupport("channelCount.caps");
@ -674,28 +654,17 @@ public final class MediaCodecInfo {
}
private static boolean isTunneling(CodecCapabilities capabilities) {
return Util.SDK_INT >= 21 && isTunnelingV21(capabilities);
}
@RequiresApi(21)
private static boolean isTunnelingV21(CodecCapabilities capabilities) {
return capabilities.isFeatureSupported(CodecCapabilities.FEATURE_TunneledPlayback);
}
private static boolean isSecure(CodecCapabilities capabilities) {
return Util.SDK_INT >= 21 && isSecureV21(capabilities);
}
@RequiresApi(21)
private static boolean isSecureV21(CodecCapabilities capabilities) {
return capabilities.isFeatureSupported(CodecCapabilities.FEATURE_SecurePlayback);
}
@RequiresApi(21)
private static boolean areSizeAndRateSupportedV21(
private static boolean areSizeAndRateSupported(
VideoCapabilities capabilities, int width, int height, double frameRate) {
// Don't ever fail due to alignment. See: https://github.com/google/ExoPlayer/issues/6551.
Point alignedSize = alignVideoSizeV21(capabilities, width, height);
Point alignedSize = alignVideoSize(capabilities, width, height);
width = alignedSize.x;
height = alignedSize.y;
@ -712,8 +681,7 @@ public final class MediaCodecInfo {
}
}
@RequiresApi(21)
private static Point alignVideoSizeV21(VideoCapabilities capabilities, int width, int height) {
private static Point alignVideoSize(VideoCapabilities capabilities, int width, int height) {
int widthAlignment = capabilities.getWidthAlignment();
int heightAlignment = capabilities.getHeightAlignment();
return new Point(

View File

@ -166,12 +166,9 @@ public final class MediaCodecUtil {
if (cachedDecoderInfos != null) {
return cachedDecoderInfos;
}
MediaCodecListCompat mediaCodecList =
Util.SDK_INT >= 21
? new MediaCodecListCompatV21(secure, tunneling)
: new MediaCodecListCompatV16();
MediaCodecListCompat mediaCodecList = new MediaCodecListCompatV21(secure, tunneling);
ArrayList<MediaCodecInfo> decoderInfos = getDecoderInfosInternal(key, mediaCodecList);
if (secure && decoderInfos.isEmpty() && 21 <= Util.SDK_INT && Util.SDK_INT <= 23) {
if (secure && decoderInfos.isEmpty() && Util.SDK_INT <= 23) {
// Some devices don't list secure decoders on API level 21 [Internal: b/18678462]. Try the
// legacy path. We also try this path on API levels 22 and 23 as a defensive measure.
mediaCodecList = new MediaCodecListCompatV16();
@ -290,9 +287,8 @@ public final class MediaCodecUtil {
for (CodecProfileLevel profileLevel : decoderInfo.getProfileLevels()) {
result = max(avcLevelToMaxFrameSize(profileLevel.level), result);
}
// We assume support for at least 480p (SDK_INT >= 21) or 360p (SDK_INT < 21), which are
// the levels mandated by the Android CDD.
result = max(result, Util.SDK_INT >= 21 ? (720 * 480) : (480 * 360));
// We assume support for at least 480p, which is the level mandated by the Android CDD.
result = max(result, 720 * 480);
}
maxH264DecodableFrameSize = result;
}
@ -562,17 +558,6 @@ public final class MediaCodecUtil {
return false;
}
// Work around broken audio decoders.
if (Util.SDK_INT < 21
&& ("CIPAACDecoder".equals(name)
|| "CIPMP3Decoder".equals(name)
|| "CIPVorbisDecoder".equals(name)
|| "CIPAMRNBDecoder".equals(name)
|| "AACDecoder".equals(name)
|| "MP3Decoder".equals(name))) {
return false;
}
// Work around https://github.com/google/ExoPlayer/issues/3249.
if (Util.SDK_INT < 24
&& ("OMX.SEC.aac.dec".equals(name) || "OMX.Exynos.AAC.Decoder".equals(name))
@ -588,26 +573,6 @@ public final class MediaCodecUtil {
return false;
}
// Work around https://github.com/google/ExoPlayer/issues/548.
// VP8 decoder on Samsung Galaxy S3/S4/S4 Mini/Tab 3/Note 2 does not render video.
if (Util.SDK_INT == 19
&& "OMX.SEC.vp8.dec".equals(name)
&& "samsung".equals(Util.MANUFACTURER)
&& (Util.DEVICE.startsWith("d2")
|| Util.DEVICE.startsWith("serrano")
|| Util.DEVICE.startsWith("jflte")
|| Util.DEVICE.startsWith("santos")
|| Util.DEVICE.startsWith("t0"))) {
return false;
}
// VP8 decoder on Samsung Galaxy S4 cannot be queried.
if (Util.SDK_INT == 19
&& Util.DEVICE.startsWith("jflte")
&& "OMX.qcom.video.decoder.vp8".equals(name)) {
return false;
}
// MTK AC3 decoder doesn't support decoding JOC streams in 2-D. See [Internal: b/69400041].
if (Util.SDK_INT <= 23
&& MimeTypes.AUDIO_E_AC3_JOC.equals(mimeType)
@ -663,19 +628,6 @@ public final class MediaCodecUtil {
});
}
if (Util.SDK_INT < 21 && decoderInfos.size() > 1) {
String firstCodecName = decoderInfos.get(0).name;
if ("OMX.SEC.mp3.dec".equals(firstCodecName)
|| "OMX.SEC.MP3.Decoder".equals(firstCodecName)
|| "OMX.brcm.audio.mp3.decoder".equals(firstCodecName)) {
// Prefer OMX.google codecs over OMX.SEC.mp3.dec, OMX.SEC.MP3.Decoder and
// OMX.brcm.audio.mp3.decoder on older devices. See:
// https://github.com/google/ExoPlayer/issues/398 and
// https://github.com/google/ExoPlayer/issues/4519.
sortByScore(decoderInfos, decoderInfo -> decoderInfo.name.startsWith("OMX.google") ? 1 : 0);
}
}
if (Util.SDK_INT < 32 && decoderInfos.size() > 1) {
String firstCodecName = decoderInfos.get(0).name;
// Prefer anything other than OMX.qti.audio.decoder.flac on older devices. See [Internal
@ -1056,7 +1008,6 @@ public final class MediaCodecUtil {
boolean isFeatureRequired(String feature, String mimeType, CodecCapabilities capabilities);
}
@RequiresApi(21)
private static final class MediaCodecListCompatV21 implements MediaCodecListCompat {
private final int codecKind;

View File

@ -17,7 +17,6 @@
package androidx.media3.exoplayer.mediacodec;
import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.common.util.Util.castNonNull;
import android.media.MediaCodec;
import android.media.MediaFormat;
@ -80,15 +79,9 @@ public final class SynchronousMediaCodecAdapter implements MediaCodecAdapter {
}
private final MediaCodec codec;
@Nullable private ByteBuffer[] inputByteBuffers;
@Nullable private ByteBuffer[] outputByteBuffers;
private SynchronousMediaCodecAdapter(MediaCodec mediaCodec) {
this.codec = mediaCodec;
if (Util.SDK_INT < 21) {
inputByteBuffers = codec.getInputBuffers();
outputByteBuffers = codec.getOutputBuffers();
}
}
@Override
@ -106,9 +99,6 @@ public final class SynchronousMediaCodecAdapter implements MediaCodecAdapter {
int index;
do {
index = codec.dequeueOutputBuffer(bufferInfo, 0);
if (index == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED && Util.SDK_INT < 21) {
outputByteBuffers = codec.getOutputBuffers();
}
} while (index == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED);
return index;
@ -122,21 +112,13 @@ public final class SynchronousMediaCodecAdapter implements MediaCodecAdapter {
@Override
@Nullable
public ByteBuffer getInputBuffer(int index) {
if (Util.SDK_INT >= 21) {
return codec.getInputBuffer(index);
} else {
return castNonNull(inputByteBuffers)[index];
}
return codec.getInputBuffer(index);
}
@Override
@Nullable
public ByteBuffer getOutputBuffer(int index) {
if (Util.SDK_INT >= 21) {
return codec.getOutputBuffer(index);
} else {
return castNonNull(outputByteBuffers)[index];
}
return codec.getOutputBuffer(index);
}
@Override
@ -158,7 +140,6 @@ public final class SynchronousMediaCodecAdapter implements MediaCodecAdapter {
}
@Override
@RequiresApi(21)
public void releaseOutputBuffer(int index, long renderTimeStampNs) {
codec.releaseOutputBuffer(index, renderTimeStampNs);
}
@ -170,8 +151,6 @@ public final class SynchronousMediaCodecAdapter implements MediaCodecAdapter {
@Override
public void release() {
inputByteBuffers = null;
outputByteBuffers = null;
try {
if (Util.SDK_INT >= 30 && Util.SDK_INT < 33) {
// Stopping the codec before releasing it works around a bug on APIs 30, 31 and 32 where

View File

@ -25,7 +25,6 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.PersistableBundle;
import androidx.annotation.RequiresApi;
import androidx.annotation.RequiresPermission;
import androidx.media3.common.util.Log;
import androidx.media3.common.util.UnstableApi;
@ -44,7 +43,6 @@ import androidx.media3.common.util.Util;
* android:exported="true"/>
* }</pre>
*/
@RequiresApi(21)
@UnstableApi
public final class PlatformScheduler implements Scheduler {

View File

@ -202,9 +202,7 @@ public final class Requirements implements Parcelable {
private boolean isDeviceIdle(Context context) {
PowerManager powerManager =
(PowerManager) Assertions.checkNotNull(context.getSystemService(Context.POWER_SERVICE));
return Util.SDK_INT >= 23
? powerManager.isDeviceIdleMode()
: Util.SDK_INT >= 20 ? !powerManager.isInteractive() : !powerManager.isScreenOn();
return Util.SDK_INT >= 23 ? powerManager.isDeviceIdleMode() : !powerManager.isInteractive();
}
private boolean isStorageNotLow(Context context) {

View File

@ -58,13 +58,10 @@ import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.Executor;
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@ -491,7 +488,6 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
private final VideoFrameReleaseControl.FrameReleaseInfo frameReleaseInfo;
private @MonotonicNonNull VideoFrameProcessor videoFrameProcessor;
@Nullable private Effect rotationEffect;
@Nullable private Format inputFormat;
private @InputType int inputType;
private long inputStreamStartPositionUs;
@ -611,21 +607,6 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
throw new UnsupportedOperationException("Unsupported input type " + inputType);
}
videoFrameReleaseControl.setFrameRate(format.frameRate);
// MediaCodec applies rotation after API 21.
if (inputType == INPUT_TYPE_SURFACE
&& Util.SDK_INT < 21
&& format.rotationDegrees != Format.NO_VALUE
&& format.rotationDegrees != 0) {
// We must apply a rotation effect.
if (rotationEffect == null
|| this.inputFormat == null
|| this.inputFormat.rotationDegrees != format.rotationDegrees) {
rotationEffect = ScaleAndRotateAccessor.createRotationEffect(format.rotationDegrees);
} // Else, the rotation effect matches the previous format's rotation degrees, keep the same
// instance.
} else {
rotationEffect = null;
}
this.inputType = inputType;
this.inputFormat = format;
@ -884,9 +865,6 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
}
ArrayList<Effect> effects = new ArrayList<>();
if (rotationEffect != null) {
effects.add(rotationEffect);
}
effects.addAll(videoEffects);
Format inputFormat = checkNotNull(this.inputFormat);
checkStateNotNull(videoFrameProcessor)
@ -1081,42 +1059,4 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
listener);
}
}
private static final class ScaleAndRotateAccessor {
private static @MonotonicNonNull Constructor<?> scaleAndRotateTransformationBuilderConstructor;
private static @MonotonicNonNull Method setRotationMethod;
private static @MonotonicNonNull Method buildScaleAndRotateTransformationMethod;
public static Effect createRotationEffect(float rotationDegrees) {
try {
prepare();
Object builder = scaleAndRotateTransformationBuilderConstructor.newInstance();
setRotationMethod.invoke(builder, rotationDegrees);
return (Effect) checkNotNull(buildScaleAndRotateTransformationMethod.invoke(builder));
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
@EnsuresNonNull({
"scaleAndRotateTransformationBuilderConstructor",
"setRotationMethod",
"buildScaleAndRotateTransformationMethod"
})
private static void prepare() throws NoSuchMethodException, ClassNotFoundException {
if (scaleAndRotateTransformationBuilderConstructor == null
|| setRotationMethod == null
|| buildScaleAndRotateTransformationMethod == null) {
// TODO: b/284964524 - Add LINT and proguard checks for media3.effect reflection.
Class<?> scaleAndRotateTransformationBuilderClass =
Class.forName("androidx.media3.effect.ScaleAndRotateTransformation$Builder");
scaleAndRotateTransformationBuilderConstructor =
scaleAndRotateTransformationBuilderClass.getConstructor();
setRotationMethod =
scaleAndRotateTransformationBuilderClass.getMethod("setRotationDegrees", float.class);
buildScaleAndRotateTransformationMethod =
scaleAndRotateTransformationBuilderClass.getMethod("build");
}
}
}
}

View File

@ -18,7 +18,6 @@ package androidx.media3.exoplayer.audio;
import static androidx.media3.common.C.FORMAT_HANDLED;
import static androidx.media3.exoplayer.RendererCapabilities.ADAPTIVE_NOT_SEAMLESS;
import static androidx.media3.exoplayer.RendererCapabilities.DECODER_SUPPORT_PRIMARY;
import static androidx.media3.exoplayer.RendererCapabilities.TUNNELING_NOT_SUPPORTED;
import static androidx.media3.exoplayer.RendererCapabilities.TUNNELING_SUPPORTED;
import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.END_OF_STREAM_ITEM;
import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.oneByteSample;
@ -96,17 +95,6 @@ public class DecoderAudioRendererTest {
audioRenderer.init(/* index= */ 0, PlayerId.UNSET, Clock.DEFAULT);
}
@Config(sdk = 19)
@Test
public void supportsFormatAtApi19() {
assertThat(audioRenderer.supportsFormat(FORMAT))
.isEqualTo(
ADAPTIVE_NOT_SEAMLESS
| TUNNELING_NOT_SUPPORTED
| FORMAT_HANDLED
| DECODER_SUPPORT_PRIMARY);
}
@Config(sdk = 21)
@Test
public void supportsFormatAtApi21() {

View File

@ -315,20 +315,6 @@ public final class DefaultAudioSinkTest {
.isEqualTo(SINK_FORMAT_SUPPORTED_WITH_TRANSCODING);
}
@Config(maxSdk = 20)
@Test
public void floatPcmNeedsTranscodingIfFloatOutputEnabledBeforeApi21() {
defaultAudioSink = new DefaultAudioSink.Builder().setEnableFloatOutput(true).build();
Format floatFormat =
STEREO_44_1_FORMAT
.buildUpon()
.setSampleMimeType(MimeTypes.AUDIO_RAW)
.setPcmEncoding(C.ENCODING_PCM_FLOAT)
.build();
assertThat(defaultAudioSink.getFormatSupport(floatFormat))
.isEqualTo(SINK_FORMAT_SUPPORTED_WITH_TRANSCODING);
}
@Config(minSdk = 21)
@Test
public void floatOutputSupportedIfFloatOutputEnabledFromApi21() {

View File

@ -21,7 +21,6 @@ import android.media.MediaCodec.BufferInfo;
import androidx.media3.common.C;
import androidx.media3.common.Format;
import androidx.media3.common.MimeTypes;
import androidx.media3.common.util.Util;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import java.nio.ByteBuffer;
@ -218,8 +217,7 @@ import org.checkerframework.checker.nullness.qual.PolyNull;
return null;
}
Locale locale =
Util.SDK_INT >= 21 ? Locale.forLanguageTag(languageTag) : new Locale(languageTag);
Locale locale = Locale.forLanguageTag(languageTag);
return locale.getISO3Language().isEmpty() ? languageTag : locale.getISO3Language();
}

View File

@ -366,7 +366,7 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
}
}
if (player.isCommandAvailable(COMMAND_STOP) || Util.SDK_INT < 21) {
if (player.isCommandAvailable(COMMAND_STOP)) {
// We must include a cancel intent for pre-L devices.
mediaStyle.setCancelButtonIntent(
actionFactory.createMediaActionPendingIntent(mediaSession, COMMAND_STOP));
@ -639,9 +639,7 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
}
private static long getPlaybackStartTimeEpochMs(Player player) {
// Changing "showWhen" causes notification flicker if SDK_INT < 21.
if (Util.SDK_INT >= 21
&& player.isPlaying()
if (player.isPlaying()
&& !player.isPlayingAd()
&& !player.isCurrentMediaItemDynamic()
&& player.getPlaybackParameters().speed == 1f) {

View File

@ -98,13 +98,8 @@ public abstract class MediaLibraryService extends MediaSessionService {
* <th>{@link ControllerInfo#getPackageName()}<br>for legacy browser</th>
* <th>{@link ControllerInfo#getUid()}<br>for legacy browser</th></tr>
* <tr>
* <td>{@code SDK_INT < 21}</td>
* <td>Actual package name via {@link Context#getPackageName()}</td>
* <td>Actual UID</td>
* </tr>
* <tr>
* <td>
* {@code 21 <= SDK_INT < 28}<br>
* {@code SDK_INT < 28}<br>
* for {@link Callback#onConnect onConnect}<br>
* and {@link Callback#onGetLibraryRoot onGetLibraryRoot}
* </td>
@ -113,7 +108,7 @@ public abstract class MediaLibraryService extends MediaSessionService {
* </tr>
* <tr>
* <td>
* {@code 21 <= SDK_INT < 28}<br>
* {@code SDK_INT < 28}<br>
* for other {@link Callback callbacks}
* </td>
* <td>{@link ControllerInfo#LEGACY_CONTROLLER_PACKAGE_NAME}</td>

View File

@ -210,14 +210,11 @@ import java.util.concurrent.TimeoutException;
MediaSession session,
MediaNotification mediaNotification,
boolean startInForegroundRequired) {
if (Util.SDK_INT >= 21) {
// Call Notification.MediaStyle#setMediaSession() indirectly.
android.media.session.MediaSession.Token fwkToken =
(android.media.session.MediaSession.Token)
session.getSessionCompat().getSessionToken().getToken();
mediaNotification.notification.extras.putParcelable(
Notification.EXTRA_MEDIA_SESSION, fwkToken);
}
// Call Notification.MediaStyle#setMediaSession() indirectly.
android.media.session.MediaSession.Token fwkToken =
(android.media.session.MediaSession.Token)
session.getSessionCompat().getSessionToken().getToken();
mediaNotification.notification.extras.putParcelable(Notification.EXTRA_MEDIA_SESSION, fwkToken);
this.mediaNotification = mediaNotification;
if (startInForegroundRequired) {
startForeground(mediaNotification);
@ -379,9 +376,7 @@ import java.util.concurrent.TimeoutException;
if (Util.SDK_INT >= 24) {
Api24.stopForeground(mediaSessionService, removeNotifications);
} else {
// For pre-L devices, we must call Service.stopForeground(true) anyway as a workaround
// that prevents the media notification from being undismissable.
mediaSessionService.stopForeground(removeNotifications || Util.SDK_INT < 21);
mediaSessionService.stopForeground(removeNotifications);
}
startedInForeground = false;
}

View File

@ -25,7 +25,6 @@ import static androidx.media3.session.SessionResult.RESULT_SUCCESS;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
@ -524,25 +523,7 @@ public class MediaSession {
return interfaceVersion;
}
/**
* Returns the package name. Can be {@link #LEGACY_CONTROLLER_PACKAGE_NAME} for
* interoperability.
*
* <p>Interoperability: Package name may not be precisely obtained for legacy controller API on
* older device. Here are details.
*
* <table>
* <caption>Summary when package name isn't precise</caption>
* <tr><th>SDK version when package name isn't precise</th>
* <th>{@code ControllerInfo#getPackageName()} for legacy controller</th>
* <tr><td>{@code SDK_INT < 21}</td>
* <td>Actual package name via {@link PackageManager#getNameForUid} with UID.<br>
* It's sufficient for most cases, but doesn't precisely distinguish caller if it
* uses shared user ID.</td>
* <tr><td>{@code 21 <= SDK_INT < 24}</td>
* <td>{@link #LEGACY_CONTROLLER_PACKAGE_NAME}</td>
* </table>
*/
/** Returns the package name, or {@link #LEGACY_CONTROLLER_PACKAGE_NAME} on API &le; 24. */
public String getPackageName() {
return remoteUserInfo.getPackageName();
}
@ -550,8 +531,8 @@ public class MediaSession {
/**
* Returns the UID of the controller. Can be a negative value for interoperability.
*
* <p>Interoperability: If {@code 21 <= SDK_INT < 28}, then UID would be a negative value
* because it cannot be obtained.
* <p>Interoperability: If {@code SDK_INT < 28}, then UID would be a negative value because it
* cannot be obtained.
*/
public int getUid() {
return remoteUserInfo.getUid();
@ -1221,7 +1202,6 @@ public class MediaSession {
* android.media.session.MediaSession} created internally by this session.
*/
@SuppressWarnings("UnnecessarilyFullyQualified") // Avoiding clash with Media3 token.
@RequiresApi(21)
@UnstableApi
public final android.media.session.MediaSession.Token getPlatformToken() {
return (android.media.session.MediaSession.Token)

View File

@ -53,11 +53,9 @@ import android.os.SystemClock;
import android.view.KeyEvent;
import android.view.ViewConfiguration;
import androidx.annotation.CheckResult;
import androidx.annotation.DoNotInline;
import androidx.annotation.FloatRange;
import androidx.annotation.GuardedBy;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.media3.common.AudioAttributes;
import androidx.media3.common.DeviceInfo;
import androidx.media3.common.MediaItem;
@ -1213,7 +1211,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
}
// Double tap detection.
int keyCode = keyEvent.getKeyCode();
boolean isTvApp = Util.SDK_INT >= 21 && Api21.isTvApp(context);
boolean isTvApp = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
boolean doubleTapCompleted = false;
switch (keyCode) {
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
@ -1910,12 +1908,4 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
}
}
}
@RequiresApi(21)
private static final class Api21 {
@DoNotInline
public static boolean isTvApp(Context context) {
return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
}
}
}

View File

@ -35,7 +35,6 @@ import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.common.util.Assertions.checkStateNotNull;
import static androidx.media3.common.util.Util.castNonNull;
import static androidx.media3.common.util.Util.postOrRun;
import static androidx.media3.session.MediaUtils.TRANSACTION_SIZE_LIMIT_IN_BYTES;
import static androidx.media3.session.SessionCommand.COMMAND_CODE_CUSTOM;
import static androidx.media3.session.SessionError.ERROR_UNKNOWN;
import static androidx.media3.session.SessionResult.RESULT_INFO_SKIPPED;
@ -1226,7 +1225,7 @@ import org.checkerframework.checker.initialization.qual.Initialized;
() -> {
int completedBitmapFutureCount = resultCount.incrementAndGet();
if (completedBitmapFutureCount == mediaItemList.size()) {
handleBitmapFuturesAllCompletedAndSetQueue(bitmapFutures, timeline, mediaItemList);
handleBitmapFuturesAllCompletedAndSetQueue(bitmapFutures, mediaItemList);
}
};
@ -1247,9 +1246,7 @@ import org.checkerframework.checker.initialization.qual.Initialized;
}
private void handleBitmapFuturesAllCompletedAndSetQueue(
List<@NullableType ListenableFuture<Bitmap>> bitmapFutures,
Timeline timeline,
List<MediaItem> mediaItems) {
List<@NullableType ListenableFuture<Bitmap>> bitmapFutures, List<MediaItem> mediaItems) {
List<QueueItem> queueItemList = new ArrayList<>();
for (int i = 0; i < bitmapFutures.size(); i++) {
@Nullable ListenableFuture<Bitmap> future = bitmapFutures.get(i);
@ -1264,22 +1261,9 @@ import org.checkerframework.checker.initialization.qual.Initialized;
queueItemList.add(LegacyConversions.convertToQueueItem(mediaItems.get(i), i, bitmap));
}
if (Util.SDK_INT < 21) {
// In order to avoid TransactionTooLargeException for below API 21, we need to
// cut the list so that it doesn't exceed the binder transaction limit.
List<QueueItem> truncatedList =
MediaUtils.truncateListBySize(queueItemList, TRANSACTION_SIZE_LIMIT_IN_BYTES);
if (truncatedList.size() != timeline.getWindowCount()) {
Log.i(
TAG,
"Sending " + truncatedList.size() + " items out of " + timeline.getWindowCount());
}
setQueue(sessionCompat, truncatedList);
} else {
// Framework MediaSession#setQueue() uses ParceledListSlice,
// which means we can safely send long lists.
setQueue(sessionCompat, queueItemList);
}
// Framework MediaSession#setQueue() uses ParceledListSlice,
// which means we can safely send long lists.
setQueue(sessionCompat, queueItemList);
}
@Override

View File

@ -300,8 +300,7 @@ public final class SessionToken {
private static MediaSessionCompat.Token createCompatToken(
Parcelable platformOrLegacyCompatToken) {
if (Util.SDK_INT >= 21
&& platformOrLegacyCompatToken instanceof android.media.session.MediaSession.Token) {
if (platformOrLegacyCompatToken instanceof android.media.session.MediaSession.Token) {
return MediaSessionCompat.Token.fromToken(platformOrLegacyCompatToken);
}
// Assume this is an android.support.v4.media.session.MediaSessionCompat.Token.

View File

@ -666,7 +666,7 @@ public final class DashStreamingTest {
MediaCodecUtil.getDecoderInfo(
MimeTypes.VIDEO_H264, /* secure= */ false, /* tunneling= */ false);
assertThat(decoderInfo).isNotNull();
assertThat(Util.SDK_INT < 21 || decoderInfo.adaptive).isTrue();
assertThat(decoderInfo.adaptive).isTrue();
}
@Test

View File

@ -17,13 +17,11 @@ package androidx.media3.test.exoplayer.playback.gts;
import static java.lang.Math.max;
import android.annotation.SuppressLint;
import android.content.Context;
import android.media.MediaCrypto;
import android.media.MediaFormat;
import android.os.Handler;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.media3.common.C;
import androidx.media3.common.Format;
import androidx.media3.decoder.DecoderInputBuffer;
@ -244,8 +242,6 @@ import java.util.ArrayList;
super.renderOutputBuffer(codec, index, presentationTimeUs);
}
@SuppressLint("UseSdkSuppress") // Not a test class or method.
@RequiresApi(21)
@Override
protected void renderOutputBufferV21(
MediaCodecAdapter codec, int index, long presentationTimeUs, long releaseTimeNs) {

View File

@ -115,9 +115,7 @@ public class EnumerateDecodersTest {
boolean isAudio = MimeTypes.isAudio(requestedMimeType);
StringBuilder result = new StringBuilder();
result.append("[requestedMimeType=").append(requestedMimeType);
if (Util.SDK_INT >= 21) {
result.append(", mimeType=").append(codecCapabilities.getMimeType());
}
result.append(", mimeType=").append(codecCapabilities.getMimeType());
result.append(", profileLevels=");
appendProfileLevels(codecCapabilities.profileLevels, result);
if (Util.SDK_INT >= 23) {
@ -125,23 +123,19 @@ public class EnumerateDecodersTest {
.append(", maxSupportedInstances=")
.append(codecCapabilities.getMaxSupportedInstances());
}
if (Util.SDK_INT >= 21) {
if (isVideo) {
result.append(", videoCapabilities=");
appendVideoCapabilities(codecCapabilities.getVideoCapabilities(), result);
result.append(", colorFormats=").append(Arrays.toString(codecCapabilities.colorFormats));
} else if (isAudio) {
result.append(", audioCapabilities=");
appendAudioCapabilities(codecCapabilities.getAudioCapabilities(), result);
}
if (isVideo) {
result.append(", videoCapabilities=");
appendVideoCapabilities(codecCapabilities.getVideoCapabilities(), result);
result.append(", colorFormats=").append(Arrays.toString(codecCapabilities.colorFormats));
} else if (isAudio) {
result.append(", audioCapabilities=");
appendAudioCapabilities(codecCapabilities.getAudioCapabilities(), result);
}
if (isVideo
&& codecCapabilities.isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback)) {
result.append(", FEATURE_AdaptivePlayback");
}
if (Util.SDK_INT >= 21
&& isVideo
&& codecCapabilities.isFeatureSupported(CodecCapabilities.FEATURE_SecurePlayback)) {
if (isVideo && codecCapabilities.isFeatureSupported(CodecCapabilities.FEATURE_SecurePlayback)) {
result.append(", FEATURE_SecurePlayback");
}
if (Util.SDK_INT >= 26
@ -149,8 +143,7 @@ public class EnumerateDecodersTest {
&& codecCapabilities.isFeatureSupported(CodecCapabilities.FEATURE_PartialFrame)) {
result.append(", FEATURE_PartialFrame");
}
if (Util.SDK_INT >= 21
&& (isVideo || isAudio)
if ((isVideo || isAudio)
&& codecCapabilities.isFeatureSupported(CodecCapabilities.FEATURE_TunneledPlayback)) {
result.append(", FEATURE_TunneledPlayback");
}

View File

@ -610,7 +610,7 @@ public class MediaControllerCompatCallbackWithMediaSessionTest {
session.setPlayer(playerConfigToUpdate);
// In API 21 and 22, onAudioInfoChanged is not called when playback is changed to local.
if (Util.SDK_INT == 21 || Util.SDK_INT == 22) {
if (Util.SDK_INT <= 22) {
PollingCheck.waitFor(
TIMEOUT_MS,
() -> {
@ -657,21 +657,13 @@ public class MediaControllerCompatCallbackWithMediaSessionTest {
session.setPlayer(playerConfig);
// In API 21+, onAudioInfoChanged() is not called when playbackType is not changed.
if (Util.SDK_INT >= 21) {
PollingCheck.waitFor(
TIMEOUT_MS,
() -> {
MediaControllerCompat.PlaybackInfo info = controllerCompat.getPlaybackInfo();
return info.getPlaybackType() == legacyPlaybackType
&& info.getAudioAttributes().getLegacyStreamType() == legacyStream;
});
} else {
assertThat(playbackInfoNotified.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
MediaControllerCompat.PlaybackInfo info = controllerCompat.getPlaybackInfo();
assertThat(info.getPlaybackType()).isEqualTo(legacyPlaybackType);
assertThat(info.getAudioAttributes().getLegacyStreamType()).isEqualTo(legacyStream);
}
PollingCheck.waitFor(
TIMEOUT_MS,
() -> {
MediaControllerCompat.PlaybackInfo info = controllerCompat.getPlaybackInfo();
return info.getPlaybackType() == legacyPlaybackType
&& info.getAudioAttributes().getLegacyStreamType() == legacyStream;
});
}
@Test
@ -709,23 +701,14 @@ public class MediaControllerCompatCallbackWithMediaSessionTest {
session.setPlayer(playerConfigToUpdate);
// In API 21+, onAudioInfoChanged() is not called when playbackType is not changed.
if (Util.SDK_INT >= 21) {
PollingCheck.waitFor(
TIMEOUT_MS,
() -> {
MediaControllerCompat.PlaybackInfo info = controllerCompat.getPlaybackInfo();
return info.getPlaybackType() == legacyPlaybackTypeToUpdate
&& info.getMaxVolume() == deviceInfoToUpdate.maxVolume
&& info.getCurrentVolume() == deviceVolumeToUpdate;
});
} else {
assertThat(playbackInfoNotified.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
MediaControllerCompat.PlaybackInfo info = controllerCompat.getPlaybackInfo();
assertThat(info.getPlaybackType()).isEqualTo(legacyPlaybackTypeToUpdate);
assertThat(info.getMaxVolume()).isEqualTo(deviceInfoToUpdate.maxVolume);
assertThat(info.getCurrentVolume()).isEqualTo(deviceVolumeToUpdate);
}
PollingCheck.waitFor(
TIMEOUT_MS,
() -> {
MediaControllerCompat.PlaybackInfo info = controllerCompat.getPlaybackInfo();
return info.getPlaybackType() == legacyPlaybackTypeToUpdate
&& info.getMaxVolume() == deviceInfoToUpdate.maxVolume
&& info.getCurrentVolume() == deviceVolumeToUpdate;
});
}
@Test
@ -1555,15 +1538,8 @@ public class MediaControllerCompatCallbackWithMediaSessionTest {
assertThat(latch.await(LONG_TIMEOUT_MS, MILLISECONDS)).isTrue();
List<QueueItem> queueFromParam = queueRef.get();
List<QueueItem> queueFromGetter = controllerCompat.getQueue();
if (Util.SDK_INT >= 21) {
assertThat(queueFromParam).hasSize(listSize);
assertThat(queueFromGetter).hasSize(listSize);
} else {
// Below API 21, only the initial part of the playlist is sent to the
// MediaControllerCompat when the list is too long.
assertThat(queueFromParam.size() < listSize).isTrue();
assertThat(queueFromGetter).hasSize(queueFromParam.size());
}
assertThat(queueFromParam).hasSize(listSize);
assertThat(queueFromGetter).hasSize(listSize);
for (int i = 0; i < queueFromParam.size(); i++) {
assertThat(queueFromParam.get(i).getDescription().getMediaId())
.isEqualTo(TestUtils.getMediaIdInFakeTimeline(i));

View File

@ -347,7 +347,7 @@ public class MediaControllerListenerWithMediaSessionCompatTest {
// We need to trigger MediaControllerCompat.Callback.onAudioInfoChanged in order to raise the
// onAudioAttributesChanged() callback. In API 21 and 22, onAudioInfoChanged is not called when
// playback is changed to local.
assumeTrue(Util.SDK_INT != 21 && Util.SDK_INT != 22);
assumeTrue(Util.SDK_INT > 22);
session.setPlaybackToRemote(
/* volumeControl= */ VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE,

View File

@ -49,7 +49,6 @@ import androidx.media3.common.Player.PositionInfo;
import androidx.media3.common.Timeline;
import androidx.media3.common.Timeline.Window;
import androidx.media3.common.util.BitmapLoader;
import androidx.media3.common.util.Util;
import androidx.media3.datasource.DataSourceBitmapLoader;
import androidx.media3.test.session.common.HandlerThreadTestRule;
import androidx.test.core.app.ApplicationProvider;
@ -200,24 +199,6 @@ public class MediaControllerMediaSessionCompatCallbackAggregationTest {
MediaItem mediaItem = timelineRef.get().getWindow(i, new Window()).mediaItem;
MediaItem expectedMediaItem =
(i == testMediaItemIndex) ? testCurrentMediaItem : testMediaItems.get(i);
if (Util.SDK_INT < 21) {
// Bitmap conversion and back gives not exactly the same byte array below API 21
MediaMetadata mediaMetadata =
mediaItem
.mediaMetadata
.buildUpon()
.setArtworkData(/* artworkData= */ null, /* artworkDataType= */ null)
.build();
MediaMetadata expectedMediaMetadata =
expectedMediaItem
.mediaMetadata
.buildUpon()
.setArtworkData(/* artworkData= */ null, /* artworkDataType= */ null)
.build();
mediaItem = mediaItem.buildUpon().setMediaMetadata(mediaMetadata).build();
expectedMediaItem =
expectedMediaItem.buildUpon().setMediaMetadata(expectedMediaMetadata).build();
}
assertThat(mediaItem).isEqualTo(expectedMediaItem);
}
assertThat(timelineChangeReasonRef.get()).isEqualTo(TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED);

View File

@ -27,7 +27,6 @@ import android.media.session.PlaybackState;
import android.os.HandlerThread;
import androidx.media3.common.Player;
import androidx.media3.common.Player.State;
import androidx.media3.common.util.Util;
import androidx.media3.test.session.common.MainLooperTestRule;
import androidx.media3.test.session.common.TestHandler;
import androidx.test.core.app.ApplicationProvider;
@ -37,7 +36,6 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.After;
import org.junit.Assume;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
@ -57,9 +55,6 @@ public class MediaControllerWithFrameworkMediaSessionTest {
@Before
public void setUp() {
if (Util.SDK_INT < 21) {
return;
}
context = ApplicationProvider.getApplicationContext();
HandlerThread handlerThread = new HandlerThread(TAG);
@ -79,7 +74,6 @@ public class MediaControllerWithFrameworkMediaSessionTest {
@SuppressWarnings("UnnecessarilyFullyQualified") // Intentionally fully qualified for fwk session.
@Test
public void createController() throws Exception {
Assume.assumeTrue(Util.SDK_INT >= 21); // For framework MediaSession.
MediaSession fwkSession = new android.media.session.MediaSession(context, TAG);
fwkSession.setActive(true);
fwkSession.setFlags(
@ -100,7 +94,6 @@ public class MediaControllerWithFrameworkMediaSessionTest {
@SuppressWarnings("UnnecessarilyFullyQualified") // Intentionally fully qualified for fwk session.
@Test
public void onPlaybackStateChanged_isNotifiedByFwkSessionChanges() throws Exception {
Assume.assumeTrue(Util.SDK_INT >= 21); // For framework MediaSession.
MediaSession fwkSession = new android.media.session.MediaSession(context, TAG);
fwkSession.setActive(true);
fwkSession.setFlags(

View File

@ -571,11 +571,8 @@ public class MediaControllerWithMediaSessionCompatTest {
assertThat(TextUtils.equals(metadata.subtitle, testSubtitle)).isTrue();
assertThat(TextUtils.equals(metadata.description, testDescription)).isTrue();
assertThat(metadata.artworkUri).isEqualTo(testIconUri);
if (Util.SDK_INT >= 21) {
// Bitmap conversion and back gives not exactly the same byte array below API 21
assertThat(metadata.artworkData).isEqualTo(testArtworkData);
}
if (Util.SDK_INT < 21 || Util.SDK_INT >= 23) {
assertThat(metadata.artworkData).isEqualTo(testArtworkData);
if (Util.SDK_INT >= 23) {
// TODO(b/199055952): Test mediaUri for all API levels once the bug is fixed.
assertThat(mediaItem.requestMetadata.mediaUri).isEqualTo(testMediaUri);
}
@ -909,19 +906,6 @@ public class MediaControllerWithMediaSessionCompatTest {
threadTestRule.getHandler().postAndSync(controller::getMediaMetadata);
assertThat(mediaMetadata.artworkData).isNotNull();
if (Util.SDK_INT < 21) {
// Bitmap conversion and back gives not exactly the same byte array below API 21
mediaMetadata =
mediaMetadata
.buildUpon()
.setArtworkData(/* artworkData= */ null, /* artworkDataType= */ null)
.build();
testMediaMetadata =
testMediaMetadata
.buildUpon()
.setArtworkData(/* artworkData= */ null, /* artworkDataType= */ null)
.build();
}
assertThat(mediaMetadata.artworkData).isEqualTo(testMediaMetadata.artworkData);
}
@ -1573,7 +1557,7 @@ public class MediaControllerWithMediaSessionCompatTest {
@Test
public void setPlaybackToLocal_notifiesDeviceInfoAndVolume() throws Exception {
if (Util.SDK_INT == 21 || Util.SDK_INT == 22) {
if (Util.SDK_INT <= 22) {
// In API 21 and 22, onAudioInfoChanged is not called.
return;
}

View File

@ -91,7 +91,7 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
private static final String TEST_URI = "http://test.test";
private static final String EXPECTED_CONTROLLER_PACKAGE_NAME =
(Util.SDK_INT < 21 || Util.SDK_INT >= 24) ? SUPPORT_APP_PACKAGE_NAME : LEGACY_CONTROLLER;
Util.SDK_INT >= 24 ? SUPPORT_APP_PACKAGE_NAME : LEGACY_CONTROLLER;
@ClassRule public static MainLooperTestRule mainLooperTestRule = new MainLooperTestRule();
@ -1200,7 +1200,7 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
@Test
public void setVolumeWithLocalVolume() throws Exception {
if (Util.SDK_INT >= 21 && audioManager.isVolumeFixed()) {
if (audioManager.isVolumeFixed()) {
// This test is not eligible for this device.
return;
}
@ -1250,7 +1250,7 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
@Test
public void adjustVolumeWithLocalVolume() throws Exception {
if (Util.SDK_INT >= 21 && audioManager.isVolumeFixed()) {
if (audioManager.isVolumeFixed()) {
// This test is not eligible for this device.
return;
}

View File

@ -541,7 +541,7 @@ public class MediaSessionCompatCallbackWithMediaControllerTest {
@Test
public void setDeviceVolume_forLocalPlayback_setsStreamVolume() throws Exception {
if (Util.SDK_INT >= 21 && audioManager.isVolumeFixed()) {
if (audioManager.isVolumeFixed()) {
// This test is not eligible for this device.
return;
}
@ -570,7 +570,7 @@ public class MediaSessionCompatCallbackWithMediaControllerTest {
@Test
public void increaseDeviceVolume_forLocalPlayback_increasesStreamVolume() throws Exception {
if (Util.SDK_INT >= 21 && audioManager.isVolumeFixed()) {
if (audioManager.isVolumeFixed()) {
// This test is not eligible for this device.
return;
}
@ -600,7 +600,7 @@ public class MediaSessionCompatCallbackWithMediaControllerTest {
@Test
public void decreaseDeviceVolume_forLocalPlayback_decreasesStreamVolume() throws Exception {
if (Util.SDK_INT >= 21 && audioManager.isVolumeFixed()) {
if (audioManager.isVolumeFixed()) {
// This test is not eligible for this device.
return;
}

View File

@ -45,7 +45,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import org.junit.After;
import org.junit.Assume;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
@ -79,9 +78,6 @@ public class MediaSessionKeyEventTest {
@Before
public void setUp() throws Exception {
if (Util.SDK_INT < 21) {
return;
}
Context context = ApplicationProvider.getApplicationContext();
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
handler = threadTestRule.getHandler();
@ -128,9 +124,6 @@ public class MediaSessionKeyEventTest {
@After
public void tearDown() throws Exception {
if (Util.SDK_INT < 21) {
return;
}
handler.postAndSync(
() -> {
if (mediaPlayer != null) {
@ -143,7 +136,6 @@ public class MediaSessionKeyEventTest {
@Test
public void playKeyEvent() throws Exception {
Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19.
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_PLAY, false);
player.awaitMethodCalled(MockPlayer.METHOD_PLAY, TIMEOUT_MS);
@ -151,7 +143,6 @@ public class MediaSessionKeyEventTest {
@Test
public void pauseKeyEvent() throws Exception {
Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19.
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_PAUSE, false);
player.awaitMethodCalled(MockPlayer.METHOD_PAUSE, TIMEOUT_MS);
@ -159,7 +150,6 @@ public class MediaSessionKeyEventTest {
@Test
public void nextKeyEvent() throws Exception {
Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19.
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_NEXT, false);
player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO_NEXT, TIMEOUT_MS);
@ -167,7 +157,6 @@ public class MediaSessionKeyEventTest {
@Test
public void previousKeyEvent() throws Exception {
Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19.
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_PREVIOUS, false);
player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO_PREVIOUS, TIMEOUT_MS);
@ -177,7 +166,6 @@ public class MediaSessionKeyEventTest {
public void
fastForwardKeyEvent_mediaNotificationControllerConnected_callFromNotificationController()
throws Exception {
Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19.
MediaController controller = connectMediaNotificationController();
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, /* doubleTap= */ false);
@ -204,8 +192,6 @@ public class MediaSessionKeyEventTest {
public void
fastForwardKeyEvent_mediaNotificationControllerNotConnected_callFromLegacyFallbackController()
throws Exception {
Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19.
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, false);
player.awaitMethodCalled(MockPlayer.METHOD_SEEK_FORWARD, TIMEOUT_MS);
@ -220,7 +206,6 @@ public class MediaSessionKeyEventTest {
@Test
public void rewindKeyEvent_mediaNotificationControllerConnected_callFromNotificationController()
throws Exception {
Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19.
MediaController controller = connectMediaNotificationController();
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_REWIND, false);
@ -246,8 +231,6 @@ public class MediaSessionKeyEventTest {
public void
rewindKeyEvent_mediaNotificationControllerNotConnected_callFromLegacyFallbackController()
throws Exception {
Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19.
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_REWIND, false);
player.awaitMethodCalled(MockPlayer.METHOD_SEEK_BACK, TIMEOUT_MS);
@ -261,7 +244,6 @@ public class MediaSessionKeyEventTest {
@Test
public void stopKeyEvent() throws Exception {
Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19.
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_STOP, false);
player.awaitMethodCalled(MockPlayer.METHOD_STOP, TIMEOUT_MS);
@ -320,7 +302,6 @@ public class MediaSessionKeyEventTest {
@Test
public void playPauseKeyEvent_playing_pause() throws Exception {
Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19.
handler.postAndSync(
() -> {
player.playWhenReady = true;
@ -334,7 +315,6 @@ public class MediaSessionKeyEventTest {
@Test
public void playPauseKeyEvent_doubleTapOnPlayPause_seekNext() throws Exception {
Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19.
handler.postAndSync(
() -> {
player.playWhenReady = true;
@ -348,7 +328,6 @@ public class MediaSessionKeyEventTest {
@Test
public void playPauseKeyEvent_doubleTapOnHeadsetHook_seekNext() throws Exception {
Assume.assumeTrue(Util.SDK_INT >= 21); // TODO: b/199064299 - Lower minSdk to 19.
handler.postAndSync(
() -> {
player.playWhenReady = true;
@ -390,10 +369,8 @@ public class MediaSessionKeyEventTest {
return SUPPORT_APP_PACKAGE_NAME;
}
// Legacy controllers
if (Util.SDK_INT < 21 || Util.SDK_INT >= 28) {
if (Util.SDK_INT >= 28) {
// Above API 28: package of the app using AudioManager.
// Below 21: package of the owner of the session. Note: This is specific to this test setup
// where `ApplicationProvider.getContext().packageName == SUPPORT_APP_PACKAGE_NAME`.
return SUPPORT_APP_PACKAGE_NAME;
} else if (Util.SDK_INT >= 24) {
// API 24 - 27: KeyEvent from system service has the package name "android".

View File

@ -1014,8 +1014,7 @@ public class MediaSessionTest {
* <p>Calling this method should only be required to test legacy behaviour.
*/
private static String getControllerCallerPackageName(ControllerInfo controllerInfo) {
return (Util.SDK_INT < 21
|| Util.SDK_INT > 23
return (Util.SDK_INT > 23
|| controllerInfo.getControllerVersion() != ControllerInfo.LEGACY_CONTROLLER_VERSION)
? ApplicationProvider.getApplicationContext().getPackageName()
: MediaSessionManager.RemoteUserInfo.LEGACY_CONTROLLER;

View File

@ -20,7 +20,6 @@ import static androidx.media3.test.session.common.CommonConstants.MOCK_MEDIA3_SE
import static androidx.media3.test.session.common.CommonConstants.SUPPORT_APP_PACKAGE_NAME;
import static androidx.media3.test.session.common.TestUtils.TIMEOUT_MS;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assume.assumeTrue;
import android.content.ComponentName;
import android.content.Context;
@ -28,7 +27,6 @@ import android.os.Bundle;
import android.os.Process;
import android.support.v4.media.session.MediaSessionCompat;
import androidx.media3.common.MediaLibraryInfo;
import androidx.media3.common.util.Util;
import androidx.media3.test.session.common.HandlerThreadTestRule;
import androidx.media3.test.session.common.MainLooperTestRule;
import androidx.media3.test.session.common.TestUtils;
@ -117,8 +115,6 @@ public class SessionTokenTest {
@Test
public void createSessionToken_withPlatformTokenFromMedia1Session_returnsTokenForLegacySession()
throws Exception {
assumeTrue(Util.SDK_INT >= 21);
MediaSessionCompat sessionCompat =
sessionTestRule.ensureReleaseAfterTest(
new MediaSessionCompat(context, "createSessionToken_withLegacyToken"));

View File

@ -29,7 +29,6 @@ import android.system.ErrnoException;
import android.system.OsConstants;
import androidx.annotation.Nullable;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
@ -148,8 +147,7 @@ public final class AssetContentProvider extends ContentProvider
}
private static boolean isBrokenPipe(IOException e) {
return Util.SDK_INT >= 21
&& e.getCause() instanceof ErrnoException
return e.getCause() instanceof ErrnoException
&& ((ErrnoException) e.getCause()).errno == OsConstants.EPIPE;
}
}

View File

@ -331,7 +331,6 @@ public class CapturingRenderersFactory implements RenderersFactory, Dumper.Dumpa
dequeuedOutputBuffers.delete(index);
}
@RequiresApi(21)
@Override
public void releaseOutputBuffer(int index, long renderTimeStampNs) {
MediaCodec.BufferInfo bufferInfo = checkNotNull(dequeuedOutputBuffers.get(index));

View File

@ -204,9 +204,6 @@ public final class DecodeOneFrameUtil {
*/
@Nullable
private static String getSupportedDecoderName(MediaFormat format) {
if (Util.SDK_INT < 21) {
throw new UnsupportedOperationException("Unable to detect decoder support under API 21.");
}
// TODO(b/266923205): De-duplicate logic from EncoderUtil.java#findCodecForFormat().
MediaCodecList mediaCodecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
// Format must not include KEY_FRAME_RATE on API21.

View File

@ -24,7 +24,6 @@ import android.content.Context;
import android.media.Image;
import android.media.MediaCodec;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.media3.common.util.UnstableApi;
import java.io.IOException;
import java.nio.ByteBuffer;
@ -43,7 +42,6 @@ import java.nio.ByteBuffer;
* channel (Y') because the {@linkplain MediaCodec decoder} decodes to luma.
*/
@UnstableApi
@RequiresApi(21)
public final class SsimHelper {
/** The default comparison interval. */

View File

@ -32,7 +32,6 @@ import android.media.MediaFormat;
import android.net.Uri;
import android.os.Handler;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.media3.common.Format;
import androidx.media3.common.MimeTypes;
import androidx.media3.common.util.ConditionVariable;
@ -55,7 +54,6 @@ import java.util.List;
* ImageReader} for use in CPU test utility functions.
*/
@UnstableApi
@RequiresApi(21)
public final class VideoDecodingWrapper implements AutoCloseable {
private static final String TAG = "VideoDecodingWrapper";

View File

@ -25,7 +25,6 @@ import android.media.MediaFormat;
import androidx.media3.common.C;
import androidx.media3.common.MimeTypes;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;
import androidx.media3.exoplayer.mediacodec.MediaCodecUtil;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@ -77,10 +76,6 @@ public final class ShadowMediaCodecConfig extends ExternalResource {
@Override
protected void before() throws Throwable {
if (Util.SDK_INT == 19) {
// Codec config not supported with Robolectric on API == 19. Skip rule set up step.
return;
}
configureCodecs(supportedMimeTypes);
}
@ -88,10 +83,6 @@ public final class ShadowMediaCodecConfig extends ExternalResource {
protected void after() {
supportedMimeTypes.clear();
MediaCodecUtil.clearDecoderInfoCache();
if (Util.SDK_INT == 19) {
// Codec config not supported with Robolectric on API == 19. Skip rule tear down step.
return;
}
ShadowMediaCodecList.reset();
ShadowMediaCodec.clearCodecs();
}

View File

@ -23,9 +23,7 @@ import android.view.accessibility.CaptioningManager;
import android.view.accessibility.CaptioningManager.CaptionStyle;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@ -116,17 +114,13 @@ public final class CaptionStyleCompat {
*/
public static CaptionStyleCompat createFromCaptionStyle(
CaptioningManager.CaptionStyle captionStyle) {
if (Util.SDK_INT >= 21) {
return createFromCaptionStyleV21(captionStyle);
} else {
return new CaptionStyleCompat(
captionStyle.foregroundColor,
captionStyle.backgroundColor,
Color.TRANSPARENT,
captionStyle.edgeType,
captionStyle.edgeColor,
captionStyle.getTypeface());
}
return new CaptionStyleCompat(
captionStyle.hasForegroundColor() ? captionStyle.foregroundColor : DEFAULT.foregroundColor,
captionStyle.hasBackgroundColor() ? captionStyle.backgroundColor : DEFAULT.backgroundColor,
captionStyle.hasWindowColor() ? captionStyle.windowColor : DEFAULT.windowColor,
captionStyle.hasEdgeType() ? captionStyle.edgeType : DEFAULT.edgeType,
captionStyle.hasEdgeColor() ? captionStyle.edgeColor : DEFAULT.edgeColor,
captionStyle.getTypeface());
}
/**
@ -151,17 +145,4 @@ public final class CaptionStyleCompat {
this.edgeColor = edgeColor;
this.typeface = typeface;
}
@RequiresApi(21)
@SuppressWarnings("ResourceType")
private static CaptionStyleCompat createFromCaptionStyleV21(
CaptioningManager.CaptionStyle captionStyle) {
return new CaptionStyleCompat(
captionStyle.hasForegroundColor() ? captionStyle.foregroundColor : DEFAULT.foregroundColor,
captionStyle.hasBackgroundColor() ? captionStyle.backgroundColor : DEFAULT.backgroundColor,
captionStyle.hasWindowColor() ? captionStyle.windowColor : DEFAULT.windowColor,
captionStyle.hasEdgeType() ? captionStyle.edgeType : DEFAULT.edgeType,
captionStyle.hasEdgeColor() ? captionStyle.edgeColor : DEFAULT.edgeColor,
captionStyle.getTypeface());
}
}

View File

@ -759,13 +759,8 @@ public class DefaultTimeBar extends View implements TimeBar {
if (duration <= 0) {
return;
}
if (Util.SDK_INT >= 21) {
info.addAction(AccessibilityAction.ACTION_SCROLL_FORWARD);
info.addAction(AccessibilityAction.ACTION_SCROLL_BACKWARD);
} else {
info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
}
info.addAction(AccessibilityAction.ACTION_SCROLL_FORWARD);
info.addAction(AccessibilityAction.ACTION_SCROLL_BACKWARD);
}
@Override

View File

@ -115,8 +115,7 @@ public class DefaultTrackNameProvider implements TrackNameProvider {
if (TextUtils.isEmpty(language) || C.LANGUAGE_UNDETERMINED.equals(language)) {
return "";
}
Locale languageLocale =
Util.SDK_INT >= 21 ? Locale.forLanguageTag(language) : new Locale(language);
Locale languageLocale = Locale.forLanguageTag(language);
Locale displayLocale = Util.getDefaultDisplayLocale();
String languageName = languageLocale.getDisplayName(displayLocale);
if (TextUtils.isEmpty(languageName)) {

View File

@ -47,9 +47,7 @@ import android.view.accessibility.AccessibilityEvent;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.DoNotInline;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.media3.common.C;
import androidx.media3.common.MediaLibraryInfo;
import androidx.media3.common.Player;
@ -857,19 +855,15 @@ public class LegacyPlayerControlView extends FrameLayout {
boolean requestPlayPauseAccessibilityFocus = false;
boolean shouldShowPlayButton = Util.shouldShowPlayButton(player, showPlayButtonIfSuppressed);
if (playButton != null) {
requestPlayPauseFocus |= !shouldShowPlayButton && playButton.isFocused();
requestPlayPauseAccessibilityFocus |=
Util.SDK_INT < 21
? requestPlayPauseFocus
: (!shouldShowPlayButton && Api21.isAccessibilityFocused(playButton));
requestPlayPauseFocus = !shouldShowPlayButton && playButton.isFocused();
requestPlayPauseAccessibilityFocus =
(!shouldShowPlayButton && playButton.isAccessibilityFocused());
playButton.setVisibility(shouldShowPlayButton ? VISIBLE : GONE);
}
if (pauseButton != null) {
requestPlayPauseFocus |= shouldShowPlayButton && pauseButton.isFocused();
requestPlayPauseAccessibilityFocus |=
Util.SDK_INT < 21
? requestPlayPauseFocus
: (shouldShowPlayButton && Api21.isAccessibilityFocused(pauseButton));
shouldShowPlayButton && pauseButton.isAccessibilityFocused();
pauseButton.setVisibility(shouldShowPlayButton ? GONE : VISIBLE);
}
if (requestPlayPauseFocus) {
@ -1357,12 +1351,4 @@ public class LegacyPlayerControlView extends FrameLayout {
}
}
}
@RequiresApi(21)
private static final class Api21 {
@DoNotInline
public static boolean isAccessibilityFocused(View view) {
return view.isAccessibilityFocused();
}
}
}

View File

@ -55,7 +55,6 @@ import androidx.annotation.DrawableRes;
import androidx.annotation.IntDef;
import androidx.annotation.IntRange;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationBuilderWithBuilderAccessor;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
@ -1013,11 +1012,10 @@ public class PlayerNotificationManager {
* @deprecated Use {@link #setMediaSessionToken(MediaSession.Token)} and pass in {@code
* (MediaSession.Token) compatToken.getToken()}.
*/
// TODO: b/333355694 - Remove the dependency on androidx.media when this method is removed.
@Deprecated
public final void setMediaSessionToken(MediaSessionCompat.Token compatToken) {
if (Util.SDK_INT >= 21) {
setMediaSessionToken((MediaSession.Token) compatToken.getToken());
}
setMediaSessionToken((MediaSession.Token) compatToken.getToken());
}
/**
@ -1028,7 +1026,6 @@ public class PlayerNotificationManager {
*
* @param token The {@link MediaSession.Token}.
*/
@RequiresApi(21)
public final void setMediaSessionToken(MediaSession.Token token) {
if (!Util.areEqual(this.mediaSessionToken, token)) {
mediaSessionToken = token;
@ -1247,8 +1244,7 @@ public class PlayerNotificationManager {
* Creates the notification given the current player state.
*
* @param player The player for which state to build a notification.
* @param builder The builder used to build the last notification, or {@code null}. Re-using the
* builder when possible can prevent notification flicker when {@code Util#SDK_INT} &lt; 21.
* @param builder The builder used to build the last notification, or {@code null}.
* @param ongoing Whether the notification should be ongoing.
* @param largeIcon The large icon to be used.
* @return The {@link NotificationCompat.Builder} on which to call {@link
@ -1291,17 +1287,7 @@ public class PlayerNotificationManager {
}
int[] actionIndicesForCompactView = getActionIndicesForCompactView(actionNames, player);
if (Util.SDK_INT >= 21) {
builder.setStyle(new MediaStyle(mediaSessionToken, actionIndicesForCompactView));
} else {
// TODO: b/333355694 - Remove dependency on androidx.media once this logic is gone.
androidx.media.app.NotificationCompat.MediaStyle mediaStyle =
new androidx.media.app.NotificationCompat.MediaStyle();
mediaStyle.setShowActionsInCompactView(actionIndicesForCompactView);
mediaStyle.setShowCancelButton(!ongoing);
mediaStyle.setCancelButtonIntent(dismissPendingIntent);
builder.setStyle(mediaStyle);
}
builder.setStyle(new MediaStyle(mediaSessionToken, actionIndicesForCompactView));
// Set intent which is sent if the user selects 'clear all'
builder.setDeleteIntent(dismissPendingIntent);
@ -1317,9 +1303,7 @@ public class PlayerNotificationManager {
.setPriority(priority)
.setDefaults(defaults);
// Changing "showWhen" causes notification flicker if SDK_INT < 21.
if (Util.SDK_INT >= 21
&& useChronometer
if (useChronometer
&& player.isCommandAvailable(COMMAND_GET_CURRENT_MEDIA_ITEM)
&& player.isPlaying()
&& !player.isPlayingAd()
@ -1628,7 +1612,6 @@ public class PlayerNotificationManager {
}
}
@RequiresApi(21)
private static final class MediaStyle extends androidx.core.app.NotificationCompat.Style {
private final int[] actionsToShowInCompact;