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