• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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.tv.menu;
18 
19 import android.content.Context;
20 import android.content.res.Resources;
21 import android.text.format.DateFormat;
22 import android.util.AttributeSet;
23 import android.view.View;
24 import android.view.ViewGroup;
25 import android.widget.TextView;
26 import android.widget.Toast;
27 
28 import com.android.tv.MainActivity;
29 import com.android.tv.R;
30 import com.android.tv.TimeShiftManager;
31 import com.android.tv.TimeShiftManager.TimeShiftActionId;
32 import com.android.tv.TvApplication;
33 import com.android.tv.common.SoftPreconditions;
34 import com.android.tv.common.feature.CommonFeatures;
35 import com.android.tv.data.Channel;
36 import com.android.tv.data.Program;
37 import com.android.tv.dvr.DvrDataManager;
38 import com.android.tv.dvr.DvrDataManager.OnDvrScheduleLoadFinishedListener;
39 import com.android.tv.dvr.DvrDataManager.ScheduledRecordingListener;
40 import com.android.tv.dvr.DvrManager;
41 import com.android.tv.dvr.DvrUiHelper;
42 import com.android.tv.dvr.ScheduledRecording;
43 import com.android.tv.dvr.ui.DvrStopRecordingFragment;
44 import com.android.tv.dvr.ui.HalfSizedDialogFragment;
45 import com.android.tv.menu.Menu.MenuShowReason;
46 import com.android.tv.ui.TunableTvView;
47 import com.android.tv.util.Utils;
48 
49 public class PlayControlsRowView extends MenuRowView {
50     private static final int NORMAL_WIDTH_MAX_BUTTON_COUNT = 5;
51     // Dimensions
52     private final int mTimeIndicatorLeftMargin;
53     private final int mTimeTextLeftMargin;
54     private final int mTimelineWidth;
55     // Views
56     private View mBackgroundView;
57     private View mTimeIndicator;
58     private TextView mTimeText;
59     private View mProgressEmptyBefore;
60     private View mProgressWatched;
61     private View mProgressBuffered;
62     private View mProgressEmptyAfter;
63     private View mControlBar;
64     private PlayControlsButton mJumpPreviousButton;
65     private PlayControlsButton mRewindButton;
66     private PlayControlsButton mPlayPauseButton;
67     private PlayControlsButton mFastForwardButton;
68     private PlayControlsButton mJumpNextButton;
69     private PlayControlsButton mRecordButton;
70     private TextView mProgramStartTimeText;
71     private TextView mProgramEndTimeText;
72     private View mUnavailableMessageText;
73     private TunableTvView mTvView;
74     private TimeShiftManager mTimeShiftManager;
75     private final DvrDataManager mDvrDataManager;
76     private final DvrManager mDvrManager;
77     private final MainActivity mMainActivity;
78 
79     private final java.text.DateFormat mTimeFormat;
80     private long mProgramStartTimeMs;
81     private long mProgramEndTimeMs;
82     private boolean mUseCompactLayout;
83     private final int mNormalButtonMargin;
84     private final int mCompactButtonMargin;
85 
86     private final ScheduledRecordingListener mScheduledRecordingListener
87             = new ScheduledRecordingListener() {
88         @Override
89         public void onScheduledRecordingAdded(ScheduledRecording... scheduledRecordings) { }
90 
91         @Override
92         public void onScheduledRecordingRemoved(ScheduledRecording... scheduledRecordings) { }
93 
94         @Override
95         public void onScheduledRecordingStatusChanged(ScheduledRecording... scheduledRecordings) {
96             Channel currentChannel = mMainActivity.getCurrentChannel();
97             if (currentChannel != null && isShown()) {
98                 for (ScheduledRecording schedule : scheduledRecordings) {
99                     if (schedule.getChannelId() == currentChannel.getId()) {
100                         updateRecordButton();
101                         break;
102                     }
103                 }
104             }
105         }
106     };
107 
PlayControlsRowView(Context context)108     public PlayControlsRowView(Context context) {
109         this(context, null);
110     }
111 
PlayControlsRowView(Context context, AttributeSet attrs)112     public PlayControlsRowView(Context context, AttributeSet attrs) {
113         this(context, attrs, 0);
114     }
115 
PlayControlsRowView(Context context, AttributeSet attrs, int defStyleAttr)116     public PlayControlsRowView(Context context, AttributeSet attrs, int defStyleAttr) {
117         this(context, attrs, defStyleAttr, 0);
118     }
119 
PlayControlsRowView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)120     public PlayControlsRowView(Context context, AttributeSet attrs, int defStyleAttr,
121             int defStyleRes) {
122         super(context, attrs, defStyleAttr, defStyleRes);
123         Resources res = context.getResources();
124         mTimeIndicatorLeftMargin =
125                 - res.getDimensionPixelSize(R.dimen.play_controls_time_indicator_width) / 2;
126         mTimeTextLeftMargin =
127                 - res.getDimensionPixelOffset(R.dimen.play_controls_time_width) / 2;
128         mTimelineWidth = res.getDimensionPixelSize(R.dimen.play_controls_width);
129         mTimeFormat = DateFormat.getTimeFormat(context);
130         mNormalButtonMargin = res.getDimensionPixelSize(R.dimen.play_controls_button_normal_margin);
131         mCompactButtonMargin =
132                 res.getDimensionPixelSize(R.dimen.play_controls_button_compact_margin);
133         if (CommonFeatures.DVR.isEnabled(context)) {
134             mDvrDataManager = TvApplication.getSingletons(context).getDvrDataManager();
135             mDvrManager = TvApplication.getSingletons(context).getDvrManager();
136         } else {
137             mDvrDataManager = null;
138             mDvrManager = null;
139         }
140         mMainActivity = (MainActivity) context;
141     }
142 
143     @Override
onAttachedToWindow()144     public void onAttachedToWindow() {
145         super.onAttachedToWindow();
146         if (mDvrDataManager != null) {
147             mDvrDataManager.addScheduledRecordingListener(mScheduledRecordingListener);
148             if (!mDvrDataManager.isDvrScheduleLoadFinished()) {
149                 mDvrDataManager.addDvrScheduleLoadFinishedListener(
150                         new OnDvrScheduleLoadFinishedListener() {
151                             @Override
152                             public void onDvrScheduleLoadFinished() {
153                                 mDvrDataManager.removeDvrScheduleLoadFinishedListener(this);
154                                 if (isShown()) {
155                                     updateRecordButton();
156                                 }
157                             }
158                         });
159             }
160 
161         }
162     }
163 
164     @Override
getContentsViewId()165     protected int getContentsViewId() {
166         return R.id.play_controls;
167     }
168 
169     @Override
onFinishInflate()170     protected void onFinishInflate() {
171         super.onFinishInflate();
172         // Clip the ViewGroup(body) to the rounded rectangle of outline.
173         findViewById(R.id.body).setClipToOutline(true);
174         mBackgroundView = findViewById(R.id.background);
175         mTimeIndicator = findViewById(R.id.time_indicator);
176         mTimeText = (TextView) findViewById(R.id.time_text);
177         mProgressEmptyBefore = findViewById(R.id.timeline_bg_start);
178         mProgressWatched = findViewById(R.id.watched);
179         mProgressBuffered = findViewById(R.id.buffered);
180         mProgressEmptyAfter = findViewById(R.id.timeline_bg_end);
181         mControlBar = findViewById(R.id.play_control_bar);
182         mJumpPreviousButton = (PlayControlsButton) findViewById(R.id.jump_previous);
183         mRewindButton = (PlayControlsButton) findViewById(R.id.rewind);
184         mPlayPauseButton = (PlayControlsButton) findViewById(R.id.play_pause);
185         mFastForwardButton = (PlayControlsButton) findViewById(R.id.fast_forward);
186         mJumpNextButton = (PlayControlsButton) findViewById(R.id.jump_next);
187         mRecordButton = (PlayControlsButton) findViewById(R.id.record);
188         mProgramStartTimeText = (TextView) findViewById(R.id.program_start_time);
189         mProgramEndTimeText = (TextView) findViewById(R.id.program_end_time);
190         mUnavailableMessageText = findViewById(R.id.unavailable_text);
191 
192         initializeButton(mJumpPreviousButton, R.drawable.lb_ic_skip_previous,
193                 R.string.play_controls_description_skip_previous, null, new Runnable() {
194             @Override
195             public void run() {
196                 if (mTimeShiftManager.isAvailable()) {
197                     mTimeShiftManager.jumpToPrevious();
198                     updateControls();
199                 }
200             }
201         });
202         initializeButton(mRewindButton, R.drawable.lb_ic_fast_rewind,
203                 R.string.play_controls_description_fast_rewind, null, new Runnable() {
204             @Override
205             public void run() {
206                 if (mTimeShiftManager.isAvailable()) {
207                     mTimeShiftManager.rewind();
208                     updateButtons();
209                 }
210             }
211         });
212         initializeButton(mPlayPauseButton, R.drawable.lb_ic_play,
213                 R.string.play_controls_description_play_pause, null, new Runnable() {
214             @Override
215             public void run() {
216                 if (mTimeShiftManager.isAvailable()) {
217                     mTimeShiftManager.togglePlayPause();
218                     updateButtons();
219                 }
220             }
221         });
222         initializeButton(mFastForwardButton, R.drawable.lb_ic_fast_forward,
223                 R.string.play_controls_description_fast_forward, null, new Runnable() {
224             @Override
225             public void run() {
226                 if (mTimeShiftManager.isAvailable()) {
227                     mTimeShiftManager.fastForward();
228                     updateButtons();
229                 }
230             }
231         });
232         initializeButton(mJumpNextButton, R.drawable.lb_ic_skip_next,
233                 R.string.play_controls_description_skip_next, null, new Runnable() {
234             @Override
235             public void run() {
236                 if (mTimeShiftManager.isAvailable()) {
237                     mTimeShiftManager.jumpToNext();
238                     updateControls();
239                 }
240             }
241         });
242         int color = getResources().getColor(R.color.play_controls_recording_icon_color_on_focus,
243                 null);
244         initializeButton(mRecordButton, R.drawable.ic_record_start, R.string
245                 .channels_item_record_start, color, new Runnable() {
246             @Override
247             public void run() {
248                 onRecordButtonClicked();
249             }
250         });
251     }
252 
isCurrentChannelRecording()253     private boolean isCurrentChannelRecording() {
254         Channel currentChannel = mMainActivity.getCurrentChannel();
255         return currentChannel != null && mDvrManager != null
256                 && mDvrManager.getCurrentRecording(currentChannel.getId()) != null;
257     }
258 
onRecordButtonClicked()259     private void onRecordButtonClicked() {
260         boolean isRecording = isCurrentChannelRecording();
261         Channel currentChannel = mMainActivity.getCurrentChannel();
262         TvApplication.getSingletons(getContext()).getTracker().sendMenuClicked(isRecording ?
263                 R.string.channels_item_record_start : R.string.channels_item_record_stop);
264         if (!isRecording) {
265             if (!(mDvrManager != null && mDvrManager.isChannelRecordable(currentChannel))) {
266                 Toast.makeText(mMainActivity, R.string.dvr_msg_cannot_record_channel,
267                         Toast.LENGTH_SHORT).show();
268             } else if (DvrUiHelper.checkStorageStatusAndShowErrorMessage(mMainActivity,
269                     currentChannel.getInputId())) {
270                 Program program = TvApplication.getSingletons(mMainActivity).getProgramDataManager()
271                         .getCurrentProgram(currentChannel.getId());
272                 if (program == null) {
273                     DvrUiHelper.showChannelRecordDurationOptions(mMainActivity, currentChannel);
274                 } else if (DvrUiHelper.handleCreateSchedule(mMainActivity, program)) {
275                     String msg = mMainActivity.getString(R.string.dvr_msg_current_program_scheduled,
276                             program.getTitle(),
277                             Utils.toTimeString(program.getEndTimeUtcMillis(), false));
278                     Toast.makeText(mMainActivity, msg, Toast.LENGTH_SHORT).show();
279                 }
280             }
281         } else if (currentChannel != null) {
282             DvrUiHelper.showStopRecordingDialog(mMainActivity, currentChannel.getId(),
283                     DvrStopRecordingFragment.REASON_USER_STOP,
284                     new HalfSizedDialogFragment.OnActionClickListener() {
285                         @Override
286                         public void onActionClick(long actionId) {
287                             if (actionId == DvrStopRecordingFragment.ACTION_STOP) {
288                                 ScheduledRecording currentRecording =
289                                         mDvrManager.getCurrentRecording(
290                                                 currentChannel.getId());
291                                 if (currentRecording != null) {
292                                     mDvrManager.stopRecording(currentRecording);
293                                 }
294                             }
295                         }
296                     });
297         }
298     }
299 
initializeButton(PlayControlsButton button, int imageResId, int descriptionId, Integer focusedIconColor, Runnable clickAction)300     private void initializeButton(PlayControlsButton button, int imageResId,
301             int descriptionId, Integer focusedIconColor, Runnable clickAction) {
302         button.setImageResId(imageResId);
303         button.setAction(clickAction);
304         if (focusedIconColor != null) {
305             button.setFocusedIconColor(focusedIconColor);
306         }
307         button.findViewById(R.id.button)
308                 .setContentDescription(getResources().getString(descriptionId));
309     }
310 
311     @Override
onBind(MenuRow row)312     public void onBind(MenuRow row) {
313         super.onBind(row);
314         PlayControlsRow playControlsRow = (PlayControlsRow) row;
315         mTvView = playControlsRow.getTvView();
316         mTimeShiftManager = playControlsRow.getTimeShiftManager();
317         mTimeShiftManager.setListener(new TimeShiftManager.Listener() {
318             @Override
319             public void onAvailabilityChanged() {
320                 updateMenuVisibility();
321                 if (isShown()) {
322                     PlayControlsRowView.this.updateAll();
323                 }
324             }
325 
326             @Override
327             public void onPlayStatusChanged(int status) {
328                 updateMenuVisibility();
329                 if (mTimeShiftManager.isAvailable() && isShown()) {
330                     updateControls();
331                 }
332             }
333 
334             @Override
335             public void onRecordTimeRangeChanged() {
336                 if (mTimeShiftManager.isAvailable() && isShown()) {
337                     updateControls();
338                 }
339             }
340 
341             @Override
342             public void onCurrentPositionChanged() {
343                 if (mTimeShiftManager.isAvailable() && isShown()) {
344                     initializeTimeline();
345                     updateControls();
346                 }
347             }
348 
349             @Override
350             public void onProgramInfoChanged() {
351                 if (mTimeShiftManager.isAvailable() && isShown()) {
352                     initializeTimeline();
353                     updateControls();
354                 }
355             }
356 
357             @Override
358             public void onActionEnabledChanged(@TimeShiftActionId int actionId, boolean enabled) {
359                 // Move focus to the play/pause button when the PREVIOUS, NEXT, REWIND or
360                 // FAST_FORWARD button is clicked and the button becomes disabled.
361                 // No need to update the UI here because the UI will be updated by other callbacks.
362                 if (!enabled &&
363                         ((actionId == TimeShiftManager.TIME_SHIFT_ACTION_ID_JUMP_TO_PREVIOUS
364                                 && mJumpPreviousButton.hasFocus())
365                         || (actionId == TimeShiftManager.TIME_SHIFT_ACTION_ID_REWIND
366                                 && mRewindButton.hasFocus())
367                         || (actionId == TimeShiftManager.TIME_SHIFT_ACTION_ID_FAST_FORWARD
368                                 && mFastForwardButton.hasFocus())
369                         || (actionId == TimeShiftManager.TIME_SHIFT_ACTION_ID_JUMP_TO_NEXT
370                                 && mJumpNextButton.hasFocus()))) {
371                     mPlayPauseButton.requestFocus();
372                 }
373             }
374         });
375         updateAll();
376     }
377 
initializeTimeline()378     private void initializeTimeline() {
379         Program program = mTimeShiftManager.getProgramAt(
380                 mTimeShiftManager.getCurrentPositionMs());
381         mProgramStartTimeMs = program.getStartTimeUtcMillis();
382         mProgramEndTimeMs = program.getEndTimeUtcMillis();
383         SoftPreconditions.checkArgument(mProgramStartTimeMs <= mProgramEndTimeMs);
384     }
385 
updateMenuVisibility()386     private void updateMenuVisibility() {
387         boolean keepMenuVisible =
388                 mTimeShiftManager.isAvailable() && !mTimeShiftManager.isNormalPlaying();
389         getMenu().setKeepVisible(keepMenuVisible);
390     }
391 
392     @Override
onSelected(boolean showTitle)393     public void onSelected(boolean showTitle) {
394         super.onSelected(showTitle);
395         updateControls();
396         postHideRippleAnimation();
397     }
398 
399     @Override
initialize(@enuShowReason int reason)400     public void initialize(@MenuShowReason int reason) {
401         super.initialize(reason);
402         switch (reason) {
403             case Menu.REASON_PLAY_CONTROLS_JUMP_TO_PREVIOUS:
404                 if (mTimeShiftManager.isActionEnabled(
405                         TimeShiftManager.TIME_SHIFT_ACTION_ID_JUMP_TO_PREVIOUS)) {
406                     setInitialFocusView(mJumpPreviousButton);
407                 } else {
408                     setInitialFocusView(mPlayPauseButton);
409                 }
410                 break;
411             case Menu.REASON_PLAY_CONTROLS_REWIND:
412                 if (mTimeShiftManager.isActionEnabled(
413                         TimeShiftManager.TIME_SHIFT_ACTION_ID_REWIND)) {
414                     setInitialFocusView(mRewindButton);
415                 } else {
416                     setInitialFocusView(mPlayPauseButton);
417                 }
418                 break;
419             case Menu.REASON_PLAY_CONTROLS_FAST_FORWARD:
420                 if (mTimeShiftManager.isActionEnabled(
421                         TimeShiftManager.TIME_SHIFT_ACTION_ID_FAST_FORWARD)) {
422                     setInitialFocusView(mFastForwardButton);
423                 } else {
424                     setInitialFocusView(mPlayPauseButton);
425                 }
426                 break;
427             case Menu.REASON_PLAY_CONTROLS_JUMP_TO_NEXT:
428                 if (mTimeShiftManager.isActionEnabled(
429                         TimeShiftManager.TIME_SHIFT_ACTION_ID_JUMP_TO_NEXT)) {
430                     setInitialFocusView(mJumpNextButton);
431                 } else {
432                     setInitialFocusView(mPlayPauseButton);
433                 }
434                 break;
435             case Menu.REASON_PLAY_CONTROLS_PLAY_PAUSE:
436             case Menu.REASON_PLAY_CONTROLS_PLAY:
437             case Menu.REASON_PLAY_CONTROLS_PAUSE:
438             default:
439                 setInitialFocusView(mPlayPauseButton);
440                 break;
441         }
442         postHideRippleAnimation();
443     }
444 
postHideRippleAnimation()445     private void postHideRippleAnimation() {
446         // Focus may be changed in another message if requestFocus is called in this message.
447         // After the focus is actually changed, hideRippleAnimation should run
448         // to reflect the result of the focus change. To be sure, hideRippleAnimation is posted.
449         post(new Runnable() {
450             @Override
451             public void run() {
452                 mJumpPreviousButton.hideRippleAnimation();
453                 mRewindButton.hideRippleAnimation();
454                 mPlayPauseButton.hideRippleAnimation();
455                 mFastForwardButton.hideRippleAnimation();
456                 mJumpNextButton.hideRippleAnimation();
457             }
458         });
459     }
460 
461     @Override
onChildFocusChange(View v, boolean hasFocus)462     protected void onChildFocusChange(View v, boolean hasFocus) {
463         super.onChildFocusChange(v, hasFocus);
464         if ((v.getParent().equals(mRewindButton) || v.getParent().equals(mFastForwardButton))
465                 && !hasFocus) {
466             if (mTimeShiftManager.getPlayStatus() == TimeShiftManager.PLAY_STATUS_PLAYING) {
467                 mTimeShiftManager.play();
468                 updateButtons();
469             }
470         }
471     }
472 
473     /**
474      * Updates the view contents. It is called from the PlayControlsRow.
475      */
update()476     public void update() {
477         updateAll();
478     }
479 
updateAll()480     private void updateAll() {
481         if (mTimeShiftManager.isAvailable() && !mTvView.isScreenBlocked()) {
482             setEnabled(true);
483             initializeTimeline();
484             mBackgroundView.setEnabled(true);
485         } else {
486             setEnabled(false);
487             mBackgroundView.setEnabled(false);
488         }
489         updateControls();
490     }
491 
updateControls()492     private void updateControls() {
493         updateTime();
494         updateProgress();
495         updateRecTimeText();
496         updateButtons();
497         updateRecordButton();
498         updateButtonMargin();
499     }
500 
updateTime()501     private void updateTime() {
502         if (isEnabled()) {
503             mTimeText.setVisibility(View.VISIBLE);
504             mTimeIndicator.setVisibility(View.VISIBLE);
505         } else {
506             mTimeText.setVisibility(View.INVISIBLE);
507             mTimeIndicator.setVisibility(View.INVISIBLE);
508             return;
509         }
510         long currentPositionMs = mTimeShiftManager.getCurrentPositionMs();
511         ViewGroup.MarginLayoutParams params =
512                 (ViewGroup.MarginLayoutParams) mTimeText.getLayoutParams();
513         int currentTimePositionPixel =
514                 convertDurationToPixel(currentPositionMs - mProgramStartTimeMs);
515         params.leftMargin = currentTimePositionPixel + mTimeTextLeftMargin;
516         mTimeText.setLayoutParams(params);
517         mTimeText.setText(getTimeString(currentPositionMs));
518         params = (ViewGroup.MarginLayoutParams) mTimeIndicator.getLayoutParams();
519         params.leftMargin = currentTimePositionPixel + mTimeIndicatorLeftMargin;
520         mTimeIndicator.setLayoutParams(params);
521     }
522 
updateProgress()523     private void updateProgress() {
524         if (isEnabled()) {
525             mProgressWatched.setVisibility(View.VISIBLE);
526             mProgressBuffered.setVisibility(View.VISIBLE);
527             mProgressEmptyAfter.setVisibility(View.VISIBLE);
528         } else {
529             mProgressWatched.setVisibility(View.INVISIBLE);
530             mProgressBuffered.setVisibility(View.INVISIBLE);
531             mProgressEmptyAfter.setVisibility(View.INVISIBLE);
532             if (mProgramStartTimeMs < mProgramEndTimeMs) {
533                 layoutProgress(mProgressEmptyBefore, mProgramStartTimeMs, mProgramEndTimeMs);
534             } else {
535                 // Not initialized yet.
536                 layoutProgress(mProgressEmptyBefore, mTimelineWidth);
537             }
538             return;
539         }
540 
541         long progressStartTimeMs = Math.min(mProgramEndTimeMs,
542                     Math.max(mProgramStartTimeMs, mTimeShiftManager.getRecordStartTimeMs()));
543         long currentPlayingTimeMs = Math.min(mProgramEndTimeMs,
544                     Math.max(mProgramStartTimeMs, mTimeShiftManager.getCurrentPositionMs()));
545         long progressEndTimeMs = Math.min(mProgramEndTimeMs,
546                     Math.max(mProgramStartTimeMs, mTimeShiftManager.getRecordEndTimeMs()));
547 
548         layoutProgress(mProgressEmptyBefore, mProgramStartTimeMs, progressStartTimeMs);
549         layoutProgress(mProgressWatched, progressStartTimeMs, currentPlayingTimeMs);
550         layoutProgress(mProgressBuffered, currentPlayingTimeMs, progressEndTimeMs);
551     }
552 
layoutProgress(View progress, long progressStartTimeMs, long progressEndTimeMs)553     private void layoutProgress(View progress, long progressStartTimeMs, long progressEndTimeMs) {
554         layoutProgress(progress, Math.max(0,
555                 convertDurationToPixel(progressEndTimeMs - progressStartTimeMs)) + 1);
556     }
557 
layoutProgress(View progress, int width)558     private void layoutProgress(View progress, int width) {
559         ViewGroup.MarginLayoutParams params =
560                 (ViewGroup.MarginLayoutParams) progress.getLayoutParams();
561         params.width = width;
562         progress.setLayoutParams(params);
563     }
564 
updateRecTimeText()565     private void updateRecTimeText() {
566         if (isEnabled()) {
567             mProgramStartTimeText.setVisibility(View.VISIBLE);
568             mProgramStartTimeText.setText(getTimeString(mProgramStartTimeMs));
569             mProgramEndTimeText.setVisibility(View.VISIBLE);
570             mProgramEndTimeText.setText(getTimeString(mProgramEndTimeMs));
571         } else {
572             mProgramStartTimeText.setVisibility(View.GONE);
573             mProgramEndTimeText.setVisibility(View.GONE);
574         }
575     }
576 
updateButtons()577     private void updateButtons() {
578         if (isEnabled()) {
579             mControlBar.setVisibility(View.VISIBLE);
580             mUnavailableMessageText.setVisibility(View.GONE);
581         } else {
582             mControlBar.setVisibility(View.INVISIBLE);
583             mUnavailableMessageText.setVisibility(View.VISIBLE);
584             return;
585         }
586 
587         if (mTimeShiftManager.getPlayStatus() == TimeShiftManager.PLAY_STATUS_PAUSED) {
588             mPlayPauseButton.setImageResId(R.drawable.lb_ic_play);
589             mPlayPauseButton.setEnabled(mTimeShiftManager.isActionEnabled(
590                     TimeShiftManager.TIME_SHIFT_ACTION_ID_PLAY));
591         } else {
592             mPlayPauseButton.setImageResId(R.drawable.lb_ic_pause);
593             mPlayPauseButton.setEnabled(mTimeShiftManager.isActionEnabled(
594                     TimeShiftManager.TIME_SHIFT_ACTION_ID_PAUSE));
595         }
596         mJumpPreviousButton.setEnabled(mTimeShiftManager.isActionEnabled(
597                 TimeShiftManager.TIME_SHIFT_ACTION_ID_JUMP_TO_PREVIOUS));
598         mRewindButton.setEnabled(mTimeShiftManager.isActionEnabled(
599                 TimeShiftManager.TIME_SHIFT_ACTION_ID_REWIND));
600         mFastForwardButton.setEnabled(mTimeShiftManager.isActionEnabled(
601                 TimeShiftManager.TIME_SHIFT_ACTION_ID_FAST_FORWARD));
602         mJumpNextButton.setEnabled(mTimeShiftManager.isActionEnabled(
603                 TimeShiftManager.TIME_SHIFT_ACTION_ID_JUMP_TO_NEXT));
604         mJumpPreviousButton.setVisibility(VISIBLE);
605         mJumpNextButton.setVisibility(VISIBLE);
606         updateButtonMargin();
607 
608         PlayControlsButton button;
609         if (mTimeShiftManager.getPlayDirection() == TimeShiftManager.PLAY_DIRECTION_FORWARD) {
610             mRewindButton.setLabel(null);
611             button = mFastForwardButton;
612         } else {
613             mFastForwardButton.setLabel(null);
614             button = mRewindButton;
615         }
616         if (mTimeShiftManager.getDisplayedPlaySpeed() == TimeShiftManager.PLAY_SPEED_1X) {
617             button.setLabel(null);
618         } else {
619             button.setLabel(getResources().getString(R.string.play_controls_speed,
620                     mTimeShiftManager.getDisplayedPlaySpeed()));
621         }
622     }
623 
updateRecordButton()624     private void updateRecordButton() {
625         if (!(mDvrManager != null
626                 && mDvrManager.isChannelRecordable(mMainActivity.getCurrentChannel()))) {
627             mRecordButton.setVisibility(View.GONE);
628             updateButtonMargin();
629             return;
630         }
631         mRecordButton.setVisibility(View.VISIBLE);
632         updateButtonMargin();
633         if (isCurrentChannelRecording()) {
634             mRecordButton.setImageResId(R.drawable.ic_record_stop);
635         } else {
636             mRecordButton.setImageResId(R.drawable.ic_record_start);
637         }
638     }
639 
updateButtonMargin()640     private void updateButtonMargin() {
641         int numOfVisibleButtons = (mJumpPreviousButton.getVisibility() == View.VISIBLE ? 1 : 0)
642                 + (mRewindButton.getVisibility() == View.VISIBLE ? 1 : 0)
643                 + (mPlayPauseButton.getVisibility() == View.VISIBLE ? 1 : 0)
644                 + (mFastForwardButton.getVisibility() == View.VISIBLE ? 1 : 0)
645                 + (mJumpNextButton.getVisibility() == View.VISIBLE ? 1 : 0)
646                 + (mRecordButton.getVisibility() == View.VISIBLE ? 1 : 0);
647         boolean useCompactLayout = numOfVisibleButtons > NORMAL_WIDTH_MAX_BUTTON_COUNT;
648         if (mUseCompactLayout == useCompactLayout) {
649             return;
650         }
651         mUseCompactLayout = useCompactLayout;
652         int margin = mUseCompactLayout ? mCompactButtonMargin : mNormalButtonMargin;
653         updateButtonMargin(mJumpPreviousButton, margin);
654         updateButtonMargin(mRewindButton, margin);
655         updateButtonMargin(mPlayPauseButton, margin);
656         updateButtonMargin(mFastForwardButton, margin);
657         updateButtonMargin(mJumpNextButton, margin);
658         updateButtonMargin(mRecordButton, margin);
659     }
660 
updateButtonMargin(PlayControlsButton button, int margin)661     private void updateButtonMargin(PlayControlsButton button, int margin) {
662         MarginLayoutParams params = (MarginLayoutParams) button.getLayoutParams();
663         params.setMargins(margin, 0, margin, 0);
664         button.setLayoutParams(params);
665     }
666 
getTimeString(long timeMs)667     private String getTimeString(long timeMs) {
668         return mTimeFormat.format(timeMs);
669     }
670 
convertDurationToPixel(long duration)671     private int convertDurationToPixel(long duration) {
672         if (mProgramEndTimeMs <= mProgramStartTimeMs) {
673             return 0;
674         }
675         return (int) (duration * mTimelineWidth / (mProgramEndTimeMs - mProgramStartTimeMs));
676     }
677 
678     @Override
onDetachedFromWindow()679     public void onDetachedFromWindow() {
680         super.onDetachedFromWindow();
681         if (mDvrDataManager != null) {
682             mDvrDataManager.removeScheduledRecordingListener(mScheduledRecordingListener);
683         }
684     }
685 }
686