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