• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.tv.menu;
18 
19 import android.content.Context;
20 import android.content.Intent;
21 import android.media.tv.TvInputInfo;
22 import android.view.View;
23 import com.android.tv.R;
24 import com.android.tv.TvSingletons;
25 import com.android.tv.analytics.Tracker;
26 import com.android.tv.common.feature.CommonFeatures;
27 import com.android.tv.data.ChannelImpl;
28 import com.android.tv.data.api.Channel;
29 import com.android.tv.dvr.DvrDataManager;
30 import com.android.tv.recommendation.Recommender;
31 import com.android.tv.util.TvInputManagerHelper;
32 import java.util.ArrayDeque;
33 import java.util.ArrayList;
34 import java.util.List;
35 
36 /** An adapter of the Channels row. */
37 public class ChannelsRowAdapter extends ItemListRowView.ItemListAdapter<ChannelsRowItem> {
38     // There are four special cards: guide, setup, dvr, applink.
39     private static final int SIZE_OF_VIEW_TYPE = 5;
40 
41     private final Context mContext;
42     private final Tracker mTracker;
43     private final Recommender mRecommender;
44     private final DvrDataManager mDvrDataManager;
45     private final int mMaxCount;
46     private final int mMinCount;
47 
48     private final View.OnClickListener mGuideOnClickListener =
49             new View.OnClickListener() {
50                 @Override
51                 public void onClick(View view) {
52                     mTracker.sendMenuClicked(R.string.channels_item_program_guide);
53                     getMainActivity().getOverlayManager().showProgramGuide();
54                 }
55             };
56 
57     private final View.OnClickListener mSetupOnClickListener =
58             new View.OnClickListener() {
59                 @Override
60                 public void onClick(View view) {
61                     mTracker.sendMenuClicked(R.string.channels_item_setup);
62                     getMainActivity().getOverlayManager().showSetupFragment();
63                 }
64             };
65 
66     private final View.OnClickListener mDvrOnClickListener =
67             new View.OnClickListener() {
68                 @Override
69                 public void onClick(View view) {
70                     mTracker.sendMenuClicked(R.string.channels_item_dvr);
71                     getMainActivity().getOverlayManager().showDvrManager();
72                 }
73             };
74 
75     private final View.OnClickListener mAppLinkOnClickListener =
76             new View.OnClickListener() {
77                 @Override
78                 public void onClick(View view) {
79                     mTracker.sendMenuClicked(R.string.channels_item_app_link);
80                     Intent intent = ((AppLinkCardView) view).getIntent();
81                     if (intent != null) {
82                         getMainActivity().startActivitySafe(intent);
83                     }
84                 }
85             };
86 
87     private final View.OnClickListener mChannelOnClickListener =
88             new View.OnClickListener() {
89                 @Override
90                 public void onClick(View view) {
91                     // Always send the label "Channels" because the channel ID or name or number
92                     // might be
93                     // sensitive.
94                     mTracker.sendMenuClicked(R.string.menu_title_channels);
95                     getMainActivity().tuneToChannel((Channel) view.getTag());
96                     getMainActivity().hideOverlaysForTune();
97                 }
98             };
99 
ChannelsRowAdapter( Context context, Recommender recommender, int minCount, int maxCount)100     public ChannelsRowAdapter(
101             Context context, Recommender recommender, int minCount, int maxCount) {
102         super(context);
103         mContext = context;
104         TvSingletons tvSingletons = TvSingletons.getSingletons(context);
105         mTracker = tvSingletons.getTracker();
106         if (CommonFeatures.DVR.isEnabled(context)) {
107             mDvrDataManager = tvSingletons.getDvrDataManager();
108         } else {
109             mDvrDataManager = null;
110         }
111         mRecommender = recommender;
112         mMinCount = minCount;
113         mMaxCount = maxCount;
114         setHasStableIds(true);
115     }
116 
117     @Override
getItemViewType(int position)118     public int getItemViewType(int position) {
119         return getItemList().get(position).getLayoutId();
120     }
121 
122     @Override
getLayoutResId(int viewType)123     protected int getLayoutResId(int viewType) {
124         return viewType;
125     }
126 
127     @Override
getItemId(int position)128     public long getItemId(int position) {
129         return getItemList().get(position).getItemId();
130     }
131 
132     @Override
onBindViewHolder(MyViewHolder viewHolder, int position)133     public void onBindViewHolder(MyViewHolder viewHolder, int position) {
134         int viewType = getItemViewType(position);
135         if (viewType == R.layout.menu_card_guide) {
136             viewHolder.itemView.setOnClickListener(mGuideOnClickListener);
137         } else if (viewType == R.layout.menu_card_setup) {
138             viewHolder.itemView.setOnClickListener(mSetupOnClickListener);
139         } else if (viewType == R.layout.menu_card_app_link) {
140             viewHolder.itemView.setOnClickListener(mAppLinkOnClickListener);
141         } else if (viewType == R.layout.menu_card_dvr) {
142             viewHolder.itemView.setOnClickListener(mDvrOnClickListener);
143             SimpleCardView view = (SimpleCardView) viewHolder.itemView;
144             view.setText(R.string.channels_item_dvr);
145         } else {
146             viewHolder.itemView.setTag(getItemList().get(position).getChannel());
147             viewHolder.itemView.setOnClickListener(mChannelOnClickListener);
148         }
149         super.onBindViewHolder(viewHolder, position);
150     }
151 
152     @Override
update()153     public void update() {
154         if (getItemCount() == 0) {
155             createItems();
156         } else {
157             updateItems();
158         }
159     }
160 
createItems()161     private void createItems() {
162         List<ChannelsRowItem> items = new ArrayList<>();
163         items.add(ChannelsRowItem.GUIDE_ITEM);
164         if (needToShowSetupItem()) {
165             items.add(ChannelsRowItem.SETUP_ITEM);
166         }
167         if (needToShowDvrItem()) {
168             items.add(ChannelsRowItem.DVR_ITEM);
169         }
170         if (needToShowAppLinkItem()) {
171             ChannelsRowItem.APP_LINK_ITEM.setChannel(
172                     new ChannelImpl.Builder(getMainActivity().getCurrentChannel()).build());
173             items.add(ChannelsRowItem.APP_LINK_ITEM);
174         }
175         for (Channel channel : getRecentChannels()) {
176             items.add(new ChannelsRowItem(channel, R.layout.menu_card_channel));
177         }
178         setItemList(items);
179     }
180 
updateItems()181     private void updateItems() {
182         List<ChannelsRowItem> items = getItemList();
183         // The current index of the item list to iterate. It starts from 1 because the first item
184         // (GUIDE) is always visible and not updated.
185         int currentIndex = 1;
186         if (updateItem(needToShowSetupItem(), ChannelsRowItem.SETUP_ITEM, currentIndex)) {
187             ++currentIndex;
188         }
189         if (updateItem(needToShowDvrItem(), ChannelsRowItem.DVR_ITEM, currentIndex)) {
190             ++currentIndex;
191         }
192         if (updateItem(needToShowAppLinkItem(), ChannelsRowItem.APP_LINK_ITEM, currentIndex)) {
193             if (!getMainActivity()
194                     .getCurrentChannel()
195                     .hasSameReadOnlyInfo(ChannelsRowItem.APP_LINK_ITEM.getChannel())) {
196                 ChannelsRowItem.APP_LINK_ITEM.setChannel(
197                         new ChannelImpl.Builder(getMainActivity().getCurrentChannel()).build());
198                 notifyItemChanged(currentIndex);
199             }
200             ++currentIndex;
201         }
202         int numOldChannels = items.size() - currentIndex;
203         if (numOldChannels > 0) {
204             while (items.size() > currentIndex) {
205                 items.remove(items.size() - 1);
206             }
207             notifyItemRangeRemoved(currentIndex, numOldChannels);
208         }
209         for (Channel channel : getRecentChannels()) {
210             items.add(new ChannelsRowItem(channel, R.layout.menu_card_channel));
211         }
212         int numNewChannels = items.size() - currentIndex;
213         if (numNewChannels > 0) {
214             notifyItemRangeInserted(currentIndex, numNewChannels);
215         }
216     }
217 
218     /** Returns {@code true} if the item should be shown. */
updateItem(boolean needToShow, ChannelsRowItem item, int index)219     private boolean updateItem(boolean needToShow, ChannelsRowItem item, int index) {
220         List<ChannelsRowItem> items = getItemList();
221         boolean isItemInList = index < items.size() && item.equals(items.get(index));
222         if (needToShow && !isItemInList) {
223             items.add(index, item);
224             notifyItemInserted(index);
225         } else if (!needToShow && isItemInList) {
226             items.remove(index);
227             notifyItemRemoved(index);
228         }
229         return needToShow;
230     }
231 
232     private boolean needToShowSetupItem() {
233         TvSingletons singletons = TvSingletons.getSingletons(mContext);
234         TvInputManagerHelper inputManager = singletons.getTvInputManagerHelper();
235         return singletons.getSetupUtils().hasNewInput(inputManager);
236     }
237 
238     private boolean needToShowDvrItem() {
239         TvInputManagerHelper inputManager =
240                 TvSingletons.getSingletons(mContext).getTvInputManagerHelper();
241         if (mDvrDataManager != null) {
242             for (TvInputInfo info : inputManager.getTvInputInfos(true, true)) {
243                 if (info.canRecord()) {
244                     return true;
245                 }
246             }
247         }
248         return false;
249     }
250 
251     private boolean needToShowAppLinkItem() {
252         TvInputManagerHelper inputManager =
253                 TvSingletons.getSingletons(mContext).getTvInputManagerHelper();
254         Channel currentChannel = getMainActivity().getCurrentChannel();
255         return currentChannel != null
256                 && currentChannel.getAppLinkType(mContext) != Channel.APP_LINK_TYPE_NONE
257                 // Sometimes applicationInfo can be null. b/28932537
258                 && inputManager.getTvInputAppInfo(currentChannel.getInputId()) != null;
259     }
260 
261     private List<Channel> getRecentChannels() {
262         List<Channel> channelList = new ArrayList<>();
263         long currentChannelId = getMainActivity().getCurrentChannelId();
264         ArrayDeque<Long> recentChannels = getMainActivity().getRecentChannels();
265         // Add the last watched channel as the first one.
266         for (long channelId : recentChannels) {
267             if (addChannelToList(
268                     channelList, mRecommender.getChannel(channelId), currentChannelId)) {
269                 break;
270             }
271         }
272         // Add the recommended channels.
273         for (Channel channel : mRecommender.recommendChannels(mMaxCount)) {
274             if (channelList.size() >= mMaxCount) {
275                 break;
276             }
277             addChannelToList(channelList, channel, currentChannelId);
278         }
279         // If the number of recommended channels is not enough, add more from the recent channel
280         // list.
281         for (long channelId : recentChannels) {
282             if (channelList.size() >= mMinCount) {
283                 break;
284             }
addChannelToList(channelList, mRecommender.getChannel(channelId), currentChannelId)285             addChannelToList(channelList, mRecommender.getChannel(channelId), currentChannelId);
286         }
287         return channelList;
288     }
289 
addChannelToList( List<Channel> channelList, Channel channel, long currentChannelId)290     private static boolean addChannelToList(
291             List<Channel> channelList, Channel channel, long currentChannelId) {
292         if (channel == null
293                 || channel.getId() == currentChannelId
294                 || channelList.contains(channel)
295                 || !channel.isBrowsable()) {
296             return false;
297         }
298         channelList.add(channel);
299         return true;
300     }
301 }
302