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