Class ShadowMediaPlayer
MediaPlayer
with Robolectric.
This shadow implementation provides much of the functionality needed to emulate MediaPlayer
initialization and playback behavior without having to play actual media files. A
summary of the features included are:
- Construction-time callback hook
ShadowMediaPlayer.CreateListener
so that newly-createdMediaPlayer
instances can have their shadows configured before they are used. - Emulation of the
OnCompletionListener
,OnErrorListener
,OnInfoListener
,OnPreparedListener
andOnSeekCompleteListener
. - Full support of the
MediaPlayer
internal states and their transition map. - Configure time parameters such as playback duration, preparation delay and
setSeekDelay(int)
. - Emulation of asynchronous callback events during playback through Robolectric's scheduling
system using the
ShadowMediaPlayer.MediaInfo
inner class. - Emulation of error behavior when methods are called from invalid states, or to throw
assertions when methods are invoked in invalid states (using
setInvalidStateBehavior(org.robolectric.shadows.ShadowMediaPlayer.InvalidStateBehavior)
). - Emulation of different playback behaviors based on the current data source, as passed in to
setDataSource()
, usingaddMediaInfo(org.robolectric.shadows.util.DataSource, org.robolectric.shadows.ShadowMediaPlayer.MediaInfo)
orsetMediaInfoProvider(MediaInfoProvider)
. - Emulation of exceptions when calling
setDataSource(org.robolectric.shadows.util.DataSource)
usingaddException(org.robolectric.shadows.util.DataSource, java.lang.RuntimeException)
.
addException(DataSource, IOException)
or a ShadowMediaPlayer.MediaInfo
instance
for that data source using addMediaInfo(DataSource, MediaInfo)
or setMediaInfoProvider(MediaInfoProvider)
before calling setDataSource(org.robolectric.shadows.util.DataSource)
, otherwise
you'll get an IllegalArgumentException
.
The current features of ShadowMediaPlayer
were focused on development for testing
playback of audio tracks. Thus support for emulating timed text and video events is incomplete.
None of these features would be particularly onerous to add/fix - contributions welcome, of
course!
-
Nested Class Summary
Modifier and TypeClassDescriptionstatic interface
Callback interface for clients that wish to be informed when a newMediaPlayer
instance is constructed.static enum
Possible behavior modes for the media player when a method is invoked in an invalid state.static interface
static class
Class specifying information for an emulated media object.static interface
Provides aShadowMediaPlayer.MediaInfo
for a givenDataSource
.static enum
Possible states for the media player to be in. -
Field Summary
Modifier and TypeFieldDescriptionprotected static ShadowMediaPlayer.CreateListener
Listener that is called when a new MediaPlayer is constructed.static final int
-
Constructor Summary
-
Method Summary
Modifier and TypeMethodDescriptionprotected void
protected void
__constructor__
(Context context, int sessionId) protected static void
protected void
_pause()
SimulatesMediaPlayer._pause()
.protected void
_release()
Simulates call toMediaPlayer._release()
.protected void
_reset()
Simulates call toMediaPlayer._reset()
.protected void
_stop()
Simulates call toMediaPlayer.release()
.static void
addException
(DataSource dataSource, IOException e) static void
addException
(DataSource dataSource, RuntimeException e) static void
addMediaInfo
(DataSource dataSource, ShadowMediaPlayer.MediaInfo info) Adds aShadowMediaPlayer.MediaInfo
for aDataSource
.protected void
attachAuxEffect
(int effectId) protected static MediaPlayer
protected static MediaPlayer
void
doSetDataSource
(DataSource dataSource) Sets the data source without doing any other emulation.void
doStart()
Starts simulated playback.void
doStop()
Pauses simulated playback.protected int
int
Useful for assertions.protected int
Simulates call toMediaPlayer.getCurrentPosition()
.int
Retrieves the current position without doing the state checking that the emulated version ofgetCurrentPosition()
does.Retrieves the data source (if any) that was passed in tosetDataSource(DataSource)
.protected int
Simulates call toMediaPlayer.getDuration()
.int
Retrieves the current duration without doing the state checking that the emulated version does.Retrieves theHandler
object used by thisShadowMediaPlayer
.Retrieves current flag specifying the behavior of the media player when a method is invoked in an invalid state.float
Retrieves the current setting for the left channel volume.Retrieves the currently selectedShadowMediaPlayer.MediaInfo
.static ShadowMediaPlayer.MediaInfo
getMediaInfo
(DataSource dataSource) int
Retrieves the pending seek setting.float
int
int
Retrieves the resource ID used in the call tocreate(Context, int)
(if any).Retrieves the source path (if any) that was passed in toMediaPlayer.setDataSource(Context, Uri, Map)
orMediaPlayer.setDataSource(Context, Uri)
.getState()
Retrieves the current state of theMediaPlayer
.int
Note: This has a funny name at the moment to avoid having to produce an API-specific shadow - if it were calledgetAudioStreamType()
then theRobolectricWiringTest
will inform us that it should be annotated withImplementation
, because there is a private method in the later API versions with the same name, however this would fail on earlier versions.protected int
protected int
void
Simulates end-of-playback.void
invokeErrorListener
(int what, int extra) Allows test cases to directly simulate invocation of the OnError event.void
invokeInfoListener
(int what, int extra) Allows test cases to directly simulate invocation of the OnInfo event.void
Allows test cases to simulate 'prepared' state by invoking callback.void
Allows test cases to simulate seek completion by invoking callback.protected boolean
protected boolean
boolean
Tests to see if the player is in the PREPARED state.boolean
Tests to see if the player is really playing.protected boolean
native_setOutputDevice
(int preferredDeviceId) void
void
postEventDelayed
(ShadowMediaPlayer.MediaEvent e, long delay) protected void
prepare()
SimulatesMediaPlayer.prepareAsync()
.protected void
SimulatesMediaPlayer.prepareAsync()
.static void
protected void
seekTo
(int seekTo) Simulates seeking to specified position.protected void
seekTo
(long seekTo, int mode) protected void
setAudioSessionId
(int sessionId) protected void
setAudioStreamType
(int audioStreamType) static void
setCreateListener
(ShadowMediaPlayer.CreateListener createListener) Sets a listener that is invoked whenever a new shadowedMediaPlayer
object is constructed.void
setCurrentPosition
(int position) Sets the current position, bypassing the normal state checking.protected void
setDataSource
(Context context, Uri uri) protected void
protected void
setDataSource
(Context context, Uri uri, Map<String, String> headers, List<HttpCookie> cookies) protected void
setDataSource
(AssetFileDescriptor assetFileDescriptor) protected void
setDataSource
(MediaDataSource mediaDataSource) protected void
setDataSource
(FileDescriptor fd, long offset, long length) protected void
setDataSource
(String path) protected void
setDataSource
(String uri, Map<String, String> headers) void
setDataSource
(DataSource dataSource) Common code path for allsetDataSource()
implementations.void
setInvalidStateBehavior
(ShadowMediaPlayer.InvalidStateBehavior invalidStateBehavior) Specifies how the media player should behave when a method is invoked in an invalid state.protected void
setLooping
(boolean looping) static void
setMediaInfoProvider
(ShadowMediaPlayer.MediaInfoProvider mediaInfoProvider) Sets aShadowMediaPlayer.MediaInfoProvider
to be used to getShadowMediaPlayer.MediaInfo
for anyDataSource
.protected void
protected void
protected void
setOnInfoListener
(MediaPlayer.OnInfoListener listener) protected void
protected void
void
setSeekDelay
(int seekDelay) Sets the length of time (ms) that seekTo() will delay before completing.void
setState
(ShadowMediaPlayer.State state) Forces the @link MediaPlayer} into the specified state.protected void
setVolume
(float left, float right) protected void
start()
Simulates private native methodMediaPlayer._start()
.Methods inherited from class org.robolectric.shadows.ShadowPlayerBase
getService
-
Field Details
-
createListener
Listener that is called when a new MediaPlayer is constructed.- See Also:
-
MEDIA_EVENT
public static final int MEDIA_EVENT- See Also:
-
-
Constructor Details
-
ShadowMediaPlayer
public ShadowMediaPlayer()
-
-
Method Details
-
__staticInitializer__
-
postEvent
-
postEventDelayed
-
create
-
create
-
__constructor__
-
__constructor__
-
setDataSource
Common code path for allsetDataSource()
implementations.* Checks for any specified exceptions for the specified data source and throws them. * Checks the current state and throws an exception if it is in an invalid state. * If no exception is thrown in either of the previous two steps, then
doSetDataSource(DataSource)
is called to set the data source. * Sets the player state toINITIALIZED
. Usually this method would not be called directly, but indirectly through one of the othersetDataSource(String)
implementations, which useDataSource.toDataSource(String)
methods to convert their discrete parameters into a singleDataSource
instance.- Parameters:
dataSource
- the data source that is being set.- Throws:
IOException
- if the specified data source has been configured to throw an IO exception.- See Also:
-
setDataSource
- Throws:
IOException
-
setDataSource
@Implementation(maxSdk=25) protected void setDataSource(Context context, Uri uri) throws IOException - Throws:
IOException
-
setDataSource
@Implementation(maxSdk=25) protected void setDataSource(Context context, Uri uri, Map<String, String> headers) throws IOException- Throws:
IOException
-
setDataSource
@Implementation(minSdk=26) protected void setDataSource(Context context, Uri uri, Map<String, String> headers, List<HttpCookie> cookies) throws IOException- Throws:
IOException
-
setDataSource
@Implementation protected void setDataSource(String uri, Map<String, String> headers) throws IOException- Throws:
IOException
-
setDataSource
@Implementation protected void setDataSource(FileDescriptor fd, long offset, long length) throws IOException - Throws:
IOException
-
setDataSource
@Implementation(minSdk=23) protected void setDataSource(MediaDataSource mediaDataSource) throws IOException - Throws:
IOException
-
setDataSource
@Implementation(minSdk=24) protected void setDataSource(AssetFileDescriptor assetFileDescriptor) throws IOException - Throws:
IOException
-
doSetDataSource
Sets the data source without doing any other emulation. Sets the internal data source only. Calling directly can be useful for setting up aShadowMediaPlayer
instance during specific testing so that you don't have to clutter your tests catching exceptions you know won't be thrown.- Parameters:
dataSource
- the data source that is being set.- See Also:
-
getMediaInfo
-
addMediaInfo
Adds aShadowMediaPlayer.MediaInfo
for aDataSource
. -
setMediaInfoProvider
Sets aShadowMediaPlayer.MediaInfoProvider
to be used to getShadowMediaPlayer.MediaInfo
for anyDataSource
.This overrides any
ShadowMediaPlayer.MediaInfo
previously set by callingaddMediaInfo(org.robolectric.shadows.util.DataSource, org.robolectric.shadows.ShadowMediaPlayer.MediaInfo)
, i.e.,ShadowMediaPlayer.MediaInfo
provided by thisShadowMediaPlayer.MediaInfoProvider
will be used instead. -
addException
-
addException
-
setOnCompletionListener
-
setOnSeekCompleteListener
@Implementation protected void setOnSeekCompleteListener(MediaPlayer.OnSeekCompleteListener listener) -
setOnPreparedListener
-
setOnInfoListener
-
setOnErrorListener
-
isLooping
-
setLooping
-
setVolume
-
isPlaying
-
prepare
SimulatesMediaPlayer.prepareAsync()
. Sleeps forpreparationDelay
ms by callingSystemClock.sleep(long)
before callinginvokePreparedListener()
.If
preparationDelay
is not positive and non-zero, there is no sleep. -
prepareAsync
SimulatesMediaPlayer.prepareAsync()
. Sets state to PREPARING and posts a callback toinvokePreparedListener()
if the current preparation delay for the current media (seegetMediaInfo()
) is >= 0, otherwise the test suite is responsible for callinginvokePreparedListener()
directly if required. -
start
Simulates private native methodMediaPlayer._start()
. Sets state to STARTED and callsdoStart()
to start scheduling playback callback events.If the current state is PLAYBACK_COMPLETED, the current position is reset to zero before starting playback.
- See Also:
-
isReallyPlaying
public boolean isReallyPlaying()Tests to see if the player is really playing.The player is defined as "really playing" if simulated playback events (including playback completion) are being scheduled and invoked and
currentPosition
is being updated as time passes. Note that while the player will normally be really playing if in the STARTED state, this is not always the case - for example, if a pending seek is in progress, or perhaps a buffer underrun is being simulated. -
doStart
public void doStart()Starts simulated playback. Until this method is called, the player is not "really playing" (seeisReallyPlaying()
for a definition of "really playing").This method is used internally by the various shadow method implementations of the MediaPlayer public API, but may also be called directly by the test suite if you wish to simulate an internal pause. For example, to simulate a buffer underrun (player is in PLAYING state but isn't actually advancing the current position through the media), you could call
doStop()
to mark the start of the buffer underrun anddoStart()
to mark its end and restart normal playback (which is whatscheduleBufferUnderrunAtOffset()
does).- See Also:
-
doStop
public void doStop()Pauses simulated playback. After this method is called, the player is no longer "really playing" (seeisReallyPlaying()
for a definition of "really playing").This method is used internally by the various shadow method implementations of the MediaPlayer public API, but may also be called directly by the test suite if you wish to simulate an internal pause.
- See Also:
-
_pause
SimulatesMediaPlayer._pause()
. InvokesdoStop()
to suspend playback event callbacks and sets the state to PAUSED.- See Also:
-
_release
Simulates call toMediaPlayer._release()
. CallsdoStop()
to suspend playback event callbacks and sets the state to END. -
_reset
Simulates call toMediaPlayer._reset()
. CallsdoStop()
to suspend playback event callbacks and sets the state to IDLE. -
_stop
Simulates call toMediaPlayer.release()
. CallsdoStop()
to suspend playback event callbacks and sets the state to STOPPED. -
attachAuxEffect
-
getAudioSessionId
-
getCurrentPosition
Simulates call toMediaPlayer.getCurrentPosition()
. Simply does the state validity checks and then invokesgetCurrentPositionRaw()
to calculate the simulated playback position.- Returns:
- The current offset (in ms) of the simulated playback.
- See Also:
-
getDuration
Simulates call toMediaPlayer.getDuration()
. Retrieves the duration as defined by the currentShadowMediaPlayer.MediaInfo
instance.- Returns:
- The duration (in ms) of the current simulated playback.
- See Also:
-
getVideoHeight
-
getVideoWidth
-
seekTo
Simulates seeking to specified position. The seek will complete afterseekDelay
ms (defaults to 0), or else if seekDelay is negative then the controlling test is expected to simulate seek completion by manually invokinginvokeSeekCompleteListener()
.- Parameters:
seekTo
- the offset (in ms) from the start of the track to seek to.
-
seekTo
-
setAudioSessionId
-
setAudioStreamType
-
setCreateListener
Sets a listener that is invoked whenever a new shadowedMediaPlayer
object is constructed.Registering a listener gives you a chance to customize the shadowed object appropriately without needing to modify the application-under-test to provide access to the instance at the appropriate point in its life cycle. This is useful because normally a new
MediaPlayer
is created andsetDataSource()
is invoked soon after, without a break in the code. Using this callback means you don't have to change this common pattern just so that you can customize the shadow for testing.- Parameters:
createListener
- the listener to be invoked
-
getHandler
Retrieves theHandler
object used by thisShadowMediaPlayer
. Can be used for posting custom asynchronous events to the thread (eg, asynchronous errors). Use this for scheduling events to take place at a particular "real" time (ie, time as measured by the scheduler). For scheduling events to occur at a particular playback offset (no matter how long playback may be paused for, or where you seek to, etc), seeShadowMediaPlayer.MediaInfo.scheduleEventAtOffset(int, ShadowMediaPlayer.MediaEvent)
and its various helpers.- Returns:
- Handler object that can be used to schedule asynchronous events on this media player.
-
getInvalidStateBehavior
Retrieves current flag specifying the behavior of the media player when a method is invoked in an invalid state. SeesetInvalidStateBehavior(InvalidStateBehavior)
for a discussion of the available modes and their associated behaviors.- Returns:
- The current invalid state behavior mode.
- See Also:
-
setInvalidStateBehavior
Specifies how the media player should behave when a method is invoked in an invalid state. Three modes are supported (as defined by theShadowMediaPlayer.InvalidStateBehavior
enum):
No invalid state checking is done at all. All methods can be invoked from any state without throwing any exceptions or invoking the error listener.SILENT
This mode is provided primarily for backwards compatibility, and for this reason it is the default. For proper testing one of the other two modes is probably preferable.
The shadow will attempt to emulate the behavior of the actualEMULATE
MediaPlayer
implementation. This is based on a reading of the documentation and on actual experiments done on a Jelly Bean device. The official documentation is not all that clear, but basically methods fall into three categories:- Those that log an error when invoked in an invalid state but don't throw an exception or
invoke
onError()
. An example isgetVideoHeight()
. - Synchronous error handling: methods always throw an exception (usually
IllegalStateException
but don't invokeonError()
. Examples areprepare()
andsetDataSource(String)
. - Asynchronous error handling: methods don't throw an exception but invoke
onError()
.
IllegalStateException
when invoked from the END state.To complicate matters slightly, the official documentation sometimes contradicts observed behavior. For example, the documentation says it is illegal to call
setDataSource(org.robolectric.shadows.util.DataSource)
from the ERROR state - however, in practice it works fine. Conversely, the documentation says that it is legal to invokegetCurrentPosition()
from the INITIALIZED state, however testing showed that this caused an error. Wherever there is a discrepancy between documented and observed behavior, this implementation has gone with the most conservative implementation (ie, it is illegal to invokesetDataSource(org.robolectric.shadows.util.DataSource)
from the ERROR state and likewise illegal to invokegetCurrentPosition()
from the INITIALIZED state.
The shadow will raise an assertion any time that a method is invoked in an invalid state. The philosophy behind this mode is that to invoke a method in an invalid state is a programming error - a bug, pure and simple. As such it should be discovered and eliminated at development and testing time, rather than anticipated and handled at runtime. Asserting is a way of testing for these bugs during testing.ASSERT
- Parameters:
invalidStateBehavior
- the behavior mode for this shadow to use during testing.- See Also:
- Those that log an error when invoked in an invalid state but don't throw an exception or
invoke
-
getMediaInfo
Retrieves the currently selectedShadowMediaPlayer.MediaInfo
. This instance is used to define current duration, preparation delay, exceptions forsetDataSource()
, playback events, etc. -
setCurrentPosition
public void setCurrentPosition(int position) Sets the current position, bypassing the normal state checking. Use with care.- Parameters:
position
- the new playback position.
-
getCurrentPositionRaw
public int getCurrentPositionRaw()Retrieves the current position without doing the state checking that the emulated version ofgetCurrentPosition()
does.- Returns:
- The current playback position within the current clip.
-
getDurationRaw
public int getDurationRaw()Retrieves the current duration without doing the state checking that the emulated version does.- Returns:
- The duration of the current clip loaded by the player.
-
getState
Retrieves the current state of theMediaPlayer
. Uses the states as defined in theMediaPlayer
documentation.- Returns:
- The current state of the
MediaPlayer
, as defined in the MediaPlayer documentation. - See Also:
-
setState
Forces the @link MediaPlayer} into the specified state. Uses the states as defined in theMediaPlayer
documentation.Note that by invoking this method directly you can get the player into an inconsistent state that a real player could not be put in (eg, in the END state but with playback events still happening). Use with care.
- Parameters:
state
- the new state of theMediaPlayer
, as defined in the MediaPlayer documentation.- See Also:
-
getTheAudioStreamType
public int getTheAudioStreamType()Note: This has a funny name at the moment to avoid having to produce an API-specific shadow - if it were calledgetAudioStreamType()
then theRobolectricWiringTest
will inform us that it should be annotated withImplementation
, because there is a private method in the later API versions with the same name, however this would fail on earlier versions.- Returns:
- audioStreamType
-
getSeekDelay
public int getSeekDelay()- Returns:
- seekDelay
-
setSeekDelay
public void setSeekDelay(int seekDelay) Sets the length of time (ms) that seekTo() will delay before completing. Default is 0. If set to -1, then seekTo() will not call the OnSeekCompleteListener automatically; you will need to call invokeSeekCompleteListener() manually.- Parameters:
seekDelay
- length of time to delay (ms)
-
getAuxEffect
public int getAuxEffect()Useful for assertions.- Returns:
- The current
auxEffect
setting.
-
getPendingSeek
public int getPendingSeek()Retrieves the pending seek setting.- Returns:
- The position to which the shadow player is seeking for the seek in progress (ie, after
the call to
seekTo(int)
but before a call toinvokeSeekCompleteListener()
). Returns-1
if no seek is in progress.
-
getDataSource
Retrieves the data source (if any) that was passed in tosetDataSource(DataSource)
.Useful for assertions.
- Returns:
- The source passed in to
setDataSource
.
-
getSourceUri
Retrieves the source path (if any) that was passed in toMediaPlayer.setDataSource(Context, Uri, Map)
orMediaPlayer.setDataSource(Context, Uri)
.- Returns:
- The source Uri passed in to
setDataSource
.
-
getSourceResId
public int getSourceResId()Retrieves the resource ID used in the call tocreate(Context, int)
(if any).- Returns:
- The resource ID passed in to
create()
, or-1
if a different method of setting the source was used.
-
getLeftVolume
public float getLeftVolume()Retrieves the current setting for the left channel volume.- Returns:
- The left channel volume.
-
getRightVolume
public float getRightVolume()- Returns:
- The right channel volume.
-
native_setOutputDevice
-
isPrepared
public boolean isPrepared()Tests to see if the player is in the PREPARED state. This is mainly used for backward compatibility.getState()
may be more useful for new testing applications.- Returns:
true
if the MediaPlayer is in the PREPARED state, false otherwise.
-
getOnCompletionListener
- Returns:
- the OnCompletionListener
-
getOnPreparedListener
- Returns:
- the OnPreparedListener
-
invokePreparedListener
public void invokePreparedListener()Allows test cases to simulate 'prepared' state by invoking callback. Sets the player's state to PREPARED and invokes thepreparedListener()
-
invokeCompletionListener
public void invokeCompletionListener()Simulates end-of-playback. Changes the player into PLAYBACK_COMPLETED state and callsonCompletion()
if a listener has been set. -
invokeSeekCompleteListener
public void invokeSeekCompleteListener()Allows test cases to simulate seek completion by invoking callback. -
invokeInfoListener
public void invokeInfoListener(int what, int extra) Allows test cases to directly simulate invocation of the OnInfo event.- Parameters:
what
- parameter to pass in towhat
inMediaPlayer.OnInfoListener.onInfo(MediaPlayer, int, int)
.extra
- parameter to pass in toextra
inMediaPlayer.OnInfoListener.onInfo(MediaPlayer, int, int)
.
-
invokeErrorListener
public void invokeErrorListener(int what, int extra) Allows test cases to directly simulate invocation of the OnError event.- Parameters:
what
- parameter to pass in towhat
inMediaPlayer.OnErrorListener.onError(MediaPlayer, int, int)
.extra
- parameter to pass in toextra
inMediaPlayer.OnErrorListener.onError(MediaPlayer, int, int)
.
-
resetStaticState
-