Gradle dependencies
compile group: 'androidx.leanback', name: 'leanback', version: '1.2.0-alpha04'
- groupId: androidx.leanback
- artifactId: leanback
- version: 1.2.0-alpha04
Artifact androidx.leanback:leanback:1.2.0-alpha04 it located at Google repository (https://maven.google.com/)
Androidx artifact mapping:
androidx.leanback:leanback com.android.support:leanback-v17
Androidx class mapping:
androidx.leanback.media.PlaybackBannerControlGlue android.support.v17.leanback.media.PlaybackBannerControlGlue
Overview
A helper class for managing a PlaybackControlsRow being displayed in
PlaybackGlueHost. It supports standard playback control actions play/pause and
skip next/previous. This helper class is a glue layer that manages interaction between the
leanback UI components PlaybackControlsRow PlaybackControlsRowPresenter
and a functional PlayerAdapter which represents the underlying
media player.
Apps must pass a PlayerAdapter in the constructor for a specific
implementation e.g. a MediaPlayerAdapter.
The glue has two action bars: primary action bars and secondary action bars. Apps
can provide additional actions by overriding PlaybackBannerControlGlue.onCreatePrimaryActions(ArrayObjectAdapter) and / or
PlaybackBaseControlGlue.onCreateSecondaryActions(ArrayObjectAdapter) and respond to actions by overriding
PlaybackBannerControlGlue.onActionClicked(Action).
The subclass is responsible for implementing the "repeat mode" in
PlaybackBannerControlGlue.onPlayCompleted().
Sample Code:
public class MyVideoFragment extends VideoFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
PlaybackBannerControlGlue playerGlue =
new PlaybackBannerControlGlue(getActivity(),
new MediaPlayerAdapter(getActivity()));
playerGlue.setHost(new VideoFragmentGlueHost(this));
playerGlue.setSubtitle("Leanback artist");
playerGlue.setTitle("Leanback team at work");
String uriPath = "android.resource://com.example.android.leanback/raw/video";
playerGlue.getPlayerAdapter().setDataSource(Uri.parse(uriPath));
playerGlue.playWhenPrepared();
}
}
Summary
Methods |
---|
public long | getCurrentPosition()
Gets current position of the player. |
public int[] | getFastForwardSpeeds()
Returns the fast forward speeds. |
public int[] | getRewindSpeeds()
Returns the rewind speeds. |
public void | onActionClicked(Action action)
Handles action clicks. |
protected void | onCreatePrimaryActions(ArrayObjectAdapter primaryActionsAdapter)
May be overridden to add primary actions to the adapter. |
protected PlaybackRowPresenter | onCreateRowPresenter()
|
public boolean | onKey(View v, int keyCode, KeyEvent event)
Handles key events and returns true if handled. |
protected void | onPlayCompleted()
Event when play finishes, subclass may handling repeat mode here. |
protected void | onPlayStateChanged()
Event when play state changed. |
public void | pause()
Pauses the media player. |
public void | play()
Starts the media player. |
public void | setControlsRow(PlaybackControlsRow controlsRow)
Sets the controls row to be managed by the glue layer. |
from PlaybackBaseControlGlue<T> | getArt, getBufferedPosition, getControlsRow, getDuration, getPlaybackRowPresenter, getPlayerAdapter, getSubtitle, getSupportedActions, getTitle, isControlsOverlayAutoHideEnabled, isPlaying, isPrepared, next, notifyItemChanged, onAttachedToHost, onCreateSecondaryActions, onDetachedFromHost, onHostStart, onHostStop, onMetadataChanged, onPreparedStateChanged, onUpdateBufferedProgress, onUpdateDuration, onUpdateProgress, previous, seekTo, setArt, setControlsOverlayAutoHideEnabled, setPlaybackRowPresenter, setSubtitle, setTitle |
from PlaybackGlue | addPlayerCallback, getContext, getHost, getPlayerCallbacks, onHostPause, onHostResume, playWhenPrepared, removePlayerCallback, setHost |
from java.lang.Object | clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
Fields
public static final int
ACTION_CUSTOM_LEFT_FIRSTThe adapter key for the first custom control on the left side
of the predefined primary controls.
public static final int
ACTION_SKIP_TO_PREVIOUSThe adapter key for the skip to previous control.
public static final int
ACTION_REWINDThe adapter key for the rewind control.
public static final int
ACTION_PLAY_PAUSEThe adapter key for the play/pause control.
public static final int
ACTION_FAST_FORWARDThe adapter key for the fast forward control.
public static final int
ACTION_SKIP_TO_NEXTThe adapter key for the skip to next control.
public static final int
ACTION_CUSTOM_RIGHT_FIRSTThe adapter key for the first custom control on the right side
of the predefined primary controls.
public static final int
PLAYBACK_SPEED_INVALIDInvalid playback speed.
public static final int
PLAYBACK_SPEED_PAUSEDSpeed representing playback state that is paused.
public static final int
PLAYBACK_SPEED_NORMALSpeed representing playback state that is playing normally.
public static final int
PLAYBACK_SPEED_FAST_L0The initial (level 0) fast forward playback speed.
The negative of this value is for rewind at the same speed.
public static final int
PLAYBACK_SPEED_FAST_L1The level 1 fast forward playback speed.
The negative of this value is for rewind at the same speed.
public static final int
PLAYBACK_SPEED_FAST_L2The level 2 fast forward playback speed.
The negative of this value is for rewind at the same speed.
public static final int
PLAYBACK_SPEED_FAST_L3The level 3 fast forward playback speed.
The negative of this value is for rewind at the same speed.
public static final int
PLAYBACK_SPEED_FAST_L4The level 4 fast forward playback speed.
The negative of this value is for rewind at the same speed.
Constructors
public
PlaybackBannerControlGlue(Context context, int[] seekSpeeds[],
PlayerAdapter impl)
Constructor for the glue.
Parameters:
context:
seekSpeeds: The array of seek speeds for fast forward and rewind. The maximum length of
the array is defined as NUMBER_OF_SEEK_SPEEDS.
impl: Implementation to underlying media player.
public
PlaybackBannerControlGlue(Context context, int[] fastForwardSpeeds[], int[] rewindSpeeds[],
PlayerAdapter impl)
Constructor for the glue.
Parameters:
context:
fastForwardSpeeds: The array of seek speeds for fast forward. The maximum length of
the array is defined as NUMBER_OF_SEEK_SPEEDS.
rewindSpeeds: The array of seek speeds for rewind. The maximum length of
the array is defined as NUMBER_OF_SEEK_SPEEDS.
impl: Implementation to underlying media player.
Methods
Sets the controls row to be managed by the glue layer. If
PlaybackControlsRow.getPrimaryActionsAdapter() is not provided, a default
ArrayObjectAdapter will be created and initialized in
PlaybackBaseControlGlue.onCreatePrimaryActions(ArrayObjectAdapter). If
PlaybackControlsRow.getSecondaryActionsAdapter() is not provided, a default
ArrayObjectAdapter will be created and initialized in
PlaybackBaseControlGlue.onCreateSecondaryActions(ArrayObjectAdapter).
The primary actions and playback state related aspects of the row
are updated by the glue.
May be overridden to add primary actions to the adapter. Default implementation add
.
Parameters:
primaryActionsAdapter: The adapter to add primary Actions.
public void
onActionClicked(
Action action)
Handles action clicks. A subclass may override this add support for additional actions.
public boolean
onKey(View v, int keyCode, KeyEvent event)
Handles key events and returns true if handled. A subclass may override this to provide
additional support.
protected void
onPlayStateChanged()
Event when play state changed.
protected void
onPlayCompleted()
Event when play finishes, subclass may handling repeat mode here.
public int[]
getFastForwardSpeeds()
Returns the fast forward speeds.
public int[]
getRewindSpeeds()
Returns the rewind speeds.
public long
getCurrentPosition()
Gets current position of the player. If the player is playing/paused, this
method returns current position from PlayerAdapter. Otherwise, if the player is
fastforwarding/rewinding, the method fake-pauses the PlayerAdapter and returns its
own calculated position.
Returns:
Current position of the player.
Starts the media player. Does nothing if PlaybackGlue.isPrepared() is false. To wait
PlaybackGlue.isPrepared() to be true before playing, use PlaybackGlue.playWhenPrepared().
Pauses the media player.
Source
/*
* 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 androidx.leanback.media;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.leanback.widget.AbstractDetailsDescriptionPresenter;
import androidx.leanback.widget.Action;
import androidx.leanback.widget.ArrayObjectAdapter;
import androidx.leanback.widget.ObjectAdapter;
import androidx.leanback.widget.PlaybackControlsRow;
import androidx.leanback.widget.PlaybackControlsRowPresenter;
import androidx.leanback.widget.PlaybackRowPresenter;
import androidx.leanback.widget.RowPresenter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* A helper class for managing a {@link PlaybackControlsRow} being displayed in
* {@link PlaybackGlueHost}. It supports standard playback control actions play/pause and
* skip next/previous. This helper class is a glue layer that manages interaction between the
* leanback UI components {@link PlaybackControlsRow} {@link PlaybackControlsRowPresenter}
* and a functional {@link PlayerAdapter} which represents the underlying
* media player.
*
* <p>Apps must pass a {@link PlayerAdapter} in the constructor for a specific
* implementation e.g. a {@link MediaPlayerAdapter}.
* </p>
*
* <p>The glue has two action bars: primary action bars and secondary action bars. Apps
* can provide additional actions by overriding {@link #onCreatePrimaryActions} and / or
* {@link #onCreateSecondaryActions} and respond to actions by overriding
* {@link #onActionClicked(Action)}.
* </p>
*
* <p>The subclass is responsible for implementing the "repeat mode" in
* {@link #onPlayCompleted()}.
* </p>
*
* Sample Code:
* <pre><code>
* public class MyVideoFragment extends VideoFragment {
* @Override
* public void onCreate(Bundle savedInstanceState) {
* super.onCreate(savedInstanceState);
* PlaybackBannerControlGlue<MediaPlayerAdapter> playerGlue =
* new PlaybackBannerControlGlue(getActivity(),
* new MediaPlayerAdapter(getActivity()));
* playerGlue.setHost(new VideoFragmentGlueHost(this));
* playerGlue.setSubtitle("Leanback artist");
* playerGlue.setTitle("Leanback team at work");
* String uriPath = "android.resource://com.example.android.leanback/raw/video";
* playerGlue.getPlayerAdapter().setDataSource(Uri.parse(uriPath));
* playerGlue.playWhenPrepared();
* }
* }
* </code></pre>
* @param <T> Type of {@link PlayerAdapter} passed in constructor.
*/
public class PlaybackBannerControlGlue<T extends PlayerAdapter>
extends PlaybackBaseControlGlue<T> {
@IntDef(
flag = true,
value = {
ACTION_CUSTOM_LEFT_FIRST,
ACTION_SKIP_TO_PREVIOUS,
ACTION_REWIND,
ACTION_PLAY_PAUSE,
ACTION_FAST_FORWARD,
ACTION_SKIP_TO_NEXT,
ACTION_CUSTOM_RIGHT_FIRST
})
@RestrictTo(LIBRARY_GROUP_PREFIX)
@Retention(RetentionPolicy.SOURCE)
public @interface ACTION_ {}
/**
* The adapter key for the first custom control on the left side
* of the predefined primary controls.
*/
public static final int ACTION_CUSTOM_LEFT_FIRST =
PlaybackBaseControlGlue.ACTION_CUSTOM_LEFT_FIRST;
/**
* The adapter key for the skip to previous control.
*/
public static final int ACTION_SKIP_TO_PREVIOUS =
PlaybackBaseControlGlue.ACTION_SKIP_TO_PREVIOUS;
/**
* The adapter key for the rewind control.
*/
public static final int ACTION_REWIND = PlaybackBaseControlGlue.ACTION_REWIND;
/**
* The adapter key for the play/pause control.
*/
public static final int ACTION_PLAY_PAUSE = PlaybackBaseControlGlue.ACTION_PLAY_PAUSE;
/**
* The adapter key for the fast forward control.
*/
public static final int ACTION_FAST_FORWARD = PlaybackBaseControlGlue.ACTION_FAST_FORWARD;
/**
* The adapter key for the skip to next control.
*/
public static final int ACTION_SKIP_TO_NEXT = PlaybackBaseControlGlue.ACTION_SKIP_TO_NEXT;
/**
* The adapter key for the first custom control on the right side
* of the predefined primary controls.
*/
public static final int ACTION_CUSTOM_RIGHT_FIRST =
PlaybackBaseControlGlue.ACTION_CUSTOM_RIGHT_FIRST;
@IntDef({
PLAYBACK_SPEED_INVALID,
PLAYBACK_SPEED_PAUSED,
PLAYBACK_SPEED_NORMAL,
PLAYBACK_SPEED_FAST_L0,
PLAYBACK_SPEED_FAST_L1,
PLAYBACK_SPEED_FAST_L2,
PLAYBACK_SPEED_FAST_L3,
PLAYBACK_SPEED_FAST_L4
})
@RestrictTo(LIBRARY_GROUP_PREFIX)
@Retention(RetentionPolicy.SOURCE)
private @interface SPEED {}
/**
* Invalid playback speed.
*/
public static final int PLAYBACK_SPEED_INVALID = -1;
/**
* Speed representing playback state that is paused.
*/
public static final int PLAYBACK_SPEED_PAUSED = 0;
/**
* Speed representing playback state that is playing normally.
*/
public static final int PLAYBACK_SPEED_NORMAL = 1;
/**
* The initial (level 0) fast forward playback speed.
* The negative of this value is for rewind at the same speed.
*/
public static final int PLAYBACK_SPEED_FAST_L0 = 10;
/**
* The level 1 fast forward playback speed.
* The negative of this value is for rewind at the same speed.
*/
public static final int PLAYBACK_SPEED_FAST_L1 = 11;
/**
* The level 2 fast forward playback speed.
* The negative of this value is for rewind at the same speed.
*/
public static final int PLAYBACK_SPEED_FAST_L2 = 12;
/**
* The level 3 fast forward playback speed.
* The negative of this value is for rewind at the same speed.
*/
public static final int PLAYBACK_SPEED_FAST_L3 = 13;
/**
* The level 4 fast forward playback speed.
* The negative of this value is for rewind at the same speed.
*/
public static final int PLAYBACK_SPEED_FAST_L4 = 14;
private static final String TAG = PlaybackBannerControlGlue.class.getSimpleName();
private static final int NUMBER_OF_SEEK_SPEEDS = PLAYBACK_SPEED_FAST_L4
- PLAYBACK_SPEED_FAST_L0 + 1;
private final int[] mFastForwardSpeeds;
private final int[] mRewindSpeeds;
private PlaybackControlsRow.SkipNextAction mSkipNextAction;
private PlaybackControlsRow.SkipPreviousAction mSkipPreviousAction;
private PlaybackControlsRow.FastForwardAction mFastForwardAction;
private PlaybackControlsRow.RewindAction mRewindAction;
@SPEED
private int mPlaybackSpeed = PLAYBACK_SPEED_PAUSED;
private long mStartTime;
private long mStartPosition = 0;
// Flag for is customized FastForward/ Rewind Action supported.
// If customized actions are not supported, the adapter can still use default behavior through
// setting ACTION_REWIND and ACTION_FAST_FORWARD as supported actions.
private boolean mIsCustomizedFastForwardSupported;
private boolean mIsCustomizedRewindSupported;
/**
* Constructor for the glue.
*
* @param context
* @param seekSpeeds The array of seek speeds for fast forward and rewind. The maximum length of
* the array is defined as NUMBER_OF_SEEK_SPEEDS.
* @param impl Implementation to underlying media player.
*/
public PlaybackBannerControlGlue(@NonNull Context context,
@NonNull int[] seekSpeeds,
T impl) {
this(context, seekSpeeds, seekSpeeds, impl);
}
/**
* Constructor for the glue.
*
* @param context
* @param fastForwardSpeeds The array of seek speeds for fast forward. The maximum length of
* the array is defined as NUMBER_OF_SEEK_SPEEDS.
* @param rewindSpeeds The array of seek speeds for rewind. The maximum length of
* the array is defined as NUMBER_OF_SEEK_SPEEDS.
* @param impl Implementation to underlying media player.
*/
public PlaybackBannerControlGlue(
@NonNull Context context,
@NonNull int[] fastForwardSpeeds,
@NonNull int[] rewindSpeeds,
T impl
) {
super(context, impl);
if (fastForwardSpeeds.length == 0 || fastForwardSpeeds.length > NUMBER_OF_SEEK_SPEEDS) {
throw new IllegalArgumentException("invalid fastForwardSpeeds array size");
}
mFastForwardSpeeds = fastForwardSpeeds;
if (rewindSpeeds.length == 0 || rewindSpeeds.length > NUMBER_OF_SEEK_SPEEDS) {
throw new IllegalArgumentException("invalid rewindSpeeds array size");
}
mRewindSpeeds = rewindSpeeds;
if ((mPlayerAdapter.getSupportedActions() & ACTION_FAST_FORWARD) != 0) {
mIsCustomizedFastForwardSupported = true;
}
if ((mPlayerAdapter.getSupportedActions() & ACTION_REWIND) != 0) {
mIsCustomizedRewindSupported = true;
}
}
@Override
public void setControlsRow(@NonNull PlaybackControlsRow controlsRow) {
super.setControlsRow(controlsRow);
onUpdatePlaybackState();
}
@Override
protected void onCreatePrimaryActions(@NonNull ArrayObjectAdapter primaryActionsAdapter) {
final long supportedActions = getSupportedActions();
if ((supportedActions & ACTION_SKIP_TO_PREVIOUS) != 0 && mSkipPreviousAction == null) {
primaryActionsAdapter.add(mSkipPreviousAction =
new PlaybackControlsRow.SkipPreviousAction(getContext()));
} else if ((supportedActions & ACTION_SKIP_TO_PREVIOUS) == 0
&& mSkipPreviousAction != null) {
primaryActionsAdapter.remove(mSkipPreviousAction);
mSkipPreviousAction = null;
}
if ((supportedActions & ACTION_REWIND) != 0 && mRewindAction == null) {
primaryActionsAdapter.add(mRewindAction =
new PlaybackControlsRow.RewindAction(getContext(), mRewindSpeeds.length));
} else if ((supportedActions & ACTION_REWIND) == 0 && mRewindAction != null) {
primaryActionsAdapter.remove(mRewindAction);
mRewindAction = null;
}
if ((supportedActions & ACTION_PLAY_PAUSE) != 0 && mPlayPauseAction == null) {
mPlayPauseAction = new PlaybackControlsRow.PlayPauseAction(getContext());
primaryActionsAdapter.add(mPlayPauseAction =
new PlaybackControlsRow.PlayPauseAction(getContext()));
} else if ((supportedActions & ACTION_PLAY_PAUSE) == 0 && mPlayPauseAction != null) {
primaryActionsAdapter.remove(mPlayPauseAction);
mPlayPauseAction = null;
}
if ((supportedActions & ACTION_FAST_FORWARD) != 0 && mFastForwardAction == null) {
mFastForwardAction = new PlaybackControlsRow.FastForwardAction(getContext(),
mFastForwardSpeeds.length);
primaryActionsAdapter.add(mFastForwardAction =
new PlaybackControlsRow.FastForwardAction(getContext(),
mFastForwardSpeeds.length));
} else if ((supportedActions & ACTION_FAST_FORWARD) == 0 && mFastForwardAction != null) {
primaryActionsAdapter.remove(mFastForwardAction);
mFastForwardAction = null;
}
if ((supportedActions & ACTION_SKIP_TO_NEXT) != 0 && mSkipNextAction == null) {
primaryActionsAdapter.add(mSkipNextAction =
new PlaybackControlsRow.SkipNextAction(getContext()));
} else if ((supportedActions & ACTION_SKIP_TO_NEXT) == 0 && mSkipNextAction != null) {
primaryActionsAdapter.remove(mSkipNextAction);
mSkipNextAction = null;
}
}
@NonNull
@Override
protected PlaybackRowPresenter onCreateRowPresenter() {
final AbstractDetailsDescriptionPresenter detailsPresenter =
new AbstractDetailsDescriptionPresenter() {
@Override
protected void onBindDescription(@NonNull ViewHolder
viewHolder, @NonNull Object object) {
PlaybackBannerControlGlue<?> glue = (PlaybackBannerControlGlue<?>) object;
viewHolder.getTitle().setText(glue.getTitle());
viewHolder.getSubtitle().setText(glue.getSubtitle());
}
};
PlaybackControlsRowPresenter rowPresenter =
new PlaybackControlsRowPresenter(detailsPresenter) {
@Override
protected void onBindRowViewHolder(
@NonNull RowPresenter.ViewHolder vh,
@NonNull Object item
) {
super.onBindRowViewHolder(vh, item);
vh.setOnKeyListener(PlaybackBannerControlGlue.this);
}
@Override
protected void onUnbindRowViewHolder(@NonNull RowPresenter.ViewHolder vh) {
super.onUnbindRowViewHolder(vh);
vh.setOnKeyListener(null);
}
};
return rowPresenter;
}
/**
* Handles action clicks. A subclass may override this add support for additional actions.
*/
@Override
public void onActionClicked(@NonNull Action action) {
dispatchAction(action, null);
}
/**
* Handles key events and returns true if handled. A subclass may override this to provide
* additional support.
*/
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_UP:
case KeyEvent.KEYCODE_DPAD_DOWN:
case KeyEvent.KEYCODE_DPAD_RIGHT:
case KeyEvent.KEYCODE_DPAD_LEFT:
case KeyEvent.KEYCODE_BACK:
case KeyEvent.KEYCODE_ESCAPE:
boolean abortSeek = mPlaybackSpeed >= PLAYBACK_SPEED_FAST_L0
|| mPlaybackSpeed <= -PLAYBACK_SPEED_FAST_L0;
if (abortSeek) {
play();
onUpdatePlaybackStatusAfterUserAction();
return keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE;
}
return false;
}
final ObjectAdapter primaryActionsAdapter = mControlsRow.getPrimaryActionsAdapter();
Action action = mControlsRow.getActionForKeyCode(primaryActionsAdapter, keyCode);
if (action == null) {
action = mControlsRow.getActionForKeyCode(mControlsRow.getSecondaryActionsAdapter(),
keyCode);
}
if (action != null) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
dispatchAction(action, event);
}
return true;
}
return false;
}
void onUpdatePlaybackStatusAfterUserAction() {
updatePlaybackState(mIsPlaying);
}
// Helper function to increment mPlaybackSpeed when necessary. The mPlaybackSpeed will control
// the UI of fast forward button in control row.
private void incrementFastForwardPlaybackSpeed() {
switch (mPlaybackSpeed) {
case PLAYBACK_SPEED_FAST_L0:
case PLAYBACK_SPEED_FAST_L1:
case PLAYBACK_SPEED_FAST_L2:
case PLAYBACK_SPEED_FAST_L3:
mPlaybackSpeed++;
break;
default:
mPlaybackSpeed = PLAYBACK_SPEED_FAST_L0;
break;
}
}
// Helper function to decrement mPlaybackSpeed when necessary. The mPlaybackSpeed will control
// the UI of rewind button in control row.
@SuppressLint("WrongConstant")
private void decrementRewindPlaybackSpeed() {
switch (mPlaybackSpeed) {
case -PLAYBACK_SPEED_FAST_L0:
case -PLAYBACK_SPEED_FAST_L1:
case -PLAYBACK_SPEED_FAST_L2:
case -PLAYBACK_SPEED_FAST_L3:
mPlaybackSpeed--;
break;
default:
mPlaybackSpeed = -PLAYBACK_SPEED_FAST_L0;
break;
}
}
/**
* Called when the given action is invoked, either by click or key event.
*/
boolean dispatchAction(Action action, KeyEvent keyEvent) {
boolean handled = false;
if (action == mPlayPauseAction) {
boolean canPlay = keyEvent == null
|| keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
|| keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY;
boolean canPause = keyEvent == null
|| keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
|| keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PAUSE;
// PLAY_PAUSE PLAY PAUSE
// playing paused paused
// paused playing playing
// ff/rw playing playing paused
if (canPause
&& (canPlay ? mPlaybackSpeed == PLAYBACK_SPEED_NORMAL :
mPlaybackSpeed != PLAYBACK_SPEED_PAUSED)) {
pause();
} else if (canPlay && mPlaybackSpeed != PLAYBACK_SPEED_NORMAL) {
play();
}
onUpdatePlaybackStatusAfterUserAction();
handled = true;
} else if (action == mSkipNextAction) {
next();
handled = true;
} else if (action == mSkipPreviousAction) {
previous();
handled = true;
} else if (action == mFastForwardAction) {
if (mPlayerAdapter.isPrepared() && mPlaybackSpeed < getMaxForwardSpeedId()) {
// When the customized fast forward action is available, it will be executed
// when fast forward button is pressed. If current media item is not playing, the UI
// will be updated to PLAYING status.
if (mIsCustomizedFastForwardSupported) {
// Change UI to Playing status.
mIsPlaying = true;
// Execute customized fast forward action.
mPlayerAdapter.fastForward();
} else {
// When the customized fast forward action is not supported, the fakePause
// operation is needed to stop the media item but still indicating the media
// item is playing from the UI perspective
// Also the fakePause() method must be called before
// incrementFastForwardPlaybackSpeed() method to make sure fake fast forward
// computation is accurate.
fakePause();
}
// Change mPlaybackSpeed to control the UI.
incrementFastForwardPlaybackSpeed();
onUpdatePlaybackStatusAfterUserAction();
}
handled = true;
} else if (action == mRewindAction) {
if (mPlayerAdapter.isPrepared() && mPlaybackSpeed > -getMaxRewindSpeedId()) {
if (mIsCustomizedFastForwardSupported) {
mIsPlaying = true;
mPlayerAdapter.rewind();
} else {
fakePause();
}
decrementRewindPlaybackSpeed();
onUpdatePlaybackStatusAfterUserAction();
}
handled = true;
}
return handled;
}
@Override
protected void onPlayStateChanged() {
if (DEBUG) Log.v(TAG, "onStateChanged");
onUpdatePlaybackState();
super.onPlayStateChanged();
}
@Override
protected void onPlayCompleted() {
super.onPlayCompleted();
mIsPlaying = false;
mPlaybackSpeed = PLAYBACK_SPEED_PAUSED;
mStartPosition = getCurrentPosition();
mStartTime = System.currentTimeMillis();
onUpdatePlaybackState();
}
void onUpdatePlaybackState() {
updatePlaybackState(mIsPlaying);
}
private void updatePlaybackState(boolean isPlaying) {
if (mControlsRow == null) {
return;
}
if (!isPlaying) {
onUpdateProgress();
mPlayerAdapter.setProgressUpdatingEnabled(false);
} else {
mPlayerAdapter.setProgressUpdatingEnabled(true);
}
if (mFadeWhenPlaying && getHost() != null) {
getHost().setControlsOverlayAutoHideEnabled(isPlaying);
}
final ArrayObjectAdapter primaryActionsAdapter =
(ArrayObjectAdapter) getControlsRow().getPrimaryActionsAdapter();
if (mPlayPauseAction != null) {
int index = !isPlaying
? PlaybackControlsRow.PlayPauseAction.INDEX_PLAY
: PlaybackControlsRow.PlayPauseAction.INDEX_PAUSE;
if (mPlayPauseAction.getIndex() != index) {
mPlayPauseAction.setIndex(index);
notifyItemChanged(primaryActionsAdapter, mPlayPauseAction);
}
}
if (mFastForwardAction != null) {
int index = 0;
if (mPlaybackSpeed >= PLAYBACK_SPEED_FAST_L0) {
index = mPlaybackSpeed - PLAYBACK_SPEED_FAST_L0 + 1;
}
if (mFastForwardAction.getIndex() != index) {
mFastForwardAction.setIndex(index);
notifyItemChanged(primaryActionsAdapter, mFastForwardAction);
}
}
if (mRewindAction != null) {
int index = 0;
if (mPlaybackSpeed <= -PLAYBACK_SPEED_FAST_L0) {
index = -mPlaybackSpeed - PLAYBACK_SPEED_FAST_L0 + 1;
}
if (mRewindAction.getIndex() != index) {
mRewindAction.setIndex(index);
notifyItemChanged(primaryActionsAdapter, mRewindAction);
}
}
}
/**
* Returns the fast forward speeds.
*/
@NonNull
public int[] getFastForwardSpeeds() {
return mFastForwardSpeeds;
}
/**
* Returns the rewind speeds.
*/
@NonNull
public int[] getRewindSpeeds() {
return mRewindSpeeds;
}
private int getMaxForwardSpeedId() {
return PLAYBACK_SPEED_FAST_L0 + (mFastForwardSpeeds.length - 1);
}
private int getMaxRewindSpeedId() {
return PLAYBACK_SPEED_FAST_L0 + (mRewindSpeeds.length - 1);
}
/**
* Gets current position of the player. If the player is playing/paused, this
* method returns current position from {@link PlayerAdapter}. Otherwise, if the player is
* fastforwarding/rewinding, the method fake-pauses the {@link PlayerAdapter} and returns its
* own calculated position.
* @return Current position of the player.
*/
@Override
public long getCurrentPosition() {
int speed;
if (mPlaybackSpeed == PlaybackControlGlue.PLAYBACK_SPEED_PAUSED
|| mPlaybackSpeed == PlaybackControlGlue.PLAYBACK_SPEED_NORMAL) {
// If the adapter is playing/paused, using the position from adapter instead.
return mPlayerAdapter.getCurrentPosition();
} else if (mPlaybackSpeed >= PlaybackControlGlue.PLAYBACK_SPEED_FAST_L0) {
// If fast forward operation is supported in this scenario, current player position
// can be get from mPlayerAdapter.getCurrentPosition() directly
if (mIsCustomizedFastForwardSupported) {
return mPlayerAdapter.getCurrentPosition();
}
int index = mPlaybackSpeed - PlaybackControlGlue.PLAYBACK_SPEED_FAST_L0;
speed = getFastForwardSpeeds()[index];
} else if (mPlaybackSpeed <= -PlaybackControlGlue.PLAYBACK_SPEED_FAST_L0) {
// If fast rewind is supported in this scenario, current player position
// can be get from mPlayerAdapter.getCurrentPosition() directly
if (mIsCustomizedRewindSupported) {
return mPlayerAdapter.getCurrentPosition();
}
int index = -mPlaybackSpeed - PlaybackControlGlue.PLAYBACK_SPEED_FAST_L0;
speed = -getRewindSpeeds()[index];
} else {
return -1;
}
long position = mStartPosition + (System.currentTimeMillis() - mStartTime) * speed;
if (position > getDuration()) {
mPlaybackSpeed = PLAYBACK_SPEED_PAUSED;
position = getDuration();
mPlayerAdapter.seekTo(position);
mStartPosition = 0;
pause();
} else if (position < 0) {
mPlaybackSpeed = PLAYBACK_SPEED_PAUSED;
position = 0;
mPlayerAdapter.seekTo(position);
mStartPosition = 0;
pause();
}
return position;
}
@Override
public void play() {
if (!mPlayerAdapter.isPrepared()) {
return;
}
// Solves the situation that a player pause at the end and click play button. At this case
// the player will restart from the beginning.
if (mPlaybackSpeed == PLAYBACK_SPEED_PAUSED
&& mPlayerAdapter.getCurrentPosition() >= mPlayerAdapter.getDuration()) {
mStartPosition = 0;
} else {
mStartPosition = getCurrentPosition();
}
mStartTime = System.currentTimeMillis();
mIsPlaying = true;
mPlaybackSpeed = PLAYBACK_SPEED_NORMAL;
mPlayerAdapter.seekTo(mStartPosition);
super.play();
onUpdatePlaybackState();
}
@Override
public void pause() {
mIsPlaying = false;
mPlaybackSpeed = PLAYBACK_SPEED_PAUSED;
mStartPosition = getCurrentPosition();
mStartTime = System.currentTimeMillis();
super.pause();
onUpdatePlaybackState();
}
/**
* Control row shows PLAY, but the media is actually paused when the player is
* fastforwarding/rewinding.
*/
private void fakePause() {
mIsPlaying = true;
mStartPosition = getCurrentPosition();
mStartTime = System.currentTimeMillis();
super.pause();
onUpdatePlaybackState();
}
}