Class ShadowMediaPlayer

java.lang.Object
org.robolectric.shadows.ShadowPlayerBase
org.robolectric.shadows.ShadowMediaPlayer

@Implements(android.media.MediaPlayer.class) public class ShadowMediaPlayer extends ShadowPlayerBase
Automated testing of media playback can be a difficult thing - especially testing that your code properly handles asynchronous errors and events. This near impossible task is made quite straightforward using this implementation of 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:

Note: One gotcha with this shadow is that you need to either configure an exception using 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!

  • Field Details

  • Constructor Details

    • ShadowMediaPlayer

      public ShadowMediaPlayer()
  • Method Details

    • __staticInitializer__

      @Implementation protected static void __staticInitializer__()
    • postEvent

      public void postEvent(ShadowMediaPlayer.MediaEvent e)
    • postEventDelayed

      public void postEventDelayed(ShadowMediaPlayer.MediaEvent e, long delay)
    • create

      @Implementation protected static MediaPlayer create(Context context, int resId)
    • create

      @Implementation protected static MediaPlayer create(Context context, Uri uri)
    • __constructor__

      @Implementation protected void __constructor__()
    • setDataSource

      public void setDataSource(DataSource dataSource) throws IOException
      Common code path for all 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.

      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

      @Implementation protected void setDataSource(String path) throws IOException
      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

      public void doSetDataSource(DataSource dataSource)
      Sets the data source without doing any other emulation. Sets the internal data source only. Calling directly can be useful for setting up a ShadowMediaPlayer 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

      public static ShadowMediaPlayer.MediaInfo getMediaInfo(DataSource dataSource)
    • addMediaInfo

      public static void addMediaInfo(DataSource dataSource, ShadowMediaPlayer.MediaInfo info)
    • setMediaInfoProvider

      public static void setMediaInfoProvider(ShadowMediaPlayer.MediaInfoProvider mediaInfoProvider)
    • addException

      public static void addException(DataSource dataSource, RuntimeException e)
    • addException

      public static void addException(DataSource dataSource, IOException e)
    • setOnCompletionListener

      @Implementation protected void setOnCompletionListener(MediaPlayer.OnCompletionListener listener)
    • setOnSeekCompleteListener

      @Implementation protected void setOnSeekCompleteListener(MediaPlayer.OnSeekCompleteListener listener)
    • setOnPreparedListener

      @Implementation protected void setOnPreparedListener(MediaPlayer.OnPreparedListener listener)
    • setOnInfoListener

      @Implementation protected void setOnInfoListener(MediaPlayer.OnInfoListener listener)
    • setOnErrorListener

      @Implementation protected void setOnErrorListener(MediaPlayer.OnErrorListener listener)
    • isLooping

      @Implementation protected boolean isLooping()
    • setLooping

      @Implementation protected void setLooping(boolean looping)
    • setVolume

      @Implementation protected void setVolume(float left, float right)
    • isPlaying

      @Implementation protected boolean isPlaying()
    • prepare

      @Implementation protected void prepare()
      Simulates 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.

      See Also:
    • prepareAsync

      @Implementation protected void prepareAsync()
      Simulates 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.
      See Also:
    • start

      @Implementation protected void start()
      Simulates private native method 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.

      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.

      Returns:
      true if the player is really playing or false if the player is internally paused.
      See Also:
    • doStart

      public void doStart()
      Starts simulated playback. Until this method is called, the player is not "really playing" (see 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).

      See Also:
    • doStop

      public void doStop()
      Pauses simulated playback. After this method is called, the player is no longer "really playing" (see 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.

      See Also:
    • _pause

      @Implementation protected void _pause()
      Simulates MediaPlayer._pause(). Invokes doStop() to suspend playback event callbacks and sets the state to PAUSED.
      See Also:
    • _release

      @Implementation protected void _release()
      Simulates call to MediaPlayer._release(). Calls doStop() to suspend playback event callbacks and sets the state to END.
    • _reset

      @Implementation protected void _reset()
      Simulates call to MediaPlayer._reset(). Calls doStop() to suspend playback event callbacks and sets the state to IDLE.
    • _stop

      @Implementation protected void _stop()
      Simulates call to MediaPlayer.release(). Calls doStop() to suspend playback event callbacks and sets the state to STOPPED.
    • attachAuxEffect

      @Implementation protected void attachAuxEffect(int effectId)
    • getAudioSessionId

      @Implementation protected int getAudioSessionId()
    • getCurrentPosition

      @Implementation protected int getCurrentPosition()
      Simulates call to MediaPlayer.getCurrentPosition(). Simply does the state validity checks and then invokes getCurrentPositionRaw() to calculate the simulated playback position.
      Returns:
      The current offset (in ms) of the simulated playback.
      See Also:
    • getDuration

      @Implementation protected int getDuration()
      Simulates call to MediaPlayer.getDuration(). Retrieves the duration as defined by the current ShadowMediaPlayer.MediaInfo instance.
      Returns:
      The duration (in ms) of the current simulated playback.
      See Also:
    • getVideoHeight

      @Implementation protected int getVideoHeight()
    • getVideoWidth

      @Implementation protected int getVideoWidth()
    • seekTo

      @Implementation protected void seekTo(int seekTo)
      Simulates seeking to specified position. The seek will complete after 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().
      Parameters:
      seekTo - the offset (in ms) from the start of the track to seek to.
    • seekTo

      @Implementation(minSdk=26) protected void seekTo(long seekTo, int mode)
    • setAudioSessionId

      @Implementation protected void setAudioSessionId(int sessionId)
    • setAudioStreamType

      @Implementation protected void setAudioStreamType(int audioStreamType)
    • setCreateListener

      public static void setCreateListener(ShadowMediaPlayer.CreateListener createListener)
      Sets a listener that is invoked whenever a new shadowed 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.

      Parameters:
      createListener - the listener to be invoked
    • getHandler

      public Handler getHandler()
      Retrieves the 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.
      Returns:
      Handler object that can be used to schedule asynchronous events on this media player.
    • getInvalidStateBehavior

      public ShadowMediaPlayer.InvalidStateBehavior getInvalidStateBehavior()
      Retrieves current flag specifying the behavior of the media player when a method is invoked in an invalid state. See setInvalidStateBehavior(InvalidStateBehavior) for a discussion of the available modes and their associated behaviors.
      Returns:
      The current invalid state behavior mode.
      See Also:
    • setInvalidStateBehavior

      public void setInvalidStateBehavior(ShadowMediaPlayer.InvalidStateBehavior invalidStateBehavior)
      Specifies how the media player should behave when a method is invoked in an invalid state. Three modes are supported (as defined by the ShadowMediaPlayer.InvalidStateBehavior enum):

      SILENT

      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.

      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

      The shadow will attempt to emulate the behavior of the actual 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 is getVideoHeight().
      • Synchronous error handling: methods always throw an exception (usually IllegalStateException but don't invoke onError(). Examples are prepare() and setDataSource(String).
      • Asynchronous error handling: methods don't throw an exception but invoke onError().
      Additionally, all three methods behave synchronously (throwing 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

      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.
      Parameters:
      invalidStateBehavior - the behavior mode for this shadow to use during testing.
      See Also:
    • getMediaInfo

      public ShadowMediaPlayer.MediaInfo getMediaInfo()
      Retrieves the currently selected ShadowMediaPlayer.MediaInfo. This instance is used to define current duration, preparation delay, exceptions for setDataSource(), playback events, etc.
      Returns:
      The currently selected ShadowMediaPlayer.MediaInfo.
      See Also:
    • 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 of getCurrentPosition() 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

      public ShadowMediaPlayer.State getState()
      Retrieves the current state of the MediaPlayer. Uses the states as defined in the MediaPlayer documentation.
      Returns:
      The current state of the MediaPlayer, as defined in the MediaPlayer documentation.
      See Also:
    • setState

      public void setState(ShadowMediaPlayer.State state)
      Forces the @link MediaPlayer} into the specified state. Uses the states as defined in the 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.

      Parameters:
      state - the new state of the MediaPlayer, 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 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.
      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 to invokeSeekCompleteListener()). Returns -1 if no seek is in progress.
    • getDataSource

      public DataSource getDataSource()
      Retrieves the data source (if any) that was passed in to setDataSource(DataSource).

      Useful for assertions.

      Returns:
      The source passed in to setDataSource.
    • getSourceUri

      public Uri getSourceUri()
      Retrieves the source path (if any) that was passed in to MediaPlayer.setDataSource(Context, Uri, Map) or MediaPlayer.setDataSource(Context, Uri).
      Returns:
      The source Uri passed in to setDataSource.
    • getSourceResId

      public int getSourceResId()
      Retrieves the resource ID used in the call to create(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

      @Implementation(minSdk=28) protected boolean native_setOutputDevice(int preferredDeviceId)
    • 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

      public MediaPlayer.OnCompletionListener getOnCompletionListener()
      Returns:
      the OnCompletionListener
    • getOnPreparedListener

      public MediaPlayer.OnPreparedListener 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 the preparedListener()
    • invokeCompletionListener

      public void invokeCompletionListener()
      Simulates end-of-playback. Changes the player into PLAYBACK_COMPLETED state and calls onCompletion() 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 to what in MediaPlayer.OnInfoListener.onInfo(MediaPlayer, int, int).
      extra - parameter to pass in to extra in MediaPlayer.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 to what in MediaPlayer.OnErrorListener.onError(MediaPlayer, int, int).
      extra - parameter to pass in to extra in MediaPlayer.OnErrorListener.onError(MediaPlayer, int, int).
    • resetStaticState

      @Resetter public static void resetStaticState()