TvApplication.java revision 7d67089aa1e9aa2123c3cd2f386d7019a1544db1
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.Application; 21import android.content.Context; 22import android.content.Intent; 23import android.content.pm.PackageInfo; 24import android.content.pm.PackageManager; 25import android.media.tv.TvInputInfo; 26import android.media.tv.TvInputManager; 27import android.os.AsyncTask; 28import android.os.Bundle; 29import android.os.StrictMode; 30import android.util.Log; 31import android.view.KeyEvent; 32 33import com.android.tv.analytics.Analytics; 34import com.android.tv.analytics.StubAnalytics; 35import com.android.tv.analytics.OptOutPreferenceHelper; 36import com.android.tv.analytics.StubAnalytics; 37import com.android.tv.analytics.Tracker; 38import com.android.tv.data.ChannelDataManager; 39import com.android.tv.data.ProgramDataManager; 40import com.android.tv.util.SystemProperties; 41import com.android.tv.util.TvInputManagerHelper; 42import com.android.tv.util.Utils; 43 44import java.util.List; 45 46public class TvApplication extends Application { 47 private static final String TAG = "TvApplication"; 48 private static final boolean DEBUG = false; 49 private static String versionName = ""; 50 51 private MainActivity mMainActivity; 52 private SelectInputActivity mSelectInputActivity; 53 private Analytics mAnalytics; 54 private Tracker mTracker; 55 private TvInputManagerHelper mTvInputManagerHelper; 56 private ChannelDataManager mChannelDataManager; 57 private ProgramDataManager mProgramDataManager; 58 private OptOutPreferenceHelper mOptPreferenceHelper; 59 60 @Override 61 public void onCreate() { 62 super.onCreate(); 63 // Only set StrictMode for ENG builds because the build server only produces userdebug 64 // builds. 65 if (BuildConfig.ENG && SystemProperties.ALLOW_STRICT_MODE.getValue()) { 66 StrictMode.setThreadPolicy( 67 new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build()); 68 StrictMode.VmPolicy.Builder vmPolicyBuilder = new StrictMode.VmPolicy.Builder() 69 .detectAll().penaltyLog(); 70 if (BuildConfig.ENG && SystemProperties.ALLOW_DEATH_PENALTY.getValue() && 71 !Utils.isRunningInTest()) { 72 // TODO turn on death penalty for tests when they stop leaking MainActivity 73 } 74 StrictMode.setVmPolicy(vmPolicyBuilder.build()); 75 } 76 77 if (BuildConfig.ENG && !SystemProperties.ALLOW_ANALYTICS_IN_ENG.getValue()) { 78 mAnalytics = StubAnalytics.getInstance(this); 79 } else { 80 mAnalytics = StubAnalytics.getInstance(this); 81 } 82 mTracker = mAnalytics.getDefaultTracker(); 83 if(Features.ANALYTICS_OPT_OUT.isEnabled(this)) { 84 mOptPreferenceHelper = new OptOutPreferenceHelper(this); 85 mOptPreferenceHelper.registerChangeListener(mAnalytics, 86 OptOutPreferenceHelper.ANALYTICS_OPT_OUT_DEFAULT_VALUE); 87 // always start with analytics off 88 mAnalytics.setAppOptOut(true); 89 // then update with the saved preference in an AsyncTask. 90 new AsyncTask<Void, Void, Boolean>() { 91 @Override 92 protected Boolean doInBackground(Void... voids) { 93 return mOptPreferenceHelper.getOptOutPreference( 94 OptOutPreferenceHelper.ANALYTICS_OPT_OUT_DEFAULT_VALUE); 95 } 96 97 @Override 98 protected void onPostExecute(Boolean result) { 99 mAnalytics.setAppOptOut(result); 100 } 101 }.execute(); 102 } 103 try { 104 PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0); 105 versionName = pInfo.versionName; 106 } catch (PackageManager.NameNotFoundException e) { 107 Log.w(TAG, "Unable to get version name.", e); 108 versionName = ""; 109 } 110 mTvInputManagerHelper = new TvInputManagerHelper(this); 111 mTvInputManagerHelper.start(); 112 if (DEBUG) Log.i(TAG, "Started Live TV " + versionName); 113 } 114 115 /** 116 * Returns the {@link Analytics}. 117 */ 118 public Analytics getAnalytics() { 119 return mAnalytics; 120 } 121 122 /** 123 * Returns the default tracker. 124 */ 125 public Tracker getTracker() { 126 return mTracker; 127 } 128 129 public OptOutPreferenceHelper getOptPreferenceHelper(){ 130 return mOptPreferenceHelper; 131 } 132 133 /** 134 * Returns {@link ChannelDataManager}. 135 */ 136 public ChannelDataManager getChannelDataManager() { 137 if (mChannelDataManager == null) { 138 mChannelDataManager = new ChannelDataManager(this, mTvInputManagerHelper, mTracker); 139 mChannelDataManager.start(); 140 } 141 return mChannelDataManager; 142 } 143 144 /** 145 * Returns {@link ProgramDataManager}. 146 */ 147 public ProgramDataManager getProgramDataManager() { 148 if (mProgramDataManager == null) { 149 mProgramDataManager = new ProgramDataManager(this); 150 mProgramDataManager.start(); 151 } 152 return mProgramDataManager; 153 } 154 155 /** 156 * Returns {@link TvInputManagerHelper}. 157 */ 158 public TvInputManagerHelper getTvInputManagerHelper() { 159 return mTvInputManagerHelper; 160 } 161 162 /** 163 * MainActivity is set in {@link MainActivity#onCreate} and cleared in 164 * {@link MainActivity#onDestroy}. 165 */ 166 public void setMainActivity(MainActivity activity) { 167 mMainActivity = activity; 168 } 169 170 /** 171 * SelectInputActivity is set in {@link SelectInputActivity#onCreate} and cleared in 172 * {@link SelectInputActivity#onDestroy}. 173 */ 174 public void setSelectInputActivity(SelectInputActivity activity) { 175 mSelectInputActivity = activity; 176 } 177 178 /** 179 * Checks if MainActivity is set or not. 180 */ 181 public boolean hasMainActivity() { 182 return (mMainActivity != null); 183 } 184 185 /** 186 * Returns true, if {@code activity} is the current activity. 187 * 188 * Note: MainActivity can start while another MainActivity destroys. In this case, the current 189 * activity is the newly created activity. 190 */ 191 public boolean isCurrentMainActivity(MainActivity activity) { 192 return mMainActivity == activity; 193 } 194 195 /** 196 * Handles the global key KEYCODE_TV. 197 */ 198 public void handleTvKey() { 199 if (mMainActivity == null || !mMainActivity.isActivityResumed()) { 200 startMainActivity(null); 201 } 202 } 203 204 /** 205 * Handles the global key KEYCODE_TV_INPUT. 206 */ 207 public void handleTvInputKey() { 208 TvInputManager tvInputManager = (TvInputManager) getSystemService(Context.TV_INPUT_SERVICE); 209 List<TvInputInfo> tvInputs = tvInputManager.getTvInputList(); 210 int inputCount = 0; 211 boolean hasTunerInput = false; 212 for (TvInputInfo input : tvInputs) { 213 if (input.isPassthroughInput()) { 214 ++inputCount; 215 } else if (!hasTunerInput) { 216 hasTunerInput = true; 217 ++inputCount; 218 } 219 } 220 if (inputCount < 2) { 221 return; 222 } 223 Activity activityToHandle = mMainActivity != null && mMainActivity.isActivityResumed() 224 ? mMainActivity : mSelectInputActivity; 225 if (activityToHandle != null) { 226 // If startActivity is called, MainActivity.onPause is unnecessarily called. To 227 // prevent it, MainActivity.dispatchKeyEvent is directly called. 228 activityToHandle.dispatchKeyEvent( 229 new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_TV_INPUT)); 230 activityToHandle.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, 231 KeyEvent.KEYCODE_TV_INPUT)); 232 } else if (mMainActivity != null && mMainActivity.isActivityStarted()) { 233 Bundle extras = new Bundle(); 234 extras.putString(Utils.EXTRA_KEY_ACTION, Utils.EXTRA_ACTION_SHOW_TV_INPUT); 235 startMainActivity(extras); 236 } else { 237 startActivity(new Intent(this, SelectInputActivity.class).setFlags( 238 Intent.FLAG_ACTIVITY_NEW_TASK)); 239 } 240 } 241 242 private void startMainActivity(Bundle extras) { 243 // The use of FLAG_ACTIVITY_NEW_TASK enables arbitrary applications to access the intent 244 // sent to the root activity. Having said that, we should be fine here since such an intent 245 // does not carry any important user data. 246 Intent intent = new Intent(this, MainActivity.class) 247 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 248 if (extras != null) { 249 intent.putExtras(extras); 250 } 251 startActivity(intent); 252 } 253 254 public static String getVersionName() { 255 return versionName; 256 } 257 258} 259