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;
18816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
19816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.media.tv.TvContract;
201abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.media.tv.TvInputInfo;
21816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.net.Uri;
22816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.os.Handler;
23ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport android.support.annotation.MainThread;
241abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.support.annotation.Nullable;
2548dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport android.util.ArraySet;
26816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.util.Log;
27816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
2848dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport com.android.tv.common.SoftPreconditions;
29816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.data.Channel;
30816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.data.ChannelDataManager;
311abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.tv.util.TvInputManagerHelper;
32816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
33816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.ArrayList;
34816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.Collections;
35816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.HashMap;
36816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.List;
37816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.Map;
38816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.Set;
39816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
40816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko/**
41816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * It manages the current tuned channel among browsable channels. And it determines the next channel
42816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * by channel up/down. But, it doesn't actually tune through TvView.
43816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */
44ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko@MainThread
45816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkopublic class ChannelTuner {
46816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final String TAG = "ChannelTuner";
47816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
48816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private boolean mStarted;
49816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private boolean mChannelDataManagerLoaded;
50816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final List<Channel> mChannels = new ArrayList<>();
51816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final List<Channel> mBrowsableChannels = new ArrayList<>();
52816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final Map<Long, Channel> mChannelMap = new HashMap<>();
53816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // TODO: need to check that mChannelIndexMap can be removed, once mCurrentChannelIndex
54816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // is changed to mCurrentChannel(Id).
55816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final Map<Long, Integer> mChannelIndexMap = new HashMap<>();
56816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
57816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final Handler mHandler = new Handler();
58816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final ChannelDataManager mChannelDataManager;
5948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private final Set<Listener> mListeners = new ArraySet<>();
601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Nullable
61816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private Channel mCurrentChannel;
621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private final TvInputManagerHelper mInputManager;
631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Nullable
641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private TvInputInfo mCurrentChannelInputInfo;
65816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
66816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final ChannelDataManager.Listener mChannelDataManagerListener =
67816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            new ChannelDataManager.Listener() {
68816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                @Override
69816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                public void onLoadFinished() {
70816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    mChannelDataManagerLoaded = true;
71816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    updateChannelData(mChannelDataManager.getChannelList());
72816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    for (Listener l : mListeners) {
73816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        l.onLoadFinished();
74816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    }
75816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
76816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
77816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                @Override
78816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                public void onChannelListUpdated() {
79816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    updateChannelData(mChannelDataManager.getChannelList());
80816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
81816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
82816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                @Override
83816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                public void onChannelBrowsableChanged() {
84816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    updateBrowsableChannels();
85816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    for (Listener l : mListeners) {
86816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        l.onBrowsableChannelListChanged();
87816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    }
88816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
89816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    };
90816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public ChannelTuner(ChannelDataManager channelDataManager, TvInputManagerHelper inputManager) {
92816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mChannelDataManager = channelDataManager;
931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mInputManager = inputManager;
94816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
95816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
96816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
97816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Starts ChannelTuner. It cannot be called twice before calling {@link #stop}.
98816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
99816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void start() {
100816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mStarted) {
101816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            throw new IllegalStateException("start is called twice");
102816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
103816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mStarted = true;
104816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mChannelDataManager.addListener(mChannelDataManagerListener);
105816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mChannelDataManager.isDbLoadFinished()) {
106816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mHandler.post(new Runnable() {
107816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                @Override
108816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                public void run() {
109816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    mChannelDataManagerListener.onLoadFinished();
110816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
111816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            });
112816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
113816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
114816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
115816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
116816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Stops ChannelTuner.
117816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
118816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void stop() {
119816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (!mStarted) {
120816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return;
121816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
122816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mStarted = false;
123816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mHandler.removeCallbacksAndMessages(null);
124816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mChannelDataManager.removeListener(mChannelDataManagerListener);
125816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mCurrentChannel = null;
126816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mChannels.clear();
127816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mBrowsableChannels.clear();
128816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mChannelMap.clear();
129816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mChannelIndexMap.clear();
130816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mChannelDataManagerLoaded = false;
131816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
132816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
133816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
134816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Returns true, if all the channels are loaded.
135816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
136816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public boolean areAllChannelsLoaded() {
137816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return mChannelDataManagerLoaded;
138816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
139816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
140816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
141816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Returns browsable channel lists.
142816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
143816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public List<Channel> getBrowsableChannelList() {
144816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return Collections.unmodifiableList(mBrowsableChannels);
145816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
146816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
147816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
148816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Returns the number of browsable channels.
149816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
150816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public int getBrowsableChannelCount() {
151816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return mBrowsableChannels.size();
152816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
153816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
154816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
155816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Returns the current channel.
156816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
1571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Nullable
158816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public Channel getCurrentChannel() {
159816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return mCurrentChannel;
160816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
161816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
162816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
163816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Sets the current channel. Call this method only when setting the current channel without
164816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * actually tuning to it.
165816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     *
166816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * @param currentChannel The new current channel to set to.
167816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
168816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void setCurrentChannel(Channel currentChannel) {
169816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mCurrentChannel = currentChannel;
170816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
171816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
172816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
173816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Returns the current channel's ID.
174816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
175816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public long getCurrentChannelId() {
176816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return mCurrentChannel != null ? mCurrentChannel.getId() : Channel.INVALID_ID;
177816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
178816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
179816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
180816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Returns the current channel's URI
181816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
182816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public Uri getCurrentChannelUri() {
183816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mCurrentChannel == null) {
184816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return null;
185816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
186816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mCurrentChannel.isPassthrough()) {
187816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return TvContract.buildChannelUriForPassthroughInput(mCurrentChannel.getInputId());
188816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        } else {
189816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return TvContract.buildChannelUri(mCurrentChannel.getId());
190816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
191816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
192816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
193816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
1941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     * Returns the current {@link TvInputInfo}.
1951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     */
1961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Nullable
1971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public TvInputInfo getCurrentInputInfo() {
1981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return mCurrentChannelInputInfo;
1991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
2001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    /**
202816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Returns true, if the current channel is for a passthrough TV input.
203816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
204816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public boolean isCurrentChannelPassthrough() {
205816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return mCurrentChannel != null && mCurrentChannel.isPassthrough();
206816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
207816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
208816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
209816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Moves the current channel to the next (or previous) browsable channel.
210816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     *
211816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * @return true, if the channel is changed to the adjacent channel. If there is no
212816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     *         browsable channel, it returns false.
213816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
214816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public boolean moveToAdjacentBrowsableChannel(boolean up) {
215816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        Channel channel = getAdjacentBrowsableChannel(up);
216816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (channel == null) {
217816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return false;
218816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
219816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        setCurrentChannelAndNotify(mChannelMap.get(channel.getId()));
220816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return true;
221816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
222816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
223816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
224816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Returns a next browsable channel. It doesn't change the current channel unlike
225816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * {@link #moveToAdjacentBrowsableChannel}.
226816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
227816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public Channel getAdjacentBrowsableChannel(boolean up) {
228816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (isCurrentChannelPassthrough() || getBrowsableChannelCount() == 0) {
229816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return null;
230816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
231816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        int channelIndex;
232816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mCurrentChannel == null) {
233816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            channelIndex = 0;
234816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            Channel channel = mChannels.get(channelIndex);
235816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (channel.isBrowsable()) {
236816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                return channel;
237816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
238816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        } else {
239816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            channelIndex = mChannelIndexMap.get(mCurrentChannel.getId());
240816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
241816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        int size = mChannels.size();
242816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        for (int i = 0; i < size; ++i) {
243816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            int nextChannelIndex = up ? channelIndex + 1 + i
244816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    : channelIndex - 1 - i + size;
245816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (nextChannelIndex >= size) {
246816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                nextChannelIndex -= size;
247816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
248816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            Channel channel = mChannels.get(nextChannelIndex);
249816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (channel.isBrowsable()) {
250816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                return channel;
251816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
252816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
253816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        Log.e(TAG, "This code should not be reached");
254816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return null;
255816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
256816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
257816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
258816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Finds the nearest browsable channel from a channel with {@code channelId}. If the channel
259816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * with {@code channelId} is browsable, the channel will be returned.
260816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
261816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public Channel findNearestBrowsableChannel(long channelId) {
262816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (getBrowsableChannelCount() == 0) {
263816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return null;
264816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
265816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        Channel channel = mChannelMap.get(channelId);
266816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (channel == null) {
267816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return mBrowsableChannels.get(0);
268816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        } else if (channel.isBrowsable()) {
269816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return channel;
270816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
271816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        int index = mChannelIndexMap.get(channelId);
272816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        int size = mChannels.size();
273816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        for (int i = 1; i <= size / 2; ++i) {
274816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            Channel upChannel = mChannels.get((index + i) % size);
275816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (upChannel.isBrowsable()) {
276816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                return upChannel;
277816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
278816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            Channel downChannel = mChannels.get((index - i + size) % size);
279816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (downChannel.isBrowsable()) {
280816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                return downChannel;
281816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
282816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
283816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        throw new IllegalStateException(
284816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                "This code should be unreachable in findNearestBrowsableChannel");
285816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
286816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
287816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
288816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Moves the current channel to {@code channel}. It can move to a non-browsable channel as well
289816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * as a browsable channel.
290816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     *
291816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * @return true, the channel change is success. But, if the channel doesn't exist, the channel
292816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     *         change will be failed and it will return false.
293816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
294816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public boolean moveToChannel(Channel channel) {
295816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (channel == null) {
296816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return false;
297816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
298816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (channel.isPassthrough()) {
299816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            setCurrentChannelAndNotify(channel);
300816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return true;
301816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
3027d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        SoftPreconditions.checkState(mChannelDataManagerLoaded, TAG, "Channel data is not loaded");
303816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        Channel newChannel = mChannelMap.get(channel.getId());
304816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (newChannel != null) {
305816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            setCurrentChannelAndNotify(newChannel);
306816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return true;
307816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
308816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return false;
309816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
310816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
311816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
312816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Resets the current channel to {@code null}.
313816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
314816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void resetCurrentChannel() {
315816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        setCurrentChannelAndNotify(null);
316816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
317816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
318816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
319816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Adds {@link Listener}.
320816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
321816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void addListener(Listener listener) {
322816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mListeners.add(listener);
323816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
324816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
325816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
326816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Removes {@link Listener}.
327816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
328816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void removeListener(Listener listener) {
329816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mListeners.remove(listener);
330816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
331816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
332816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public interface Listener {
333816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        /**
334816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko         * Called when all the channels are loaded.
335816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko         */
336816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        void onLoadFinished();
337816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        /**
338816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko         * Called when the browsable channel list is changed.
339816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko         */
340816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        void onBrowsableChannelListChanged();
341816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        /**
342816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko         * Called when the current channel is removed.
343816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko         */
344816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        void onCurrentChannelUnavailable(Channel channel);
345816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        /**
346816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko         * Called when the current channel is changed.
347816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko         */
348816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        void onChannelChanged(Channel previousChannel, Channel currentChannel);
349816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
350816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
351816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private void setCurrentChannelAndNotify(Channel channel) {
352816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mCurrentChannel == channel
353816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                || (channel != null && channel.hasSameReadOnlyInfo(mCurrentChannel))) {
354816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return;
355816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
356816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        Channel previousChannel = mCurrentChannel;
357816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mCurrentChannel = channel;
3581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (mCurrentChannel != null) {
3591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            mCurrentChannelInputInfo = mInputManager.getTvInputInfo(mCurrentChannel.getInputId());
3601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
361816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        for (Listener l : mListeners) {
362816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            l.onChannelChanged(previousChannel, mCurrentChannel);
363816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
364816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
365816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
366816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private void updateChannelData(List<Channel> channels) {
367816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mChannels.clear();
368816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mChannels.addAll(channels);
369816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
370816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mChannelMap.clear();
371816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mChannelIndexMap.clear();
372816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        for (int i = 0; i < channels.size(); ++i) {
373816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            Channel channel = channels.get(i);
374816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            long channelId = channel.getId();
375816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mChannelMap.put(channelId, channel);
376816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mChannelIndexMap.put(channelId, i);
377816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
378816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        updateBrowsableChannels();
379816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
380816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mCurrentChannel != null && !mCurrentChannel.isPassthrough()) {
381816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            Channel prevChannel = mCurrentChannel;
382816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            setCurrentChannelAndNotify(mChannelMap.get(mCurrentChannel.getId()));
383816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (mCurrentChannel == null) {
384816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                for (Listener l : mListeners) {
385816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    l.onCurrentChannelUnavailable(prevChannel);
386816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
387816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
388816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
389816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        // TODO: Do not call onBrowsableChannelListChanged, when only non-browsable
390816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        // channels are changed.
391816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        for (Listener l : mListeners) {
392816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            l.onBrowsableChannelListChanged();
393816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
394816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
395816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
396816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private void updateBrowsableChannels() {
397816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mBrowsableChannels.clear();
398816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        for (Channel channel : mChannels) {
399816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (channel.isBrowsable()) {
400816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                mBrowsableChannels.add(channel);
401816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
402816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
403816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
404816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko}
405