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