| /* |
| * Copyright (C) 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.tv; |
| |
| import android.annotation.TargetApi; |
| import android.app.Activity; |
| import android.app.Application; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.pm.PackageInfo; |
| import android.content.pm.PackageManager; |
| import android.media.tv.TvContract; |
| import android.media.tv.TvInputInfo; |
| import android.media.tv.TvInputManager; |
| import android.media.tv.TvInputManager.TvInputCallback; |
| import android.os.Build; |
| import android.os.Bundle; |
| import android.os.StrictMode; |
| import android.support.annotation.Nullable; |
| import android.support.v4.os.BuildCompat; |
| import android.util.Log; |
| import android.view.KeyEvent; |
| |
| import com.android.tv.analytics.Analytics; |
| import com.android.tv.analytics.StubAnalytics; |
| import com.android.tv.analytics.StubAnalytics; |
| import com.android.tv.analytics.Tracker; |
| import com.android.tv.common.BuildConfig; |
| import com.android.tv.common.SharedPreferencesUtils; |
| import com.android.tv.common.TvCommonUtils; |
| import com.android.tv.common.feature.CommonFeatures; |
| import com.android.tv.common.ui.setup.animation.SetupAnimationHelper; |
| import com.android.tv.data.ChannelDataManager; |
| import com.android.tv.data.ProgramDataManager; |
| import com.android.tv.dvr.DvrDataManager; |
| import com.android.tv.dvr.DvrDataManagerImpl; |
| import com.android.tv.dvr.DvrManager; |
| import com.android.tv.dvr.DvrRecordingService; |
| import com.android.tv.dvr.DvrSessionManager; |
| import com.android.tv.util.Clock; |
| import com.android.tv.util.SetupUtils; |
| import com.android.tv.util.SystemProperties; |
| import com.android.tv.util.TvInputManagerHelper; |
| import com.android.tv.util.Utils; |
| import com.android.usbtuner.UsbTunerPreferences; |
| import com.android.usbtuner.setup.TunerSetupActivity; |
| import com.android.usbtuner.tvinput.UsbTunerTvInputService; |
| |
| import java.util.List; |
| |
| public class TvApplication extends Application implements ApplicationSingletons { |
| private static final String TAG = "TvApplication"; |
| private static final boolean DEBUG = false; |
| |
| /** |
| * Returns the @{@link ApplicationSingletons} using the application context. |
| */ |
| public static ApplicationSingletons getSingletons(Context context) { |
| return (ApplicationSingletons) context.getApplicationContext(); |
| } |
| private String mVersionName = ""; |
| |
| private final MainActivityWrapper mMainActivityWrapper = new MainActivityWrapper(); |
| |
| private SelectInputActivity mSelectInputActivity; |
| private Analytics mAnalytics; |
| private Tracker mTracker; |
| private TvInputManagerHelper mTvInputManagerHelper; |
| private ChannelDataManager mChannelDataManager; |
| private ProgramDataManager mProgramDataManager; |
| private DvrManager mDvrManager; |
| private DvrDataManager mDvrDataManager; |
| @Nullable |
| private DvrSessionManager mDvrSessionManager; |
| |
| @Override |
| public void onCreate() { |
| super.onCreate(); |
| SharedPreferencesUtils.initialize(this); |
| try { |
| PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0); |
| mVersionName = pInfo.versionName; |
| } catch (PackageManager.NameNotFoundException e) { |
| Log.w(TAG, "Unable to find package '" + getPackageName() + "'.", e); |
| mVersionName = ""; |
| } |
| Log.i(TAG, "Starting Live TV " + getVersionName()); |
| // Only set StrictMode for ENG builds because the build server only produces userdebug |
| // builds. |
| if (BuildConfig.ENG && SystemProperties.ALLOW_STRICT_MODE.getValue()) { |
| StrictMode.setThreadPolicy( |
| new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build()); |
| StrictMode.VmPolicy.Builder vmPolicyBuilder = new StrictMode.VmPolicy.Builder() |
| .detectAll().penaltyLog(); |
| if (BuildConfig.ENG && SystemProperties.ALLOW_DEATH_PENALTY.getValue() && |
| !TvCommonUtils.isRunningInTest()) { |
| // TODO turn on death penalty for tests when they stop leaking MainActivity |
| } |
| StrictMode.setVmPolicy(vmPolicyBuilder.build()); |
| } |
| if (BuildConfig.ENG && !SystemProperties.ALLOW_ANALYTICS_IN_ENG.getValue()) { |
| mAnalytics = StubAnalytics.getInstance(this); |
| } else { |
| mAnalytics = StubAnalytics.getInstance(this); |
| } |
| mTracker = mAnalytics.getDefaultTracker(); |
| mTvInputManagerHelper = new TvInputManagerHelper(this); |
| mTvInputManagerHelper.start(); |
| mTvInputManagerHelper.addCallback(new TvInputCallback() { |
| @Override |
| public void onInputAdded(String inputId) { |
| handleInputCountChanged(); |
| } |
| |
| @Override |
| public void onInputRemoved(String inputId) { |
| handleInputCountChanged(); |
| } |
| }); |
| if (CommonFeatures.DVR.isEnabled(this) && BuildCompat.isAtLeastN()) { |
| mDvrManager = new DvrManager(this); |
| //NOTE: DvrRecordingService just keeps running. |
| DvrRecordingService.startService(this); |
| } |
| // In SetupFragment, transitions are set in the constructor. Because the fragment can be |
| // created in Activity.onCreate() by the framework, SetupAnimationHelper should be |
| // initialized here before Activity.onCreate() is called. |
| SetupAnimationHelper.initialize(this); |
| if (DEBUG) Log.i(TAG, "Started Live TV " + mVersionName); |
| } |
| |
| /** |
| * Returns the {@link DvrManager}. |
| */ |
| @Override |
| public DvrManager getDvrManager() { |
| return mDvrManager; |
| } |
| |
| @Override |
| @TargetApi(Build.VERSION_CODES.N) |
| public DvrSessionManager getDvrSessionManger() { |
| if (mDvrSessionManager == null) { |
| mDvrSessionManager = new DvrSessionManager(this); |
| } |
| return mDvrSessionManager; |
| } |
| |
| /** |
| * Returns the {@link Analytics}. |
| */ |
| @Override |
| public Analytics getAnalytics() { |
| return mAnalytics; |
| } |
| |
| /** |
| * Returns the default tracker. |
| */ |
| @Override |
| public Tracker getTracker() { |
| return mTracker; |
| } |
| |
| |
| /** |
| * Returns {@link ChannelDataManager}. |
| */ |
| @Override |
| public ChannelDataManager getChannelDataManager() { |
| if (mChannelDataManager == null) { |
| mChannelDataManager = new ChannelDataManager(this, mTvInputManagerHelper); |
| mChannelDataManager.start(); |
| } |
| return mChannelDataManager; |
| } |
| |
| /** |
| * Returns {@link ProgramDataManager}. |
| */ |
| @Override |
| public ProgramDataManager getProgramDataManager() { |
| if (mProgramDataManager == null) { |
| mProgramDataManager = new ProgramDataManager(this); |
| mProgramDataManager.start(); |
| } |
| return mProgramDataManager; |
| } |
| |
| /** |
| * Returns {@link DvrDataManager}. |
| */ |
| @TargetApi(Build.VERSION_CODES.N) |
| @Override |
| public DvrDataManager getDvrDataManager() { |
| if (mDvrDataManager == null) { |
| DvrDataManagerImpl dvrDataManager = new DvrDataManagerImpl(this, Clock.SYSTEM); |
| mDvrDataManager = dvrDataManager; |
| dvrDataManager.start(); |
| } |
| return mDvrDataManager; |
| } |
| |
| /** |
| * Returns {@link TvInputManagerHelper}. |
| */ |
| @Override |
| public TvInputManagerHelper getTvInputManagerHelper() { |
| return mTvInputManagerHelper; |
| } |
| |
| /** |
| * Returns the main activity information. |
| */ |
| @Override |
| public MainActivityWrapper getMainActivityWrapper() { |
| return mMainActivityWrapper; |
| } |
| |
| /** |
| * SelectInputActivity is set in {@link SelectInputActivity#onCreate} and cleared in |
| * {@link SelectInputActivity#onDestroy}. |
| */ |
| public void setSelectInputActivity(SelectInputActivity activity) { |
| mSelectInputActivity = activity; |
| } |
| |
| /** |
| * Handles the global key KEYCODE_TV. |
| */ |
| public void handleTvKey() { |
| if (!mMainActivityWrapper.isResumed()) { |
| startMainActivity(null); |
| } |
| } |
| |
| /** |
| * Handles the global key KEYCODE_TV_INPUT. |
| */ |
| public void handleTvInputKey() { |
| TvInputManager tvInputManager = (TvInputManager) getSystemService(Context.TV_INPUT_SERVICE); |
| List<TvInputInfo> tvInputs = tvInputManager.getTvInputList(); |
| int inputCount = 0; |
| boolean hasTunerInput = false; |
| for (TvInputInfo input : tvInputs) { |
| if (input.isPassthroughInput()) { |
| if (!input.isHidden(this)) { |
| ++inputCount; |
| } |
| } else if (!hasTunerInput) { |
| hasTunerInput = true; |
| ++inputCount; |
| } |
| } |
| if (inputCount < 2) { |
| return; |
| } |
| Activity activityToHandle = mMainActivityWrapper.isResumed() |
| ? mMainActivityWrapper.getMainActivity() : mSelectInputActivity; |
| if (activityToHandle != null) { |
| // If startActivity is called, MainActivity.onPause is unnecessarily called. To |
| // prevent it, MainActivity.dispatchKeyEvent is directly called. |
| activityToHandle.dispatchKeyEvent( |
| new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_TV_INPUT)); |
| activityToHandle.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, |
| KeyEvent.KEYCODE_TV_INPUT)); |
| } else if (mMainActivityWrapper.isStarted()) { |
| Bundle extras = new Bundle(); |
| extras.putString(Utils.EXTRA_KEY_ACTION, Utils.EXTRA_ACTION_SHOW_TV_INPUT); |
| startMainActivity(extras); |
| } else { |
| startActivity(new Intent(this, SelectInputActivity.class).setFlags( |
| Intent.FLAG_ACTIVITY_NEW_TASK)); |
| } |
| } |
| |
| private void startMainActivity(Bundle extras) { |
| // The use of FLAG_ACTIVITY_NEW_TASK enables arbitrary applications to access the intent |
| // sent to the root activity. Having said that, we should be fine here since such an intent |
| // does not carry any important user data. |
| Intent intent = new Intent(this, MainActivity.class) |
| .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| if (extras != null) { |
| intent.putExtras(extras); |
| } |
| startActivity(intent); |
| } |
| |
| /** |
| * Returns the version name of the live channels. |
| * |
| * @see PackageInfo#versionName |
| */ |
| public String getVersionName() { |
| return mVersionName; |
| } |
| |
| /** |
| * Checks the input counts and enable/disable TvActivity. Also updates the input list in |
| * {@link SetupUtils}. |
| */ |
| public void handleInputCountChanged() { |
| handleInputCountChanged(false, false, false); |
| } |
| |
| /** |
| * Checks the input counts and enable/disable TvActivity. Also updates the input list in |
| * {@link SetupUtils}. |
| * |
| * @param calledByTunerServiceChanged true if it is called when UsbTunerTvInputService |
| * is enabled or disabled. |
| * @param tunerServiceEnabled it's available only when calledByTunerServiceChanged is true. |
| * @param dontKillApp when TvActivity is enabled or disabled by this method, the app restarts |
| * by default. But, if dontKillApp is true, the app won't restart. |
| */ |
| public void handleInputCountChanged(boolean calledByTunerServiceChanged, |
| boolean tunerServiceEnabled, boolean dontKillApp) { |
| TvInputManager inputManager = (TvInputManager) getSystemService(Context.TV_INPUT_SERVICE); |
| boolean enable = (calledByTunerServiceChanged && tunerServiceEnabled) |
| || Features.UNHIDE.isEnabled(TvApplication.this); |
| if (!enable) { |
| List<TvInputInfo> inputs = inputManager.getTvInputList(); |
| boolean skipTunerInputCheck = false; |
| // Enable the TvActivity only if there is at least one tuner type input. |
| if (!skipTunerInputCheck) { |
| for (TvInputInfo input : inputs) { |
| if (calledByTunerServiceChanged && !tunerServiceEnabled |
| && UsbTunerTvInputService.getInputId(this).equals(input.getId())) { |
| continue; |
| } |
| if (input.getType() == TvInputInfo.TYPE_TUNER) { |
| enable = true; |
| break; |
| } |
| } |
| } |
| if (DEBUG) Log.d(TAG, "Enable MainActivity: " + enable); |
| } |
| PackageManager packageManager = getPackageManager(); |
| ComponentName name = new ComponentName(this, TvActivity.class); |
| int newState = enable ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : |
| PackageManager.COMPONENT_ENABLED_STATE_DISABLED; |
| if (packageManager.getComponentEnabledSetting(name) != newState) { |
| packageManager.setComponentEnabledSetting(name, newState, |
| dontKillApp ? PackageManager.DONT_KILL_APP : 0); |
| } |
| SetupUtils.getInstance(TvApplication.this).onInputListUpdated(inputManager); |
| } |
| } |