MainActivity.java revision adcef7667a2fb24d830a37dc0356865f7618f5cd
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; 18 19import android.app.Activity; 20import android.app.PendingIntent; 21import android.app.SearchManager; 22import android.content.ActivityNotFoundException; 23import android.content.BroadcastReceiver; 24import android.content.ComponentName; 25import android.content.ContentResolver; 26import android.content.ContentUris; 27import android.content.Context; 28import android.content.Intent; 29import android.content.IntentFilter; 30import android.content.pm.PackageManager; 31import android.content.res.Configuration; 32import android.database.Cursor; 33import android.hardware.display.DisplayManager; 34import android.media.tv.TvContentRating; 35import android.media.tv.TvContract; 36import android.media.tv.TvContract.Channels; 37import android.media.tv.TvInputInfo; 38import android.media.tv.TvInputManager; 39import android.media.tv.TvInputManager.TvInputCallback; 40import android.media.tv.TvTrackInfo; 41import android.media.tv.TvView.OnUnhandledInputEventListener; 42import android.net.Uri; 43import android.os.Build; 44import android.os.Bundle; 45import android.os.Handler; 46import android.os.Message; 47import android.os.PowerManager; 48import android.provider.Settings; 49import android.support.annotation.IntDef; 50import android.support.annotation.NonNull; 51import android.support.annotation.Nullable; 52import android.text.TextUtils; 53import android.util.ArraySet; 54import android.util.Log; 55import android.view.Display; 56import android.view.Gravity; 57import android.view.InputEvent; 58import android.view.KeyEvent; 59import android.view.View; 60import android.view.ViewGroup; 61import android.view.ViewTreeObserver; 62import android.view.Window; 63import android.view.WindowManager; 64import android.view.accessibility.AccessibilityEvent; 65import android.view.accessibility.AccessibilityManager; 66import android.widget.FrameLayout; 67import android.widget.Toast; 68import com.android.tv.analytics.SendChannelStatusRunnable; 69import com.android.tv.analytics.SendConfigInfoRunnable; 70import com.android.tv.analytics.Tracker; 71import com.android.tv.common.BuildConfig; 72import com.android.tv.common.CommonPreferences; 73import com.android.tv.common.SoftPreconditions; 74import com.android.tv.common.TvContentRatingCache; 75import com.android.tv.common.WeakHandler; 76import com.android.tv.common.feature.CommonFeatures; 77import com.android.tv.common.memory.MemoryManageable; 78import com.android.tv.common.ui.setup.OnActionClickListener; 79import com.android.tv.common.util.CommonUtils; 80import com.android.tv.common.util.ContentUriUtils; 81import com.android.tv.common.util.Debug; 82import com.android.tv.common.util.DurationTimer; 83import com.android.tv.common.util.PermissionUtils; 84import com.android.tv.common.util.SystemProperties; 85import com.android.tv.data.ChannelDataManager; 86import com.android.tv.data.ChannelImpl; 87import com.android.tv.data.OnCurrentProgramUpdatedListener; 88import com.android.tv.data.Program; 89import com.android.tv.data.ProgramDataManager; 90import com.android.tv.data.StreamInfo; 91import com.android.tv.data.WatchedHistoryManager; 92import com.android.tv.data.api.Channel; 93import com.android.tv.dialog.HalfSizedDialogFragment; 94import com.android.tv.dialog.PinDialogFragment; 95import com.android.tv.dialog.PinDialogFragment.OnPinCheckedListener; 96import com.android.tv.dialog.SafeDismissDialogFragment; 97import com.android.tv.dvr.DvrManager; 98import com.android.tv.dvr.data.ScheduledRecording; 99import com.android.tv.dvr.recorder.ConflictChecker; 100import com.android.tv.dvr.ui.DvrStopRecordingFragment; 101import com.android.tv.dvr.ui.DvrUiHelper; 102import com.android.tv.menu.Menu; 103import com.android.tv.onboarding.OnboardingActivity; 104import com.android.tv.parental.ContentRatingsManager; 105import com.android.tv.parental.ParentalControlSettings; 106import com.android.tv.perf.EventNames; 107import com.android.tv.perf.PerformanceMonitor; 108import com.android.tv.perf.TimerEvent; 109import com.android.tv.recommendation.ChannelPreviewUpdater; 110import com.android.tv.recommendation.NotificationService; 111import com.android.tv.search.ProgramGuideSearchFragment; 112import com.android.tv.ui.ChannelBannerView; 113import com.android.tv.ui.InputBannerView; 114import com.android.tv.ui.KeypadChannelSwitchView; 115import com.android.tv.ui.SelectInputView; 116import com.android.tv.ui.SelectInputView.OnInputSelectedCallback; 117import com.android.tv.ui.TunableTvView; 118import com.android.tv.ui.TunableTvView.BlockScreenType; 119import com.android.tv.ui.TunableTvView.OnTuneListener; 120import com.android.tv.ui.TvOverlayManager; 121import com.android.tv.ui.TvViewUiManager; 122import com.android.tv.ui.sidepanel.ClosedCaptionFragment; 123import com.android.tv.ui.sidepanel.CustomizeChannelListFragment; 124import com.android.tv.ui.sidepanel.DeveloperOptionFragment; 125import com.android.tv.ui.sidepanel.DisplayModeFragment; 126import com.android.tv.ui.sidepanel.MultiAudioFragment; 127import com.android.tv.ui.sidepanel.SettingsFragment; 128import com.android.tv.ui.sidepanel.SideFragment; 129import com.android.tv.ui.sidepanel.parentalcontrols.ParentalControlsFragment; 130import com.android.tv.util.AsyncDbTask; 131import com.android.tv.util.CaptionSettings; 132import com.android.tv.util.OnboardingUtils; 133import com.android.tv.util.RecurringRunner; 134import com.android.tv.util.SetupUtils; 135import com.android.tv.util.TvInputManagerHelper; 136import com.android.tv.util.TvSettings; 137import com.android.tv.util.TvTrackInfoUtils; 138import com.android.tv.util.Utils; 139import com.android.tv.util.ViewCache; 140import com.android.tv.util.account.AccountHelper; 141import com.android.tv.util.images.ImageCache; 142 143import java.lang.annotation.Retention; 144import java.lang.annotation.RetentionPolicy; 145import java.util.ArrayDeque; 146import java.util.ArrayList; 147import java.util.HashSet; 148import java.util.List; 149import java.util.Objects; 150import java.util.Set; 151import java.util.concurrent.Executor; 152import java.util.concurrent.TimeUnit; 153 154/** The main activity for the Live TV app. */ 155public class MainActivity extends Activity implements OnActionClickListener, OnPinCheckedListener { 156 private static final String TAG = "MainActivity"; 157 private static final boolean DEBUG = false; 158 159 @Retention(RetentionPolicy.SOURCE) 160 @IntDef({ 161 KEY_EVENT_HANDLER_RESULT_PASSTHROUGH, 162 KEY_EVENT_HANDLER_RESULT_NOT_HANDLED, 163 KEY_EVENT_HANDLER_RESULT_HANDLED, 164 KEY_EVENT_HANDLER_RESULT_DISPATCH_TO_OVERLAY 165 }) 166 public @interface KeyHandlerResultType {} 167 168 public static final int KEY_EVENT_HANDLER_RESULT_PASSTHROUGH = 0; 169 public static final int KEY_EVENT_HANDLER_RESULT_NOT_HANDLED = 1; 170 public static final int KEY_EVENT_HANDLER_RESULT_HANDLED = 2; 171 public static final int KEY_EVENT_HANDLER_RESULT_DISPATCH_TO_OVERLAY = 3; 172 173 private static final boolean USE_BACK_KEY_LONG_PRESS = false; 174 175 private static final float FRAME_RATE_FOR_FILM = 23.976f; 176 private static final float FRAME_RATE_EPSILON = 0.1f; 177 178 private static final int PERMISSIONS_REQUEST_READ_TV_LISTINGS = 1; 179 private static final String PERMISSION_READ_TV_LISTINGS = "android.permission.READ_TV_LISTINGS"; 180 181 // Tracker screen names. 182 public static final String SCREEN_NAME = "Main"; 183 private static final String SCREEN_PIP = "PIP"; 184 private static final String SCREEN_BEHIND_NAME = "Behind"; 185 186 private static final float REFRESH_RATE_EPSILON = 0.01f; 187 private static final HashSet<Integer> BLACKLIST_KEYCODE_TO_TIS; 188 // These keys won't be passed to TIS in addition to gamepad buttons. 189 static { 190 BLACKLIST_KEYCODE_TO_TIS = new HashSet<>(); 191 BLACKLIST_KEYCODE_TO_TIS.add(KeyEvent.KEYCODE_TV_INPUT); 192 BLACKLIST_KEYCODE_TO_TIS.add(KeyEvent.KEYCODE_MENU); 193 BLACKLIST_KEYCODE_TO_TIS.add(KeyEvent.KEYCODE_CHANNEL_UP); 194 BLACKLIST_KEYCODE_TO_TIS.add(KeyEvent.KEYCODE_CHANNEL_DOWN); 195 BLACKLIST_KEYCODE_TO_TIS.add(KeyEvent.KEYCODE_VOLUME_UP); 196 BLACKLIST_KEYCODE_TO_TIS.add(KeyEvent.KEYCODE_VOLUME_DOWN); 197 BLACKLIST_KEYCODE_TO_TIS.add(KeyEvent.KEYCODE_VOLUME_MUTE); 198 BLACKLIST_KEYCODE_TO_TIS.add(KeyEvent.KEYCODE_MUTE); 199 BLACKLIST_KEYCODE_TO_TIS.add(KeyEvent.KEYCODE_SEARCH); 200 BLACKLIST_KEYCODE_TO_TIS.add(KeyEvent.KEYCODE_WINDOW); 201 } 202 203 private static final IntentFilter SYSTEM_INTENT_FILTER = new IntentFilter(); 204 205 static { 206 SYSTEM_INTENT_FILTER.addAction(TvInputManager.ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED); 207 SYSTEM_INTENT_FILTER.addAction(Intent.ACTION_SCREEN_OFF); 208 SYSTEM_INTENT_FILTER.addAction(Intent.ACTION_SCREEN_ON); 209 SYSTEM_INTENT_FILTER.addAction(Intent.ACTION_TIME_CHANGED); 210 } 211 212 private static final int REQUEST_CODE_START_SETUP_ACTIVITY = 1; 213 private static final int REQUEST_CODE_NOW_PLAYING = 2; 214 215 private static final String KEY_INIT_CHANNEL_ID = "com.android.tv.init_channel_id"; 216 217 // Change channels with key long press. 218 private static final int CHANNEL_CHANGE_NORMAL_SPEED_DURATION_MS = 3000; 219 private static final int CHANNEL_CHANGE_DELAY_MS_IN_MAX_SPEED = 50; 220 private static final int CHANNEL_CHANGE_DELAY_MS_IN_NORMAL_SPEED = 200; 221 private static final int CHANNEL_CHANGE_INITIAL_DELAY_MILLIS = 500; 222 223 private static final int MSG_CHANNEL_DOWN_PRESSED = 1000; 224 private static final int MSG_CHANNEL_UP_PRESSED = 1001; 225 226 private static final int TVVIEW_SET_MAIN_TIMEOUT_MS = 3000; 227 228 // Lazy initialization. 229 // Delay 1 second in order not to interrupt the first tune. 230 private static final long LAZY_INITIALIZATION_DELAY = TimeUnit.SECONDS.toMillis(1); 231 232 private static final int UNDEFINED_TRACK_INDEX = -1; 233 private static final long START_UP_TIMER_RESET_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(3); 234 235 private AccessibilityManager mAccessibilityManager; 236 private ChannelDataManager mChannelDataManager; 237 private ProgramDataManager mProgramDataManager; 238 private TvInputManagerHelper mTvInputManagerHelper; 239 private ChannelTuner mChannelTuner; 240 private final TvOptionsManager mTvOptionsManager = new TvOptionsManager(this); 241 private TvViewUiManager mTvViewUiManager; 242 private TimeShiftManager mTimeShiftManager; 243 private Tracker mTracker; 244 private final DurationTimer mMainDurationTimer = new DurationTimer(); 245 private final DurationTimer mTuneDurationTimer = new DurationTimer(); 246 private DvrManager mDvrManager; 247 private ConflictChecker mDvrConflictChecker; 248 private SetupUtils mSetupUtils; 249 250 private View mContentView; 251 private TunableTvView mTvView; 252 private Bundle mTuneParams; 253 @Nullable private Uri mInitChannelUri; 254 @Nullable private String mParentInputIdWhenScreenOff; 255 private boolean mScreenOffIntentReceived; 256 private boolean mShowProgramGuide; 257 private boolean mShowSelectInputView; 258 private TvInputInfo mInputToSetUp; 259 private final List<MemoryManageable> mMemoryManageables = new ArrayList<>(); 260 private MediaSessionWrapper mMediaSessionWrapper; 261 private final MyOnTuneListener mOnTuneListener = new MyOnTuneListener(); 262 263 private String mInputIdUnderSetup; 264 private boolean mIsSetupActivityCalledByPopup; 265 private AudioManagerHelper mAudioManagerHelper; 266 private boolean mTunePending; 267 private boolean mDebugNonFullSizeScreen; 268 private boolean mActivityResumed; 269 private boolean mActivityStarted; 270 private boolean mShouldTuneToTunerChannel; 271 private boolean mUseKeycodeBlacklist; 272 private boolean mShowLockedChannelsTemporarily; 273 private boolean mBackKeyPressed; 274 private boolean mNeedShowBackKeyGuide; 275 private boolean mVisibleBehind; 276 private boolean mShowNewSourcesFragment = true; 277 private String mTunerInputId; 278 private boolean mOtherActivityLaunched; 279 private PerformanceMonitor mPerformanceMonitor; 280 281 private boolean mIsInPIPMode; 282 private boolean mIsFilmModeSet; 283 private float mDefaultRefreshRate; 284 285 private TvOverlayManager mOverlayManager; 286 287 // mIsCurrentChannelUnblockedByUser and mWasChannelUnblockedBeforeShrunkenByUser are used for 288 // keeping the channel unblocking status while TV view is shrunken. 289 private boolean mIsCurrentChannelUnblockedByUser; 290 private boolean mWasChannelUnblockedBeforeShrunkenByUser; 291 private Channel mChannelBeforeShrunkenTvView; 292 private boolean mIsCompletingShrunkenTvView; 293 294 private TvContentRating mLastAllowedRatingForCurrentChannel; 295 private TvContentRating mAllowedRatingBeforeShrunken; 296 297 private CaptionSettings mCaptionSettings; 298 // Lazy initialization 299 private boolean mLazyInitialized; 300 301 private static final int MAX_RECENT_CHANNELS = 5; 302 private final ArrayDeque<Long> mRecentChannels = new ArrayDeque<>(MAX_RECENT_CHANNELS); 303 304 private RecurringRunner mSendConfigInfoRecurringRunner; 305 private RecurringRunner mChannelStatusRecurringRunner; 306 307 private final Handler mHandler = new MainActivityHandler(this); 308 private final Set<OnActionClickListener> mOnActionClickListeners = new ArraySet<>(); 309 310 private final BroadcastReceiver mBroadcastReceiver = 311 new BroadcastReceiver() { 312 @Override 313 public void onReceive(Context context, Intent intent) { 314 switch (intent.getAction()) { 315 case Intent.ACTION_SCREEN_OFF: 316 if (DEBUG) Log.d(TAG, "Received ACTION_SCREEN_OFF"); 317 // We need to stop TvView, when the screen is turned off. If not and TIS 318 // uses MediaPlayer, a device may not go to the sleep mode and audio 319 // can be heard, because MediaPlayer keeps playing media by its wake 320 // lock. 321 mScreenOffIntentReceived = true; 322 markCurrentChannelDuringScreenOff(); 323 stopAll(true); 324 break; 325 case Intent.ACTION_SCREEN_ON: 326 if (DEBUG) Log.d(TAG, "Received ACTION_SCREEN_ON"); 327 if (!mActivityResumed && mVisibleBehind) { 328 // ACTION_SCREEN_ON is usually called after onResume. But, if media 329 // is played under launcher with requestVisibleBehind(true), 330 // onResume will not be called. In this case, we need to resume 331 // TvView explicitly. 332 resumeTvIfNeeded(); 333 } 334 break; 335 case TvInputManager.ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED: 336 if (DEBUG) Log.d(TAG, "Received parental control settings change"); 337 applyParentalControlSettings(); 338 checkChannelLockNeeded(mTvView, null); 339 break; 340 case Intent.ACTION_TIME_CHANGED: 341 // Re-tune the current channel to prevent incorrect behavior of 342 // trick-play. 343 // See: b/37393628 344 if (mChannelTuner.getCurrentChannel() != null) { 345 tune(true); 346 } 347 break; 348 default: // fall out 349 } 350 } 351 }; 352 353 private final OnCurrentProgramUpdatedListener mOnCurrentProgramUpdatedListener = 354 new OnCurrentProgramUpdatedListener() { 355 @Override 356 public void onCurrentProgramUpdated(long channelId, Program program) { 357 // Do not update channel banner by this notification 358 // when the time shifting is available. 359 if (mTimeShiftManager.isAvailable()) { 360 return; 361 } 362 Channel channel = mTvView.getCurrentChannel(); 363 if (channel != null && channel.getId() == channelId) { 364 mOverlayManager.updateChannelBannerAndShowIfNeeded( 365 TvOverlayManager.UPDATE_CHANNEL_BANNER_REASON_UPDATE_INFO); 366 mMediaSessionWrapper.update(mTvView.isBlocked(), channel, program); 367 } 368 } 369 }; 370 371 private final ChannelTuner.Listener mChannelTunerListener = 372 new ChannelTuner.Listener() { 373 @Override 374 public void onLoadFinished() { 375 Debug.getTimer(Debug.TAG_START_UP_TIMER) 376 .log("MainActivity.mChannelTunerListener.onLoadFinished"); 377 mSetupUtils.markNewChannelsBrowsable(); 378 if (mActivityResumed) { 379 resumeTvIfNeeded(); 380 } 381 mOverlayManager.onBrowsableChannelsUpdated(); 382 } 383 384 @Override 385 public void onBrowsableChannelListChanged() { 386 mOverlayManager.onBrowsableChannelsUpdated(); 387 } 388 389 @Override 390 public void onCurrentChannelUnavailable(Channel channel) { 391 if (mChannelTuner.moveToAdjacentBrowsableChannel(true)) { 392 tune(true); 393 } else { 394 stopTv("onCurrentChannelUnavailable()", false); 395 } 396 } 397 398 @Override 399 public void onChannelChanged(Channel previousChannel, Channel currentChannel) {} 400 }; 401 402 private final Runnable mRestoreMainViewRunnable = 403 new Runnable() { 404 @Override 405 public void run() { 406 restoreMainTvView(); 407 } 408 }; 409 private ProgramGuideSearchFragment mSearchFragment; 410 411 private final TvInputCallback mTvInputCallback = 412 new TvInputCallback() { 413 @Override 414 public void onInputAdded(String inputId) { 415 if (TvFeatures.TUNER.isEnabled(MainActivity.this) 416 && mTunerInputId.equals(inputId) 417 && CommonPreferences.shouldShowSetupActivity(MainActivity.this)) { 418 Intent intent = 419 TvSingletons.getSingletons(MainActivity.this) 420 .getTunerSetupIntent(MainActivity.this); 421 startActivity(intent); 422 CommonPreferences.setShouldShowSetupActivity(MainActivity.this, false); 423 mSetupUtils.markAsKnownInput(mTunerInputId); 424 } 425 } 426 }; 427 428 private void applyParentalControlSettings() { 429 boolean parentalControlEnabled = 430 mTvInputManagerHelper.getParentalControlSettings().isParentalControlsEnabled(); 431 mTvView.onParentalControlChanged(parentalControlEnabled); 432 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 433 ChannelPreviewUpdater.getInstance(this).updatePreviewDataForChannelsImmediately(); 434 } 435 } 436 437 @Override 438 protected void onCreate(Bundle savedInstanceState) { 439 mAccessibilityManager = 440 (AccessibilityManager) getSystemService(Context.ACCESSIBILITY_SERVICE); 441 TvSingletons tvSingletons = TvSingletons.getSingletons(this); 442 mPerformanceMonitor = tvSingletons.getPerformanceMonitor(); 443 TimerEvent timer = mPerformanceMonitor.startTimer(); 444 DurationTimer startUpDebugTimer = Debug.getTimer(Debug.TAG_START_UP_TIMER); 445 if (!startUpDebugTimer.isStarted() 446 || startUpDebugTimer.getDuration() > START_UP_TIMER_RESET_THRESHOLD_MS) { 447 // TvApplication can start by other reason before MainActivty is launched. 448 // In this case, we restart the timer. 449 startUpDebugTimer.start(); 450 } 451 startUpDebugTimer.log("MainActivity.onCreate"); 452 if (DEBUG) { 453 Log.d(TAG, "onCreate()"); 454 } 455 Starter.start(this); 456 super.onCreate(savedInstanceState); 457 if (!tvSingletons.getTvInputManagerHelper().hasTvInputManager()) { 458 Log.wtf(TAG, "Stopping because device does not have a TvInputManager"); 459 finishAndRemoveTask(); 460 return; 461 } 462 mPerformanceMonitor = tvSingletons.getPerformanceMonitor(); 463 mSetupUtils = tvSingletons.getSetupUtils(); 464 465 TvApplication tvApplication = (TvApplication) getApplication(); 466 mChannelDataManager = tvApplication.getChannelDataManager(); 467 // In API 23, TvContract.isChannelUriForPassthroughInput is hidden. 468 boolean isPassthroughInput = 469 TvContract.isChannelUriForPassthroughInput(getIntent().getData()); 470 boolean tuneToPassthroughInput = 471 Intent.ACTION_VIEW.equals(getIntent().getAction()) && isPassthroughInput; 472 boolean channelLoadedAndNoChannelAvailable = 473 mChannelDataManager.isDbLoadFinished() 474 && mChannelDataManager.getChannelCount() <= 0; 475 if ((OnboardingUtils.isFirstRunWithCurrentVersion(this) 476 || channelLoadedAndNoChannelAvailable) 477 && !tuneToPassthroughInput 478 && !CommonUtils.isRunningInTest()) { 479 startOnboardingActivity(); 480 return; 481 } 482 setContentView(R.layout.activity_tv); 483 mProgramDataManager = tvApplication.getProgramDataManager(); 484 mTvInputManagerHelper = tvApplication.getTvInputManagerHelper(); 485 mTvView = (TunableTvView) findViewById(R.id.main_tunable_tv_view); 486 mTvView.initialize(mProgramDataManager, mTvInputManagerHelper); 487 mTvView.setOnUnhandledInputEventListener( 488 new OnUnhandledInputEventListener() { 489 @Override 490 public boolean onUnhandledInputEvent(InputEvent event) { 491 if (DEBUG) { 492 Log.d(TAG, "onUnhandledInputEvent " + event); 493 } 494 if (isKeyEventBlocked()) { 495 return true; 496 } 497 if (event instanceof KeyEvent) { 498 KeyEvent keyEvent = (KeyEvent) event; 499 if (keyEvent.getAction() == KeyEvent.ACTION_DOWN 500 && keyEvent.isLongPress()) { 501 if (onKeyLongPress(keyEvent.getKeyCode(), keyEvent)) { 502 return true; 503 } 504 } 505 if (keyEvent.getAction() == KeyEvent.ACTION_UP) { 506 return onKeyUp(keyEvent.getKeyCode(), keyEvent); 507 } else if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) { 508 return onKeyDown(keyEvent.getKeyCode(), keyEvent); 509 } 510 } 511 return false; 512 } 513 }); 514 mTvView.setOnTalkBackDpadKeyListener(keycode -> handleUpDownKeys(keycode, null)); 515 long channelId = Utils.getLastWatchedChannelId(this); 516 String inputId = Utils.getLastWatchedTunerInputId(this); 517 if (!isPassthroughInput 518 && inputId != null 519 && channelId != Channel.INVALID_ID) { 520 mTvView.warmUpInput(inputId, TvContract.buildChannelUri(channelId)); 521 } 522 523 tvApplication.getMainActivityWrapper().onMainActivityCreated(this); 524 if (BuildConfig.ENG && SystemProperties.ALLOW_STRICT_MODE.getValue()) { 525 Toast.makeText(this, "Using Strict Mode for eng builds", Toast.LENGTH_SHORT).show(); 526 } 527 mTracker = tvApplication.getTracker(); 528 if (TvFeatures.TUNER.isEnabled(this)) { 529 mTvInputManagerHelper.addCallback(mTvInputCallback); 530 } 531 mTunerInputId = tvSingletons.getEmbeddedTunerInputId(); 532 mProgramDataManager.addOnCurrentProgramUpdatedListener( 533 Channel.INVALID_ID, mOnCurrentProgramUpdatedListener); 534 mProgramDataManager.setPrefetchEnabled(true); 535 mChannelTuner = new ChannelTuner(mChannelDataManager, mTvInputManagerHelper); 536 mChannelTuner.addListener(mChannelTunerListener); 537 mChannelTuner.start(); 538 mMemoryManageables.add(mProgramDataManager); 539 mMemoryManageables.add(ImageCache.getInstance()); 540 mMemoryManageables.add(TvContentRatingCache.getInstance()); 541 if (CommonFeatures.DVR.isEnabled(this)) { 542 mDvrManager = tvApplication.getDvrManager(); 543 } 544 mTimeShiftManager = 545 new TimeShiftManager( 546 this, 547 mTvView, 548 mProgramDataManager, 549 mTracker, 550 new OnCurrentProgramUpdatedListener() { 551 @Override 552 public void onCurrentProgramUpdated(long channelId, Program program) { 553 mMediaSessionWrapper.update( 554 mTvView.isBlocked(), getCurrentChannel(), program); 555 switch (mTimeShiftManager.getLastActionId()) { 556 case TimeShiftManager.TIME_SHIFT_ACTION_ID_REWIND: 557 case TimeShiftManager.TIME_SHIFT_ACTION_ID_FAST_FORWARD: 558 case TimeShiftManager.TIME_SHIFT_ACTION_ID_JUMP_TO_PREVIOUS: 559 case TimeShiftManager.TIME_SHIFT_ACTION_ID_JUMP_TO_NEXT: 560 mOverlayManager.updateChannelBannerAndShowIfNeeded( 561 TvOverlayManager 562 .UPDATE_CHANNEL_BANNER_REASON_FORCE_SHOW); 563 break; 564 case TimeShiftManager.TIME_SHIFT_ACTION_ID_PAUSE: 565 case TimeShiftManager.TIME_SHIFT_ACTION_ID_PLAY: 566 default: 567 mOverlayManager.updateChannelBannerAndShowIfNeeded( 568 TvOverlayManager 569 .UPDATE_CHANNEL_BANNER_REASON_UPDATE_INFO); 570 break; 571 } 572 } 573 }); 574 575 DisplayManager displayManager = (DisplayManager) getSystemService(Context.DISPLAY_SERVICE); 576 Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY); 577 mDefaultRefreshRate = display.getRefreshRate(); 578 579 if (!PermissionUtils.hasAccessWatchedHistory(this)) { 580 WatchedHistoryManager watchedHistoryManager = 581 new WatchedHistoryManager(getApplicationContext()); 582 watchedHistoryManager.start(); 583 mTvView.setWatchedHistoryManager(watchedHistoryManager); 584 } 585 mTvViewUiManager = 586 new TvViewUiManager( 587 this, 588 mTvView, 589 (FrameLayout) findViewById(android.R.id.content), 590 mTvOptionsManager); 591 592 mContentView = findViewById(android.R.id.content); 593 ViewGroup sceneContainer = (ViewGroup) findViewById(R.id.scene_container); 594 ChannelBannerView channelBannerView = 595 (ChannelBannerView) 596 getLayoutInflater().inflate(R.layout.channel_banner, sceneContainer, false); 597 KeypadChannelSwitchView keypadChannelSwitchView = 598 (KeypadChannelSwitchView) 599 getLayoutInflater() 600 .inflate(R.layout.keypad_channel_switch, sceneContainer, false); 601 InputBannerView inputBannerView = 602 (InputBannerView) 603 getLayoutInflater().inflate(R.layout.input_banner, sceneContainer, false); 604 SelectInputView selectInputView = 605 (SelectInputView) 606 getLayoutInflater().inflate(R.layout.select_input, sceneContainer, false); 607 selectInputView.setOnInputSelectedCallback( 608 new OnInputSelectedCallback() { 609 @Override 610 public void onTunerInputSelected() { 611 Channel currentChannel = mChannelTuner.getCurrentChannel(); 612 if (currentChannel != null && !currentChannel.isPassthrough()) { 613 hideOverlays(); 614 } else { 615 tuneToLastWatchedChannelForTunerInput(); 616 } 617 } 618 619 @Override 620 public void onPassthroughInputSelected(@NonNull TvInputInfo input) { 621 Channel currentChannel = mChannelTuner.getCurrentChannel(); 622 String currentInputId = 623 currentChannel == null ? null : currentChannel.getInputId(); 624 if (TextUtils.equals(input.getId(), currentInputId)) { 625 hideOverlays(); 626 } else { 627 tuneToChannel(ChannelImpl.createPassthroughChannel(input.getId())); 628 } 629 } 630 631 private void hideOverlays() { 632 getOverlayManager() 633 .hideOverlays( 634 TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_DIALOG 635 | TvOverlayManager 636 .FLAG_HIDE_OVERLAYS_KEEP_SIDE_PANELS 637 | TvOverlayManager 638 .FLAG_HIDE_OVERLAYS_KEEP_PROGRAM_GUIDE 639 | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_MENU 640 | TvOverlayManager 641 .FLAG_HIDE_OVERLAYS_KEEP_FRAGMENT); 642 } 643 }); 644 mSearchFragment = new ProgramGuideSearchFragment(); 645 mOverlayManager = 646 new TvOverlayManager( 647 this, 648 mChannelTuner, 649 mTvView, 650 mTvOptionsManager, 651 keypadChannelSwitchView, 652 channelBannerView, 653 inputBannerView, 654 selectInputView, 655 sceneContainer, 656 mSearchFragment); 657 mAccessibilityManager.addAccessibilityStateChangeListener(mOverlayManager); 658 659 mAudioManagerHelper = new AudioManagerHelper(this, mTvView); 660 Intent nowPlayingIntent = new Intent(this, MainActivity.class); 661 PendingIntent pendingIntent = 662 PendingIntent.getActivity(this, REQUEST_CODE_NOW_PLAYING, nowPlayingIntent, 0); 663 mMediaSessionWrapper = new MediaSessionWrapper(this, pendingIntent); 664 665 mTvViewUiManager.restoreDisplayMode(false); 666 if (!handleIntent(getIntent())) { 667 finish(); 668 return; 669 } 670 671 mSendConfigInfoRecurringRunner = 672 new RecurringRunner( 673 this, 674 TimeUnit.DAYS.toMillis(1), 675 new SendConfigInfoRunnable(mTracker, mTvInputManagerHelper), 676 null); 677 mSendConfigInfoRecurringRunner.start(); 678 mChannelStatusRecurringRunner = 679 SendChannelStatusRunnable.startChannelStatusRecurringRunner( 680 this, mTracker, mChannelDataManager); 681 682 // To avoid not updating Rating systems when changing language. 683 mTvInputManagerHelper.getContentRatingsManager().update(); 684 if (CommonFeatures.DVR.isEnabled(this) 685 && TvFeatures.SHOW_UPCOMING_CONFLICT_DIALOG.isEnabled(this)) { 686 mDvrConflictChecker = new ConflictChecker(this); 687 } 688 initForTest(); 689 Debug.getTimer(Debug.TAG_START_UP_TIMER).log("MainActivity.onCreate end"); 690 mPerformanceMonitor.stopTimer(timer, EventNames.MAIN_ACTIVITY_ONCREATE); 691 } 692 693 private void startOnboardingActivity() { 694 startActivity(OnboardingActivity.buildIntent(this, getIntent())); 695 finish(); 696 } 697 698 @Override 699 public void onConfigurationChanged(Configuration newConfig) { 700 super.onConfigurationChanged(newConfig); 701 float density = getResources().getDisplayMetrics().density; 702 mTvViewUiManager.onConfigurationChanged( 703 (int) (newConfig.screenWidthDp * density), 704 (int) (newConfig.screenHeightDp * density)); 705 } 706 707 @Override 708 public void onRequestPermissionsResult( 709 int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 710 if (requestCode == PERMISSIONS_REQUEST_READ_TV_LISTINGS) { 711 if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { 712 // Start reload of dependent data 713 mChannelDataManager.reload(); 714 mProgramDataManager.reload(); 715 716 // Restart live channels. 717 Intent intent = getIntent(); 718 finish(); 719 startActivity(intent); 720 } else { 721 Toast.makeText( 722 this, 723 R.string.msg_read_tv_listing_permission_denied, 724 Toast.LENGTH_LONG) 725 .show(); 726 finish(); 727 } 728 } 729 } 730 731 @BlockScreenType 732 private int getDesiredBlockScreenType() { 733 if (!mActivityResumed) { 734 return TunableTvView.BLOCK_SCREEN_TYPE_NO_UI; 735 } 736 if (isUnderShrunkenTvView()) { 737 return TunableTvView.BLOCK_SCREEN_TYPE_SHRUNKEN_TV_VIEW; 738 } 739 if (mOverlayManager.needHideTextOnMainView()) { 740 return TunableTvView.BLOCK_SCREEN_TYPE_NO_UI; 741 } 742 SafeDismissDialogFragment currentDialog = mOverlayManager.getCurrentDialog(); 743 if (currentDialog != null) { 744 // If PIN dialog is shown for unblocking the channel lock or content ratings lock, 745 // keeping the unlocking message is more natural instead of changing it. 746 if (currentDialog instanceof PinDialogFragment) { 747 int type = ((PinDialogFragment) currentDialog).getType(); 748 if (type == PinDialogFragment.PIN_DIALOG_TYPE_UNLOCK_CHANNEL 749 || type == PinDialogFragment.PIN_DIALOG_TYPE_UNLOCK_PROGRAM) { 750 return TunableTvView.BLOCK_SCREEN_TYPE_NORMAL; 751 } 752 } 753 return TunableTvView.BLOCK_SCREEN_TYPE_NO_UI; 754 } 755 if (mOverlayManager.isSetupFragmentActive() 756 || mOverlayManager.isNewSourcesFragmentActive()) { 757 return TunableTvView.BLOCK_SCREEN_TYPE_NO_UI; 758 } 759 return TunableTvView.BLOCK_SCREEN_TYPE_NORMAL; 760 } 761 762 @Override 763 protected void onNewIntent(Intent intent) { 764 if (DEBUG) { 765 Log.d(TAG, "onNewIntent(): " + intent); 766 } 767 if (mOverlayManager == null) { 768 // It's called before onCreate. The intent will be handled at onCreate. b/30725058 769 return; 770 } 771 mOverlayManager.getSideFragmentManager().hideAll(false); 772 if (!handleIntent(intent) && !mActivityStarted) { 773 // If the activity is stopped and not destroyed, finish the activity. 774 // Otherwise, just ignore the intent. 775 finish(); 776 } 777 } 778 779 @Override 780 protected void onStart() { 781 TimerEvent timer = mPerformanceMonitor.startTimer(); 782 if (DEBUG) { 783 Log.d(TAG, "onStart()"); 784 } 785 super.onStart(); 786 mScreenOffIntentReceived = false; 787 mActivityStarted = true; 788 mTracker.sendMainStart(); 789 mMainDurationTimer.start(); 790 791 applyParentalControlSettings(); 792 registerReceiver(mBroadcastReceiver, SYSTEM_INTENT_FILTER); 793 794 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { 795 Intent notificationIntent = new Intent(this, NotificationService.class); 796 notificationIntent.setAction(NotificationService.ACTION_SHOW_RECOMMENDATION); 797 startService(notificationIntent); 798 } 799 TvSingletons singletons = TvSingletons.getSingletons(this); 800 singletons.getTunerInputController().executeNetworkTunerDiscoveryAsyncTask(this); 801 singletons.getEpgFetcher().fetchImmediatelyIfNeeded(); 802 mPerformanceMonitor.stopTimer(timer, EventNames.MAIN_ACTIVITY_ONSTART); 803 } 804 805 @Override 806 protected void onResume() { 807 TimerEvent timer = mPerformanceMonitor.startTimer(); 808 Debug.getTimer(Debug.TAG_START_UP_TIMER).log("MainActivity.onResume start"); 809 if (DEBUG) Log.d(TAG, "onResume()"); 810 super.onResume(); 811 mIsInPIPMode = false; 812 if (!PermissionUtils.hasAccessAllEpg(this) 813 && checkSelfPermission(PERMISSION_READ_TV_LISTINGS) 814 != PackageManager.PERMISSION_GRANTED) { 815 requestPermissions( 816 new String[] {PERMISSION_READ_TV_LISTINGS}, 817 PERMISSIONS_REQUEST_READ_TV_LISTINGS); 818 } 819 mTracker.sendScreenView(SCREEN_NAME); 820 821 SystemProperties.updateSystemProperties(); 822 mNeedShowBackKeyGuide = true; 823 mActivityResumed = true; 824 mShowNewSourcesFragment = true; 825 mOtherActivityLaunched = false; 826 mAudioManagerHelper.requestAudioFocus(); 827 828 if (mTvView.isPlaying()) { 829 // Every time onResume() is called the activity will be assumed to not have requested 830 // visible behind. 831 requestVisibleBehind(true); 832 } 833 Set<String> failedScheduledRecordingInfoSet = 834 Utils.getFailedScheduledRecordingInfoSet(getApplicationContext()); 835 if (Utils.hasRecordingFailedReason( 836 getApplicationContext(), TvInputManager.RECORDING_ERROR_INSUFFICIENT_SPACE) 837 && !failedScheduledRecordingInfoSet.isEmpty()) { 838 runAfterAttachedToWindow( 839 new Runnable() { 840 @Override 841 public void run() { 842 DvrUiHelper.showDvrInsufficientSpaceErrorDialog( 843 MainActivity.this, failedScheduledRecordingInfoSet); 844 } 845 }); 846 } 847 848 if (mChannelTuner.areAllChannelsLoaded()) { 849 mSetupUtils.markNewChannelsBrowsable(); 850 resumeTvIfNeeded(); 851 } 852 mOverlayManager.showMenuWithTimeShiftPauseIfNeeded(); 853 854 // NOTE: The following codes are related to pop up an overlay UI after resume. When 855 // the following code is changed, please modify willShowOverlayUiWhenResume() accordingly. 856 if (mInputToSetUp != null) { 857 startSetupActivity(mInputToSetUp, false); 858 mInputToSetUp = null; 859 } else if (mShowProgramGuide) { 860 mShowProgramGuide = false; 861 // This will delay the start of the animation until after the Live Channel app is 862 // shown. Without this the animation is completed before it is actually visible on 863 // the screen. 864 mHandler.post( 865 new Runnable() { 866 @Override 867 public void run() { 868 mOverlayManager.showProgramGuide(); 869 } 870 }); 871 } else if (mShowSelectInputView) { 872 mShowSelectInputView = false; 873 // mShowSelectInputView is true when the activity is started/resumed because the 874 // TV_INPUT button was pressed in a different app. This will delay the start of 875 // the animation until after the Live Channel app is shown. Without this the 876 // animation is completed before it is actually visible on the screen. 877 mHandler.post( 878 new Runnable() { 879 @Override 880 public void run() { 881 mOverlayManager.showSelectInputView(); 882 } 883 }); 884 } 885 if (mDvrConflictChecker != null) { 886 mDvrConflictChecker.start(); 887 } 888 Debug.getTimer(Debug.TAG_START_UP_TIMER).log("MainActivity.onResume end"); 889 mPerformanceMonitor.stopTimer(timer, EventNames.MAIN_ACTIVITY_ONRESUME); 890 } 891 892 @Override 893 protected void onPause() { 894 if (DEBUG) Log.d(TAG, "onPause()"); 895 if (mDvrConflictChecker != null) { 896 mDvrConflictChecker.stop(); 897 } 898 finishChannelChangeIfNeeded(); 899 mActivityResumed = false; 900 mOverlayManager.hideOverlays(TvOverlayManager.FLAG_HIDE_OVERLAYS_DEFAULT); 901 mTvView.setBlockScreenType(TunableTvView.BLOCK_SCREEN_TYPE_NO_UI); 902 mBackKeyPressed = false; 903 mShowLockedChannelsTemporarily = false; 904 mShouldTuneToTunerChannel = false; 905 if (!mVisibleBehind) { 906 if (mIsInPIPMode) { 907 mTracker.sendScreenView(SCREEN_PIP); 908 } else { 909 mTracker.sendScreenView(""); 910 mAudioManagerHelper.abandonAudioFocus(); 911 mMediaSessionWrapper.setPlaybackState(false); 912 } 913 } else { 914 mTracker.sendScreenView(SCREEN_BEHIND_NAME); 915 } 916 TvSingletons.getSingletons(this).getExperimentLoader().asyncRefreshExperiments(this); 917 super.onPause(); 918 } 919 920 /** Returns true if {@link #onResume} is called and {@link #onPause} is not called yet. */ 921 public boolean isActivityResumed() { 922 return mActivityResumed; 923 } 924 925 /** Returns true if {@link #onStart} is called and {@link #onStop} is not called yet. */ 926 public boolean isActivityStarted() { 927 return mActivityStarted; 928 } 929 930 @Override 931 public boolean requestVisibleBehind(boolean enable) { 932 boolean state = super.requestVisibleBehind(enable); 933 mVisibleBehind = state; 934 return state; 935 } 936 937 @Override 938 public void onPinChecked(boolean checked, int type, String rating) { 939 if (checked) { 940 switch (type) { 941 case PinDialogFragment.PIN_DIALOG_TYPE_UNLOCK_CHANNEL: 942 blockOrUnblockScreen(mTvView, false); 943 mIsCurrentChannelUnblockedByUser = true; 944 break; 945 case PinDialogFragment.PIN_DIALOG_TYPE_UNLOCK_PROGRAM: 946 TvContentRating unblockedRating = TvContentRating.unflattenFromString(rating); 947 mLastAllowedRatingForCurrentChannel = unblockedRating; 948 mTvView.unblockContent(unblockedRating); 949 break; 950 case PinDialogFragment.PIN_DIALOG_TYPE_ENTER_PIN: 951 mOverlayManager 952 .getSideFragmentManager() 953 .show(new ParentalControlsFragment(), false); 954 // fall through. 955 case PinDialogFragment.PIN_DIALOG_TYPE_NEW_PIN: 956 mOverlayManager.getSideFragmentManager().showSidePanel(true); 957 break; 958 default: // fall out 959 } 960 } else if (type == PinDialogFragment.PIN_DIALOG_TYPE_ENTER_PIN) { 961 mOverlayManager.getSideFragmentManager().hideAll(false); 962 } 963 } 964 965 private void resumeTvIfNeeded() { 966 if (DEBUG) Log.d(TAG, "resumeTvIfNeeded()"); 967 if (!mTvView.isPlaying() 968 || mInitChannelUri != null 969 || (mShouldTuneToTunerChannel && mChannelTuner.isCurrentChannelPassthrough())) { 970 if (TvContract.isChannelUriForPassthroughInput(mInitChannelUri)) { 971 // The target input may not be ready yet, especially, just after screen on. 972 String inputId = mInitChannelUri.getPathSegments().get(1); 973 TvInputInfo input = mTvInputManagerHelper.getTvInputInfo(inputId); 974 if (input == null) { 975 input = mTvInputManagerHelper.getTvInputInfo(mParentInputIdWhenScreenOff); 976 if (input == null) { 977 SoftPreconditions.checkState(false, TAG, "Input disappear."); 978 finish(); 979 } else { 980 mInitChannelUri = 981 TvContract.buildChannelUriForPassthroughInput(input.getId()); 982 } 983 } 984 } 985 mParentInputIdWhenScreenOff = null; 986 startTv(mInitChannelUri); 987 mInitChannelUri = null; 988 } 989 // Make sure TV app has the main TV view to handle the case that TvView is used in other 990 // application. 991 restoreMainTvView(); 992 mTvView.setBlockScreenType(getDesiredBlockScreenType()); 993 } 994 995 private void startTv(Uri channelUri) { 996 if (DEBUG) Log.d(TAG, "startTv Uri=" + channelUri); 997 if ((channelUri == null || !TvContract.isChannelUriForPassthroughInput(channelUri)) 998 && mChannelTuner.isCurrentChannelPassthrough()) { 999 // For passthrough TV input, channelUri is always given. If TV app is launched 1000 // by TV app icon in a launcher, channelUri is null. So if passthrough TV input 1001 // is playing, we stop the passthrough TV input. 1002 stopTv(); 1003 } 1004 SoftPreconditions.checkState( 1005 TvContract.isChannelUriForPassthroughInput(channelUri) 1006 || mChannelTuner.areAllChannelsLoaded(), 1007 TAG, 1008 "startTV assumes that ChannelDataManager is already loaded."); 1009 if (mTvView.isPlaying()) { 1010 // TV has already started. 1011 if (channelUri == null || channelUri.equals(mChannelTuner.getCurrentChannelUri())) { 1012 // Simply adjust the volume without tune. 1013 mAudioManagerHelper.setVolumeByAudioFocusStatus(); 1014 return; 1015 } 1016 stopTv(); 1017 } 1018 if (mChannelTuner.getCurrentChannel() != null) { 1019 Log.w(TAG, "The current channel should be reset before"); 1020 mChannelTuner.resetCurrentChannel(); 1021 } 1022 if (channelUri == null) { 1023 // If any initial channel id is not given, remember the last channel the user watched. 1024 long channelId = Utils.getLastWatchedChannelId(this); 1025 if (channelId != Channel.INVALID_ID) { 1026 channelUri = TvContract.buildChannelUri(channelId); 1027 } 1028 } 1029 1030 if (channelUri == null) { 1031 mChannelTuner.moveToChannel(mChannelTuner.findNearestBrowsableChannel(0)); 1032 } else { 1033 if (TvContract.isChannelUriForPassthroughInput(channelUri)) { 1034 ChannelImpl channel = ChannelImpl.createPassthroughChannel(channelUri); 1035 mChannelTuner.moveToChannel(channel); 1036 } else { 1037 long channelId = ContentUris.parseId(channelUri); 1038 Channel channel = mChannelDataManager.getChannel(channelId); 1039 if (channel == null || !mChannelTuner.moveToChannel(channel)) { 1040 mChannelTuner.moveToChannel(mChannelTuner.findNearestBrowsableChannel(0)); 1041 Log.w( 1042 TAG, 1043 "The requested channel (id=" 1044 + channelId 1045 + ") doesn't exist. " 1046 + "The first channel will be tuned to."); 1047 } 1048 } 1049 } 1050 1051 mTvView.start(); 1052 mAudioManagerHelper.setVolumeByAudioFocusStatus(); 1053 tune(true); 1054 } 1055 1056 @Override 1057 protected void onStop() { 1058 if (DEBUG) Log.d(TAG, "onStop()"); 1059 if (mScreenOffIntentReceived) { 1060 mScreenOffIntentReceived = false; 1061 } else { 1062 PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); 1063 if (!powerManager.isInteractive()) { 1064 // We added to check isInteractive as well as SCREEN_OFF intent, because 1065 // calling timing of the intent SCREEN_OFF is not consistent. b/25953633. 1066 // If we verify that checking isInteractive is enough, we can remove the logic 1067 // for SCREEN_OFF intent. 1068 markCurrentChannelDuringScreenOff(); 1069 } 1070 } 1071 mActivityStarted = false; 1072 stopAll(false); 1073 unregisterReceiver(mBroadcastReceiver); 1074 mTracker.sendMainStop(mMainDurationTimer.reset()); 1075 super.onStop(); 1076 } 1077 1078 /** Handles screen off to keep the current channel for next screen on. */ 1079 private void markCurrentChannelDuringScreenOff() { 1080 mInitChannelUri = mChannelTuner.getCurrentChannelUri(); 1081 if (mChannelTuner.isCurrentChannelPassthrough()) { 1082 // When ACTION_SCREEN_OFF is invoked, some CEC devices may be already 1083 // removed. So we need to get the input info from ChannelTuner instead of 1084 // TvInputManagerHelper. 1085 TvInputInfo input = mChannelTuner.getCurrentInputInfo(); 1086 mParentInputIdWhenScreenOff = input.getParentId(); 1087 if (DEBUG) Log.d(TAG, "Parent input: " + mParentInputIdWhenScreenOff); 1088 } 1089 } 1090 1091 private void stopAll(boolean keepVisibleBehind) { 1092 mOverlayManager.hideOverlays(TvOverlayManager.FLAG_HIDE_OVERLAYS_WITHOUT_ANIMATION); 1093 stopTv("stopAll()", keepVisibleBehind); 1094 } 1095 1096 public TvInputManagerHelper getTvInputManagerHelper() { 1097 return mTvInputManagerHelper; 1098 } 1099 1100 /** 1101 * Starts setup activity for the given input {@code input}. 1102 * 1103 * @param calledByPopup If true, startSetupActivity is invoked from the setup fragment. 1104 */ 1105 public void startSetupActivity(TvInputInfo input, boolean calledByPopup) { 1106 Intent intent = CommonUtils.createSetupIntent(input); 1107 if (intent == null) { 1108 Toast.makeText(this, R.string.msg_no_setup_activity, Toast.LENGTH_SHORT).show(); 1109 return; 1110 } 1111 // Even though other app can handle the intent, the setup launched by Live channels 1112 // should go through Live channels SetupPassthroughActivity. 1113 intent.setComponent(new ComponentName(this, SetupPassthroughActivity.class)); 1114 try { 1115 // Now we know that the user intends to set up this input. Grant permission for writing 1116 // EPG data. 1117 SetupUtils.grantEpgPermission(this, input.getServiceInfo().packageName); 1118 1119 mInputIdUnderSetup = input.getId(); 1120 mIsSetupActivityCalledByPopup = calledByPopup; 1121 // Call requestVisibleBehind(false) before starting other activity. 1122 // In Activity.requestVisibleBehind(false), this activity is scheduled to be stopped 1123 // immediately if other activity is about to start. And this activity is scheduled to 1124 // to be stopped again after onPause(). 1125 stopTv("startSetupActivity()", false); 1126 startActivityForResult(intent, REQUEST_CODE_START_SETUP_ACTIVITY); 1127 } catch (ActivityNotFoundException e) { 1128 mInputIdUnderSetup = null; 1129 Toast.makeText( 1130 this, 1131 getString( 1132 R.string.msg_unable_to_start_setup_activity, 1133 input.loadLabel(this)), 1134 Toast.LENGTH_SHORT) 1135 .show(); 1136 return; 1137 } 1138 if (calledByPopup) { 1139 mOverlayManager.hideOverlays( 1140 TvOverlayManager.FLAG_HIDE_OVERLAYS_WITHOUT_ANIMATION 1141 | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_FRAGMENT); 1142 } else { 1143 mOverlayManager.hideOverlays( 1144 TvOverlayManager.FLAG_HIDE_OVERLAYS_WITHOUT_ANIMATION 1145 | TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_SIDE_PANEL_HISTORY); 1146 } 1147 } 1148 1149 public boolean hasCaptioningSettingsActivity() { 1150 return Utils.isIntentAvailable(this, new Intent(Settings.ACTION_CAPTIONING_SETTINGS)); 1151 } 1152 1153 public void startSystemCaptioningSettingsActivity() { 1154 Intent intent = new Intent(Settings.ACTION_CAPTIONING_SETTINGS); 1155 try { 1156 startActivitySafe(intent); 1157 } catch (ActivityNotFoundException e) { 1158 Toast.makeText( 1159 this, 1160 getString(R.string.msg_unable_to_start_system_captioning_settings), 1161 Toast.LENGTH_SHORT) 1162 .show(); 1163 } 1164 } 1165 1166 public ChannelDataManager getChannelDataManager() { 1167 return mChannelDataManager; 1168 } 1169 1170 public ProgramDataManager getProgramDataManager() { 1171 return mProgramDataManager; 1172 } 1173 1174 public TvOptionsManager getTvOptionsManager() { 1175 return mTvOptionsManager; 1176 } 1177 1178 public TvViewUiManager getTvViewUiManager() { 1179 return mTvViewUiManager; 1180 } 1181 1182 public TimeShiftManager getTimeShiftManager() { 1183 return mTimeShiftManager; 1184 } 1185 1186 /** Returns the instance of {@link TvOverlayManager}. */ 1187 public TvOverlayManager getOverlayManager() { 1188 return mOverlayManager; 1189 } 1190 1191 /** Returns the {@link ConflictChecker}. */ 1192 @Nullable 1193 public ConflictChecker getDvrConflictChecker() { 1194 return mDvrConflictChecker; 1195 } 1196 1197 public Channel getCurrentChannel() { 1198 return mChannelTuner.getCurrentChannel(); 1199 } 1200 1201 public long getCurrentChannelId() { 1202 return mChannelTuner.getCurrentChannelId(); 1203 } 1204 1205 /** 1206 * Returns the current program which the user is watching right now. 1207 * 1208 * <p>It might be a live program. If the time shifting is available, it can be a past program, 1209 * too. 1210 */ 1211 public Program getCurrentProgram() { 1212 if (!isChannelChangeKeyDownReceived() && mTimeShiftManager.isAvailable()) { 1213 // We shouldn't get current program from TimeShiftManager during channel tunning 1214 return mTimeShiftManager.getCurrentProgram(); 1215 } 1216 return mProgramDataManager.getCurrentProgram(getCurrentChannelId()); 1217 } 1218 1219 /** 1220 * Returns the current playing time in milliseconds. 1221 * 1222 * <p>If the time shifting is available, the time is the playing position of the program, 1223 * otherwise, the system current time. 1224 */ 1225 public long getCurrentPlayingPosition() { 1226 if (mTimeShiftManager.isAvailable()) { 1227 return mTimeShiftManager.getCurrentPositionMs(); 1228 } 1229 return System.currentTimeMillis(); 1230 } 1231 1232 private Channel getBrowsableChannel() { 1233 Channel curChannel = mChannelTuner.getCurrentChannel(); 1234 if (curChannel != null && curChannel.isBrowsable()) { 1235 return curChannel; 1236 } else { 1237 return mChannelTuner.getAdjacentBrowsableChannel(true); 1238 } 1239 } 1240 1241 /** 1242 * Call {@link Activity#startActivity} in a safe way. 1243 * 1244 * @see LauncherActivity 1245 */ 1246 public void startActivitySafe(Intent intent) { 1247 LauncherActivity.startActivitySafe(this, intent); 1248 } 1249 1250 /** Show settings fragment. */ 1251 public void showSettingsFragment() { 1252 if (!mChannelTuner.areAllChannelsLoaded()) { 1253 // Show ChannelSourcesFragment only if all the channels are loaded. 1254 return; 1255 } 1256 mOverlayManager.getSideFragmentManager().show(new SettingsFragment()); 1257 } 1258 1259 public void showMerchantCollection() { 1260 startActivitySafe(OnboardingUtils.ONLINE_STORE_INTENT); 1261 } 1262 1263 /** 1264 * It is called when shrunken TvView is desired, such as EditChannelFragment and 1265 * ChannelsLockedFragment. 1266 */ 1267 public void startShrunkenTvView( 1268 boolean showLockedChannelsTemporarily, boolean willMainViewBeTunerInput) { 1269 mChannelBeforeShrunkenTvView = mTvView.getCurrentChannel(); 1270 mWasChannelUnblockedBeforeShrunkenByUser = mIsCurrentChannelUnblockedByUser; 1271 mAllowedRatingBeforeShrunken = mLastAllowedRatingForCurrentChannel; 1272 mTvViewUiManager.startShrunkenTvView(); 1273 1274 if (showLockedChannelsTemporarily) { 1275 mShowLockedChannelsTemporarily = true; 1276 checkChannelLockNeeded(mTvView, null); 1277 } 1278 1279 mTvView.setBlockScreenType(getDesiredBlockScreenType()); 1280 } 1281 1282 /** 1283 * It is called when shrunken TvView is no longer desired, such as EditChannelFragment and 1284 * ChannelsLockedFragment. 1285 */ 1286 public void endShrunkenTvView() { 1287 mTvViewUiManager.endShrunkenTvView(); 1288 mIsCompletingShrunkenTvView = true; 1289 1290 Channel returnChannel = mChannelBeforeShrunkenTvView; 1291 if (returnChannel == null 1292 || (!returnChannel.isPassthrough() && !returnChannel.isBrowsable())) { 1293 // Try to tune to the next best channel instead. 1294 returnChannel = getBrowsableChannel(); 1295 } 1296 mShowLockedChannelsTemporarily = false; 1297 1298 // The current channel is mTvView.getCurrentChannel() and need to tune to the returnChannel. 1299 if (!Objects.equals(mTvView.getCurrentChannel(), returnChannel)) { 1300 final Channel channel = returnChannel; 1301 Runnable tuneAction = 1302 new Runnable() { 1303 @Override 1304 public void run() { 1305 tuneToChannel(channel); 1306 if (mChannelBeforeShrunkenTvView == null 1307 || !mChannelBeforeShrunkenTvView.equals(channel)) { 1308 Utils.setLastWatchedChannel(MainActivity.this, channel); 1309 } 1310 mIsCompletingShrunkenTvView = false; 1311 mIsCurrentChannelUnblockedByUser = 1312 mWasChannelUnblockedBeforeShrunkenByUser; 1313 mTvView.setBlockScreenType(getDesiredBlockScreenType()); 1314 } 1315 }; 1316 mTvViewUiManager.fadeOutTvView(tuneAction); 1317 // Will automatically fade-in when video becomes available. 1318 } else { 1319 checkChannelLockNeeded(mTvView, null); 1320 mIsCompletingShrunkenTvView = false; 1321 mIsCurrentChannelUnblockedByUser = mWasChannelUnblockedBeforeShrunkenByUser; 1322 mTvView.setBlockScreenType(getDesiredBlockScreenType()); 1323 } 1324 } 1325 1326 private boolean isUnderShrunkenTvView() { 1327 return mTvViewUiManager.isUnderShrunkenTvView() || mIsCompletingShrunkenTvView; 1328 } 1329 1330 /** 1331 * Returns {@code true} if the tunable tv view is blocked by resource conflict or by parental 1332 * control, otherwise {@code false}. 1333 */ 1334 public boolean isScreenBlockedByResourceConflictOrParentalControl() { 1335 return mTvView.getVideoUnavailableReason() 1336 == TunableTvView.VIDEO_UNAVAILABLE_REASON_NO_RESOURCE 1337 || mTvView.isBlocked(); 1338 } 1339 1340 @Override 1341 public void onActivityResult(int requestCode, int resultCode, Intent data) { 1342 switch (requestCode) { 1343 case REQUEST_CODE_START_SETUP_ACTIVITY: 1344 if (resultCode == RESULT_OK) { 1345 int count = mChannelDataManager.getChannelCountForInput(mInputIdUnderSetup); 1346 String text; 1347 if (count > 0) { 1348 text = 1349 getResources() 1350 .getQuantityString( 1351 R.plurals.msg_channel_added, count, count); 1352 } else { 1353 text = getString(R.string.msg_no_channel_added); 1354 } 1355 Toast.makeText(MainActivity.this, text, Toast.LENGTH_SHORT).show(); 1356 mInputIdUnderSetup = null; 1357 if (mChannelTuner.getCurrentChannel() == null) { 1358 mChannelTuner.moveToAdjacentBrowsableChannel(true); 1359 } 1360 if (mTunePending) { 1361 tune(true); 1362 } 1363 } else { 1364 mInputIdUnderSetup = null; 1365 } 1366 if (!mIsSetupActivityCalledByPopup) { 1367 mOverlayManager.getSideFragmentManager().showSidePanel(false); 1368 } 1369 break; 1370 case REQUEST_CODE_NOW_PLAYING: 1371 // nothing needs to be done. onResume will restore everything. 1372 break; 1373 default: 1374 // do nothing 1375 } 1376 if (data != null) { 1377 String errorMessage = data.getStringExtra(LauncherActivity.ERROR_MESSAGE); 1378 if (!TextUtils.isEmpty(errorMessage)) { 1379 Toast.makeText(MainActivity.this, errorMessage, Toast.LENGTH_SHORT).show(); 1380 } 1381 } 1382 } 1383 1384 @Override 1385 public boolean dispatchKeyEvent(KeyEvent event) { 1386 if (SystemProperties.LOG_KEYEVENT.getValue()) Log.d(TAG, "dispatchKeyEvent(" + event + ")"); 1387 // If an activity is closed on a back key down event, back key down events with none zero 1388 // repeat count or a back key up event can be happened without the first back key down 1389 // event which should be ignored in this activity. 1390 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { 1391 if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { 1392 mBackKeyPressed = true; 1393 } 1394 if (!mBackKeyPressed) { 1395 return true; 1396 } 1397 if (event.getAction() == KeyEvent.ACTION_UP) { 1398 mBackKeyPressed = false; 1399 } 1400 } 1401 1402 // When side panel is closing, it has the focus. 1403 // Keep the focus, but just don't deliver the key events. 1404 if ((mContentView.hasFocusable() && !mOverlayManager.getSideFragmentManager().isHiding()) 1405 || mOverlayManager.getSideFragmentManager().isActive()) { 1406 return super.dispatchKeyEvent(event); 1407 } 1408 if (BLACKLIST_KEYCODE_TO_TIS.contains(event.getKeyCode()) 1409 || KeyEvent.isGamepadButton(event.getKeyCode())) { 1410 // If the event is in blacklisted or gamepad key, do not pass it to session. 1411 // Gamepad keys are blacklisted to support TV UIs and here's the detail. 1412 // If there's a TIS granted RECEIVE_INPUT_EVENT, TIF sends key events to TIS 1413 // and return immediately saying that the event is handled. 1414 // In this case, fallback key will be injected but with FLAG_CANCELED 1415 // while gamepads support DPAD_CENTER and BACK by fallback. 1416 // Since we don't expect that TIS want to handle gamepad buttons now, 1417 // blacklist gamepad buttons and wait for next fallback keys. 1418 // TODO: Need to consider other fallback keys (e.g. ESCAPE) 1419 return super.dispatchKeyEvent(event); 1420 } 1421 return dispatchKeyEventToSession(event) || super.dispatchKeyEvent(event); 1422 } 1423 1424 /** Notifies the key input focus is changed to the TV view. */ 1425 public void updateKeyInputFocus() { 1426 mHandler.post( 1427 new Runnable() { 1428 @Override 1429 public void run() { 1430 mTvView.setBlockScreenType(getDesiredBlockScreenType()); 1431 } 1432 }); 1433 } 1434 1435 // It should be called before onResume. 1436 private boolean handleIntent(Intent intent) { 1437 // Reset the closed caption settings when the activity is 1)created or 2) restarted. 1438 // And do not reset while TvView is playing. 1439 if (!mTvView.isPlaying()) { 1440 mCaptionSettings = new CaptionSettings(this); 1441 } 1442 mShouldTuneToTunerChannel = intent.getBooleanExtra(Utils.EXTRA_KEY_FROM_LAUNCHER, false); 1443 mInitChannelUri = null; 1444 1445 String extraAction = intent.getStringExtra(Utils.EXTRA_KEY_ACTION); 1446 if (!TextUtils.isEmpty(extraAction)) { 1447 if (DEBUG) Log.d(TAG, "Got an extra action: " + extraAction); 1448 if (Utils.EXTRA_ACTION_SHOW_TV_INPUT.equals(extraAction)) { 1449 String lastWatchedChannelUri = Utils.getLastWatchedChannelUri(this); 1450 if (lastWatchedChannelUri != null) { 1451 mInitChannelUri = Uri.parse(lastWatchedChannelUri); 1452 } 1453 mShowSelectInputView = true; 1454 } 1455 } 1456 1457 if (TvInputManager.ACTION_SETUP_INPUTS.equals(intent.getAction())) { 1458 runAfterAttachedToWindow( 1459 new Runnable() { 1460 @Override 1461 public void run() { 1462 mOverlayManager.showSetupFragment(); 1463 } 1464 }); 1465 } else if (Intent.ACTION_VIEW.equals(intent.getAction())) { 1466 Uri uri = intent.getData(); 1467 if (Utils.isProgramsUri(uri)) { 1468 // When the URI points to the programs (directory, not an individual item), go to 1469 // the program guide. The intention here is to respond to 1470 // "content://android.media.tv/program", not 1471 // "content://android.media.tv/program/XXX". 1472 // Later, we might want to add handling of individual programs too. 1473 mShowProgramGuide = true; 1474 return true; 1475 } 1476 // In case the channel is given explicitly, use it. 1477 mInitChannelUri = uri; 1478 if (DEBUG) Log.d(TAG, "ACTION_VIEW with " + mInitChannelUri); 1479 if (Channels.CONTENT_URI.equals(mInitChannelUri)) { 1480 // Tune to default channel. 1481 mInitChannelUri = null; 1482 mShouldTuneToTunerChannel = true; 1483 return true; 1484 } 1485 if ((!Utils.isChannelUriForOneChannel(mInitChannelUri) 1486 && !Utils.isChannelUriForInput(mInitChannelUri))) { 1487 Log.w( 1488 TAG, 1489 "Malformed channel uri " + mInitChannelUri + " tuning to default instead"); 1490 mInitChannelUri = null; 1491 return true; 1492 } 1493 mTuneParams = intent.getExtras(); 1494 String programUriString = intent.getStringExtra(SearchManager.EXTRA_DATA_KEY); 1495 Uri programUriFromIntent = 1496 programUriString == null ? null : Uri.parse(programUriString); 1497 long channelIdFromIntent = ContentUriUtils.safeParseId(mInitChannelUri); 1498 if (programUriFromIntent != null && channelIdFromIntent != Channel.INVALID_ID) { 1499 new AsyncQueryProgramTask( 1500 TvSingletons.getSingletons(this).getDbExecutor(), 1501 getContentResolver(), 1502 programUriFromIntent, 1503 Program.PROJECTION, 1504 null, 1505 null, 1506 null, 1507 channelIdFromIntent) 1508 .executeOnDbThread(); 1509 } 1510 if (mTuneParams == null) { 1511 mTuneParams = new Bundle(); 1512 } 1513 if (Utils.isChannelUriForTunerInput(mInitChannelUri)) { 1514 mTuneParams.putLong(KEY_INIT_CHANNEL_ID, channelIdFromIntent); 1515 } else if (TvContract.isChannelUriForPassthroughInput(mInitChannelUri)) { 1516 // If mInitChannelUri is for a passthrough TV input. 1517 String inputId = mInitChannelUri.getPathSegments().get(1); 1518 TvInputInfo input = mTvInputManagerHelper.getTvInputInfo(inputId); 1519 if (input == null) { 1520 mInitChannelUri = null; 1521 Toast.makeText(this, R.string.msg_no_specific_input, Toast.LENGTH_SHORT).show(); 1522 return false; 1523 } else if (!input.isPassthroughInput()) { 1524 mInitChannelUri = null; 1525 Toast.makeText(this, R.string.msg_not_passthrough_input, Toast.LENGTH_SHORT) 1526 .show(); 1527 return false; 1528 } 1529 } else if (mInitChannelUri != null) { 1530 // Handle the URI built by TvContract.buildChannelsUriForInput(). 1531 String inputId = mInitChannelUri.getQueryParameter("input"); 1532 long channelId = Utils.getLastWatchedChannelIdForInput(this, inputId); 1533 if (channelId == Channel.INVALID_ID) { 1534 String[] projection = {Channels._ID}; 1535 long time = System.currentTimeMillis(); 1536 try (Cursor cursor = 1537 getContentResolver().query(uri, projection, null, null, null)) { 1538 if (cursor != null && cursor.moveToNext()) { 1539 channelId = cursor.getLong(0); 1540 } 1541 } 1542 Debug.getTimer(Debug.TAG_START_UP_TIMER) 1543 .log( 1544 "MainActivity queries DB for " 1545 + "last channel check (" 1546 + (System.currentTimeMillis() - time) 1547 + "ms)"); 1548 } 1549 if (channelId == Channel.INVALID_ID) { 1550 // Couldn't find any channel probably because the input hasn't been set up. 1551 // Try to set it up. 1552 mInitChannelUri = null; 1553 mInputToSetUp = mTvInputManagerHelper.getTvInputInfo(inputId); 1554 } else { 1555 mInitChannelUri = TvContract.buildChannelUri(channelId); 1556 mTuneParams.putLong(KEY_INIT_CHANNEL_ID, channelId); 1557 } 1558 } 1559 } 1560 return true; 1561 } 1562 1563 private class AsyncQueryProgramTask extends AsyncDbTask.AsyncQueryTask<Program> { 1564 private final long mChannelIdFromIntent; 1565 1566 public AsyncQueryProgramTask( 1567 Executor executor, 1568 ContentResolver contentResolver, 1569 Uri uri, 1570 String[] projection, 1571 String selection, 1572 String[] selectionArgs, 1573 String orderBy, 1574 long channelId) { 1575 super(executor, contentResolver, uri, projection, selection, selectionArgs, orderBy); 1576 mChannelIdFromIntent = channelId; 1577 } 1578 1579 @Override 1580 protected Program onQuery(Cursor c) { 1581 Program program = null; 1582 if (c != null && c.moveToNext()) { 1583 program = Program.fromCursor(c); 1584 } 1585 return program; 1586 } 1587 1588 @Override 1589 protected void onPostExecute(Program program) { 1590 if (program == null || program.getStartTimeUtcMillis() <= System.currentTimeMillis()) { 1591 // null or current program 1592 return; 1593 } 1594 Channel channel = mChannelDataManager.getChannel(mChannelIdFromIntent); 1595 if (channel != null) { 1596 ScheduledRecording scheduledRecording = 1597 TvSingletons.getSingletons(MainActivity.this) 1598 .getDvrDataManager() 1599 .getScheduledRecordingForProgramId(program.getId()); 1600 DvrUiHelper.checkStorageStatusAndShowErrorMessage( 1601 MainActivity.this, 1602 channel.getInputId(), 1603 new Runnable() { 1604 @Override 1605 public void run() { 1606 if (CommonFeatures.DVR.isEnabled(MainActivity.this) 1607 && scheduledRecording == null 1608 && mDvrManager.isProgramRecordable(program)) { 1609 DvrUiHelper.requestRecordingFutureProgram( 1610 MainActivity.this, program, false); 1611 } else { 1612 DvrUiHelper.showProgramInfoDialog(MainActivity.this, program); 1613 } 1614 } 1615 }); 1616 } 1617 } 1618 } 1619 1620 private void stopTv() { 1621 stopTv(null, false); 1622 } 1623 1624 private void stopTv(String logForCaller, boolean keepVisibleBehind) { 1625 if (logForCaller != null) { 1626 Log.i(TAG, "stopTv is called at " + logForCaller + "."); 1627 } else { 1628 if (DEBUG) Log.d(TAG, "stopTv()"); 1629 } 1630 if (mTvView.isPlaying()) { 1631 mTvView.stop(); 1632 if (!keepVisibleBehind) { 1633 requestVisibleBehind(false); 1634 } 1635 mAudioManagerHelper.abandonAudioFocus(); 1636 mMediaSessionWrapper.setPlaybackState(false); 1637 } 1638 TvSingletons.getSingletons(this) 1639 .getMainActivityWrapper() 1640 .notifyCurrentChannelChange(this, null); 1641 mChannelTuner.resetCurrentChannel(); 1642 mTunePending = false; 1643 } 1644 1645 private void scheduleRestoreMainTvView() { 1646 mHandler.removeCallbacks(mRestoreMainViewRunnable); 1647 mHandler.postDelayed(mRestoreMainViewRunnable, TVVIEW_SET_MAIN_TIMEOUT_MS); 1648 } 1649 1650 /** Says {@code text} when accessibility is turned on. */ 1651 private void sendAccessibilityText(String text) { 1652 if (mAccessibilityManager.isEnabled()) { 1653 AccessibilityEvent event = AccessibilityEvent.obtain(); 1654 event.setClassName(getClass().getName()); 1655 event.setPackageName(getPackageName()); 1656 event.setEventType(AccessibilityEvent.TYPE_ANNOUNCEMENT); 1657 event.getText().add(text); 1658 mAccessibilityManager.sendAccessibilityEvent(event); 1659 } 1660 } 1661 1662 private void tune(boolean updateChannelBanner) { 1663 if (DEBUG) Log.d(TAG, "tune()"); 1664 mTuneDurationTimer.start(); 1665 1666 lazyInitializeIfNeeded(); 1667 1668 // Prerequisites to be able to tune. 1669 if (mInputIdUnderSetup != null) { 1670 mTunePending = true; 1671 return; 1672 } 1673 mTunePending = false; 1674 final Channel channel = mChannelTuner.getCurrentChannel(); 1675 SoftPreconditions.checkState(channel != null); 1676 if (channel == null) { 1677 return; 1678 } 1679 if (!mChannelTuner.isCurrentChannelPassthrough()) { 1680 if (mTvInputManagerHelper.getTunerTvInputSize() == 0) { 1681 Toast.makeText(this, R.string.msg_no_input, Toast.LENGTH_SHORT).show(); 1682 finish(); 1683 return; 1684 } 1685 1686 if (mSetupUtils.isFirstTune()) { 1687 if (!mChannelTuner.areAllChannelsLoaded()) { 1688 // tune() will be called, once all channels are loaded. 1689 stopTv("tune()", false); 1690 return; 1691 } 1692 if (mChannelDataManager.getChannelCount() > 0) { 1693 mOverlayManager.showIntroDialog(); 1694 } else { 1695 startOnboardingActivity(); 1696 return; 1697 } 1698 } 1699 mShowNewSourcesFragment = false; 1700 if (mChannelTuner.getBrowsableChannelCount() == 0 1701 && mChannelDataManager.getChannelCount() > 0 1702 && !mOverlayManager.getSideFragmentManager().isActive()) { 1703 if (!mChannelTuner.areAllChannelsLoaded()) { 1704 return; 1705 } 1706 if (mTvInputManagerHelper.getTunerTvInputSize() == 1) { 1707 mOverlayManager 1708 .getSideFragmentManager() 1709 .show(new CustomizeChannelListFragment()); 1710 } else { 1711 mOverlayManager.showSetupFragment(); 1712 } 1713 return; 1714 } 1715 if (!CommonUtils.isRunningInTest() 1716 && mShowNewSourcesFragment 1717 && mSetupUtils.hasUnrecognizedInput(mTvInputManagerHelper)) { 1718 // Show new channel sources fragment. 1719 runAfterAttachedToWindow( 1720 new Runnable() { 1721 @Override 1722 public void run() { 1723 mOverlayManager.runAfterOverlaysAreClosed( 1724 new Runnable() { 1725 @Override 1726 public void run() { 1727 mOverlayManager.showNewSourcesFragment(); 1728 } 1729 }); 1730 } 1731 }); 1732 } 1733 mSetupUtils.onTuned(); 1734 if (mTuneParams != null) { 1735 Long initChannelId = mTuneParams.getLong(KEY_INIT_CHANNEL_ID); 1736 if (initChannelId == channel.getId()) { 1737 mTuneParams.remove(KEY_INIT_CHANNEL_ID); 1738 } else { 1739 mTuneParams = null; 1740 } 1741 } 1742 } 1743 1744 mIsCurrentChannelUnblockedByUser = false; 1745 if (!isUnderShrunkenTvView()) { 1746 mLastAllowedRatingForCurrentChannel = null; 1747 } 1748 // For every tune, we need to inform the tuned channel or input to a user, 1749 // if Talkback is turned on. 1750 sendAccessibilityText( 1751 mChannelTuner.isCurrentChannelPassthrough() 1752 ? Utils.loadLabel( 1753 this, mTvInputManagerHelper.getTvInputInfo(channel.getInputId())) 1754 : channel.getDisplayText()); 1755 1756 boolean success = mTvView.tuneTo(channel, mTuneParams, mOnTuneListener); 1757 mOnTuneListener.onTune(channel, isUnderShrunkenTvView()); 1758 1759 mTuneParams = null; 1760 if (!success) { 1761 Toast.makeText(this, R.string.msg_tune_failed, Toast.LENGTH_SHORT).show(); 1762 return; 1763 } 1764 1765 // Explicitly make the TV view main to make the selected input an HDMI-CEC active source. 1766 mTvView.setMain(); 1767 scheduleRestoreMainTvView(); 1768 if (!isUnderShrunkenTvView()) { 1769 if (!channel.isPassthrough()) { 1770 addToRecentChannels(channel.getId()); 1771 } 1772 Utils.setLastWatchedChannel(this, channel); 1773 TvSingletons.getSingletons(this) 1774 .getMainActivityWrapper() 1775 .notifyCurrentChannelChange(this, channel); 1776 } 1777 // We have to provide channel here instead of using TvView's channel, because TvView's 1778 // channel might be null when there's tuner conflict. In that case, TvView will resets 1779 // its current channel onConnectionFailed(). 1780 checkChannelLockNeeded(mTvView, channel); 1781 if (updateChannelBanner) { 1782 mOverlayManager.updateChannelBannerAndShowIfNeeded( 1783 TvOverlayManager.UPDATE_CHANNEL_BANNER_REASON_TUNE); 1784 } 1785 if (mActivityResumed) { 1786 // requestVisibleBehind should be called after onResume() is called. But, when 1787 // launcher is over the TV app and the screen is turned off and on, tune() can 1788 // be called during the pause state by mBroadcastReceiver (Intent.ACTION_SCREEN_ON). 1789 requestVisibleBehind(true); 1790 } 1791 mMediaSessionWrapper.update(mTvView.isBlocked(), getCurrentChannel(), getCurrentProgram()); 1792 } 1793 1794 // Runs the runnable after the activity is attached to window to show the fragment transition 1795 // animation. 1796 // The runnable runs asynchronously to show the animation a little better even when system is 1797 // busy at the moment it is called. 1798 // If the activity is paused shortly, runnable may not be called because all the fragments 1799 // should be closed when the activity is paused. 1800 private void runAfterAttachedToWindow(final Runnable runnable) { 1801 final Runnable runOnlyIfActivityIsResumed = 1802 new Runnable() { 1803 @Override 1804 public void run() { 1805 if (mActivityResumed) { 1806 runnable.run(); 1807 } 1808 } 1809 }; 1810 if (mContentView.isAttachedToWindow()) { 1811 mHandler.post(runOnlyIfActivityIsResumed); 1812 } else { 1813 mContentView 1814 .getViewTreeObserver() 1815 .addOnWindowAttachListener( 1816 new ViewTreeObserver.OnWindowAttachListener() { 1817 @Override 1818 public void onWindowAttached() { 1819 mContentView 1820 .getViewTreeObserver() 1821 .removeOnWindowAttachListener(this); 1822 mHandler.post(runOnlyIfActivityIsResumed); 1823 } 1824 1825 @Override 1826 public void onWindowDetached() {} 1827 }); 1828 } 1829 } 1830 1831 boolean isNowPlayingProgram(Channel channel, Program program) { 1832 return program == null 1833 ? (channel != null 1834 && getCurrentProgram() == null 1835 && channel.equals(getCurrentChannel())) 1836 : program.equals(getCurrentProgram()); 1837 } 1838 1839 private void addToRecentChannels(long channelId) { 1840 if (!mRecentChannels.remove(channelId)) { 1841 if (mRecentChannels.size() >= MAX_RECENT_CHANNELS) { 1842 mRecentChannels.removeLast(); 1843 } 1844 } 1845 mRecentChannels.addFirst(channelId); 1846 mOverlayManager.getMenu().onRecentChannelsChanged(); 1847 } 1848 1849 /** Returns the recently tuned channels. */ 1850 public ArrayDeque<Long> getRecentChannels() { 1851 return mRecentChannels; 1852 } 1853 1854 private void checkChannelLockNeeded(TunableTvView tvView, Channel currentChannel) { 1855 if (currentChannel == null) { 1856 currentChannel = tvView.getCurrentChannel(); 1857 } 1858 if (tvView.isPlaying() && currentChannel != null) { 1859 if (getParentalControlSettings().isParentalControlsEnabled() 1860 && currentChannel.isLocked() 1861 && !mShowLockedChannelsTemporarily 1862 && !(isUnderShrunkenTvView() 1863 && currentChannel.equals(mChannelBeforeShrunkenTvView) 1864 && mWasChannelUnblockedBeforeShrunkenByUser)) { 1865 if (DEBUG) Log.d(TAG, "Channel " + currentChannel.getId() + " is locked"); 1866 blockOrUnblockScreen(tvView, true); 1867 } else { 1868 blockOrUnblockScreen(tvView, false); 1869 } 1870 } 1871 } 1872 1873 private void blockOrUnblockScreen(TunableTvView tvView, boolean blockOrUnblock) { 1874 tvView.blockOrUnblockScreen(blockOrUnblock); 1875 if (tvView == mTvView) { 1876 mOverlayManager.updateChannelBannerAndShowIfNeeded( 1877 TvOverlayManager.UPDATE_CHANNEL_BANNER_REASON_LOCK_OR_UNLOCK); 1878 mMediaSessionWrapper.update(blockOrUnblock, getCurrentChannel(), getCurrentProgram()); 1879 } 1880 } 1881 1882 /** Hide the overlays when tuning to a channel from the menu (e.g. Channels). */ 1883 public void hideOverlaysForTune() { 1884 mOverlayManager.hideOverlays(TvOverlayManager.FLAG_HIDE_OVERLAYS_KEEP_SCENE); 1885 } 1886 1887 public boolean needToKeepSetupScreenWhenHidingOverlay() { 1888 return mInputIdUnderSetup != null && mIsSetupActivityCalledByPopup; 1889 } 1890 1891 // For now, this only takes care of 24fps. 1892 private void applyDisplayRefreshRate(float videoFrameRate) { 1893 boolean is24Fps = Math.abs(videoFrameRate - FRAME_RATE_FOR_FILM) < FRAME_RATE_EPSILON; 1894 if (mIsFilmModeSet && !is24Fps) { 1895 setPreferredRefreshRate(mDefaultRefreshRate); 1896 mIsFilmModeSet = false; 1897 } else if (!mIsFilmModeSet && is24Fps) { 1898 DisplayManager displayManager = 1899 (DisplayManager) getSystemService(Context.DISPLAY_SERVICE); 1900 Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY); 1901 1902 float[] refreshRates = display.getSupportedRefreshRates(); 1903 for (float refreshRate : refreshRates) { 1904 // Be conservative and set only when the display refresh rate supports 24fps. 1905 if (Math.abs(videoFrameRate - refreshRate) < REFRESH_RATE_EPSILON) { 1906 setPreferredRefreshRate(refreshRate); 1907 mIsFilmModeSet = true; 1908 return; 1909 } 1910 } 1911 } 1912 } 1913 1914 private void setPreferredRefreshRate(float refreshRate) { 1915 Window window = getWindow(); 1916 WindowManager.LayoutParams layoutParams = window.getAttributes(); 1917 layoutParams.preferredRefreshRate = refreshRate; 1918 window.setAttributes(layoutParams); 1919 } 1920 1921 private void applyMultiAudio() { 1922 List<TvTrackInfo> tracks = getTracks(TvTrackInfo.TYPE_AUDIO); 1923 if (tracks == null) { 1924 mTvOptionsManager.onMultiAudioChanged(null); 1925 return; 1926 } 1927 1928 String id = TvSettings.getMultiAudioId(this); 1929 String language = TvSettings.getMultiAudioLanguage(this); 1930 int channelCount = TvSettings.getMultiAudioChannelCount(this); 1931 TvTrackInfo bestTrack = 1932 TvTrackInfoUtils.getBestTrackInfo(tracks, id, language, channelCount); 1933 if (bestTrack != null) { 1934 String selectedTrack = getSelectedTrack(TvTrackInfo.TYPE_AUDIO); 1935 if (!bestTrack.getId().equals(selectedTrack)) { 1936 selectTrack(TvTrackInfo.TYPE_AUDIO, bestTrack, UNDEFINED_TRACK_INDEX); 1937 } else { 1938 mTvOptionsManager.onMultiAudioChanged( 1939 Utils.getMultiAudioString(this, bestTrack, false)); 1940 } 1941 return; 1942 } 1943 mTvOptionsManager.onMultiAudioChanged(null); 1944 } 1945 1946 private void applyClosedCaption() { 1947 List<TvTrackInfo> tracks = getTracks(TvTrackInfo.TYPE_SUBTITLE); 1948 if (tracks == null) { 1949 mTvOptionsManager.onClosedCaptionsChanged(null, UNDEFINED_TRACK_INDEX); 1950 return; 1951 } 1952 1953 boolean enabled = mCaptionSettings.isEnabled(); 1954 mTvView.setClosedCaptionEnabled(enabled); 1955 1956 String selectedTrackId = getSelectedTrack(TvTrackInfo.TYPE_SUBTITLE); 1957 TvTrackInfo alternativeTrack = null; 1958 int alternativeTrackIndex = UNDEFINED_TRACK_INDEX; 1959 if (enabled) { 1960 String language = mCaptionSettings.getLanguage(); 1961 String trackId = mCaptionSettings.getTrackId(); 1962 for (int i = 0; i < tracks.size(); i++) { 1963 TvTrackInfo track = tracks.get(i); 1964 if (Utils.isEqualLanguage(track.getLanguage(), language)) { 1965 if (track.getId().equals(trackId)) { 1966 if (!track.getId().equals(selectedTrackId)) { 1967 selectTrack(TvTrackInfo.TYPE_SUBTITLE, track, i); 1968 } else { 1969 // Already selected. Update the option string only. 1970 mTvOptionsManager.onClosedCaptionsChanged(track, i); 1971 } 1972 if (DEBUG) { 1973 Log.d( 1974 TAG, 1975 "Subtitle Track Selected {id=" 1976 + track.getId() 1977 + ", language=" 1978 + track.getLanguage() 1979 + "}"); 1980 } 1981 return; 1982 } else if (alternativeTrack == null) { 1983 alternativeTrack = track; 1984 alternativeTrackIndex = i; 1985 } 1986 } 1987 } 1988 if (alternativeTrack != null) { 1989 if (!alternativeTrack.getId().equals(selectedTrackId)) { 1990 selectTrack(TvTrackInfo.TYPE_SUBTITLE, alternativeTrack, alternativeTrackIndex); 1991 } else { 1992 mTvOptionsManager.onClosedCaptionsChanged( 1993 alternativeTrack, alternativeTrackIndex); 1994 } 1995 if (DEBUG) { 1996 Log.d( 1997 TAG, 1998 "Subtitle Track Selected {id=" 1999 + alternativeTrack.getId() 2000 + ", language=" 2001 + alternativeTrack.getLanguage() 2002 + "}"); 2003 } 2004 return; 2005 } 2006 } 2007 if (selectedTrackId != null) { 2008 selectTrack(TvTrackInfo.TYPE_SUBTITLE, null, UNDEFINED_TRACK_INDEX); 2009 if (DEBUG) Log.d(TAG, "Subtitle Track Unselected"); 2010 return; 2011 } 2012 mTvOptionsManager.onClosedCaptionsChanged(null, UNDEFINED_TRACK_INDEX); 2013 } 2014 2015 public void showProgramGuideSearchFragment() { 2016 getFragmentManager() 2017 .beginTransaction() 2018 .replace(R.id.fragment_container, mSearchFragment) 2019 .addToBackStack(null) 2020 .commit(); 2021 } 2022 2023 @Override 2024 protected void onSaveInstanceState(Bundle outState) { 2025 // Do not save instance state because restoring instance state when TV app died 2026 // unexpectedly can cause some problems like initializing fragments duplicately and 2027 // accessing resource before it is initialized. 2028 } 2029 2030 @Override 2031 protected void onDestroy() { 2032 if (DEBUG) Log.d(TAG, "onDestroy()"); 2033 Debug.getTimer(Debug.TAG_START_UP_TIMER).reset(); 2034 SideFragment.releaseRecycledViewPool(); 2035 ViewCache.getInstance().clear(); 2036 if (mTvView != null) { 2037 mTvView.release(); 2038 } 2039 if (mChannelTuner != null) { 2040 mChannelTuner.removeListener(mChannelTunerListener); 2041 mChannelTuner.stop(); 2042 } 2043 TvApplication application = ((TvApplication) getApplication()); 2044 if (mProgramDataManager != null) { 2045 mProgramDataManager.removeOnCurrentProgramUpdatedListener( 2046 Channel.INVALID_ID, mOnCurrentProgramUpdatedListener); 2047 if (application.getMainActivityWrapper().isCurrent(this)) { 2048 mProgramDataManager.setPrefetchEnabled(false); 2049 } 2050 } 2051 if (mOverlayManager != null) { 2052 mAccessibilityManager.removeAccessibilityStateChangeListener(mOverlayManager); 2053 mOverlayManager.release(); 2054 } 2055 mMemoryManageables.clear(); 2056 if (mMediaSessionWrapper != null) { 2057 mMediaSessionWrapper.release(); 2058 } 2059 if (mAudioManagerHelper != null) { 2060 mAudioManagerHelper.release(); 2061 } 2062 mHandler.removeCallbacksAndMessages(null); 2063 application.getMainActivityWrapper().onMainActivityDestroyed(this); 2064 if (mSendConfigInfoRecurringRunner != null) { 2065 mSendConfigInfoRecurringRunner.stop(); 2066 mSendConfigInfoRecurringRunner = null; 2067 } 2068 if (mChannelStatusRecurringRunner != null) { 2069 mChannelStatusRecurringRunner.stop(); 2070 mChannelStatusRecurringRunner = null; 2071 } 2072 if (mTvInputManagerHelper != null) { 2073 mTvInputManagerHelper.clearTvInputLabels(); 2074 if (TvFeatures.TUNER.isEnabled(this)) { 2075 mTvInputManagerHelper.removeCallback(mTvInputCallback); 2076 } 2077 } 2078 super.onDestroy(); 2079 } 2080 2081 @Override 2082 public boolean onKeyDown(int keyCode, KeyEvent event) { 2083 if (SystemProperties.LOG_KEYEVENT.getValue()) { 2084 Log.d(TAG, "onKeyDown(" + keyCode + ", " + event + ")"); 2085 } 2086 switch (mOverlayManager.onKeyDown(keyCode, event)) { 2087 case KEY_EVENT_HANDLER_RESULT_DISPATCH_TO_OVERLAY: 2088 return super.onKeyDown(keyCode, event); 2089 case KEY_EVENT_HANDLER_RESULT_HANDLED: 2090 return true; 2091 case KEY_EVENT_HANDLER_RESULT_NOT_HANDLED: 2092 return false; 2093 case KEY_EVENT_HANDLER_RESULT_PASSTHROUGH: 2094 default: 2095 // fall through 2096 } 2097 if (mSearchFragment.isVisible()) { 2098 return super.onKeyDown(keyCode, event); 2099 } 2100 if (!mChannelTuner.areAllChannelsLoaded()) { 2101 return false; 2102 } 2103 if (handleUpDownKeys(keyCode, event)) { 2104 return true; 2105 } 2106 return super.onKeyDown(keyCode, event); 2107 } 2108 2109 private boolean handleUpDownKeys(int keyCode, @Nullable KeyEvent event) { 2110 if (!mChannelTuner.isCurrentChannelPassthrough()) { 2111 switch (keyCode) { 2112 case KeyEvent.KEYCODE_CHANNEL_UP: 2113 case KeyEvent.KEYCODE_DPAD_UP: 2114 if ((event == null || event.getRepeatCount() == 0) 2115 && mChannelTuner.getBrowsableChannelCount() > 0) { 2116 // message sending should be done before moving channel, because we use the 2117 // existence of message to decide if users are switching channel. 2118 if (event != null) { 2119 mHandler.sendMessageDelayed( 2120 mHandler.obtainMessage( 2121 MSG_CHANNEL_UP_PRESSED, System.currentTimeMillis()), 2122 CHANNEL_CHANGE_INITIAL_DELAY_MILLIS); 2123 } 2124 moveToAdjacentChannel(true, false); 2125 mTracker.sendChannelUp(); 2126 } 2127 return true; 2128 case KeyEvent.KEYCODE_CHANNEL_DOWN: 2129 case KeyEvent.KEYCODE_DPAD_DOWN: 2130 if ((event == null || event.getRepeatCount() == 0) 2131 && mChannelTuner.getBrowsableChannelCount() > 0) { 2132 // message sending should be done before moving channel, because we use the 2133 // existence of message to decide if users are switching channel. 2134 if (event != null) { 2135 mHandler.sendMessageDelayed( 2136 mHandler.obtainMessage( 2137 MSG_CHANNEL_DOWN_PRESSED, System.currentTimeMillis()), 2138 CHANNEL_CHANGE_INITIAL_DELAY_MILLIS); 2139 } 2140 moveToAdjacentChannel(false, false); 2141 mTracker.sendChannelDown(); 2142 } 2143 return true; 2144 default: // fall out 2145 } 2146 } 2147 return false; 2148 } 2149 2150 @Override 2151 public boolean onKeyUp(int keyCode, KeyEvent event) { 2152 /* 2153 * The following keyboard keys map to these remote keys or "debug actions" 2154 * - -------- 2155 * A KEYCODE_MEDIA_AUDIO_TRACK 2156 * D debug: show debug options 2157 * E updateChannelBannerAndShowIfNeeded 2158 * G debug: refresh cloud epg 2159 * I KEYCODE_TV_INPUT 2160 * O debug: show display mode option 2161 * S KEYCODE_CAPTIONS: select subtitle 2162 * W debug: toggle screen size 2163 * V KEYCODE_MEDIA_RECORD debug: record the current channel for 30 sec 2164 */ 2165 if (SystemProperties.LOG_KEYEVENT.getValue()) { 2166 Log.d(TAG, "onKeyUp(" + keyCode + ", " + event + ")"); 2167 } 2168 // If we are in the middle of channel change, finish it before showing overlays. 2169 finishChannelChangeIfNeeded(); 2170 2171 if (event.getKeyCode() == KeyEvent.KEYCODE_SEARCH) { 2172 // Prevent MainActivity from being closed by onVisibleBehindCanceled() 2173 mOtherActivityLaunched = true; 2174 return false; 2175 } 2176 switch (mOverlayManager.onKeyUp(keyCode, event)) { 2177 case KEY_EVENT_HANDLER_RESULT_DISPATCH_TO_OVERLAY: 2178 return super.onKeyUp(keyCode, event); 2179 case KEY_EVENT_HANDLER_RESULT_HANDLED: 2180 return true; 2181 case KEY_EVENT_HANDLER_RESULT_NOT_HANDLED: 2182 return false; 2183 case KEY_EVENT_HANDLER_RESULT_PASSTHROUGH: 2184 default: 2185 // fall through 2186 } 2187 if (mSearchFragment.isVisible()) { 2188 if (keyCode == KeyEvent.KEYCODE_BACK) { 2189 getFragmentManager().popBackStack(); 2190 return true; 2191 } 2192 return super.onKeyUp(keyCode, event); 2193 } 2194 if (keyCode == KeyEvent.KEYCODE_BACK) { 2195 // When the event is from onUnhandledInputEvent, onBackPressed is not automatically 2196 // called. Therefore, we need to explicitly call onBackPressed(). 2197 onBackPressed(); 2198 return true; 2199 } 2200 2201 if (!mChannelTuner.areAllChannelsLoaded()) { 2202 // Now channel map is under loading. 2203 } else if (mChannelTuner.getBrowsableChannelCount() == 0) { 2204 switch (keyCode) { 2205 case KeyEvent.KEYCODE_CHANNEL_UP: 2206 case KeyEvent.KEYCODE_DPAD_UP: 2207 case KeyEvent.KEYCODE_CHANNEL_DOWN: 2208 case KeyEvent.KEYCODE_DPAD_DOWN: 2209 case KeyEvent.KEYCODE_NUMPAD_ENTER: 2210 case KeyEvent.KEYCODE_DPAD_CENTER: 2211 case KeyEvent.KEYCODE_E: 2212 case KeyEvent.KEYCODE_MENU: 2213 showSettingsFragment(); 2214 return true; 2215 default: // fall out 2216 } 2217 } else { 2218 if (KeypadChannelSwitchView.isChannelNumberKey(keyCode)) { 2219 mOverlayManager.showKeypadChannelSwitch(keyCode); 2220 return true; 2221 } 2222 switch (keyCode) { 2223 case KeyEvent.KEYCODE_DPAD_RIGHT: 2224 if (!mTvView.isVideoOrAudioAvailable() 2225 && mTvView.getVideoUnavailableReason() 2226 == TunableTvView.VIDEO_UNAVAILABLE_REASON_NO_RESOURCE) { 2227 DvrUiHelper.startSchedulesActivityForTuneConflict( 2228 this, mChannelTuner.getCurrentChannel()); 2229 return true; 2230 } 2231 if (!PermissionUtils.hasModifyParentalControls(this)) { 2232 return true; 2233 } 2234 PinDialogFragment dialog = null; 2235 if (mTvView.isScreenBlocked()) { 2236 dialog = 2237 PinDialogFragment.create( 2238 PinDialogFragment.PIN_DIALOG_TYPE_UNLOCK_CHANNEL); 2239 } else if (mTvView.isContentBlocked()) { 2240 dialog = 2241 PinDialogFragment.create( 2242 PinDialogFragment.PIN_DIALOG_TYPE_UNLOCK_PROGRAM, 2243 mTvView.getBlockedContentRating().flattenToString()); 2244 } 2245 if (dialog != null) { 2246 mOverlayManager.showDialogFragment( 2247 PinDialogFragment.DIALOG_TAG, dialog, false); 2248 } 2249 return true; 2250 case KeyEvent.KEYCODE_WINDOW: 2251 enterPictureInPictureMode(); 2252 return true; 2253 case KeyEvent.KEYCODE_ENTER: 2254 case KeyEvent.KEYCODE_NUMPAD_ENTER: 2255 case KeyEvent.KEYCODE_E: 2256 case KeyEvent.KEYCODE_DPAD_CENTER: 2257 case KeyEvent.KEYCODE_MENU: 2258 if (event.isCanceled()) { 2259 // Ignore canceled key. 2260 // Note that if there's a TIS granted RECEIVE_INPUT_EVENT, 2261 // fallback keys not blacklisted will have FLAG_CANCELED. 2262 // See dispatchKeyEvent() for detail. 2263 return true; 2264 } 2265 if (keyCode != KeyEvent.KEYCODE_MENU) { 2266 mOverlayManager.updateChannelBannerAndShowIfNeeded( 2267 TvOverlayManager.UPDATE_CHANNEL_BANNER_REASON_FORCE_SHOW); 2268 } 2269 if (keyCode != KeyEvent.KEYCODE_E) { 2270 mOverlayManager.showMenu(Menu.REASON_NONE); 2271 } 2272 return true; 2273 case KeyEvent.KEYCODE_CHANNEL_UP: 2274 case KeyEvent.KEYCODE_DPAD_UP: 2275 case KeyEvent.KEYCODE_CHANNEL_DOWN: 2276 case KeyEvent.KEYCODE_DPAD_DOWN: 2277 // Channel change is already done in the head of this method. 2278 return true; 2279 case KeyEvent.KEYCODE_S: 2280 if (!SystemProperties.USE_DEBUG_KEYS.getValue()) { 2281 break; 2282 } 2283 // fall through. 2284 case KeyEvent.KEYCODE_CAPTIONS: 2285 mOverlayManager.getSideFragmentManager().show(new ClosedCaptionFragment()); 2286 return true; 2287 case KeyEvent.KEYCODE_A: 2288 if (!SystemProperties.USE_DEBUG_KEYS.getValue()) { 2289 break; 2290 } 2291 // fall through. 2292 case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: 2293 mOverlayManager.getSideFragmentManager().show(new MultiAudioFragment()); 2294 return true; 2295 case KeyEvent.KEYCODE_INFO: 2296 mOverlayManager.showBanner(); 2297 return true; 2298 case KeyEvent.KEYCODE_MEDIA_RECORD: 2299 case KeyEvent.KEYCODE_V: 2300 Channel currentChannel = getCurrentChannel(); 2301 if (currentChannel != null && mDvrManager != null) { 2302 boolean isRecording = 2303 mDvrManager.getCurrentRecording(currentChannel.getId()) != null; 2304 if (!isRecording) { 2305 if (!mDvrManager.isChannelRecordable(currentChannel)) { 2306 Toast.makeText( 2307 this, 2308 R.string.dvr_msg_cannot_record_program, 2309 Toast.LENGTH_SHORT) 2310 .show(); 2311 } else { 2312 Program program = 2313 mProgramDataManager.getCurrentProgram( 2314 currentChannel.getId()); 2315 DvrUiHelper.checkStorageStatusAndShowErrorMessage( 2316 this, 2317 currentChannel.getInputId(), 2318 new Runnable() { 2319 @Override 2320 public void run() { 2321 DvrUiHelper.requestRecordingCurrentProgram( 2322 MainActivity.this, 2323 currentChannel, 2324 program, 2325 false); 2326 } 2327 }); 2328 } 2329 } else { 2330 DvrUiHelper.showStopRecordingDialog( 2331 this, 2332 currentChannel.getId(), 2333 DvrStopRecordingFragment.REASON_USER_STOP, 2334 new HalfSizedDialogFragment.OnActionClickListener() { 2335 @Override 2336 public void onActionClick(long actionId) { 2337 if (actionId == DvrStopRecordingFragment.ACTION_STOP) { 2338 ScheduledRecording currentRecording = 2339 mDvrManager.getCurrentRecording( 2340 currentChannel.getId()); 2341 if (currentRecording != null) { 2342 mDvrManager.stopRecording(currentRecording); 2343 } 2344 } 2345 } 2346 }); 2347 } 2348 } 2349 return true; 2350 default: // fall out 2351 } 2352 } 2353 if (keyCode == KeyEvent.KEYCODE_WINDOW) { 2354 // Consumes the PIP button to prevent entering PIP mode 2355 // in case that TV isn't showing properly (e.g. no browsable channel) 2356 return true; 2357 } 2358 if (SystemProperties.USE_DEBUG_KEYS.getValue() || BuildConfig.ENG) { 2359 switch (keyCode) { 2360 case KeyEvent.KEYCODE_W: 2361 mDebugNonFullSizeScreen = !mDebugNonFullSizeScreen; 2362 if (mDebugNonFullSizeScreen) { 2363 FrameLayout.LayoutParams params = 2364 (FrameLayout.LayoutParams) mTvView.getLayoutParams(); 2365 params.width = 960; 2366 params.height = 540; 2367 params.gravity = Gravity.START; 2368 mTvView.setTvViewLayoutParams(params); 2369 } else { 2370 FrameLayout.LayoutParams params = 2371 (FrameLayout.LayoutParams) mTvView.getLayoutParams(); 2372 params.width = ViewGroup.LayoutParams.MATCH_PARENT; 2373 params.height = ViewGroup.LayoutParams.MATCH_PARENT; 2374 params.gravity = Gravity.CENTER; 2375 mTvView.setTvViewLayoutParams(params); 2376 } 2377 return true; 2378 case KeyEvent.KEYCODE_CTRL_LEFT: 2379 case KeyEvent.KEYCODE_CTRL_RIGHT: 2380 mUseKeycodeBlacklist = !mUseKeycodeBlacklist; 2381 return true; 2382 case KeyEvent.KEYCODE_O: 2383 mOverlayManager.getSideFragmentManager().show(new DisplayModeFragment()); 2384 return true; 2385 case KeyEvent.KEYCODE_D: 2386 mOverlayManager.getSideFragmentManager().show(new DeveloperOptionFragment()); 2387 return true; 2388 default: // fall out 2389 } 2390 } 2391 return super.onKeyUp(keyCode, event); 2392 } 2393 2394 @Override 2395 public boolean onKeyLongPress(int keyCode, KeyEvent event) { 2396 if (SystemProperties.LOG_KEYEVENT.getValue()) Log.d(TAG, "onKeyLongPress(" + event); 2397 if (USE_BACK_KEY_LONG_PRESS) { 2398 // Treat the BACK key long press as the normal press since we changed the behavior in 2399 // onBackPressed(). 2400 if (keyCode == KeyEvent.KEYCODE_BACK) { 2401 // It takes long time for TV app to finish, so stop TV first. 2402 stopAll(false); 2403 super.onBackPressed(); 2404 return true; 2405 } 2406 } 2407 return false; 2408 } 2409 2410 @Override 2411 public void onUserInteraction() { 2412 super.onUserInteraction(); 2413 if (mOverlayManager != null) { 2414 mOverlayManager.onUserInteraction(); 2415 } 2416 } 2417 2418 @Override 2419 public void enterPictureInPictureMode() { 2420 // We need to hide overlay first, before moving the activity to PIP. If not, UI will 2421 // be shown during PIP stack resizing, because UI and its animation is stuck during 2422 // PIP resizing. 2423 mIsInPIPMode = true; 2424 if (mOverlayManager.isOverlayOpened()) { 2425 mOverlayManager.hideOverlays(TvOverlayManager.FLAG_HIDE_OVERLAYS_WITHOUT_ANIMATION); 2426 mHandler.post( 2427 new Runnable() { 2428 @Override 2429 public void run() { 2430 MainActivity.super.enterPictureInPictureMode(); 2431 } 2432 }); 2433 } else { 2434 MainActivity.super.enterPictureInPictureMode(); 2435 } 2436 } 2437 2438 @Override 2439 public void onWindowFocusChanged(boolean hasFocus) { 2440 if (!hasFocus) { 2441 finishChannelChangeIfNeeded(); 2442 } 2443 } 2444 2445 /** 2446 * Returns {@code true} if one of the channel changing keys are pressed and not released yet. 2447 */ 2448 public boolean isChannelChangeKeyDownReceived() { 2449 return mHandler.hasMessages(MSG_CHANNEL_UP_PRESSED) 2450 || mHandler.hasMessages(MSG_CHANNEL_DOWN_PRESSED); 2451 } 2452 2453 private void finishChannelChangeIfNeeded() { 2454 if (!isChannelChangeKeyDownReceived()) { 2455 return; 2456 } 2457 mHandler.removeMessages(MSG_CHANNEL_UP_PRESSED); 2458 mHandler.removeMessages(MSG_CHANNEL_DOWN_PRESSED); 2459 if (mChannelTuner.getBrowsableChannelCount() > 0) { 2460 if (!mTvView.isPlaying()) { 2461 // We expect that mTvView is already played. But, it is sometimes not. 2462 // TODO: we figure out the reason when mTvView is not played. 2463 Log.w(TAG, "TV view isn't played in finishChannelChangeIfNeeded"); 2464 } 2465 tuneToChannel(mChannelTuner.getCurrentChannel()); 2466 } else { 2467 showSettingsFragment(); 2468 } 2469 } 2470 2471 private boolean dispatchKeyEventToSession(final KeyEvent event) { 2472 if (SystemProperties.LOG_KEYEVENT.getValue()) { 2473 Log.d(TAG, "dispatchKeyEventToSession(" + event + ")"); 2474 } 2475 boolean handled = false; 2476 if (mTvView != null) { 2477 handled = mTvView.dispatchKeyEvent(event); 2478 } 2479 if (isKeyEventBlocked()) { 2480 if ((event.getKeyCode() == KeyEvent.KEYCODE_BACK 2481 || event.getKeyCode() == KeyEvent.KEYCODE_BUTTON_B) 2482 && mNeedShowBackKeyGuide) { 2483 // KeyEvent.KEYCODE_BUTTON_B is also used like the back button. 2484 Toast.makeText(this, R.string.msg_back_key_guide, Toast.LENGTH_SHORT).show(); 2485 mNeedShowBackKeyGuide = false; 2486 } 2487 return true; 2488 } 2489 return handled; 2490 } 2491 2492 private boolean isKeyEventBlocked() { 2493 // If the current channel is a passthrough channel, we don't handle the key events in TV 2494 // activity. Instead, the key event will be handled by the passthrough TV input. 2495 return mChannelTuner.isCurrentChannelPassthrough(); 2496 } 2497 2498 private void tuneToLastWatchedChannelForTunerInput() { 2499 if (!mChannelTuner.isCurrentChannelPassthrough()) { 2500 return; 2501 } 2502 stopTv(); 2503 startTv(null); 2504 } 2505 2506 public void tuneToChannel(Channel channel) { 2507 if (channel == null) { 2508 if (mTvView.isPlaying()) { 2509 mTvView.reset(); 2510 } 2511 } else { 2512 if (!mTvView.isPlaying()) { 2513 startTv(channel.getUri()); 2514 } else if (channel.equals(mTvView.getCurrentChannel())) { 2515 mOverlayManager.updateChannelBannerAndShowIfNeeded( 2516 TvOverlayManager.UPDATE_CHANNEL_BANNER_REASON_TUNE); 2517 } else if (channel.equals(mChannelTuner.getCurrentChannel())) { 2518 // Channel banner is already updated in moveToAdjacentChannel 2519 tune(false); 2520 } else if (mChannelTuner.moveToChannel(channel)) { 2521 // Channel banner would be updated inside of tune. 2522 tune(true); 2523 } else { 2524 showSettingsFragment(); 2525 } 2526 } 2527 } 2528 2529 /** 2530 * This method just moves the channel in the channel map and updates the channel banner, but 2531 * doesn't actually tune to the channel. The caller of this method should call {@link #tune} in 2532 * the end. 2533 * 2534 * @param channelUp {@code true} for channel up, and {@code false} for channel down. 2535 * @param fastTuning {@code true} if fast tuning is requested. 2536 */ 2537 private void moveToAdjacentChannel(boolean channelUp, boolean fastTuning) { 2538 if (mChannelTuner.moveToAdjacentBrowsableChannel(channelUp)) { 2539 mOverlayManager.updateChannelBannerAndShowIfNeeded( 2540 fastTuning 2541 ? TvOverlayManager.UPDATE_CHANNEL_BANNER_REASON_TUNE_FAST 2542 : TvOverlayManager.UPDATE_CHANNEL_BANNER_REASON_TUNE); 2543 } 2544 } 2545 2546 /** Set the main TV view which holds HDMI-CEC active source based on the sound mode */ 2547 private void restoreMainTvView() { 2548 mTvView.setMain(); 2549 } 2550 2551 @Override 2552 public void onVisibleBehindCanceled() { 2553 stopTv("onVisibleBehindCanceled()", false); 2554 mTracker.sendScreenView(""); 2555 mAudioManagerHelper.abandonAudioFocus(); 2556 mMediaSessionWrapper.setPlaybackState(false); 2557 mVisibleBehind = false; 2558 if (!mOtherActivityLaunched && Build.VERSION.SDK_INT == Build.VERSION_CODES.M) { 2559 // Workaround: in M, onStop is not called, even though it should be called after 2560 // onVisibleBehindCanceled is called. As a workaround, we call finish(). 2561 finish(); 2562 } 2563 super.onVisibleBehindCanceled(); 2564 } 2565 2566 @Override 2567 public void startActivityForResult(Intent intent, int requestCode) { 2568 mOtherActivityLaunched = true; 2569 if (intent.getCategories() == null 2570 || !intent.getCategories().contains(Intent.CATEGORY_HOME)) { 2571 // Workaround b/30150267 2572 requestVisibleBehind(false); 2573 } 2574 super.startActivityForResult(intent, requestCode); 2575 } 2576 2577 public List<TvTrackInfo> getTracks(int type) { 2578 return mTvView.getTracks(type); 2579 } 2580 2581 public String getSelectedTrack(int type) { 2582 return mTvView.getSelectedTrack(type); 2583 } 2584 2585 private void selectTrack(int type, TvTrackInfo track, int trackIndex) { 2586 mTvView.selectTrack(type, track == null ? null : track.getId()); 2587 if (type == TvTrackInfo.TYPE_AUDIO) { 2588 mTvOptionsManager.onMultiAudioChanged( 2589 track == null ? null : Utils.getMultiAudioString(this, track, false)); 2590 } else if (type == TvTrackInfo.TYPE_SUBTITLE) { 2591 mTvOptionsManager.onClosedCaptionsChanged(track, trackIndex); 2592 } 2593 } 2594 2595 public void selectAudioTrack(String trackId) { 2596 saveMultiAudioSetting(trackId); 2597 applyMultiAudio(); 2598 } 2599 2600 private void saveMultiAudioSetting(String trackId) { 2601 List<TvTrackInfo> tracks = getTracks(TvTrackInfo.TYPE_AUDIO); 2602 if (tracks != null) { 2603 for (TvTrackInfo track : tracks) { 2604 if (track.getId().equals(trackId)) { 2605 TvSettings.setMultiAudioId(this, track.getId()); 2606 TvSettings.setMultiAudioLanguage(this, track.getLanguage()); 2607 TvSettings.setMultiAudioChannelCount(this, track.getAudioChannelCount()); 2608 return; 2609 } 2610 } 2611 } 2612 TvSettings.setMultiAudioId(this, null); 2613 TvSettings.setMultiAudioLanguage(this, null); 2614 TvSettings.setMultiAudioChannelCount(this, 0); 2615 } 2616 2617 public void selectSubtitleTrack(int option, String trackId) { 2618 saveClosedCaptionSetting(option, trackId); 2619 applyClosedCaption(); 2620 } 2621 2622 public void selectSubtitleLanguage(int option, String language, String trackId) { 2623 mCaptionSettings.setEnableOption(option); 2624 mCaptionSettings.setLanguage(language); 2625 mCaptionSettings.setTrackId(trackId); 2626 applyClosedCaption(); 2627 } 2628 2629 private void saveClosedCaptionSetting(int option, String trackId) { 2630 mCaptionSettings.setEnableOption(option); 2631 if (option == CaptionSettings.OPTION_ON) { 2632 List<TvTrackInfo> tracks = getTracks(TvTrackInfo.TYPE_SUBTITLE); 2633 if (tracks != null) { 2634 for (TvTrackInfo track : tracks) { 2635 if (track.getId().equals(trackId)) { 2636 mCaptionSettings.setLanguage(track.getLanguage()); 2637 mCaptionSettings.setTrackId(trackId); 2638 return; 2639 } 2640 } 2641 } 2642 } 2643 } 2644 2645 private void updateAvailabilityToast() { 2646 if (mTvView.isVideoAvailable() 2647 || !Objects.equals( 2648 mTvView.getCurrentChannel(), mChannelTuner.getCurrentChannel())) { 2649 return; 2650 } 2651 2652 switch (mTvView.getVideoUnavailableReason()) { 2653 case TunableTvView.VIDEO_UNAVAILABLE_REASON_NOT_TUNED: 2654 case TunableTvView.VIDEO_UNAVAILABLE_REASON_NO_RESOURCE: 2655 case TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING: 2656 case TvInputManager.VIDEO_UNAVAILABLE_REASON_BUFFERING: 2657 case TvInputManager.VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY: 2658 case TvInputManager.VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL: 2659 return; 2660 case TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN: 2661 default: 2662 Toast.makeText(this, R.string.msg_channel_unavailable_unknown, Toast.LENGTH_SHORT) 2663 .show(); 2664 break; 2665 } 2666 } 2667 2668 /** Returns {@code true} if some overlay UI will be shown when the activity is resumed. */ 2669 public boolean willShowOverlayUiWhenResume() { 2670 return mInputToSetUp != null || mShowProgramGuide || mShowSelectInputView; 2671 } 2672 2673 /** Returns the current parental control settings. */ 2674 public ParentalControlSettings getParentalControlSettings() { 2675 return mTvInputManagerHelper.getParentalControlSettings(); 2676 } 2677 2678 /** Returns a ContentRatingsManager instance. */ 2679 public ContentRatingsManager getContentRatingsManager() { 2680 return mTvInputManagerHelper.getContentRatingsManager(); 2681 } 2682 2683 /** Returns the current captioning settings. */ 2684 public CaptionSettings getCaptionSettings() { 2685 return mCaptionSettings; 2686 } 2687 2688 /** Adds the {@link OnActionClickListener}. */ 2689 public void addOnActionClickListener(OnActionClickListener listener) { 2690 mOnActionClickListeners.add(listener); 2691 } 2692 2693 /** Removes the {@link OnActionClickListener}. */ 2694 public void removeOnActionClickListener(OnActionClickListener listener) { 2695 mOnActionClickListeners.remove(listener); 2696 } 2697 2698 @Override 2699 public boolean onActionClick(String category, int actionId, Bundle params) { 2700 // There should be only one action listener per an action. 2701 for (OnActionClickListener l : mOnActionClickListeners) { 2702 if (l.onActionClick(category, actionId, params)) { 2703 return true; 2704 } 2705 } 2706 return false; 2707 } 2708 2709 // Initialize TV app for test. The setup process should be finished before the Live TV app is 2710 // started. We only enable all the channels here. 2711 private void initForTest() { 2712 if (!CommonUtils.isRunningInTest()) { 2713 return; 2714 } 2715 2716 Utils.enableAllChannels(this); 2717 } 2718 2719 // Lazy initialization 2720 private void lazyInitializeIfNeeded() { 2721 // Already initialized. 2722 if (mLazyInitialized) { 2723 return; 2724 } 2725 mLazyInitialized = true; 2726 // Running initialization. 2727 mHandler.postDelayed( 2728 new Runnable() { 2729 @Override 2730 public void run() { 2731 if (mActivityStarted) { 2732 initAnimations(); 2733 initSideFragments(); 2734 initMenuItemViews(); 2735 } 2736 } 2737 }, 2738 LAZY_INITIALIZATION_DELAY); 2739 } 2740 2741 private void initAnimations() { 2742 mTvViewUiManager.initAnimatorIfNeeded(); 2743 mOverlayManager.initAnimatorIfNeeded(); 2744 } 2745 2746 private void initSideFragments() { 2747 SideFragment.preloadItemViews(this); 2748 } 2749 2750 private void initMenuItemViews() { 2751 mOverlayManager.getMenu().preloadItemViews(); 2752 } 2753 2754 @Override 2755 public void onTrimMemory(int level) { 2756 super.onTrimMemory(level); 2757 for (MemoryManageable memoryManageable : mMemoryManageables) { 2758 memoryManageable.performTrimMemory(level); 2759 } 2760 } 2761 2762 private static class MainActivityHandler extends WeakHandler<MainActivity> { 2763 MainActivityHandler(MainActivity mainActivity) { 2764 super(mainActivity); 2765 } 2766 2767 @Override 2768 protected void handleMessage(Message msg, @NonNull MainActivity mainActivity) { 2769 switch (msg.what) { 2770 case MSG_CHANNEL_DOWN_PRESSED: 2771 long startTime = (Long) msg.obj; 2772 // message re-sending should be done before moving channel, because we use the 2773 // existence of message to decide if users are switching channel. 2774 sendMessageDelayed(Message.obtain(msg), getDelay(startTime)); 2775 mainActivity.moveToAdjacentChannel(false, true); 2776 break; 2777 case MSG_CHANNEL_UP_PRESSED: 2778 startTime = (Long) msg.obj; 2779 // message re-sending should be done before moving channel, because we use the 2780 // existence of message to decide if users are switching channel. 2781 sendMessageDelayed(Message.obtain(msg), getDelay(startTime)); 2782 mainActivity.moveToAdjacentChannel(true, true); 2783 break; 2784 default: // fall out 2785 } 2786 } 2787 2788 private long getDelay(long startTime) { 2789 if (System.currentTimeMillis() - startTime > CHANNEL_CHANGE_NORMAL_SPEED_DURATION_MS) { 2790 return CHANNEL_CHANGE_DELAY_MS_IN_MAX_SPEED; 2791 } 2792 return CHANNEL_CHANGE_DELAY_MS_IN_NORMAL_SPEED; 2793 } 2794 } 2795 2796 private class MyOnTuneListener implements OnTuneListener { 2797 boolean mUnlockAllowedRatingBeforeShrunken = true; 2798 boolean mWasUnderShrunkenTvView; 2799 Channel mChannel; 2800 2801 private void onTune(Channel channel, boolean wasUnderShrukenTvView) { 2802 Debug.getTimer(Debug.TAG_START_UP_TIMER).log("MainActivity.MyOnTuneListener.onTune"); 2803 mChannel = channel; 2804 mWasUnderShrunkenTvView = wasUnderShrukenTvView; 2805 } 2806 2807 @Override 2808 public void onUnexpectedStop(Channel channel) { 2809 stopTv(); 2810 startTv(null); 2811 } 2812 2813 @Override 2814 public void onTuneFailed(Channel channel) { 2815 Log.w(TAG, "onTuneFailed(" + channel + ")"); 2816 if (mTvView.isFadedOut()) { 2817 mTvView.removeFadeEffect(); 2818 } 2819 Toast.makeText( 2820 MainActivity.this, 2821 R.string.msg_channel_unavailable_unknown, 2822 Toast.LENGTH_SHORT) 2823 .show(); 2824 } 2825 2826 @Override 2827 public void onStreamInfoChanged(StreamInfo info) { 2828 if (info.isVideoAvailable() && mTuneDurationTimer.isRunning()) { 2829 mTracker.sendChannelTuneTime(info.getCurrentChannel(), mTuneDurationTimer.reset()); 2830 } 2831 if (info.isVideoOrAudioAvailable() && mChannel.equals(getCurrentChannel())) { 2832 mOverlayManager.updateChannelBannerAndShowIfNeeded( 2833 TvOverlayManager.UPDATE_CHANNEL_BANNER_REASON_UPDATE_STREAM_INFO); 2834 } 2835 applyDisplayRefreshRate(info.getVideoFrameRate()); 2836 mTvViewUiManager.updateTvAspectRatio(); 2837 applyMultiAudio(); 2838 applyClosedCaption(); 2839 mOverlayManager.getMenu().onStreamInfoChanged(); 2840 if (mTvView.isVideoAvailable()) { 2841 mTvViewUiManager.fadeInTvView(); 2842 } 2843 if (!mTvView.isContentBlocked() && !mTvView.isScreenBlocked()) { 2844 updateAvailabilityToast(); 2845 } 2846 mHandler.removeCallbacks(mRestoreMainViewRunnable); 2847 restoreMainTvView(); 2848 } 2849 2850 @Override 2851 public void onChannelRetuned(Uri channel) { 2852 if (channel == null) { 2853 return; 2854 } 2855 Channel currentChannel = 2856 mChannelDataManager.getChannel(ContentUriUtils.safeParseId(channel)); 2857 if (currentChannel == null) { 2858 Log.e( 2859 TAG, 2860 "onChannelRetuned is called but can't find a channel with the URI " 2861 + channel); 2862 return; 2863 } 2864 if (isChannelChangeKeyDownReceived()) { 2865 // Ignore this message if the user is changing the channel. 2866 return; 2867 } 2868 mChannelTuner.setCurrentChannel(currentChannel); 2869 mTvView.setCurrentChannel(currentChannel); 2870 mOverlayManager.updateChannelBannerAndShowIfNeeded( 2871 TvOverlayManager.UPDATE_CHANNEL_BANNER_REASON_TUNE); 2872 } 2873 2874 @Override 2875 public void onContentBlocked() { 2876 Debug.getTimer(Debug.TAG_START_UP_TIMER) 2877 .log("MainActivity.MyOnTuneListener.onContentBlocked removes timer"); 2878 Debug.removeTimer(Debug.TAG_START_UP_TIMER); 2879 mTuneDurationTimer.reset(); 2880 TvContentRating rating = mTvView.getBlockedContentRating(); 2881 // When tuneTo was called while TV view was shrunken, if the channel id is the same 2882 // with the channel watched before shrunken, we allow the rating which was allowed 2883 // before. 2884 if (mWasUnderShrunkenTvView 2885 && mUnlockAllowedRatingBeforeShrunken 2886 && mChannelBeforeShrunkenTvView.equals(mChannel) 2887 && rating.equals(mAllowedRatingBeforeShrunken)) { 2888 mUnlockAllowedRatingBeforeShrunken = isUnderShrunkenTvView(); 2889 mTvView.unblockContent(rating); 2890 } 2891 mOverlayManager.setBlockingContentRating(rating); 2892 mTvViewUiManager.fadeInTvView(); 2893 mMediaSessionWrapper.update(true, getCurrentChannel(), getCurrentProgram()); 2894 } 2895 2896 @Override 2897 public void onContentAllowed() { 2898 if (!isUnderShrunkenTvView()) { 2899 mUnlockAllowedRatingBeforeShrunken = false; 2900 } 2901 mOverlayManager.setBlockingContentRating(null); 2902 mMediaSessionWrapper.update(false, getCurrentChannel(), getCurrentProgram()); 2903 } 2904 } 2905} 2906