Download File
Download Project
Settings
Line Wrap
Themes
default
ambiance
bespin
dracula
eclipse
material
mbo
mdn-like
neat
solarized dark
ttcn
zenburn
PlaybackFragment.java
/* * Copyright (c) 2017 The Android Open Source Project * * 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 * * http://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. */ package com.example.android.assistantplayback; import android.app.Activity; import android.os.Bundle; import android.support.v17.leanback.app.VideoFragment; import android.support.v17.leanback.app.VideoFragmentGlueHost; import android.support.v17.leanback.media.MediaPlayerAdapter; import android.support.v17.leanback.media.PlaybackGlue; import android.support.v17.leanback.media.PlaybackTransportControlGlue; import android.support.v4.media.MediaDescriptionCompat; import android.support.v4.media.session.MediaControllerCompat; import android.support.v4.media.session.MediaSessionCompat; import android.support.v4.media.session.PlaybackStateCompat; import android.util.Log; import com.example.android.assistantplayback.playlist.ListPlaylistAdapter; import com.example.android.assistantplayback.playlist.MockPlaylistAdapterFactory; /** * Plays back a video. Syncs media session and playback glue during video playback.
*
* A movie's id is required to be passed in by id via the activity's extras. */ public class PlaybackFragment extends VideoFragment { private static final String TAG = "PlaybackFragment"; public static final String EXTRA_MOVIE_ID = "com.example.android.assistantplayback.extra.MOVIE_ID"; public static final long AVAILABLE_MEDIA_ACTIONS = PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_STOP | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS | PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_SEEK_TO; private MediaSessionCompat mSession; private MediaSessionCallback mMediaSessionCallback; private PlaybackTransportControlGlue
mPlayerGlue; private ListPlaylistAdapter> mPlaylistAdapter; private final PlaybackGlue.PlayerCallback playWhenReadyPlayerCallback = new PlayWhenReadyPlayerCallback(); private final PlaybackGlue.PlayerCallback playPausePlayerCallback = new PlaybackGlue.PlayerCallback() { @Override public void onPlayStateChanged(PlaybackGlue glue) { super.onPlayStateChanged(glue); Log.d(TAG, "PlayerCallback: onPlayStateChanged()"); int state = glue.isPlaying() ? PlaybackStateCompat.STATE_PLAYING : PlaybackStateCompat.STATE_PAUSED; mMediaSessionCallback.updatePlaybackState( state, mPlayerGlue.getCurrentPosition(), mPlaylistAdapter.getCurrentItem()); Log.d(TAG, "PlayerCallback: playback state: " + state); } }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); int movieId = getActivity().getIntent().getIntExtra(EXTRA_MOVIE_ID, -1); if (movieId == -1) { Log.w(TAG, "Invalid movieId, cannot playback."); throw new IllegalArgumentException("Invalid movieId " + movieId); } mPlaylistAdapter = MockPlaylistAdapterFactory.createMoviePlaylistAdapterWithActiveMovieId(movieId); mSession = new MediaSessionCompat(getContext(), TAG); mSession.setFlags( MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS); mSession.setActive(true); MediaControllerCompat.setMediaController((Activity) getContext(), mSession.getController()); mPlayerGlue = new PrimaryPlaybackControlsGlue<>( getContext(), new MediaPlayerAdapter(getContext()), mSession.getController()); mPlayerGlue.setHost(new VideoFragmentGlueHost(this)); mPlayerGlue.addPlayerCallback(playWhenReadyPlayerCallback); mPlayerGlue.addPlayerCallback(playPausePlayerCallback); mMediaSessionCallback = new MediaSessionCallback(mPlayerGlue); mSession.setCallback(mMediaSessionCallback); playMedia(mPlaylistAdapter.getCurrentItem()); } private void playMedia(MediaDescriptionCompat media) { mPlayerGlue.setTitle(media.getTitle()); mPlayerGlue.setSubtitle(media.getDescription()); mPlayerGlue.getPlayerAdapter().setDataSource(media.getMediaUri()); } /** Checks if the glue is prepared before telling the glue to start playback. */ private static class PlayWhenReadyPlayerCallback extends PlaybackGlue.PlayerCallback { @Override public void onPreparedStateChanged(PlaybackGlue glue) { super.onPreparedStateChanged(glue); if (glue.isPrepared()) { glue.play(); } } } /** * Delegates media commands sent from the assistant to the glue.
* Note that Play/Pause are handled via key code input, not MediaSession. */ private class MediaSessionCallback extends MediaSessionCompat.Callback { private final PlaybackTransportControlGlue> mGlue; public MediaSessionCallback(PlaybackTransportControlGlue> glue) { mGlue = glue; } @Override public void onPlay() { Log.d(TAG, "MediaSessionCallback: onPlay()"); mGlue.play(); updatePlaybackStateToPlaying( mGlue.getCurrentPosition(), mPlaylistAdapter.getCurrentItem()); } @Override public void onPause() { Log.d(TAG, "MediaSessionCallback: onPause()"); mGlue.pause(); updatePlaybackState( PlaybackStateCompat.STATE_PAUSED, mGlue.getCurrentPosition(), mPlaylistAdapter.getCurrentItem()); } @Override public void onSeekTo(long position) { Log.d(TAG, "MediaSessionCallback: onSeekTo()"); mGlue.seekTo(position); updatePlaybackStateToPlaying(position, mPlaylistAdapter.getCurrentItem()); } /* * User wishes to stop playback. Finish activity and go back to where the user came * from, which might not be your app. For example, Assistant's search results. * * Note that if the user started playback of a TV series or a playlist, then we would * not want to destroy the media session. Instead we would release the player and not * destroy the fragment in case the user wants to continue on in the playlist/TV series. * Since this sample only demonstrates movies, we will destroy the session and the fragment. */ @Override public void onStop() { Log.d(TAG, "MediaSessionCallback: onStop()"); // In onDestroy(), the player will be released. If you are using ExoPlayer, you will // need to manually release the player. getActivity().finish(); } @Override public void onSkipToNext() { Log.d(TAG, "MediaSessionCallback: onSkipToNext()"); playAndUpdateMediaSession(mPlaylistAdapter.skipToNextItem()); } @Override public void onSkipToPrevious() { Log.d(TAG, "MediaSessionCallback: onSkipToPrevious()"); playAndUpdateMediaSession(mPlaylistAdapter.skipToPreviousItem()); } private void playAndUpdateMediaSession(MediaDescriptionCompat media) { playMedia(media); mSession.setMetadata(mPlaylistAdapter.mapToMetadata(media)); updatePlaybackStateToPlaying(0L, media); } private void updatePlaybackStateToPlaying( long position, MediaDescriptionCompat description) { updatePlaybackState(PlaybackStateCompat.STATE_PLAYING, position, description); } private void updatePlaybackState( @PlaybackStateCompat.State int state, long position, MediaDescriptionCompat description) { PlaybackStateCompat.Builder builder = new PlaybackStateCompat.Builder() .setActions(AVAILABLE_MEDIA_ACTIONS) .setActiveQueueItemId(Long.parseLong(description.getMediaId())) .setState(state, position, 1.0f); mSession.setPlaybackState(builder.build()); } } }