blob: a91c7b03cb0e9b2de0d4d6ea37f86c4f3513e91f [file] [log] [blame]
/*
* 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;
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 {
* &#64;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> {
/** @hide */
@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)
@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;
/** @hide */
@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)
@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.PlayPauseAction mPlayPauseAction;
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(Context context,
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(Context context,
int[] fastForwardSpeeds,
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(PlaybackControlsRow controlsRow) {
super.setControlsRow(controlsRow);
onUpdatePlaybackState();
}
@Override
protected void onCreatePrimaryActions(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;
}
}
@Override
protected PlaybackRowPresenter onCreateRowPresenter() {
final AbstractDetailsDescriptionPresenter detailsPresenter =
new AbstractDetailsDescriptionPresenter() {
@Override
protected void onBindDescription(ViewHolder
viewHolder, 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(RowPresenter.ViewHolder vh, Object item) {
super.onBindRowViewHolder(vh, item);
vh.setOnKeyListener(PlaybackBannerControlGlue.this);
}
@Override
protected void onUnbindRowViewHolder(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(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.
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.PLAY
: PlaybackControlsRow.PlayPauseAction.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();
}
}