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