Home | History | Annotate | Download | only in widget
      1 /*
      2  * Copyright 2018 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package androidx.media.widget;
     18 
     19 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
     20 
     21 import android.content.Context;
     22 import android.content.res.Resources;
     23 import android.graphics.Point;
     24 import android.os.Bundle;
     25 import android.support.v4.media.MediaMetadataCompat;
     26 import android.support.v4.media.session.MediaControllerCompat;
     27 import android.support.v4.media.session.PlaybackStateCompat;
     28 import android.util.AttributeSet;
     29 import android.view.Gravity;
     30 import android.view.KeyEvent;
     31 import android.view.LayoutInflater;
     32 import android.view.MotionEvent;
     33 import android.view.View;
     34 import android.view.ViewGroup;
     35 import android.view.WindowManager;
     36 import android.widget.AdapterView;
     37 import android.widget.BaseAdapter;
     38 import android.widget.ImageButton;
     39 import android.widget.ImageView;
     40 import android.widget.LinearLayout;
     41 import android.widget.ListView;
     42 import android.widget.PopupWindow;
     43 import android.widget.ProgressBar;
     44 import android.widget.RelativeLayout;
     45 import android.widget.SeekBar;
     46 import android.widget.SeekBar.OnSeekBarChangeListener;
     47 import android.widget.TextView;
     48 
     49 import androidx.annotation.IntDef;
     50 import androidx.annotation.NonNull;
     51 import androidx.annotation.Nullable;
     52 import androidx.annotation.RequiresApi;
     53 import androidx.annotation.RestrictTo;
     54 import androidx.media.SessionToken2;
     55 import androidx.mediarouter.app.MediaRouteButton;
     56 import androidx.mediarouter.media.MediaRouteSelector;
     57 
     58 import java.lang.annotation.Retention;
     59 import java.lang.annotation.RetentionPolicy;
     60 import java.util.ArrayList;
     61 import java.util.Arrays;
     62 import java.util.Formatter;
     63 import java.util.List;
     64 import java.util.Locale;
     65 
     66 /**
     67  * A View that contains the controls for {@link android.media.MediaPlayer}.
     68  * It provides a wide range of buttons that serve the following functions: play/pause,
     69  * rewind/fast-forward, skip to next/previous, select subtitle track, enter/exit full screen mode,
     70  * adjust video quality, select audio track, mute/unmute, and adjust playback speed.
     71  *
     72  * <p>
     73  * <em> MediaControlView2 can be initialized in two different ways: </em>
     74  * 1) When initializing {@link VideoView2} a default MediaControlView2 is created.
     75  * 2) Initialize MediaControlView2 programmatically and add it to a {@link ViewGroup} instance.
     76  *
     77  * In the first option, VideoView2 automatically connects MediaControlView2 to MediaController,
     78  * which is necessary to communicate with MediaSession. In the second option, however, the
     79  * developer needs to manually retrieve a MediaController instance from MediaSession and set it to
     80  * MediaControlView2.
     81  *
     82  * <p>
     83  * There is no separate method that handles the show/hide behavior for MediaControlView2. Instead,
     84  * one can directly change the visibility of this view by calling {@link View#setVisibility(int)}.
     85  * The values supported are View.VISIBLE and View.GONE.
     86  *
     87  * <p>
     88  * In addition, the following customizations are supported:
     89  * 1) Set focus to the play/pause button by calling requestPlayButtonFocus().
     90  * 2) Set full screen mode
     91  *
     92  */
     93 @RequiresApi(21) // TODO correct minSdk API use incompatibilities and remove before release.
     94 public class MediaControlView2 extends BaseLayout {
     95     /**
     96      * @hide
     97      */
     98     @RestrictTo(LIBRARY_GROUP)
     99     @IntDef({
    100             BUTTON_PLAY_PAUSE,
    101             BUTTON_FFWD,
    102             BUTTON_REW,
    103             BUTTON_NEXT,
    104             BUTTON_PREV,
    105             BUTTON_SUBTITLE,
    106             BUTTON_FULL_SCREEN,
    107             BUTTON_OVERFLOW,
    108             BUTTON_MUTE,
    109             BUTTON_ASPECT_RATIO,
    110             BUTTON_SETTINGS
    111     })
    112     @Retention(RetentionPolicy.SOURCE)
    113     public @interface Button {}
    114 
    115     /**
    116      * MediaControlView2 button value for playing and pausing media.
    117      * @hide
    118      */
    119     @RestrictTo(LIBRARY_GROUP)
    120     public static final int BUTTON_PLAY_PAUSE = 1;
    121     /**
    122      * MediaControlView2 button value for jumping 30 seconds forward.
    123      * @hide
    124      */
    125     @RestrictTo(LIBRARY_GROUP)
    126     public static final int BUTTON_FFWD = 2;
    127     /**
    128      * MediaControlView2 button value for jumping 10 seconds backward.
    129      * @hide
    130      */
    131     @RestrictTo(LIBRARY_GROUP)
    132     public static final int BUTTON_REW = 3;
    133     /**
    134      * MediaControlView2 button value for jumping to next media.
    135      * @hide
    136      */
    137     @RestrictTo(LIBRARY_GROUP)
    138     public static final int BUTTON_NEXT = 4;
    139     /**
    140      * MediaControlView2 button value for jumping to previous media.
    141      * @hide
    142      */
    143     @RestrictTo(LIBRARY_GROUP)
    144     public static final int BUTTON_PREV = 5;
    145     /**
    146      * MediaControlView2 button value for showing/hiding subtitle track.
    147      * @hide
    148      */
    149     @RestrictTo(LIBRARY_GROUP)
    150     public static final int BUTTON_SUBTITLE = 6;
    151     /**
    152      * MediaControlView2 button value for toggling full screen.
    153      * @hide
    154      */
    155     @RestrictTo(LIBRARY_GROUP)
    156     public static final int BUTTON_FULL_SCREEN = 7;
    157     /**
    158      * MediaControlView2 button value for showing/hiding overflow buttons.
    159      * @hide
    160      */
    161     @RestrictTo(LIBRARY_GROUP)
    162     public static final int BUTTON_OVERFLOW = 8;
    163     /**
    164      * MediaControlView2 button value for muting audio.
    165      * @hide
    166      */
    167     @RestrictTo(LIBRARY_GROUP)
    168     public static final int BUTTON_MUTE = 9;
    169     /**
    170      * MediaControlView2 button value for adjusting aspect ratio of view.
    171      * @hide
    172      */
    173     @RestrictTo(LIBRARY_GROUP)
    174     public static final int BUTTON_ASPECT_RATIO = 10;
    175     /**
    176      * MediaControlView2 button value for showing/hiding settings page.
    177      * @hide
    178      */
    179     @RestrictTo(LIBRARY_GROUP)
    180     public static final int BUTTON_SETTINGS = 11;
    181 
    182     private static final String TAG = "MediaControlView2";
    183 
    184     static final String KEY_VIDEO_TRACK_COUNT = "VideoTrackCount";
    185     static final String KEY_AUDIO_TRACK_COUNT = "AudioTrackCount";
    186     static final String KEY_SUBTITLE_TRACK_COUNT = "SubtitleTrackCount";
    187     static final String KEY_PLAYBACK_SPEED = "PlaybackSpeed";
    188     static final String KEY_SELECTED_AUDIO_INDEX = "SelectedAudioIndex";
    189     static final String KEY_SELECTED_SUBTITLE_INDEX = "SelectedSubtitleIndex";
    190     static final String EVENT_UPDATE_TRACK_STATUS = "UpdateTrackStatus";
    191     static final String KEY_STATE_IS_ADVERTISEMENT = "MediaTypeAdvertisement";
    192     static final String EVENT_UPDATE_MEDIA_TYPE_STATUS = "UpdateMediaTypeStatus";
    193 
    194     // String for sending command to show subtitle to MediaSession.
    195     static final String COMMAND_SHOW_SUBTITLE = "showSubtitle";
    196     // String for sending command to hide subtitle to MediaSession.
    197     static final String COMMAND_HIDE_SUBTITLE = "hideSubtitle";
    198     // String for sending command to select audio track to MediaSession.
    199     static final String COMMAND_SELECT_AUDIO_TRACK = "SelectTrack";
    200     // String for sending command to set playback speed to MediaSession.
    201     static final String COMMAND_SET_PLAYBACK_SPEED = "SetPlaybackSpeed";
    202     // String for sending command to mute audio to MediaSession.
    203     static final String COMMAND_MUTE = "Mute";
    204     // String for sending command to unmute audio to MediaSession.
    205     static final String COMMAND_UNMUTE = "Unmute";
    206 
    207     private static final int SETTINGS_MODE_AUDIO_TRACK = 0;
    208     private static final int SETTINGS_MODE_PLAYBACK_SPEED = 1;
    209     private static final int SETTINGS_MODE_HELP = 2;
    210     private static final int SETTINGS_MODE_SUBTITLE_TRACK = 3;
    211     private static final int SETTINGS_MODE_VIDEO_QUALITY = 4;
    212     private static final int SETTINGS_MODE_MAIN = 5;
    213     private static final int PLAYBACK_SPEED_1x_INDEX = 3;
    214 
    215     private static final int MEDIA_TYPE_DEFAULT = 0;
    216     private static final int MEDIA_TYPE_MUSIC = 1;
    217     private static final int MEDIA_TYPE_ADVERTISEMENT = 2;
    218 
    219     private static final int SIZE_TYPE_EMBEDDED = 0;
    220     private static final int SIZE_TYPE_FULL = 1;
    221     private static final int SIZE_TYPE_MINIMAL = 2;
    222 
    223     private static final int MAX_PROGRESS = 1000;
    224     private static final int DEFAULT_PROGRESS_UPDATE_TIME_MS = 1000;
    225     private static final int REWIND_TIME_MS = 10000;
    226     private static final int FORWARD_TIME_MS = 30000;
    227     private static final int AD_SKIP_WAIT_TIME_MS = 5000;
    228     private static final int RESOURCE_NON_EXISTENT = -1;
    229     private static final String RESOURCE_EMPTY = "";
    230 
    231     private Resources mResources;
    232     private MediaControllerCompat mController;
    233     private MediaControllerCompat.TransportControls mControls;
    234     private PlaybackStateCompat mPlaybackState;
    235     private MediaMetadataCompat mMetadata;
    236     private OnFullScreenListener mOnFullScreenListener;
    237     private int mDuration;
    238     private int mPrevState;
    239     private int mPrevWidth;
    240     private int mPrevHeight;
    241     private int mOriginalLeftBarWidth;
    242     private int mVideoTrackCount;
    243     private int mAudioTrackCount;
    244     private int mSubtitleTrackCount;
    245     private int mSettingsMode;
    246     private int mSelectedSubtitleTrackIndex;
    247     private int mSelectedAudioTrackIndex;
    248     private int mSelectedVideoQualityIndex;
    249     private int mSelectedSpeedIndex;
    250     private int mEmbeddedSettingsItemWidth;
    251     private int mFullSettingsItemWidth;
    252     private int mSettingsItemHeight;
    253     private int mSettingsWindowMargin;
    254     private int mMediaType;
    255     private int mSizeType;
    256     private int mOrientation;
    257     private long mPlaybackActions;
    258     private boolean mDragging;
    259     private boolean mIsFullScreen;
    260     private boolean mOverflowExpanded;
    261     private boolean mIsStopped;
    262     private boolean mSubtitleIsEnabled;
    263     private boolean mSeekAvailable;
    264     private boolean mIsAdvertisement;
    265     private boolean mIsMute;
    266     private boolean mNeedUXUpdate;
    267 
    268     // Relating to Title Bar View
    269     private ViewGroup mRoot;
    270     private View mTitleBar;
    271     private TextView mTitleView;
    272     private View mAdExternalLink;
    273     private ImageButton mBackButton;
    274     private MediaRouteButton mRouteButton;
    275     private MediaRouteSelector mRouteSelector;
    276 
    277     // Relating to Center View
    278     private ViewGroup mCenterView;
    279     private View mTransportControls;
    280     private ImageButton mPlayPauseButton;
    281     private ImageButton mFfwdButton;
    282     private ImageButton mRewButton;
    283     private ImageButton mNextButton;
    284     private ImageButton mPrevButton;
    285 
    286     // Relating to Minimal Extra View
    287     private LinearLayout mMinimalExtraView;
    288 
    289     // Relating to Progress Bar View
    290     private ProgressBar mProgress;
    291     private View mProgressBuffer;
    292 
    293     // Relating to Bottom Bar View
    294     private ViewGroup mBottomBar;
    295 
    296     // Relating to Bottom Bar Left View
    297     private ViewGroup mBottomBarLeftView;
    298     private ViewGroup mTimeView;
    299     private TextView mEndTime;
    300     private TextView mCurrentTime;
    301     private TextView mAdSkipView;
    302     private StringBuilder mFormatBuilder;
    303     private Formatter mFormatter;
    304 
    305     // Relating to Bottom Bar Right View
    306     private ViewGroup mBottomBarRightView;
    307     private ViewGroup mBasicControls;
    308     private ViewGroup mExtraControls;
    309     private ViewGroup mCustomButtons;
    310     private ImageButton mSubtitleButton;
    311     private ImageButton mFullScreenButton;
    312     private ImageButton mOverflowButtonRight;
    313     private ImageButton mOverflowButtonLeft;
    314     private ImageButton mMuteButton;
    315     private ImageButton mVideoQualityButton;
    316     private ImageButton mSettingsButton;
    317     private TextView mAdRemainingView;
    318 
    319     // Relating to Settings List View
    320     private ListView mSettingsListView;
    321     private PopupWindow mSettingsWindow;
    322     private SettingsAdapter mSettingsAdapter;
    323     private SubSettingsAdapter mSubSettingsAdapter;
    324     private List<String> mSettingsMainTextsList;
    325     private List<String> mSettingsSubTextsList;
    326     private List<Integer> mSettingsIconIdsList;
    327     private List<String> mSubtitleDescriptionsList;
    328     private List<String> mAudioTrackList;
    329     private List<String> mVideoQualityList;
    330     private List<String> mPlaybackSpeedTextList;
    331     private List<Float> mPlaybackSpeedList;
    332 
    333     public MediaControlView2(@NonNull Context context) {
    334         this(context, null);
    335     }
    336 
    337     public MediaControlView2(@NonNull Context context, @Nullable AttributeSet attrs) {
    338         this(context, attrs, 0);
    339     }
    340 
    341     public MediaControlView2(@NonNull Context context, @Nullable AttributeSet attrs,
    342             int defStyleAttr) {
    343         super(context, attrs, defStyleAttr);
    344 
    345         mResources = context.getResources();
    346         // Inflate MediaControlView2 from XML
    347         mRoot = makeControllerView();
    348         addView(mRoot);
    349     }
    350 
    351     /**
    352      * Sets MediaSession2 token to control corresponding MediaSession2.
    353      * @hide
    354      */
    355     @RestrictTo(LIBRARY_GROUP)
    356     public void setMediaSessionToken(SessionToken2 token) {
    357     }
    358 
    359     /**
    360      * Registers a callback to be invoked when the fullscreen mode should be changed.
    361      * @param l The callback that will be run
    362      */
    363     public void setOnFullScreenListener(OnFullScreenListener l) {
    364         mOnFullScreenListener = l;
    365     }
    366 
    367     /**
    368      * Sets MediaController instance to MediaControlView2, which makes it possible to send and
    369      * receive data between MediaControlView2 and VideoView2. This method does not need to be called
    370      * when MediaControlView2 is initialized with VideoView2.
    371      * @hide TODO: remove once the implementation is revised
    372      */
    373     @RestrictTo(LIBRARY_GROUP)
    374     public void setController(MediaControllerCompat controller) {
    375         mController = controller;
    376         if (controller != null) {
    377             mControls = mController.getTransportControls();
    378             // Set mMetadata and mPlaybackState to existing MediaSession variables since they may
    379             // be called before the callback is called
    380             mPlaybackState = mController.getPlaybackState();
    381             mMetadata = mController.getMetadata();
    382             updateDuration();
    383             updateTitle();
    384 
    385             mController.registerCallback(new MediaControllerCallback());
    386         }
    387     }
    388 
    389     /**
    390      * Changes the visibility state of an individual button. Default value is View.Visible.
    391      *
    392      * @param button the {@code Button} assigned to individual buttons
    393      * <ul>
    394      * <li>{@link #BUTTON_PLAY_PAUSE}
    395      * <li>{@link #BUTTON_FFWD}
    396      * <li>{@link #BUTTON_REW}
    397      * <li>{@link #BUTTON_NEXT}
    398      * <li>{@link #BUTTON_PREV}
    399      * <li>{@link #BUTTON_SUBTITLE}
    400      * <li>{@link #BUTTON_FULL_SCREEN}
    401      * <li>{@link #BUTTON_MUTE}
    402      * <li>{@link #BUTTON_OVERFLOW}
    403      * <li>{@link #BUTTON_ASPECT_RATIO}
    404      * <li>{@link #BUTTON_SETTINGS}
    405      * </ul>
    406      * @param visibility One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.
    407      * @hide
    408      */
    409     @RestrictTo(LIBRARY_GROUP)
    410     public void setButtonVisibility(@Button int button, /*@Visibility*/ int visibility) {
    411         switch (button) {
    412             case MediaControlView2.BUTTON_PLAY_PAUSE:
    413                 if (mPlayPauseButton != null && canPause()) {
    414                     mPlayPauseButton.setVisibility(visibility);
    415                 }
    416                 break;
    417             case MediaControlView2.BUTTON_FFWD:
    418                 if (mFfwdButton != null && canSeekForward()) {
    419                     mFfwdButton.setVisibility(visibility);
    420                 }
    421                 break;
    422             case MediaControlView2.BUTTON_REW:
    423                 if (mRewButton != null && canSeekBackward()) {
    424                     mRewButton.setVisibility(visibility);
    425                 }
    426                 break;
    427             case MediaControlView2.BUTTON_NEXT:
    428                 if (mNextButton != null) {
    429                     mNextButton.setVisibility(visibility);
    430                 }
    431                 break;
    432             case MediaControlView2.BUTTON_PREV:
    433                 if (mPrevButton != null) {
    434                     mPrevButton.setVisibility(visibility);
    435                 }
    436                 break;
    437             case MediaControlView2.BUTTON_SUBTITLE:
    438                 if (mSubtitleButton != null && mSubtitleTrackCount > 0) {
    439                     mSubtitleButton.setVisibility(visibility);
    440                 }
    441                 break;
    442             case MediaControlView2.BUTTON_FULL_SCREEN:
    443                 if (mFullScreenButton != null) {
    444                     mFullScreenButton.setVisibility(visibility);
    445                 }
    446                 break;
    447             case MediaControlView2.BUTTON_OVERFLOW:
    448                 if (mOverflowButtonRight != null) {
    449                     mOverflowButtonRight.setVisibility(visibility);
    450                 }
    451                 break;
    452             case MediaControlView2.BUTTON_MUTE:
    453                 if (mMuteButton != null) {
    454                     mMuteButton.setVisibility(visibility);
    455                 }
    456                 break;
    457             case MediaControlView2.BUTTON_SETTINGS:
    458                 if (mSettingsButton != null) {
    459                     mSettingsButton.setVisibility(visibility);
    460                 }
    461                 break;
    462             default:
    463                 break;
    464         }
    465     }
    466 
    467     /**
    468      *  Requests focus for the play/pause button.
    469      */
    470     public void requestPlayButtonFocus() {
    471         if (mPlayPauseButton != null) {
    472             mPlayPauseButton.requestFocus();
    473         }
    474     }
    475 
    476     /**
    477      * Interface definition of a callback to be invoked to inform the fullscreen mode is changed.
    478      * Application should handle the fullscreen mode accordingly.
    479      */
    480     public interface OnFullScreenListener {
    481         /**
    482          * Called to indicate a fullscreen mode change.
    483          */
    484         void onFullScreen(View view, boolean fullScreen);
    485     }
    486 
    487     @Override
    488     public CharSequence getAccessibilityClassName() {
    489         return MediaControlView2.class.getName();
    490     }
    491 
    492     @Override
    493     public boolean onTouchEvent(MotionEvent ev) {
    494         return false;
    495     }
    496 
    497     @Override
    498     public boolean onTrackballEvent(MotionEvent ev) {
    499         return false;
    500     }
    501 
    502     @Override
    503     public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    504         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    505         // Update layout when this view's width changes in order to avoid any UI overlap between
    506         // transport controls.
    507         if (mPrevWidth != getMeasuredWidth()
    508                 || mPrevHeight != getMeasuredHeight() || mNeedUXUpdate) {
    509             // Dismiss SettingsWindow if it is showing.
    510             mSettingsWindow.dismiss();
    511 
    512             // These views may not have been initialized yet.
    513             if (mTransportControls.getWidth() == 0 || mTimeView.getWidth() == 0) {
    514                 return;
    515             }
    516 
    517             int currWidth = getMeasuredWidth();
    518             int currHeight = getMeasuredHeight();
    519             WindowManager manager = (WindowManager) getContext().getApplicationContext()
    520                     .getSystemService(Context.WINDOW_SERVICE);
    521             Point screenSize = new Point();
    522             manager.getDefaultDisplay().getSize(screenSize);
    523             int screenWidth = screenSize.x;
    524             int screenHeight = screenSize.y;
    525             int iconSize = mResources.getDimensionPixelSize(R.dimen.mcv2_icon_size);
    526 
    527             if (mMediaType == MEDIA_TYPE_DEFAULT) {
    528                 // Max number of icons inside BottomBarRightView for Music mode is 4.
    529                 int maxIconCount = 4;
    530                 updateLayout(maxIconCount, iconSize, currWidth, currHeight, screenWidth,
    531                         screenHeight);
    532 
    533             } else if (mMediaType == MEDIA_TYPE_MUSIC) {
    534                 if (mNeedUXUpdate) {
    535                     // One-time operation for Music media type
    536                     mBasicControls.removeView(mMuteButton);
    537                     mExtraControls.addView(mMuteButton, 0);
    538                     mVideoQualityButton.setVisibility(View.GONE);
    539                     if (mFfwdButton != null) {
    540                         mFfwdButton.setVisibility(View.GONE);
    541                     }
    542                     if (mRewButton != null) {
    543                         mRewButton.setVisibility(View.GONE);
    544                     }
    545                 }
    546                 mNeedUXUpdate = false;
    547 
    548                 // Max number of icons inside BottomBarRightView for Music mode is 3.
    549                 int maxIconCount = 3;
    550                 updateLayout(maxIconCount, iconSize, currWidth, currHeight, screenWidth,
    551                         screenHeight);
    552             }
    553             mPrevWidth = currWidth;
    554             mPrevHeight = currHeight;
    555         }
    556         // Update title bar parameters in order to avoid overlap between title view and the right
    557         // side of the title bar.
    558         updateTitleBarLayout();
    559     }
    560 
    561     @Override
    562     public void setEnabled(boolean enabled) {
    563         super.setEnabled(enabled);
    564 
    565         if (mPlayPauseButton != null) {
    566             mPlayPauseButton.setEnabled(enabled);
    567         }
    568         if (mFfwdButton != null) {
    569             mFfwdButton.setEnabled(enabled);
    570         }
    571         if (mRewButton != null) {
    572             mRewButton.setEnabled(enabled);
    573         }
    574         if (mNextButton != null) {
    575             mNextButton.setEnabled(enabled);
    576         }
    577         if (mPrevButton != null) {
    578             mPrevButton.setEnabled(enabled);
    579         }
    580         if (mProgress != null) {
    581             mProgress.setEnabled(enabled);
    582         }
    583         disableUnsupportedButtons();
    584     }
    585 
    586     @Override
    587     public void onVisibilityAggregated(boolean isVisible) {
    588         super.onVisibilityAggregated(isVisible);
    589 
    590         if (isVisible) {
    591             disableUnsupportedButtons();
    592             removeCallbacks(mUpdateProgress);
    593             post(mUpdateProgress);
    594         } else {
    595             removeCallbacks(mUpdateProgress);
    596         }
    597     }
    598 
    599     void setRouteSelector(MediaRouteSelector selector) {
    600         mRouteSelector = selector;
    601         if (mRouteSelector != null && !mRouteSelector.isEmpty()) {
    602             mRouteButton.setRouteSelector(selector);
    603             mRouteButton.setVisibility(View.VISIBLE);
    604         } else {
    605             mRouteButton.setRouteSelector(MediaRouteSelector.EMPTY);
    606             mRouteButton.setVisibility(View.GONE);
    607         }
    608     }
    609 
    610     ///////////////////////////////////////////////////
    611     // Protected or private methods
    612     ///////////////////////////////////////////////////
    613 
    614     private boolean isPlaying() {
    615         if (mPlaybackState != null) {
    616             return mPlaybackState.getState() == PlaybackStateCompat.STATE_PLAYING;
    617         }
    618         return false;
    619     }
    620 
    621     private int getCurrentPosition() {
    622         mPlaybackState = mController.getPlaybackState();
    623         if (mPlaybackState != null) {
    624             return (int) mPlaybackState.getPosition();
    625         }
    626         return 0;
    627     }
    628 
    629     private int getBufferPercentage() {
    630         if (mDuration == 0) {
    631             return 0;
    632         }
    633         mPlaybackState = mController.getPlaybackState();
    634         if (mPlaybackState != null) {
    635             long bufferedPos = mPlaybackState.getBufferedPosition();
    636             return (bufferedPos == -1) ? -1 : (int) (bufferedPos * 100 / mDuration);
    637         }
    638         return 0;
    639     }
    640 
    641     private boolean canPause() {
    642         if (mPlaybackState != null) {
    643             return (mPlaybackState.getActions() & PlaybackStateCompat.ACTION_PAUSE) != 0;
    644         }
    645         return true;
    646     }
    647 
    648     private boolean canSeekBackward() {
    649         if (mPlaybackState != null) {
    650             return (mPlaybackState.getActions() & PlaybackStateCompat.ACTION_REWIND) != 0;
    651         }
    652         return true;
    653     }
    654 
    655     private boolean canSeekForward() {
    656         if (mPlaybackState != null) {
    657             return (mPlaybackState.getActions() & PlaybackStateCompat.ACTION_FAST_FORWARD) != 0;
    658         }
    659         return true;
    660     }
    661 
    662     /**
    663      * Create the view that holds the widgets that control playback.
    664      * Derived classes can override this to create their own.
    665      *
    666      * @return The controller view.
    667      */
    668     private ViewGroup makeControllerView() {
    669         ViewGroup root = (ViewGroup) inflateLayout(getContext(), R.layout.media_controller);
    670         initControllerView(root);
    671         return root;
    672     }
    673 
    674     private View inflateLayout(Context context, int resId) {
    675         LayoutInflater inflater = (LayoutInflater) context
    676                 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    677         return inflater.inflate(resId, null);
    678     }
    679 
    680     @SuppressWarnings("deprecation")
    681     private void initControllerView(ViewGroup v) {
    682         // Relating to Title Bar View
    683         mTitleBar = v.findViewById(R.id.title_bar);
    684         mTitleView = v.findViewById(R.id.title_text);
    685         mAdExternalLink = v.findViewById(R.id.ad_external_link);
    686         mBackButton = v.findViewById(R.id.back);
    687         if (mBackButton != null) {
    688             mBackButton.setOnClickListener(mBackListener);
    689             mBackButton.setVisibility(View.GONE);
    690         }
    691         mRouteButton = v.findViewById(R.id.cast);
    692 
    693         // Relating to Center View
    694         mCenterView = v.findViewById(R.id.center_view);
    695         mTransportControls = inflateTransportControls(R.layout.embedded_transport_controls);
    696         mCenterView.addView(mTransportControls);
    697 
    698         // Relating to Minimal Extra View
    699         mMinimalExtraView = (LinearLayout) v.findViewById(R.id.minimal_extra_view);
    700         LinearLayout.LayoutParams params =
    701                 (LinearLayout.LayoutParams) mMinimalExtraView.getLayoutParams();
    702         int iconSize = mResources.getDimensionPixelSize(R.dimen.mcv2_icon_size);
    703         int marginSize = mResources.getDimensionPixelSize(R.dimen.mcv2_icon_margin);
    704         params.setMargins(0, (iconSize + marginSize * 2) * (-1), 0, 0);
    705         mMinimalExtraView.setLayoutParams(params);
    706         mMinimalExtraView.setVisibility(View.GONE);
    707 
    708         // Relating to Progress Bar View
    709         mProgress = v.findViewById(R.id.progress);
    710         if (mProgress != null) {
    711             if (mProgress instanceof SeekBar) {
    712                 SeekBar seeker = (SeekBar) mProgress;
    713                 seeker.setOnSeekBarChangeListener(mSeekListener);
    714                 seeker.setProgressDrawable(mResources.getDrawable(R.drawable.custom_progress));
    715                 seeker.setThumb(mResources.getDrawable(R.drawable.custom_progress_thumb));
    716             }
    717             mProgress.setMax(MAX_PROGRESS);
    718         }
    719         mProgressBuffer = v.findViewById(R.id.progress_buffer);
    720 
    721         // Relating to Bottom Bar View
    722         mBottomBar = v.findViewById(R.id.bottom_bar);
    723 
    724         // Relating to Bottom Bar Left View
    725         mBottomBarLeftView = v.findViewById(R.id.bottom_bar_left);
    726         mTimeView = v.findViewById(R.id.time);
    727         mEndTime = v.findViewById(R.id.time_end);
    728         mCurrentTime = v.findViewById(R.id.time_current);
    729         mAdSkipView = v.findViewById(R.id.ad_skip_time);
    730         mFormatBuilder = new StringBuilder();
    731         mFormatter = new Formatter(mFormatBuilder, Locale.getDefault());
    732 
    733         // Relating to Bottom Bar Right View
    734         mBottomBarRightView = v.findViewById(R.id.bottom_bar_right);
    735         mBasicControls = v.findViewById(R.id.basic_controls);
    736         mExtraControls = v.findViewById(R.id.extra_controls);
    737         mCustomButtons = v.findViewById(R.id.custom_buttons);
    738         mSubtitleButton = v.findViewById(R.id.subtitle);
    739         if (mSubtitleButton != null) {
    740             mSubtitleButton.setOnClickListener(mSubtitleListener);
    741         }
    742         mFullScreenButton = v.findViewById(R.id.fullscreen);
    743         if (mFullScreenButton != null) {
    744             mFullScreenButton.setOnClickListener(mFullScreenListener);
    745         }
    746         mOverflowButtonRight = v.findViewById(R.id.overflow_right);
    747         if (mOverflowButtonRight != null) {
    748             mOverflowButtonRight.setOnClickListener(mOverflowRightListener);
    749         }
    750         mOverflowButtonLeft = v.findViewById(R.id.overflow_left);
    751         if (mOverflowButtonLeft != null) {
    752             mOverflowButtonLeft.setOnClickListener(mOverflowLeftListener);
    753         }
    754         mMuteButton = v.findViewById(R.id.mute);
    755         if (mMuteButton != null) {
    756             mMuteButton.setOnClickListener(mMuteButtonListener);
    757         }
    758         mSettingsButton = v.findViewById(R.id.settings);
    759         if (mSettingsButton != null) {
    760             mSettingsButton.setOnClickListener(mSettingsButtonListener);
    761         }
    762         mVideoQualityButton = v.findViewById(R.id.video_quality);
    763         if (mVideoQualityButton != null) {
    764             mVideoQualityButton.setOnClickListener(mVideoQualityListener);
    765         }
    766         mAdRemainingView = v.findViewById(R.id.ad_remaining);
    767 
    768         // Relating to Settings List View
    769         initializeSettingsLists();
    770         mSettingsListView = (ListView) inflateLayout(getContext(), R.layout.settings_list);
    771         mSettingsAdapter = new SettingsAdapter(mSettingsMainTextsList, mSettingsSubTextsList,
    772                 mSettingsIconIdsList);
    773         mSubSettingsAdapter = new SubSettingsAdapter(null, 0);
    774         mSettingsListView.setAdapter(mSettingsAdapter);
    775         mSettingsListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
    776         mSettingsListView.setOnItemClickListener(mSettingsItemClickListener);
    777 
    778         mEmbeddedSettingsItemWidth = mResources.getDimensionPixelSize(
    779                 R.dimen.mcv2_embedded_settings_width);
    780         mFullSettingsItemWidth = mResources.getDimensionPixelSize(R.dimen.mcv2_full_settings_width);
    781         mSettingsItemHeight = mResources.getDimensionPixelSize(
    782                 R.dimen.mcv2_settings_height);
    783         mSettingsWindowMargin = (-1) * mResources.getDimensionPixelSize(
    784                 R.dimen.mcv2_settings_offset);
    785         mSettingsWindow = new PopupWindow(mSettingsListView, mEmbeddedSettingsItemWidth,
    786                 LayoutParams.WRAP_CONTENT, true);
    787     }
    788 
    789     /**
    790      * Disable pause or seek buttons if the stream cannot be paused or seeked.
    791      * This requires the control interface to be a MediaPlayerControlExt
    792      */
    793     private void disableUnsupportedButtons() {
    794         try {
    795             if (mPlayPauseButton != null && !canPause()) {
    796                 mPlayPauseButton.setEnabled(false);
    797             }
    798             if (mRewButton != null && !canSeekBackward()) {
    799                 mRewButton.setEnabled(false);
    800             }
    801             if (mFfwdButton != null && !canSeekForward()) {
    802                 mFfwdButton.setEnabled(false);
    803             }
    804             if (mProgress != null && !canSeekBackward() && !canSeekForward()) {
    805                 mProgress.setEnabled(false);
    806             }
    807         } catch (IncompatibleClassChangeError ex) {
    808             // We were given an old version of the interface, that doesn't have
    809             // the canPause/canSeekXYZ methods. This is OK, it just means we
    810             // assume the media can be paused and seeked, and so we don't disable
    811             // the buttons.
    812         }
    813     }
    814 
    815     private final Runnable mUpdateProgress = new Runnable() {
    816         @Override
    817         public void run() {
    818             int pos = setProgress();
    819             boolean isShowing = getVisibility() == View.VISIBLE;
    820             if (!mDragging && isShowing && isPlaying()) {
    821                 postDelayed(mUpdateProgress,
    822                         DEFAULT_PROGRESS_UPDATE_TIME_MS - (pos % DEFAULT_PROGRESS_UPDATE_TIME_MS));
    823             }
    824         }
    825     };
    826 
    827     private String stringForTime(int timeMs) {
    828         int totalSeconds = timeMs / 1000;
    829 
    830         int seconds = totalSeconds % 60;
    831         int minutes = (totalSeconds / 60) % 60;
    832         int hours = totalSeconds / 3600;
    833 
    834         mFormatBuilder.setLength(0);
    835         if (hours > 0) {
    836             return mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).toString();
    837         } else {
    838             return mFormatter.format("%02d:%02d", minutes, seconds).toString();
    839         }
    840     }
    841 
    842     private int setProgress() {
    843         if (mController == null || mDragging) {
    844             return 0;
    845         }
    846         int positionOnProgressBar = 0;
    847         int currentPosition = getCurrentPosition();
    848         if (mDuration > 0) {
    849             positionOnProgressBar = (int) (MAX_PROGRESS * (long) currentPosition / mDuration);
    850         }
    851         if (mProgress != null && currentPosition != mDuration) {
    852             mProgress.setProgress(positionOnProgressBar);
    853             // If the media is a local file, there is no need to set a buffer, so set secondary
    854             // progress to maximum.
    855             if (getBufferPercentage() < 0) {
    856                 mProgress.setSecondaryProgress(MAX_PROGRESS);
    857             } else {
    858                 mProgress.setSecondaryProgress(getBufferPercentage() * 10);
    859             }
    860         }
    861 
    862         if (mEndTime != null) {
    863             mEndTime.setText(stringForTime(mDuration));
    864 
    865         }
    866         if (mCurrentTime != null) {
    867             mCurrentTime.setText(stringForTime(currentPosition));
    868         }
    869 
    870         if (mIsAdvertisement) {
    871             // Update the remaining number of seconds until the first 5 seconds of the
    872             // advertisement.
    873             if (mAdSkipView != null) {
    874                 if (currentPosition <= AD_SKIP_WAIT_TIME_MS) {
    875                     if (mAdSkipView.getVisibility() == View.GONE) {
    876                         mAdSkipView.setVisibility(View.VISIBLE);
    877                     }
    878                     String skipTimeText = mResources.getString(
    879                             R.string.MediaControlView2_ad_skip_wait_time,
    880                             ((AD_SKIP_WAIT_TIME_MS - currentPosition) / 1000 + 1));
    881                     mAdSkipView.setText(skipTimeText);
    882                 } else {
    883                     if (mAdSkipView.getVisibility() == View.VISIBLE) {
    884                         mAdSkipView.setVisibility(View.GONE);
    885                         mNextButton.setEnabled(true);
    886                         mNextButton.clearColorFilter();
    887                     }
    888                 }
    889             }
    890             // Update the remaining number of seconds of the advertisement.
    891             if (mAdRemainingView != null) {
    892                 int remainingTime =
    893                         (mDuration - currentPosition < 0) ? 0 : (mDuration - currentPosition);
    894                 String remainingTimeText = mResources.getString(
    895                         R.string.MediaControlView2_ad_remaining_time,
    896                         stringForTime(remainingTime));
    897                 mAdRemainingView.setText(remainingTimeText);
    898             }
    899         }
    900         return currentPosition;
    901     }
    902 
    903     private void togglePausePlayState() {
    904         if (isPlaying()) {
    905             mControls.pause();
    906             mPlayPauseButton.setImageDrawable(
    907                     mResources.getDrawable(R.drawable.ic_play_circle_filled, null));
    908             mPlayPauseButton.setContentDescription(
    909                     mResources.getString(R.string.mcv2_play_button_desc));
    910         } else {
    911             mControls.play();
    912             mPlayPauseButton.setImageDrawable(
    913                     mResources.getDrawable(R.drawable.ic_pause_circle_filled, null));
    914             mPlayPauseButton.setContentDescription(
    915                     mResources.getString(R.string.mcv2_pause_button_desc));
    916         }
    917     }
    918 
    919     // There are two scenarios that can trigger the seekbar listener to trigger:
    920     //
    921     // The first is the user using the touchpad to adjust the posititon of the
    922     // seekbar's thumb. In this case onStartTrackingTouch is called followed by
    923     // a number of onProgressChanged notifications, concluded by onStopTrackingTouch.
    924     // We're setting the field "mDragging" to true for the duration of the dragging
    925     // session to avoid jumps in the position in case of ongoing playback.
    926     //
    927     // The second scenario involves the user operating the scroll ball, in this
    928     // case there WON'T BE onStartTrackingTouch/onStopTrackingTouch notifications,
    929     // we will simply apply the updated position without suspending regular updates.
    930     private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
    931         @Override
    932         public void onStartTrackingTouch(SeekBar bar) {
    933             if (!mSeekAvailable) {
    934                 return;
    935             }
    936 
    937             mDragging = true;
    938 
    939             // By removing these pending progress messages we make sure
    940             // that a) we won't update the progress while the user adjusts
    941             // the seekbar and b) once the user is done dragging the thumb
    942             // we will post one of these messages to the queue again and
    943             // this ensures that there will be exactly one message queued up.
    944             removeCallbacks(mUpdateProgress);
    945 
    946             // Check if playback is currently stopped. In this case, update the pause button to
    947             // show the play image instead of the replay image.
    948             if (mIsStopped) {
    949                 mPlayPauseButton.setImageDrawable(
    950                         mResources.getDrawable(R.drawable.ic_play_circle_filled, null));
    951                 mPlayPauseButton.setContentDescription(
    952                         mResources.getString(R.string.mcv2_play_button_desc));
    953                 mIsStopped = false;
    954             }
    955         }
    956 
    957         @Override
    958         public void onProgressChanged(SeekBar bar, int progress, boolean fromUser) {
    959             if (!mSeekAvailable) {
    960                 return;
    961             }
    962             if (!fromUser) {
    963                 // We're not interested in programmatically generated changes to
    964                 // the progress bar's position.
    965                 return;
    966             }
    967             if (mDuration > 0) {
    968                 int position = (int) (((long) mDuration * progress) / MAX_PROGRESS);
    969                 mControls.seekTo(position);
    970 
    971                 if (mCurrentTime != null) {
    972                     mCurrentTime.setText(stringForTime(position));
    973                 }
    974             }
    975         }
    976 
    977         @Override
    978         public void onStopTrackingTouch(SeekBar bar) {
    979             if (!mSeekAvailable) {
    980                 return;
    981             }
    982             mDragging = false;
    983 
    984             setProgress();
    985 
    986             // Ensure that progress is properly updated in the future,
    987             // the call to show() does not guarantee this because it is a
    988             // no-op if we are already showing.
    989             post(mUpdateProgress);
    990         }
    991     };
    992 
    993     private final OnClickListener mPlayPauseListener = new OnClickListener() {
    994         @Override
    995         public void onClick(View v) {
    996             togglePausePlayState();
    997         }
    998     };
    999 
   1000     private final OnClickListener mRewListener = new OnClickListener() {
   1001         @Override
   1002         public void onClick(View v) {
   1003             int pos = getCurrentPosition() - REWIND_TIME_MS;
   1004             mControls.seekTo(pos);
   1005             setProgress();
   1006         }
   1007     };
   1008 
   1009     private final OnClickListener mFfwdListener = new OnClickListener() {
   1010         @Override
   1011         public void onClick(View v) {
   1012             int pos = getCurrentPosition() + FORWARD_TIME_MS;
   1013             mControls.seekTo(pos);
   1014             setProgress();
   1015         }
   1016     };
   1017 
   1018     private final OnClickListener mNextListener = new OnClickListener() {
   1019         @Override
   1020         public void onClick(View v) {
   1021             mControls.skipToNext();
   1022         }
   1023     };
   1024 
   1025     private final OnClickListener mPrevListener = new OnClickListener() {
   1026         @Override
   1027         public void onClick(View v) {
   1028             mControls.skipToPrevious();
   1029         }
   1030     };
   1031 
   1032     private final OnClickListener mBackListener = new OnClickListener() {
   1033         @Override
   1034         public void onClick(View v) {
   1035             View parent = (View) getParent();
   1036             if (parent != null) {
   1037                 parent.onKeyDown(KeyEvent.KEYCODE_BACK,
   1038                         new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK));
   1039             }
   1040         }
   1041     };
   1042 
   1043     private final OnClickListener mSubtitleListener = new OnClickListener() {
   1044         @Override
   1045         public void onClick(View v) {
   1046             mSettingsMode = SETTINGS_MODE_SUBTITLE_TRACK;
   1047             mSubSettingsAdapter.setTexts(mSubtitleDescriptionsList);
   1048             mSubSettingsAdapter.setCheckPosition(mSelectedSubtitleTrackIndex);
   1049             displaySettingsWindow(mSubSettingsAdapter);
   1050         }
   1051     };
   1052 
   1053     private final OnClickListener mVideoQualityListener = new OnClickListener() {
   1054         @Override
   1055         public void onClick(View v) {
   1056             mSettingsMode = SETTINGS_MODE_VIDEO_QUALITY;
   1057             mSubSettingsAdapter.setTexts(mVideoQualityList);
   1058             mSubSettingsAdapter.setCheckPosition(mSelectedVideoQualityIndex);
   1059             displaySettingsWindow(mSubSettingsAdapter);
   1060         }
   1061     };
   1062 
   1063     private final OnClickListener mFullScreenListener = new OnClickListener() {
   1064         @Override
   1065         public void onClick(View v) {
   1066             if (mOnFullScreenListener == null) {
   1067                 return;
   1068             }
   1069 
   1070             final boolean isEnteringFullScreen = !mIsFullScreen;
   1071             if (isEnteringFullScreen) {
   1072                 mFullScreenButton.setImageDrawable(
   1073                         mResources.getDrawable(R.drawable.ic_fullscreen_exit, null));
   1074             } else {
   1075                 mFullScreenButton.setImageDrawable(
   1076                         mResources.getDrawable(R.drawable.ic_fullscreen, null));
   1077             }
   1078             mIsFullScreen = isEnteringFullScreen;
   1079             mOnFullScreenListener.onFullScreen(MediaControlView2.this,
   1080                     mIsFullScreen);
   1081         }
   1082     };
   1083 
   1084     private final OnClickListener mOverflowRightListener = new OnClickListener() {
   1085         @Override
   1086         public void onClick(View v) {
   1087             mBasicControls.setVisibility(View.GONE);
   1088             mExtraControls.setVisibility(View.VISIBLE);
   1089         }
   1090     };
   1091 
   1092     private final OnClickListener mOverflowLeftListener = new OnClickListener() {
   1093         @Override
   1094         public void onClick(View v) {
   1095             mBasicControls.setVisibility(View.VISIBLE);
   1096             mExtraControls.setVisibility(View.GONE);
   1097         }
   1098     };
   1099 
   1100     private final OnClickListener mMuteButtonListener = new OnClickListener() {
   1101         @Override
   1102         public void onClick(View v) {
   1103             if (!mIsMute) {
   1104                 mMuteButton.setImageDrawable(
   1105                         mResources.getDrawable(R.drawable.ic_mute, null));
   1106                 mMuteButton.setContentDescription(
   1107                         mResources.getString(R.string.mcv2_muted_button_desc));
   1108                 mIsMute = true;
   1109                 mController.sendCommand(COMMAND_MUTE, null, null);
   1110             } else {
   1111                 mMuteButton.setImageDrawable(
   1112                         mResources.getDrawable(R.drawable.ic_unmute, null));
   1113                 mMuteButton.setContentDescription(
   1114                         mResources.getString(R.string.mcv2_unmuted_button_desc));
   1115                 mIsMute = false;
   1116                 mController.sendCommand(COMMAND_UNMUTE, null, null);
   1117             }
   1118         }
   1119     };
   1120 
   1121     private final OnClickListener mSettingsButtonListener = new OnClickListener() {
   1122         @Override
   1123         public void onClick(View v) {
   1124             mSettingsMode = SETTINGS_MODE_MAIN;
   1125             mSettingsAdapter.setSubTexts(mSettingsSubTextsList);
   1126             displaySettingsWindow(mSettingsAdapter);
   1127         }
   1128     };
   1129 
   1130     private final AdapterView.OnItemClickListener mSettingsItemClickListener =
   1131             new AdapterView.OnItemClickListener() {
   1132         @Override
   1133         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
   1134             switch (mSettingsMode) {
   1135                 case SETTINGS_MODE_MAIN:
   1136                     if (position == SETTINGS_MODE_AUDIO_TRACK) {
   1137                         mSubSettingsAdapter.setTexts(mAudioTrackList);
   1138                         mSubSettingsAdapter.setCheckPosition(mSelectedAudioTrackIndex);
   1139                         mSettingsMode = SETTINGS_MODE_AUDIO_TRACK;
   1140                     } else if (position == SETTINGS_MODE_PLAYBACK_SPEED) {
   1141                         mSubSettingsAdapter.setTexts(mPlaybackSpeedTextList);
   1142                         mSubSettingsAdapter.setCheckPosition(mSelectedSpeedIndex);
   1143                         mSettingsMode = SETTINGS_MODE_PLAYBACK_SPEED;
   1144                     } else if (position == SETTINGS_MODE_HELP) {
   1145                         mSettingsWindow.dismiss();
   1146                         return;
   1147                     }
   1148                     displaySettingsWindow(mSubSettingsAdapter);
   1149                     break;
   1150                 case SETTINGS_MODE_AUDIO_TRACK:
   1151                     if (position != mSelectedAudioTrackIndex) {
   1152                         mSelectedAudioTrackIndex = position;
   1153                         if (mAudioTrackCount > 0) {
   1154                             Bundle extra = new Bundle();
   1155                             extra.putInt(KEY_SELECTED_AUDIO_INDEX, position);
   1156                             mController.sendCommand(COMMAND_SELECT_AUDIO_TRACK, extra, null);
   1157                         }
   1158                         mSettingsSubTextsList.set(SETTINGS_MODE_AUDIO_TRACK,
   1159                                 mSubSettingsAdapter.getMainText(position));
   1160                     }
   1161                     mSettingsWindow.dismiss();
   1162                     break;
   1163                 case SETTINGS_MODE_PLAYBACK_SPEED:
   1164                     if (position != mSelectedSpeedIndex) {
   1165                         mSelectedSpeedIndex = position;
   1166                         Bundle extra = new Bundle();
   1167                         extra.putFloat(KEY_PLAYBACK_SPEED, mPlaybackSpeedList.get(position));
   1168                         mController.sendCommand(COMMAND_SET_PLAYBACK_SPEED, extra, null);
   1169                         mSettingsSubTextsList.set(SETTINGS_MODE_PLAYBACK_SPEED,
   1170                                 mSubSettingsAdapter.getMainText(position));
   1171                     }
   1172                     mSettingsWindow.dismiss();
   1173                     break;
   1174                 case SETTINGS_MODE_HELP:
   1175                     break;
   1176                 case SETTINGS_MODE_SUBTITLE_TRACK:
   1177                     if (position != mSelectedSubtitleTrackIndex) {
   1178                         mSelectedSubtitleTrackIndex = position;
   1179                         if (position > 0) {
   1180                             Bundle extra = new Bundle();
   1181                             extra.putInt(KEY_SELECTED_SUBTITLE_INDEX, position - 1);
   1182                             mController.sendCommand(COMMAND_SHOW_SUBTITLE, extra, null);
   1183                             mSubtitleButton.setImageDrawable(
   1184                                     mResources.getDrawable(R.drawable.ic_subtitle_on, null));
   1185                             mSubtitleButton.setContentDescription(
   1186                                     mResources.getString(R.string.mcv2_cc_is_on));
   1187                             mSubtitleIsEnabled = true;
   1188                         } else {
   1189                             mController.sendCommand(COMMAND_HIDE_SUBTITLE, null, null);
   1190                             mSubtitleButton.setImageDrawable(
   1191                                     mResources.getDrawable(R.drawable.ic_subtitle_off, null));
   1192                             mSubtitleButton.setContentDescription(
   1193                                     mResources.getString(R.string.mcv2_cc_is_off));
   1194                             mSubtitleIsEnabled = false;
   1195                         }
   1196                     }
   1197                     mSettingsWindow.dismiss();
   1198                     break;
   1199                 case SETTINGS_MODE_VIDEO_QUALITY:
   1200                     mSelectedVideoQualityIndex = position;
   1201                     mSettingsWindow.dismiss();
   1202                     break;
   1203             }
   1204         }
   1205     };
   1206 
   1207     private void updateDuration() {
   1208         if (mMetadata != null) {
   1209             if (mMetadata.containsKey(MediaMetadataCompat.METADATA_KEY_DURATION)) {
   1210                 mDuration = (int) mMetadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION);
   1211                 // update progress bar
   1212                 setProgress();
   1213             }
   1214         }
   1215     }
   1216 
   1217     private void updateTitle() {
   1218         if (mMetadata != null) {
   1219             if (mMetadata.containsKey(MediaMetadataCompat.METADATA_KEY_TITLE)) {
   1220                 mTitleView.setText(mMetadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE));
   1221             }
   1222         }
   1223     }
   1224 
   1225     // The title bar is made up of two separate LinearLayouts. If the sum of the two bars are
   1226     // greater than the length of the title bar, reduce the size of the left bar (which makes the
   1227     // TextView that contains the title of the media file shrink).
   1228     private void updateTitleBarLayout() {
   1229         if (mTitleBar != null) {
   1230             int titleBarWidth = mTitleBar.getWidth();
   1231 
   1232             View leftBar = mTitleBar.findViewById(R.id.title_bar_left);
   1233             View rightBar = mTitleBar.findViewById(R.id.title_bar_right);
   1234             int leftBarWidth = leftBar.getWidth();
   1235             int rightBarWidth = rightBar.getWidth();
   1236 
   1237             RelativeLayout.LayoutParams params =
   1238                     (RelativeLayout.LayoutParams) leftBar.getLayoutParams();
   1239             if (leftBarWidth + rightBarWidth > titleBarWidth) {
   1240                 params.width = titleBarWidth - rightBarWidth;
   1241                 mOriginalLeftBarWidth = leftBarWidth;
   1242             } else if (leftBarWidth + rightBarWidth < titleBarWidth && mOriginalLeftBarWidth != 0) {
   1243                 params.width = mOriginalLeftBarWidth;
   1244                 mOriginalLeftBarWidth = 0;
   1245             }
   1246             leftBar.setLayoutParams(params);
   1247         }
   1248     }
   1249 
   1250     private void updateAudioMetadata() {
   1251         if (mMediaType != MEDIA_TYPE_MUSIC) {
   1252             return;
   1253         }
   1254 
   1255         if (mMetadata != null) {
   1256             String titleText = "";
   1257             String artistText = "";
   1258             if (mMetadata.containsKey(MediaMetadataCompat.METADATA_KEY_TITLE)) {
   1259                 titleText = mMetadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE);
   1260             } else {
   1261                 titleText = mResources.getString(R.string.mcv2_music_title_unknown_text);
   1262             }
   1263 
   1264             if (mMetadata.containsKey(MediaMetadataCompat.METADATA_KEY_ARTIST)) {
   1265                 artistText = mMetadata.getString(MediaMetadataCompat.METADATA_KEY_ARTIST);
   1266             } else {
   1267                 artistText = mResources.getString(R.string.mcv2_music_artist_unknown_text);
   1268             }
   1269 
   1270             // Update title for Embedded size type
   1271             mTitleView.setText(titleText + " - " + artistText);
   1272 
   1273             // Set to true to update layout inside onMeasure()
   1274             mNeedUXUpdate = true;
   1275         }
   1276     }
   1277 
   1278     private void updateLayout() {
   1279         if (mIsAdvertisement) {
   1280             mRewButton.setVisibility(View.GONE);
   1281             mFfwdButton.setVisibility(View.GONE);
   1282             mPrevButton.setVisibility(View.GONE);
   1283             mTimeView.setVisibility(View.GONE);
   1284 
   1285             mAdSkipView.setVisibility(View.VISIBLE);
   1286             mAdRemainingView.setVisibility(View.VISIBLE);
   1287             mAdExternalLink.setVisibility(View.VISIBLE);
   1288 
   1289             mProgress.setEnabled(false);
   1290             mNextButton.setEnabled(false);
   1291             mNextButton.setColorFilter(R.color.gray);
   1292         } else {
   1293             mRewButton.setVisibility(View.VISIBLE);
   1294             mFfwdButton.setVisibility(View.VISIBLE);
   1295             mPrevButton.setVisibility(View.VISIBLE);
   1296             mTimeView.setVisibility(View.VISIBLE);
   1297 
   1298             mAdSkipView.setVisibility(View.GONE);
   1299             mAdRemainingView.setVisibility(View.GONE);
   1300             mAdExternalLink.setVisibility(View.GONE);
   1301 
   1302             mProgress.setEnabled(true);
   1303             mNextButton.setEnabled(true);
   1304             mNextButton.clearColorFilter();
   1305             disableUnsupportedButtons();
   1306         }
   1307     }
   1308 
   1309     private void updateLayout(int maxIconCount, int iconSize, int currWidth,
   1310              int currHeight, int screenWidth, int screenHeight) {
   1311         int bottomBarRightWidthMax = iconSize * maxIconCount;
   1312         int fullWidth = mTransportControls.getWidth() + mTimeView.getWidth()
   1313                 + bottomBarRightWidthMax;
   1314         int embeddedWidth = mTimeView.getWidth() + bottomBarRightWidthMax;
   1315         int screenMaxLength = Math.max(screenWidth, screenHeight);
   1316 
   1317         boolean isFullSize = (mMediaType == MEDIA_TYPE_DEFAULT) ? (currWidth == screenMaxLength) :
   1318                 (currWidth == screenWidth && currHeight == screenHeight);
   1319 
   1320         if (isFullSize) {
   1321             if (mSizeType != SIZE_TYPE_FULL) {
   1322                 updateLayoutForSizeChange(SIZE_TYPE_FULL);
   1323                 if (mMediaType == MEDIA_TYPE_MUSIC) {
   1324                     mTitleView.setVisibility(View.GONE);
   1325                 }
   1326             }
   1327         } else if (embeddedWidth <= currWidth) {
   1328             if (mSizeType != SIZE_TYPE_EMBEDDED) {
   1329                 updateLayoutForSizeChange(SIZE_TYPE_EMBEDDED);
   1330                 if (mMediaType == MEDIA_TYPE_MUSIC) {
   1331                     mTitleView.setVisibility(View.VISIBLE);
   1332                 }
   1333             }
   1334         } else {
   1335             if (mSizeType != SIZE_TYPE_MINIMAL) {
   1336                 updateLayoutForSizeChange(SIZE_TYPE_MINIMAL);
   1337                 if (mMediaType == MEDIA_TYPE_MUSIC) {
   1338                     mTitleView.setVisibility(View.GONE);
   1339                 }
   1340             }
   1341         }
   1342     }
   1343 
   1344     @SuppressWarnings("deprecation")
   1345     private void updateLayoutForSizeChange(int sizeType) {
   1346         mSizeType = sizeType;
   1347         RelativeLayout.LayoutParams timeViewParams =
   1348                 (RelativeLayout.LayoutParams) mTimeView.getLayoutParams();
   1349         SeekBar seeker = (SeekBar) mProgress;
   1350         switch (mSizeType) {
   1351             case SIZE_TYPE_EMBEDDED:
   1352                 // Relating to Title Bar
   1353                 mTitleBar.setVisibility(View.VISIBLE);
   1354                 mBackButton.setVisibility(View.GONE);
   1355                 mTitleView.setPadding(
   1356                         mResources.getDimensionPixelSize(R.dimen.mcv2_embedded_icon_padding),
   1357                         mTitleView.getPaddingTop(),
   1358                         mTitleView.getPaddingRight(),
   1359                         mTitleView.getPaddingBottom());
   1360 
   1361                 // Relating to Full Screen Button
   1362                 mMinimalExtraView.setVisibility(View.GONE);
   1363                 mFullScreenButton = mBottomBarRightView.findViewById(R.id.fullscreen);
   1364                 mFullScreenButton.setOnClickListener(mFullScreenListener);
   1365 
   1366                 // Relating to Center View
   1367                 mCenterView.removeAllViews();
   1368                 mBottomBarLeftView.removeView(mTransportControls);
   1369                 mBottomBarLeftView.setVisibility(View.GONE);
   1370                 mTransportControls = inflateTransportControls(R.layout.embedded_transport_controls);
   1371                 mCenterView.addView(mTransportControls);
   1372 
   1373                 // Relating to Progress Bar
   1374                 seeker.setThumb(mResources.getDrawable(R.drawable.custom_progress_thumb));
   1375                 mProgressBuffer.setVisibility(View.VISIBLE);
   1376 
   1377                 // Relating to Bottom Bar
   1378                 mBottomBar.setVisibility(View.VISIBLE);
   1379                 if (timeViewParams.getRules()[RelativeLayout.LEFT_OF] != 0) {
   1380                     timeViewParams.removeRule(RelativeLayout.LEFT_OF);
   1381                     timeViewParams.addRule(RelativeLayout.RIGHT_OF, R.id.bottom_bar_left);
   1382                 }
   1383                 break;
   1384             case SIZE_TYPE_FULL:
   1385                 // Relating to Title Bar
   1386                 mTitleBar.setVisibility(View.VISIBLE);
   1387                 mBackButton.setVisibility(View.VISIBLE);
   1388                 mTitleView.setPadding(
   1389                         0,
   1390                         mTitleView.getPaddingTop(),
   1391                         mTitleView.getPaddingRight(),
   1392                         mTitleView.getPaddingBottom());
   1393 
   1394                 // Relating to Full Screen Button
   1395                 mMinimalExtraView.setVisibility(View.GONE);
   1396                 mFullScreenButton = mBottomBarRightView.findViewById(R.id.fullscreen);
   1397                 mFullScreenButton.setOnClickListener(mFullScreenListener);
   1398 
   1399                 // Relating to Center View
   1400                 mCenterView.removeAllViews();
   1401                 mBottomBarLeftView.removeView(mTransportControls);
   1402                 mTransportControls = inflateTransportControls(R.layout.full_transport_controls);
   1403                 mBottomBarLeftView.addView(mTransportControls, 0);
   1404                 mBottomBarLeftView.setVisibility(View.VISIBLE);
   1405 
   1406                 // Relating to Progress Bar
   1407                 seeker.setThumb(mResources.getDrawable(R.drawable.custom_progress_thumb));
   1408                 mProgressBuffer.setVisibility(View.VISIBLE);
   1409 
   1410                 // Relating to Bottom Bar
   1411                 mBottomBar.setVisibility(View.VISIBLE);
   1412                 if (timeViewParams.getRules()[RelativeLayout.RIGHT_OF] != 0) {
   1413                     timeViewParams.removeRule(RelativeLayout.RIGHT_OF);
   1414                     timeViewParams.addRule(RelativeLayout.LEFT_OF, R.id.bottom_bar_right);
   1415                 }
   1416                 break;
   1417             case SIZE_TYPE_MINIMAL:
   1418                 // Relating to Title Bar
   1419                 mTitleBar.setVisibility(View.GONE);
   1420                 mBackButton.setVisibility(View.GONE);
   1421 
   1422                 // Relating to Full Screen Button
   1423                 mMinimalExtraView.setVisibility(View.VISIBLE);
   1424                 mFullScreenButton = mMinimalExtraView.findViewById(R.id.minimal_fullscreen);
   1425                 mFullScreenButton.setOnClickListener(mFullScreenListener);
   1426 
   1427                 // Relating to Center View
   1428                 mCenterView.removeAllViews();
   1429                 mBottomBarLeftView.removeView(mTransportControls);
   1430                 mTransportControls = inflateTransportControls(R.layout.minimal_transport_controls);
   1431                 mCenterView.addView(mTransportControls);
   1432 
   1433                 // Relating to Progress Bar
   1434                 seeker.setThumb(null);
   1435                 mProgressBuffer.setVisibility(View.GONE);
   1436 
   1437                 // Relating to Bottom Bar
   1438                 mBottomBar.setVisibility(View.GONE);
   1439                 break;
   1440         }
   1441         mTimeView.setLayoutParams(timeViewParams);
   1442 
   1443         if (isPlaying()) {
   1444             mPlayPauseButton.setImageDrawable(
   1445                     mResources.getDrawable(R.drawable.ic_pause_circle_filled, null));
   1446             mPlayPauseButton.setContentDescription(
   1447                     mResources.getString(R.string.mcv2_pause_button_desc));
   1448         } else {
   1449             mPlayPauseButton.setImageDrawable(
   1450                     mResources.getDrawable(R.drawable.ic_play_circle_filled, null));
   1451             mPlayPauseButton.setContentDescription(
   1452                     mResources.getString(R.string.mcv2_play_button_desc));
   1453         }
   1454 
   1455         if (mIsFullScreen) {
   1456             mFullScreenButton.setImageDrawable(
   1457                     mResources.getDrawable(R.drawable.ic_fullscreen_exit, null));
   1458         } else {
   1459             mFullScreenButton.setImageDrawable(
   1460                     mResources.getDrawable(R.drawable.ic_fullscreen, null));
   1461         }
   1462     }
   1463 
   1464     private View inflateTransportControls(int layoutId) {
   1465         View v = inflateLayout(getContext(), layoutId);
   1466         mPlayPauseButton = v.findViewById(R.id.pause);
   1467         if (mPlayPauseButton != null) {
   1468             mPlayPauseButton.requestFocus();
   1469             mPlayPauseButton.setOnClickListener(mPlayPauseListener);
   1470         }
   1471         mFfwdButton = v.findViewById(R.id.ffwd);
   1472         if (mFfwdButton != null) {
   1473             mFfwdButton.setOnClickListener(mFfwdListener);
   1474             if (mMediaType == MEDIA_TYPE_MUSIC) {
   1475                 mFfwdButton.setVisibility(View.GONE);
   1476             }
   1477         }
   1478         mRewButton = v.findViewById(R.id.rew);
   1479         if (mRewButton != null) {
   1480             mRewButton.setOnClickListener(mRewListener);
   1481             if (mMediaType == MEDIA_TYPE_MUSIC) {
   1482                 mRewButton.setVisibility(View.GONE);
   1483             }
   1484         }
   1485         mNextButton = v.findViewById(R.id.next);
   1486         if (mNextButton != null) {
   1487             mNextButton.setOnClickListener(mNextListener);
   1488             mNextButton.setVisibility(View.GONE);
   1489         }
   1490         mPrevButton = v.findViewById(R.id.prev);
   1491         if (mPrevButton != null) {
   1492             mPrevButton.setOnClickListener(mPrevListener);
   1493             mPrevButton.setVisibility(View.GONE);
   1494         }
   1495         return v;
   1496     }
   1497 
   1498     private void initializeSettingsLists() {
   1499         mSettingsMainTextsList = new ArrayList<String>();
   1500         mSettingsMainTextsList.add(
   1501                 mResources.getString(R.string.MediaControlView2_audio_track_text));
   1502         mSettingsMainTextsList.add(
   1503                 mResources.getString(R.string.MediaControlView2_playback_speed_text));
   1504         mSettingsMainTextsList.add(
   1505                 mResources.getString(R.string.MediaControlView2_help_text));
   1506 
   1507         mSettingsSubTextsList = new ArrayList<String>();
   1508         mSettingsSubTextsList.add(
   1509                 mResources.getString(R.string.MediaControlView2_audio_track_none_text));
   1510         mSettingsSubTextsList.add(
   1511                 mResources.getStringArray(
   1512                         R.array.MediaControlView2_playback_speeds)[PLAYBACK_SPEED_1x_INDEX]);
   1513         mSettingsSubTextsList.add(RESOURCE_EMPTY);
   1514 
   1515         mSettingsIconIdsList = new ArrayList<Integer>();
   1516         mSettingsIconIdsList.add(R.drawable.ic_audiotrack);
   1517         mSettingsIconIdsList.add(R.drawable.ic_play_circle_filled);
   1518         mSettingsIconIdsList.add(R.drawable.ic_help);
   1519 
   1520         mAudioTrackList = new ArrayList<String>();
   1521         mAudioTrackList.add(
   1522                 mResources.getString(R.string.MediaControlView2_audio_track_none_text));
   1523 
   1524         mVideoQualityList = new ArrayList<String>();
   1525         mVideoQualityList.add(
   1526                 mResources.getString(R.string.MediaControlView2_video_quality_auto_text));
   1527 
   1528         mPlaybackSpeedTextList = new ArrayList<String>(Arrays.asList(
   1529                 mResources.getStringArray(R.array.MediaControlView2_playback_speeds)));
   1530         // Select the "1x" speed as the default value.
   1531         mSelectedSpeedIndex = PLAYBACK_SPEED_1x_INDEX;
   1532 
   1533         mPlaybackSpeedList = new ArrayList<Float>();
   1534         int[] speeds = mResources.getIntArray(R.array.speed_multiplied_by_100);
   1535         for (int i = 0; i < speeds.length; i++) {
   1536             float speed = (float) speeds[i] / 100.0f;
   1537             mPlaybackSpeedList.add(speed);
   1538         }
   1539     }
   1540 
   1541     private void displaySettingsWindow(BaseAdapter adapter) {
   1542         // Set Adapter
   1543         mSettingsListView.setAdapter(adapter);
   1544 
   1545         // Set width of window
   1546         int itemWidth = (mSizeType == SIZE_TYPE_EMBEDDED)
   1547                 ? mEmbeddedSettingsItemWidth : mFullSettingsItemWidth;
   1548         mSettingsWindow.setWidth(itemWidth);
   1549 
   1550         // Calculate height of window and show
   1551         int totalHeight = adapter.getCount() * mSettingsItemHeight;
   1552         mSettingsWindow.dismiss();
   1553         mSettingsWindow.showAsDropDown(this, mSettingsWindowMargin,
   1554                 mSettingsWindowMargin - totalHeight, Gravity.BOTTOM | Gravity.RIGHT);
   1555     }
   1556 
   1557     private class MediaControllerCallback extends MediaControllerCompat.Callback {
   1558         @Override
   1559         public void onPlaybackStateChanged(PlaybackStateCompat state) {
   1560             mPlaybackState = state;
   1561 
   1562             // Update pause button depending on playback state for the following two reasons:
   1563             //   1) Need to handle case where app customizes playback state behavior when app
   1564             //      activity is resumed.
   1565             //   2) Need to handle case where the media file reaches end of duration.
   1566             if (mPlaybackState.getState() != mPrevState) {
   1567                 switch (mPlaybackState.getState()) {
   1568                     case PlaybackStateCompat.STATE_PLAYING:
   1569                         mPlayPauseButton.setImageDrawable(
   1570                                 mResources.getDrawable(R.drawable.ic_pause_circle_filled, null));
   1571                         mPlayPauseButton.setContentDescription(
   1572                                 mResources.getString(R.string.mcv2_pause_button_desc));
   1573                         removeCallbacks(mUpdateProgress);
   1574                         post(mUpdateProgress);
   1575                         break;
   1576                     case PlaybackStateCompat.STATE_PAUSED:
   1577                         mPlayPauseButton.setImageDrawable(
   1578                                 mResources.getDrawable(R.drawable.ic_play_circle_filled, null));
   1579                         mPlayPauseButton.setContentDescription(
   1580                                 mResources.getString(R.string.mcv2_play_button_desc));
   1581                         break;
   1582                     case PlaybackStateCompat.STATE_STOPPED:
   1583                         mPlayPauseButton.setImageDrawable(
   1584                                 mResources.getDrawable(R.drawable.ic_replay_circle_filled, null));
   1585                         mPlayPauseButton.setContentDescription(
   1586                                 mResources.getString(R.string.mcv2_replay_button_desc));
   1587                         mIsStopped = true;
   1588                         break;
   1589                     default:
   1590                         break;
   1591                 }
   1592                 mPrevState = mPlaybackState.getState();
   1593             }
   1594 
   1595             if (mPlaybackActions != mPlaybackState.getActions()) {
   1596                 long newActions = mPlaybackState.getActions();
   1597                 if ((newActions & PlaybackStateCompat.ACTION_PAUSE) != 0) {
   1598                     mPlayPauseButton.setVisibility(View.VISIBLE);
   1599                 }
   1600                 if ((newActions & PlaybackStateCompat.ACTION_REWIND) != 0
   1601                         && mMediaType != MEDIA_TYPE_MUSIC) {
   1602                     if (mRewButton != null) {
   1603                         mRewButton.setVisibility(View.VISIBLE);
   1604                     }
   1605                 }
   1606                 if ((newActions & PlaybackStateCompat.ACTION_FAST_FORWARD) != 0
   1607                         && mMediaType != MEDIA_TYPE_MUSIC) {
   1608                     if (mFfwdButton != null) {
   1609                         mFfwdButton.setVisibility(View.VISIBLE);
   1610                     }
   1611                 }
   1612                 if ((newActions & PlaybackStateCompat.ACTION_SEEK_TO) != 0) {
   1613                     mSeekAvailable = true;
   1614                 } else {
   1615                     mSeekAvailable = false;
   1616                 }
   1617                 mPlaybackActions = newActions;
   1618             }
   1619 
   1620             // Add buttons if custom actions are present.
   1621             List<PlaybackStateCompat.CustomAction> customActions =
   1622                     mPlaybackState.getCustomActions();
   1623             mCustomButtons.removeAllViews();
   1624             if (customActions.size() > 0) {
   1625                 for (final PlaybackStateCompat.CustomAction action : customActions) {
   1626                     ImageButton button = new ImageButton(getContext(),
   1627                             null /* AttributeSet */, 0 /* Style */);
   1628                     // Refer Constructor with argument (int defStyleRes) of View.java
   1629                     button.setImageResource(action.getIcon());
   1630                     final String actionString = action.getAction().toString();
   1631                     button.setOnClickListener(new OnClickListener() {
   1632                         @Override
   1633                         public void onClick(View v) {
   1634                             mControls.sendCustomAction(actionString, action.getExtras());
   1635                             setVisibility(View.VISIBLE);
   1636                         }
   1637                     });
   1638                     mCustomButtons.addView(button);
   1639                 }
   1640             }
   1641         }
   1642 
   1643         @Override
   1644         public void onMetadataChanged(MediaMetadataCompat metadata) {
   1645             mMetadata = metadata;
   1646             updateDuration();
   1647             updateTitle();
   1648             updateAudioMetadata();
   1649         }
   1650 
   1651         @Override
   1652         public void onSessionEvent(String event, Bundle extras) {
   1653             switch (event) {
   1654                 case EVENT_UPDATE_TRACK_STATUS:
   1655                     mVideoTrackCount = extras.getInt(KEY_VIDEO_TRACK_COUNT);
   1656                     // If there is one or more audio tracks, and this information has not been
   1657                     // reflected into the Settings window yet, automatically check the first track.
   1658                     // Otherwise, the Audio Track selection will be defaulted to "None".
   1659                     mAudioTrackCount = extras.getInt(KEY_AUDIO_TRACK_COUNT);
   1660                     mAudioTrackList = new ArrayList<String>();
   1661                     if (mAudioTrackCount > 0) {
   1662                         for (int i = 0; i < mAudioTrackCount; i++) {
   1663                             String track = mResources.getString(
   1664                                     R.string.MediaControlView2_audio_track_number_text, i + 1);
   1665                             mAudioTrackList.add(track);
   1666                         }
   1667                         // Change sub text inside the Settings window.
   1668                         mSettingsSubTextsList.set(SETTINGS_MODE_AUDIO_TRACK,
   1669                                 mAudioTrackList.get(0));
   1670                     } else {
   1671                         mAudioTrackList.add(mResources.getString(
   1672                                 R.string.MediaControlView2_audio_track_none_text));
   1673                     }
   1674                     if (mVideoTrackCount == 0 && mAudioTrackCount > 0) {
   1675                         mMediaType = MEDIA_TYPE_MUSIC;
   1676                     }
   1677 
   1678                     mSubtitleTrackCount = extras.getInt(KEY_SUBTITLE_TRACK_COUNT);
   1679                     mSubtitleDescriptionsList = new ArrayList<String>();
   1680                     if (mSubtitleTrackCount > 0) {
   1681                         mSubtitleButton.setVisibility(View.VISIBLE);
   1682                         mSubtitleButton.setEnabled(true);
   1683                         mSubtitleDescriptionsList.add(mResources.getString(
   1684                                 R.string.MediaControlView2_subtitle_off_text));
   1685                         for (int i = 0; i < mSubtitleTrackCount; i++) {
   1686                             String track = mResources.getString(
   1687                                     R.string.MediaControlView2_subtitle_track_number_text, i + 1);
   1688                             mSubtitleDescriptionsList.add(track);
   1689                         }
   1690                     } else {
   1691                         mSubtitleButton.setVisibility(View.GONE);
   1692                         mSubtitleButton.setEnabled(false);
   1693                     }
   1694                     break;
   1695                 case EVENT_UPDATE_MEDIA_TYPE_STATUS:
   1696                     boolean newStatus = extras.getBoolean(KEY_STATE_IS_ADVERTISEMENT);
   1697                     if (newStatus != mIsAdvertisement) {
   1698                         mIsAdvertisement = newStatus;
   1699                         updateLayout();
   1700                     }
   1701                     break;
   1702             }
   1703         }
   1704     }
   1705 
   1706     private class SettingsAdapter extends BaseAdapter {
   1707         private List<Integer> mIconIds;
   1708         private List<String> mMainTexts;
   1709         private List<String> mSubTexts;
   1710 
   1711         SettingsAdapter(List<String> mainTexts, @Nullable List<String> subTexts,
   1712                 @Nullable List<Integer> iconIds) {
   1713             mMainTexts = mainTexts;
   1714             mSubTexts = subTexts;
   1715             mIconIds = iconIds;
   1716         }
   1717 
   1718         public void updateSubTexts(List<String> subTexts) {
   1719             mSubTexts = subTexts;
   1720             notifyDataSetChanged();
   1721         }
   1722 
   1723         public String getMainText(int position) {
   1724             if (mMainTexts != null) {
   1725                 if (position < mMainTexts.size()) {
   1726                     return mMainTexts.get(position);
   1727                 }
   1728             }
   1729             return RESOURCE_EMPTY;
   1730         }
   1731 
   1732         @Override
   1733         public int getCount() {
   1734             return (mMainTexts == null) ? 0 : mMainTexts.size();
   1735         }
   1736 
   1737         @Override
   1738         public long getItemId(int position) {
   1739             // Auto-generated method stub--does not have any purpose here
   1740             return 0;
   1741         }
   1742 
   1743         @Override
   1744         public Object getItem(int position) {
   1745             // Auto-generated method stub--does not have any purpose here
   1746             return null;
   1747         }
   1748 
   1749         @Override
   1750         public View getView(int position, View convertView, ViewGroup container) {
   1751             View row;
   1752             if (mSizeType == SIZE_TYPE_FULL) {
   1753                 row = inflateLayout(getContext(), R.layout.full_settings_list_item);
   1754             } else {
   1755                 row = inflateLayout(getContext(), R.layout.embedded_settings_list_item);
   1756             }
   1757             TextView mainTextView = (TextView) row.findViewById(R.id.main_text);
   1758             TextView subTextView = (TextView) row.findViewById(R.id.sub_text);
   1759             ImageView iconView = (ImageView) row.findViewById(R.id.icon);
   1760 
   1761             // Set main text
   1762             mainTextView.setText(mMainTexts.get(position));
   1763 
   1764             // Remove sub text and center the main text if sub texts do not exist at all or the sub
   1765             // text at this particular position is empty.
   1766             if (mSubTexts == null || RESOURCE_EMPTY.equals(mSubTexts.get(position))) {
   1767                 subTextView.setVisibility(View.GONE);
   1768             } else {
   1769                 // Otherwise, set sub text.
   1770                 subTextView.setText(mSubTexts.get(position));
   1771             }
   1772 
   1773             // Remove main icon and set visibility to gone if icons are set to null or the icon at
   1774             // this particular position is set to RESOURCE_NON_EXISTENT.
   1775             if (mIconIds == null || mIconIds.get(position) == RESOURCE_NON_EXISTENT) {
   1776                 iconView.setVisibility(View.GONE);
   1777             } else {
   1778                 // Otherwise, set main icon.
   1779                 iconView.setImageDrawable(mResources.getDrawable(mIconIds.get(position), null));
   1780             }
   1781             return row;
   1782         }
   1783 
   1784         public void setSubTexts(List<String> subTexts) {
   1785             mSubTexts = subTexts;
   1786         }
   1787     }
   1788 
   1789     private class SubSettingsAdapter extends BaseAdapter {
   1790         private List<String> mTexts;
   1791         private int mCheckPosition;
   1792 
   1793         SubSettingsAdapter(List<String> texts, int checkPosition) {
   1794             mTexts = texts;
   1795             mCheckPosition = checkPosition;
   1796         }
   1797 
   1798         public String getMainText(int position) {
   1799             if (mTexts != null) {
   1800                 if (position < mTexts.size()) {
   1801                     return mTexts.get(position);
   1802                 }
   1803             }
   1804             return RESOURCE_EMPTY;
   1805         }
   1806 
   1807         @Override
   1808         public int getCount() {
   1809             return (mTexts == null) ? 0 : mTexts.size();
   1810         }
   1811 
   1812         @Override
   1813         public long getItemId(int position) {
   1814             // Auto-generated method stub--does not have any purpose here
   1815             return 0;
   1816         }
   1817 
   1818         @Override
   1819         public Object getItem(int position) {
   1820             // Auto-generated method stub--does not have any purpose here
   1821             return null;
   1822         }
   1823 
   1824         @Override
   1825         public View getView(int position, View convertView, ViewGroup container) {
   1826             View row;
   1827             if (mSizeType == SIZE_TYPE_FULL) {
   1828                 row = inflateLayout(getContext(), R.layout.full_sub_settings_list_item);
   1829             } else {
   1830                 row = inflateLayout(getContext(), R.layout.embedded_sub_settings_list_item);
   1831             }
   1832             TextView textView = (TextView) row.findViewById(R.id.text);
   1833             ImageView checkView = (ImageView) row.findViewById(R.id.check);
   1834 
   1835             textView.setText(mTexts.get(position));
   1836             if (position != mCheckPosition) {
   1837                 checkView.setVisibility(View.INVISIBLE);
   1838             }
   1839             return row;
   1840         }
   1841 
   1842         public void setTexts(List<String> texts) {
   1843             mTexts = texts;
   1844         }
   1845 
   1846         public void setCheckPosition(int checkPosition) {
   1847             mCheckPosition = checkPosition;
   1848         }
   1849     }
   1850 }
   1851