• 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 
24 import com.android.tv.ApplicationSingletons;
25 import com.android.tv.R;
26 import com.android.tv.TvApplication;
27 import com.android.tv.analytics.Tracker;
28 import com.android.tv.common.feature.CommonFeatures;
29 import com.android.tv.data.Channel;
30 import com.android.tv.dvr.DvrDataManager;
31 import com.android.tv.recommendation.Recommender;
32 import com.android.tv.util.SetupUtils;
33 import com.android.tv.util.TvInputManagerHelper;
34 
35 import java.util.ArrayDeque;
36 import java.util.ArrayList;
37 import java.util.List;
38 
39 /**
40  * An adapter of the Channels row.
41  */
42 public class ChannelsRowAdapter extends ItemListRowView.ItemListAdapter<ChannelsRowItem> {
43     // There are four special cards: guide, setup, dvr, applink.
44     private static final int SIZE_OF_VIEW_TYPE = 5;
45 
46     private final Context mContext;
47     private final Tracker mTracker;
48     private final Recommender mRecommender;
49     private final DvrDataManager mDvrDataManager;
50     private final int mMaxCount;
51     private final int mMinCount;
52 
53     private final View.OnClickListener mGuideOnClickListener = new View.OnClickListener() {
54         @Override
55         public void onClick(View view) {
56             mTracker.sendMenuClicked(R.string.channels_item_program_guide);
57             getMainActivity().getOverlayManager().showProgramGuide();
58         }
59     };
60 
61     private final View.OnClickListener mSetupOnClickListener = new View.OnClickListener() {
62         @Override
63         public void onClick(View view) {
64             mTracker.sendMenuClicked(R.string.channels_item_setup);
65             getMainActivity().getOverlayManager().showSetupFragment();
66         }
67     };
68 
69     private final View.OnClickListener mDvrOnClickListener = new View.OnClickListener() {
70         @Override
71         public void onClick(View view) {
72             mTracker.sendMenuClicked(R.string.channels_item_dvr);
73             getMainActivity().getOverlayManager().showDvrManager();
74         }
75     };
76 
77     private final View.OnClickListener mAppLinkOnClickListener = new View.OnClickListener() {
78         @Override
79         public void onClick(View view) {
80             mTracker.sendMenuClicked(R.string.channels_item_app_link);
81             Intent intent = ((AppLinkCardView) view).getIntent();
82             if (intent != null) {
83                 getMainActivity().startActivitySafe(intent);
84             }
85         }
86     };
87 
88     private final View.OnClickListener mChannelOnClickListener = 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 might be
92             // sensitive.
93             mTracker.sendMenuClicked(R.string.menu_title_channels);
94             getMainActivity().tuneToChannel((Channel) view.getTag());
95             getMainActivity().hideOverlaysForTune();
96         }
97     };
98 
ChannelsRowAdapter(Context context, Recommender recommender, int minCount, int maxCount)99     public ChannelsRowAdapter(Context context, Recommender recommender,
100             int minCount, int maxCount) {
101         super(context);
102         mContext = context;
103         ApplicationSingletons appSingletons = TvApplication.getSingletons(context);
104         mTracker = appSingletons.getTracker();
105         if (CommonFeatures.DVR.isEnabled(context)) {
106             mDvrDataManager = appSingletons.getDvrDataManager();
107         } else {
108             mDvrDataManager = null;
109         }
110         mRecommender = recommender;
111         mMinCount = minCount;
112         mMaxCount = maxCount;
113         setHasStableIds(true);
114     }
115 
116     @Override
getItemViewType(int position)117     public int getItemViewType(int position) {
118         return getItemList().get(position).getLayoutId();
119     }
120 
121     @Override
getLayoutResId(int viewType)122     protected int getLayoutResId(int viewType) {
123         return viewType;
124     }
125 
126     @Override
getItemId(int position)127     public long getItemId(int position) {
128         return getItemList().get(position).getItemId();
129     }
130 
131     @Override
onBindViewHolder(MyViewHolder viewHolder, int position)132     public void onBindViewHolder(MyViewHolder viewHolder, int position) {
133         int viewType = getItemViewType(position);
134         if (viewType == R.layout.menu_card_guide) {
135             viewHolder.itemView.setOnClickListener(mGuideOnClickListener);
136         } else if (viewType == R.layout.menu_card_setup) {
137             viewHolder.itemView.setOnClickListener(mSetupOnClickListener);
138         } else if (viewType == R.layout.menu_card_app_link) {
139             viewHolder.itemView.setOnClickListener(mAppLinkOnClickListener);
140         } else if (viewType == R.layout.menu_card_dvr) {
141             viewHolder.itemView.setOnClickListener(mDvrOnClickListener);
142             SimpleCardView view = (SimpleCardView) viewHolder.itemView;
143             view.setText(R.string.channels_item_dvr);
144         } else {
145             viewHolder.itemView.setTag(getItemList().get(position).getChannel());
146             viewHolder.itemView.setOnClickListener(mChannelOnClickListener);
147         }
148         super.onBindViewHolder(viewHolder, position);
149     }
150 
151     @Override
update()152     public void update() {
153         if (getItemCount() == 0) {
154             createItems();
155         } else {
156             updateItems();
157         }
158     }
159 
createItems()160     private void createItems() {
161         List<ChannelsRowItem> items = new ArrayList<>();
162         items.add(ChannelsRowItem.GUIDE_ITEM);
163         if (needToShowSetupItem()) {
164             items.add(ChannelsRowItem.SETUP_ITEM);
165         }
166         if (needToShowDvrItem()) {
167             items.add(ChannelsRowItem.DVR_ITEM);
168         }
169         if (needToShowAppLinkItem()) {
170             ChannelsRowItem.APP_LINK_ITEM.setChannel(
171                     new Channel.Builder(getMainActivity().getCurrentChannel()).build());
172             items.add(ChannelsRowItem.APP_LINK_ITEM);
173         }
174         for (Channel channel : getRecentChannels()) {
175             items.add(new ChannelsRowItem(channel, R.layout.menu_card_channel));
176         }
177         setItemList(items);
178     }
179 
updateItems()180     private void updateItems() {
181         List<ChannelsRowItem> items = getItemList();
182         // The current index of the item list to iterate. It starts from 1 because the first item
183         // (GUIDE) is always visible and not updated.
184         int currentIndex = 1;
185         if (updateItem(needToShowSetupItem(), ChannelsRowItem.SETUP_ITEM, currentIndex)) {
186             ++currentIndex;
187         }
188         if (updateItem(needToShowDvrItem(), ChannelsRowItem.DVR_ITEM, currentIndex)) {
189             ++currentIndex;
190         }
191         if (updateItem(needToShowAppLinkItem(), ChannelsRowItem.APP_LINK_ITEM, currentIndex)) {
192             if (!getMainActivity().getCurrentChannel()
193                     .hasSameReadOnlyInfo(ChannelsRowItem.APP_LINK_ITEM.getChannel())) {
194                 ChannelsRowItem.APP_LINK_ITEM.setChannel(
195                         new Channel.Builder(getMainActivity().getCurrentChannel()).build());
196                 notifyItemChanged(currentIndex);
197             }
198             ++currentIndex;
199         }
200         int numOldChannels = items.size() - currentIndex;
201         if (numOldChannels > 0) {
202             while (items.size() > currentIndex) {
203                 items.remove(items.size() - 1);
204             }
205             notifyItemRangeRemoved(currentIndex, numOldChannels);
206         }
207         for (Channel channel : getRecentChannels()) {
208             items.add(new ChannelsRowItem(channel, R.layout.menu_card_channel));
209         }
210         int numNewChannels = items.size() - currentIndex;
211         if (numNewChannels > 0) {
212             notifyItemRangeInserted(currentIndex, numNewChannels);
213         }
214     }
215 
216     /**
217      * Returns {@code true} if the item should be shown.
218      */
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         TvInputManagerHelper inputManager =
234                 TvApplication.getSingletons(mContext).getTvInputManagerHelper();
235         return SetupUtils.getInstance(mContext).hasNewInput(inputManager);
236     }
237 
238     private boolean needToShowDvrItem() {
239         TvInputManagerHelper inputManager =
240                 TvApplication.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                 TvApplication.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 || channel.getId() == currentChannelId
293                 || channelList.contains(channel) || !channel.isBrowsable()) {
294             return false;
295         }
296         channelList.add(channel);
297         return true;
298     }
299 }
300