Android APIs
public class

TransportMediator

extends TransportController
java.lang.Object
   ↳ android.support.v4.media.TransportController
     ↳ android.support.v4.media.TransportMediator

Class Overview

Helper for implementing a media transport control (with play, pause, skip, and other media actions). Takes care of both key events and advanced features like RemoteControlClient. This class is intended to serve as an intermediary between transport controls (whether they be on-screen controls, hardware buttons, remote controls) and the actual player. The player is represented by a single TransportPerformer that must be supplied to this class. On-screen controls that want to control and show the state of the player should do this through calls to the TransportController interface.

Here is a simple but fairly complete sample of a video player that is built around this class. Note that the MediaController class used here is not the one included in the standard Android framework, but a custom implementation. Real applications often implement their own transport controls, or you can copy the implementation here out of Support4Demos.

import android.support.v4.media.TransportMediator;
import android.support.v4.media.TransportPerformer;
import com.example.android.supportv4.R;

import android.app.ActionBar;
import android.content.Context;
import android.media.MediaPlayer;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.View;

import android.app.Activity;
import android.net.Uri;
import android.os.Bundle;
import android.widget.VideoView;

public class TransportControllerActivity extends Activity {

    /**
     * TODO: Set the path variable to a streaming video URL or a local media
     * file path.
     */
    private Content mContent;
    private TransportMediator mTransportMediator;
    private MediaController mMediaController;

    /**
     * Handle actions from on-screen media controls.  Most of these are simple re-directs
     * to the VideoView; some we need to capture to update our state.
     */
    TransportPerformer mTransportPerformer = new TransportPerformer() {
        @Override public void onStart() {
            mContent.start();
        }

        @Override public void onStop() {
            mContent.pause();
        }

        @Override public void onPause() {
            mContent.pause();
        }

        @Override public long onGetDuration() {
            return mContent.getDuration();
        }

        @Override public long onGetCurrentPosition() {
            return mContent.getCurrentPosition();
        }

        @Override public void onSeekTo(long pos) {
            mContent.seekTo((int)pos);
        }

        @Override public boolean onIsPlaying() {
            return mContent.isPlaying();
        }

        @Override public int onGetBufferPercentage() {
            return mContent.getBufferPercentage();
        }

        @Override public int onGetTransportControlFlags() {
            int flags = TransportMediator.FLAG_KEY_MEDIA_PLAY
                    | TransportMediator.FLAG_KEY_MEDIA_PLAY_PAUSE
                    | TransportMediator.FLAG_KEY_MEDIA_STOP;
            if (mContent.canPause()) {
                flags |= TransportMediator.FLAG_KEY_MEDIA_PAUSE;
            }
            if (mContent.canSeekBackward()) {
                flags |= TransportMediator.FLAG_KEY_MEDIA_REWIND;
            }
            if (mContent.canSeekForward()) {
                flags |= TransportMediator.FLAG_KEY_MEDIA_FAST_FORWARD;
            }
            return flags;
        }
    };

    /**
     * This is the actual video player.  It is the top-level content of
     * the activity's view hierarchy, going under the status bar and nav
     * bar areas.
     */
    public static class Content extends VideoView implements
            View.OnSystemUiVisibilityChangeListener, View.OnClickListener,
            ActionBar.OnMenuVisibilityListener, MediaPlayer.OnPreparedListener,
            MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener {
        Activity mActivity;
        TransportMediator mTransportMediator;
        MediaController mMediaController;
        boolean mAddedMenuListener;
        boolean mMenusOpen;
        boolean mPaused;
        boolean mNavVisible;
        int mLastSystemUiVis;

        Runnable mNavHider = new Runnable() {
            @Override public void run() {
                setNavVisibility(false);
            }
        };

        Runnable mProgressUpdater = new Runnable() {
            @Override public void run() {
                mMediaController.updateProgress();
                getHandler().postDelayed(this, 1000);
            }
        };

        public Content(Context context, AttributeSet attrs) {
            super(context, attrs);
            setOnSystemUiVisibilityChangeListener(this);
            setOnClickListener(this);
            setOnPreparedListener(this);
            setOnCompletionListener(this);
            setOnErrorListener(this);
        }

        public void init(Activity activity, TransportMediator transportMediator,
                MediaController mediaController) {
            // This called by the containing activity to supply the surrounding
            // state of the video player that it will interact with.
            mActivity = activity;
            mTransportMediator = transportMediator;
            mMediaController = mediaController;
            pause();
        }

        @Override protected void onAttachedToWindow() {
            super.onAttachedToWindow();
            if (mActivity != null) {
                mAddedMenuListener = true;
                mActivity.getActionBar().addOnMenuVisibilityListener(this);
            }
        }

        @Override protected void onDetachedFromWindow() {
            super.onDetachedFromWindow();
            if (mAddedMenuListener) {
                mActivity.getActionBar().removeOnMenuVisibilityListener(this);
            }
            mNavVisible = false;
        }

        @Override public void onSystemUiVisibilityChange(int visibility) {
            // Detect when we go out of nav-hidden mode, to clear our state
            // back to having the full UI chrome up.  Only do this when
            // the state is changing and nav is no longer hidden.
            int diff = mLastSystemUiVis ^ visibility;
            mLastSystemUiVis = visibility;
            if ((diff&SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
                    && (visibility&SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0) {
                setNavVisibility(true);
            }
        }

        @Override protected void onWindowVisibilityChanged(int visibility) {
            super.onWindowVisibilityChanged(visibility);

            // When we become visible or invisible, play is paused.
            pause();
        }

        @Override public void onClick(View v) {
            // Clicking anywhere makes the navigation visible.
            setNavVisibility(true);
        }

        @Override public void onMenuVisibilityChanged(boolean isVisible) {
            mMenusOpen = isVisible;
            setNavVisibility(true);
        }

        @Override
        public void onPrepared(MediaPlayer mp) {
            mMediaController.setEnabled(true);
        }

        @Override
        public void onCompletion(MediaPlayer mp) {
            mTransportMediator.pausePlaying();
            pause();
        }

        @Override
        public boolean onError(MediaPlayer mp, int what, int extra) {
            mTransportMediator.pausePlaying();
            pause();
            return false;
        }

        @Override public void start() {
            super.start();
            mPaused = false;
            setKeepScreenOn(true);
            setNavVisibility(true);
            mMediaController.refresh();
            scheduleProgressUpdater();
        }

        @Override public void pause() {
            super.pause();
            mPaused = true;
            setKeepScreenOn(false);
            setNavVisibility(true);
            mMediaController.refresh();
            scheduleProgressUpdater();
        }

        void scheduleProgressUpdater() {
            Handler h = getHandler();
            if (h != null) {
                if (mNavVisible && !mPaused) {
                    h.removeCallbacks(mProgressUpdater);
                    h.post(mProgressUpdater);
                } else {
                    h.removeCallbacks(mProgressUpdater);
                }
            }
        }

        void setNavVisibility(boolean visible) {
            int newVis = SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                    | SYSTEM_UI_FLAG_LAYOUT_STABLE;
            if (!visible) {
                newVis |= SYSTEM_UI_FLAG_LOW_PROFILE | SYSTEM_UI_FLAG_FULLSCREEN
                        | SYSTEM_UI_FLAG_HIDE_NAVIGATION;
            }

            // If we are now visible, schedule a timer for us to go invisible.
            if (visible) {
                Handler h = getHandler();
                if (h != null) {
                    h.removeCallbacks(mNavHider);
                    if (!mMenusOpen && !mPaused) {
                        // If the menus are open or play is paused, we will not auto-hide.
                        h.postDelayed(mNavHider, 3000);
                    }
                }
            }

            // Set the new desired visibility.
            setSystemUiVisibility(newVis);
            mNavVisible = visible;
            mMediaController.setVisibility(visible ? VISIBLE : INVISIBLE);
            scheduleProgressUpdater();
        }
    }

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setContentView(R.layout.videoview);

        // Find the video player in our UI.
        mContent = (Content) findViewById(R.id.content);

        // Create transport controller to control video, giving the callback
        // interface to receive actions from.
        mTransportMediator = new TransportMediator(this, mTransportPerformer);

        // Create and initialize the media control UI.
        mMediaController = (MediaController) findViewById(R.id.media_controller);
        mMediaController.setMediaPlayer(mTransportMediator);

        // We're just playing a built-in demo video.
        mContent.init(this, mTransportMediator, mMediaController);
        mContent.setVideoURI(Uri.parse("android.resource://" + getPackageName() +
                "/" + R.raw.videoviewdemo));
    }

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        // We first dispatch keys to the transport controller -- we want it
        // to get to consume any media keys rather than letting whoever has focus
        // in the view hierarchy to potentially eat it.
        if (mTransportMediator.dispatchKeyEvent(event)) {
            return true;
        }

        return super.dispatchKeyEvent(event);
    }
}

Summary

Constants
int FLAG_KEY_MEDIA_FAST_FORWARD Synonym for {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_FAST_FORWARD RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD
int FLAG_KEY_MEDIA_NEXT Synonym for {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_NEXT RemoteControlClient.FLAG_KEY_MEDIA_NEXT
int FLAG_KEY_MEDIA_PAUSE Synonym for {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_PAUSE RemoteControlClient.FLAG_KEY_MEDIA_PAUSE
int FLAG_KEY_MEDIA_PLAY Synonym for {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_PLAY RemoteControlClient.FLAG_KEY_MEDIA_PLAY
int FLAG_KEY_MEDIA_PLAY_PAUSE Synonym for {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_PLAY_PAUSE RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE
int FLAG_KEY_MEDIA_PREVIOUS Synonym for {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_PREVIOUS RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS
int FLAG_KEY_MEDIA_REWIND Synonym for {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_REWIND RemoteControlClient.FLAG_KEY_MEDIA_REWIND
int FLAG_KEY_MEDIA_STOP Synonym for {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_STOP RemoteControlClient.FLAG_KEY_MEDIA_STOP
int KEYCODE_MEDIA_PAUSE Synonym for KeyEvent.KEYCODE_MEDIA_PAUSE
int KEYCODE_MEDIA_PLAY Synonym for KeyEvent.KEYCODE_MEDIA_PLAY
int KEYCODE_MEDIA_RECORD Synonym for KeyEvent.KEYCODE_MEDIA_RECORD
Public Constructors
TransportMediator(Activity activity, TransportPerformer callbacks)
TransportMediator(View view, TransportPerformer callbacks)
Public Methods
void destroy()
Optionally call when no longer using the TransportController.
boolean dispatchKeyEvent(KeyEvent event)
Must call from Activity.dispatchKeyEvent to give the transport an opportunity to intercept media keys.
int getBufferPercentage()
Retrieve amount, in percentage (0-100), that the media stream has been buffered on to the local device.
long getCurrentPosition()
Retrieve the current playback location in the media stream, in milliseconds.
long getDuration()
Retrieve the total duration of the media stream, in milliseconds.
Object getRemoteControlClient()
Return the RemoteControlClient associated with this transport.
int getTransportControlFlags()
Retrieves the flags for the media transport control buttons that this transport supports.
boolean isPlaying()
Return whether the player is currently playing its stream.
void pausePlaying()
Move the controller into the paused state.
void refreshState()
void registerStateListener(TransportStateListener listener)
Start listening to changes in playback state.
void seekTo(long pos)
Move to a new location in the media stream.
void startPlaying()
Move the controller into the playing state.
void stopPlaying()
Move the controller into the stopped state.
void unregisterStateListener(TransportStateListener listener)
Stop listening to changes in playback state.
[Expand]
Inherited Methods
From class android.support.v4.media.TransportController
From class java.lang.Object

Constants

public static final int FLAG_KEY_MEDIA_FAST_FORWARD

Synonym for {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_FAST_FORWARD RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD

Constant Value: 64 (0x00000040)

public static final int FLAG_KEY_MEDIA_NEXT

Synonym for {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_NEXT RemoteControlClient.FLAG_KEY_MEDIA_NEXT

Constant Value: 128 (0x00000080)

public static final int FLAG_KEY_MEDIA_PAUSE

Synonym for {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_PAUSE RemoteControlClient.FLAG_KEY_MEDIA_PAUSE

Constant Value: 16 (0x00000010)

public static final int FLAG_KEY_MEDIA_PLAY

Synonym for {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_PLAY RemoteControlClient.FLAG_KEY_MEDIA_PLAY

Constant Value: 4 (0x00000004)

public static final int FLAG_KEY_MEDIA_PLAY_PAUSE

Synonym for {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_PLAY_PAUSE RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE

Constant Value: 8 (0x00000008)

public static final int FLAG_KEY_MEDIA_PREVIOUS

Synonym for {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_PREVIOUS RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS

Constant Value: 1 (0x00000001)

public static final int FLAG_KEY_MEDIA_REWIND

Synonym for {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_REWIND RemoteControlClient.FLAG_KEY_MEDIA_REWIND

Constant Value: 2 (0x00000002)

public static final int FLAG_KEY_MEDIA_STOP

Synonym for {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_STOP RemoteControlClient.FLAG_KEY_MEDIA_STOP

Constant Value: 32 (0x00000020)

public static final int KEYCODE_MEDIA_PAUSE

Constant Value: 127 (0x0000007f)

public static final int KEYCODE_MEDIA_PLAY

Constant Value: 126 (0x0000007e)

public static final int KEYCODE_MEDIA_RECORD

Constant Value: 130 (0x00000082)

Public Constructors

public TransportMediator (Activity activity, TransportPerformer callbacks)

public TransportMediator (View view, TransportPerformer callbacks)

Public Methods

public void destroy ()

Optionally call when no longer using the TransportController. Its resources will also be automatically cleaned up when your activity/view is detached from its window, so you don't normally need to call this explicitly.

public boolean dispatchKeyEvent (KeyEvent event)

Must call from Activity.dispatchKeyEvent to give the transport an opportunity to intercept media keys. Any such keys will show up in TransportPerformer.

public int getBufferPercentage ()

Retrieve amount, in percentage (0-100), that the media stream has been buffered on to the local device. Return 100 if the stream is always local.

public long getCurrentPosition ()

Retrieve the current playback location in the media stream, in milliseconds.

public long getDuration ()

Retrieve the total duration of the media stream, in milliseconds.

public Object getRemoteControlClient ()

Return the RemoteControlClient associated with this transport. This returns a generic Object since the RemoteControlClient is not availble before ICE_CREAM_SANDWICH. Further, this class will not use RemoteControlClient in its implementation until JELLY_BEAN_MR2. You should always check for null here and not do anything with the RemoteControlClient if none is given; this way you don't need to worry about the current platform API version.

Note that this class takes possession of the RemoteControlClient.OnGetPlaybackPositionListener and RemoteControlClient.OnPlaybackPositionUpdateListener callbacks; you will interact with these through TransportPerformer.onGetCurrentPosition and TransportPerformer.onSeekTo, respectively.

public int getTransportControlFlags ()

Retrieves the flags for the media transport control buttons that this transport supports. Result is a combination of the following flags: FLAG_KEY_MEDIA_PREVIOUS, FLAG_KEY_MEDIA_REWIND, FLAG_KEY_MEDIA_PLAY, FLAG_KEY_MEDIA_PLAY_PAUSE, FLAG_KEY_MEDIA_PAUSE, FLAG_KEY_MEDIA_STOP, FLAG_KEY_MEDIA_FAST_FORWARD, FLAG_KEY_MEDIA_NEXT

public boolean isPlaying ()

Return whether the player is currently playing its stream.

public void pausePlaying ()

Move the controller into the paused state. This updates the remote control client to indicate it is paused, but keeps audio focus.

public void refreshState ()

public void registerStateListener (TransportStateListener listener)

Start listening to changes in playback state.

public void seekTo (long pos)

Move to a new location in the media stream.

Parameters
pos Position to move to, in milliseconds.

public void startPlaying ()

Move the controller into the playing state. This updates the remote control client to indicate it is playing, and takes audio focus for the app.

public void stopPlaying ()

Move the controller into the stopped state. This updates the remote control client to indicate it is stopped, and removes audio focus from the app.

public void unregisterStateListener (TransportStateListener listener)

Stop listening to changes in playback state.