TvApplication.java revision 3a72b93e554bd22a5c64e71a6956d9604ce05108
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.ComponentName;
22import android.content.Context;
23import android.content.Intent;
24import android.content.pm.PackageInfo;
25import android.content.pm.PackageManager;
26import android.media.tv.TvInputInfo;
27import android.media.tv.TvInputManager;
28import android.media.tv.TvInputManager.TvInputCallback;
29import android.os.Bundle;
30import android.os.StrictMode;
31import android.support.annotation.NonNull;
32import android.support.annotation.Nullable;
33import android.support.annotation.UiThread;
34import android.util.Log;
35import android.view.KeyEvent;
36
37import com.android.tv.analytics.Analytics;
38import com.android.tv.analytics.StubAnalytics;
39import com.android.tv.analytics.StubAnalytics;
40import com.android.tv.analytics.Tracker;
41import com.android.tv.common.BuildConfig;
42import com.android.tv.common.SharedPreferencesUtils;
43import com.android.tv.common.TvCommonUtils;
44import com.android.tv.common.feature.CommonFeatures;
45import com.android.tv.common.ui.setup.animation.SetupAnimationHelper;
46import com.android.tv.data.ChannelDataManager;
47import com.android.tv.data.ProgramDataManager;
48import com.android.tv.dvr.DvrDataManager;
49import com.android.tv.dvr.DvrDataManagerImpl;
50import com.android.tv.dvr.DvrDataManagerInMemoryImpl;
51import com.android.tv.dvr.DvrManager;
52import com.android.tv.dvr.DvrRecordingService;
53import com.android.tv.dvr.DvrSessionManager;
54import com.android.tv.util.SetupUtils;
55import com.android.tv.util.SystemProperties;
56import com.android.tv.util.TvInputManagerHelper;
57import com.android.tv.util.Utils;
58
59import java.util.List;
60
61public class TvApplication extends Application implements ApplicationSingletons {
62    private static final String TAG = "TvApplication";
63    private static final boolean DEBUG = false;
64
65    /**
66     * Returns the @{@link ApplicationSingletons} using the application context.
67     */
68    public static ApplicationSingletons getSingletons(Context context) {
69        return (ApplicationSingletons) context.getApplicationContext();
70    }
71    private String mVersionName = "";
72
73    private final MainActivityWrapper mMainActivityWrapper = new MainActivityWrapper();
74
75    private SelectInputActivity mSelectInputActivity;
76    private Analytics mAnalytics;
77    private Tracker mTracker;
78    private TvInputManagerHelper mTvInputManagerHelper;
79    private ChannelDataManager mChannelDataManager;
80    private ProgramDataManager mProgramDataManager;
81    private DvrManager mDvrManager;
82    private DvrDataManager mDvrDataManager;
83    @Nullable
84    private DvrSessionManager mDvrSessionManager;
85
86    @Override
87    public void onCreate() {
88        super.onCreate();
89        SharedPreferencesUtils.initialize(this);
90        try {
91            PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
92            mVersionName = pInfo.versionName;
93        } catch (PackageManager.NameNotFoundException e) {
94            Log.w(TAG, "Unable to find package '" + getPackageName() + "'.", e);
95            mVersionName = "";
96        }
97        Log.i(TAG, "Starting Live TV " + getVersionName());
98        // Only set StrictMode for ENG builds because the build server only produces userdebug
99        // builds.
100        if (BuildConfig.ENG && SystemProperties.ALLOW_STRICT_MODE.getValue()) {
101            StrictMode.setThreadPolicy(
102                    new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
103            StrictMode.VmPolicy.Builder vmPolicyBuilder = new StrictMode.VmPolicy.Builder()
104                    .detectAll().penaltyLog();
105            if (BuildConfig.ENG && SystemProperties.ALLOW_DEATH_PENALTY.getValue() &&
106                    !TvCommonUtils.isRunningInTest()) {
107                // TODO turn on death penalty for tests when they stop leaking MainActivity
108            }
109            StrictMode.setVmPolicy(vmPolicyBuilder.build());
110        }
111        if (BuildConfig.ENG && !SystemProperties.ALLOW_ANALYTICS_IN_ENG.getValue()) {
112            mAnalytics = StubAnalytics.getInstance(this);
113        } else {
114            mAnalytics = StubAnalytics.getInstance(this);
115        }
116        mTracker = mAnalytics.getDefaultTracker();
117        mTvInputManagerHelper = new TvInputManagerHelper(this);
118        mTvInputManagerHelper.start();
119        mTvInputManagerHelper.addCallback(new TvInputCallback() {
120            @Override
121            public void onInputAdded(String inputId) {
122                handleInputCountChanged();
123            }
124
125            @Override
126            public void onInputRemoved(String inputId) {
127                handleInputCountChanged();
128            }
129        });
130        if (CommonFeatures.DVR.isEnabled(this)) {
131            mDvrManager = new DvrManager(this);
132            //NOTE: DvrRecordingService just keeps running.
133            DvrRecordingService.startService(this);
134        }
135        // In SetupFragment, transitions are set in the constructor. Because the fragment can be
136        // created in Activity.onCreate() by the framework, SetupAnimationHelper should be
137        // initialized here before Activity.onCreate() is called.
138        SetupAnimationHelper.initialize(this);
139        if (DEBUG) Log.i(TAG, "Started Live TV " + mVersionName);
140    }
141
142    /**
143     * Returns the {@link DvrManager}.
144     */
145    @Override
146    public DvrManager getDvrManager() {
147        return mDvrManager;
148    }
149
150    @Override
151    public DvrSessionManager getDvrSessionManger() {
152        if (mDvrSessionManager == null) {
153            mDvrSessionManager = new DvrSessionManager(this);
154        }
155        return mDvrSessionManager;
156    }
157
158    /**
159     * Returns the {@link Analytics}.
160     */
161    @Override
162    public Analytics getAnalytics() {
163        return mAnalytics;
164    }
165
166    /**
167     * Returns the default tracker.
168     */
169    @Override
170    public Tracker getTracker() {
171        return mTracker;
172    }
173
174
175    /**
176     * Returns {@link ChannelDataManager}.
177     */
178    @Override
179    public ChannelDataManager getChannelDataManager() {
180        if (mChannelDataManager == null) {
181            mChannelDataManager = new ChannelDataManager(this, mTvInputManagerHelper);
182            mChannelDataManager.start();
183        }
184        return mChannelDataManager;
185    }
186
187    /**
188     * Returns {@link ProgramDataManager}.
189     */
190    @Override
191    public ProgramDataManager getProgramDataManager() {
192        if (mProgramDataManager == null) {
193            mProgramDataManager = new ProgramDataManager(this);
194            mProgramDataManager.start();
195        }
196        return mProgramDataManager;
197    }
198
199    /**
200     * Returns {@link DvrDataManager}.
201     */
202    @Override
203    public DvrDataManager getDvrDataManager() {
204        if (mDvrDataManager == null) {
205            if(SystemProperties.USE_IN_MEMORY_DVR_DB.getValue()){
206                mDvrDataManager = new DvrDataManagerInMemoryImpl(this);
207            } else {
208                DvrDataManagerImpl dvrDataManager = new DvrDataManagerImpl(this);
209                mDvrDataManager = dvrDataManager;
210                dvrDataManager.start();
211            }
212        }
213        return mDvrDataManager;
214    }
215
216    /**
217     * Returns {@link TvInputManagerHelper}.
218     */
219    @Override
220    public TvInputManagerHelper getTvInputManagerHelper() {
221        return mTvInputManagerHelper;
222    }
223
224    /**
225     * Returns the main activity information.
226     */
227    @Override
228    public MainActivityWrapper getMainActivityWrapper() {
229        return mMainActivityWrapper;
230    }
231
232    /**
233     * SelectInputActivity is set in {@link SelectInputActivity#onCreate} and cleared in
234     * {@link SelectInputActivity#onDestroy}.
235     */
236    public void setSelectInputActivity(SelectInputActivity activity) {
237        mSelectInputActivity = activity;
238    }
239
240    /**
241     * Handles the global key KEYCODE_TV.
242     */
243    public void handleTvKey() {
244        if (!mMainActivityWrapper.isResumed()) {
245            startMainActivity(null);
246        }
247    }
248
249    /**
250     * Handles the global key KEYCODE_TV_INPUT.
251     */
252    public void handleTvInputKey() {
253        TvInputManager tvInputManager = (TvInputManager) getSystemService(Context.TV_INPUT_SERVICE);
254        List<TvInputInfo> tvInputs = tvInputManager.getTvInputList();
255        int inputCount = 0;
256        boolean hasTunerInput = false;
257        for (TvInputInfo input : tvInputs) {
258            if (input.isPassthroughInput()) {
259                ++inputCount;
260            } else if (!hasTunerInput) {
261                hasTunerInput = true;
262                ++inputCount;
263            }
264        }
265        if (inputCount < 2) {
266            return;
267        }
268        Activity activityToHandle = mMainActivityWrapper.isResumed()
269                ? mMainActivityWrapper.getMainActivity() : mSelectInputActivity;
270        if (activityToHandle != null) {
271            // If startActivity is called, MainActivity.onPause is unnecessarily called. To
272            // prevent it, MainActivity.dispatchKeyEvent is directly called.
273            activityToHandle.dispatchKeyEvent(
274                    new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_TV_INPUT));
275            activityToHandle.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP,
276                    KeyEvent.KEYCODE_TV_INPUT));
277        } else if (mMainActivityWrapper.isStarted()) {
278            Bundle extras = new Bundle();
279            extras.putString(Utils.EXTRA_KEY_ACTION, Utils.EXTRA_ACTION_SHOW_TV_INPUT);
280            startMainActivity(extras);
281        } else {
282            startActivity(new Intent(this, SelectInputActivity.class).setFlags(
283                    Intent.FLAG_ACTIVITY_NEW_TASK));
284        }
285    }
286
287    private void startMainActivity(Bundle extras) {
288        // The use of FLAG_ACTIVITY_NEW_TASK enables arbitrary applications to access the intent
289        // sent to the root activity. Having said that, we should be fine here since such an intent
290        // does not carry any important user data.
291        Intent intent = new Intent(this, MainActivity.class)
292                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
293        if (extras != null) {
294            intent.putExtras(extras);
295        }
296        startActivity(intent);
297    }
298
299    /**
300     * Returns the version name of the live channels.
301     *
302     * @see PackageInfo#versionName
303     */
304    public String getVersionName() {
305        return mVersionName;
306    }
307
308    /**
309     * Checks the input counts and enable/disable TvActivity. Also updates the input list in
310     * {@link SetupUtils}.
311     */
312    public void handleInputCountChanged() {
313        TvInputManager inputManager = (TvInputManager) getSystemService(Context.TV_INPUT_SERVICE);
314        boolean enable = false;
315        if (Features.UNHIDE.isEnabled(TvApplication.this)) {
316            enable = true;
317        } else {
318            List<TvInputInfo> inputs = inputManager.getTvInputList();
319            // Enable the TvActivity only if there is at least one tuner type input.
320            for (TvInputInfo input : inputs) {
321                if (input.getType() == TvInputInfo.TYPE_TUNER) {
322                    enable = true;
323                    break;
324                }
325            }
326            if (DEBUG) Log.d(TAG, "Enable MainActivity: " + enable);
327        }
328        PackageManager packageManager = getPackageManager();
329        ComponentName name = new ComponentName(this, TvActivity.class);
330        int newState = enable ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED :
331                PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
332        if (packageManager.getComponentEnabledSetting(name) != newState) {
333            packageManager.setComponentEnabledSetting(name, newState, 0);
334        }
335        SetupUtils.getInstance(TvApplication.this).onInputListUpdated(inputManager);
336    }
337}
338