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