1/* 2 * Copyright (C) 2017 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 17package com.example.partnersupportsampletvinput; 18 19import android.app.FragmentManager; 20import android.content.ContentResolver; 21import android.database.Cursor; 22import android.os.AsyncTask; 23import android.os.Bundle; 24import android.os.SystemClock; 25import android.support.annotation.NonNull; 26import android.support.media.tv.Channel; 27import android.support.media.tv.TvContractCompat; 28import android.support.v17.leanback.app.GuidedStepFragment; 29import android.support.v17.leanback.widget.GuidanceStylist; 30import android.support.v17.leanback.widget.GuidanceStylist.Guidance; 31import android.support.v17.leanback.widget.GuidedAction; 32import android.view.LayoutInflater; 33import android.view.View; 34import android.view.ViewGroup; 35import java.util.ArrayList; 36import java.util.HashMap; 37import java.util.Iterator; 38import java.util.List; 39 40/** Channel Scan Fragment shows detected channels */ 41public class ChannelScanFragment extends GuidedStepFragment { 42 public static final long ACTION_ID_SCAN_RESULTS = 1; 43 public static final String KEY_POSTCODE = "postcode"; 44 public static final String KEY_CHANNEL_NUMBERS = "channel numbers"; 45 private GuidedAction mNextAction; 46 private List<Channel> mChannels = new ArrayList<>(); 47 private Bundle mSavedInstanceState; 48 49 private AsyncTask<Void, Void, List<Channel>> mChannelScanTask; 50 51 @Override 52 public void onCreate(Bundle savedInstanceState) { 53 super.onCreate(savedInstanceState); 54 mSavedInstanceState = savedInstanceState; 55 } 56 57 @Override 58 public View onCreateView( 59 LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 60 View v = super.onCreateView(inflater, container, savedInstanceState); 61 mChannelScanTask = 62 new AsyncTask<Void, Void, List<Channel>>() { 63 @Override 64 protected List<Channel> doInBackground(Void... voids) { 65 return scanChannel(); 66 } 67 68 @Override 69 protected void onPostExecute(List<Channel> result) { 70 mChannels = result; 71 mNextAction.setFocusable(true); 72 mNextAction.setEnabled(true); 73 int position = findButtonActionPositionById(GuidedAction.ACTION_ID_NEXT); 74 if (position >= 0) { 75 notifyButtonActionChanged(position); 76 View buttonView = getButtonActionItemView(position); 77 buttonView.setFocusable(true); 78 buttonView.requestFocus(); 79 } 80 } 81 }; 82 mChannelScanTask.execute(); 83 return v; 84 } 85 86 @Override 87 public void onDestroyView() { 88 super.onDestroyView(); 89 mNextAction = null; 90 if (mChannelScanTask != null) { 91 mChannelScanTask.cancel(true); 92 mChannelScanTask = null; 93 } 94 } 95 96 @Override 97 public void onResume() { 98 super.onResume(); 99 // reset actions 100 List<GuidedAction> actions = new ArrayList<>(); 101 onCreateActions(actions, mSavedInstanceState); 102 setActions(actions); 103 actions = new ArrayList<>(); 104 onCreateButtonActions(actions, mSavedInstanceState); 105 setButtonActions(actions); 106 } 107 108 @NonNull 109 @Override 110 public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) { 111 return new Guidance("Channel Scan", "Fake scanning for channels", null, null); 112 } 113 114 @Override 115 public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) { 116 actions.add( 117 new GuidedAction.Builder(getActivity()) 118 .id(ACTION_ID_SCAN_RESULTS) 119 .title("Channels Found:") 120 .description("") 121 .multilineDescription(true) 122 .focusable(false) 123 .infoOnly(true) 124 .build()); 125 } 126 127 @Override 128 public void onCreateButtonActions( 129 @NonNull List<GuidedAction> actions, Bundle savedInstanceState) { 130 mNextAction = 131 new GuidedAction.Builder(getActivity()) 132 .id(GuidedAction.ACTION_ID_NEXT) 133 .title("NEXT") 134 .focusable(false) 135 .enabled(false) 136 .build(); 137 actions.add(mNextAction); 138 actions.add( 139 new GuidedAction.Builder(getActivity()) 140 .id(GuidedAction.ACTION_ID_CANCEL) 141 .title("CANCEL") 142 .build()); 143 } 144 145 @Override 146 public void onGuidedActionClicked(GuidedAction action) { 147 if (action == null) { 148 return; 149 } 150 FragmentManager fm = getFragmentManager(); 151 switch ((int) action.getId()) { 152 case (int) GuidedAction.ACTION_ID_NEXT: 153 LineupSelectionFragment fragment = new LineupSelectionFragment(); 154 Bundle args = new Bundle(); 155 args.putString(KEY_POSTCODE, getArguments().getString(KEY_POSTCODE)); 156 ArrayList<String> channelNumbers = new ArrayList<>(); 157 for (Channel channel : mChannels) { 158 if (channel != null) { 159 channelNumbers.add(channel.getDisplayNumber()); 160 } 161 } 162 args.putStringArrayList(KEY_CHANNEL_NUMBERS, channelNumbers); 163 fragment.setArguments(args); 164 GuidedStepFragment.add(getFragmentManager(), fragment); 165 break; 166 case (int) GuidedAction.ACTION_ID_CANCEL: 167 fm.popBackStack(); 168 break; 169 default: 170 } 171 } 172 173 private List<Channel> scanChannel() { 174 List<Channel> channels = new ArrayList<>(); 175 channels.add(createFakeChannel("KQED", "9.1", 1)); 176 channels.add(createFakeChannel("BBC", "2-2", 2)); 177 channels.add(createFakeChannel("What\'s on", "1.1", 3)); 178 channels.add(createFakeChannel("FOX", "7.1", 4)); 179 channels.add(createFakeChannel("Play Movie", "1.1", 5)); 180 channels.add(createFakeChannel("PBS", "9.2", 6)); 181 channels.add(createFakeChannel("ApiTestChannel", "A.1", 7)); 182 updateChannels(channels); 183 184 return channels; 185 } 186 187 private void updateChannels(List<Channel> channels) { 188 HashMap<Integer, Channel> networkId2newChannel = new HashMap<>(); 189 for (Channel channel : channels) { 190 networkId2newChannel.put(channel.getOriginalNetworkId(), channel); 191 } 192 193 ContentResolver cr = getActivity().getContentResolver(); 194 Cursor cursor = 195 cr.query( 196 TvContractCompat.buildChannelsUriForInput(SampleTvInputService.INPUT_ID), 197 Channel.PROJECTION, 198 null, 199 null, 200 null, 201 null); 202 ArrayList<Channel> existingChannels = new ArrayList<>(); 203 while (cursor.moveToNext()) { 204 existingChannels.add(Channel.fromCursor(cursor)); 205 } 206 Iterator<Channel> i = existingChannels.iterator(); 207 while (i.hasNext()) { 208 Channel existingChannel = i.next(); 209 if (networkId2newChannel.containsKey(existingChannel.getOriginalNetworkId())) { 210 i.remove(); 211 Channel newChannel = 212 networkId2newChannel.remove(existingChannel.getOriginalNetworkId()); 213 cr.update( 214 TvContractCompat.buildChannelUri(existingChannel.getId()), 215 newChannel.toContentValues(), 216 null, 217 null); 218 } 219 } 220 for (Channel newChannel : networkId2newChannel.values()) { 221 cr.insert(TvContractCompat.Channels.CONTENT_URI, newChannel.toContentValues()); 222 } 223 for (Channel existingChannel : existingChannels) { 224 cr.delete(TvContractCompat.buildChannelUri(existingChannel.getId()), null, null); 225 } 226 } 227 228 private Channel createFakeChannel(String name, String number, int networkId) { 229 Channel channel = 230 new Channel.Builder() 231 .setDisplayName(name) 232 .setDisplayNumber(number) 233 .setInputId(SampleTvInputService.INPUT_ID) 234 .setOriginalNetworkId(networkId) 235 .build(); 236 onChannelDetected(channel); 237 SystemClock.sleep(1000); 238 239 return channel; 240 } 241 242 private void onChannelDetected(Channel channel) { 243 getActivity() 244 .runOnUiThread( 245 new Runnable() { 246 @Override 247 public void run() { 248 List<GuidedAction> actions = getActions(); 249 actions.add( 250 new GuidedAction.Builder(getActivity()) 251 .id(ACTION_ID_SCAN_RESULTS) 252 .title( 253 channel.getDisplayNumber() 254 + " " 255 + channel.getDisplayName()) 256 .focusable(false) 257 .build()); 258 if (actions.size() >= 7) { 259 actions.remove(1); 260 } 261 setActions(actions); 262 } 263 }); 264 } 265} 266