Home | History | Annotate | Download | only in menu
      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 
    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
    118     public int getItemViewType(int position) {
    119         return getItemList().get(position).getLayoutId();
    120     }
    121 
    122     @Override
    123     protected int getLayoutResId(int viewType) {
    124         return viewType;
    125     }
    126 
    127     @Override
    128     public long getItemId(int position) {
    129         return getItemList().get(position).getItemId();
    130     }
    131 
    132     @Override
    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
    153     public void update() {
    154         if (getItemCount() == 0) {
    155             createItems();
    156         } else {
    157             updateItems();
    158         }
    159     }
    160 
    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 
    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. */
    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             }
    285             addChannelToList(channelList, mRecommender.getChannel(channelId), currentChannelId);
    286         }
    287         return channelList;
    288     }
    289 
    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