1816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko/*
2816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Copyright (C) 2015 The Android Open Source Project
3816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko *
4816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Licensed under the Apache License, Version 2.0 (the "License");
5816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * you may not use this file except in compliance with the License.
6816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * You may obtain a copy of the License at
7816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko *
8816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko *      http://www.apache.org/licenses/LICENSE-2.0
9816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko *
10816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Unless required by applicable law or agreed to in writing, software
11816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * distributed under the License is distributed on an "AS IS" BASIS,
12816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * See the License for the specific language governing permissions and
14816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * limitations under the License.
15816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */
16816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
17816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkopackage com.android.tv.util;
18816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
19816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.content.ComponentName;
20816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.content.Context;
21816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.content.Intent;
22816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.content.SharedPreferences;
23816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.media.tv.TvContract;
24816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.media.tv.TvInputInfo;
25816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.media.tv.TvInputManager;
26816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.os.Build;
27816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.preference.PreferenceManager;
28ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport android.support.annotation.Nullable;
29ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport android.support.annotation.UiThread;
3048dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport android.text.TextUtils;
3148dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport android.util.ArraySet;
32816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.util.Log;
33816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
341abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.tv.ApplicationSingletons;
357d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport com.android.tv.TvApplication;
3648dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport com.android.tv.common.SoftPreconditions;
377d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport com.android.tv.data.Channel;
387d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport com.android.tv.data.ChannelDataManager;
397d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
40ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport java.util.Collections;
41816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.HashSet;
42816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.Set;
43816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
44816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko/**
45816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * A utility class related to input setup.
46816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */
47816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkopublic class SetupUtils {
48816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final String TAG = "SetupUtils";
49816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final boolean DEBUG = false;
50816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
51816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // Known inputs are inputs which are shown in SetupView before. When a new input is installed,
52816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // the input will not be included in "PREF_KEY_KNOWN_INPUTS".
53816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final String PREF_KEY_KNOWN_INPUTS = "known_inputs";
547d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    // Set up inputs are inputs whose setup activity has been launched and finished successfully.
55816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final String PREF_KEY_SET_UP_INPUTS = "set_up_inputs";
56ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    // Recognized inputs means that the user already knows the inputs are installed.
57ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    private static final String PREF_KEY_RECOGNIZED_INPUTS = "recognized_inputs";
58816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final String PREF_KEY_IS_FIRST_TUNE = "is_first_tune";
59816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static SetupUtils sSetupUtils;
60816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
617d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    private final TvApplication mTvApplication;
62816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final SharedPreferences mSharedPreferences;
63816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final Set<String> mKnownInputs;
64816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final Set<String> mSetUpInputs;
65ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    private final Set<String> mRecognizedInputs;
66816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private boolean mIsFirstTune;
671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private final String mUsbTunerInputId;
68816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
697d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    private SetupUtils(TvApplication tvApplication) {
707d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        mTvApplication = tvApplication;
717d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(tvApplication);
7248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mSetUpInputs = new ArraySet<>();
73ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        mSetUpInputs.addAll(mSharedPreferences.getStringSet(PREF_KEY_SET_UP_INPUTS,
74ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                Collections.<String>emptySet()));
7548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mKnownInputs = new ArraySet<>();
76ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        mKnownInputs.addAll(mSharedPreferences.getStringSet(PREF_KEY_KNOWN_INPUTS,
77ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                Collections.<String>emptySet()));
7848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mRecognizedInputs = new ArraySet<>();
79ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        mRecognizedInputs.addAll(mSharedPreferences.getStringSet(PREF_KEY_RECOGNIZED_INPUTS,
80ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                mKnownInputs));
81816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mIsFirstTune = mSharedPreferences.getBoolean(PREF_KEY_IS_FIRST_TUNE, true);
821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mUsbTunerInputId = TvContract.buildInputId(new ComponentName(tvApplication,
831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                com.android.usbtuner.tvinput.UsbTunerTvInputService.class));
84816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
85816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
86816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
87816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Gets an instance of {@link SetupUtils}.
88816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
89816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public static SetupUtils getInstance(Context context) {
90816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (sSetupUtils != null) {
91816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return sSetupUtils;
92816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
937d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        sSetupUtils = new SetupUtils((TvApplication) context.getApplicationContext());
94816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return sSetupUtils;
95816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
96816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
977d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    /**
987d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko     * Additional work after the setup of TV input.
997d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko     */
100ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    public void onTvInputSetupFinished(final String inputId,
101ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            @Nullable final Runnable postRunnable) {
1027d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        // When TIS adds several channels, ChannelDataManager.Listener.onChannelList
1037d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        // Updated() can be called several times. In this case, it is hard to detect
1047d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        // which one is the last callback. To reduce error prune, we update channel
1057d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        // list again and make all channels of {@code inputId} browsable.
1067d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        onSetupDone(inputId);
1077d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        final ChannelDataManager manager = mTvApplication.getChannelDataManager();
1087d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        if (!manager.isDbLoadFinished()) {
1097d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            manager.addListener(new ChannelDataManager.Listener() {
1107d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                @Override
1117d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                public void onLoadFinished() {
1127d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                    manager.removeListener(this);
1137d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                    updateChannelBrowsable(mTvApplication, inputId, postRunnable);
1147d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                }
1157d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
1167d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                @Override
1177d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                public void onChannelListUpdated() { }
1187d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
1197d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                @Override
1207d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                public void onChannelBrowsableChanged() { }
1217d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            });
1227d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        } else {
1237d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            updateChannelBrowsable(mTvApplication, inputId, postRunnable);
1247d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        }
1257d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    }
1267d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
1277d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    private static void updateChannelBrowsable(Context context, final String inputId,
1287d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            final Runnable postRunnable) {
1291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        ApplicationSingletons appSingletons = TvApplication.getSingletons(context);
1301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        final ChannelDataManager manager = appSingletons.getChannelDataManager();
1317d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        manager.updateChannels(new Runnable() {
1327d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            @Override
1337d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            public void run() {
1347d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                boolean browsableChanged = false;
1357d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                for (Channel channel : manager.getChannelList()) {
1367d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                    if (channel.getInputId().equals(inputId)) {
1377d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                        if (!channel.isBrowsable()) {
1387d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                            manager.updateBrowsable(channel.getId(), true, true);
1397d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                            browsableChanged = true;
1407d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                        }
1417d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                    }
1427d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                }
1437d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                if (browsableChanged) {
1447d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                    manager.notifyChannelBrowsableChanged();
1457d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                    manager.applyUpdatedValuesToDb();
1467d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                }
1477d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                if (postRunnable != null) {
1487d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                    postRunnable.run();
1497d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                }
1507d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            }
1517d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        });
1527d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    }
1537d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
154ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    /**
155ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     * Marks the channels in newly installed inputs browsable.
156ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     */
157ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    @UiThread
158ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    public void markNewChannelsBrowsable() {
159ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        Set<String> newInputsWithChannels = new HashSet<>();
160ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        TvInputManagerHelper tvInputManagerHelper = mTvApplication.getTvInputManagerHelper();
161ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        ChannelDataManager channelDataManager = mTvApplication.getChannelDataManager();
162ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        SoftPreconditions.checkState(channelDataManager.isDbLoadFinished());
163ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        for (TvInputInfo input : tvInputManagerHelper.getTvInputInfos(true, true)) {
164ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            String inputId = input.getId();
165ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            if (!isSetupDone(inputId) && channelDataManager.getChannelCountForInput(inputId) > 0) {
166ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                onSetupDone(inputId);
167ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                newInputsWithChannels.add(inputId);
168ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                if (DEBUG) {
169ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                    Log.d(TAG, "New input " + inputId + " has "
170ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                            + channelDataManager.getChannelCountForInput(inputId)
171ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                            + " channels");
172ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                }
173ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            }
174ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        }
175ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        if (!newInputsWithChannels.isEmpty()) {
176ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            for (Channel channel : channelDataManager.getChannelList()) {
177ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                if (newInputsWithChannels.contains(channel.getInputId())) {
178ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                    channelDataManager.updateBrowsable(channel.getId(), true);
179ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                }
180ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            }
181ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            channelDataManager.applyUpdatedValuesToDb();
182ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        }
183ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    }
184ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
185816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public boolean isFirstTune() {
186816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return mIsFirstTune;
187816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
188816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
189816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
190816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Returns true, if the input with {@code inputId} is newly installed.
191816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
192816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public boolean isNewInput(String inputId) {
193816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return !mKnownInputs.contains(inputId);
194816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
195816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
196816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
197816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Marks an input with {@code inputId} as a known input. Once it is marked, {@link #isNewInput}
198816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * will return false.
199816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
200816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void markAsKnownInput(String inputId) {
201816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mKnownInputs.add(inputId);
202ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        mRecognizedInputs.add(inputId);
203ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        mSharedPreferences.edit().putStringSet(PREF_KEY_KNOWN_INPUTS, mKnownInputs)
204ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                .putStringSet(PREF_KEY_RECOGNIZED_INPUTS, mRecognizedInputs).apply();
205816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
206816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
207816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
2087d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko     * Returns {@code true}, if {@code inputId}'s setup has been done before.
209816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
2107d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    public boolean isSetupDone(String inputId) {
2117d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        boolean done = mSetUpInputs.contains(inputId);
212816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (DEBUG) {
2137d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            Log.d(TAG, "isSetupDone: (input=" + inputId + ", result= " + done + ")");
214816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
2157d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        return done;
216816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
217816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
218816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
219816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Returns true, if there is any newly installed input.
220816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
221816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public boolean hasNewInput(TvInputManagerHelper inputManager) {
222816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        for (TvInputInfo input : inputManager.getTvInputInfos(true, true)) {
223816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (isNewInput(input.getId())) {
224816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                return true;
225816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
226816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
227816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return false;
228816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
229816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
230816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
231ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     * Checks whether the given input is already recognized by the user or not.
232ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     */
233ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    private boolean isRecognizedInput(String inputId) {
234ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        return mRecognizedInputs.contains(inputId);
235ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    }
236ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
237ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    /**
238ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     * Marks all the inputs as recognized inputs. Once it is marked, {@link #isRecognizedInput} will
239ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     * return {@code true}.
240ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     */
241ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    public void markAllInputsRecognized(TvInputManagerHelper inputManager) {
242ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        for (TvInputInfo input : inputManager.getTvInputInfos(true, true)) {
243ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            mRecognizedInputs.add(input.getId());
244ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        }
245ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        mSharedPreferences.edit().putStringSet(PREF_KEY_RECOGNIZED_INPUTS, mRecognizedInputs)
246ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                .apply();
247ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    }
248ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
249ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    /**
250ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     * Checks whether there are any unrecognized inputs.
251ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     */
252ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    public boolean hasUnrecognizedInput(TvInputManagerHelper inputManager) {
253ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        for (TvInputInfo input : inputManager.getTvInputInfos(true, true)) {
254ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            if (!isRecognizedInput(input.getId())) {
255ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                return true;
256ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            }
257ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        }
258ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        return false;
259ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    }
260ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
261ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    /**
262816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Grants permission for writing EPG data to all verified packages.
263816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     *
264816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * @param context The Context used for granting permission.
265816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
266816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public static void grantEpgPermissionToSetUpPackages(Context context) {
26748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
26848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            // Can't grant permission.
26948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            return;
27048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
27148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
27248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        // Find all already-verified packages.
27348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        Set<String> setUpPackages = new HashSet<>();
27448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
27548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        for (String input : sp.getStringSet(PREF_KEY_SET_UP_INPUTS, Collections.EMPTY_SET)) {
27648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            if (!TextUtils.isEmpty(input)) {
27748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                ComponentName componentName = ComponentName.unflattenFromString(input);
27848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                if (componentName != null) {
27948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                    setUpPackages.add(componentName.getPackageName());
2803a72b93e554bd22a5c64e71a6956d9604ce05108Youngsang Cho                }
281816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
282369b6a409204a9b2a95f7ba575d7c3b7bdc94ab7Youngsang Cho        }
28348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
28448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        for (String packageName : setUpPackages) {
28548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            grantEpgPermission(context, packageName);
28648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
287816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
288816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
289816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
290816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Grants permission for writing EPG data to a given package.
291816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     *
292816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * @param context The Context used for granting permission.
293816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * @param packageName The name of the package to give permission.
294816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
295816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public static void grantEpgPermission(Context context, String packageName) {
296816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        // TvProvider allows granting of Uri permissions starting from MNC.
2977d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
298816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (DEBUG) {
299816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                Log.d(TAG, "grantEpgPermission(context=" + context + ", packageName=" + packageName
300816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        + ")");
301816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
302816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            try {
303816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                int modeFlags = Intent.FLAG_GRANT_WRITE_URI_PERMISSION
304816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION;
305816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                context.grantUriPermission(packageName, TvContract.Channels.CONTENT_URI, modeFlags);
306816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                context.grantUriPermission(packageName, TvContract.Programs.CONTENT_URI, modeFlags);
307816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            } catch (SecurityException e) {
308816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                Log.e(TAG, "Either TvProvider does not allow granting of Uri permissions or the app"
3097d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                        + " does not have permission.", e);
310816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
311816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
312816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
313816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
314816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
315816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Called when Live channels app is launched. Once it is called, {@link
316816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * #isFirstTune} will return false.
317816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
318816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void onTuned() {
319816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (!mIsFirstTune) {
320816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return;
321816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
322816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mIsFirstTune = false;
323816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mSharedPreferences.edit().putBoolean(PREF_KEY_IS_FIRST_TUNE, false).apply();
324816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
325816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
326816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
327816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Called when input list is changed. It mainly handles input removals.
328816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
329816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void onInputListUpdated(TvInputManager manager) {
330ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        // mRecognizedInputs > mKnownInputs > mSetUpInputs.
331ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        Set<String> removedInputList = new HashSet<>(mRecognizedInputs);
332816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        for (TvInputInfo input : manager.getTvInputList()) {
333816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            removedInputList.remove(input.getId());
334816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
3351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // A USB tuner device can be temporarily unplugged. We do not remove the USB tuner input
3361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // from the known inputs so that the input won't appear as a new input whenever the user
3371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // plugs in the USB tuner device again.
3381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        removedInputList.remove(mUsbTunerInputId);
339816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
340816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (!removedInputList.isEmpty()) {
341816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            for (String input : removedInputList) {
342ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                mRecognizedInputs.remove(input);
343816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                mSetUpInputs.remove(input);
344816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                mKnownInputs.remove(input);
345816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
346ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            mSharedPreferences.edit().putStringSet(PREF_KEY_SET_UP_INPUTS, mSetUpInputs)
347ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                    .putStringSet(PREF_KEY_KNOWN_INPUTS, mKnownInputs)
34848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                    .putStringSet(PREF_KEY_RECOGNIZED_INPUTS, mRecognizedInputs).apply();
349816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
350816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
351816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
352816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
3537d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko     * Called when an setup is done. Once it is called, {@link #isSetupDone} returns {@code true}
3547d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko     * for {@code inputId}.
355816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
3567d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    public void onSetupDone(String inputId) {
3571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        SoftPreconditions.checkState(inputId != null);
3587d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        if (DEBUG) Log.d(TAG, "onSetupDone: input=" + inputId);
359ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        if (!mRecognizedInputs.contains(inputId)) {
360ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            Log.i(TAG, "An unrecognized input's setup has been done. inputId=" + inputId);
361ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            mRecognizedInputs.add(inputId);
36248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            mSharedPreferences.edit().putStringSet(PREF_KEY_RECOGNIZED_INPUTS, mRecognizedInputs)
363ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                    .apply();
364ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        }
3657d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        if (!mKnownInputs.contains(inputId)) {
3667d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            Log.i(TAG, "An unknown input's setup has been done. inputId=" + inputId);
3677d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            mKnownInputs.add(inputId);
368ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            mSharedPreferences.edit().putStringSet(PREF_KEY_KNOWN_INPUTS, mKnownInputs).apply();
369ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        }
370ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        if (!mSetUpInputs.contains(inputId)) {
371ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            mSetUpInputs.add(inputId);
372ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            mSharedPreferences.edit().putStringSet(PREF_KEY_SET_UP_INPUTS, mSetUpInputs).apply();
3737d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        }
374816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
375816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko}
376