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