TvApplication.java revision a1589bd48e05abbee991e0cdd27fa402a5dc5001
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.annotation.TargetApi; 20import android.app.Activity; 21import android.content.ComponentName; 22import android.content.Context; 23import android.content.Intent; 24import android.content.SharedPreferences; 25import android.content.pm.PackageInfo; 26import android.content.pm.PackageManager; 27import android.media.tv.TvContract; 28import android.media.tv.TvInputInfo; 29import android.media.tv.TvInputManager; 30import android.media.tv.TvInputManager.TvInputCallback; 31import android.os.Build; 32import android.os.Bundle; 33import android.support.annotation.Nullable; 34import android.text.TextUtils; 35import android.util.Log; 36import android.view.KeyEvent; 37import com.android.tv.common.BaseApplication; 38import com.android.tv.common.feature.CommonFeatures; 39import com.android.tv.common.recording.RecordingStorageStatusManager; 40import com.android.tv.common.ui.setup.animation.SetupAnimationHelper; 41import com.android.tv.common.util.Clock; 42import com.android.tv.common.util.Debug; 43import com.android.tv.common.util.SharedPreferencesUtils; 44import com.android.tv.data.ChannelDataManager; 45import com.android.tv.data.PreviewDataManager; 46import com.android.tv.data.ProgramDataManager; 47import com.android.tv.data.epg.EpgFetcher; 48import com.android.tv.data.epg.EpgFetcherImpl; 49import com.android.tv.dvr.DvrDataManager; 50import com.android.tv.dvr.DvrDataManagerImpl; 51import com.android.tv.dvr.DvrManager; 52import com.android.tv.dvr.DvrScheduleManager; 53import com.android.tv.dvr.DvrStorageStatusManager; 54import com.android.tv.dvr.DvrWatchedPositionManager; 55import com.android.tv.dvr.recorder.RecordingScheduler; 56import com.android.tv.perf.PerformanceMonitor; 57import com.android.tv.recommendation.ChannelPreviewUpdater; 58import com.android.tv.recommendation.RecordedProgramPreviewUpdater; 59import com.android.tv.tuner.TunerInputController; 60import com.android.tv.tuner.util.TunerInputInfoUtils; 61import com.android.tv.util.SetupUtils; 62import com.android.tv.util.TvInputManagerHelper; 63import com.android.tv.util.Utils; 64import java.util.List; 65 66/** 67 * Live TV application. 68 * 69 * <p>This includes all the Google specific hooks. 70 */ 71public abstract class TvApplication extends BaseApplication implements TvSingletons, Starter { 72 private static final String TAG = "TvApplication"; 73 private static final boolean DEBUG = false; 74 75 /** Namespace for LiveChannels configs. LiveChannels configs are kept in piper. */ 76 public static final String CONFIGNS_P4 = "configns:p4"; 77 78 /** 79 * Broadcast Action: The user has updated LC to a new version that supports tuner input. {@link 80 * TunerInputController} will receive this intent to check the existence of tuner input when the 81 * new version is first launched. 82 */ 83 public static final String ACTION_APPLICATION_FIRST_LAUNCHED = 84 " com.android.tv.action.APPLICATION_FIRST_LAUNCHED"; 85 86 private static final String PREFERENCE_IS_FIRST_LAUNCH = "is_first_launch"; 87 88 private String mVersionName = ""; 89 90 private final MainActivityWrapper mMainActivityWrapper = new MainActivityWrapper(); 91 92 private SelectInputActivity mSelectInputActivity; 93 private ChannelDataManager mChannelDataManager; 94 private volatile ProgramDataManager mProgramDataManager; 95 private PreviewDataManager mPreviewDataManager; 96 private DvrManager mDvrManager; 97 private DvrScheduleManager mDvrScheduleManager; 98 private DvrDataManager mDvrDataManager; 99 private DvrWatchedPositionManager mDvrWatchedPositionManager; 100 private RecordingScheduler mRecordingScheduler; 101 private RecordingStorageStatusManager mDvrStorageStatusManager; 102 @Nullable private InputSessionManager mInputSessionManager; 103 // STOP-SHIP: Remove this variable when Tuner Process is split to another application. 104 // When this variable is null, we don't know in which process TvApplication runs. 105 private Boolean mRunningInMainProcess; 106 private PerformanceMonitor mPerformanceMonitor; 107 private TvInputManagerHelper mTvInputManagerHelper; 108 private boolean mStarted; 109 private EpgFetcher mEpgFetcher; 110 private TunerInputController mTunerInputController; 111 112 @Override 113 public void onCreate() { 114 super.onCreate(); 115 SharedPreferencesUtils.initialize( 116 this, 117 new Runnable() { 118 @Override 119 public void run() { 120 if (mRunningInMainProcess != null && mRunningInMainProcess) { 121 checkTunerServiceOnFirstLaunch(); 122 } 123 } 124 }); 125 try { 126 PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0); 127 mVersionName = pInfo.versionName; 128 } catch (PackageManager.NameNotFoundException e) { 129 Log.w(TAG, "Unable to find package '" + getPackageName() + "'.", e); 130 mVersionName = ""; 131 } 132 Log.i(TAG, "Starting Live TV " + getVersionName()); 133 134 // In SetupFragment, transitions are set in the constructor. Because the fragment can be 135 // created in Activity.onCreate() by the framework, SetupAnimationHelper should be 136 // initialized here before Activity.onCreate() is called. 137 mEpgFetcher = EpgFetcherImpl.create(this); 138 SetupAnimationHelper.initialize(this); 139 getTvInputManagerHelper(); 140 141 Log.i(TAG, "Started Live TV " + mVersionName); 142 Debug.getTimer(Debug.TAG_START_UP_TIMER).log("finish TvApplication.onCreate"); 143 } 144 145 /** Initializes application. It is a noop if called twice. */ 146 @Override 147 public void start() { 148 if (mStarted) { 149 return; 150 } 151 mStarted = true; 152 mRunningInMainProcess = true; 153 Debug.getTimer(Debug.TAG_START_UP_TIMER).log("start TvApplication.start"); 154 if (mRunningInMainProcess) { 155 getTvInputManagerHelper() 156 .addCallback( 157 new TvInputCallback() { 158 @Override 159 public void onInputAdded(String inputId) { 160 if (TvFeatures.TUNER.isEnabled(TvApplication.this) 161 && TextUtils.equals( 162 inputId, getEmbeddedTunerInputId())) { 163 TunerInputInfoUtils.updateTunerInputInfo( 164 TvApplication.this); 165 } 166 handleInputCountChanged(); 167 } 168 169 @Override 170 public void onInputRemoved(String inputId) { 171 handleInputCountChanged(); 172 } 173 }); 174 if (TvFeatures.TUNER.isEnabled(this)) { 175 // If the tuner input service is added before the app is started, we need to 176 // handle it here. 177 TunerInputInfoUtils.updateTunerInputInfo(TvApplication.this); 178 } 179 if (CommonFeatures.DVR.isEnabled(this)) { 180 mDvrScheduleManager = new DvrScheduleManager(this); 181 mDvrManager = new DvrManager(this); 182 mRecordingScheduler = RecordingScheduler.createScheduler(this); 183 } 184 mEpgFetcher.startRoutineService(); 185 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 186 ChannelPreviewUpdater.getInstance(this).startRoutineService(); 187 RecordedProgramPreviewUpdater.getInstance(this) 188 .updatePreviewDataForRecordedPrograms(); 189 } 190 } 191 Debug.getTimer(Debug.TAG_START_UP_TIMER).log("finish TvApplication.start"); 192 } 193 194 private void checkTunerServiceOnFirstLaunch() { 195 SharedPreferences sharedPreferences = 196 this.getSharedPreferences( 197 SharedPreferencesUtils.SHARED_PREF_FEATURES, Context.MODE_PRIVATE); 198 boolean isFirstLaunch = sharedPreferences.getBoolean(PREFERENCE_IS_FIRST_LAUNCH, true); 199 if (isFirstLaunch) { 200 if (DEBUG) Log.d(TAG, "Congratulations, it's the first launch!"); 201 getTunerInputController() 202 .onCheckingUsbTunerStatus(this, ACTION_APPLICATION_FIRST_LAUNCHED); 203 SharedPreferences.Editor editor = sharedPreferences.edit(); 204 editor.putBoolean(PREFERENCE_IS_FIRST_LAUNCH, false); 205 editor.apply(); 206 } 207 } 208 209 @Override 210 public EpgFetcher getEpgFetcher() { 211 return mEpgFetcher; 212 } 213 214 @Override 215 public synchronized SetupUtils getSetupUtils() { 216 return SetupUtils.createForTvSingletons(this); 217 } 218 219 /** Returns the {@link DvrManager}. */ 220 @Override 221 public DvrManager getDvrManager() { 222 return mDvrManager; 223 } 224 225 /** Returns the {@link DvrScheduleManager}. */ 226 @Override 227 public DvrScheduleManager getDvrScheduleManager() { 228 return mDvrScheduleManager; 229 } 230 231 /** Returns the {@link RecordingScheduler}. */ 232 @Override 233 @Nullable 234 public RecordingScheduler getRecordingScheduler() { 235 return mRecordingScheduler; 236 } 237 238 /** Returns the {@link DvrWatchedPositionManager}. */ 239 @Override 240 public DvrWatchedPositionManager getDvrWatchedPositionManager() { 241 if (mDvrWatchedPositionManager == null) { 242 mDvrWatchedPositionManager = new DvrWatchedPositionManager(this); 243 } 244 return mDvrWatchedPositionManager; 245 } 246 247 @Override 248 @TargetApi(Build.VERSION_CODES.N) 249 public InputSessionManager getInputSessionManager() { 250 if (mInputSessionManager == null) { 251 mInputSessionManager = new InputSessionManager(this); 252 } 253 return mInputSessionManager; 254 } 255 256 /** Returns {@link ChannelDataManager}. */ 257 @Override 258 public ChannelDataManager getChannelDataManager() { 259 if (mChannelDataManager == null) { 260 mChannelDataManager = new ChannelDataManager(this, getTvInputManagerHelper()); 261 mChannelDataManager.start(); 262 } 263 return mChannelDataManager; 264 } 265 266 @Override 267 public boolean isChannelDataManagerLoadFinished() { 268 return mChannelDataManager != null && mChannelDataManager.isDbLoadFinished(); 269 } 270 271 /** Returns {@link ProgramDataManager}. */ 272 @Override 273 public ProgramDataManager getProgramDataManager() { 274 if (mProgramDataManager != null) { 275 return mProgramDataManager; 276 } 277 Utils.runInMainThreadAndWait( 278 new Runnable() { 279 @Override 280 public void run() { 281 if (mProgramDataManager == null) { 282 mProgramDataManager = new ProgramDataManager(TvApplication.this); 283 mProgramDataManager.start(); 284 } 285 } 286 }); 287 return mProgramDataManager; 288 } 289 290 @Override 291 public boolean isProgramDataManagerCurrentProgramsLoadFinished() { 292 return mProgramDataManager != null && mProgramDataManager.isCurrentProgramsLoadFinished(); 293 } 294 295 /** Returns {@link PreviewDataManager}. */ 296 @TargetApi(Build.VERSION_CODES.O) 297 @Override 298 public PreviewDataManager getPreviewDataManager() { 299 if (mPreviewDataManager == null) { 300 mPreviewDataManager = new PreviewDataManager(this); 301 mPreviewDataManager.start(); 302 } 303 return mPreviewDataManager; 304 } 305 306 /** Returns {@link DvrDataManager}. */ 307 @TargetApi(Build.VERSION_CODES.N) 308 @Override 309 public DvrDataManager getDvrDataManager() { 310 if (mDvrDataManager == null) { 311 DvrDataManagerImpl dvrDataManager = new DvrDataManagerImpl(this, Clock.SYSTEM); 312 mDvrDataManager = dvrDataManager; 313 dvrDataManager.start(); 314 } 315 return mDvrDataManager; 316 } 317 318 @Override 319 @TargetApi(Build.VERSION_CODES.N) 320 public RecordingStorageStatusManager getRecordingStorageStatusManager() { 321 if (mDvrStorageStatusManager == null) { 322 mDvrStorageStatusManager = new DvrStorageStatusManager(this); 323 } 324 return mDvrStorageStatusManager; 325 } 326 327 /** Returns the main activity information. */ 328 @Override 329 public MainActivityWrapper getMainActivityWrapper() { 330 return mMainActivityWrapper; 331 } 332 333 /** Returns {@link TvInputManagerHelper}. */ 334 @Override 335 public TvInputManagerHelper getTvInputManagerHelper() { 336 if (mTvInputManagerHelper == null) { 337 mTvInputManagerHelper = new TvInputManagerHelper(this); 338 mTvInputManagerHelper.start(); 339 } 340 return mTvInputManagerHelper; 341 } 342 343 @Override 344 public synchronized TunerInputController getTunerInputController() { 345 if (mTunerInputController == null) { 346 mTunerInputController = 347 new TunerInputController( 348 ComponentName.unflattenFromString(getEmbeddedTunerInputId())); 349 } 350 return mTunerInputController; 351 } 352 353 @Override 354 public boolean isRunningInMainProcess() { 355 return mRunningInMainProcess != null && mRunningInMainProcess; 356 } 357 358 /** 359 * SelectInputActivity is set in {@link SelectInputActivity#onCreate} and cleared in {@link 360 * SelectInputActivity#onDestroy}. 361 */ 362 public void setSelectInputActivity(SelectInputActivity activity) { 363 mSelectInputActivity = activity; 364 } 365 366 public void handleGuideKey() { 367 if (!mMainActivityWrapper.isResumed()) { 368 startActivity(new Intent(Intent.ACTION_VIEW, TvContract.Programs.CONTENT_URI)); 369 } else { 370 mMainActivityWrapper.getMainActivity().getOverlayManager().toggleProgramGuide(); 371 } 372 } 373 374 /** Handles the global key KEYCODE_TV. */ 375 public void handleTvKey() { 376 if (!mMainActivityWrapper.isResumed()) { 377 startMainActivity(null); 378 } 379 } 380 381 /** Handles the global key KEYCODE_TV_INPUT. */ 382 public void handleTvInputKey() { 383 TvInputManager tvInputManager = (TvInputManager) getSystemService(Context.TV_INPUT_SERVICE); 384 List<TvInputInfo> tvInputs = tvInputManager.getTvInputList(); 385 int inputCount = 0; 386 boolean hasTunerInput = false; 387 for (TvInputInfo input : tvInputs) { 388 if (input.isPassthroughInput()) { 389 if (!input.isHidden(this)) { 390 ++inputCount; 391 } 392 } else if (!hasTunerInput) { 393 hasTunerInput = true; 394 ++inputCount; 395 } 396 } 397 if (inputCount < 2) { 398 return; 399 } 400 Activity activityToHandle = 401 mMainActivityWrapper.isResumed() 402 ? mMainActivityWrapper.getMainActivity() 403 : mSelectInputActivity; 404 if (activityToHandle != null) { 405 // If startActivity is called, MainActivity.onPause is unnecessarily called. To 406 // prevent it, MainActivity.dispatchKeyEvent is directly called. 407 activityToHandle.dispatchKeyEvent( 408 new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_TV_INPUT)); 409 activityToHandle.dispatchKeyEvent( 410 new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_TV_INPUT)); 411 } else if (mMainActivityWrapper.isStarted()) { 412 Bundle extras = new Bundle(); 413 extras.putString(Utils.EXTRA_KEY_ACTION, Utils.EXTRA_ACTION_SHOW_TV_INPUT); 414 startMainActivity(extras); 415 } else { 416 startActivity( 417 new Intent(this, SelectInputActivity.class) 418 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); 419 } 420 } 421 422 private void startMainActivity(Bundle extras) { 423 // The use of FLAG_ACTIVITY_NEW_TASK enables arbitrary applications to access the intent 424 // sent to the root activity. Having said that, we should be fine here since such an intent 425 // does not carry any important user data. 426 Intent intent = 427 new Intent(this, MainActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 428 if (extras != null) { 429 intent.putExtras(extras); 430 } 431 startActivity(intent); 432 } 433 434 /** 435 * Returns the version name of the live channels. 436 * 437 * @see PackageInfo#versionName 438 */ 439 public String getVersionName() { 440 return mVersionName; 441 } 442 443 /** 444 * Checks the input counts and enable/disable TvActivity. Also upda162 the input list in {@link 445 * SetupUtils}. 446 */ 447 @Override 448 public void handleInputCountChanged() { 449 handleInputCountChanged(false, false, false); 450 } 451 452 /** 453 * Checks the input counts and enable/disable TvActivity. Also updates the input list in {@link 454 * SetupUtils}. 455 * 456 * @param calledByTunerServiceChanged true if it is called when BaseTunerTvInputService is 457 * enabled or disabled. 458 * @param tunerServiceEnabled it's available only when calledByTunerServiceChanged is true. 459 * @param dontKillApp when TvActivity is enabled or disabled by this method, the app restarts by 460 * default. But, if dontKillApp is true, the app won't restart. 461 */ 462 public void handleInputCountChanged( 463 boolean calledByTunerServiceChanged, boolean tunerServiceEnabled, boolean dontKillApp) { 464 TvInputManager inputManager = (TvInputManager) getSystemService(Context.TV_INPUT_SERVICE); 465 boolean enable = 466 (calledByTunerServiceChanged && tunerServiceEnabled) 467 || TvFeatures.UNHIDE.isEnabled(TvApplication.this); 468 if (!enable) { 469 List<TvInputInfo> inputs = inputManager.getTvInputList(); 470 boolean skipTunerInputCheck = false; 471 // Enable the TvActivity only if there is at least one tuner type input. 472 if (!skipTunerInputCheck) { 473 for (TvInputInfo input : inputs) { 474 if (calledByTunerServiceChanged 475 && !tunerServiceEnabled 476 && getEmbeddedTunerInputId().equals(input.getId())) { 477 continue; 478 } 479 if (input.getType() == TvInputInfo.TYPE_TUNER) { 480 enable = true; 481 break; 482 } 483 } 484 } 485 if (DEBUG) Log.d(TAG, "Enable MainActivity: " + enable); 486 } 487 PackageManager packageManager = getPackageManager(); 488 ComponentName name = new ComponentName(this, TvActivity.class); 489 int newState = 490 enable 491 ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED 492 : PackageManager.COMPONENT_ENABLED_STATE_DISABLED; 493 if (packageManager.getComponentEnabledSetting(name) != newState) { 494 packageManager.setComponentEnabledSetting( 495 name, newState, dontKillApp ? PackageManager.DONT_KILL_APP : 0); 496 Log.i(TAG, (enable ? "Un-hide" : "Hide") + " Live TV."); 497 } 498 getSetupUtils().onInputListUpdated(inputManager); 499 } 500} 501