blob: 7486a98146698f10a943005df32f78950a2a52d8 [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.partnersupportsampletvinput;
import android.app.Activity;
import android.app.FragmentManager;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.leanback.app.GuidedStepFragment;
import androidx.leanback.widget.GuidanceStylist;
import androidx.leanback.widget.GuidanceStylist.Guidance;
import androidx.leanback.widget.GuidedAction;
import com.google.android.tv.partner.support.EpgContract;
import com.google.android.tv.partner.support.EpgInput;
import com.google.android.tv.partner.support.EpgInputs;
import com.google.android.tv.partner.support.Lineup;
import com.google.android.tv.partner.support.Lineups;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
/** Lineup Selection Fragment for users to select a lineup */
public class LineupSelectionFragment extends GuidedStepFragment {
public static final boolean DEBUG = false;
public static final String TAG = "LineupSelectionFragment";
public static final String KEY_TITLE = "title";
public static final String KEY_DESCRIPTION = "description";
private static final long ACTION_ID_STATUS = 1;
private static final long ACTION_ID_LINEUPS = 2;
private static final Pattern CHANNEL_NUMBER_DELIMITER = Pattern.compile("([ .-])");
private Activity mActivity;
private GuidedAction mStatusAction;
private String mPostcode;
private List<String> mChannelNumbers;
private Map<GuidedAction, Lineup> mActionLineupMap = new HashMap<>();
private AsyncTask<Void, Void, List<Pair<Lineup, Integer>>> mFetchLineupTask;
@Override
public void onCreate(Bundle savedInstanceState) {
mActivity = getActivity();
mStatusAction =
new GuidedAction.Builder(mActivity)
.id(ACTION_ID_STATUS)
.title("Loading lineups")
.description("please wait")
.focusable(false)
.build();
mFetchLineupTask =
new AsyncTask<Void, Void, List<Pair<Lineup, Integer>>>() {
@Override
protected List<Pair<Lineup, Integer>> doInBackground(Void... voids) {
return lineupChannelMatchCount(getLineups(), getChannelNumbers());
}
@Override
protected void onPostExecute(List<Pair<Lineup, Integer>> result) {
List<GuidedAction> actions = new ArrayList<>();
if (result.isEmpty()) {
mStatusAction.setTitle("No lineup found for postcode: " + mPostcode);
mStatusAction.setDescription("");
mStatusAction.setFocusable(true);
notifyActionChanged(findActionPositionById(ACTION_ID_STATUS));
return;
}
mActionLineupMap = new HashMap<>();
for (Pair<Lineup, Integer> pair : result) {
Lineup lineup = pair.first;
String title =
TextUtils.isEmpty(lineup.getName())
? lineup.getId()
: lineup.getName();
GuidedAction action =
new GuidedAction.Builder(getActivity())
.id(ACTION_ID_LINEUPS)
.title(title)
.description(pair.second + " channels match")
.build();
mActionLineupMap.put(action, lineup);
actions.add(action);
}
setActions(actions);
}
};
super.onCreate(savedInstanceState);
}
@NonNull
@Override
public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
return new Guidance(
"LineupSelectionFragment", "LineupSelectionFragment Description", null, null);
}
@Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = super.onCreateView(inflater, container, savedInstanceState);
if (!mFetchLineupTask.isCancelled()) {
mPostcode = getArguments().getString(ChannelScanFragment.KEY_POSTCODE);
mChannelNumbers =
getArguments().getStringArrayList(ChannelScanFragment.KEY_CHANNEL_NUMBERS);
mFetchLineupTask.execute();
}
return v;
}
@Override
public void onDestroyView() {
mFetchLineupTask.cancel(true);
super.onDestroyView();
}
@Override
public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
actions.add(mStatusAction);
}
@Override
public void onGuidedActionClicked(GuidedAction action) {
if (action == null) {
return;
}
switch ((int) action.getId()) {
case (int) ACTION_ID_STATUS:
finishGuidedStepFragments();
break;
case (int) ACTION_ID_LINEUPS:
FragmentManager fm = getFragmentManager();
super.onGuidedActionClicked(action);
onLineupSelected(mActionLineupMap.get(action));
ResultFragment fragment = new ResultFragment();
Bundle args = new Bundle();
args.putString(KEY_TITLE, action.getTitle().toString());
args.putString(KEY_DESCRIPTION, action.getDescription().toString());
fragment.setArguments(args);
GuidedStepFragment.add(fm, fragment);
break;
default:
}
}
private List<Pair<Lineup, Integer>> lineupChannelMatchCount(
List<Lineup> lineups, List<String> localChannels) {
List<Pair<Lineup, Integer>> result = new ArrayList<>();
for (Lineup lineup : lineups) {
result.add(Pair.create(lineup, getMatchCount(lineup.getChannels(), localChannels)));
}
// sort in decreasing order
Collections.sort(
result,
new Comparator<Pair<Lineup, Integer>>() {
@Override
public int compare(Pair<Lineup, Integer> pair, Pair<Lineup, Integer> other) {
return Integer.compare(other.second, pair.second);
}
});
return result;
}
private static int getMatchCount(List<String> lineupChannels, List<String> localChannels) {
int count = 0;
for (String lineupChannel : lineupChannels) {
if (TextUtils.isEmpty(lineupChannel)) {
continue;
}
List<String> parsedNumbers = parseChannelNumber(lineupChannel);
for (String channel : localChannels) {
if (TextUtils.isEmpty(channel)) {
continue;
}
if (matchChannelNumber(parsedNumbers, parseChannelNumber(channel))) {
if (DEBUG) {
Log.d(TAG, lineupChannel + " matches " + channel);
}
count++;
break;
}
}
}
return count;
}
private List<Lineup> getLineups() {
return new ArrayList<>(Lineups.query(mActivity.getContentResolver(), mPostcode));
}
private List<String> getChannelNumbers() {
return mChannelNumbers;
}
private void onLineupSelected(@Nullable Lineup lineup) {
if (lineup == null) {
return;
}
ContentValues values = new ContentValues();
values.put(EpgContract.EpgInputs.COLUMN_INPUT_ID, SampleTvInputService.INPUT_ID);
values.put(EpgContract.EpgInputs.COLUMN_LINEUP_ID, lineup.getId());
ContentResolver contentResolver = getActivity().getContentResolver();
EpgInput epgInput = EpgInputs.queryEpgInput(contentResolver, SampleTvInputService.INPUT_ID);
if (epgInput == null) {
contentResolver.insert(EpgContract.EpgInputs.CONTENT_URI, values);
} else {
values.put(EpgContract.EpgInputs.COLUMN_ID, epgInput.getId());
EpgInputs.update(contentResolver, EpgInput.createEpgChannel(values));
}
}
/**
* Parses the channel number string to a list of numbers (major number, minor number, etc.).
*
* @param channelNumber the display number of the channel
* @return a list of numbers
*/
private static List<String> parseChannelNumber(String channelNumber) {
// TODO: add tests for this method
List<String> numbers =
new ArrayList<>(
Arrays.asList(TextUtils.split(channelNumber, CHANNEL_NUMBER_DELIMITER)));
numbers.removeAll(Collections.singleton(""));
if (numbers.size() < 1 || numbers.size() > 2) {
Log.w(TAG, "unsupported channel number format: " + channelNumber);
return new ArrayList<>();
}
return numbers;
}
/**
* Checks whether two lists of channel numbers match or not. If the sizes are different,
* additional elements are ignore.
*/
private static boolean matchChannelNumber(List<String> numbers, List<String> other) {
if (numbers.isEmpty() || other.isEmpty()) {
return false;
}
int i = 0;
int j = 0;
while (i < numbers.size() && j < other.size()) {
if (!numbers.get(i++).equals(other.get(j++))) {
return false;
}
}
return true;
}
}