Merge pull request #52 from androidx/release-1.0

r1.0.0-alpha03
This commit is contained in:
Ian Baker 2022-03-15 15:37:28 +00:00 committed by GitHub
commit 72a4fb082d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 1050 additions and 746 deletions

View File

@ -94,15 +94,13 @@ to prevent build errors.
Cloning the repository and depending on the modules locally is required when Cloning the repository and depending on the modules locally is required when
using some libraries. It's also a suitable approach if you want to make local using some libraries. It's also a suitable approach if you want to make local
changes, or if you want to use the main branch. changes, or if you want to use the `main` branch.
First, clone the repository into a local directory and checkout the desired First, clone the repository into a local directory:
branch:
```sh ```sh
git clone https://github.com/androidx/media.git git clone https://github.com/androidx/media.git
cd media cd media
git checkout main
``` ```
Next, add the following to your project's `settings.gradle` file, replacing Next, add the following to your project's `settings.gradle` file, replacing
@ -129,7 +127,7 @@ implementation project(':media-lib-ui')
Development work happens on the `main` branch. Pull requests should normally be Development work happens on the `main` branch. Pull requests should normally be
made to this branch. made to this branch.
We plan to add a release branch soon. The `release` branch holds the most recent stable release.
#### Using Android Studio #### Using Android Studio

View File

@ -1,6 +1,30 @@
# Release notes # Release notes
### 1.0.0-alpha02 (2022-03-09) ### 1.0.0-alpha03 (2022-03-14)
This release corresponds to the
[ExoPlayer 2.17.1 release](https://github.com/google/ExoPlayer/releases/tag/r2.17.1).
* Audio:
* Fix error checking audio capabilities for Dolby Atmos (E-AC3-JOC) in
HLS.
* Extractors:
* FMP4: Fix issue where emsg sample metadata could be output in the wrong
order for streams containing both v0 and v1 emsg atoms
([#9996](https://github.com/google/ExoPlayer/issues/9996)).
* Text:
* Fix the interaction of `SingleSampleMediaSource.Factory.setTrackId` and
`MediaItem.SubtitleConfiguration.Builder.setId` to prioritise the
`SubtitleConfiguration` field and fall back to the `Factory` value if
it's not set
([#10016](https://github.com/google/ExoPlayer/issues/10016)).
* Ad playback:
* Fix audio underruns between ad periods in live HLS SSAI streams.
### 1.0.0-alpha02 (2022-03-02)
This release corresponds to the
[ExoPlayer 2.17.0 release](https://github.com/google/ExoPlayer/releases/tag/r2.17.0).
* Core Library: * Core Library:
* Add protected method `DefaultRenderersFactory.getCodecAdapterFactory()` * Add protected method `DefaultRenderersFactory.getCodecAdapterFactory()`
@ -18,7 +42,7 @@
from a secure codec to another codec from a secure codec to another codec
([#8696](https://github.com/google/ExoPlayer/issues/8696)). ([#8696](https://github.com/google/ExoPlayer/issues/8696)).
* Add `MediaCodecAdapter.getMetrics()` to allow users obtain metrics data * Add `MediaCodecAdapter.getMetrics()` to allow users obtain metrics data
from `MediaCodec`. from `MediaCodec`
([#9766](https://github.com/google/ExoPlayer/issues/9766)). ([#9766](https://github.com/google/ExoPlayer/issues/9766)).
* Fix Maven dependency resolution * Fix Maven dependency resolution
([#8353](https://github.com/google/ExoPlayer/issues/8353)). ([#8353](https://github.com/google/ExoPlayer/issues/8353)).
@ -63,17 +87,17 @@
under sufficient network bandwidth even if playback is very close to the under sufficient network bandwidth even if playback is very close to the
live edge ([#9784](https://github.com/google/ExoPlayer/issues/9784)). live edge ([#9784](https://github.com/google/ExoPlayer/issues/9784)).
* Video: * Video:
* Fix decoder fallback logic for Dolby Vision * Fix decoder fallback logic for Dolby Vision to use a compatible
to use a compatible H264/H265 decoder if needed. H264/H265 decoder if needed.
* Audio: * Audio:
* Fix decoder fallback logic for Dolby Atmos (E-AC3-JOC) * Fix decoder fallback logic for Dolby Atmos (E-AC3-JOC) to use a
to use a compatible E-AC3 decoder if needed. compatible E-AC3 decoder if needed.
* Change `AudioCapabilities` APIs to require passing explicitly * Change `AudioCapabilities` APIs to require passing explicitly
`AudioCapabilities.DEFAULT_AUDIO_CAPABILITIES` instead of `null`. `AudioCapabilities.DEFAULT_AUDIO_CAPABILITIES` instead of `null`.
* Allow customization of the `AudioTrack` buffer size calculation by * Allow customization of the `AudioTrack` buffer size calculation by
injecting an `AudioTrackBufferSizeProvider` to `DefaultAudioSink`. injecting an `AudioTrackBufferSizeProvider` to `DefaultAudioSink`
([#8891](https://github.com/google/ExoPlayer/issues/8891)). ([#8891](https://github.com/google/ExoPlayer/issues/8891)).
* Retry `AudioTrack` creation if the requested buffer size was > 1MB. * Retry `AudioTrack` creation if the requested buffer size was > 1MB
([#9712](https://github.com/google/ExoPlayer/issues/9712)). ([#9712](https://github.com/google/ExoPlayer/issues/9712)).
* Extractors: * Extractors:
* WAV: Add support for RF64 streams * WAV: Add support for RF64 streams
@ -120,7 +144,8 @@
* Support the `forced-subtitle` track role * Support the `forced-subtitle` track role
([#9727](https://github.com/google/ExoPlayer/issues/9727)). ([#9727](https://github.com/google/ExoPlayer/issues/9727)).
* Stop interpreting the `main` track role as `C.SELECTION_FLAG_DEFAULT`. * Stop interpreting the `main` track role as `C.SELECTION_FLAG_DEFAULT`.
* Fix base URL exclusion logic for manifests that do not declare the DVB namespace ([#9856](https://github.com/google/ExoPlayer/issues/9856)). * Fix base URL exclusion logic for manifests that do not declare the DVB
namespace ([#9856](https://github.com/google/ExoPlayer/issues/9856)).
* Support relative `MPD.Location` URLs * Support relative `MPD.Location` URLs
([#9939](https://github.com/google/ExoPlayer/issues/9939)). ([#9939](https://github.com/google/ExoPlayer/issues/9939)).
* HLS: * HLS:
@ -133,8 +158,6 @@
`HlsMediaSource.Factory.setAllowChunklessPreparation(false)`. `HlsMediaSource.Factory.setAllowChunklessPreparation(false)`.
* Support key-frame accurate seeking in HLS * Support key-frame accurate seeking in HLS
([#2882](https://github.com/google/ExoPlayer/issues/2882)). ([#2882](https://github.com/google/ExoPlayer/issues/2882)).
* Correctly populate `Format.label` for audio only HLS streams
([#9608](https://github.com/google/ExoPlayer/issues/9608)).
* RTSP: * RTSP:
* Provide a client API to override the `SocketFactory` used for any server * Provide a client API to override the `SocketFactory` used for any server
connection ([#9606](https://github.com/google/ExoPlayer/pull/9606)). connection ([#9606](https://github.com/google/ExoPlayer/pull/9606)).
@ -154,12 +177,10 @@
* Fix potential NPE in `Transformer.getProgress` when releasing the muxer * Fix potential NPE in `Transformer.getProgress` when releasing the muxer
throws. throws.
* Add a demo app for applying transformations. * Add a demo app for applying transformations.
* The transformer module is no longer included by depending on
`com.google.android.exoplayer:exoplayer`. To continue using transformer,
add an additional dependency on
`com.google.android.exoplayer:exoplayer-transformer`.
* MediaSession extension: * MediaSession extension:
* By default, `MediaSessionConnector` now clears the playlist on stop. Apps that want the playlist to be retained can call `setClearMediaItemsOnStop(false)` on the connector. * By default, `MediaSessionConnector` now clears the playlist on stop.
Apps that want the playlist to be retained can call
`setClearMediaItemsOnStop(false)` on the connector.
* Cast extension: * Cast extension:
* Fix bug that prevented `CastPlayer` from calling `onIsPlayingChanged` * Fix bug that prevented `CastPlayer` from calling `onIsPlayingChanged`
correctly ([#9792](https://github.com/google/ExoPlayer/issues/9792)). correctly ([#9792](https://github.com/google/ExoPlayer/issues/9792)).
@ -178,38 +199,38 @@
([#9528](https://github.com/google/ExoPlayer/issues/9528)). ([#9528](https://github.com/google/ExoPlayer/issues/9528)).
* Remove deprecated symbols: * Remove deprecated symbols:
* Remove `Player.EventLister`. Use `Player.Listener` instead. * Remove `Player.EventLister`. Use `Player.Listener` instead.
* Remove `MediaSourceFactory#setDrmSessionManager`, * Remove `MediaSourceFactory.setDrmSessionManager`,
`MediaSourceFactory#setDrmHttpDataSourceFactory`, and `MediaSourceFactory.setDrmHttpDataSourceFactory`, and
`MediaSourceFactory#setDrmUserAgent`. Use `MediaSourceFactory.setDrmUserAgent`. Use
`MediaSourceFactory#setDrmSessionManagerProvider` instead. `MediaSourceFactory.setDrmSessionManagerProvider` instead.
* Remove `MediaSourceFactory#setStreamKeys`. Use * Remove `MediaSourceFactory.setStreamKeys`. Use
`MediaItem.Builder#setStreamKeys` instead. `MediaItem.Builder.setStreamKeys` instead.
* Remove `MediaSourceFactory#createMediaSource(Uri)`. Use * Remove `MediaSourceFactory.createMediaSource(Uri)`. Use
`MediaSourceFactory#createMediaSource(MediaItem)` instead. `MediaSourceFactory.createMediaSource(MediaItem)` instead.
* Remove `setTag` from `DashMediaSource`, `HlsMediaSource` and * Remove `setTag` from `DashMediaSource`, `HlsMediaSource` and
`SsMediaSource`. Use `MediaItem.Builder#setTag` instead. `SsMediaSource`. Use `MediaItem.Builder.setTag` instead.
* Remove `DashMediaSource#setLivePresentationDelayMs(long, boolean)`. Use * Remove `DashMediaSource.setLivePresentationDelayMs(long, boolean)`. Use
`MediaItem.Builder#setLiveConfiguration` and `MediaItem.Builder.setLiveConfiguration` and
`MediaItem.LiveConfiguration.Builder#setTargetOffsetMs` to override the `MediaItem.LiveConfiguration.Builder.setTargetOffsetMs` to override the
manifest, or `DashMediaSource#setFallbackTargetLiveOffsetMs` to provide manifest, or `DashMediaSource.setFallbackTargetLiveOffsetMs` to provide
a fallback value. a fallback value.
* Remove `(Simple)ExoPlayer.setThrowsWhenUsingWrongThread`. Opting out of * Remove `(Simple)ExoPlayer.setThrowsWhenUsingWrongThread`. Opting out of
the thread enforcement is no longer possible. the thread enforcement is no longer possible.
* Remove `ActionFile` and `ActionFileUpgradeUtil`. Use ExoPlayer 2.16.1 or * Remove `ActionFile` and `ActionFileUpgradeUtil`. Use ExoPlayer 2.16.1 or
before to use `ActionFileUpgradeUtil` to merge legacy action files into before to use `ActionFileUpgradeUtil` to merge legacy action files into
`DefaultDownloadIndex`. `DefaultDownloadIndex`.
* Remove `ProgressiveMediaSource#setExtractorsFactory`. Use * Remove `ProgressiveMediaSource.setExtractorsFactory`. Use
`ProgressiveMediaSource.Factory(DataSource.Factory, ExtractorsFactory)` `ProgressiveMediaSource.Factory(DataSource.Factory, ExtractorsFactory)`
constructor instead. constructor instead.
* Remove `ProgressiveMediaSource.Factory#setTag` and, and * Remove `ProgressiveMediaSource.Factory.setTag` and
`ProgressiveMediaSource.Factory#setCustomCacheKey`. Use `ProgressiveMediaSource.Factory.setCustomCacheKey`. Use
`MediaItem.Builder#setTag` and `MediaItem.Builder#setCustomCacheKey` `MediaItem.Builder.setTag` and `MediaItem.Builder.setCustomCacheKey`
instead. instead.
* Remove `DefaultRenderersFactory(Context, @ExtensionRendererMode int)` * Remove `DefaultRenderersFactory(Context, @ExtensionRendererMode int)`
and `DefaultRenderersFactory(Context, @ExtensionRendererMode int, long)` and `DefaultRenderersFactory(Context, @ExtensionRendererMode int, long)`
constructors. Use the `DefaultRenderersFactory(Context)` constructor, constructors. Use the `DefaultRenderersFactory(Context)` constructor,
`DefaultRenderersFactory#setExtensionRendererMode`, and `DefaultRenderersFactory.setExtensionRendererMode`, and
`DefaultRenderersFactory#setAllowedVideoJoiningTimeMs` instead. `DefaultRenderersFactory.setAllowedVideoJoiningTimeMs` instead.
* Remove all public `CronetDataSource` constructors. Use * Remove all public `CronetDataSource` constructors. Use
`CronetDataSource.Factory` instead. `CronetDataSource.Factory` instead.
* Change the following `IntDefs` to `@Target(TYPE_USE)` only. This may break * Change the following `IntDefs` to `@Target(TYPE_USE)` only. This may break

View File

@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
project.ext { project.ext {
releaseVersion = '1.0.0-alpha02' releaseVersion = '1.0.0-alpha03'
releaseVersionCode = 1_000_000_0_02 releaseVersionCode = 1_000_000_0_03
minSdkVersion = 16 minSdkVersion = 16
appTargetSdkVersion = 29 appTargetSdkVersion = 29
// Upgrading this requires [Internal ref: b/193254928] to be fixed, or some // Upgrading this requires [Internal ref: b/193254928] to be fixed, or some

View File

@ -30,6 +30,7 @@ import android.widget.ListView
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.media3.common.MediaItem import androidx.media3.common.MediaItem
import androidx.media3.common.Player
import androidx.media3.session.MediaBrowser import androidx.media3.session.MediaBrowser
import androidx.media3.session.SessionToken import androidx.media3.session.SessionToken
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
@ -179,6 +180,9 @@ class PlayableFolderActivity : AppCompatActivity() {
returnConvertView.findViewById<TextView>(R.id.add_button).setOnClickListener { returnConvertView.findViewById<TextView>(R.id.add_button).setOnClickListener {
val browser = this@PlayableFolderActivity.browser ?: return@setOnClickListener val browser = this@PlayableFolderActivity.browser ?: return@setOnClickListener
browser.addMediaItem(mediaItem) browser.addMediaItem(mediaItem)
if (browser.playbackState == Player.STATE_IDLE) {
browser.prepare()
}
Snackbar.make( Snackbar.make(
findViewById<LinearLayout>(R.id.linear_layout), findViewById<LinearLayout>(R.id.linear_layout),
getString(R.string.added_media_item_format, mediaItem.mediaMetadata.title), getString(R.string.added_media_item_format, mediaItem.mediaMetadata.title),

View File

@ -96,7 +96,6 @@ class PlaybackService : MediaLibraryService() {
val item = MediaItemTree.getItemFromTitle(mediaTitle) ?: MediaItemTree.getRandomItem() val item = MediaItemTree.getItemFromTitle(mediaTitle) ?: MediaItemTree.getRandomItem()
player.setMediaItem(item) player.setMediaItem(item)
player.prepare()
} }
override fun onSetMediaUri( override fun onSetMediaUri(

Binary file not shown.

View File

@ -1,6 +1,5 @@
#Wed Mar 04 12:41:50 GMT 2020
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https://services.gradle.org/distributions/gradle-7.3.3-all.zip

302
gradlew vendored
View File

@ -1,79 +1,129 @@
#!/usr/bin/env bash #!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
############################################################################## ##############################################################################
## #
## Gradle start up script for UN*X # Gradle start up script for POSIX generated by Gradle.
## #
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
############################################################################## ##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. # Attempt to set APP_HOME
DEFAULT_JVM_OPTS=""
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle" APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"` APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum" MAX_FD=maximum
warn ( ) { warn () {
echo "$*" echo "$*"
} } >&2
die ( ) { die () {
echo echo
echo "$*" echo "$*"
echo echo
exit 1 exit 1
} } >&2
# OS specific support (must be 'true' or 'false'). # OS specific support (must be 'true' or 'false').
cygwin=false cygwin=false
msys=false msys=false
darwin=false darwin=false
case "`uname`" in nonstop=false
CYGWIN* ) case "$( uname )" in #(
cygwin=true CYGWIN* ) cygwin=true ;; #(
;; Darwin* ) darwin=true ;; #(
Darwin* ) MSYS* | MINGW* ) msys=true ;; #(
darwin=true NONSTOP* ) nonstop=true ;;
;;
MINGW* )
msys=true
;;
esac esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables # IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java" JAVACMD=$JAVA_HOME/jre/sh/java
else else
JAVACMD="$JAVA_HOME/bin/java" JAVACMD=$JAVA_HOME/bin/java
fi fi
if [ ! -x "$JAVACMD" ] ; then if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@ -82,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi fi
else else
JAVACMD="java" JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the Please set the JAVA_HOME variable in your environment to match the
@ -90,75 +140,95 @@ location of your Java installation."
fi fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
MAX_FD_LIMIT=`ulimit -H -n` case $MAX_FD in #(
if [ $? -eq 0 ] ; then max*)
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD=$( ulimit -H -n ) ||
MAX_FD="$MAX_FD_LIMIT" warn "Could not query maximum file descriptor limit"
fi esac
ulimit -n $MAX_FD case $MAX_FD in #(
if [ $? -ne 0 ] ; then '' | soft) :;; #(
warn "Could not set maximum file descriptor limit: $MAX_FD" *)
fi ulimit -n "$MAX_FD" ||
else warn "Could not set maximum file descriptor limit to $MAX_FD"
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac esac
fi fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules # Collect all arguments for the java command, stacking in reverse order:
function splitJvmOpts() { # * args from the command line
JVM_OPTS=("$@") # * the main class name
} # * -classpath
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS # * -D...appname settings
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" # * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" # For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

179
gradlew.bat vendored
View File

@ -1,90 +1,89 @@
@if "%DEBUG%" == "" @echo off @rem
@rem ########################################################################## @rem Copyright 2015 the original author or authors.
@rem @rem
@rem Gradle startup script for Windows @rem Licensed under the Apache License, Version 2.0 (the "License");
@rem @rem you may not use this file except in compliance with the License.
@rem ########################################################################## @rem You may obtain a copy of the License at
@rem
@rem Set local scope for the variables with windows NT shell @rem https://www.apache.org/licenses/LICENSE-2.0
if "%OS%"=="Windows_NT" setlocal @rem
@rem Unless required by applicable law or agreed to in writing, software
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. @rem distributed under the License is distributed on an "AS IS" BASIS,
set DEFAULT_JVM_OPTS= @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
set DIRNAME=%~dp0 @rem limitations under the License.
if "%DIRNAME%" == "" set DIRNAME=. @rem
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% @if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem Find java.exe @rem
if defined JAVA_HOME goto findJavaFromJavaHome @rem Gradle startup script for Windows
@rem
set JAVA_EXE=java.exe @rem ##########################################################################
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init @rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. set DIRNAME=%~dp0
echo. if "%DIRNAME%" == "" set DIRNAME=.
echo Please set the JAVA_HOME variable in your environment to match the set APP_BASE_NAME=%~n0
echo location of your Java installation. set APP_HOME=%DIRNAME%
goto fail @rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=% @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set JAVA_EXE=%JAVA_HOME%/bin/java.exe set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
if exist "%JAVA_EXE%" goto init @rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% set JAVA_EXE=java.exe
echo. %JAVA_EXE% -version >NUL 2>&1
echo Please set the JAVA_HOME variable in your environment to match the if "%ERRORLEVEL%" == "0" goto execute
echo location of your Java installation.
echo.
goto fail echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
:init echo Please set the JAVA_HOME variable in your environment to match the
@rem Get command-line arguments, handling Windowz variants echo location of your Java installation.
if not "%OS%" == "Windows_NT" goto win9xME_args goto fail
if "%@eval[2+2]" == "4" goto 4NT_args
:findJavaFromJavaHome
:win9xME_args set JAVA_HOME=%JAVA_HOME:"=%
@rem Slurp the command line arguments. set JAVA_EXE=%JAVA_HOME%/bin/java.exe
set CMD_LINE_ARGS=
set _SKIP=2 if exist "%JAVA_EXE%" goto execute
:win9xME_args_slurp echo.
if "x%~1" == "x" goto execute echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
set CMD_LINE_ARGS=%* echo Please set the JAVA_HOME variable in your environment to match the
goto execute echo location of your Java installation.
:4NT_args goto fail
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$ :execute
@rem Setup the command line
:execute
@rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
@rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
:end @rem End local scope for the variables with windows NT shell
@rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
:fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code!
rem the _cmd.exe /c_ return code! if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1
exit /b 1
:mainEnd
:mainEnd if "%OS%"=="Windows_NT" endlocal
if "%OS%"=="Windows_NT" endlocal
:omega
:omega

View File

@ -29,11 +29,11 @@ public final class MediaLibraryInfo {
/** The version of the library expressed as a string, for example "1.2.3" or "1.2.3-beta01". */ /** The version of the library expressed as a string, for example "1.2.3" or "1.2.3-beta01". */
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION_INT) or vice versa. // Intentionally hardcoded. Do not derive from other constants (e.g. VERSION_INT) or vice versa.
public static final String VERSION = "1.0.0-alpha02"; public static final String VERSION = "1.0.0-alpha03";
/** The version of the library expressed as {@code TAG + "/" + VERSION}. */ /** The version of the library expressed as {@code TAG + "/" + VERSION}. */
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa. // Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
public static final String VERSION_SLASHY = "AndroidXMedia3/1.0.0-alpha02"; public static final String VERSION_SLASHY = "AndroidXMedia3/1.0.0-alpha03";
/** /**
* The version of the library expressed as an integer, for example 1002003300. * The version of the library expressed as an integer, for example 1002003300.
@ -47,7 +47,7 @@ public final class MediaLibraryInfo {
* (123-045-006-3-00). * (123-045-006-3-00).
*/ */
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa. // Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
public static final int VERSION_INT = 1_000_000_0_02; public static final int VERSION_INT = 1_000_000_0_03;
/** Whether the library was compiled with {@link Assertions} checks enabled. */ /** Whether the library was compiled with {@link Assertions} checks enabled. */
public static final boolean ASSERTIONS_ENABLED = true; public static final boolean ASSERTIONS_ENABLED = true;

View File

@ -56,6 +56,7 @@ import androidx.media3.exoplayer.DefaultMediaClock.PlaybackParametersListener;
import androidx.media3.exoplayer.analytics.AnalyticsCollector; import androidx.media3.exoplayer.analytics.AnalyticsCollector;
import androidx.media3.exoplayer.analytics.PlayerId; import androidx.media3.exoplayer.analytics.PlayerId;
import androidx.media3.exoplayer.drm.DrmSession; import androidx.media3.exoplayer.drm.DrmSession;
import androidx.media3.exoplayer.metadata.MetadataRenderer;
import androidx.media3.exoplayer.source.BehindLiveWindowException; import androidx.media3.exoplayer.source.BehindLiveWindowException;
import androidx.media3.exoplayer.source.MediaPeriod; import androidx.media3.exoplayer.source.MediaPeriod;
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId; import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
@ -2228,6 +2229,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
return reading.info.isFollowedByTransitionToSameStream return reading.info.isFollowedByTransitionToSameStream
&& nextPeriod.prepared && nextPeriod.prepared
&& (renderer instanceof TextRenderer // [internal: b/181312195] && (renderer instanceof TextRenderer // [internal: b/181312195]
|| renderer instanceof MetadataRenderer
|| renderer.getReadingPositionUs() >= nextPeriod.getStartPositionRendererTime()); || renderer.getReadingPositionUs() >= nextPeriod.getStartPositionRendererTime());
} }

View File

@ -1739,8 +1739,11 @@ public final class DefaultAudioSink implements AudioSink {
// the channel count for this encoding, but before then there is no way to query it so we // the channel count for this encoding, but before then there is no way to query it so we
// assume 6 channel audio is supported. // assume 6 channel audio is supported.
if (Util.SDK_INT >= 29) { if (Util.SDK_INT >= 29) {
// Default to 48 kHz if the format doesn't have a sample rate (for example, for chunkless
// HLS preparation). See [Internal: b/222127949].
int sampleRate = format.sampleRate != Format.NO_VALUE ? format.sampleRate : 48000;
channelCount = channelCount =
getMaxSupportedChannelCountForPassthroughV29(C.ENCODING_E_AC3_JOC, format.sampleRate); getMaxSupportedChannelCountForPassthroughV29(C.ENCODING_E_AC3_JOC, sampleRate);
if (channelCount == 0) { if (channelCount == 0) {
Log.w(TAG, "E-AC3 JOC encoding supported but no channel count supported"); Log.w(TAG, "E-AC3 JOC encoding supported but no channel count supported");
return null; return null;

View File

@ -24,6 +24,8 @@ import static java.lang.annotation.ElementType.TYPE_USE;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
/** Thrown when the requested DRM scheme is not supported. */ /** Thrown when the requested DRM scheme is not supported. */
@ -35,8 +37,9 @@ public final class UnsupportedDrmException extends Exception {
* #REASON_INSTANTIATION_ERROR}. * #REASON_INSTANTIATION_ERROR}.
*/ */
// @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility // @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility
// with Kotlin usages from before TYPE_USE was added. @Retention(RetentionPolicy.SOURCE) // with Kotlin usages from before TYPE_USE was added.
@Documented @Documented
@Retention(RetentionPolicy.SOURCE)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE})
@IntDef({REASON_UNSUPPORTED_SCHEME, REASON_INSTANTIATION_ERROR}) @IntDef({REASON_UNSUPPORTED_SCHEME, REASON_INSTANTIATION_ERROR})
public @interface Reason {} public @interface Reason {}

View File

@ -74,11 +74,12 @@ public final class SingleSampleMediaSource extends BaseMediaSource {
} }
/** /**
* Sets an optional track id to be used. * @deprecated Use {@link MediaItem.SubtitleConfiguration.Builder#setId(String)} instead (on the
* * {@link MediaItem.SubtitleConfiguration} passed to {@link
* @param trackId An optional track id. * #createMediaSource(MediaItem.SubtitleConfiguration, long)}). {@code trackId} will only be
* @return This factory, for convenience. * used if {@link MediaItem.SubtitleConfiguration#id} is {@code null}.
*/ */
@Deprecated
public Factory setTrackId(@Nullable String trackId) { public Factory setTrackId(@Nullable String trackId) {
this.trackId = trackId; this.trackId = trackId;
return this; return this;
@ -157,29 +158,28 @@ public final class SingleSampleMediaSource extends BaseMediaSource {
this.durationUs = durationUs; this.durationUs = durationUs;
this.loadErrorHandlingPolicy = loadErrorHandlingPolicy; this.loadErrorHandlingPolicy = loadErrorHandlingPolicy;
this.treatLoadErrorsAsEndOfStream = treatLoadErrorsAsEndOfStream; this.treatLoadErrorsAsEndOfStream = treatLoadErrorsAsEndOfStream;
mediaItem = this.mediaItem =
new MediaItem.Builder() new MediaItem.Builder()
.setUri(Uri.EMPTY) .setUri(Uri.EMPTY)
.setMediaId(subtitleConfiguration.uri.toString()) .setMediaId(subtitleConfiguration.uri.toString())
.setSubtitleConfigurations(ImmutableList.of(subtitleConfiguration)) .setSubtitleConfigurations(ImmutableList.of(subtitleConfiguration))
.setTag(tag) .setTag(tag)
.build(); .build();
format = this.format =
new Format.Builder() new Format.Builder()
.setId(trackId)
.setSampleMimeType(firstNonNull(subtitleConfiguration.mimeType, MimeTypes.TEXT_UNKNOWN)) .setSampleMimeType(firstNonNull(subtitleConfiguration.mimeType, MimeTypes.TEXT_UNKNOWN))
.setLanguage(subtitleConfiguration.language) .setLanguage(subtitleConfiguration.language)
.setSelectionFlags(subtitleConfiguration.selectionFlags) .setSelectionFlags(subtitleConfiguration.selectionFlags)
.setRoleFlags(subtitleConfiguration.roleFlags) .setRoleFlags(subtitleConfiguration.roleFlags)
.setLabel(subtitleConfiguration.label) .setLabel(subtitleConfiguration.label)
.setId(subtitleConfiguration.id) .setId(subtitleConfiguration.id != null ? subtitleConfiguration.id : trackId)
.build(); .build();
dataSpec = this.dataSpec =
new DataSpec.Builder() new DataSpec.Builder()
.setUri(subtitleConfiguration.uri) .setUri(subtitleConfiguration.uri)
.setFlags(DataSpec.FLAG_ALLOW_GZIP) .setFlags(DataSpec.FLAG_ALLOW_GZIP)
.build(); .build();
timeline = this.timeline =
new SinglePeriodTimeline( new SinglePeriodTimeline(
durationUs, durationUs,
/* isSeekable= */ true, /* isSeekable= */ true,

View File

@ -105,11 +105,7 @@ import java.util.Map;
import org.checkerframework.checker.nullness.qual.EnsuresNonNull; import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** /** MediaSource for IMA server side inserted ad streams. */
* MediaSource for IMA server side inserted ad streams.
*
* <p>TODO(bachinger) add code snippet from PlayerActivity
*/
@UnstableApi @UnstableApi
public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSource<Void> { public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSource<Void> {
@ -119,8 +115,6 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
* *
* <p>Apps can use the {@link ImaServerSideAdInsertionMediaSource.Factory} to customized the * <p>Apps can use the {@link ImaServerSideAdInsertionMediaSource.Factory} to customized the
* {@link DefaultMediaSourceFactory} that is used to build a player: * {@link DefaultMediaSourceFactory} that is used to build a player:
*
* <p>TODO(bachinger) add code snippet from PlayerActivity
*/ */
public static final class Factory implements MediaSource.Factory { public static final class Factory implements MediaSource.Factory {
@ -461,6 +455,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
@Nullable private IOException loadError; @Nullable private IOException loadError;
private @MonotonicNonNull Timeline contentTimeline; private @MonotonicNonNull Timeline contentTimeline;
private AdPlaybackState adPlaybackState; private AdPlaybackState adPlaybackState;
private int firstSeenAdIndexInAdGroup;
private ImaServerSideAdInsertionMediaSource( private ImaServerSideAdInsertionMediaSource(
MediaItem mediaItem, MediaItem mediaItem,
@ -698,18 +693,21 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
return adPlaybackState; return adPlaybackState;
} }
private static AdPlaybackState addLiveAdBreak( private AdPlaybackState addLiveAdBreak(
Ad ad, long currentPeriodPositionUs, AdPlaybackState adPlaybackState) { Ad ad, long currentPeriodPositionUs, AdPlaybackState adPlaybackState) {
AdPodInfo adPodInfo = ad.getAdPodInfo(); AdPodInfo adPodInfo = ad.getAdPodInfo();
long adDurationUs = secToUs(ad.getDuration()); long adDurationUs = secToUs(ad.getDuration());
int adIndexInAdGroup = adPodInfo.getAdPosition() - 1; int adIndexInAdGroup = adPodInfo.getAdPosition() - 1;
// TODO(b/208398934) Support seeking backwards. // TODO(b/208398934) Support seeking backwards.
if (adIndexInAdGroup == 0 || adPlaybackState.adGroupCount == 1) { if (adIndexInAdGroup == 0 || adPlaybackState.adGroupCount == 1) {
firstSeenAdIndexInAdGroup = adIndexInAdGroup;
// Adjust count and ad index in case we joined the live stream within an ad group.
int adCount = adPodInfo.getTotalAds() - firstSeenAdIndexInAdGroup;
adIndexInAdGroup -= firstSeenAdIndexInAdGroup;
// First ad of group. Create a new group with all ads. // First ad of group. Create a new group with all ads.
long[] adDurationsUs = long[] adDurationsUs =
updateAdDurationAndPropagate( updateAdDurationAndPropagate(
new long[adPodInfo.getTotalAds()], new long[adCount],
adIndexInAdGroup, adIndexInAdGroup,
adDurationUs, adDurationUs,
secToUs(adPodInfo.getMaxDuration())); secToUs(adPodInfo.getMaxDuration()));
@ -721,6 +719,11 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
/* adDurationsUs...= */ adDurationsUs); /* adDurationsUs...= */ adDurationsUs);
} else { } else {
int adGroupIndex = adPlaybackState.adGroupCount - 2; int adGroupIndex = adPlaybackState.adGroupCount - 2;
adIndexInAdGroup -= firstSeenAdIndexInAdGroup;
if (adPodInfo.getTotalAds() == adPodInfo.getAdPosition()) {
// Reset the ad index whe we are at the last ad in the group.
firstSeenAdIndexInAdGroup = 0;
}
adPlaybackState = adPlaybackState =
updateAdDurationInAdGroup(adGroupIndex, adIndexInAdGroup, adDurationUs, adPlaybackState); updateAdDurationInAdGroup(adGroupIndex, adIndexInAdGroup, adDurationUs, adPlaybackState);
AdPlaybackState.AdGroup adGroup = adPlaybackState.getAdGroup(adGroupIndex); AdPlaybackState.AdGroup adGroup = adPlaybackState.getAdGroup(adGroupIndex);
@ -857,7 +860,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
long positionInWindowUs = long positionInWindowUs =
timeline.getPeriod(player.getCurrentPeriodIndex(), new Timeline.Period()) timeline.getPeriod(player.getCurrentPeriodIndex(), new Timeline.Period())
.positionInWindowUs; .positionInWindowUs;
long currentPeriodPosition = msToUs(player.getCurrentPosition()) - positionInWindowUs; long currentPeriodPosition = msToUs(player.getContentPosition()) - positionInWindowUs;
newAdPlaybackState = newAdPlaybackState =
addLiveAdBreak( addLiveAdBreak(
event.getAd(), event.getAd(),

View File

@ -666,14 +666,23 @@ public class FragmentedMp4Extractor implements Extractor {
emsgTrackOutput.sampleData(encodedEventMessage, sampleSize); emsgTrackOutput.sampleData(encodedEventMessage, sampleSize);
} }
// Output the sample metadata. This is made a little complicated because emsg-v0 atoms // Output the sample metadata.
// have presentation time *delta* while v1 atoms have absolute presentation time.
if (sampleTimeUs == C.TIME_UNSET) { if (sampleTimeUs == C.TIME_UNSET) {
// We need the first sample timestamp in the segment before we can output the metadata. // We're processing a v0 emsg atom, which contains a presentation time delta, and cannot yet
// calculate its absolute sample timestamp. Defer outputting the metadata until we can.
pendingMetadataSampleInfos.addLast( pendingMetadataSampleInfos.addLast(
new MetadataSampleInfo(presentationTimeDeltaUs, sampleSize)); new MetadataSampleInfo(
presentationTimeDeltaUs, /* sampleTimeIsRelative= */ true, sampleSize));
pendingMetadataSampleBytes += sampleSize;
} else if (!pendingMetadataSampleInfos.isEmpty()) {
// We also need to defer outputting metadata if pendingMetadataSampleInfos is non-empty, else
// we will output metadata for samples in the wrong order. See:
// https://github.com/google/ExoPlayer/issues/9996.
pendingMetadataSampleInfos.addLast(
new MetadataSampleInfo(sampleTimeUs, /* sampleTimeIsRelative= */ false, sampleSize));
pendingMetadataSampleBytes += sampleSize; pendingMetadataSampleBytes += sampleSize;
} else { } else {
// We can output the sample metadata immediately.
if (timestampAdjuster != null) { if (timestampAdjuster != null) {
sampleTimeUs = timestampAdjuster.adjustSampleTimestamp(sampleTimeUs); sampleTimeUs = timestampAdjuster.adjustSampleTimestamp(sampleTimeUs);
} }
@ -1459,19 +1468,30 @@ public class FragmentedMp4Extractor implements Extractor {
return true; return true;
} }
/**
* Called immediately after outputting a non-metadata sample, to output any pending metadata
* samples.
*
* @param sampleTimeUs The timestamp of the non-metadata sample that was just output.
*/
private void outputPendingMetadataSamples(long sampleTimeUs) { private void outputPendingMetadataSamples(long sampleTimeUs) {
while (!pendingMetadataSampleInfos.isEmpty()) { while (!pendingMetadataSampleInfos.isEmpty()) {
MetadataSampleInfo sampleInfo = pendingMetadataSampleInfos.removeFirst(); MetadataSampleInfo metadataSampleInfo = pendingMetadataSampleInfos.removeFirst();
pendingMetadataSampleBytes -= sampleInfo.size; pendingMetadataSampleBytes -= metadataSampleInfo.size;
long metadataTimeUs = sampleTimeUs + sampleInfo.presentationTimeDeltaUs; long metadataSampleTimeUs = metadataSampleInfo.sampleTimeUs;
if (metadataSampleInfo.sampleTimeIsRelative) {
// The metadata sample timestamp is relative to the timestamp of the non-metadata sample
// that was just output. Make it absolute.
metadataSampleTimeUs += sampleTimeUs;
}
if (timestampAdjuster != null) { if (timestampAdjuster != null) {
metadataTimeUs = timestampAdjuster.adjustSampleTimestamp(metadataTimeUs); metadataSampleTimeUs = timestampAdjuster.adjustSampleTimestamp(metadataSampleTimeUs);
} }
for (TrackOutput emsgTrackOutput : emsgTrackOutputs) { for (TrackOutput emsgTrackOutput : emsgTrackOutputs) {
emsgTrackOutput.sampleMetadata( emsgTrackOutput.sampleMetadata(
metadataTimeUs, metadataSampleTimeUs,
C.BUFFER_FLAG_KEY_FRAME, C.BUFFER_FLAG_KEY_FRAME,
sampleInfo.size, metadataSampleInfo.size,
pendingMetadataSampleBytes, pendingMetadataSampleBytes,
null); null);
} }
@ -1577,11 +1597,13 @@ public class FragmentedMp4Extractor implements Extractor {
/** Holds data corresponding to a metadata sample. */ /** Holds data corresponding to a metadata sample. */
private static final class MetadataSampleInfo { private static final class MetadataSampleInfo {
public final long presentationTimeDeltaUs; public final long sampleTimeUs;
public final boolean sampleTimeIsRelative;
public final int size; public final int size;
public MetadataSampleInfo(long presentationTimeDeltaUs, int size) { public MetadataSampleInfo(long sampleTimeUs, boolean sampleTimeIsRelative, int size) {
this.presentationTimeDeltaUs = presentationTimeDeltaUs; this.sampleTimeUs = sampleTimeUs;
this.sampleTimeIsRelative = sampleTimeIsRelative;
this.size = size; this.size = size;
} }
} }

View File

@ -27,6 +27,7 @@ import android.os.Bundle;
import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat;
import androidx.core.graphics.drawable.IconCompat; import androidx.core.graphics.drawable.IconCompat;
import androidx.media3.common.MediaMetadata; import androidx.media3.common.MediaMetadata;
import androidx.media3.common.Player;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util; import androidx.media3.common.util.Util;
@ -93,20 +94,21 @@ import androidx.media3.common.util.Util;
IconCompat.createWithResource(context, R.drawable.media3_notification_seek_to_previous), IconCompat.createWithResource(context, R.drawable.media3_notification_seek_to_previous),
context.getString(R.string.media3_controls_seek_to_previous_description), context.getString(R.string.media3_controls_seek_to_previous_description),
MediaNotification.ActionFactory.COMMAND_SKIP_TO_PREVIOUS)); MediaNotification.ActionFactory.COMMAND_SKIP_TO_PREVIOUS));
if (mediaController.getPlayWhenReady()) { if (mediaController.getPlaybackState() == Player.STATE_ENDED
// Pause action. || !mediaController.getPlayWhenReady()) {
builder.addAction(
actionFactory.createMediaAction(
IconCompat.createWithResource(context, R.drawable.media3_notification_pause),
context.getString(R.string.media3_controls_pause_description),
MediaNotification.ActionFactory.COMMAND_PAUSE));
} else {
// Play action. // Play action.
builder.addAction( builder.addAction(
actionFactory.createMediaAction( actionFactory.createMediaAction(
IconCompat.createWithResource(context, R.drawable.media3_notification_play), IconCompat.createWithResource(context, R.drawable.media3_notification_play),
context.getString(R.string.media3_controls_play_description), context.getString(R.string.media3_controls_play_description),
MediaNotification.ActionFactory.COMMAND_PLAY)); MediaNotification.ActionFactory.COMMAND_PLAY));
} else {
// Pause action.
builder.addAction(
actionFactory.createMediaAction(
IconCompat.createWithResource(context, R.drawable.media3_notification_pause),
context.getString(R.string.media3_controls_pause_description),
MediaNotification.ActionFactory.COMMAND_PAUSE));
} }
// Skip to next action. // Skip to next action.
builder.addAction( builder.addAction(

View File

@ -198,7 +198,7 @@ import java.util.concurrent.atomic.AtomicReference;
try { try {
int page = options.getInt(EXTRA_PAGE); int page = options.getInt(EXTRA_PAGE);
int pageSize = options.getInt(EXTRA_PAGE_SIZE); int pageSize = options.getInt(EXTRA_PAGE_SIZE);
if (page > 0 && pageSize > 0) { if (page >= 0 && pageSize > 0) {
// Requesting the list of children through pagination. // Requesting the list of children through pagination.
@Nullable @Nullable
LibraryParams params = LibraryParams params =
@ -223,7 +223,7 @@ import java.util.concurrent.atomic.AtomicReference;
parentId, parentId,
/* page= */ 0, /* page= */ 0,
/* pageSize= */ Integer.MAX_VALUE, /* pageSize= */ Integer.MAX_VALUE,
/* extras= */ null); /* params= */ null);
sendLibraryResultWithMediaItemsWhenReady(result, future); sendLibraryResultWithMediaItemsWhenReady(result, future);
}); });
} }

View File

@ -196,7 +196,9 @@ import java.util.concurrent.TimeoutException;
@Override @Override
public void onEvents(Player player, Player.Events events) { public void onEvents(Player player, Player.Events events) {
if (events.containsAny( if (events.containsAny(
Player.EVENT_PLAY_WHEN_READY_CHANGED, Player.EVENT_MEDIA_METADATA_CHANGED)) { Player.EVENT_PLAYBACK_STATE_CHANGED,
Player.EVENT_PLAY_WHEN_READY_CHANGED,
Player.EVENT_MEDIA_METADATA_CHANGED)) {
updateNotification(session); updateNotification(session);
} }
} }

View File

@ -28,6 +28,8 @@ import static androidx.media3.common.Player.COMMAND_SET_REPEAT_MODE;
import static androidx.media3.common.Player.COMMAND_SET_SHUFFLE_MODE; import static androidx.media3.common.Player.COMMAND_SET_SHUFFLE_MODE;
import static androidx.media3.common.Player.COMMAND_SET_SPEED_AND_PITCH; import static androidx.media3.common.Player.COMMAND_SET_SPEED_AND_PITCH;
import static androidx.media3.common.Player.COMMAND_STOP; import static androidx.media3.common.Player.COMMAND_STOP;
import static androidx.media3.common.Player.STATE_ENDED;
import static androidx.media3.common.Player.STATE_IDLE;
import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.common.util.Assertions.checkStateNotNull; import static androidx.media3.common.util.Assertions.checkStateNotNull;
import static androidx.media3.common.util.Util.postOrRun; import static androidx.media3.common.util.Util.postOrRun;
@ -231,7 +233,17 @@ import org.checkerframework.checker.initialization.qual.Initialized;
} else { } else {
dispatchSessionTaskWithPlayerCommand( dispatchSessionTaskWithPlayerCommand(
COMMAND_PLAY_PAUSE, COMMAND_PLAY_PAUSE,
(controller) -> sessionImpl.getPlayerWrapper().play(), (controller) -> {
PlayerWrapper playerWrapper = sessionImpl.getPlayerWrapper();
@Player.State int playbackState = playerWrapper.getPlaybackState();
if (playbackState == STATE_IDLE) {
playerWrapper.prepare();
} else if (playbackState == STATE_ENDED) {
playerWrapper.seekTo(
playerWrapper.getCurrentMediaItemIndex(), /* positionMs= */ C.TIME_UNSET);
}
playerWrapper.play();
},
remoteUserInfo); remoteUserInfo);
} }
} }
@ -285,7 +297,17 @@ import org.checkerframework.checker.initialization.qual.Initialized;
public void onPlay() { public void onPlay() {
dispatchSessionTaskWithPlayerCommand( dispatchSessionTaskWithPlayerCommand(
COMMAND_PLAY_PAUSE, COMMAND_PLAY_PAUSE,
controller -> sessionImpl.getPlayerWrapper().play(), controller -> {
PlayerWrapper playerWrapper = sessionImpl.getPlayerWrapper();
@Player.State int playbackState = playerWrapper.getPlaybackState();
if (playbackState == Player.STATE_IDLE) {
playerWrapper.prepare();
} else if (playbackState == Player.STATE_ENDED) {
playerWrapper.seekTo(
playerWrapper.getCurrentMediaItemIndex(), /* positionMs= */ C.TIME_UNSET);
}
playerWrapper.play();
},
sessionCompat.getCurrentControllerInfo()); sessionCompat.getCurrentControllerInfo());
} }
@ -321,7 +343,15 @@ import org.checkerframework.checker.initialization.qual.Initialized;
if (sessionImpl.onSetMediaUriOnHandler( if (sessionImpl.onSetMediaUriOnHandler(
controller, mediaUri, extras == null ? Bundle.EMPTY : extras) controller, mediaUri, extras == null ? Bundle.EMPTY : extras)
== RESULT_SUCCESS) { == RESULT_SUCCESS) {
sessionImpl.getPlayerWrapper().play(); PlayerWrapper playerWrapper = sessionImpl.getPlayerWrapper();
@Player.State int playbackState = playerWrapper.getPlaybackState();
if (playbackState == Player.STATE_IDLE) {
playerWrapper.prepare();
} else if (playbackState == STATE_ENDED) {
playerWrapper.seekTo(
playerWrapper.getCurrentMediaItemIndex(), /* positionMs= */ C.TIME_UNSET);
}
playerWrapper.play();
} }
}); });
} }

View File

@ -181,7 +181,6 @@ public class MediaSessionAndControllerTest {
MockPlayer player = MockPlayer player =
new MockPlayer.Builder() new MockPlayer.Builder()
.setApplicationLooper(threadTestRule.getHandler().getLooper()) .setApplicationLooper(threadTestRule.getHandler().getLooper())
.setLatchCount(1)
.build(); .build();
MediaSession session = MediaSession session =
sessionTestRule.ensureReleaseAfterTest( sessionTestRule.ensureReleaseAfterTest(
@ -190,8 +189,7 @@ public class MediaSessionAndControllerTest {
threadTestRule.getHandler().postAndSync(controller::play); threadTestRule.getHandler().postAndSync(controller::play);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_PLAY, TIMEOUT_MS);
assertThat(player.playCalled).isTrue();
} }
@Test @Test

View File

@ -74,7 +74,6 @@ public class MediaSessionCallbackTest {
context = ApplicationProvider.getApplicationContext(); context = ApplicationProvider.getApplicationContext();
player = player =
new MockPlayer.Builder() new MockPlayer.Builder()
.setLatchCount(1)
.setApplicationLooper(threadTestRule.getHandler().getLooper()) .setApplicationLooper(threadTestRule.getHandler().getLooper())
.build(); .build();
} }
@ -157,15 +156,14 @@ public class MediaSessionCallbackTest {
controllerTestRule.createRemoteController(session.getToken()); controllerTestRule.createRemoteController(session.getToken());
controller.prepare(); controller.prepare();
assertThat(player.countDownLatch.await(NO_RESPONSE_TIMEOUT_MS, MILLISECONDS)).isFalse(); Thread.sleep(NO_RESPONSE_TIMEOUT_MS);
assertThat(player.prepareCalled).isFalse(); assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_PREPARE)).isFalse();
assertThat(commands).hasSize(1); assertThat(commands).hasSize(1);
assertThat(commands.get(0)).isEqualTo(Player.COMMAND_PREPARE); assertThat(commands.get(0)).isEqualTo(Player.COMMAND_PREPARE);
controller.play(); controller.play();
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_PLAY, TIMEOUT_MS);
assertThat(player.playCalled).isTrue(); assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_PREPARE)).isFalse();
assertThat(player.prepareCalled).isFalse();
assertThat(commands).hasSize(2); assertThat(commands).hasSize(2);
assertThat(commands.get(1)).isEqualTo(Player.COMMAND_PLAY_PAUSE); assertThat(commands.get(1)).isEqualTo(Player.COMMAND_PLAY_PAUSE);
} }

View File

@ -94,8 +94,7 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
public void setUp() { public void setUp() {
context = ApplicationProvider.getApplicationContext(); context = ApplicationProvider.getApplicationContext();
handler = threadTestRule.getHandler(); handler = threadTestRule.getHandler();
player = player = new MockPlayer.Builder().setApplicationLooper(handler.getLooper()).build();
new MockPlayer.Builder().setLatchCount(1).setApplicationLooper(handler.getLooper()).build();
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
} }
@ -206,8 +205,8 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true); context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true);
controller.getTransportControls().play(); controller.getTransportControls().play();
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.playCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_PLAY, TIMEOUT_MS);
} }
@Test @Test
@ -222,8 +221,8 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true); context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true);
controller.getTransportControls().pause(); controller.getTransportControls().pause();
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.pauseCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_PAUSE, TIMEOUT_MS);
} }
@Test @Test
@ -238,8 +237,8 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true); context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true);
controller.getTransportControls().stop(); controller.getTransportControls().stop();
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.stopCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_STOP, TIMEOUT_MS);
} }
@Test @Test
@ -254,8 +253,8 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true); context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true);
controller.getTransportControls().prepare(); controller.getTransportControls().prepare();
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.prepareCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_PREPARE, TIMEOUT_MS);
} }
@Test @Test
@ -271,8 +270,8 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
long seekPosition = 12125L; long seekPosition = 12125L;
controller.getTransportControls().seekTo(seekPosition); controller.getTransportControls().seekTo(seekPosition);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.seekToCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO, TIMEOUT_MS);
assertThat(player.seekPositionMs).isEqualTo(seekPosition); assertThat(player.seekPositionMs).isEqualTo(seekPosition);
} }
@ -289,8 +288,8 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
float testSpeed = 2.0f; float testSpeed = 2.0f;
controller.getTransportControls().setPlaybackSpeed(testSpeed); controller.getTransportControls().setPlaybackSpeed(testSpeed);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.setPlaybackSpeedCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SET_PLAYBACK_SPEED, TIMEOUT_MS);
assertThat(player.playbackParameters.speed).isEqualTo(testSpeed); assertThat(player.playbackParameters.speed).isEqualTo(testSpeed);
} }
@ -316,8 +315,7 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
MediaDescriptionCompat desc = new MediaDescriptionCompat.Builder().setMediaId(mediaId).build(); MediaDescriptionCompat desc = new MediaDescriptionCompat.Builder().setMediaId(mediaId).build();
controller.addQueueItem(desc); controller.addQueueItem(desc);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_ADD_MEDIA_ITEM, TIMEOUT_MS);
assertThat(player.addMediaItemCalled).isTrue();
assertThat(player.mediaItem.mediaId).isEqualTo(mediaId); assertThat(player.mediaItem.mediaId).isEqualTo(mediaId);
} }
@ -344,8 +342,7 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
MediaDescriptionCompat desc = new MediaDescriptionCompat.Builder().setMediaId(mediaId).build(); MediaDescriptionCompat desc = new MediaDescriptionCompat.Builder().setMediaId(mediaId).build();
controller.addQueueItem(desc, testIndex); controller.addQueueItem(desc, testIndex);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_ADD_MEDIA_ITEM_WITH_INDEX, TIMEOUT_MS);
assertThat(player.addMediaItemWithIndexCalled).isTrue();
assertThat(player.index).isEqualTo(testIndex); assertThat(player.index).isEqualTo(testIndex);
assertThat(player.mediaItem.mediaId).isEqualTo(mediaId); assertThat(player.mediaItem.mediaId).isEqualTo(mediaId);
} }
@ -375,8 +372,7 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
new MediaDescriptionCompat.Builder().setMediaId(targetItem.mediaId).build(); new MediaDescriptionCompat.Builder().setMediaId(targetItem.mediaId).build();
controller.removeQueueItem(desc); controller.removeQueueItem(desc);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_REMOVE_MEDIA_ITEM, TIMEOUT_MS);
assertThat(player.removeMediaItemCalled).isTrue();
assertThat(player.index).isEqualTo(targetIndex); assertThat(player.index).isEqualTo(targetIndex);
} }
@ -392,8 +388,8 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true); context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true);
controller.getTransportControls().skipToPrevious(); controller.getTransportControls().skipToPrevious();
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.seekToPreviousCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO_PREVIOUS, TIMEOUT_MS);
} }
@Test @Test
@ -408,8 +404,8 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true); context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true);
controller.getTransportControls().skipToNext(); controller.getTransportControls().skipToNext();
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.seekToNextCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO_NEXT, TIMEOUT_MS);
} }
@Test @Test
@ -434,8 +430,8 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
int targetIndex = 3; int targetIndex = 3;
controller.getTransportControls().skipToQueueItem(queue.get(targetIndex).getQueueId()); controller.getTransportControls().skipToQueueItem(queue.get(targetIndex).getQueueId());
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(
assertThat(player.seekToDefaultPositionWithMediaItemIndexCalled).isTrue(); MockPlayer.METHOD_SEEK_TO_DEFAULT_POSITION_WITH_MEDIA_ITEM_INDEX, TIMEOUT_MS);
assertThat(player.seekMediaItemIndex).isEqualTo(targetIndex); assertThat(player.seekMediaItemIndex).isEqualTo(targetIndex);
} }
@ -452,9 +448,8 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
@PlaybackStateCompat.ShuffleMode int testShuffleMode = PlaybackStateCompat.SHUFFLE_MODE_GROUP; @PlaybackStateCompat.ShuffleMode int testShuffleMode = PlaybackStateCompat.SHUFFLE_MODE_GROUP;
controller.getTransportControls().setShuffleMode(testShuffleMode); controller.getTransportControls().setShuffleMode(testShuffleMode);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.setShuffleModeCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SET_SHUFFLE_MODE, TIMEOUT_MS);
assertThat(player.shuffleModeEnabled).isTrue(); assertThat(player.shuffleModeEnabled).isTrue();
} }
@ -471,9 +466,8 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
int testRepeatMode = Player.REPEAT_MODE_ALL; int testRepeatMode = Player.REPEAT_MODE_ALL;
controller.getTransportControls().setRepeatMode(testRepeatMode); controller.getTransportControls().setRepeatMode(testRepeatMode);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.setRepeatModeCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SET_REPEAT_MODE, TIMEOUT_MS);
assertThat(player.repeatMode).isEqualTo(testRepeatMode); assertThat(player.repeatMode).isEqualTo(testRepeatMode);
} }
@ -488,7 +482,7 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
new RemoteMediaControllerCompat( new RemoteMediaControllerCompat(
context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true); context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true);
MockPlayer remotePlayer = MockPlayer remotePlayer =
new MockPlayer.Builder().setLatchCount(1).setApplicationLooper(handler.getLooper()).build(); new MockPlayer.Builder().setApplicationLooper(handler.getLooper()).build();
handler.postAndSync( handler.postAndSync(
() -> { () -> {
remotePlayer.deviceInfo = remotePlayer.deviceInfo =
@ -501,8 +495,7 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
int targetVolume = 50; int targetVolume = 50;
controller.setVolumeTo(targetVolume, /* flags= */ 0); controller.setVolumeTo(targetVolume, /* flags= */ 0);
assertThat(remotePlayer.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); remotePlayer.awaitMethodCalled(MockPlayer.METHOD_SET_DEVICE_VOLUME, TIMEOUT_MS);
assertThat(remotePlayer.setDeviceVolumeCalled).isTrue();
assertThat(remotePlayer.deviceVolume).isEqualTo(targetVolume); assertThat(remotePlayer.deviceVolume).isEqualTo(targetVolume);
} }
@ -517,7 +510,7 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
new RemoteMediaControllerCompat( new RemoteMediaControllerCompat(
context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true); context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true);
MockPlayer remotePlayer = MockPlayer remotePlayer =
new MockPlayer.Builder().setLatchCount(1).setApplicationLooper(handler.getLooper()).build(); new MockPlayer.Builder().setApplicationLooper(handler.getLooper()).build();
handler.postAndSync( handler.postAndSync(
() -> { () -> {
remotePlayer.deviceInfo = remotePlayer.deviceInfo =
@ -529,8 +522,7 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
controller.adjustVolume(AudioManager.ADJUST_RAISE, /* flags= */ 0); controller.adjustVolume(AudioManager.ADJUST_RAISE, /* flags= */ 0);
assertThat(remotePlayer.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); remotePlayer.awaitMethodCalled(MockPlayer.METHOD_INCREASE_DEVICE_VOLUME, TIMEOUT_MS);
assertThat(remotePlayer.increaseDeviceVolumeCalled).isTrue();
} }
@Test @Test
@ -544,7 +536,7 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
new RemoteMediaControllerCompat( new RemoteMediaControllerCompat(
context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true); context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true);
MockPlayer remotePlayer = MockPlayer remotePlayer =
new MockPlayer.Builder().setLatchCount(1).setApplicationLooper(handler.getLooper()).build(); new MockPlayer.Builder().setApplicationLooper(handler.getLooper()).build();
handler.postAndSync( handler.postAndSync(
() -> { () -> {
remotePlayer.deviceInfo = remotePlayer.deviceInfo =
@ -556,8 +548,7 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
controller.adjustVolume(AudioManager.ADJUST_LOWER, /* flags= */ 0); controller.adjustVolume(AudioManager.ADJUST_LOWER, /* flags= */ 0);
assertThat(remotePlayer.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); remotePlayer.awaitMethodCalled(MockPlayer.METHOD_DECREASE_DEVICE_VOLUME, TIMEOUT_MS);
assertThat(remotePlayer.decreaseDeviceVolumeCalled).isTrue();
} }
@Test @Test
@ -704,7 +695,9 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
controller = controller =
new RemoteMediaControllerCompat( new RemoteMediaControllerCompat(
context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true); context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true);
controller.sendCommand(testCommand, testArgs, /* cb= */ null); controller.sendCommand(testCommand, testArgs, /* cb= */ null);
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
} }
@ -723,13 +716,15 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
.setId("controllerCallback_sessionRejects") .setId("controllerCallback_sessionRejects")
.setSessionCallback(sessionCallback) .setSessionCallback(sessionCallback)
.build(); .build();
// Session will not accept the controller's commands. // Session will not accept the controller's commands.
controller = controller =
new RemoteMediaControllerCompat( new RemoteMediaControllerCompat(
context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true); context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true);
controller.getTransportControls().play(); controller.getTransportControls().play();
assertThat(player.countDownLatch.await(NO_RESPONSE_TIMEOUT_MS, MILLISECONDS)).isFalse();
Thread.sleep(NO_RESPONSE_TIMEOUT_MS);
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_PLAY)).isFalse();
} }
@Test @Test
@ -757,10 +752,11 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
controller = controller =
new RemoteMediaControllerCompat( new RemoteMediaControllerCompat(
context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true); context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true);
controller.getTransportControls().prepareFromUri(mediaUri, bundle); controller.getTransportControls().prepareFromUri(mediaUri, bundle);
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_PREPARE, TIMEOUT_MS);
assertThat(player.prepareCalled).isTrue();
} }
@Test @Test
@ -788,10 +784,11 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
controller = controller =
new RemoteMediaControllerCompat( new RemoteMediaControllerCompat(
context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true); context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true);
controller.getTransportControls().playFromUri(request, bundle); controller.getTransportControls().playFromUri(request, bundle);
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_PLAY, TIMEOUT_MS);
assertThat(player.playCalled).isTrue();
} }
@Test @Test
@ -820,10 +817,11 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
controller = controller =
new RemoteMediaControllerCompat( new RemoteMediaControllerCompat(
context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true); context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true);
controller.getTransportControls().prepareFromMediaId(request, bundle); controller.getTransportControls().prepareFromMediaId(request, bundle);
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_PREPARE, TIMEOUT_MS);
assertThat(player.prepareCalled).isTrue();
} }
@Test @Test
@ -852,10 +850,11 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
controller = controller =
new RemoteMediaControllerCompat( new RemoteMediaControllerCompat(
context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true); context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true);
controller.getTransportControls().playFromMediaId(mediaId, bundle); controller.getTransportControls().playFromMediaId(mediaId, bundle);
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_PLAY, TIMEOUT_MS);
assertThat(player.playCalled).isTrue();
} }
@Test @Test
@ -884,10 +883,11 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
controller = controller =
new RemoteMediaControllerCompat( new RemoteMediaControllerCompat(
context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true); context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true);
controller.getTransportControls().prepareFromSearch(query, bundle); controller.getTransportControls().prepareFromSearch(query, bundle);
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_PREPARE, TIMEOUT_MS);
assertThat(player.prepareCalled).isTrue();
} }
@Test @Test
@ -916,10 +916,11 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
controller = controller =
new RemoteMediaControllerCompat( new RemoteMediaControllerCompat(
context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true); context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true);
controller.getTransportControls().playFromSearch(query, bundle); controller.getTransportControls().playFromSearch(query, bundle);
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_PLAY, TIMEOUT_MS);
assertThat(player.playCalled).isTrue();
} }
@Test @Test
@ -944,7 +945,6 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
return Futures.immediateFuture(new SessionResult(RESULT_SUCCESS)); return Futures.immediateFuture(new SessionResult(RESULT_SUCCESS));
} }
}; };
handler.postAndSync( handler.postAndSync(
() -> { () -> {
List<MediaItem> mediaItems = MediaTestUtils.createMediaItems(mediaId); List<MediaItem> mediaItems = MediaTestUtils.createMediaItems(mediaId);
@ -958,7 +958,9 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
controller = controller =
new RemoteMediaControllerCompat( new RemoteMediaControllerCompat(
context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true); context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true);
controller.getTransportControls().setRating(rating); controller.getTransportControls().setRating(rating);
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
} }
@ -991,16 +993,17 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true); context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true);
controller.getTransportControls().pause(); controller.getTransportControls().pause();
assertThat(latchForPause.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); assertThat(latchForPause.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.countDownLatch.await(NO_RESPONSE_TIMEOUT_MS, MILLISECONDS)).isFalse(); Thread.sleep(NO_RESPONSE_TIMEOUT_MS);
assertThat(player.pauseCalled).isFalse(); assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_PAUSE)).isFalse();
assertThat(commands).hasSize(1); assertThat(commands).hasSize(1);
assertThat(commands.get(0)).isEqualTo(COMMAND_PLAY_PAUSE); assertThat(commands.get(0)).isEqualTo(COMMAND_PLAY_PAUSE);
controller.getTransportControls().prepare(); controller.getTransportControls().prepare();
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.prepareCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_PREPARE, TIMEOUT_MS);
assertThat(player.pauseCalled).isFalse(); assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_PAUSE)).isFalse();
assertThat(commands).hasSize(2); assertThat(commands).hasSize(2);
assertThat(commands.get(1)).isEqualTo(COMMAND_PREPARE); assertThat(commands.get(1)).isEqualTo(COMMAND_PREPARE);
} }
@ -1056,7 +1059,8 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
session = null; session = null;
controller.getTransportControls().play(); controller.getTransportControls().play();
assertThat(player.countDownLatch.await(NO_RESPONSE_TIMEOUT_MS, MILLISECONDS)).isFalse(); Thread.sleep(NO_RESPONSE_TIMEOUT_MS);
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_PLAY)).isFalse();
// Ensure that the controller cannot use newly create session with the same ID. // Ensure that the controller cannot use newly create session with the same ID.
// Recreated session has different session stub, so previously created controller // Recreated session has different session stub, so previously created controller
@ -1068,7 +1072,8 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
.build(); .build();
controller.getTransportControls().play(); controller.getTransportControls().play();
assertThat(player.countDownLatch.await(NO_RESPONSE_TIMEOUT_MS, MILLISECONDS)).isFalse(); Thread.sleep(NO_RESPONSE_TIMEOUT_MS);
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_PLAY)).isFalse();
} }
private static class TestSessionCallback implements SessionCallback { private static class TestSessionCallback implements SessionCallback {

View File

@ -89,8 +89,7 @@ public class MediaSessionKeyEventTest {
Context context = ApplicationProvider.getApplicationContext(); Context context = ApplicationProvider.getApplicationContext();
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
handler = threadTestRule.getHandler(); handler = threadTestRule.getHandler();
player = player = new MockPlayer.Builder().setApplicationLooper(handler.getLooper()).build();
new MockPlayer.Builder().setLatchCount(1).setApplicationLooper(handler.getLooper()).build();
sessionCallback = new TestSessionCallback(); sessionCallback = new TestSessionCallback();
session = new MediaSession.Builder(context, player).setSessionCallback(sessionCallback).build(); session = new MediaSession.Builder(context, player).setSessionCallback(sessionCallback).build();
@ -120,7 +119,7 @@ public class MediaSessionKeyEventTest {
} }
@After @After
public void cleanUp() throws Exception { public void tearDown() throws Exception {
handler.postAndSync( handler.postAndSync(
() -> { () -> {
if (mediaPlayer != null) { if (mediaPlayer != null) {
@ -131,55 +130,46 @@ public class MediaSessionKeyEventTest {
session.release(); session.release();
} }
private void dispatchMediaKeyEvent(int keyCode, boolean doubleTap) {
audioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
audioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keyCode));
if (doubleTap) {
audioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
audioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keyCode));
}
}
@Test @Test
public void playKeyEvent() throws Exception { public void playKeyEvent() throws Exception {
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_PLAY, false); dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_PLAY, false);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.playCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_PLAY, TIMEOUT_MS);
} }
@Test @Test
public void pauseKeyEvent() throws Exception { public void pauseKeyEvent() throws Exception {
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_PAUSE, false); dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_PAUSE, false);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.pauseCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_PAUSE, TIMEOUT_MS);
} }
@Test @Test
public void nextKeyEvent() throws Exception { public void nextKeyEvent() throws Exception {
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_NEXT, false); dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_NEXT, false);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.seekToNextCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO_NEXT, TIMEOUT_MS);
} }
@Test @Test
public void previousKeyEvent() throws Exception { public void previousKeyEvent() throws Exception {
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_PREVIOUS, false); dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_PREVIOUS, false);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.seekToPreviousCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO_PREVIOUS, TIMEOUT_MS);
} }
@Test @Test
public void stopKeyEvent() throws Exception { public void stopKeyEvent() throws Exception {
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_STOP, false); dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_STOP, false);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.stopCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_STOP, TIMEOUT_MS);
} }
@Test @Test
public void playPauseKeyEvent_play() throws Exception { public void playPauseKeyEvent_play() throws Exception {
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, false); dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, false);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.playCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_PLAY, TIMEOUT_MS);
} }
@Test @Test
@ -188,18 +178,28 @@ public class MediaSessionKeyEventTest {
() -> { () -> {
player.playWhenReady = true; player.playWhenReady = true;
}); });
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, false); dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, false);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.pauseCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_PAUSE, TIMEOUT_MS);
} }
@Test @Test
public void playPauseKeyEvent_doubleTapIsTranslatedToSkipToNext() throws Exception { public void playPauseKeyEvent_doubleTapIsTranslatedToSkipToNext() throws Exception {
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, true); dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, true);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.seekToNextCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO_NEXT, TIMEOUT_MS);
assertThat(player.playCalled).isFalse(); assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_PLAY)).isFalse();
assertThat(player.pauseCalled).isFalse(); assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_PAUSE)).isFalse();
}
private void dispatchMediaKeyEvent(int keyCode, boolean doubleTap) {
audioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
audioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keyCode));
if (doubleTap) {
audioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
audioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keyCode));
}
} }
private static class TestSessionCallback implements MediaSession.SessionCallback { private static class TestSessionCallback implements MediaSession.SessionCallback {

View File

@ -88,7 +88,7 @@ public class MediaSessionPermissionTest {
} }
@After @After
public void cleanUp() { public void tearDown() {
if (session != null) { if (session != null) {
session.release(); session.release();
session = null; session = null;
@ -97,55 +97,6 @@ public class MediaSessionPermissionTest {
callback = null; callback = null;
} }
private void createSessionWithAvailableCommands(
SessionCommands sessionCommands, Player.Commands playerCommands) {
player =
new MockPlayer.Builder()
.setLatchCount(1)
.setApplicationLooper(threadTestRule.getHandler().getLooper())
.build();
callback =
new MySessionCallback() {
@Override
public MediaSession.ConnectionResult onConnect(
MediaSession session, ControllerInfo controller) {
if (!TextUtils.equals(SUPPORT_APP_PACKAGE_NAME, controller.getPackageName())) {
return MediaSession.ConnectionResult.reject();
}
return MediaSession.ConnectionResult.accept(sessionCommands, playerCommands);
}
};
if (this.session != null) {
this.session.release();
}
this.session =
new MediaSession.Builder(context, player)
.setId(SESSION_ID)
.setSessionCallback(callback)
.build();
}
private SessionCommands createSessionCommandsWith(SessionCommand command) {
return new SessionCommands.Builder().add(command).build();
}
private void testOnCommandRequest(int commandCode, PermissionTestTask runnable) throws Exception {
createSessionWithAvailableCommands(
SessionCommands.EMPTY, createPlayerCommandsWith(commandCode));
runnable.run(controllerTestRule.createRemoteController(session.getToken()));
assertThat(callback.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(callback.onCommandRequestCalled).isTrue();
assertThat(callback.command).isEqualTo(commandCode);
createSessionWithAvailableCommands(
SessionCommands.EMPTY, createPlayerCommandsWithout(commandCode));
runnable.run(controllerTestRule.createRemoteController(session.getToken()));
assertThat(callback.countDownLatch.await(NO_RESPONSE_TIMEOUT_MS, MILLISECONDS)).isFalse();
assertThat(callback.onCommandRequestCalled).isFalse();
}
@Test @Test
public void play() throws Exception { public void play() throws Exception {
testOnCommandRequest(COMMAND_PLAY_PAUSE, RemoteMediaController::play); testOnCommandRequest(COMMAND_PLAY_PAUSE, RemoteMediaController::play);
@ -409,4 +360,52 @@ public class MediaSessionPermissionTest {
return Futures.immediateFuture(new SessionResult(RESULT_SUCCESS)); return Futures.immediateFuture(new SessionResult(RESULT_SUCCESS));
} }
} }
private void createSessionWithAvailableCommands(
SessionCommands sessionCommands, Player.Commands playerCommands) {
player =
new MockPlayer.Builder()
.setApplicationLooper(threadTestRule.getHandler().getLooper())
.build();
callback =
new MySessionCallback() {
@Override
public MediaSession.ConnectionResult onConnect(
MediaSession session, ControllerInfo controller) {
if (!TextUtils.equals(SUPPORT_APP_PACKAGE_NAME, controller.getPackageName())) {
return MediaSession.ConnectionResult.reject();
}
return MediaSession.ConnectionResult.accept(sessionCommands, playerCommands);
}
};
if (this.session != null) {
this.session.release();
}
this.session =
new MediaSession.Builder(context, player)
.setId(SESSION_ID)
.setSessionCallback(callback)
.build();
}
private SessionCommands createSessionCommandsWith(SessionCommand command) {
return new SessionCommands.Builder().add(command).build();
}
private void testOnCommandRequest(int commandCode, PermissionTestTask runnable) throws Exception {
createSessionWithAvailableCommands(
SessionCommands.EMPTY, createPlayerCommandsWith(commandCode));
runnable.run(controllerTestRule.createRemoteController(session.getToken()));
assertThat(callback.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(callback.onCommandRequestCalled).isTrue();
assertThat(callback.command).isEqualTo(commandCode);
createSessionWithAvailableCommands(
SessionCommands.EMPTY, createPlayerCommandsWithout(commandCode));
runnable.run(controllerTestRule.createRemoteController(session.getToken()));
assertThat(callback.countDownLatch.await(NO_RESPONSE_TIMEOUT_MS, MILLISECONDS)).isFalse();
assertThat(callback.onCommandRequestCalled).isFalse();
}
} }

View File

@ -16,10 +16,8 @@
package androidx.media3.session; package androidx.media3.session;
import static androidx.media3.test.session.common.CommonConstants.SUPPORT_APP_PACKAGE_NAME; import static androidx.media3.test.session.common.CommonConstants.SUPPORT_APP_PACKAGE_NAME;
import static androidx.media3.test.session.common.TestUtils.LONG_TIMEOUT_MS;
import static androidx.media3.test.session.common.TestUtils.TIMEOUT_MS; import static androidx.media3.test.session.common.TestUtils.TIMEOUT_MS;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import androidx.media3.common.DeviceInfo; import androidx.media3.common.DeviceInfo;
import androidx.media3.common.MediaItem; import androidx.media3.common.MediaItem;
@ -63,7 +61,6 @@ public class MediaSessionPlayerTest {
public void setUp() throws Exception { public void setUp() throws Exception {
player = player =
new MockPlayer.Builder() new MockPlayer.Builder()
.setLatchCount(1)
.setApplicationLooper(threadTestRule.getHandler().getLooper()) .setApplicationLooper(threadTestRule.getHandler().getLooper())
.setMediaItems(/* itemCount= */ 5) .setMediaItems(/* itemCount= */ 5)
.build(); .build();
@ -87,62 +84,64 @@ public class MediaSessionPlayerTest {
} }
@After @After
public void cleanUp() { public void tearDown() throws Exception {
if (session != null) { controller.release();
session.release(); session.release();
}
} }
@Test @Test
public void play() throws Exception { public void play() throws Exception {
controller.play(); controller.play();
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.playCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_PLAY, TIMEOUT_MS);
} }
@Test @Test
public void pause() throws Exception { public void pause() throws Exception {
controller.pause(); controller.pause();
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.pauseCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_PAUSE, TIMEOUT_MS);
} }
@Test @Test
public void prepare() throws Exception { public void prepare() throws Exception {
controller.prepare(); controller.prepare();
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.prepareCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_PREPARE, TIMEOUT_MS);
} }
@Test @Test
public void stop() throws Exception { public void stop() throws Exception {
controller.stop(); controller.stop();
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.stopCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_STOP, TIMEOUT_MS);
} }
@Test @Test
public void setPlayWhenReady() throws Exception { public void setPlayWhenReady() throws Exception {
boolean testPlayWhenReady = true; boolean testPlayWhenReady = true;
controller.setPlayWhenReady(testPlayWhenReady); controller.setPlayWhenReady(testPlayWhenReady);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.setPlayWhenReadyCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SET_PLAY_WHEN_READY, TIMEOUT_MS);
assertThat(player.playWhenReady).isEqualTo(testPlayWhenReady); assertThat(player.playWhenReady).isEqualTo(testPlayWhenReady);
} }
@Test @Test
public void seekToDefaultPosition() throws Exception { public void seekToDefaultPosition() throws Exception {
controller.seekToDefaultPosition(); controller.seekToDefaultPosition();
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.seekToDefaultPositionCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO_DEFAULT_POSITION, TIMEOUT_MS);
} }
@Test @Test
public void seekToDefaultPosition_withMediaItemIndex() throws Exception { public void seekToDefaultPosition_withMediaItemIndex() throws Exception {
int mediaItemIndex = 3; int mediaItemIndex = 3;
controller.seekToDefaultPosition(mediaItemIndex); controller.seekToDefaultPosition(mediaItemIndex);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.seekToDefaultPositionWithMediaItemIndexCalled).isTrue(); player.awaitMethodCalled(
MockPlayer.METHOD_SEEK_TO_DEFAULT_POSITION_WITH_MEDIA_ITEM_INDEX, TIMEOUT_MS);
assertThat(player.seekMediaItemIndex).isEqualTo(mediaItemIndex); assertThat(player.seekMediaItemIndex).isEqualTo(mediaItemIndex);
} }
@ -150,8 +149,8 @@ public class MediaSessionPlayerTest {
public void seekTo() throws Exception { public void seekTo() throws Exception {
long seekPositionMs = 12125L; long seekPositionMs = 12125L;
controller.seekTo(seekPositionMs); controller.seekTo(seekPositionMs);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.seekToCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO, TIMEOUT_MS);
assertThat(player.seekPositionMs).isEqualTo(seekPositionMs); assertThat(player.seekPositionMs).isEqualTo(seekPositionMs);
} }
@ -159,9 +158,10 @@ public class MediaSessionPlayerTest {
public void seekTo_withMediaItemIndex() throws Exception { public void seekTo_withMediaItemIndex() throws Exception {
int mediaItemIndex = 3; int mediaItemIndex = 3;
long seekPositionMs = 12125L; long seekPositionMs = 12125L;
controller.seekTo(mediaItemIndex, seekPositionMs); controller.seekTo(mediaItemIndex, seekPositionMs);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.seekToWithMediaItemIndexCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO_WITH_MEDIA_ITEM_INDEX, TIMEOUT_MS);
assertThat(player.seekMediaItemIndex).isEqualTo(mediaItemIndex); assertThat(player.seekMediaItemIndex).isEqualTo(mediaItemIndex);
assertThat(player.seekPositionMs).isEqualTo(seekPositionMs); assertThat(player.seekPositionMs).isEqualTo(seekPositionMs);
} }
@ -169,8 +169,10 @@ public class MediaSessionPlayerTest {
@Test @Test
public void setPlaybackSpeed() throws Exception { public void setPlaybackSpeed() throws Exception {
float testSpeed = 1.5f; float testSpeed = 1.5f;
controller.setPlaybackSpeed(testSpeed); controller.setPlaybackSpeed(testSpeed);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
player.awaitMethodCalled(MockPlayer.METHOD_SET_PLAYBACK_SPEED, TIMEOUT_MS);
assertThat(player.playbackParameters.speed).isEqualTo(testSpeed); assertThat(player.playbackParameters.speed).isEqualTo(testSpeed);
} }
@ -178,9 +180,10 @@ public class MediaSessionPlayerTest {
public void setPlaybackParameters() throws Exception { public void setPlaybackParameters() throws Exception {
PlaybackParameters testPlaybackParameters = PlaybackParameters testPlaybackParameters =
new PlaybackParameters(/* speed= */ 1.4f, /* pitch= */ 2.3f); new PlaybackParameters(/* speed= */ 1.4f, /* pitch= */ 2.3f);
controller.setPlaybackParameters(testPlaybackParameters); controller.setPlaybackParameters(testPlaybackParameters);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.setPlaybackParametersCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SET_PLAYBACK_PARAMETERS, TIMEOUT_MS);
assertThat(player.playbackParameters).isEqualTo(testPlaybackParameters); assertThat(player.playbackParameters).isEqualTo(testPlaybackParameters);
} }
@ -194,8 +197,7 @@ public class MediaSessionPlayerTest {
controller.setMediaItem(item); controller.setMediaItem(item);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEM, TIMEOUT_MS);
assertThat(player.setMediaItemCalled).isTrue();
assertThat(player.mediaItem).isEqualTo(item); assertThat(player.mediaItem).isEqualTo(item);
assertThat(player.startPositionMs).isEqualTo(startPositionMs); assertThat(player.startPositionMs).isEqualTo(startPositionMs);
assertThat(player.resetPosition).isEqualTo(resetPosition); assertThat(player.resetPosition).isEqualTo(resetPosition);
@ -211,8 +213,7 @@ public class MediaSessionPlayerTest {
controller.setMediaItem(item); controller.setMediaItem(item);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEM, TIMEOUT_MS);
assertThat(player.setMediaItemCalled).isTrue();
assertThat(player.mediaItem).isEqualTo(item); assertThat(player.mediaItem).isEqualTo(item);
assertThat(player.startPositionMs).isEqualTo(startPositionMs); assertThat(player.startPositionMs).isEqualTo(startPositionMs);
assertThat(player.resetPosition).isEqualTo(resetPosition); assertThat(player.resetPosition).isEqualTo(resetPosition);
@ -228,8 +229,7 @@ public class MediaSessionPlayerTest {
controller.setMediaItem(item); controller.setMediaItem(item);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEM, TIMEOUT_MS);
assertThat(player.setMediaItemCalled).isTrue();
assertThat(player.mediaItem).isEqualTo(item); assertThat(player.mediaItem).isEqualTo(item);
assertThat(player.startPositionMs).isEqualTo(startPositionMs); assertThat(player.startPositionMs).isEqualTo(startPositionMs);
assertThat(player.resetPosition).isEqualTo(resetPosition); assertThat(player.resetPosition).isEqualTo(resetPosition);
@ -241,8 +241,7 @@ public class MediaSessionPlayerTest {
controller.setMediaItems(items); controller.setMediaItems(items);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS, TIMEOUT_MS);
assertThat(player.setMediaItemsCalled).isTrue();
assertThat(player.mediaItems).isEqualTo(items); assertThat(player.mediaItems).isEqualTo(items);
assertThat(player.resetPosition).isFalse(); assertThat(player.resetPosition).isFalse();
} }
@ -253,8 +252,7 @@ public class MediaSessionPlayerTest {
controller.setMediaItems(items, /* resetPosition= */ true); controller.setMediaItems(items, /* resetPosition= */ true);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS_WITH_RESET_POSITION, TIMEOUT_MS);
assertThat(player.setMediaItemsWithResetPositionCalled).isTrue();
assertThat(player.mediaItems).isEqualTo(items); assertThat(player.mediaItems).isEqualTo(items);
assertThat(player.resetPosition).isTrue(); assertThat(player.resetPosition).isTrue();
} }
@ -267,8 +265,7 @@ public class MediaSessionPlayerTest {
controller.setMediaItems(items, startMediaItemIndex, startPositionMs); controller.setMediaItems(items, startMediaItemIndex, startPositionMs);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS_WITH_START_INDEX, TIMEOUT_MS);
assertThat(player.setMediaItemsWithStartIndexCalled).isTrue();
assertThat(player.mediaItems).isEqualTo(items); assertThat(player.mediaItems).isEqualTo(items);
assertThat(player.startMediaItemIndex).isEqualTo(startMediaItemIndex); assertThat(player.startMediaItemIndex).isEqualTo(startMediaItemIndex);
assertThat(player.startPositionMs).isEqualTo(startPositionMs); assertThat(player.startPositionMs).isEqualTo(startPositionMs);
@ -279,9 +276,10 @@ public class MediaSessionPlayerTest {
int listSize = 4; int listSize = 4;
List<MediaItem> list = MediaTestUtils.createMediaItems(listSize); List<MediaItem> list = MediaTestUtils.createMediaItems(listSize);
list.set(2, list.get(1)); list.set(2, list.get(1));
controller.setMediaItems(list); controller.setMediaItems(list);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.setMediaItemsCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS, TIMEOUT_MS);
assertThat(player.mediaItems.size()).isEqualTo(listSize); assertThat(player.mediaItems.size()).isEqualTo(listSize);
for (int i = 0; i < listSize; i++) { for (int i = 0; i < listSize; i++) {
assertThat(player.mediaItems.get(i).mediaId).isEqualTo(list.get(i).mediaId); assertThat(player.mediaItems.get(i).mediaId).isEqualTo(list.get(i).mediaId);
@ -293,9 +291,8 @@ public class MediaSessionPlayerTest {
int listSize = 5000; int listSize = 5000;
// Make client app to generate a long list, and call setMediaItems() with it. // Make client app to generate a long list, and call setMediaItems() with it.
controller.createAndSetFakeMediaItems(listSize); controller.createAndSetFakeMediaItems(listSize);
assertThat(player.countDownLatch.await(LONG_TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.setMediaItemsCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS, TIMEOUT_MS);
assertThat(player.mediaItems).isNotNull(); assertThat(player.mediaItems).isNotNull();
assertThat(player.mediaItems.size()).isEqualTo(listSize); assertThat(player.mediaItems.size()).isEqualTo(listSize);
for (int i = 0; i < listSize; i++) { for (int i = 0; i < listSize; i++) {
@ -310,8 +307,7 @@ public class MediaSessionPlayerTest {
controller.setPlaylistMetadata(playlistMetadata); controller.setPlaylistMetadata(playlistMetadata);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SET_PLAYLIST_METADATA, TIMEOUT_MS);
assertThat(player.setPlaylistMetadataCalled).isTrue();
assertThat(player.playlistMetadata).isEqualTo(playlistMetadata); assertThat(player.playlistMetadata).isEqualTo(playlistMetadata);
} }
@ -321,8 +317,7 @@ public class MediaSessionPlayerTest {
controller.addMediaItem(mediaItem); controller.addMediaItem(mediaItem);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_ADD_MEDIA_ITEM, TIMEOUT_MS);
assertThat(player.addMediaItemCalled).isTrue();
assertThat(player.mediaItem).isEqualTo(mediaItem); assertThat(player.mediaItem).isEqualTo(mediaItem);
} }
@ -333,8 +328,7 @@ public class MediaSessionPlayerTest {
controller.addMediaItem(index, mediaItem); controller.addMediaItem(index, mediaItem);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_ADD_MEDIA_ITEM_WITH_INDEX, TIMEOUT_MS);
assertThat(player.addMediaItemWithIndexCalled).isTrue();
assertThat(player.index).isEqualTo(index); assertThat(player.index).isEqualTo(index);
assertThat(player.mediaItem).isEqualTo(mediaItem); assertThat(player.mediaItem).isEqualTo(mediaItem);
} }
@ -346,8 +340,7 @@ public class MediaSessionPlayerTest {
controller.addMediaItems(mediaItems); controller.addMediaItems(mediaItems);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_ADD_MEDIA_ITEMS, TIMEOUT_MS);
assertThat(player.addMediaItemsCalled).isTrue();
assertThat(player.mediaItems).isEqualTo(mediaItems); assertThat(player.mediaItems).isEqualTo(mediaItems);
} }
@ -359,8 +352,7 @@ public class MediaSessionPlayerTest {
controller.addMediaItems(index, mediaItems); controller.addMediaItems(index, mediaItems);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_ADD_MEDIA_ITEMS_WITH_INDEX, TIMEOUT_MS);
assertThat(player.addMediaItemsWithIndexCalled).isTrue();
assertThat(player.index).isEqualTo(index); assertThat(player.index).isEqualTo(index);
assertThat(player.mediaItems).isEqualTo(mediaItems); assertThat(player.mediaItems).isEqualTo(mediaItems);
} }
@ -371,8 +363,7 @@ public class MediaSessionPlayerTest {
controller.removeMediaItem(index); controller.removeMediaItem(index);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_REMOVE_MEDIA_ITEM, TIMEOUT_MS);
assertThat(player.removeMediaItemCalled).isTrue();
assertThat(player.index).isEqualTo(index); assertThat(player.index).isEqualTo(index);
} }
@ -383,8 +374,7 @@ public class MediaSessionPlayerTest {
controller.removeMediaItems(fromIndex, toIndex); controller.removeMediaItems(fromIndex, toIndex);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_REMOVE_MEDIA_ITEMS, TIMEOUT_MS);
assertThat(player.removeMediaItemsCalled).isTrue();
assertThat(player.fromIndex).isEqualTo(fromIndex); assertThat(player.fromIndex).isEqualTo(fromIndex);
assertThat(player.toIndex).isEqualTo(toIndex); assertThat(player.toIndex).isEqualTo(toIndex);
} }
@ -393,8 +383,7 @@ public class MediaSessionPlayerTest {
public void clearMediaItems() throws Exception { public void clearMediaItems() throws Exception {
controller.clearMediaItems(); controller.clearMediaItems();
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_CLEAR_MEDIA_ITEMS, TIMEOUT_MS);
assertThat(player.clearMediaItemsCalled).isTrue();
} }
@Test @Test
@ -404,8 +393,7 @@ public class MediaSessionPlayerTest {
controller.moveMediaItem(index, newIndex); controller.moveMediaItem(index, newIndex);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_MOVE_MEDIA_ITEM, TIMEOUT_MS);
assertThat(player.moveMediaItemCalled).isTrue();
assertThat(player.index).isEqualTo(index); assertThat(player.index).isEqualTo(index);
assertThat(player.newIndex).isEqualTo(newIndex); assertThat(player.newIndex).isEqualTo(newIndex);
} }
@ -418,8 +406,7 @@ public class MediaSessionPlayerTest {
controller.moveMediaItems(fromIndex, toIndex, newIndex); controller.moveMediaItems(fromIndex, toIndex, newIndex);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_MOVE_MEDIA_ITEMS, TIMEOUT_MS);
assertThat(player.moveMediaItemsCalled).isTrue();
assertThat(player.fromIndex).isEqualTo(fromIndex); assertThat(player.fromIndex).isEqualTo(fromIndex);
assertThat(player.toIndex).isEqualTo(toIndex); assertThat(player.toIndex).isEqualTo(toIndex);
assertThat(player.newIndex).isEqualTo(newIndex); assertThat(player.newIndex).isEqualTo(newIndex);
@ -428,68 +415,69 @@ public class MediaSessionPlayerTest {
@Test @Test
public void seekToPreviousMediaItem() throws Exception { public void seekToPreviousMediaItem() throws Exception {
controller.seekToPreviousMediaItem(); controller.seekToPreviousMediaItem();
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.seekToPreviousMediaItemCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO_PREVIOUS_MEDIA_ITEM, TIMEOUT_MS);
} }
@Test @Test
public void seekToNextMediaItem() throws Exception { public void seekToNextMediaItem() throws Exception {
controller.seekToNextMediaItem(); controller.seekToNextMediaItem();
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.seekToNextMediaItemCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO_NEXT_MEDIA_ITEM, TIMEOUT_MS);
} }
@Test @Test
public void seekToPrevious() throws Exception { public void seekToPrevious() throws Exception {
controller.seekToPrevious(); controller.seekToPrevious();
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.seekToPreviousCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO_PREVIOUS, TIMEOUT_MS);
} }
@Test @Test
public void seekToNext() throws Exception { public void seekToNext() throws Exception {
controller.seekToNext(); controller.seekToNext();
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.seekToNextCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO_NEXT, TIMEOUT_MS);
} }
@Test @Test
public void setShuffleModeEnabled() throws Exception { public void setShuffleModeEnabled() throws Exception {
boolean testShuffleModeEnabled = true; boolean testShuffleModeEnabled = true;
controller.setShuffleModeEnabled(testShuffleModeEnabled);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.setShuffleModeCalled).isTrue(); controller.setShuffleModeEnabled(testShuffleModeEnabled);
player.awaitMethodCalled(MockPlayer.METHOD_SET_SHUFFLE_MODE, TIMEOUT_MS);
assertThat(player.shuffleModeEnabled).isEqualTo(testShuffleModeEnabled); assertThat(player.shuffleModeEnabled).isEqualTo(testShuffleModeEnabled);
} }
@Test @Test
public void setRepeatMode() throws Exception { public void setRepeatMode() throws Exception {
int testRepeatMode = Player.REPEAT_MODE_ALL; int testRepeatMode = Player.REPEAT_MODE_ALL;
controller.setRepeatMode(testRepeatMode);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.setRepeatModeCalled).isTrue(); controller.setRepeatMode(testRepeatMode);
player.awaitMethodCalled(MockPlayer.METHOD_SET_REPEAT_MODE, TIMEOUT_MS);
assertThat(player.repeatMode).isEqualTo(testRepeatMode); assertThat(player.repeatMode).isEqualTo(testRepeatMode);
} }
@Test @Test
public void setVolume() throws Exception { public void setVolume() throws Exception {
float testVolume = .123f; float testVolume = .123f;
controller.setVolume(testVolume); controller.setVolume(testVolume);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.setVolumeCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SET_VOLUME, TIMEOUT_MS);
assertThat(player.volume).isEqualTo(testVolume); assertThat(player.volume).isEqualTo(testVolume);
} }
@Test @Test
public void setDeviceVolume() throws Exception { public void setDeviceVolume() throws Exception {
changePlaybackTypeToRemote(); changePlaybackTypeToRemote();
int testVolume = 12; int testVolume = 12;
controller.setDeviceVolume(testVolume); controller.setDeviceVolume(testVolume);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.setDeviceVolumeCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SET_DEVICE_VOLUME, TIMEOUT_MS);
assertThat(player.deviceVolume).isEqualTo(testVolume); assertThat(player.deviceVolume).isEqualTo(testVolume);
} }
@ -499,8 +487,7 @@ public class MediaSessionPlayerTest {
controller.increaseDeviceVolume(); controller.increaseDeviceVolume();
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_INCREASE_DEVICE_VOLUME, TIMEOUT_MS);
assertThat(player.increaseDeviceVolumeCalled).isTrue();
} }
@Test @Test
@ -509,16 +496,16 @@ public class MediaSessionPlayerTest {
controller.decreaseDeviceVolume(); controller.decreaseDeviceVolume();
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_DECREASE_DEVICE_VOLUME, TIMEOUT_MS);
assertThat(player.decreaseDeviceVolumeCalled).isTrue();
} }
@Test @Test
public void setDeviceMuted() throws Exception { public void setDeviceMuted() throws Exception {
player.deviceMuted = false; player.deviceMuted = false;
controller.setDeviceMuted(true); controller.setDeviceMuted(true);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(player.setDeviceMutedCalled).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SET_DEVICE_MUTED, TIMEOUT_MS);
assertThat(player.deviceMuted).isTrue(); assertThat(player.deviceMuted).isTrue();
} }
@ -526,16 +513,14 @@ public class MediaSessionPlayerTest {
public void seekBack() throws Exception { public void seekBack() throws Exception {
controller.seekBack(); controller.seekBack();
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SEEK_BACK, TIMEOUT_MS);
assertThat(player.seekBackCalled).isTrue();
} }
@Test @Test
public void seekForward() throws Exception { public void seekForward() throws Exception {
controller.seekForward(); controller.seekForward();
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SEEK_FORWARD, TIMEOUT_MS);
assertThat(player.seekForwardCalled).isTrue();
} }
@Test @Test
@ -545,8 +530,7 @@ public class MediaSessionPlayerTest {
controller.setTrackSelectionParameters(trackSelectionParameters); controller.setTrackSelectionParameters(trackSelectionParameters);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SET_TRACK_SELECTION_PARAMETERS, TIMEOUT_MS);
assertThat(player.setTrackSelectionParametersCalled).isTrue();
assertThat(player.trackSelectionParameters).isEqualTo(trackSelectionParameters); assertThat(player.trackSelectionParameters).isEqualTo(trackSelectionParameters);
} }

View File

@ -47,6 +47,7 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.ClassRule; import org.junit.ClassRule;
import org.junit.Rule; import org.junit.Rule;
@ -78,8 +79,7 @@ public class MediaSessionTest {
public void setUp() throws Exception { public void setUp() throws Exception {
context = ApplicationProvider.getApplicationContext(); context = ApplicationProvider.getApplicationContext();
handler = threadTestRule.getHandler(); handler = threadTestRule.getHandler();
player = player = new MockPlayer.Builder().setApplicationLooper(handler.getLooper()).build();
new MockPlayer.Builder().setLatchCount(1).setApplicationLooper(handler.getLooper()).build();
session = session =
sessionTestRule.ensureReleaseAfterTest( sessionTestRule.ensureReleaseAfterTest(
@ -107,6 +107,16 @@ public class MediaSessionTest {
.get(TIMEOUT_MS, MILLISECONDS); .get(TIMEOUT_MS, MILLISECONDS);
} }
@After
public void tearDown() throws Exception {
if ((controller != null)) {
threadTestRule.getHandler().postAndSync(() -> controller.release());
}
if (session != null) {
session.release();
}
}
@Test @Test
public void builder() { public void builder() {
MediaSession.Builder builder; MediaSession.Builder builder;
@ -394,8 +404,7 @@ public class MediaSessionTest {
long testSeekPositionMs = 1234; long testSeekPositionMs = 1234;
controllerCompat.getTransportControls().seekTo(testSeekPositionMs); controllerCompat.getTransportControls().seekTo(testSeekPositionMs);
assertThat(player.countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO, TIMEOUT_MS);
assertThat(player.seekToCalled).isTrue();
assertThat(player.seekPositionMs).isEqualTo(testSeekPositionMs); assertThat(player.seekPositionMs).isEqualTo(testSeekPositionMs);
} }

View File

@ -44,81 +44,98 @@ public class MockPlayerTest {
@Test @Test
public void play() { public void play() {
player.play(); player.play();
assertThat(player.playCalled).isTrue();
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_PLAY)).isTrue();
} }
@Test @Test
public void pause() { public void pause() {
player.pause(); player.pause();
assertThat(player.pauseCalled).isTrue();
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_PAUSE)).isTrue();
} }
@Test @Test
public void prepare() { public void prepare() {
player.prepare(); player.prepare();
assertThat(player.prepareCalled).isTrue();
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_PREPARE)).isTrue();
} }
@Test @Test
public void stop() { public void stop() {
player.stop(); player.stop();
assertThat(player.stopCalled).isTrue();
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_STOP)).isTrue();
} }
@Test @Test
public void release() { public void release() {
player.release(); player.release();
assertThat(player.releaseCalled).isTrue();
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_RELEASE)).isTrue();
} }
@Test @Test
public void setPlayWhenReady() { public void setPlayWhenReady() {
boolean testPlayWhenReady = false; boolean testPlayWhenReady = false;
player.setPlayWhenReady(testPlayWhenReady); player.setPlayWhenReady(testPlayWhenReady);
assertThat(player.setPlayWhenReadyCalled).isTrue();
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_SET_PLAY_WHEN_READY)).isTrue();
} }
@Test @Test
public void seekTo() { public void seekTo() {
long pos = 1004L; long pos = 1004L;
player.seekTo(pos); player.seekTo(pos);
assertThat(player.seekToCalled).isTrue();
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_SEEK_TO)).isTrue();
assertThat(player.seekPositionMs).isEqualTo(pos); assertThat(player.seekPositionMs).isEqualTo(pos);
} }
@Test @Test
public void seekBack() { public void seekBack() {
player.seekBack(); player.seekBack();
assertThat(player.seekBackCalled).isTrue();
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_SEEK_BACK)).isTrue();
} }
@Test @Test
public void seekForward() { public void seekForward() {
player.seekForward(); player.seekForward();
assertThat(player.seekForwardCalled).isTrue();
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_SEEK_FORWARD)).isTrue();
} }
@Test @Test
public void setPlaybackParameters() { public void setPlaybackParameters() {
PlaybackParameters playbackParameters = new PlaybackParameters(/* speed= */ 1.5f); PlaybackParameters playbackParameters = new PlaybackParameters(/* speed= */ 1.5f);
player.setPlaybackParameters(playbackParameters); player.setPlaybackParameters(playbackParameters);
assertThat(player.setPlaybackParametersCalled).isTrue();
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_SET_PLAYBACK_PARAMETERS)).isTrue();
assertThat(player.playbackParameters).isEqualTo(playbackParameters); assertThat(player.playbackParameters).isEqualTo(playbackParameters);
} }
@Test @Test
public void setPlaybackSpeed() { public void setPlaybackSpeed() {
float speed = 1.5f; float speed = 1.5f;
player.setPlaybackSpeed(speed); player.setPlaybackSpeed(speed);
assertThat(player.setPlaybackSpeedCalled).isTrue();
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_SET_PLAYBACK_SPEED)).isTrue();
assertThat(player.playbackParameters.speed).isEqualTo(speed); assertThat(player.playbackParameters.speed).isEqualTo(speed);
} }
@Test @Test
public void setMediaItem() { public void setMediaItem() {
MediaItem mediaItem = MediaTestUtils.createMediaItem("setMediaItem"); MediaItem mediaItem = MediaTestUtils.createMediaItem("setMediaItem");
player.setMediaItem(mediaItem); player.setMediaItem(mediaItem);
assertThat(player.setMediaItemCalled).isTrue();
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_SET_MEDIA_ITEM)).isTrue();
assertThat(player.mediaItem).isSameInstanceAs(mediaItem); assertThat(player.mediaItem).isSameInstanceAs(mediaItem);
} }
@ -126,8 +143,11 @@ public class MockPlayerTest {
public void setMediaItem_withStartPosition() { public void setMediaItem_withStartPosition() {
MediaItem mediaItem = MediaTestUtils.createMediaItem("setMediaItem"); MediaItem mediaItem = MediaTestUtils.createMediaItem("setMediaItem");
long startPositionMs = 321L; long startPositionMs = 321L;
player.setMediaItem(mediaItem, startPositionMs); player.setMediaItem(mediaItem, startPositionMs);
assertThat(player.setMediaItemWithStartPositionCalled).isTrue();
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_SET_MEDIA_ITEM_WITH_START_POSITION))
.isTrue();
assertThat(player.startPositionMs).isEqualTo(startPositionMs); assertThat(player.startPositionMs).isEqualTo(startPositionMs);
assertThat(player.mediaItem).isSameInstanceAs(mediaItem); assertThat(player.mediaItem).isSameInstanceAs(mediaItem);
} }
@ -136,8 +156,11 @@ public class MockPlayerTest {
public void setMediaItem_withResetPosition() { public void setMediaItem_withResetPosition() {
MediaItem mediaItem = MediaTestUtils.createMediaItem("setMediaItem"); MediaItem mediaItem = MediaTestUtils.createMediaItem("setMediaItem");
boolean resetPosition = true; boolean resetPosition = true;
player.setMediaItem(mediaItem, resetPosition); player.setMediaItem(mediaItem, resetPosition);
assertThat(player.setMediaItemWithResetPositionCalled).isTrue();
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_SET_MEDIA_ITEM_WITH_RESET_POSITION))
.isTrue();
assertThat(player.resetPosition).isEqualTo(resetPosition); assertThat(player.resetPosition).isEqualTo(resetPosition);
assertThat(player.mediaItem).isEqualTo(mediaItem); assertThat(player.mediaItem).isEqualTo(mediaItem);
} }
@ -145,8 +168,10 @@ public class MockPlayerTest {
@Test @Test
public void setMediaItems() { public void setMediaItems() {
List<MediaItem> list = MediaTestUtils.createMediaItems(/* size= */ 2); List<MediaItem> list = MediaTestUtils.createMediaItems(/* size= */ 2);
player.setMediaItems(list); player.setMediaItems(list);
assertThat(player.setMediaItemsCalled).isTrue();
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS)).isTrue();
assertThat(player.mediaItems).isSameInstanceAs(list); assertThat(player.mediaItems).isSameInstanceAs(list);
} }
@ -154,8 +179,11 @@ public class MockPlayerTest {
public void setMediaItems_withResetPosition() { public void setMediaItems_withResetPosition() {
List<MediaItem> list = MediaTestUtils.createMediaItems(/* size= */ 2); List<MediaItem> list = MediaTestUtils.createMediaItems(/* size= */ 2);
boolean resetPosition = true; boolean resetPosition = true;
player.setMediaItems(list, resetPosition); player.setMediaItems(list, resetPosition);
assertThat(player.setMediaItemsWithResetPositionCalled).isTrue();
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS_WITH_RESET_POSITION))
.isTrue();
assertThat(player.resetPosition).isEqualTo(resetPosition); assertThat(player.resetPosition).isEqualTo(resetPosition);
assertThat(player.mediaItems).isSameInstanceAs(list); assertThat(player.mediaItems).isSameInstanceAs(list);
} }
@ -165,8 +193,11 @@ public class MockPlayerTest {
List<MediaItem> list = MediaTestUtils.createMediaItems(/* size= */ 2); List<MediaItem> list = MediaTestUtils.createMediaItems(/* size= */ 2);
int startWindowIndex = 3; int startWindowIndex = 3;
long startPositionMs = 132L; long startPositionMs = 132L;
player.setMediaItems(list, startWindowIndex, startPositionMs); player.setMediaItems(list, startWindowIndex, startPositionMs);
assertThat(player.setMediaItemsWithStartIndexCalled).isTrue();
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS_WITH_START_INDEX))
.isTrue();
assertThat(player.startMediaItemIndex).isEqualTo(startWindowIndex); assertThat(player.startMediaItemIndex).isEqualTo(startWindowIndex);
assertThat(player.startPositionMs).isEqualTo(startPositionMs); assertThat(player.startPositionMs).isEqualTo(startPositionMs);
assertThat(player.mediaItems).isSameInstanceAs(list); assertThat(player.mediaItems).isSameInstanceAs(list);
@ -176,8 +207,10 @@ public class MockPlayerTest {
public void setMediaItems_withDuplicatedItems() { public void setMediaItems_withDuplicatedItems() {
List<MediaItem> list = MediaTestUtils.createMediaItems(/* size= */ 4); List<MediaItem> list = MediaTestUtils.createMediaItems(/* size= */ 4);
list.set(2, list.get(1)); list.set(2, list.get(1));
player.setMediaItems(list); player.setMediaItems(list);
assertThat(player.setMediaItemsCalled).isTrue();
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS)).isTrue();
assertThat(player.mediaItems).isSameInstanceAs(list); assertThat(player.mediaItems).isSameInstanceAs(list);
} }
@ -187,7 +220,7 @@ public class MockPlayerTest {
player.setPlaylistMetadata(playlistMetadata); player.setPlaylistMetadata(playlistMetadata);
assertThat(player.setPlaylistMetadataCalled).isTrue(); assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_SET_PLAYLIST_METADATA)).isTrue();
assertThat(player.playlistMetadata).isSameInstanceAs(playlistMetadata); assertThat(player.playlistMetadata).isSameInstanceAs(playlistMetadata);
} }
@ -197,7 +230,7 @@ public class MockPlayerTest {
player.addMediaItem(mediaItem); player.addMediaItem(mediaItem);
assertThat(player.addMediaItemCalled).isTrue(); assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_ADD_MEDIA_ITEM)).isTrue();
assertThat(player.mediaItem).isSameInstanceAs(mediaItem); assertThat(player.mediaItem).isSameInstanceAs(mediaItem);
} }
@ -208,7 +241,7 @@ public class MockPlayerTest {
player.addMediaItem(index, mediaItem); player.addMediaItem(index, mediaItem);
assertThat(player.addMediaItemWithIndexCalled).isTrue(); assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_ADD_MEDIA_ITEM_WITH_INDEX)).isTrue();
assertThat(player.index).isEqualTo(index); assertThat(player.index).isEqualTo(index);
assertThat(player.mediaItem).isSameInstanceAs(mediaItem); assertThat(player.mediaItem).isSameInstanceAs(mediaItem);
} }
@ -221,7 +254,7 @@ public class MockPlayerTest {
player.addMediaItems(index, mediaItems); player.addMediaItems(index, mediaItems);
assertThat(player.addMediaItemsWithIndexCalled).isTrue(); assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_ADD_MEDIA_ITEMS_WITH_INDEX)).isTrue();
assertThat(player.index).isEqualTo(index); assertThat(player.index).isEqualTo(index);
assertThat(player.mediaItems).isSameInstanceAs(mediaItems); assertThat(player.mediaItems).isSameInstanceAs(mediaItems);
} }
@ -234,7 +267,7 @@ public class MockPlayerTest {
player.addMediaItems(index, mediaItems); player.addMediaItems(index, mediaItems);
assertThat(player.addMediaItemsWithIndexCalled).isTrue(); assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_ADD_MEDIA_ITEMS_WITH_INDEX)).isTrue();
assertThat(player.index).isEqualTo(index); assertThat(player.index).isEqualTo(index);
assertThat(player.mediaItems).isSameInstanceAs(mediaItems); assertThat(player.mediaItems).isSameInstanceAs(mediaItems);
} }
@ -245,7 +278,7 @@ public class MockPlayerTest {
player.removeMediaItem(index); player.removeMediaItem(index);
assertThat(player.removeMediaItemCalled).isTrue(); assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_REMOVE_MEDIA_ITEM)).isTrue();
assertThat(player.index).isEqualTo(index); assertThat(player.index).isEqualTo(index);
} }
@ -256,7 +289,7 @@ public class MockPlayerTest {
player.removeMediaItems(fromIndex, toIndex); player.removeMediaItems(fromIndex, toIndex);
assertThat(player.removeMediaItemsCalled).isTrue(); assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_REMOVE_MEDIA_ITEMS)).isTrue();
assertThat(player.fromIndex).isEqualTo(fromIndex); assertThat(player.fromIndex).isEqualTo(fromIndex);
assertThat(player.toIndex).isEqualTo(toIndex); assertThat(player.toIndex).isEqualTo(toIndex);
} }
@ -265,7 +298,7 @@ public class MockPlayerTest {
public void clearMediaItems() { public void clearMediaItems() {
player.clearMediaItems(); player.clearMediaItems();
assertThat(player.clearMediaItemsCalled).isTrue(); assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_CLEAR_MEDIA_ITEMS)).isTrue();
} }
@Test @Test
@ -275,7 +308,7 @@ public class MockPlayerTest {
player.moveMediaItem(index, newIndex); player.moveMediaItem(index, newIndex);
assertThat(player.moveMediaItemCalled).isTrue(); assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_MOVE_MEDIA_ITEM)).isTrue();
assertThat(player.index).isEqualTo(index); assertThat(player.index).isEqualTo(index);
assertThat(player.newIndex).isEqualTo(newIndex); assertThat(player.newIndex).isEqualTo(newIndex);
} }
@ -288,7 +321,7 @@ public class MockPlayerTest {
player.moveMediaItems(fromIndex, toIndex, newIndex); player.moveMediaItems(fromIndex, toIndex, newIndex);
assertThat(player.moveMediaItemsCalled).isTrue(); assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_MOVE_MEDIA_ITEMS)).isTrue();
assertThat(player.fromIndex).isEqualTo(fromIndex); assertThat(player.fromIndex).isEqualTo(fromIndex);
assertThat(player.toIndex).isEqualTo(toIndex); assertThat(player.toIndex).isEqualTo(toIndex);
assertThat(player.newIndex).isEqualTo(newIndex); assertThat(player.newIndex).isEqualTo(newIndex);
@ -297,76 +330,92 @@ public class MockPlayerTest {
@Test @Test
public void seekToPreviousMediaItem() { public void seekToPreviousMediaItem() {
player.seekToPreviousMediaItem(); player.seekToPreviousMediaItem();
assertThat(player.seekToPreviousMediaItemCalled).isTrue();
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_SEEK_TO_PREVIOUS_MEDIA_ITEM)).isTrue();
} }
@Test @Test
public void seekToNextMediaItem() { public void seekToNextMediaItem() {
player.seekToNextMediaItem(); player.seekToNextMediaItem();
assertThat(player.seekToNextMediaItemCalled).isTrue();
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_SEEK_TO_NEXT_MEDIA_ITEM)).isTrue();
} }
@Test @Test
public void seekToPrevious() { public void seekToPrevious() {
player.seekToPrevious(); player.seekToPrevious();
assertThat(player.seekToPreviousCalled).isTrue();
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_SEEK_TO_PREVIOUS)).isTrue();
} }
@Test @Test
public void seekToNext() { public void seekToNext() {
player.seekToNext(); player.seekToNext();
assertThat(player.seekToNextCalled).isTrue();
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_SEEK_TO_NEXT)).isTrue();
} }
@Test @Test
public void setShuffleModeEnabled() { public void setShuffleModeEnabled() {
boolean testShuffleModeEnabled = true; boolean testShuffleModeEnabled = true;
player.setShuffleModeEnabled(testShuffleModeEnabled); player.setShuffleModeEnabled(testShuffleModeEnabled);
assertThat(player.setShuffleModeCalled).isTrue();
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_SET_SHUFFLE_MODE)).isTrue();
assertThat(player.shuffleModeEnabled).isEqualTo(testShuffleModeEnabled); assertThat(player.shuffleModeEnabled).isEqualTo(testShuffleModeEnabled);
} }
@Test @Test
public void setRepeatMode() { public void setRepeatMode() {
int testRepeatMode = Player.REPEAT_MODE_ALL; int testRepeatMode = Player.REPEAT_MODE_ALL;
player.setRepeatMode(testRepeatMode); player.setRepeatMode(testRepeatMode);
assertThat(player.setRepeatModeCalled).isTrue();
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_SET_REPEAT_MODE)).isTrue();
assertThat(player.repeatMode).isEqualTo(testRepeatMode); assertThat(player.repeatMode).isEqualTo(testRepeatMode);
} }
@Test @Test
public void setVolume() { public void setVolume() {
float testVolume = .123f; float testVolume = .123f;
player.setVolume(testVolume); player.setVolume(testVolume);
assertThat(player.setVolumeCalled).isTrue();
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_SET_VOLUME)).isTrue();
assertThat(player.volume).isEqualTo(testVolume); assertThat(player.volume).isEqualTo(testVolume);
} }
@Test @Test
public void setDeviceVolume() { public void setDeviceVolume() {
int testVolume = 12; int testVolume = 12;
player.setDeviceVolume(testVolume); player.setDeviceVolume(testVolume);
assertThat(player.setDeviceVolumeCalled).isTrue();
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_SET_DEVICE_VOLUME)).isTrue();
assertThat(player.deviceVolume).isEqualTo(testVolume); assertThat(player.deviceVolume).isEqualTo(testVolume);
} }
@Test @Test
public void increaseDeviceVolume() { public void increaseDeviceVolume() {
player.increaseDeviceVolume(); player.increaseDeviceVolume();
assertThat(player.increaseDeviceVolumeCalled).isTrue();
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_INCREASE_DEVICE_VOLUME)).isTrue();
} }
@Test @Test
public void decreaseDeviceVolume() { public void decreaseDeviceVolume() {
player.decreaseDeviceVolume(); player.decreaseDeviceVolume();
assertThat(player.decreaseDeviceVolumeCalled).isTrue();
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_DECREASE_DEVICE_VOLUME)).isTrue();
} }
@Test @Test
public void setDeviceMuted() { public void setDeviceMuted() {
player.deviceMuted = false; player.deviceMuted = false;
player.setDeviceMuted(true); player.setDeviceMuted(true);
assertThat(player.setDeviceMutedCalled).isTrue();
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_SET_DEVICE_MUTED)).isTrue();
assertThat(player.deviceMuted).isTrue(); assertThat(player.deviceMuted).isTrue();
} }
@ -377,7 +426,8 @@ public class MockPlayerTest {
player.setTrackSelectionParameters(trackSelectionParameters); player.setTrackSelectionParameters(trackSelectionParameters);
assertThat(player.setTrackSelectionParametersCalled).isTrue(); assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_SET_TRACK_SELECTION_PARAMETERS))
.isTrue();
assertThat(player.trackSelectionParameters).isSameInstanceAs(trackSelectionParameters); assertThat(player.trackSelectionParameters).isSameInstanceAs(trackSelectionParameters);
} }
} }

View File

@ -134,8 +134,7 @@ public class MockMediaLibraryService extends MediaLibraryService {
return (MediaLibrarySession) onGetSessionHandler.onGetSession(controllerInfo); return (MediaLibrarySession) onGetSessionHandler.onGetSession(controllerInfo);
} }
MockPlayer player = MockPlayer player = new MockPlayer.Builder().setApplicationLooper(handler.getLooper()).build();
new MockPlayer.Builder().setLatchCount(1).setApplicationLooper(handler.getLooper()).build();
MediaLibrarySessionCallback callback = registry.getSessionCallback(); MediaLibrarySessionCallback callback = registry.getSessionCallback();
session = session =

View File

@ -15,11 +15,15 @@
*/ */
package androidx.media3.session; package androidx.media3.session;
import static androidx.media3.common.util.Assertions.checkNotNull;
import static java.lang.annotation.ElementType.TYPE_USE;
import android.os.Looper; import android.os.Looper;
import android.view.Surface; import android.view.Surface;
import android.view.SurfaceHolder; import android.view.SurfaceHolder;
import android.view.SurfaceView; import android.view.SurfaceView;
import android.view.TextureView; import android.view.TextureView;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.collection.ArraySet; import androidx.collection.ArraySet;
import androidx.media3.common.AudioAttributes; import androidx.media3.common.AudioAttributes;
@ -37,20 +41,162 @@ import androidx.media3.common.TrackSelectionParameters;
import androidx.media3.common.TracksInfo; import androidx.media3.common.TracksInfo;
import androidx.media3.common.VideoSize; import androidx.media3.common.VideoSize;
import androidx.media3.common.text.Cue; import androidx.media3.common.text.Cue;
import androidx.media3.common.util.ConditionVariable;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util; import androidx.media3.common.util.Util;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.List; import java.util.List;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeoutException;
/** A mock implementation of {@link Player} for testing. */ /** A mock implementation of {@link Player} for testing. */
@UnstableApi @UnstableApi
public class MockPlayer implements Player { public class MockPlayer implements Player {
public final CountDownLatch countDownLatch; /** Player methods. */
@Documented
@Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({
METHOD_ADD_MEDIA_ITEM,
METHOD_ADD_MEDIA_ITEMS,
METHOD_ADD_MEDIA_ITEM_WITH_INDEX,
METHOD_ADD_MEDIA_ITEMS_WITH_INDEX,
METHOD_CLEAR_MEDIA_ITEMS,
METHOD_DECREASE_DEVICE_VOLUME,
METHOD_INCREASE_DEVICE_VOLUME,
METHOD_MOVE_MEDIA_ITEM,
METHOD_MOVE_MEDIA_ITEMS,
METHOD_PAUSE,
METHOD_PLAY,
METHOD_PREPARE,
METHOD_RELEASE,
METHOD_REMOVE_MEDIA_ITEM,
METHOD_REMOVE_MEDIA_ITEMS,
METHOD_SEEK_BACK,
METHOD_SEEK_FORWARD,
METHOD_SEEK_TO,
METHOD_SEEK_TO_DEFAULT_POSITION,
METHOD_SEEK_TO_DEFAULT_POSITION_WITH_MEDIA_ITEM_INDEX,
METHOD_SEEK_TO_NEXT,
METHOD_SEEK_TO_NEXT_MEDIA_ITEM,
METHOD_SEEK_TO_PREVIOUS,
METHOD_SEEK_TO_PREVIOUS_MEDIA_ITEM,
METHOD_SEEK_TO_WITH_MEDIA_ITEM_INDEX,
METHOD_SET_DEVICE_MUTED,
METHOD_SET_DEVICE_VOLUME,
METHOD_SET_MEDIA_ITEM,
METHOD_SET_MEDIA_ITEM_WITH_RESET_POSITION,
METHOD_SET_MEDIA_ITEM_WITH_START_POSITION,
METHOD_SET_MEDIA_ITEMS,
METHOD_SET_MEDIA_ITEMS_WITH_RESET_POSITION,
METHOD_SET_MEDIA_ITEMS_WITH_START_INDEX,
METHOD_SET_PLAY_WHEN_READY,
METHOD_SET_PLAYBACK_PARAMETERS,
METHOD_SET_PLAYBACK_SPEED,
METHOD_SET_PLAYLIST_METADATA,
METHOD_SET_REPEAT_MODE,
METHOD_SET_SHUFFLE_MODE,
METHOD_SET_TRACK_SELECTION_PARAMETERS,
METHOD_SET_VOLUME,
METHOD_STOP
})
public @interface Method {}
/** Maps to {@link Player#addMediaItem(MediaItem)}. */
public static final int METHOD_ADD_MEDIA_ITEM = 0;
/** Maps to {@link Player#addMediaItems(List)}. */
public static final int METHOD_ADD_MEDIA_ITEMS = 1;
/** Maps to {@link Player#addMediaItem(int, MediaItem)}. */
public static final int METHOD_ADD_MEDIA_ITEM_WITH_INDEX = 2;
/** Maps to {@link Player#addMediaItems(int, List)}. */
public static final int METHOD_ADD_MEDIA_ITEMS_WITH_INDEX = 3;
/** Maps to {@link Player#clearMediaItems()}. */
public static final int METHOD_CLEAR_MEDIA_ITEMS = 4;
/** Maps to {@link Player#decreaseDeviceVolume()}. */
public static final int METHOD_DECREASE_DEVICE_VOLUME = 5;
/** Maps to {@link Player#increaseDeviceVolume()}. */
public static final int METHOD_INCREASE_DEVICE_VOLUME = 6;
/** Maps to {@link Player#moveMediaItem(int, int)}. */
public static final int METHOD_MOVE_MEDIA_ITEM = 7;
/** Maps to {@link Player#moveMediaItems(int, int, int)}. */
public static final int METHOD_MOVE_MEDIA_ITEMS = 8;
/** Maps to {@link Player#pause()}. */
public static final int METHOD_PAUSE = 9;
/** Maps to {@link Player#play()}. */
public static final int METHOD_PLAY = 10;
/** Maps to {@link Player#prepare()}. */
public static final int METHOD_PREPARE = 11;
/** Maps to {@link Player#release()}. */
public static final int METHOD_RELEASE = 12;
/** Maps to {@link Player#removeMediaItem(int)}. */
public static final int METHOD_REMOVE_MEDIA_ITEM = 13;
/** Maps to {@link Player#removeMediaItems(int, int)}. */
public static final int METHOD_REMOVE_MEDIA_ITEMS = 14;
/** Maps to {@link Player#seekBack()}. */
public static final int METHOD_SEEK_BACK = 15;
/** Maps to {@link Player#seekForward()}. */
public static final int METHOD_SEEK_FORWARD = 16;
/** Maps to {@link Player#seekTo(long)}. */
public static final int METHOD_SEEK_TO = 17;
/** Maps to {@link Player#seekToDefaultPosition()}. */
public static final int METHOD_SEEK_TO_DEFAULT_POSITION = 18;
/** Maps to {@link Player#seekToDefaultPosition(int)}. */
public static final int METHOD_SEEK_TO_DEFAULT_POSITION_WITH_MEDIA_ITEM_INDEX = 19;
/** Maps to {@link Player#seekToNext()}. */
public static final int METHOD_SEEK_TO_NEXT = 20;
/** Maps to {@link Player#seekToNextMediaItem()}. */
public static final int METHOD_SEEK_TO_NEXT_MEDIA_ITEM = 21;
/** Maps to {@link Player#seekToPrevious()}. */
public static final int METHOD_SEEK_TO_PREVIOUS = 22;
/** Maps to {@link Player#seekToPreviousMediaItem()}. */
public static final int METHOD_SEEK_TO_PREVIOUS_MEDIA_ITEM = 23;
/** Maps to {@link Player#seekTo(int, long)}. */
public static final int METHOD_SEEK_TO_WITH_MEDIA_ITEM_INDEX = 24;
/** Maps to {@link Player#setDeviceMuted(boolean)}. */
public static final int METHOD_SET_DEVICE_MUTED = 25;
/** Maps to {@link Player#setDeviceVolume(int)}. */
public static final int METHOD_SET_DEVICE_VOLUME = 26;
/** Maps to {@link Player#setMediaItem(MediaItem)}. */
public static final int METHOD_SET_MEDIA_ITEM = 27;
/** Maps to {@link Player#setMediaItem(MediaItem, boolean)}. */
public static final int METHOD_SET_MEDIA_ITEM_WITH_RESET_POSITION = 28;
/** Maps to {@link Player#setMediaItem(MediaItem, long)}. */
public static final int METHOD_SET_MEDIA_ITEM_WITH_START_POSITION = 29;
/** Maps to {@link Player#setMediaItems(List)}. */
public static final int METHOD_SET_MEDIA_ITEMS = 30;
/** Maps to {@link Player#setMediaItems(List, boolean)}. */
public static final int METHOD_SET_MEDIA_ITEMS_WITH_RESET_POSITION = 31;
/** Maps to {@link Player#setMediaItems(List, int, long)}. */
public static final int METHOD_SET_MEDIA_ITEMS_WITH_START_INDEX = 32;
/** Maps to {@link Player#setPlayWhenReady(boolean)}. */
public static final int METHOD_SET_PLAY_WHEN_READY = 33;
/** Maps to {@link Player#setPlaybackParameters(PlaybackParameters)}. */
public static final int METHOD_SET_PLAYBACK_PARAMETERS = 34;
/** Maps to {@link Player#setPlaybackSpeed(float)}. */
public static final int METHOD_SET_PLAYBACK_SPEED = 35;
/** Maps to {@link Player#setPlaylistMetadata(MediaMetadata)}. */
public static final int METHOD_SET_PLAYLIST_METADATA = 36;
/** Maps to {@link Player#setRepeatMode(int)}. */
public static final int METHOD_SET_REPEAT_MODE = 37;
/** Maps to {@link Player#setShuffleModeEnabled(boolean)}. */
public static final int METHOD_SET_SHUFFLE_MODE = 38;
/** Maps to {@link Player#setTrackSelectionParameters(TrackSelectionParameters)}. */
public static final int METHOD_SET_TRACK_SELECTION_PARAMETERS = 39;
/** Maps to {@link Player#setVolume(float)}. */
public static final int METHOD_SET_VOLUME = 40;
/** Maps to {@link Player#stop()}. */
public static final int METHOD_STOP = 41;
private final boolean changePlayerStateWithTransportControl; private final boolean changePlayerStateWithTransportControl;
private final Looper applicationLooper; private final Looper applicationLooper;
private final ArraySet<Listener> listeners = new ArraySet<>(); private final ArraySet<Listener> listeners = new ArraySet<>();
private final ImmutableMap<@Method Integer, ConditionVariable> conditionVariables =
createMethodConditionVariables();
@Nullable PlaybackException playerError; @Nullable PlaybackException playerError;
public AudioAttributes audioAttributes; public AudioAttributes audioAttributes;
@ -106,51 +252,7 @@ public class MockPlayer implements Player {
public long maxSeekToPreviousPositionMs; public long maxSeekToPreviousPositionMs;
public TrackSelectionParameters trackSelectionParameters; public TrackSelectionParameters trackSelectionParameters;
public boolean playCalled;
public boolean pauseCalled;
public boolean prepareCalled;
public boolean stopCalled;
public boolean releaseCalled;
public boolean seekToDefaultPositionCalled;
public boolean seekToDefaultPositionWithMediaItemIndexCalled;
public boolean seekToCalled;
public boolean seekToWithMediaItemIndexCalled;
public boolean setPlaybackSpeedCalled;
public boolean setPlaybackParametersCalled;
public boolean setMediaItemCalled;
public boolean setMediaItemWithStartPositionCalled;
public boolean setMediaItemWithResetPositionCalled;
public boolean setMediaItemsCalled;
public boolean setMediaItemsWithResetPositionCalled;
public boolean setMediaItemsWithStartIndexCalled;
public boolean setPlaylistMetadataCalled;
public boolean addMediaItemCalled;
public boolean addMediaItemWithIndexCalled;
public boolean addMediaItemsCalled;
public boolean addMediaItemsWithIndexCalled;
public boolean removeMediaItemCalled;
public boolean removeMediaItemsCalled;
public boolean clearMediaItemsCalled;
public boolean moveMediaItemCalled;
public boolean moveMediaItemsCalled;
public boolean seekToPreviousMediaItemCalled;
public boolean seekToNextMediaItemCalled;
public boolean seekToPreviousCalled;
public boolean seekToNextCalled;
public boolean setRepeatModeCalled;
public boolean setShuffleModeCalled;
public boolean setVolumeCalled;
public boolean setDeviceVolumeCalled;
public boolean increaseDeviceVolumeCalled;
public boolean decreaseDeviceVolumeCalled;
public boolean setDeviceMutedCalled;
public boolean setPlayWhenReadyCalled;
public boolean seekBackCalled;
public boolean seekForwardCalled;
public boolean setTrackSelectionParametersCalled;
private MockPlayer(Builder builder) { private MockPlayer(Builder builder) {
countDownLatch = new CountDownLatch(builder.latchCount);
changePlayerStateWithTransportControl = builder.changePlayerStateWithTransportControl; changePlayerStateWithTransportControl = builder.changePlayerStateWithTransportControl;
applicationLooper = builder.applicationLooper; applicationLooper = builder.applicationLooper;
@ -203,13 +305,12 @@ public class MockPlayer implements Player {
@Override @Override
public void release() { public void release() {
releaseCalled = true; checkNotNull(conditionVariables.get(METHOD_RELEASE)).open();
} }
@Override @Override
public void stop() { public void stop() {
stopCalled = true; checkNotNull(conditionVariables.get(METHOD_STOP)).open();
countDownLatch.countDown();
} }
@Deprecated @Deprecated
@ -236,8 +337,7 @@ public class MockPlayer implements Player {
@Override @Override
public void play() { public void play() {
playCalled = true; checkNotNull(conditionVariables.get(METHOD_PLAY)).open();
countDownLatch.countDown();
if (changePlayerStateWithTransportControl) { if (changePlayerStateWithTransportControl) {
notifyPlayWhenReadyChanged( notifyPlayWhenReadyChanged(
/* playWhenReady= */ true, Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST); /* playWhenReady= */ true, Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST);
@ -246,8 +346,7 @@ public class MockPlayer implements Player {
@Override @Override
public void pause() { public void pause() {
pauseCalled = true; checkNotNull(conditionVariables.get(METHOD_PAUSE)).open();
countDownLatch.countDown();
if (changePlayerStateWithTransportControl) { if (changePlayerStateWithTransportControl) {
notifyPlayWhenReadyChanged( notifyPlayWhenReadyChanged(
/* playWhenReady= */ false, Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST); /* playWhenReady= */ false, Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST);
@ -256,8 +355,7 @@ public class MockPlayer implements Player {
@Override @Override
public void prepare() { public void prepare() {
prepareCalled = true; checkNotNull(conditionVariables.get(METHOD_PREPARE)).open();
countDownLatch.countDown();
if (changePlayerStateWithTransportControl) { if (changePlayerStateWithTransportControl) {
notifyPlaybackStateChanged(Player.STATE_READY); notifyPlaybackStateChanged(Player.STATE_READY);
} }
@ -265,30 +363,27 @@ public class MockPlayer implements Player {
@Override @Override
public void seekToDefaultPosition() { public void seekToDefaultPosition() {
seekToDefaultPositionCalled = true; checkNotNull(conditionVariables.get(METHOD_SEEK_TO_DEFAULT_POSITION)).open();
countDownLatch.countDown();
} }
@Override @Override
public void seekToDefaultPosition(int mediaItemIndex) { public void seekToDefaultPosition(int mediaItemIndex) {
seekToDefaultPositionWithMediaItemIndexCalled = true;
seekMediaItemIndex = mediaItemIndex; seekMediaItemIndex = mediaItemIndex;
countDownLatch.countDown(); checkNotNull(conditionVariables.get(METHOD_SEEK_TO_DEFAULT_POSITION_WITH_MEDIA_ITEM_INDEX))
.open();
} }
@Override @Override
public void seekTo(long positionMs) { public void seekTo(long positionMs) {
seekToCalled = true;
seekPositionMs = positionMs; seekPositionMs = positionMs;
countDownLatch.countDown(); checkNotNull(conditionVariables.get(METHOD_SEEK_TO)).open();
} }
@Override @Override
public void seekTo(int mediaItemIndex, long positionMs) { public void seekTo(int mediaItemIndex, long positionMs) {
seekToWithMediaItemIndexCalled = true;
seekMediaItemIndex = mediaItemIndex; seekMediaItemIndex = mediaItemIndex;
seekPositionMs = positionMs; seekPositionMs = positionMs;
countDownLatch.countDown(); checkNotNull(conditionVariables.get(METHOD_SEEK_TO_WITH_MEDIA_ITEM_INDEX)).open();
} }
@Override @Override
@ -298,8 +393,7 @@ public class MockPlayer implements Player {
@Override @Override
public void seekBack() { public void seekBack() {
seekBackCalled = true; checkNotNull(conditionVariables.get(METHOD_SEEK_BACK)).open();
countDownLatch.countDown();
} }
@Override @Override
@ -309,8 +403,7 @@ public class MockPlayer implements Player {
@Override @Override
public void seekForward() { public void seekForward() {
seekForwardCalled = true; checkNotNull(conditionVariables.get(METHOD_SEEK_FORWARD)).open();
countDownLatch.countDown();
} }
@Override @Override
@ -508,16 +601,14 @@ public class MockPlayer implements Player {
@Override @Override
public void setPlaybackParameters(PlaybackParameters playbackParameters) { public void setPlaybackParameters(PlaybackParameters playbackParameters) {
setPlaybackParametersCalled = true;
this.playbackParameters = playbackParameters; this.playbackParameters = playbackParameters;
countDownLatch.countDown(); checkNotNull(conditionVariables.get(METHOD_SET_PLAYBACK_PARAMETERS)).open();
} }
@Override @Override
public void setPlaybackSpeed(float speed) { public void setPlaybackSpeed(float speed) {
setPlaybackSpeedCalled = true;
playbackParameters = new PlaybackParameters(speed); playbackParameters = new PlaybackParameters(speed);
countDownLatch.countDown(); checkNotNull(conditionVariables.get(METHOD_SET_PLAYBACK_SPEED)).open();
} }
@Override @Override
@ -527,9 +618,8 @@ public class MockPlayer implements Player {
@Override @Override
public void setVolume(float volume) { public void setVolume(float volume) {
setVolumeCalled = true;
this.volume = volume; this.volume = volume;
countDownLatch.countDown(); checkNotNull(conditionVariables.get(METHOD_SET_VOLUME)).open();
} }
@Override @Override
@ -554,35 +644,30 @@ public class MockPlayer implements Player {
@Override @Override
public void setDeviceVolume(int volume) { public void setDeviceVolume(int volume) {
setDeviceVolumeCalled = true;
deviceVolume = volume; deviceVolume = volume;
countDownLatch.countDown(); checkNotNull(conditionVariables.get(METHOD_SET_DEVICE_VOLUME)).open();
} }
@Override @Override
public void increaseDeviceVolume() { public void increaseDeviceVolume() {
increaseDeviceVolumeCalled = true; checkNotNull(conditionVariables.get(METHOD_INCREASE_DEVICE_VOLUME)).open();
countDownLatch.countDown();
} }
@Override @Override
public void decreaseDeviceVolume() { public void decreaseDeviceVolume() {
decreaseDeviceVolumeCalled = true; checkNotNull(conditionVariables.get(METHOD_DECREASE_DEVICE_VOLUME)).open();
countDownLatch.countDown();
} }
@Override @Override
public void setDeviceMuted(boolean muted) { public void setDeviceMuted(boolean muted) {
setDeviceMutedCalled = true;
deviceMuted = muted; deviceMuted = muted;
countDownLatch.countDown(); checkNotNull(conditionVariables.get(METHOD_SET_DEVICE_MUTED)).open();
} }
@Override @Override
public void setPlayWhenReady(boolean playWhenReady) { public void setPlayWhenReady(boolean playWhenReady) {
this.setPlayWhenReadyCalled = true;
this.playWhenReady = playWhenReady; this.playWhenReady = playWhenReady;
countDownLatch.countDown(); checkNotNull(conditionVariables.get(METHOD_SET_PLAY_WHEN_READY)).open();
} }
@Override @Override
@ -626,49 +711,43 @@ public class MockPlayer implements Player {
@Override @Override
public void setMediaItem(MediaItem mediaItem) { public void setMediaItem(MediaItem mediaItem) {
setMediaItemCalled = true;
this.mediaItem = mediaItem; this.mediaItem = mediaItem;
countDownLatch.countDown(); checkNotNull(conditionVariables.get(METHOD_SET_MEDIA_ITEM)).open();
} }
@Override @Override
public void setMediaItem(MediaItem mediaItem, long startPositionMs) { public void setMediaItem(MediaItem mediaItem, long startPositionMs) {
setMediaItemWithStartPositionCalled = true;
this.mediaItem = mediaItem; this.mediaItem = mediaItem;
this.startPositionMs = startPositionMs; this.startPositionMs = startPositionMs;
countDownLatch.countDown(); checkNotNull(conditionVariables.get(METHOD_SET_MEDIA_ITEM_WITH_START_POSITION)).open();
} }
@Override @Override
public void setMediaItem(MediaItem mediaItem, boolean resetPosition) { public void setMediaItem(MediaItem mediaItem, boolean resetPosition) {
setMediaItemWithResetPositionCalled = true;
this.mediaItem = mediaItem; this.mediaItem = mediaItem;
this.resetPosition = resetPosition; this.resetPosition = resetPosition;
countDownLatch.countDown(); checkNotNull(conditionVariables.get(METHOD_SET_MEDIA_ITEM_WITH_RESET_POSITION)).open();
} }
@Override @Override
public void setMediaItems(List<MediaItem> mediaItems) { public void setMediaItems(List<MediaItem> mediaItems) {
setMediaItemsCalled = true;
this.mediaItems = mediaItems; this.mediaItems = mediaItems;
countDownLatch.countDown(); checkNotNull(conditionVariables.get(METHOD_SET_MEDIA_ITEMS)).open();
} }
@Override @Override
public void setMediaItems(List<MediaItem> mediaItems, boolean resetPosition) { public void setMediaItems(List<MediaItem> mediaItems, boolean resetPosition) {
setMediaItemsWithResetPositionCalled = true;
this.mediaItems = mediaItems; this.mediaItems = mediaItems;
this.resetPosition = resetPosition; this.resetPosition = resetPosition;
countDownLatch.countDown(); checkNotNull(conditionVariables.get(METHOD_SET_MEDIA_ITEMS_WITH_RESET_POSITION)).open();
} }
@Override @Override
public void setMediaItems(List<MediaItem> mediaItems, int startIndex, long startPositionMs) { public void setMediaItems(List<MediaItem> mediaItems, int startIndex, long startPositionMs) {
setMediaItemsWithStartIndexCalled = true;
this.mediaItems = mediaItems; this.mediaItems = mediaItems;
this.startMediaItemIndex = startIndex; this.startMediaItemIndex = startIndex;
this.startPositionMs = startPositionMs; this.startPositionMs = startPositionMs;
countDownLatch.countDown(); checkNotNull(conditionVariables.get(METHOD_SET_MEDIA_ITEMS_WITH_START_INDEX)).open();
} }
@Override @Override
@ -678,9 +757,8 @@ public class MockPlayer implements Player {
@Override @Override
public void setPlaylistMetadata(MediaMetadata playlistMetadata) { public void setPlaylistMetadata(MediaMetadata playlistMetadata) {
setPlaylistMetadataCalled = true;
this.playlistMetadata = playlistMetadata; this.playlistMetadata = playlistMetadata;
countDownLatch.countDown(); checkNotNull(conditionVariables.get(METHOD_SET_PLAYLIST_METADATA)).open();
} }
@Deprecated @Deprecated
@ -775,70 +853,61 @@ public class MockPlayer implements Player {
@Override @Override
public void addMediaItem(MediaItem mediaItem) { public void addMediaItem(MediaItem mediaItem) {
addMediaItemCalled = true;
this.mediaItem = mediaItem; this.mediaItem = mediaItem;
countDownLatch.countDown(); checkNotNull(conditionVariables.get(METHOD_ADD_MEDIA_ITEM)).open();
} }
@Override @Override
public void addMediaItem(int index, MediaItem mediaItem) { public void addMediaItem(int index, MediaItem mediaItem) {
addMediaItemWithIndexCalled = true;
this.index = index; this.index = index;
this.mediaItem = mediaItem; this.mediaItem = mediaItem;
countDownLatch.countDown(); checkNotNull(conditionVariables.get(METHOD_ADD_MEDIA_ITEM_WITH_INDEX)).open();
} }
@Override @Override
public void addMediaItems(List<MediaItem> mediaItems) { public void addMediaItems(List<MediaItem> mediaItems) {
addMediaItemsCalled = true;
this.mediaItems = mediaItems; this.mediaItems = mediaItems;
countDownLatch.countDown(); checkNotNull(conditionVariables.get(METHOD_ADD_MEDIA_ITEMS)).open();
} }
@Override @Override
public void addMediaItems(int index, List<MediaItem> mediaItems) { public void addMediaItems(int index, List<MediaItem> mediaItems) {
addMediaItemsWithIndexCalled = true;
this.index = index; this.index = index;
this.mediaItems = mediaItems; this.mediaItems = mediaItems;
countDownLatch.countDown(); checkNotNull(conditionVariables.get(METHOD_ADD_MEDIA_ITEMS_WITH_INDEX)).open();
} }
@Override @Override
public void removeMediaItem(int index) { public void removeMediaItem(int index) {
removeMediaItemCalled = true;
this.index = index; this.index = index;
countDownLatch.countDown(); checkNotNull(conditionVariables.get(METHOD_REMOVE_MEDIA_ITEM)).open();
} }
@Override @Override
public void removeMediaItems(int fromIndex, int toIndex) { public void removeMediaItems(int fromIndex, int toIndex) {
removeMediaItemsCalled = true;
this.fromIndex = fromIndex; this.fromIndex = fromIndex;
this.toIndex = toIndex; this.toIndex = toIndex;
countDownLatch.countDown(); checkNotNull(conditionVariables.get(METHOD_REMOVE_MEDIA_ITEMS)).open();
} }
@Override @Override
public void clearMediaItems() { public void clearMediaItems() {
clearMediaItemsCalled = true; checkNotNull(conditionVariables.get(METHOD_CLEAR_MEDIA_ITEMS)).open();
countDownLatch.countDown();
} }
@Override @Override
public void moveMediaItem(int currentIndex, int newIndex) { public void moveMediaItem(int currentIndex, int newIndex) {
moveMediaItemCalled = true;
this.index = currentIndex; this.index = currentIndex;
this.newIndex = newIndex; this.newIndex = newIndex;
countDownLatch.countDown(); checkNotNull(conditionVariables.get(METHOD_MOVE_MEDIA_ITEM)).open();
} }
@Override @Override
public void moveMediaItems(int fromIndex, int toIndex, int newIndex) { public void moveMediaItems(int fromIndex, int toIndex, int newIndex) {
moveMediaItemsCalled = true;
this.fromIndex = fromIndex; this.fromIndex = fromIndex;
this.toIndex = toIndex; this.toIndex = toIndex;
this.newIndex = newIndex; this.newIndex = newIndex;
countDownLatch.countDown(); checkNotNull(conditionVariables.get(METHOD_MOVE_MEDIA_ITEMS)).open();
} }
@Deprecated @Deprecated
@ -901,20 +970,17 @@ public class MockPlayer implements Player {
@Override @Override
public void seekToPreviousMediaItem() { public void seekToPreviousMediaItem() {
seekToPreviousMediaItemCalled = true; checkNotNull(conditionVariables.get(METHOD_SEEK_TO_PREVIOUS_MEDIA_ITEM)).open();
countDownLatch.countDown();
} }
@Override @Override
public void seekToNextMediaItem() { public void seekToNextMediaItem() {
seekToNextMediaItemCalled = true; checkNotNull(conditionVariables.get(METHOD_SEEK_TO_NEXT_MEDIA_ITEM)).open();
countDownLatch.countDown();
} }
@Override @Override
public void seekToPrevious() { public void seekToPrevious() {
seekToPreviousCalled = true; checkNotNull(conditionVariables.get(METHOD_SEEK_TO_PREVIOUS)).open();
countDownLatch.countDown();
} }
@Override @Override
@ -924,8 +990,7 @@ public class MockPlayer implements Player {
@Override @Override
public void seekToNext() { public void seekToNext() {
seekToNextCalled = true; checkNotNull(conditionVariables.get(METHOD_SEEK_TO_NEXT)).open();
countDownLatch.countDown();
} }
@Override @Override
@ -935,9 +1000,8 @@ public class MockPlayer implements Player {
@Override @Override
public void setRepeatMode(int repeatMode) { public void setRepeatMode(int repeatMode) {
setRepeatModeCalled = true;
this.repeatMode = repeatMode; this.repeatMode = repeatMode;
countDownLatch.countDown(); checkNotNull(conditionVariables.get(METHOD_SET_REPEAT_MODE)).open();
} }
@Override @Override
@ -947,9 +1011,8 @@ public class MockPlayer implements Player {
@Override @Override
public void setShuffleModeEnabled(boolean shuffleModeEnabled) { public void setShuffleModeEnabled(boolean shuffleModeEnabled) {
setShuffleModeCalled = true;
this.shuffleModeEnabled = shuffleModeEnabled; this.shuffleModeEnabled = shuffleModeEnabled;
countDownLatch.countDown(); checkNotNull(conditionVariables.get(METHOD_SET_SHUFFLE_MODE)).open();
} }
@Override @Override
@ -1001,9 +1064,6 @@ public class MockPlayer implements Player {
@Override @Override
public VideoSize getVideoSize() { public VideoSize getVideoSize() {
if (videoSize == null) {
videoSize = VideoSize.UNKNOWN;
}
return videoSize; return videoSize;
} }
@ -1134,9 +1194,8 @@ public class MockPlayer implements Player {
@Override @Override
public void setTrackSelectionParameters(TrackSelectionParameters parameters) { public void setTrackSelectionParameters(TrackSelectionParameters parameters) {
setTrackSelectionParametersCalled = true;
trackSelectionParameters = parameters; trackSelectionParameters = parameters;
countDownLatch.countDown(); checkNotNull(conditionVariables.get(METHOD_SET_TRACK_SELECTION_PARAMETERS)).open();
} }
@Override @Override
@ -1144,10 +1203,73 @@ public class MockPlayer implements Player {
return applicationLooper; return applicationLooper;
} }
/** Returns whether {@code method} has been called at least once. */
public boolean hasMethodBeenCalled(@Method int method) {
return checkNotNull(conditionVariables.get(method)).isOpen();
}
/**
* Awaits up to {@code timeOutMs} until {@code method} is called, otherwise throws a {@link
* TimeoutException}.
*/
public void awaitMethodCalled(@Method int method, long timeOutMs)
throws TimeoutException, InterruptedException {
if (!checkNotNull(conditionVariables.get(method)).block(timeOutMs)) {
throw new TimeoutException(
Util.formatInvariant("Method %d not called after %f ms", method, timeOutMs));
}
}
private static ImmutableMap<@Method Integer, ConditionVariable> createMethodConditionVariables() {
return new ImmutableMap.Builder<@Method Integer, ConditionVariable>()
.put(METHOD_ADD_MEDIA_ITEM, new ConditionVariable())
.put(METHOD_ADD_MEDIA_ITEMS, new ConditionVariable())
.put(METHOD_ADD_MEDIA_ITEM_WITH_INDEX, new ConditionVariable())
.put(METHOD_ADD_MEDIA_ITEMS_WITH_INDEX, new ConditionVariable())
.put(METHOD_CLEAR_MEDIA_ITEMS, new ConditionVariable())
.put(METHOD_DECREASE_DEVICE_VOLUME, new ConditionVariable())
.put(METHOD_INCREASE_DEVICE_VOLUME, new ConditionVariable())
.put(METHOD_MOVE_MEDIA_ITEM, new ConditionVariable())
.put(METHOD_MOVE_MEDIA_ITEMS, new ConditionVariable())
.put(METHOD_PAUSE, new ConditionVariable())
.put(METHOD_PLAY, new ConditionVariable())
.put(METHOD_PREPARE, new ConditionVariable())
.put(METHOD_RELEASE, new ConditionVariable())
.put(METHOD_REMOVE_MEDIA_ITEM, new ConditionVariable())
.put(METHOD_REMOVE_MEDIA_ITEMS, new ConditionVariable())
.put(METHOD_SEEK_BACK, new ConditionVariable())
.put(METHOD_SEEK_FORWARD, new ConditionVariable())
.put(METHOD_SEEK_TO, new ConditionVariable())
.put(METHOD_SEEK_TO_DEFAULT_POSITION, new ConditionVariable())
.put(METHOD_SEEK_TO_DEFAULT_POSITION_WITH_MEDIA_ITEM_INDEX, new ConditionVariable())
.put(METHOD_SEEK_TO_NEXT, new ConditionVariable())
.put(METHOD_SEEK_TO_NEXT_MEDIA_ITEM, new ConditionVariable())
.put(METHOD_SEEK_TO_PREVIOUS, new ConditionVariable())
.put(METHOD_SEEK_TO_PREVIOUS_MEDIA_ITEM, new ConditionVariable())
.put(METHOD_SEEK_TO_WITH_MEDIA_ITEM_INDEX, new ConditionVariable())
.put(METHOD_SET_DEVICE_MUTED, new ConditionVariable())
.put(METHOD_SET_DEVICE_VOLUME, new ConditionVariable())
.put(METHOD_SET_MEDIA_ITEM, new ConditionVariable())
.put(METHOD_SET_MEDIA_ITEM_WITH_RESET_POSITION, new ConditionVariable())
.put(METHOD_SET_MEDIA_ITEM_WITH_START_POSITION, new ConditionVariable())
.put(METHOD_SET_MEDIA_ITEMS, new ConditionVariable())
.put(METHOD_SET_MEDIA_ITEMS_WITH_RESET_POSITION, new ConditionVariable())
.put(METHOD_SET_MEDIA_ITEMS_WITH_START_INDEX, new ConditionVariable())
.put(METHOD_SET_PLAY_WHEN_READY, new ConditionVariable())
.put(METHOD_SET_PLAYBACK_PARAMETERS, new ConditionVariable())
.put(METHOD_SET_PLAYBACK_SPEED, new ConditionVariable())
.put(METHOD_SET_PLAYLIST_METADATA, new ConditionVariable())
.put(METHOD_SET_REPEAT_MODE, new ConditionVariable())
.put(METHOD_SET_SHUFFLE_MODE, new ConditionVariable())
.put(METHOD_SET_TRACK_SELECTION_PARAMETERS, new ConditionVariable())
.put(METHOD_SET_VOLUME, new ConditionVariable())
.put(METHOD_STOP, new ConditionVariable())
.buildOrThrow();
}
/** Builder for {@link MockPlayer}. */ /** Builder for {@link MockPlayer}. */
public static final class Builder { public static final class Builder {
private int latchCount;
private boolean changePlayerStateWithTransportControl; private boolean changePlayerStateWithTransportControl;
private Looper applicationLooper; private Looper applicationLooper;
private int itemCount; private int itemCount;
@ -1156,11 +1278,6 @@ public class MockPlayer implements Player {
applicationLooper = Util.getCurrentOrMainLooper(); applicationLooper = Util.getCurrentOrMainLooper();
} }
public Builder setLatchCount(int latchCount) {
this.latchCount = latchCount;
return this;
}
public Builder setChangePlayerStateWithTransportControl( public Builder setChangePlayerStateWithTransportControl(
boolean changePlayerStateWithTransportControl) { boolean changePlayerStateWithTransportControl) {
this.changePlayerStateWithTransportControl = changePlayerStateWithTransportControl; this.changePlayerStateWithTransportControl = changePlayerStateWithTransportControl;

View File

@ -156,22 +156,10 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
* <li>Corresponding method: {@link #setKeepContentOnPlayerReset(boolean)} * <li>Corresponding method: {@link #setKeepContentOnPlayerReset(boolean)}
* <li>Default: {@code false} * <li>Default: {@code false}
* </ul> * </ul>
* <li><b>{@code player_layout_id}</b> - Specifies the id of the layout to be inflated. See below
* for more details.
* <ul>
* <li>Corresponding method: None
* <li>Default: {@code R.layout.exo_player_view}
* </ul>
* <li><b>{@code controller_layout_id}</b> - Specifies the id of the layout resource to be
* inflated by the child {@link PlayerControlView}. See below for more details.
* <ul>
* <li>Corresponding method: None
* <li>Default: {@code R.layout.exo_player_control_view}
* </ul>
* <li>All attributes that can be set on {@link PlayerControlView} and {@link DefaultTimeBar} can * <li>All attributes that can be set on {@link PlayerControlView} and {@link DefaultTimeBar} can
* also be set on a PlayerView, and will be propagated to the inflated {@link * also be set on a PlayerView, and will be propagated to the inflated {@link
* PlayerControlView} unless the layout is overridden to specify a custom {@code * PlayerControlView} unless the layout is overridden to specify a custom {@code
* exo_controller} (see below). * exo_controller}.
* </ul> * </ul>
* *
* <h2>Overriding drawables</h2> * <h2>Overriding drawables</h2>