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.parentalcontrols; 18 19 import android.database.ContentObserver; 20 import android.media.tv.TvContract; 21 import android.net.Uri; 22 import android.os.Build.VERSION; 23 import android.os.Build.VERSION_CODES; 24 import android.os.Bundle; 25 import android.os.Handler; 26 import android.support.v17.leanback.widget.VerticalGridView; 27 import android.view.KeyEvent; 28 import android.view.LayoutInflater; 29 import android.view.View; 30 import android.view.ViewGroup; 31 import android.widget.TextView; 32 import com.android.tv.R; 33 import com.android.tv.data.ChannelNumber; 34 import com.android.tv.data.api.Channel; 35 import com.android.tv.recommendation.ChannelPreviewUpdater; 36 import com.android.tv.ui.OnRepeatedKeyInterceptListener; 37 import com.android.tv.ui.sidepanel.ActionItem; 38 import com.android.tv.ui.sidepanel.ChannelCheckItem; 39 import com.android.tv.ui.sidepanel.DividerItem; 40 import com.android.tv.ui.sidepanel.Item; 41 import com.android.tv.ui.sidepanel.SideFragment; 42 import java.util.ArrayList; 43 import java.util.Collections; 44 import java.util.Comparator; 45 import java.util.List; 46 47 public class ChannelsBlockedFragment extends SideFragment { 48 private static final String TRACKER_LABEL = "Channels blocked"; 49 private int mBlockedChannelCount; 50 private final List<Channel> mChannels = new ArrayList<>(); 51 private long mLastFocusedChannelId = Channel.INVALID_ID; 52 private int mSelectedPosition = INVALID_POSITION; 53 private boolean mUpdated; 54 private final ContentObserver mProgramUpdateObserver = 55 new ContentObserver(new Handler()) { 56 @Override 57 public void onChange(boolean selfChange, Uri uri) { 58 notifyItemsChanged(); 59 } 60 }; 61 private final Item mLockAllItem = new BlockAllItem(); 62 private final List<Item> mItems = new ArrayList<>(); 63 64 @Override onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)65 public View onCreateView( 66 LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 67 View view = super.onCreateView(inflater, container, savedInstanceState); 68 if (mSelectedPosition != INVALID_POSITION) { 69 setSelectedPosition(mSelectedPosition); 70 } 71 VerticalGridView listView = (VerticalGridView) view.findViewById(R.id.side_panel_list); 72 listView.setOnKeyInterceptListener( 73 new OnRepeatedKeyInterceptListener(listView) { 74 @Override 75 public boolean onInterceptKeyEvent(KeyEvent event) { 76 // In order to send tune operation once for continuous channel up/down 77 // events, 78 // we only call the moveToChannel method on ACTION_UP event of channel 79 // switch keys. 80 if (event.getAction() == KeyEvent.ACTION_UP) { 81 switch (event.getKeyCode()) { 82 case KeyEvent.KEYCODE_DPAD_UP: 83 case KeyEvent.KEYCODE_DPAD_DOWN: 84 if (mLastFocusedChannelId != Channel.INVALID_ID) { 85 getMainActivity() 86 .tuneToChannel( 87 getChannelDataManager() 88 .getChannel(mLastFocusedChannelId)); 89 } 90 break; 91 } 92 } 93 return super.onInterceptKeyEvent(event); 94 } 95 }); 96 getActivity() 97 .getContentResolver() 98 .registerContentObserver( 99 TvContract.Programs.CONTENT_URI, true, mProgramUpdateObserver); 100 getMainActivity().startShrunkenTvView(true, true); 101 mUpdated = false; 102 return view; 103 } 104 105 @Override onDestroyView()106 public void onDestroyView() { 107 getActivity().getContentResolver().unregisterContentObserver(mProgramUpdateObserver); 108 getChannelDataManager().applyUpdatedValuesToDb(); 109 getMainActivity().endShrunkenTvView(); 110 if (VERSION.SDK_INT >= VERSION_CODES.O && mUpdated) { 111 ChannelPreviewUpdater.getInstance(getMainActivity()) 112 .updatePreviewDataForChannelsImmediately(); 113 } 114 super.onDestroyView(); 115 } 116 117 @Override getTitle()118 protected String getTitle() { 119 return getString(R.string.option_channels_locked); 120 } 121 122 @Override getTrackerLabel()123 public String getTrackerLabel() { 124 return TRACKER_LABEL; 125 } 126 127 @Override getItemList()128 protected List<Item> getItemList() { 129 mItems.clear(); 130 mItems.add(mLockAllItem); 131 mChannels.clear(); 132 mChannels.addAll(getChannelDataManager().getChannelList()); 133 Collections.sort( 134 mChannels, 135 new Comparator<Channel>() { 136 @Override 137 public int compare(Channel lhs, Channel rhs) { 138 if (lhs.isBrowsable() != rhs.isBrowsable()) { 139 return lhs.isBrowsable() ? -1 : 1; 140 } 141 return ChannelNumber.compare( 142 lhs.getDisplayNumber(), rhs.getDisplayNumber()); 143 } 144 }); 145 146 final long currentChannelId = getMainActivity().getCurrentChannelId(); 147 boolean hasHiddenChannels = false; 148 for (Channel channel : mChannels) { 149 if (!channel.isBrowsable() && !hasHiddenChannels) { 150 mItems.add(new DividerItem(getString(R.string.option_channels_subheader_hidden))); 151 hasHiddenChannels = true; 152 } 153 mItems.add(new ChannelBlockedItem(channel)); 154 if (channel.isLocked()) { 155 ++mBlockedChannelCount; 156 } 157 if (channel.getId() == currentChannelId) { 158 mSelectedPosition = mItems.size() - 1; 159 } 160 } 161 return mItems; 162 } 163 164 private class BlockAllItem extends ActionItem { 165 private TextView mTextView; 166 BlockAllItem()167 public BlockAllItem() { 168 super(null); 169 } 170 171 @Override onBind(View view)172 protected void onBind(View view) { 173 super.onBind(view); 174 mTextView = (TextView) view.findViewById(R.id.title); 175 } 176 177 @Override onUpdate()178 protected void onUpdate() { 179 super.onUpdate(); 180 updateText(); 181 } 182 183 @Override onUnbind()184 protected void onUnbind() { 185 super.onUnbind(); 186 mTextView = null; 187 } 188 189 @Override onSelected()190 protected void onSelected() { 191 boolean lock = !areAllChannelsBlocked(); 192 for (Channel channel : mChannels) { 193 getChannelDataManager().updateLocked(channel.getId(), lock); 194 } 195 mBlockedChannelCount = lock ? mChannels.size() : 0; 196 notifyItemsChanged(); 197 mUpdated = true; 198 } 199 200 @Override onFocused()201 protected void onFocused() { 202 super.onFocused(); 203 mLastFocusedChannelId = Channel.INVALID_ID; 204 } 205 updateText()206 private void updateText() { 207 mTextView.setText( 208 getString( 209 areAllChannelsBlocked() 210 ? R.string.option_channels_unlock_all 211 : R.string.option_channels_lock_all)); 212 } 213 areAllChannelsBlocked()214 private boolean areAllChannelsBlocked() { 215 return mBlockedChannelCount == mChannels.size(); 216 } 217 } 218 219 private class ChannelBlockedItem extends ChannelCheckItem { ChannelBlockedItem(Channel channel)220 private ChannelBlockedItem(Channel channel) { 221 super(channel, getChannelDataManager(), getProgramDataManager()); 222 } 223 224 @Override getResourceId()225 protected int getResourceId() { 226 return R.layout.option_item_channel_lock; 227 } 228 229 @Override onUpdate()230 protected void onUpdate() { 231 super.onUpdate(); 232 setChecked(getChannel().isLocked()); 233 } 234 235 @Override onSelected()236 protected void onSelected() { 237 super.onSelected(); 238 getChannelDataManager().updateLocked(getChannel().getId(), isChecked()); 239 mBlockedChannelCount += isChecked() ? 1 : -1; 240 notifyItemChanged(mLockAllItem); 241 mUpdated = true; 242 } 243 244 @Override onFocused()245 protected void onFocused() { 246 super.onFocused(); 247 mLastFocusedChannelId = getChannel().getId(); 248 } 249 } 250 } 251