aboutsummaryrefslogtreecommitdiff
path: root/src/com/android/tv/util/TvInputManagerHelper.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/tv/util/TvInputManagerHelper.java')
-rw-r--r--src/com/android/tv/util/TvInputManagerHelper.java358
1 files changed, 343 insertions, 15 deletions
diff --git a/src/com/android/tv/util/TvInputManagerHelper.java b/src/com/android/tv/util/TvInputManagerHelper.java
index 121f56ed..730a985b 100644
--- a/src/com/android/tv/util/TvInputManagerHelper.java
+++ b/src/com/android/tv/util/TvInputManagerHelper.java
@@ -18,20 +18,26 @@ package com.android.tv.util;
import android.content.Context;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.hardware.hdmi.HdmiDeviceInfo;
import android.media.tv.TvInputInfo;
import android.media.tv.TvInputManager;
import android.media.tv.TvInputManager.TvInputCallback;
import android.os.Handler;
import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Log;
import com.android.tv.Features;
import com.android.tv.common.SoftPreconditions;
+import com.android.tv.common.TvCommonUtils;
import com.android.tv.parental.ContentRatingsManager;
import com.android.tv.parental.ParentalControlSettings;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
@@ -42,14 +48,64 @@ import java.util.Map;
public class TvInputManagerHelper {
private static final String TAG = "TvInputManagerHelper";
private static final boolean DEBUG = false;
+
+ /**
+ * Types of HDMI device and bundled tuner.
+ */
+ public static final int TYPE_CEC_DEVICE = -2;
+ public static final int TYPE_BUNDLED_TUNER = -3;
+ public static final int TYPE_CEC_DEVICE_RECORDER = -4;
+ public static final int TYPE_CEC_DEVICE_PLAYBACK = -5;
+ public static final int TYPE_MHL_MOBILE = -6;
+
+ private static final String PERMISSION_ACCESS_ALL_EPG_DATA =
+ "com.android.providers.tv.permission.ACCESS_ALL_EPG_DATA";
+ private static final String [] mPhysicalTunerBlackList = {
+ };
+ private static final String META_LABEL_SORT_KEY = "input_sort_key";
+
+ /**
+ * The default tv input priority to show.
+ */
+ private static final ArrayList<Integer> DEFAULT_TV_INPUT_PRIORITY = new ArrayList<>();
+ static {
+ DEFAULT_TV_INPUT_PRIORITY.add(TYPE_BUNDLED_TUNER);
+ DEFAULT_TV_INPUT_PRIORITY.add(TvInputInfo.TYPE_TUNER);
+ DEFAULT_TV_INPUT_PRIORITY.add(TYPE_CEC_DEVICE);
+ DEFAULT_TV_INPUT_PRIORITY.add(TYPE_CEC_DEVICE_RECORDER);
+ DEFAULT_TV_INPUT_PRIORITY.add(TYPE_CEC_DEVICE_PLAYBACK);
+ DEFAULT_TV_INPUT_PRIORITY.add(TYPE_MHL_MOBILE);
+ DEFAULT_TV_INPUT_PRIORITY.add(TvInputInfo.TYPE_HDMI);
+ DEFAULT_TV_INPUT_PRIORITY.add(TvInputInfo.TYPE_DVI);
+ DEFAULT_TV_INPUT_PRIORITY.add(TvInputInfo.TYPE_COMPONENT);
+ DEFAULT_TV_INPUT_PRIORITY.add(TvInputInfo.TYPE_SVIDEO);
+ DEFAULT_TV_INPUT_PRIORITY.add(TvInputInfo.TYPE_COMPOSITE);
+ DEFAULT_TV_INPUT_PRIORITY.add(TvInputInfo.TYPE_DISPLAY_PORT);
+ DEFAULT_TV_INPUT_PRIORITY.add(TvInputInfo.TYPE_VGA);
+ DEFAULT_TV_INPUT_PRIORITY.add(TvInputInfo.TYPE_SCART);
+ DEFAULT_TV_INPUT_PRIORITY.add(TvInputInfo.TYPE_OTHER);
+ }
+
private static final String[] PARTNER_TUNER_INPUT_PREFIX_BLACKLIST = {
};
+ private static final String[] TESTABLE_INPUTS = {
+ "com.android.tv.testinput/.TestTvInputService"
+ };
+
private final Context mContext;
+ private final PackageManager mPackageManager;
private final TvInputManager mTvInputManager;
private final Map<String, Integer> mInputStateMap = new HashMap<>();
private final Map<String, TvInputInfo> mInputMap = new HashMap<>();
+ private final Map<String, String> mTvInputLabels = new ArrayMap<>();
+ private final Map<String, String> mTvInputCustomLabels = new ArrayMap<>();
private final Map<String, Boolean> mInputIdToPartnerInputMap = new HashMap<>();
+
+ private final Map<String, CharSequence> mTvInputApplicationLabels = new ArrayMap<>();
+ private final Map<String, Drawable> mTvInputApplicationIcons = new ArrayMap<>();
+ private final Map<String, Drawable> mTvInputAppliactionBanners = new ArrayMap<>();
+
private final TvInputCallback mInternalCallback = new TvInputCallback() {
@Override
public void onInputStateChanged(String inputId, int state) {
@@ -72,6 +128,11 @@ public class TvInputManagerHelper {
TvInputInfo info = mTvInputManager.getTvInputInfo(inputId);
if (info != null) {
mInputMap.put(inputId, info);
+ mTvInputLabels.put(inputId, info.loadLabel(mContext).toString());
+ CharSequence inputCustomLabel = info.loadCustomLabel(mContext);
+ if (inputCustomLabel != null) {
+ mTvInputCustomLabels.put(inputId, inputCustomLabel.toString());
+ }
mInputStateMap.put(inputId, mTvInputManager.getInputState(inputId));
mInputIdToPartnerInputMap.put(inputId, isPartnerInput(info));
}
@@ -85,6 +146,11 @@ public class TvInputManagerHelper {
public void onInputRemoved(String inputId) {
if (DEBUG) Log.d(TAG, "onInputRemoved " + inputId);
mInputMap.remove(inputId);
+ mTvInputLabels.remove(inputId);
+ mTvInputCustomLabels.remove(inputId);
+ mTvInputApplicationLabels.remove(inputId);
+ mTvInputApplicationIcons.remove(inputId);
+ mTvInputAppliactionBanners.remove(inputId);
mInputStateMap.remove(inputId);
mInputIdToPartnerInputMap.remove(inputId);
mContentRatingsManager.update();
@@ -103,6 +169,14 @@ public class TvInputManagerHelper {
}
TvInputInfo info = mTvInputManager.getTvInputInfo(inputId);
mInputMap.put(inputId, info);
+ mTvInputLabels.put(inputId, info.loadLabel(mContext).toString());
+ CharSequence inputCustomLabel = info.loadCustomLabel(mContext);
+ if (inputCustomLabel != null) {
+ mTvInputCustomLabels.put(inputId, inputCustomLabel.toString());
+ }
+ mTvInputApplicationLabels.remove(inputId);
+ mTvInputApplicationIcons.remove(inputId);
+ mTvInputAppliactionBanners.remove(inputId);
for (TvInputCallback callback : mCallbacks) {
callback.onInputUpdated(inputId);
}
@@ -114,6 +188,11 @@ public class TvInputManagerHelper {
public void onTvInputInfoUpdated(TvInputInfo inputInfo) {
if (DEBUG) Log.d(TAG, "onTvInputInfoUpdated " + inputInfo);
mInputMap.put(inputInfo.getId(), inputInfo);
+ mTvInputLabels.put(inputInfo.getId(), inputInfo.loadLabel(mContext).toString());
+ CharSequence inputCustomLabel = inputInfo.loadCustomLabel(mContext);
+ if (inputCustomLabel != null) {
+ mTvInputCustomLabels.put(inputInfo.getId(), inputCustomLabel.toString());
+ }
for (TvInputCallback callback : mCallbacks) {
callback.onTvInputInfoUpdated(inputInfo);
}
@@ -131,13 +210,18 @@ public class TvInputManagerHelper {
public TvInputManagerHelper(Context context) {
mContext = context.getApplicationContext();
+ mPackageManager = context.getPackageManager();
mTvInputManager = (TvInputManager) context.getSystemService(Context.TV_INPUT_SERVICE);
mContentRatingsManager = new ContentRatingsManager(context);
mParentalControlSettings = new ParentalControlSettings(context);
- mTvInputInfoComparator = new TvInputInfoComparator(this);
+ mTvInputInfoComparator = new InputComparatorInternal(this);
}
public void start() {
+ if (!hasTvInputManager()) {
+ // Not a TV device
+ return;
+ }
if (mStarted) {
return;
}
@@ -145,6 +229,11 @@ public class TvInputManagerHelper {
mStarted = true;
mTvInputManager.registerCallback(mInternalCallback, mHandler);
mInputMap.clear();
+ mTvInputLabels.clear();
+ mTvInputCustomLabels.clear();
+ mTvInputApplicationLabels.clear();
+ mTvInputApplicationIcons.clear();
+ mTvInputAppliactionBanners.clear();
mInputStateMap.clear();
mInputIdToPartnerInputMap.clear();
for (TvInputInfo input : mTvInputManager.getTvInputList()) {
@@ -171,9 +260,23 @@ public class TvInputManagerHelper {
mStarted = false;
mInputStateMap.clear();
mInputMap.clear();
+ mTvInputLabels.clear();
+ mTvInputCustomLabels.clear();
+ mTvInputApplicationLabels.clear();
+ mTvInputApplicationIcons.clear();
+ mTvInputAppliactionBanners.clear();;
mInputIdToPartnerInputMap.clear();
}
+ /**
+ * Clears the TvInput labels map.
+ */
+ public void clearTvInputLabels() {
+ mTvInputLabels.clear();
+ mTvInputCustomLabels.clear();
+ mTvInputApplicationLabels.clear();
+ }
+
public List<TvInputInfo> getTvInputInfos(boolean availableOnly, boolean tunerOnly) {
ArrayList<TvInputInfo> list = new ArrayList<>();
for (Map.Entry<String, Integer> pair : mInputStateMap.entrySet()) {
@@ -192,7 +295,7 @@ public class TvInputManagerHelper {
/**
* Returns the default comparator for {@link TvInputInfo}.
- * See {@link TvInputInfoComparator} for detail.
+ * See {@link InputComparatorInternal} for detail.
*/
public Comparator<TvInputInfo> getDefaultTvInputInfoComparator() {
return mTvInputInfoComparator;
@@ -237,15 +340,81 @@ public class TvInputManagerHelper {
}
/**
- * Loads label of {@code info}.
+ * Is (Context.TV_INPUT_SERVICE) available.
*
- * It's visible for comparator test to mock TvInputInfo.
- * Package private is enough for this method, but public is necessary to workaround mockito
- * bug.
+ * <p>This is only available on TV devices.
+ */
+ public boolean hasTvInputManager() {
+ return mTvInputManager != null;
+ }
+
+ /**
+ * Loads label of {@code info}.
*/
- @VisibleForTesting
public String loadLabel(TvInputInfo info) {
- return info.loadLabel(mContext).toString();
+ String label = mTvInputLabels.get(info.getId());
+ if (label == null) {
+ label = info.loadLabel(mContext).toString();
+ mTvInputLabels.put(info.getId(), label);
+ }
+ return label;
+ }
+
+ /**
+ * Loads custom label of {@code info}
+ */
+ public String loadCustomLabel(TvInputInfo info) {
+ String customLabel = mTvInputCustomLabels.get(info.getId());
+ if (customLabel == null) {
+ CharSequence customLabelCharSequence = info.loadCustomLabel(mContext);
+ if (customLabelCharSequence != null) {
+ customLabel = customLabelCharSequence.toString();
+ mTvInputCustomLabels.put(info.getId(), customLabel);
+ }
+ }
+ return customLabel;
+ }
+
+ /**
+ * Gets the tv input application's label.
+ */
+ public CharSequence getTvInputApplicationLabel(CharSequence inputId) {
+ return mTvInputApplicationLabels.get(inputId);
+ }
+
+ /**
+ * Stores the tv input application's label.
+ */
+ public void setTvInputApplicationLabel(String inputId, CharSequence label) {
+ mTvInputApplicationLabels.put(inputId, label);
+ }
+
+ /**
+ * Gets the tv input application's icon.
+ */
+ public Drawable getTvInputApplicationIcon(String inputId) {
+ return mTvInputApplicationIcons.get(inputId);
+ }
+
+ /**
+ * Stores the tv input application's icon.
+ */
+ public void setTvInputApplicationIcon(String inputId, Drawable icon) {
+ mTvInputApplicationIcons.put(inputId, icon);
+ }
+
+ /**
+ * Gets the tv input application's banner.
+ */
+ public Drawable getTvInputApplicationBanner(String inputId) {
+ return mTvInputAppliactionBanners.get(inputId);
+ }
+
+ /**
+ * Stores the tv input application's banner.
+ */
+ public void setTvInputApplicationBanner(String inputId, Drawable banner) {
+ mTvInputAppliactionBanners.put(inputId, banner);
}
/**
@@ -321,14 +490,54 @@ public class TvInputManagerHelper {
return mContentRatingsManager;
}
- private boolean isInBlackList(String inputId) {
- if (!Features.USE_PARTNER_INPUT_BLACKLIST.isEnabled(mContext)) {
+ private int getInputSortKey(TvInputInfo input) {
+ return input.getServiceInfo().metaData.getInt(META_LABEL_SORT_KEY,
+ Integer.MAX_VALUE);
+ }
+
+ private boolean isInputPhysicalTuner(TvInputInfo input) {
+ String packageName = input.getServiceInfo().packageName;
+ if (Arrays.asList(mPhysicalTunerBlackList).contains(packageName)) {
return false;
}
- for (String disabledTunerInputPrefix : PARTNER_TUNER_INPUT_PREFIX_BLACKLIST) {
- if (inputId.contains(disabledTunerInputPrefix)) {
- return true;
+
+ if (input.createSetupIntent() == null) {
+ return false;
+ } else {
+ boolean mayBeTunerInput = mPackageManager.checkPermission(
+ PERMISSION_ACCESS_ALL_EPG_DATA, input.getServiceInfo().packageName)
+ == PackageManager.PERMISSION_GRANTED;
+ if (!mayBeTunerInput) {
+ try {
+ ApplicationInfo ai = mPackageManager.getApplicationInfo(
+ input.getServiceInfo().packageName, 0);
+ if ((ai.flags & (ApplicationInfo.FLAG_SYSTEM
+ | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) == 0) {
+ return false;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private boolean isInBlackList(String inputId) {
+ if (Features.USE_PARTNER_INPUT_BLACKLIST.isEnabled(mContext)) {
+ for (String disabledTunerInputPrefix : PARTNER_TUNER_INPUT_PREFIX_BLACKLIST) {
+ if (inputId.contains(disabledTunerInputPrefix)) {
+ return true;
+ }
+ }
+ }
+ if (TvCommonUtils.isRunningInTest()) {
+ for (String testableInput : TESTABLE_INPUTS) {
+ if (testableInput.equals(inputId)) {
+ return false;
+ }
}
+ return true;
}
return false;
}
@@ -342,10 +551,10 @@ public class TvInputManagerHelper {
* (i.e. Mockito's spy doesn't work)
*/
@VisibleForTesting
- static class TvInputInfoComparator implements Comparator<TvInputInfo> {
+ static class InputComparatorInternal implements Comparator<TvInputInfo> {
private final TvInputManagerHelper mInputManager;
- public TvInputInfoComparator(TvInputManagerHelper inputManager) {
+ public InputComparatorInternal(TvInputManagerHelper inputManager) {
mInputManager = inputManager;
}
@@ -357,4 +566,123 @@ public class TvInputManagerHelper {
return mInputManager.loadLabel(lhs).compareTo(mInputManager.loadLabel(rhs));
}
}
+
+ /**
+ * A comparator used for {@link com.android.tv.ui.SelectInputView} to show the list of
+ * TV inputs.
+ */
+ public static class HardwareInputComparator implements Comparator<TvInputInfo> {
+ private Map<Integer, Integer> mTypePriorities = new HashMap<>();
+ private final TvInputManagerHelper mTvInputManagerHelper;
+ private final Context mContext;
+
+ public HardwareInputComparator(Context context, TvInputManagerHelper tvInputManagerHelper) {
+ mContext = context;
+ mTvInputManagerHelper = tvInputManagerHelper;
+ setupDeviceTypePriorities();
+ }
+
+ @Override
+ public int compare(TvInputInfo lhs, TvInputInfo rhs) {
+ if (lhs == null) {
+ return (rhs == null) ? 0 : 1;
+ }
+ if (rhs == null) {
+ return -1;
+ }
+
+ boolean enabledL = (mTvInputManagerHelper.getInputState(lhs)
+ != TvInputManager.INPUT_STATE_DISCONNECTED);
+ boolean enabledR = (mTvInputManagerHelper.getInputState(rhs)
+ != TvInputManager.INPUT_STATE_DISCONNECTED);
+ if (enabledL != enabledR) {
+ return enabledL ? -1 : 1;
+ }
+
+ int priorityL = getPriority(lhs);
+ int priorityR = getPriority(rhs);
+ if (priorityL != priorityR) {
+ return priorityL - priorityR;
+ }
+
+ if (lhs.getType() == TvInputInfo.TYPE_TUNER
+ && rhs.getType() == TvInputInfo.TYPE_TUNER) {
+ boolean isPhysicalL = mTvInputManagerHelper.isInputPhysicalTuner(lhs);
+ boolean isPhysicalR = mTvInputManagerHelper.isInputPhysicalTuner(rhs);
+ if (isPhysicalL != isPhysicalR) {
+ return isPhysicalL ? -1 : 1;
+ }
+ }
+
+ int sortKeyL = mTvInputManagerHelper.getInputSortKey(lhs);
+ int sortKeyR = mTvInputManagerHelper.getInputSortKey(rhs);
+ if (sortKeyL != sortKeyR) {
+ return sortKeyR - sortKeyL;
+ }
+
+ String parentLabelL = lhs.getParentId() != null
+ ? getLabel(mTvInputManagerHelper.getTvInputInfo(lhs.getParentId()))
+ : getLabel(mTvInputManagerHelper.getTvInputInfo(lhs.getId()));
+ String parentLabelR = rhs.getParentId() != null
+ ? getLabel(mTvInputManagerHelper.getTvInputInfo(rhs.getParentId()))
+ : getLabel(mTvInputManagerHelper.getTvInputInfo(rhs.getId()));
+
+ if (!TextUtils.equals(parentLabelL, parentLabelR)) {
+ return parentLabelL.compareToIgnoreCase(parentLabelR);
+ }
+ return getLabel(lhs).compareToIgnoreCase(getLabel(rhs));
+ }
+
+ private String getLabel(TvInputInfo input) {
+ if (input == null) {
+ return "";
+ }
+ String label = mTvInputManagerHelper.loadCustomLabel(input);
+ if (TextUtils.isEmpty(label)) {
+ label = mTvInputManagerHelper.loadLabel(input);
+ }
+ return label;
+ }
+
+ private int getPriority(TvInputInfo info) {
+ Integer priority = null;
+ if (mTypePriorities != null) {
+ priority = mTypePriorities.get(getTvInputTypeForPriority(info));
+ }
+ if (priority != null) {
+ return priority;
+ }
+ return Integer.MAX_VALUE;
+ }
+
+ private void setupDeviceTypePriorities() {
+ mTypePriorities = Partner.getInstance(mContext).getInputsOrderMap();
+
+ // Fill in any missing priorities in the map we got from the OEM
+ int priority = mTypePriorities.size();
+ for (int type : DEFAULT_TV_INPUT_PRIORITY) {
+ if (!mTypePriorities.containsKey(type)) {
+ mTypePriorities.put(type, priority++);
+ }
+ }
+ }
+
+ private int getTvInputTypeForPriority(TvInputInfo info) {
+ if (info.getHdmiDeviceInfo() != null) {
+ if (info.getHdmiDeviceInfo().isCecDevice()) {
+ switch (info.getHdmiDeviceInfo().getDeviceType()) {
+ case HdmiDeviceInfo.DEVICE_RECORDER:
+ return TYPE_CEC_DEVICE_RECORDER;
+ case HdmiDeviceInfo.DEVICE_PLAYBACK:
+ return TYPE_CEC_DEVICE_PLAYBACK;
+ default:
+ return TYPE_CEC_DEVICE;
+ }
+ } else if (info.getHdmiDeviceInfo().isMhlDevice()) {
+ return TYPE_MHL_MOBILE;
+ }
+ }
+ return info.getType();
+ }
+ }
}