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.ui.sidepanel; 18 19 import android.content.Context; 20 import android.content.SharedPreferences; 21 import android.media.tv.TvContract.Channels; 22 import android.os.Bundle; 23 import android.support.v17.leanback.widget.VerticalGridView; 24 import android.view.KeyEvent; 25 import android.view.LayoutInflater; 26 import android.view.View; 27 import android.view.ViewGroup; 28 import android.widget.TextView; 29 30 import com.android.tv.MainActivity; 31 import com.android.tv.R; 32 import com.android.tv.common.SharedPreferencesUtils; 33 import com.android.tv.data.Channel; 34 import com.android.tv.data.ChannelNumber; 35 import com.android.tv.ui.OnRepeatedKeyInterceptListener; 36 import com.android.tv.util.TvInputManagerHelper; 37 import com.android.tv.util.Utils; 38 39 import java.util.ArrayList; 40 import java.util.Collections; 41 import java.util.Comparator; 42 import java.util.Iterator; 43 import java.util.List; 44 45 public class CustomizeChannelListFragment extends SideFragment { 46 private static final int GROUP_BY_SOURCE = 0; 47 private static final int GROUP_BY_HD_SD = 1; 48 private static final String TRACKER_LABEL = "customize channel list"; 49 50 private static final String PREF_KEY_GROUP_SETTINGS = "pref_key_group_settigns"; 51 52 private final List<Channel> mChannels = new ArrayList<>(); 53 private long mInitialChannelId = Channel.INVALID_ID; 54 private long mLastFocusedChannelId = Channel.INVALID_ID; 55 56 private static Integer sGroupingType; 57 private TvInputManagerHelper mInputManager; 58 private Channel.DefaultComparator mChannelComparator; 59 private boolean mGroupByFragmentRunning; 60 61 private final List<Item> mItems = new ArrayList<>(); 62 63 @Override 64 public void onCreate(Bundle savedInstanceState) { 65 super.onCreate(savedInstanceState); 66 mInputManager = getMainActivity().getTvInputManagerHelper(); 67 mInitialChannelId = getMainActivity().getCurrentChannelId(); 68 mChannelComparator = new Channel.DefaultComparator(getActivity(), mInputManager); 69 if (sGroupingType == null) { 70 SharedPreferences sharedPreferences = getContext().getSharedPreferences( 71 SharedPreferencesUtils.SHARED_PREF_UI_SETTINGS, Context.MODE_PRIVATE); 72 sGroupingType = sharedPreferences.getInt(PREF_KEY_GROUP_SETTINGS, GROUP_BY_SOURCE); 73 } 74 } 75 76 @Override 77 public View onCreateView(LayoutInflater inflater, ViewGroup container, 78 Bundle savedInstanceState) { 79 View view = super.onCreateView(inflater, container, savedInstanceState); 80 VerticalGridView listView = (VerticalGridView) view.findViewById(R.id.side_panel_list); 81 listView.setOnKeyInterceptListener(new OnRepeatedKeyInterceptListener(listView) { 82 @Override 83 public boolean onInterceptKeyEvent(KeyEvent event) { 84 // In order to send tune operation once for continuous channel up/down events, 85 // we only call the moveToChannel method on ACTION_UP event of channel switch keys. 86 if (event.getAction() == KeyEvent.ACTION_UP) { 87 switch (event.getKeyCode()) { 88 case KeyEvent.KEYCODE_DPAD_UP: 89 case KeyEvent.KEYCODE_DPAD_DOWN: 90 if (mLastFocusedChannelId != Channel.INVALID_ID) { 91 getMainActivity().tuneToChannel( 92 getChannelDataManager().getChannel(mLastFocusedChannelId)); 93 } 94 break; 95 } 96 } 97 return super.onInterceptKeyEvent(event); 98 } 99 }); 100 101 if (!mGroupByFragmentRunning) { 102 getMainActivity().startShrunkenTvView(false, true); 103 104 int initialChannelPosition = INVALID_POSITION; 105 int i = 0; 106 for (Item item : mItems) { 107 if (item instanceof ChannelItem 108 && ((ChannelItem) item).getChannel().getId() == mInitialChannelId) { 109 initialChannelPosition = i; 110 break; 111 } 112 ++i; 113 } 114 if (initialChannelPosition != INVALID_POSITION) { 115 setSelectedPosition(initialChannelPosition); 116 } else { 117 setSelectedPosition(0); 118 } 119 mLastFocusedChannelId = mInitialChannelId; 120 MainActivity tvActivity = getMainActivity(); 121 if (mLastFocusedChannelId != Channel.INVALID_ID && 122 mLastFocusedChannelId != tvActivity.getCurrentChannelId()) { 123 tvActivity.tuneToChannel(getChannelDataManager().getChannel(mLastFocusedChannelId)); 124 } 125 } 126 mGroupByFragmentRunning = false; 127 return view; 128 } 129 130 @Override 131 public void onDestroyView() { 132 getChannelDataManager().applyUpdatedValuesToDb(); 133 super.onDestroyView(); 134 } 135 136 @Override 137 public void onDestroy() { 138 super.onDestroy(); 139 getMainActivity().endShrunkenTvView(); 140 } 141 142 @Override 143 protected String getTitle() { 144 return getString(R.string.side_panel_title_edit_channels_for_an_input); 145 } 146 147 @Override 148 public String getTrackerLabel() { 149 return TRACKER_LABEL; 150 } 151 152 @Override 153 protected List<Item> getItemList() { 154 mItems.clear(); 155 mChannels.clear(); 156 mChannels.addAll(getChannelDataManager().getChannelList()); 157 if (sGroupingType == GROUP_BY_SOURCE) { 158 addItemForGroupBySource(mItems); 159 } else { 160 // GROUP_BY_HD_SD 161 addItemForGroupByHdSd(mItems); 162 } 163 return mItems; 164 } 165 166 private void cleanUpOneChannelGroupItem(List<Item> items) { 167 Iterator<Item> iter = items.iterator(); 168 while (iter.hasNext()) { 169 Item item = iter.next(); 170 if (item instanceof SelectGroupItem) { 171 SelectGroupItem selectGroupItem = (SelectGroupItem) item; 172 if (selectGroupItem.mChannelItemsInGroup.size() == 1) { 173 selectGroupItem.mChannelItemsInGroup.get(0).mSelectGroupItem = null; 174 iter.remove(); 175 } 176 } 177 } 178 } 179 180 private void addItemForGroupBySource(List<Item> items) { 181 items.add(new GroupBySubMenu(getString(R.string.edit_channels_group_by_sources))); 182 SelectGroupItem selectGroupItem = null; 183 ArrayList<Channel> channels = new ArrayList<>(mChannels); 184 Collections.sort(channels, mChannelComparator); 185 186 String inputId = null; 187 for (Channel channel: channels) { 188 if (!channel.getInputId().equals(inputId)) { 189 inputId = channel.getInputId(); 190 String inputLabel = Utils.loadLabel(getActivity(), 191 mInputManager.getTvInputInfo(inputId)); 192 items.add(new DividerItem(inputLabel)); 193 selectGroupItem = new SelectGroupItem(); 194 items.add(selectGroupItem); 195 } 196 ChannelItem channelItem = new ChannelItem(channel, selectGroupItem); 197 items.add(channelItem); 198 selectGroupItem.addChannelItem(channelItem); 199 } 200 cleanUpOneChannelGroupItem(items); 201 } 202 203 private void addItemForGroupByHdSd(List<Item> items) { 204 items.add(new GroupBySubMenu(getString(R.string.edit_channels_group_by_hd_sd))); 205 SelectGroupItem selectGroupItem = null; 206 ArrayList<Channel> channels = new ArrayList<>(mChannels); 207 Collections.sort(channels, new Comparator<Channel>() { 208 @Override 209 public int compare(Channel lhs, Channel rhs) { 210 boolean lhsHd = isHdChannel(lhs); 211 boolean rhsHd = isHdChannel(rhs); 212 if (lhsHd == rhsHd) { 213 return ChannelNumber.compare(lhs.getDisplayNumber(), rhs.getDisplayNumber()); 214 } else { 215 return lhsHd ? -1 : 1; 216 } 217 } 218 }); 219 220 Boolean isHdGroup = null; 221 for (Channel channel: channels) { 222 boolean isHd = isHdChannel(channel); 223 if (isHdGroup == null || isHd != isHdGroup) { 224 isHdGroup = isHd; 225 items.add(new DividerItem(isHd 226 ? getString(R.string.edit_channels_group_divider_for_hd) 227 : getString(R.string.edit_channels_group_divider_for_sd))); 228 selectGroupItem = new SelectGroupItem(); 229 items.add(selectGroupItem); 230 } 231 ChannelItem channelItem = new ChannelItem(channel, selectGroupItem); 232 items.add(channelItem); 233 selectGroupItem.addChannelItem(channelItem); 234 } 235 cleanUpOneChannelGroupItem(items); 236 } 237 238 private static boolean isHdChannel(Channel channel) { 239 String videoFormat = channel.getVideoFormat(); 240 return videoFormat != null && 241 (Channels.VIDEO_FORMAT_720P.equals(videoFormat) 242 || Channels.VIDEO_FORMAT_1080I.equals(videoFormat) 243 || Channels.VIDEO_FORMAT_1080P.equals(videoFormat) 244 || Channels.VIDEO_FORMAT_2160P.equals(videoFormat) 245 || Channels.VIDEO_FORMAT_4320P.equals(videoFormat)); 246 } 247 248 private class SelectGroupItem extends ActionItem { 249 private final List<ChannelItem> mChannelItemsInGroup = new ArrayList<>(); 250 private TextView mTextView; 251 private boolean mAllChecked; 252 253 public SelectGroupItem() { 254 super(null); 255 } 256 257 private void addChannelItem(ChannelItem channelItem) { 258 mChannelItemsInGroup.add(channelItem); 259 } 260 261 @Override 262 protected void onBind(View view) { 263 super.onBind(view); 264 mTextView = (TextView) view.findViewById(R.id.title); 265 } 266 267 @Override 268 protected void onUpdate() { 269 super.onUpdate(); 270 mAllChecked = true; 271 for (ChannelItem channelItem : mChannelItemsInGroup) { 272 if (!channelItem.getChannel().isBrowsable()) { 273 mAllChecked = false; 274 break; 275 } 276 } 277 mTextView.setText(getString(mAllChecked 278 ? R.string.edit_channels_item_deselect_group 279 : R.string.edit_channels_item_select_group)); 280 } 281 282 @Override 283 protected void onSelected() { 284 for (ChannelItem channelItem : mChannelItemsInGroup) { 285 Channel channel = channelItem.getChannel(); 286 if (channel.isBrowsable() == mAllChecked) { 287 getChannelDataManager().updateBrowsable(channel.getId(), !mAllChecked, true); 288 channelItem.notifyUpdated(); 289 } 290 } 291 getChannelDataManager().notifyChannelBrowsableChanged(); 292 mAllChecked = !mAllChecked; 293 mTextView.setText(getString(mAllChecked 294 ? R.string.edit_channels_item_deselect_group 295 : R.string.edit_channels_item_select_group)); 296 } 297 } 298 299 private class ChannelItem extends ChannelCheckItem { 300 private SelectGroupItem mSelectGroupItem; 301 302 public ChannelItem(Channel channel, SelectGroupItem selectGroupItem) { 303 super(channel, getChannelDataManager(), getProgramDataManager()); 304 mSelectGroupItem = selectGroupItem; 305 } 306 307 @Override 308 protected void onUpdate() { 309 super.onUpdate(); 310 setChecked(getChannel().isBrowsable()); 311 } 312 313 @Override 314 protected void onSelected() { 315 super.onSelected(); 316 getChannelDataManager().updateBrowsable(getChannel().getId(), isChecked()); 317 if (mSelectGroupItem != null) { 318 mSelectGroupItem.notifyUpdated(); 319 } 320 } 321 322 @Override 323 protected void onFocused() { 324 super.onFocused(); 325 mLastFocusedChannelId = getChannel().getId(); 326 } 327 } 328 329 public static class GroupByFragment extends SideFragment { 330 @Override 331 protected String getTitle() { 332 return getString(R.string.side_panel_title_group_by); 333 } 334 @Override 335 public String getTrackerLabel() { 336 return GroupBySubMenu.TRACKER_LABEL; 337 } 338 339 @Override 340 protected List<Item> getItemList() { 341 List<Item> items = new ArrayList<>(); 342 items.add(new RadioButtonItem( 343 getString(R.string.edit_channels_group_by_sources)) { 344 @Override 345 protected void onSelected() { 346 super.onSelected(); 347 setGroupingType(GROUP_BY_SOURCE); 348 closeFragment(); 349 } 350 }); 351 items.add(new RadioButtonItem( 352 getString(R.string.edit_channels_group_by_hd_sd)) { 353 @Override 354 protected void onSelected() { 355 super.onSelected(); 356 setGroupingType(GROUP_BY_HD_SD); 357 closeFragment(); 358 } 359 }); 360 ((RadioButtonItem) items.get(sGroupingType)).setChecked(true); 361 return items; 362 } 363 364 private void setGroupingType(int groupingType) { 365 sGroupingType = groupingType; 366 SharedPreferences sharedPreferences = getContext().getSharedPreferences( 367 SharedPreferencesUtils.SHARED_PREF_UI_SETTINGS, Context.MODE_PRIVATE); 368 sharedPreferences.edit().putInt(PREF_KEY_GROUP_SETTINGS, groupingType).apply(); 369 } 370 } 371 372 private class GroupBySubMenu extends SubMenuItem { 373 private static final String TRACKER_LABEL = "Group by"; 374 public GroupBySubMenu(String description) { 375 super(getString(R.string.edit_channels_item_group_by), description, 376 getMainActivity().getOverlayManager().getSideFragmentManager()); 377 } 378 379 @Override 380 protected SideFragment getFragment() { 381 return new GroupByFragment(); 382 } 383 384 @Override 385 protected void onSelected() { 386 mGroupByFragmentRunning = true; 387 super.onSelected(); 388 } 389 } 390 } 391