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.os.Build;
23 import android.support.v4.os.BuildCompat;
24 import android.view.View;
25 
26 import com.android.tv.MainActivity;
27 import com.android.tv.R;
28 import com.android.tv.TvApplication;
29 import com.android.tv.analytics.Tracker;
30 import com.android.tv.common.feature.CommonFeatures;
31 import com.android.tv.data.Channel;
32 import com.android.tv.recommendation.Recommender;
33 import com.android.tv.util.SetupUtils;
34 import com.android.tv.util.TvInputManagerHelper;
35 import com.android.tv.util.Utils;
36 
37 import java.util.ArrayList;
38 import java.util.List;
39 
40 /**
41  * An adapter of the Channels row.
42  */
43 public class ChannelsRowAdapter extends ItemListRowView.ItemListAdapter<Channel> {
44     // There are four special cards: guide, setup, dvr, record, applink.
45     private static final int SIZE_OF_VIEW_TYPE = 5;
46 
47     private final Context mContext;
48     private final Tracker mTracker;
49     private final Recommender mRecommender;
50     private final int mMaxCount;
51     private final int mMinCount;
52     private final boolean mDvrFeatureEnabled;
53     private final int[] mViewType = new int[SIZE_OF_VIEW_TYPE];
54 
55     private final View.OnClickListener mGuideOnClickListener = new View.OnClickListener() {
56         @Override
57         public void onClick(View view) {
58             mTracker.sendMenuClicked(R.string.channels_item_program_guide);
59             getMainActivity().getOverlayManager().showProgramGuide();
60         }
61     };
62 
63     private final View.OnClickListener mSetupOnClickListener = new View.OnClickListener() {
64         @Override
65         public void onClick(View view) {
66             mTracker.sendMenuClicked(R.string.channels_item_setup);
67             getMainActivity().getOverlayManager().showSetupFragment();
68         }
69     };
70 
71     private final View.OnClickListener mDvrOnClickListener = new View.OnClickListener() {
72         @Override
73         public void onClick(View view) {
74             Utils.showToastMessageForDeveloperFeature(view.getContext());
75             mTracker.sendMenuClicked(R.string.channels_item_dvr);
76             getMainActivity().getOverlayManager().showDvrManager();
77         }
78     };
79 
80     private final View.OnClickListener mRecordOnClickListener = new View.OnClickListener() {
81         @Override
82         public void onClick(View view) {
83             Utils.showToastMessageForDeveloperFeature(view.getContext());
84             RecordCardView v = ((RecordCardView) view);
85             boolean isRecording = v.isRecording();
86             mTracker.sendMenuClicked(isRecording ? R.string.channels_item_record_start
87                     : R.string.channels_item_record_stop);
88             if (!isRecording) {
89                 v.startRecording();
90             } else {
91                 v.stopRecording();
92             }
93         }
94     };
95 
96     private final View.OnClickListener mAppLinkOnClickListener = new View.OnClickListener() {
97         @Override
98         public void onClick(View view) {
99             mTracker.sendMenuClicked(R.string.channels_item_app_link);
100             Intent intent = ((AppLinkCardView) view).getIntent();
101             if (intent != null) {
102                 getMainActivity().startActivitySafe(intent);
103             }
104         }
105     };
106 
107     private final View.OnClickListener mChannelOnClickListener = new View.OnClickListener() {
108         @Override
109         public void onClick(View view) {
110             // Always send the label "Channels" because the channel ID or name or number might be
111             // sensitive.
112             mTracker.sendMenuClicked(R.string.menu_title_channels);
113             getMainActivity().tuneToChannel((Channel) view.getTag());
114             getMainActivity().hideOverlaysForTune();
115         }
116     };
117 
ChannelsRowAdapter(Context context, Recommender recommender, int minCount, int maxCount)118     public ChannelsRowAdapter(Context context, Recommender recommender,
119             int minCount, int maxCount) {
120         super(context);
121         mTracker = TvApplication.getSingletons(context).getTracker();
122         mContext = context;
123         mRecommender = recommender;
124         mMinCount = minCount;
125         mMaxCount = maxCount;
126         mDvrFeatureEnabled = CommonFeatures.DVR.isEnabled(mContext) && BuildCompat.isAtLeastN();
127     }
128 
129     @Override
getItemViewType(int position)130     public int getItemViewType(int position) {
131         if (position >= SIZE_OF_VIEW_TYPE) {
132             return R.layout.menu_card_channel;
133         }
134         return mViewType[position];
135     }
136 
137     @Override
getLayoutResId(int viewType)138     protected int getLayoutResId(int viewType) {
139         return viewType;
140     }
141 
142     @Override
onBindViewHolder(MyViewHolder viewHolder, int position)143     public void onBindViewHolder(MyViewHolder viewHolder, int position) {
144         super.onBindViewHolder(viewHolder, position);
145 
146         int viewType = getItemViewType(position);
147         if (viewType == R.layout.menu_card_guide) {
148             viewHolder.itemView.setOnClickListener(mGuideOnClickListener);
149         } else if (viewType == R.layout.menu_card_setup) {
150             viewHolder.itemView.setOnClickListener(mSetupOnClickListener);
151         } else if (viewType == R.layout.menu_card_app_link) {
152             viewHolder.itemView.setOnClickListener(mAppLinkOnClickListener);
153         } else if (viewType == R.layout.menu_card_dvr) {
154             viewHolder.itemView.setOnClickListener(mDvrOnClickListener);
155         } else if (viewType == R.layout.menu_card_record) {
156             viewHolder.itemView.setOnClickListener(mRecordOnClickListener);
157         } else {
158             viewHolder.itemView.setTag(getItemList().get(position));
159             viewHolder.itemView.setOnClickListener(mChannelOnClickListener);
160         }
161     }
162 
163     @Override
update()164     public void update() {
165         List<Channel> channelList = new ArrayList<>();
166         Channel dummyChannel = new Channel.Builder().build();
167         // For guide item
168         channelList.add(dummyChannel);
169         // For setup item
170         TvInputManagerHelper inputManager = TvApplication.getSingletons(mContext)
171                 .getTvInputManagerHelper();
172         boolean showSetupCard = SetupUtils.getInstance(mContext).hasNewInput(inputManager);
173         Channel currentChannel = ((MainActivity) mContext).getCurrentChannel();
174         boolean showAppLinkCard = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
175                 && currentChannel != null
176                 && currentChannel.getAppLinkType(mContext) != Channel.APP_LINK_TYPE_NONE;
177         boolean showDvrCard = false;
178         boolean showRecordCard = false;
179         if (mDvrFeatureEnabled) {
180             for (TvInputInfo info : inputManager.getTvInputInfos(true, true)) {
181                 if (info.canRecord()) {
182                     showDvrCard = true;
183                     break;
184                 }
185             }
186             if (currentChannel != null && currentChannel.getInputId() != null) {
187                 TvInputInfo inputInfo = inputManager.getTvInputInfo(currentChannel.getInputId());
188                 if ((inputInfo.canRecord() && inputInfo.getTunerCount() > 1)) {
189                     showRecordCard = true;
190                 }
191             }
192         }
193 
194         mViewType[0] = R.layout.menu_card_guide;
195         int index = 1;
196         if (showSetupCard) {
197             channelList.add(dummyChannel);
198             mViewType[index++] = R.layout.menu_card_setup;
199         }
200         if (showDvrCard) {
201             channelList.add(dummyChannel);
202             mViewType[index++] = R.layout.menu_card_dvr;
203         }
204         if (showRecordCard) {
205             channelList.add(currentChannel);
206             mViewType[index++] = R.layout.menu_card_record;
207         }
208         if (showAppLinkCard) {
209             channelList.add(currentChannel);
210             mViewType[index++] = R.layout.menu_card_app_link;
211         }
212         for ( ; index < mViewType.length; ++index) {
213             mViewType[index] = R.layout.menu_card_channel;
214         }
215         channelList.addAll(getRecentChannels());
216         setItemList(channelList);
217     }
218 
getRecentChannels()219     private List<Channel> getRecentChannels() {
220         List<Channel> channelList = new ArrayList<>();
221         for (Channel channel : mRecommender.recommendChannels(mMaxCount)) {
222             if (channel.isBrowsable()) {
223                 channelList.add(channel);
224             }
225         }
226         int count = channelList.size();
227         // If the number of recommended channels is not enough, add more from the recent channel
228         // list.
229         if (count < mMinCount && mContext instanceof MainActivity) {
230             for (long channelId : ((MainActivity) mContext).getRecentChannels()) {
231                 Channel channel = mRecommender.getChannel(channelId);
232                 if (channel == null || channelList.contains(channel)
233                         || !channel.isBrowsable()) {
234                    continue;
235                 }
236                 channelList.add(channel);
237                 if (++count >= mMinCount) {
238                     break;
239                 }
240             }
241         }
242         return channelList;
243     }
244 }
245