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