blob: bc370d8a8704aa9b075f9d8f508001bee5edf4b0 [file] [log] [blame]
/*
* Copyright 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.android.widget;
import android.content.res.Resources;
import android.media.MediaMetadata;
import android.media.session.MediaController;
import android.media.session.PlaybackState;
import android.media.update.MediaControlView2Provider;
import android.media.update.ViewProvider;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
import android.widget.ImageButton;
import android.widget.MediaControlView2;
import android.widget.ProgressBar;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import com.android.media.update.ApiHelper;
import com.android.media.update.R;
import java.util.Formatter;
import java.util.Locale;
public class MediaControlView2Impl implements MediaControlView2Provider {
private static final String TAG = "MediaControlView2";
private final MediaControlView2 mInstance;
private final ViewProvider mSuperProvider;
static final String COMMAND_SHOW_SUBTITLE = "showSubtitle";
static final String COMMAND_HIDE_SUBTITLE = "hideSubtitle";
static final String COMMAND_SET_FULLSCREEN = "setFullscreen";
static final String ARGUMENT_KEY_FULLSCREEN = "fullScreen";
static final String KEY_STATE_CONTAINS_SUBTITLE = "StateContainsSubtitle";
static final String EVENT_UPDATE_SUBTITLE_STATUS = "UpdateSubtitleStatus";
private static final int MAX_PROGRESS = 1000;
private static final int DEFAULT_PROGRESS_UPDATE_TIME_MS = 1000;
private static final int DEFAULT_TIMEOUT_MS = 2000;
private static final int REWIND_TIME_MS = 10000;
private static final int FORWARD_TIME_MS = 30000;
private final AccessibilityManager mAccessibilityManager;
private MediaController mController;
private MediaController.TransportControls mControls;
private PlaybackState mPlaybackState;
private MediaMetadata mMetadata;
private ProgressBar mProgress;
private TextView mEndTime, mCurrentTime;
private TextView mTitleView;
private int mDuration;
private int mPrevState;
private long mPlaybackActions;
private boolean mShowing;
private boolean mDragging;
private boolean mIsFullScreen;
private boolean mOverflowExpanded;
private boolean mIsStopped;
private boolean mSubtitleIsEnabled;
private boolean mContainsSubtitle;
private boolean mSeekAvailable;
private View.OnClickListener mNextListener, mPrevListener;
private ImageButton mPlayPauseButton;
private ImageButton mFfwdButton;
private ImageButton mRewButton;
private ImageButton mNextButton;
private ImageButton mPrevButton;
private ViewGroup mBasicControls;
private ImageButton mSubtitleButton;
private ImageButton mFullScreenButton;
private ImageButton mOverflowButtonRight;
private ViewGroup mExtraControls;
private ImageButton mOverflowButtonLeft;
private ImageButton mMuteButton;
private ImageButton mAspectRationButton;
private ImageButton mSettingsButton;
private CharSequence mPlayDescription;
private CharSequence mPauseDescription;
private CharSequence mReplayDescription;
private StringBuilder mFormatBuilder;
private Formatter mFormatter;
public MediaControlView2Impl(
MediaControlView2 instance, ViewProvider superProvider) {
mInstance = instance;
mSuperProvider = superProvider;
mAccessibilityManager = AccessibilityManager.getInstance(mInstance.getContext());
// Inflate MediaControlView2 from XML
View root = makeControllerView();
mInstance.addView(root);
}
@Override
public void setController_impl(MediaController controller) {
mController = controller;
if (controller != null) {
mControls = controller.getTransportControls();
// Set mMetadata and mPlaybackState to existing MediaSession variables since they may
// be called before the callback is called
mPlaybackState = mController.getPlaybackState();
mMetadata = mController.getMetadata();
updateDuration();
updateTitle();
mController.registerCallback(new MediaControllerCallback());
}
}
@Override
public void show_impl() {
mInstance.show(DEFAULT_TIMEOUT_MS);
}
@Override
public void show_impl(int timeout) {
if (!mShowing) {
setProgress();
if (mPlayPauseButton != null) {
mPlayPauseButton.requestFocus();
}
disableUnsupportedButtons();
mInstance.setVisibility(View.VISIBLE);
mShowing = true;
}
// cause the progress bar to be updated even if mShowing
// was already true. This happens, for example, if we're
// paused with the progress bar showing the user hits play.
mInstance.post(mShowProgress);
if (timeout != 0 && !mAccessibilityManager.isTouchExplorationEnabled()) {
mInstance.removeCallbacks(mFadeOut);
mInstance.postDelayed(mFadeOut, timeout);
}
}
@Override
public boolean isShowing_impl() {
return mShowing;
}
@Override
public void hide_impl() {
if (mShowing) {
try {
mInstance.removeCallbacks(mShowProgress);
// Remove existing call to mFadeOut to avoid from being called later.
mInstance.removeCallbacks(mFadeOut);
mInstance.setVisibility(View.GONE);
} catch (IllegalArgumentException ex) {
Log.w(TAG, "already removed");
}
mShowing = false;
}
}
@Override
public void showSubtitle_impl() {
mController.sendCommand(COMMAND_SHOW_SUBTITLE, null, null);
}
@Override
public void hideSubtitle_impl() {
mController.sendCommand(COMMAND_HIDE_SUBTITLE, null, null);
}
@Override
public void setPrevNextListeners_impl(View.OnClickListener next, View.OnClickListener prev) {
mNextListener = next;
mPrevListener = prev;
if (mNextButton != null) {
mNextButton.setOnClickListener(mNextListener);
mNextButton.setEnabled(mNextListener != null);
mNextButton.setVisibility(View.VISIBLE);
}
if (mPrevButton != null) {
mPrevButton.setOnClickListener(mPrevListener);
mPrevButton.setEnabled(mPrevListener != null);
mPrevButton.setVisibility(View.VISIBLE);
}
}
@Override
public void setButtonVisibility_impl(int button, boolean visible) {
switch (button) {
case MediaControlView2.BUTTON_PLAY_PAUSE:
if (mPlayPauseButton != null && canPause()) {
mPlayPauseButton.setVisibility((visible) ? View.VISIBLE : View.GONE);
}
break;
case MediaControlView2.BUTTON_FFWD:
if (mFfwdButton != null && canSeekForward()) {
mFfwdButton.setVisibility((visible) ? View.VISIBLE : View.GONE);
}
break;
case MediaControlView2.BUTTON_REW:
if (mRewButton != null && canSeekBackward()) {
mRewButton.setVisibility((visible) ? View.VISIBLE : View.GONE);
}
break;
case MediaControlView2.BUTTON_NEXT:
// TODO: this button is not visible unless its listener is manually set. Should this
// function still be provided?
if (mNextButton != null) {
mNextButton.setVisibility((visible) ? View.VISIBLE : View.GONE);
}
break;
case MediaControlView2.BUTTON_PREV:
// TODO: this button is not visible unless its listener is manually set. Should this
// function still be provided?
if (mPrevButton != null) {
mPrevButton.setVisibility((visible) ? View.VISIBLE : View.GONE);
}
break;
case MediaControlView2.BUTTON_SUBTITLE:
if (mSubtitleButton != null && mContainsSubtitle) {
mSubtitleButton.setVisibility((visible) ? View.VISIBLE : View.GONE);
}
break;
case MediaControlView2.BUTTON_FULL_SCREEN:
if (mFullScreenButton != null) {
mFullScreenButton.setVisibility((visible) ? View.VISIBLE : View.GONE);
}
break;
case MediaControlView2.BUTTON_OVERFLOW:
if (mOverflowButtonRight != null) {
mOverflowButtonRight.setVisibility((visible) ? View.VISIBLE : View.GONE);
}
break;
case MediaControlView2.BUTTON_MUTE:
if (mMuteButton != null) {
mMuteButton.setVisibility((visible) ? View.VISIBLE : View.GONE);
}
break;
case MediaControlView2.BUTTON_ASPECT_RATIO:
if (mAspectRationButton != null) {
mAspectRationButton.setVisibility((visible) ? View.VISIBLE : View.GONE);
}
break;
case MediaControlView2.BUTTON_SETTINGS:
if (mSettingsButton != null) {
mSettingsButton.setVisibility((visible) ? View.VISIBLE : View.GONE);
}
break;
default:
break;
}
}
@Override
public void onAttachedToWindow_impl() {
mSuperProvider.onAttachedToWindow_impl();
}
@Override
public void onDetachedFromWindow_impl() {
mSuperProvider.onDetachedFromWindow_impl();
}
@Override
public CharSequence getAccessibilityClassName_impl() {
return MediaControlView2.class.getName();
}
@Override
public boolean onTouchEvent_impl(MotionEvent ev) {
return false;
}
// TODO: Should this function be removed?
@Override
public boolean onTrackballEvent_impl(MotionEvent ev) {
mInstance.show(DEFAULT_TIMEOUT_MS);
return false;
}
@Override
public boolean onKeyDown_impl(int keyCode, KeyEvent event) {
return mSuperProvider.onKeyDown_impl(keyCode, event);
}
@Override
public void onFinishInflate_impl() {
mSuperProvider.onFinishInflate_impl();
}
@Override
public boolean dispatchKeyEvent_impl(KeyEvent event) {
int keyCode = event.getKeyCode();
final boolean uniqueDown = event.getRepeatCount() == 0
&& event.getAction() == KeyEvent.ACTION_DOWN;
if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK
|| keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
|| keyCode == KeyEvent.KEYCODE_SPACE) {
if (uniqueDown) {
togglePausePlayState();
mInstance.show(DEFAULT_TIMEOUT_MS);
if (mPlayPauseButton != null) {
mPlayPauseButton.requestFocus();
}
}
return true;
} else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
if (uniqueDown && !isPlaying()) {
togglePausePlayState();
mInstance.show(DEFAULT_TIMEOUT_MS);
}
return true;
} else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP
|| keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
if (uniqueDown && isPlaying()) {
togglePausePlayState();
mInstance.show(DEFAULT_TIMEOUT_MS);
}
return true;
} else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
|| keyCode == KeyEvent.KEYCODE_VOLUME_UP
|| keyCode == KeyEvent.KEYCODE_VOLUME_MUTE
|| keyCode == KeyEvent.KEYCODE_CAMERA) {
// don't show the controls for volume adjustment
return mSuperProvider.dispatchKeyEvent_impl(event);
} else if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU) {
if (uniqueDown) {
mInstance.hide();
}
return true;
}
mInstance.show(DEFAULT_TIMEOUT_MS);
return mSuperProvider.dispatchKeyEvent_impl(event);
}
@Override
public void setEnabled_impl(boolean enabled) {
if (mPlayPauseButton != null) {
mPlayPauseButton.setEnabled(enabled);
}
if (mFfwdButton != null) {
mFfwdButton.setEnabled(enabled);
}
if (mRewButton != null) {
mRewButton.setEnabled(enabled);
}
if (mNextButton != null) {
mNextButton.setEnabled(enabled);
}
if (mPrevButton != null) {
mPrevButton.setEnabled(enabled);
}
if (mProgress != null) {
mProgress.setEnabled(enabled);
}
disableUnsupportedButtons();
mSuperProvider.setEnabled_impl(enabled);
}
///////////////////////////////////////////////////
// Protected or private methods
///////////////////////////////////////////////////
private boolean isPlaying() {
if (mPlaybackState != null) {
return mPlaybackState.getState() == PlaybackState.STATE_PLAYING;
}
return false;
}
private int getCurrentPosition() {
mPlaybackState = mController.getPlaybackState();
if (mPlaybackState != null) {
return (int) mPlaybackState.getPosition();
}
return 0;
}
private int getBufferPercentage() {
if (mDuration == 0) {
return 0;
}
mPlaybackState = mController.getPlaybackState();
if (mPlaybackState != null) {
return (int) (mPlaybackState.getBufferedPosition() * 100) / mDuration;
}
return 0;
}
private boolean canPause() {
if (mPlaybackState != null) {
return (mPlaybackState.getActions() & PlaybackState.ACTION_PAUSE) != 0;
}
return true;
}
private boolean canSeekBackward() {
if (mPlaybackState != null) {
return (mPlaybackState.getActions() & PlaybackState.ACTION_REWIND) != 0;
}
return true;
}
private boolean canSeekForward() {
if (mPlaybackState != null) {
return (mPlaybackState.getActions() & PlaybackState.ACTION_FAST_FORWARD) != 0;
}
return true;
}
/**
* Create the view that holds the widgets that control playback.
* Derived classes can override this to create their own.
*
* @return The controller view.
* @hide This doesn't work as advertised
*/
protected View makeControllerView() {
View root = ApiHelper.inflateLibLayout(mInstance.getContext(), R.layout.media_controller);
initControllerView(root);
return root;
}
private void initControllerView(View v) {
Resources res = ApiHelper.getLibResources();
mPlayDescription = res.getText(R.string.lockscreen_play_button_content_description);
mPauseDescription = res.getText(R.string.lockscreen_pause_button_content_description);
mReplayDescription = res.getText(R.string.lockscreen_replay_button_content_description);
mPlayPauseButton = v.findViewById(R.id.pause);
if (mPlayPauseButton != null) {
mPlayPauseButton.requestFocus();
mPlayPauseButton.setOnClickListener(mPlayPauseListener);
mPlayPauseButton.setColorFilter(R.integer.gray);
mPlayPauseButton.setEnabled(false);
}
mFfwdButton = v.findViewById(R.id.ffwd);
if (mFfwdButton != null) {
mFfwdButton.setOnClickListener(mFfwdListener);
mFfwdButton.setColorFilter(R.integer.gray);
mFfwdButton.setEnabled(false);
}
mRewButton = v.findViewById(R.id.rew);
if (mRewButton != null) {
mRewButton.setOnClickListener(mRewListener);
mRewButton.setColorFilter(R.integer.gray);
mRewButton.setEnabled(false);
}
mNextButton = v.findViewById(R.id.next);
if (mNextButton != null) {
mNextButton.setVisibility(View.GONE);
}
mPrevButton = v.findViewById(R.id.prev);
if (mPrevButton != null) {
mPrevButton.setVisibility(View.GONE);
}
mBasicControls = v.findViewById(R.id.basic_controls);
mSubtitleButton = v.findViewById(R.id.subtitle);
if (mSubtitleButton != null) {
mSubtitleButton.setOnClickListener(mSubtitleListener);
mSubtitleButton.setColorFilter(R.integer.gray);
mSubtitleButton.setEnabled(false);
}
mFullScreenButton = v.findViewById(R.id.fullscreen);
if (mFullScreenButton != null) {
mFullScreenButton.setOnClickListener(mFullScreenListener);
// TODO: Show Fullscreen button when only it is possible.
}
mOverflowButtonRight = v.findViewById(R.id.overflow_right);
if (mOverflowButtonRight != null) {
mOverflowButtonRight.setOnClickListener(mOverflowRightListener);
}
// TODO: should these buttons be shown as default?
mExtraControls = v.findViewById(R.id.extra_controls);
mOverflowButtonLeft = v.findViewById(R.id.overflow_left);
if (mOverflowButtonLeft != null) {
mOverflowButtonLeft.setOnClickListener(mOverflowLeftListener);
}
mMuteButton = v.findViewById(R.id.mute);
mAspectRationButton = v.findViewById(R.id.aspect_ratio);
mSettingsButton = v.findViewById(R.id.settings);
mProgress = v.findViewById(R.id.mediacontroller_progress);
if (mProgress != null) {
if (mProgress instanceof SeekBar) {
SeekBar seeker = (SeekBar) mProgress;
seeker.setOnSeekBarChangeListener(mSeekListener);
}
mProgress.setMax(MAX_PROGRESS);
}
mTitleView = v.findViewById(R.id.title_text);
mEndTime = v.findViewById(R.id.time);
mCurrentTime = v.findViewById(R.id.time_current);
mFormatBuilder = new StringBuilder();
mFormatter = new Formatter(mFormatBuilder, Locale.getDefault());
}
/**
* Disable pause or seek buttons if the stream cannot be paused or seeked.
* This requires the control interface to be a MediaPlayerControlExt
*/
private void disableUnsupportedButtons() {
try {
if (mPlayPauseButton != null && !canPause()) {
mPlayPauseButton.setEnabled(false);
}
if (mRewButton != null && !canSeekBackward()) {
mRewButton.setEnabled(false);
}
if (mFfwdButton != null && !canSeekForward()) {
mFfwdButton.setEnabled(false);
}
// TODO What we really should do is add a canSeek to the MediaPlayerControl interface;
// this scheme can break the case when applications want to allow seek through the
// progress bar but disable forward/backward buttons.
//
// However, currently the flags SEEK_BACKWARD_AVAILABLE, SEEK_FORWARD_AVAILABLE,
// and SEEK_AVAILABLE are all (un)set together; as such the aforementioned issue
// shouldn't arise in existing applications.
if (mProgress != null && !canSeekBackward() && !canSeekForward()) {
mProgress.setEnabled(false);
}
} catch (IncompatibleClassChangeError ex) {
// We were given an old version of the interface, that doesn't have
// the canPause/canSeekXYZ methods. This is OK, it just means we
// assume the media can be paused and seeked, and so we don't disable
// the buttons.
}
}
private final Runnable mFadeOut = new Runnable() {
@Override
public void run() {
if (isPlaying()) {
mInstance.hide();
}
}
};
private final Runnable mShowProgress = new Runnable() {
@Override
public void run() {
int pos = setProgress();
if (!mDragging && mShowing && isPlaying()) {
mInstance.postDelayed(mShowProgress,
DEFAULT_PROGRESS_UPDATE_TIME_MS - (pos % DEFAULT_PROGRESS_UPDATE_TIME_MS));
}
}
};
private String stringForTime(int timeMs) {
int totalSeconds = timeMs / 1000;
int seconds = totalSeconds % 60;
int minutes = (totalSeconds / 60) % 60;
int hours = totalSeconds / 3600;
mFormatBuilder.setLength(0);
if (hours > 0) {
return mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).toString();
} else {
return mFormatter.format("%02d:%02d", minutes, seconds).toString();
}
}
private int setProgress() {
if (mController == null || mDragging) {
return 0;
}
int positionOnProgressBar = 0;
int currentPosition = getCurrentPosition();
if (mDuration > 0) {
positionOnProgressBar = (int) (MAX_PROGRESS * (long) currentPosition / mDuration);
}
if (mProgress != null && currentPosition != mDuration) {
mProgress.setProgress(positionOnProgressBar);
mProgress.setSecondaryProgress(getBufferPercentage() * 10);
}
if (mEndTime != null) {
mEndTime.setText(stringForTime(mDuration));
}
if (mCurrentTime != null) {
mCurrentTime.setText(stringForTime(currentPosition));
}
return currentPosition;
}
private void togglePausePlayState() {
if (isPlaying()) {
mControls.pause();
mPlayPauseButton.setImageDrawable(
ApiHelper.getLibResources().getDrawable(
R.drawable.ic_play_circle_filled, null));
mPlayPauseButton.setContentDescription(mPlayDescription);
} else {
mControls.play();
mPlayPauseButton.setImageDrawable(
ApiHelper.getLibResources().getDrawable(
R.drawable.ic_pause_circle_filled, null));
mPlayPauseButton.setContentDescription(mPauseDescription);
}
}
// There are two scenarios that can trigger the seekbar listener to trigger:
//
// The first is the user using the touchpad to adjust the posititon of the
// seekbar's thumb. In this case onStartTrackingTouch is called followed by
// a number of onProgressChanged notifications, concluded by onStopTrackingTouch.
// We're setting the field "mDragging" to true for the duration of the dragging
// session to avoid jumps in the position in case of ongoing playback.
//
// The second scenario involves the user operating the scroll ball, in this
// case there WON'T BE onStartTrackingTouch/onStopTrackingTouch notifications,
// we will simply apply the updated position without suspending regular updates.
private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
@Override
public void onStartTrackingTouch(SeekBar bar) {
if (!mSeekAvailable) {
return;
}
mInstance.show(3600000);
mDragging = true;
// By removing these pending progress messages we make sure
// that a) we won't update the progress while the user adjusts
// the seekbar and b) once the user is done dragging the thumb
// we will post one of these messages to the queue again and
// this ensures that there will be exactly one message queued up.
mInstance.removeCallbacks(mShowProgress);
// Check if playback is currently stopped. In this case, update the pause button to
// show the play image instead of the replay image.
if (mIsStopped) {
mPlayPauseButton.setImageDrawable(
ApiHelper.getLibResources().getDrawable(
R.drawable.ic_play_circle_filled, null));
mPlayPauseButton.setContentDescription(mPlayDescription);
mIsStopped = false;
}
}
@Override
public void onProgressChanged(SeekBar bar, int progress, boolean fromUser) {
if (!mSeekAvailable) {
return;
}
if (!fromUser) {
// We're not interested in programmatically generated changes to
// the progress bar's position.
return;
}
if (mDuration > 0) {
int newPosition = (int) (((long) mDuration * progress) / MAX_PROGRESS);
mControls.seekTo(newPosition);
if (mCurrentTime != null) {
mCurrentTime.setText(stringForTime(newPosition));
}
}
}
@Override
public void onStopTrackingTouch(SeekBar bar) {
if (!mSeekAvailable) {
return;
}
mDragging = false;
setProgress();
mInstance.show(DEFAULT_TIMEOUT_MS);
// Ensure that progress is properly updated in the future,
// the call to show() does not guarantee this because it is a
// no-op if we are already showing.
mInstance.post(mShowProgress);
}
};
private final View.OnClickListener mPlayPauseListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
togglePausePlayState();
mInstance.show(DEFAULT_TIMEOUT_MS);
}
};
private final View.OnClickListener mRewListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
int pos = getCurrentPosition() - REWIND_TIME_MS;
mControls.seekTo(pos);
setProgress();
mInstance.show(DEFAULT_TIMEOUT_MS);
}
};
private final View.OnClickListener mFfwdListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
int pos = getCurrentPosition() + FORWARD_TIME_MS;
mControls.seekTo(pos);
setProgress();
mInstance.show(DEFAULT_TIMEOUT_MS);
}
};
private final View.OnClickListener mSubtitleListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!mSubtitleIsEnabled) {
mSubtitleButton.setImageDrawable(
ApiHelper.getLibResources().getDrawable(
R.drawable.ic_media_subtitle_enabled, null));
mInstance.showSubtitle();
mSubtitleIsEnabled = true;
} else {
mSubtitleButton.setImageDrawable(
ApiHelper.getLibResources().getDrawable(
R.drawable.ic_media_subtitle_disabled, null));
mInstance.hideSubtitle();
mSubtitleIsEnabled = false;
}
mInstance.show(DEFAULT_TIMEOUT_MS);
}
};
private final View.OnClickListener mFullScreenListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
final boolean isEnteringFullScreen = !mIsFullScreen;
// TODO: Re-arrange the button layouts according to the UX.
if (isEnteringFullScreen) {
mFullScreenButton.setImageDrawable(
ApiHelper.getLibResources().getDrawable(
R.drawable.ic_fullscreen_exit, null));
} else {
mFullScreenButton.setImageDrawable(
ApiHelper.getLibResources().getDrawable(R.drawable.ic_fullscreen, null));
}
Bundle args = new Bundle();
args.putBoolean(ARGUMENT_KEY_FULLSCREEN, isEnteringFullScreen);
mController.sendCommand(COMMAND_SET_FULLSCREEN, args, null);
mIsFullScreen = isEnteringFullScreen;
mInstance.show(DEFAULT_TIMEOUT_MS);
}
};
private final View.OnClickListener mOverflowRightListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
mBasicControls.setVisibility(View.GONE);
mExtraControls.setVisibility(View.VISIBLE);
mInstance.show(DEFAULT_TIMEOUT_MS);
}
};
private final View.OnClickListener mOverflowLeftListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
mBasicControls.setVisibility(View.VISIBLE);
mExtraControls.setVisibility(View.GONE);
}
};
private void updateDuration() {
if (mMetadata != null) {
if (mMetadata.containsKey(MediaMetadata.METADATA_KEY_DURATION)) {
mDuration = (int) mMetadata.getLong(MediaMetadata.METADATA_KEY_DURATION);
// update progress bar
setProgress();
}
}
}
private void updateTitle() {
if (mMetadata != null) {
if (mMetadata.containsKey(MediaMetadata.METADATA_KEY_TITLE)) {
mTitleView.setText(mMetadata.getString(MediaMetadata.METADATA_KEY_TITLE));
}
}
}
private class MediaControllerCallback extends MediaController.Callback {
@Override
public void onPlaybackStateChanged(PlaybackState state) {
mPlaybackState = state;
// Update pause button depending on playback state for the following two reasons:
// 1) Need to handle case where app customizes playback state behavior when app
// activity is resumed.
// 2) Need to handle case where the media file reaches end of duration.
if (mPlaybackState.getState() != mPrevState) {
switch (mPlaybackState.getState()) {
case PlaybackState.STATE_PLAYING:
mPlayPauseButton.setImageDrawable(
ApiHelper.getLibResources().getDrawable(
R.drawable.ic_pause_circle_filled, null));
mPlayPauseButton.setContentDescription(mPauseDescription);
break;
case PlaybackState.STATE_PAUSED:
mPlayPauseButton.setImageDrawable(
ApiHelper.getLibResources().getDrawable(
R.drawable.ic_play_circle_filled, null));
mPlayPauseButton.setContentDescription(mPlayDescription);
break;
case PlaybackState.STATE_STOPPED:
mPlayPauseButton.setImageDrawable(
ApiHelper.getLibResources().getDrawable(
R.drawable.ic_replay, null));
mPlayPauseButton.setContentDescription(mReplayDescription);
mIsStopped = true;
break;
default:
break;
}
mPrevState = mPlaybackState.getState();
}
if (mPlaybackActions != mPlaybackState.getActions()) {
long newActions = mPlaybackState.getActions();
if ((newActions & PlaybackState.ACTION_PAUSE) != 0) {
mPlayPauseButton.clearColorFilter();
mPlayPauseButton.setEnabled(true);
}
if ((newActions & PlaybackState.ACTION_REWIND) != 0) {
mRewButton.clearColorFilter();
mRewButton.setEnabled(true);
}
if ((newActions & PlaybackState.ACTION_FAST_FORWARD) != 0) {
mFfwdButton.clearColorFilter();
mFfwdButton.setEnabled(true);
}
if ((newActions & PlaybackState.ACTION_SEEK_TO) != 0) {
mSeekAvailable = true;
} else {
mSeekAvailable = false;
}
mPlaybackActions = newActions;
}
}
@Override
public void onMetadataChanged(MediaMetadata metadata) {
mMetadata = metadata;
updateDuration();
updateTitle();
}
@Override
public void onSessionEvent(String event, Bundle extras) {
if (event.equals(EVENT_UPDATE_SUBTITLE_STATUS)) {
boolean newSubtitleStatus = extras.getBoolean(KEY_STATE_CONTAINS_SUBTITLE);
if (newSubtitleStatus != mContainsSubtitle) {
if (newSubtitleStatus) {
mSubtitleButton.clearColorFilter();
mSubtitleButton.setEnabled(true);
} else {
mSubtitleButton.setColorFilter(R.integer.gray);
mSubtitleButton.setEnabled(false);
}
mContainsSubtitle = newSubtitleStatus;
}
}
}
}
}