@Implements(android.media.MediaPlayer.class) public class ShadowMediaPlayer extends ShadowPlayerBase
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:
ShadowMediaPlayer.CreateListener
so that newly-created MediaPlayer
instances can have their shadows configured before they are used.
OnCompletionListener
, OnErrorListener
,
OnInfoListener
, OnPreparedListener
and OnSeekCompleteListener
.
MediaPlayer
internal states and their transition map.
setSeekDelay(int)
.
ShadowMediaPlayer.MediaInfo
inner class.
setInvalidStateBehavior(org.robolectric.shadows.ShadowMediaPlayer.InvalidStateBehavior)
).
setDataSource()
, using addMediaInfo(org.robolectric.shadows.util.DataSource, org.robolectric.shadows.ShadowMediaPlayer.MediaInfo)
or setMediaInfoProvider(MediaInfoProvider)
.
setDataSource(org.robolectric.shadows.util.DataSource)
using addException(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!
Modifier and Type | Class | Description |
---|---|---|
static interface |
ShadowMediaPlayer.CreateListener |
Callback interface for clients that wish to be informed when a new
MediaPlayer instance
is constructed. |
static class |
ShadowMediaPlayer.InvalidStateBehavior |
Possible behavior modes for the media player when a method is invoked in an invalid state.
|
static interface |
ShadowMediaPlayer.MediaEvent |
|
static class |
ShadowMediaPlayer.MediaInfo |
Class specifying information for an emulated media object.
|
static interface |
ShadowMediaPlayer.MediaInfoProvider |
Provides a
ShadowMediaPlayer.MediaInfo for a given DataSource . |
static class |
ShadowMediaPlayer.State |
Possible states for the media player to be in.
|
Modifier and Type | Field | Description |
---|---|---|
protected static ShadowMediaPlayer.CreateListener |
createListener |
Listener that is called when a new MediaPlayer is constructed.
|
static int |
MEDIA_EVENT |
Constructor | Description |
---|---|
ShadowMediaPlayer() |
Modifier and Type | Method | Description |
---|---|---|
protected void |
__constructor__() |
|
protected static void |
__staticInitializer__() |
|
protected void |
_pause() |
Simulates
MediaPlayer._pause() . |
protected void |
_release() |
Simulates call to
MediaPlayer._release() . |
protected void |
_reset() |
Simulates call to
MediaPlayer._reset() . |
protected void |
_stop() |
Simulates call to
MediaPlayer.release() . |
static void |
addException(DataSource dataSource,
IOException e) |
|
static void |
addException(DataSource dataSource,
RuntimeException e) |
|
static void |
addMediaInfo(DataSource dataSource,
ShadowMediaPlayer.MediaInfo info) |
Adds a
ShadowMediaPlayer.MediaInfo for a DataSource . |
protected void |
attachAuxEffect(int effectId) |
|
protected static MediaPlayer |
create(Context context,
int resId) |
|
protected static MediaPlayer |
create(Context context,
Uri uri) |
|
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 |
getAudioSessionId() |
|
int |
getAuxEffect() |
Useful for assertions.
|
protected int |
getCurrentPosition() |
Simulates call to
MediaPlayer.getCurrentPosition() . |
int |
getCurrentPositionRaw() |
Retrieves the current position without doing the state checking that the emulated version of
getCurrentPosition() does. |
DataSource |
getDataSource() |
Retrieves the data source (if any) that was passed in to
setDataSource(DataSource) . |
protected int |
getDuration() |
Simulates call to
MediaPlayer.getDuration() . |
int |
getDurationRaw() |
Retrieves the current duration without doing the state checking that the emulated version does.
|
Handler |
getHandler() |
Retrieves the
Handler object used by this ShadowMediaPlayer . |
ShadowMediaPlayer.InvalidStateBehavior |
getInvalidStateBehavior() |
Retrieves current flag specifying the behavior of the media player when a method is invoked in
an invalid state.
|
float |
getLeftVolume() |
Retrieves the current setting for the left channel volume.
|
ShadowMediaPlayer.MediaInfo |
getMediaInfo() |
Retrieves the currently selected
ShadowMediaPlayer.MediaInfo . |
static ShadowMediaPlayer.MediaInfo |
getMediaInfo(DataSource dataSource) |
|
MediaPlayer.OnCompletionListener |
getOnCompletionListener() |
|
MediaPlayer.OnPreparedListener |
getOnPreparedListener() |
|
int |
getPendingSeek() |
Retrieves the pending seek setting.
|
float |
getRightVolume() |
|
int |
getSeekDelay() |
|
int |
getSourceResId() |
Retrieves the resource ID used in the call to
create(Context, int) (if any). |
Uri |
getSourceUri() |
Retrieves the source path (if any) that was passed in to
MediaPlayer.setDataSource(Context, Uri, Map) or MediaPlayer.setDataSource(Context,
Uri) . |
ShadowMediaPlayer.State |
getState() |
Retrieves the current state of the
MediaPlayer . |
int |
getTheAudioStreamType() |
Note: This has a funny name at the moment to avoid having to produce an API-specific shadow -
if it were called
getAudioStreamType() then the RobolectricWiringTest will
inform us that it should be annotated with Implementation , because there is a private
method in the later API versions with the same name, however this would fail on earlier
versions. |
protected int |
getVideoHeight() |
|
protected int |
getVideoWidth() |
|
void |
invokeCompletionListener() |
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 |
invokePreparedListener() |
Allows test cases to simulate 'prepared' state by invoking callback.
|
void |
invokeSeekCompleteListener() |
Allows test cases to simulate seek completion by invoking callback.
|
protected boolean |
isLooping() |
|
protected boolean |
isPlaying() |
|
boolean |
isPrepared() |
Tests to see if the player is in the PREPARED state.
|
boolean |
isReallyPlaying() |
Tests to see if the player is really playing.
|
protected boolean |
native_setOutputDevice(int preferredDeviceId) |
|
void |
postEvent(ShadowMediaPlayer.MediaEvent e) |
|
void |
postEventDelayed(ShadowMediaPlayer.MediaEvent e,
long delay) |
|
protected void |
prepare() |
Simulates
MediaPlayer.prepareAsync() . |
protected void |
prepareAsync() |
Simulates
MediaPlayer.prepareAsync() . |
static void |
resetStaticState() |
|
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 shadowed
MediaPlayer 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 |
setDataSource(Context context,
Uri uri,
Map<String,String> headers) |
|
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 all
setDataSource() 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 a
ShadowMediaPlayer.MediaInfoProvider to be used to get ShadowMediaPlayer.MediaInfo for any DataSource . |
protected void |
setOnCompletionListener(MediaPlayer.OnCompletionListener listener) |
|
protected void |
setOnErrorListener(MediaPlayer.OnErrorListener listener) |
|
protected void |
setOnInfoListener(MediaPlayer.OnInfoListener listener) |
|
protected void |
setOnPreparedListener(MediaPlayer.OnPreparedListener listener) |
|
protected void |
setOnSeekCompleteListener(MediaPlayer.OnSeekCompleteListener listener) |
|
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 method
MediaPlayer._start() . |
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
getService
protected static ShadowMediaPlayer.CreateListener createListener
setCreateListener(CreateListener)
public static final int MEDIA_EVENT
@Implementation protected static void __staticInitializer__()
public void postEvent(ShadowMediaPlayer.MediaEvent e)
public void postEventDelayed(ShadowMediaPlayer.MediaEvent e, long delay)
@Implementation protected static MediaPlayer create(Context context, int resId)
@Implementation protected static MediaPlayer create(Context context, Uri uri)
@Implementation protected void __constructor__()
public void setDataSource(DataSource dataSource) throws IOException
setDataSource()
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 to
INITIALIZED
. Usually this method would not be called directly, but indirectly through
one of the other setDataSource(String)
implementations, which use DataSource.toDataSource(String)
methods to convert their discrete parameters into a single
DataSource
instance.
dataSource
- the data source that is being set.IOException
- if the specified data source has been configured to throw an IO exception.addException(DataSource, IOException)
,
addException(DataSource, RuntimeException)
,
doSetDataSource(DataSource)
@Implementation protected void setDataSource(String path) throws IOException
IOException
@Implementation(maxSdk=25) protected void setDataSource(Context context, Uri uri) throws IOException
IOException
@Implementation(minSdk=14, maxSdk=25) protected void setDataSource(Context context, Uri uri, Map<String,String> headers) throws IOException
IOException
@Implementation(minSdk=26) protected void setDataSource(Context context, Uri uri, Map<String,String> headers, List<HttpCookie> cookies) throws IOException
IOException
@Implementation protected void setDataSource(String uri, Map<String,String> headers) throws IOException
IOException
@Implementation protected void setDataSource(FileDescriptor fd, long offset, long length) throws IOException
IOException
@Implementation(minSdk=23) protected void setDataSource(MediaDataSource mediaDataSource) throws IOException
IOException
@Implementation(minSdk=24) protected void setDataSource(AssetFileDescriptor assetFileDescriptor) throws IOException
IOException
public void doSetDataSource(DataSource dataSource)
ShadowMediaPlayer
instance during
specific testing so that you don't have to clutter your tests catching exceptions you know
won't be thrown.dataSource
- the data source that is being set.setDataSource(DataSource)
public static ShadowMediaPlayer.MediaInfo getMediaInfo(DataSource dataSource)
public static void addMediaInfo(DataSource dataSource, ShadowMediaPlayer.MediaInfo info)
ShadowMediaPlayer.MediaInfo
for a DataSource
.
This overrides any ShadowMediaPlayer.MediaInfoProvider
previously set by calling setMediaInfoProvider(org.robolectric.shadows.ShadowMediaPlayer.MediaInfoProvider)
, i.e., the provider will not be used for any DataSource
.
public static void setMediaInfoProvider(ShadowMediaPlayer.MediaInfoProvider mediaInfoProvider)
ShadowMediaPlayer.MediaInfoProvider
to be used to get ShadowMediaPlayer.MediaInfo
for any DataSource
.
This overrides any ShadowMediaPlayer.MediaInfo
previously set by calling addMediaInfo(org.robolectric.shadows.util.DataSource, org.robolectric.shadows.ShadowMediaPlayer.MediaInfo)
, i.e.,
ShadowMediaPlayer.MediaInfo
provided by this ShadowMediaPlayer.MediaInfoProvider
will be used instead.
public static void addException(DataSource dataSource, RuntimeException e)
public static void addException(DataSource dataSource, IOException e)
@Implementation protected void setOnCompletionListener(MediaPlayer.OnCompletionListener listener)
@Implementation protected void setOnSeekCompleteListener(MediaPlayer.OnSeekCompleteListener listener)
@Implementation protected void setOnPreparedListener(MediaPlayer.OnPreparedListener listener)
@Implementation protected void setOnInfoListener(MediaPlayer.OnInfoListener listener)
@Implementation protected void setOnErrorListener(MediaPlayer.OnErrorListener listener)
@Implementation protected boolean isLooping()
@Implementation protected void setLooping(boolean looping)
@Implementation protected void setVolume(float left, float right)
@Implementation protected boolean isPlaying()
@Implementation protected void prepare()
MediaPlayer.prepareAsync()
. Sleeps for preparationDelay
ms by calling SystemClock.sleep(long)
before calling invokePreparedListener()
.
If preparationDelay
is not positive and non-zero, there is no sleep.
@Implementation protected void prepareAsync()
MediaPlayer.prepareAsync()
. Sets state to PREPARING and posts a callback to
invokePreparedListener()
if the current preparation delay for the current media (see
getMediaInfo()
) is >= 0, otherwise the test suite is responsible for calling invokePreparedListener()
directly if required.@Implementation protected void start()
MediaPlayer._start()
. Sets state to STARTED and calls
doStart()
to start scheduling playback callback events.
If the current state is PLAYBACK_COMPLETED, the current position is reset to zero before starting playback.
doStart()
public boolean isReallyPlaying()
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.
public void doStart()
isReallyPlaying()
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 and doStart()
to mark its
end and restart normal playback (which is what scheduleBufferUnderrunAtOffset()
does).
isReallyPlaying()
,
doStop()
public void doStop()
isReallyPlaying()
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.
isReallyPlaying()
,
doStart()
@Implementation protected void _pause()
MediaPlayer._pause()
. Invokes doStop()
to suspend playback event
callbacks and sets the state to PAUSED.doStop()
@Implementation protected void _release()
MediaPlayer._release()
. Calls doStop()
to suspend playback
event callbacks and sets the state to END.@Implementation protected void _reset()
MediaPlayer._reset()
. Calls doStop()
to suspend playback
event callbacks and sets the state to IDLE.@Implementation protected void _stop()
MediaPlayer.release()
. Calls doStop()
to suspend playback
event callbacks and sets the state to STOPPED.@Implementation protected void attachAuxEffect(int effectId)
@Implementation protected int getAudioSessionId()
@Implementation protected int getCurrentPosition()
MediaPlayer.getCurrentPosition()
. Simply does the state validity
checks and then invokes getCurrentPositionRaw()
to calculate the simulated playback
position.getCurrentPositionRaw()
@Implementation protected int getDuration()
MediaPlayer.getDuration()
. Retrieves the duration as defined by the
current ShadowMediaPlayer.MediaInfo
instance.addMediaInfo(DataSource, MediaInfo)
@Implementation protected int getVideoHeight()
@Implementation protected int getVideoWidth()
@Implementation protected void seekTo(int seekTo)
seekDelay
ms
(defaults to 0), or else if seekDelay is negative then the controlling test is expected to
simulate seek completion by manually invoking invokeSeekCompleteListener()
.seekTo
- the offset (in ms) from the start of the track to seek to.@Implementation(minSdk=26) protected void seekTo(long seekTo, int mode)
@Implementation protected void setAudioSessionId(int sessionId)
@Implementation protected void setAudioStreamType(int audioStreamType)
public static void setCreateListener(ShadowMediaPlayer.CreateListener createListener)
MediaPlayer
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 and setDataSource()
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.
createListener
- the listener to be invokedpublic Handler getHandler()
Handler
object used by this ShadowMediaPlayer
. 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), see ShadowMediaPlayer.MediaInfo.scheduleEventAtOffset(int, ShadowMediaPlayer.MediaEvent)
and its various helpers.public ShadowMediaPlayer.InvalidStateBehavior getInvalidStateBehavior()
setInvalidStateBehavior(InvalidStateBehavior)
for a discussion
of the available modes and their associated behaviors.setInvalidStateBehavior(org.robolectric.shadows.ShadowMediaPlayer.InvalidStateBehavior)
public void setInvalidStateBehavior(ShadowMediaPlayer.InvalidStateBehavior invalidStateBehavior)
ShadowMediaPlayer.InvalidStateBehavior
enum):
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.
EMULATE
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:
onError()
. An example is getVideoHeight()
.
IllegalStateException
but don't invoke onError()
. Examples are prepare()
and setDataSource(String)
.
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 invoke getCurrentPosition()
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 invoke setDataSource(org.robolectric.shadows.util.DataSource)
from the ERROR state and likewise illegal to
invoke getCurrentPosition()
from the INITIALIZED state.
ASSERT
invalidStateBehavior
- the behavior mode for this shadow to use during testing.getInvalidStateBehavior()
public ShadowMediaPlayer.MediaInfo getMediaInfo()
ShadowMediaPlayer.MediaInfo
. This instance is used to define current
duration, preparation delay, exceptions for setDataSource()
, playback events, etc.public void setCurrentPosition(int position)
position
- the new playback position.public int getCurrentPositionRaw()
getCurrentPosition()
does.public int getDurationRaw()
public ShadowMediaPlayer.State getState()
MediaPlayer
. Uses the states as defined in the
MediaPlayer
documentation.MediaPlayer
, as defined in the MediaPlayer
documentation.setState(org.robolectric.shadows.ShadowMediaPlayer.State)
,
MediaPlayer
public void setState(ShadowMediaPlayer.State state)
MediaPlayer
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.
state
- the new state of the MediaPlayer
, as defined in the MediaPlayer
documentation.getState()
,
MediaPlayer
public int getTheAudioStreamType()
getAudioStreamType()
then the RobolectricWiringTest
will
inform us that it should be annotated with Implementation
, because there is a private
method in the later API versions with the same name, however this would fail on earlier
versions.public int getSeekDelay()
public void setSeekDelay(int seekDelay)
seekDelay
- length of time to delay (ms)public int getAuxEffect()
auxEffect
setting.public int getPendingSeek()
seekTo(int)
but before a call to invokeSeekCompleteListener()
).
Returns -1
if no seek is in progress.public DataSource getDataSource()
setDataSource(DataSource)
.
Useful for assertions.
setDataSource
.public Uri getSourceUri()
MediaPlayer.setDataSource(Context, Uri, Map)
or MediaPlayer.setDataSource(Context,
Uri)
.setDataSource
.public int getSourceResId()
create(Context, int)
(if any).create()
, or -1
if a different method of
setting the source was used.public float getLeftVolume()
public float getRightVolume()
@Implementation(minSdk=28) protected boolean native_setOutputDevice(int preferredDeviceId)
public boolean isPrepared()
getState()
may be more useful for new testing applications.true
if the MediaPlayer is in the PREPARED state, false otherwise.public MediaPlayer.OnCompletionListener getOnCompletionListener()
public MediaPlayer.OnPreparedListener getOnPreparedListener()
public void invokePreparedListener()
preparedListener()
public void invokeCompletionListener()
onCompletion()
if a listener has been set.public void invokeSeekCompleteListener()
public void invokeInfoListener(int what, int extra)
what
- parameter to pass in to what
in MediaPlayer.OnInfoListener.onInfo(MediaPlayer, int, int)
.extra
- parameter to pass in to extra
in MediaPlayer.OnInfoListener.onInfo(MediaPlayer, int, int)
.public void invokeErrorListener(int what, int extra)
what
- parameter to pass in to what
in MediaPlayer.OnErrorListener.onError(MediaPlayer, int, int)
.extra
- parameter to pass in to extra
in MediaPlayer.OnErrorListener.onError(MediaPlayer, int, int)
.@Resetter public static void resetStaticState()