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