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