1193 lines
47 KiB
Java
1193 lines
47 KiB
Java
![]() |
/*
|
||
|
* Copyright (C) 2014 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 android.media.tv;
|
||
|
|
||
|
import android.annotation.IntDef;
|
||
|
import android.annotation.NonNull;
|
||
|
import android.annotation.StringRes;
|
||
|
import android.annotation.SystemApi;
|
||
|
import android.compat.annotation.UnsupportedAppUsage;
|
||
|
import android.content.ComponentName;
|
||
|
import android.content.Context;
|
||
|
import android.content.Intent;
|
||
|
import android.content.pm.PackageManager;
|
||
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||
|
import android.content.pm.ResolveInfo;
|
||
|
import android.content.pm.ServiceInfo;
|
||
|
import android.content.res.Resources;
|
||
|
import android.content.res.TypedArray;
|
||
|
import android.content.res.XmlResourceParser;
|
||
|
import android.graphics.drawable.Drawable;
|
||
|
import android.graphics.drawable.Icon;
|
||
|
import android.hardware.hdmi.HdmiControlManager;
|
||
|
import android.hardware.hdmi.HdmiDeviceInfo;
|
||
|
import android.hardware.hdmi.HdmiUtils;
|
||
|
import android.hardware.hdmi.HdmiUtils.HdmiAddressRelativePosition;
|
||
|
import android.net.Uri;
|
||
|
import android.os.Build;
|
||
|
import android.os.Bundle;
|
||
|
import android.os.Parcel;
|
||
|
import android.os.Parcelable;
|
||
|
import android.os.UserHandle;
|
||
|
import android.provider.Settings;
|
||
|
import android.text.TextUtils;
|
||
|
import android.util.AttributeSet;
|
||
|
import android.util.Log;
|
||
|
import android.util.SparseIntArray;
|
||
|
import android.util.Xml;
|
||
|
|
||
|
import org.xmlpull.v1.XmlPullParser;
|
||
|
import org.xmlpull.v1.XmlPullParserException;
|
||
|
|
||
|
import java.io.IOException;
|
||
|
import java.io.InputStream;
|
||
|
import java.lang.annotation.Retention;
|
||
|
import java.lang.annotation.RetentionPolicy;
|
||
|
import java.util.HashMap;
|
||
|
import java.util.HashSet;
|
||
|
import java.util.Locale;
|
||
|
import java.util.Map;
|
||
|
import java.util.Objects;
|
||
|
import java.util.Set;
|
||
|
|
||
|
/**
|
||
|
* This class is used to specify meta information of a TV input.
|
||
|
*/
|
||
|
public final class TvInputInfo implements Parcelable {
|
||
|
private static final boolean DEBUG = false;
|
||
|
private static final String TAG = "TvInputInfo";
|
||
|
|
||
|
/** @hide */
|
||
|
@Retention(RetentionPolicy.SOURCE)
|
||
|
@IntDef({TYPE_TUNER, TYPE_OTHER, TYPE_COMPOSITE, TYPE_SVIDEO, TYPE_SCART, TYPE_COMPONENT,
|
||
|
TYPE_VGA, TYPE_DVI, TYPE_HDMI, TYPE_DISPLAY_PORT})
|
||
|
public @interface Type {}
|
||
|
|
||
|
// Should be in sync with frameworks/base/core/res/res/values/attrs.xml
|
||
|
/**
|
||
|
* TV input type: the TV input service is a tuner which provides channels.
|
||
|
*/
|
||
|
public static final int TYPE_TUNER = 0;
|
||
|
/**
|
||
|
* TV input type: a generic hardware TV input type.
|
||
|
*/
|
||
|
public static final int TYPE_OTHER = 1000;
|
||
|
/**
|
||
|
* TV input type: the TV input service represents a composite port.
|
||
|
*/
|
||
|
public static final int TYPE_COMPOSITE = 1001;
|
||
|
/**
|
||
|
* TV input type: the TV input service represents a SVIDEO port.
|
||
|
*/
|
||
|
public static final int TYPE_SVIDEO = 1002;
|
||
|
/**
|
||
|
* TV input type: the TV input service represents a SCART port.
|
||
|
*/
|
||
|
public static final int TYPE_SCART = 1003;
|
||
|
/**
|
||
|
* TV input type: the TV input service represents a component port.
|
||
|
*/
|
||
|
public static final int TYPE_COMPONENT = 1004;
|
||
|
/**
|
||
|
* TV input type: the TV input service represents a VGA port.
|
||
|
*/
|
||
|
public static final int TYPE_VGA = 1005;
|
||
|
/**
|
||
|
* TV input type: the TV input service represents a DVI port.
|
||
|
*/
|
||
|
public static final int TYPE_DVI = 1006;
|
||
|
/**
|
||
|
* TV input type: the TV input service is HDMI. (e.g. HDMI 1)
|
||
|
*/
|
||
|
public static final int TYPE_HDMI = 1007;
|
||
|
/**
|
||
|
* TV input type: the TV input service represents a display port.
|
||
|
*/
|
||
|
public static final int TYPE_DISPLAY_PORT = 1008;
|
||
|
|
||
|
/**
|
||
|
* Used as a String extra field in setup intents created by {@link #createSetupIntent()} to
|
||
|
* supply the ID of a specific TV input to set up.
|
||
|
*/
|
||
|
public static final String EXTRA_INPUT_ID = "android.media.tv.extra.INPUT_ID";
|
||
|
|
||
|
private final ResolveInfo mService;
|
||
|
|
||
|
private final String mId;
|
||
|
private final int mType;
|
||
|
private final boolean mIsHardwareInput;
|
||
|
|
||
|
// TODO: Remove mIconUri when createTvInputInfo() is removed.
|
||
|
private Uri mIconUri;
|
||
|
|
||
|
private final CharSequence mLabel;
|
||
|
private final int mLabelResId;
|
||
|
private final Icon mIcon;
|
||
|
private final Icon mIconStandby;
|
||
|
private final Icon mIconDisconnected;
|
||
|
|
||
|
// Attributes from XML meta data.
|
||
|
private final String mSetupActivity;
|
||
|
private final boolean mCanRecord;
|
||
|
private final boolean mCanPauseRecording;
|
||
|
private final int mTunerCount;
|
||
|
|
||
|
// Attributes specific to HDMI
|
||
|
private final HdmiDeviceInfo mHdmiDeviceInfo;
|
||
|
private final boolean mIsConnectedToHdmiSwitch;
|
||
|
@HdmiAddressRelativePosition
|
||
|
private final int mHdmiConnectionRelativePosition;
|
||
|
private final String mParentId;
|
||
|
|
||
|
private final Bundle mExtras;
|
||
|
|
||
|
/**
|
||
|
* Create a new instance of the TvInputInfo class, instantiating it from the given Context,
|
||
|
* ResolveInfo, and HdmiDeviceInfo.
|
||
|
*
|
||
|
* @param service The ResolveInfo returned from the package manager about this TV input service.
|
||
|
* @param hdmiDeviceInfo The HdmiDeviceInfo for a HDMI CEC logical device.
|
||
|
* @param parentId The ID of this TV input's parent input. {@code null} if none exists.
|
||
|
* @param label The label of this TvInputInfo. If it is {@code null} or empty, {@code service}
|
||
|
* label will be loaded.
|
||
|
* @param iconUri The {@link android.net.Uri} to load the icon image. See
|
||
|
* {@link android.content.ContentResolver#openInputStream}. If it is {@code null},
|
||
|
* the application icon of {@code service} will be loaded.
|
||
|
* @hide
|
||
|
* @deprecated Use {@link Builder} instead.
|
||
|
*/
|
||
|
@Deprecated
|
||
|
@SystemApi
|
||
|
public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service,
|
||
|
HdmiDeviceInfo hdmiDeviceInfo, String parentId, String label, Uri iconUri)
|
||
|
throws XmlPullParserException, IOException {
|
||
|
TvInputInfo info = new TvInputInfo.Builder(context, service)
|
||
|
.setHdmiDeviceInfo(hdmiDeviceInfo)
|
||
|
.setParentId(parentId)
|
||
|
.setLabel(label)
|
||
|
.build();
|
||
|
info.mIconUri = iconUri;
|
||
|
return info;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create a new instance of the TvInputInfo class, instantiating it from the given Context,
|
||
|
* ResolveInfo, and HdmiDeviceInfo.
|
||
|
*
|
||
|
* @param service The ResolveInfo returned from the package manager about this TV input service.
|
||
|
* @param hdmiDeviceInfo The HdmiDeviceInfo for a HDMI CEC logical device.
|
||
|
* @param parentId The ID of this TV input's parent input. {@code null} if none exists.
|
||
|
* @param labelRes The label resource ID of this TvInputInfo. If it is {@code 0},
|
||
|
* {@code service} label will be loaded.
|
||
|
* @param icon The {@link android.graphics.drawable.Icon} to load the icon image. If it is
|
||
|
* {@code null}, the application icon of {@code service} will be loaded.
|
||
|
* @hide
|
||
|
* @deprecated Use {@link Builder} instead.
|
||
|
*/
|
||
|
@Deprecated
|
||
|
@SystemApi
|
||
|
public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service,
|
||
|
HdmiDeviceInfo hdmiDeviceInfo, String parentId, int labelRes, Icon icon)
|
||
|
throws XmlPullParserException, IOException {
|
||
|
return new TvInputInfo.Builder(context, service)
|
||
|
.setHdmiDeviceInfo(hdmiDeviceInfo)
|
||
|
.setParentId(parentId)
|
||
|
.setLabel(labelRes)
|
||
|
.setIcon(icon)
|
||
|
.build();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create a new instance of the TvInputInfo class, instantiating it from the given Context,
|
||
|
* ResolveInfo, and TvInputHardwareInfo.
|
||
|
*
|
||
|
* @param service The ResolveInfo returned from the package manager about this TV input service.
|
||
|
* @param hardwareInfo The TvInputHardwareInfo for a TV input hardware device.
|
||
|
* @param label The label of this TvInputInfo. If it is {@code null} or empty, {@code service}
|
||
|
* label will be loaded.
|
||
|
* @param iconUri The {@link android.net.Uri} to load the icon image. See
|
||
|
* {@link android.content.ContentResolver#openInputStream}. If it is {@code null},
|
||
|
* the application icon of {@code service} will be loaded.
|
||
|
* @hide
|
||
|
* @deprecated Use {@link Builder} instead.
|
||
|
*/
|
||
|
@Deprecated
|
||
|
@SystemApi
|
||
|
public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service,
|
||
|
TvInputHardwareInfo hardwareInfo, String label, Uri iconUri)
|
||
|
throws XmlPullParserException, IOException {
|
||
|
TvInputInfo info = new TvInputInfo.Builder(context, service)
|
||
|
.setTvInputHardwareInfo(hardwareInfo)
|
||
|
.setLabel(label)
|
||
|
.build();
|
||
|
info.mIconUri = iconUri;
|
||
|
return info;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create a new instance of the TvInputInfo class, instantiating it from the given Context,
|
||
|
* ResolveInfo, and TvInputHardwareInfo.
|
||
|
*
|
||
|
* @param service The ResolveInfo returned from the package manager about this TV input service.
|
||
|
* @param hardwareInfo The TvInputHardwareInfo for a TV input hardware device.
|
||
|
* @param labelRes The label resource ID of this TvInputInfo. If it is {@code 0},
|
||
|
* {@code service} label will be loaded.
|
||
|
* @param icon The {@link android.graphics.drawable.Icon} to load the icon image. If it is
|
||
|
* {@code null}, the application icon of {@code service} will be loaded.
|
||
|
* @hide
|
||
|
* @deprecated Use {@link Builder} instead.
|
||
|
*/
|
||
|
@Deprecated
|
||
|
@SystemApi
|
||
|
public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service,
|
||
|
TvInputHardwareInfo hardwareInfo, int labelRes, Icon icon)
|
||
|
throws XmlPullParserException, IOException {
|
||
|
return new TvInputInfo.Builder(context, service)
|
||
|
.setTvInputHardwareInfo(hardwareInfo)
|
||
|
.setLabel(labelRes)
|
||
|
.setIcon(icon)
|
||
|
.build();
|
||
|
}
|
||
|
|
||
|
private TvInputInfo(ResolveInfo service, String id, int type, boolean isHardwareInput,
|
||
|
CharSequence label, int labelResId, Icon icon, Icon iconStandby, Icon iconDisconnected,
|
||
|
String setupActivity, boolean canRecord, boolean canPauseRecording, int tunerCount,
|
||
|
HdmiDeviceInfo hdmiDeviceInfo, boolean isConnectedToHdmiSwitch,
|
||
|
@HdmiAddressRelativePosition int hdmiConnectionRelativePosition, String parentId,
|
||
|
Bundle extras) {
|
||
|
mService = service;
|
||
|
mId = id;
|
||
|
mType = type;
|
||
|
mIsHardwareInput = isHardwareInput;
|
||
|
mLabel = label;
|
||
|
mLabelResId = labelResId;
|
||
|
mIcon = icon;
|
||
|
mIconStandby = iconStandby;
|
||
|
mIconDisconnected = iconDisconnected;
|
||
|
mSetupActivity = setupActivity;
|
||
|
mCanRecord = canRecord;
|
||
|
mCanPauseRecording = canPauseRecording;
|
||
|
mTunerCount = tunerCount;
|
||
|
mHdmiDeviceInfo = hdmiDeviceInfo;
|
||
|
mIsConnectedToHdmiSwitch = isConnectedToHdmiSwitch;
|
||
|
mHdmiConnectionRelativePosition = hdmiConnectionRelativePosition;
|
||
|
mParentId = parentId;
|
||
|
mExtras = extras;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns a unique ID for this TV input. The ID is generated from the package and class name
|
||
|
* implementing the TV input service.
|
||
|
*/
|
||
|
public String getId() {
|
||
|
return mId;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the parent input ID.
|
||
|
*
|
||
|
* <p>A TV input may have a parent input if the TV input is actually a logical representation of
|
||
|
* a device behind the hardware port represented by the parent input.
|
||
|
* For example, a HDMI CEC logical device, connected to a HDMI port, appears as another TV
|
||
|
* input. In this case, the parent input of this logical device is the HDMI port.
|
||
|
*
|
||
|
* <p>Applications may group inputs by parent input ID to provide an easier access to inputs
|
||
|
* sharing the same physical port. In the example of HDMI CEC, logical HDMI CEC devices behind
|
||
|
* the same HDMI port have the same parent ID, which is the ID representing the port. Thus
|
||
|
* applications can group the hardware HDMI port and the logical HDMI CEC devices behind it
|
||
|
* together using this method.
|
||
|
*
|
||
|
* @return the ID of the parent input, if exists. Returns {@code null} if the parent input is
|
||
|
* not specified.
|
||
|
*/
|
||
|
public String getParentId() {
|
||
|
return mParentId;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the information of the service that implements this TV input.
|
||
|
*/
|
||
|
public ServiceInfo getServiceInfo() {
|
||
|
return mService.serviceInfo;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the component of the service that implements this TV input.
|
||
|
* @hide
|
||
|
*/
|
||
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
||
|
public ComponentName getComponent() {
|
||
|
return new ComponentName(mService.serviceInfo.packageName, mService.serviceInfo.name);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns an intent to start the setup activity for this TV input.
|
||
|
*/
|
||
|
public Intent createSetupIntent() {
|
||
|
if (!TextUtils.isEmpty(mSetupActivity)) {
|
||
|
Intent intent = new Intent(Intent.ACTION_MAIN);
|
||
|
intent.setClassName(mService.serviceInfo.packageName, mSetupActivity);
|
||
|
intent.putExtra(EXTRA_INPUT_ID, getId());
|
||
|
return intent;
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns an intent to start the settings activity for this TV input.
|
||
|
*
|
||
|
* @deprecated Use {@link #createSetupIntent()} instead. Settings activity is deprecated.
|
||
|
* Use setup activity instead to provide settings.
|
||
|
*/
|
||
|
@Deprecated
|
||
|
public Intent createSettingsIntent() {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the type of this TV input.
|
||
|
*/
|
||
|
@Type
|
||
|
public int getType() {
|
||
|
return mType;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the number of tuners this TV input has.
|
||
|
*
|
||
|
* <p>This method is valid only for inputs of type {@link #TYPE_TUNER}. For inputs of other
|
||
|
* types, it returns 0.
|
||
|
*
|
||
|
* <p>Tuners correspond to physical/logical resources that allow reception of TV signal. Having
|
||
|
* <i>N</i> tuners means that the TV input is capable of receiving <i>N</i> different channels
|
||
|
* concurrently.
|
||
|
*/
|
||
|
public int getTunerCount() {
|
||
|
return mTunerCount;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns {@code true} if this TV input can record TV programs, {@code false} otherwise.
|
||
|
*/
|
||
|
public boolean canRecord() {
|
||
|
return mCanRecord;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns {@code true} if this TV input can pause recording TV programs,
|
||
|
* {@code false} otherwise.
|
||
|
*/
|
||
|
public boolean canPauseRecording() {
|
||
|
return mCanPauseRecording;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns domain-specific extras associated with this TV input.
|
||
|
*/
|
||
|
public Bundle getExtras() {
|
||
|
return mExtras;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the HDMI device information of this TV input.
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemApi
|
||
|
public HdmiDeviceInfo getHdmiDeviceInfo() {
|
||
|
if (mType == TYPE_HDMI) {
|
||
|
return mHdmiDeviceInfo;
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns {@code true} if this TV input is pass-though which does not have any real channels in
|
||
|
* TvProvider. {@code false} otherwise.
|
||
|
*
|
||
|
* @see TvContract#buildChannelUriForPassthroughInput(String)
|
||
|
*/
|
||
|
public boolean isPassthroughInput() {
|
||
|
return mType != TYPE_TUNER;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns {@code true} if this TV input represents a hardware device. (e.g. built-in tuner,
|
||
|
* HDMI1) {@code false} otherwise.
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemApi
|
||
|
public boolean isHardwareInput() {
|
||
|
return mIsHardwareInput;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns {@code true}, if a CEC device for this TV input is connected to an HDMI switch, i.e.,
|
||
|
* the device isn't directly connected to a HDMI port.
|
||
|
* TODO(b/110094868): add @Deprecated for Q
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemApi
|
||
|
public boolean isConnectedToHdmiSwitch() {
|
||
|
return mIsConnectedToHdmiSwitch;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the relative position of this HDMI input.
|
||
|
* TODO(b/110094868): unhide for Q
|
||
|
* @hide
|
||
|
*/
|
||
|
@HdmiAddressRelativePosition
|
||
|
public int getHdmiConnectionRelativePosition() {
|
||
|
return mHdmiConnectionRelativePosition;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks if this TV input is marked hidden by the user in the settings.
|
||
|
*
|
||
|
* @param context Supplies a {@link Context} used to check if this TV input is hidden.
|
||
|
* @return {@code true} if the user marked this TV input hidden in settings. {@code false}
|
||
|
* otherwise.
|
||
|
*/
|
||
|
public boolean isHidden(Context context) {
|
||
|
return TvInputSettings.isHidden(context, mId, UserHandle.myUserId());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Loads the user-displayed label for this TV input.
|
||
|
*
|
||
|
* @param context Supplies a {@link Context} used to load the label.
|
||
|
* @return a CharSequence containing the TV input's label. If the TV input does not have
|
||
|
* a label, its name is returned.
|
||
|
*/
|
||
|
public CharSequence loadLabel(@NonNull Context context) {
|
||
|
if (mLabelResId != 0) {
|
||
|
return context.getPackageManager().getText(mService.serviceInfo.packageName,
|
||
|
mLabelResId, null);
|
||
|
} else if (!TextUtils.isEmpty(mLabel)) {
|
||
|
return mLabel;
|
||
|
}
|
||
|
return mService.loadLabel(context.getPackageManager());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Loads the custom label set by user in settings.
|
||
|
*
|
||
|
* @param context Supplies a {@link Context} used to load the custom label.
|
||
|
* @return a CharSequence containing the TV input's custom label. {@code null} if there is no
|
||
|
* custom label.
|
||
|
*/
|
||
|
public CharSequence loadCustomLabel(Context context) {
|
||
|
return TvInputSettings.getCustomLabel(context, mId, UserHandle.myUserId());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Loads the user-displayed icon for this TV input.
|
||
|
*
|
||
|
* @param context Supplies a {@link Context} used to load the icon.
|
||
|
* @return a Drawable containing the TV input's icon. If the TV input does not have an icon,
|
||
|
* application's icon is returned. If it's unavailable too, {@code null} is returned.
|
||
|
*/
|
||
|
public Drawable loadIcon(@NonNull Context context) {
|
||
|
if (mIcon != null) {
|
||
|
return mIcon.loadDrawable(context);
|
||
|
} else if (mIconUri != null) {
|
||
|
try (InputStream is = context.getContentResolver().openInputStream(mIconUri)) {
|
||
|
Drawable drawable = Drawable.createFromStream(is, null);
|
||
|
if (drawable != null) {
|
||
|
return drawable;
|
||
|
}
|
||
|
} catch (IOException e) {
|
||
|
Log.w(TAG, "Loading the default icon due to a failure on loading " + mIconUri, e);
|
||
|
// Falls back.
|
||
|
}
|
||
|
}
|
||
|
return loadServiceIcon(context);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Loads the user-displayed icon for this TV input per input state.
|
||
|
*
|
||
|
* @param context Supplies a {@link Context} used to load the icon.
|
||
|
* @param state The input state. Should be one of the followings.
|
||
|
* {@link TvInputManager#INPUT_STATE_CONNECTED},
|
||
|
* {@link TvInputManager#INPUT_STATE_CONNECTED_STANDBY} and
|
||
|
* {@link TvInputManager#INPUT_STATE_DISCONNECTED}.
|
||
|
* @return a Drawable containing the TV input's icon for the given state or {@code null} if such
|
||
|
* an icon is not defined.
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemApi
|
||
|
public Drawable loadIcon(@NonNull Context context, int state) {
|
||
|
if (state == TvInputManager.INPUT_STATE_CONNECTED) {
|
||
|
return loadIcon(context);
|
||
|
} else if (state == TvInputManager.INPUT_STATE_CONNECTED_STANDBY) {
|
||
|
if (mIconStandby != null) {
|
||
|
return mIconStandby.loadDrawable(context);
|
||
|
}
|
||
|
} else if (state == TvInputManager.INPUT_STATE_DISCONNECTED) {
|
||
|
if (mIconDisconnected != null) {
|
||
|
return mIconDisconnected.loadDrawable(context);
|
||
|
}
|
||
|
} else {
|
||
|
throw new IllegalArgumentException("Unknown state: " + state);
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public int describeContents() {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public int hashCode() {
|
||
|
return mId.hashCode();
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public boolean equals(Object o) {
|
||
|
if (o == this) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (!(o instanceof TvInputInfo)) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
TvInputInfo obj = (TvInputInfo) o;
|
||
|
return Objects.equals(mService, obj.mService)
|
||
|
&& TextUtils.equals(mId, obj.mId)
|
||
|
&& mType == obj.mType
|
||
|
&& mIsHardwareInput == obj.mIsHardwareInput
|
||
|
&& TextUtils.equals(mLabel, obj.mLabel)
|
||
|
&& Objects.equals(mIconUri, obj.mIconUri)
|
||
|
&& mLabelResId == obj.mLabelResId
|
||
|
&& Objects.equals(mIcon, obj.mIcon)
|
||
|
&& Objects.equals(mIconStandby, obj.mIconStandby)
|
||
|
&& Objects.equals(mIconDisconnected, obj.mIconDisconnected)
|
||
|
&& TextUtils.equals(mSetupActivity, obj.mSetupActivity)
|
||
|
&& mCanRecord == obj.mCanRecord
|
||
|
&& mCanPauseRecording == obj.mCanPauseRecording
|
||
|
&& mTunerCount == obj.mTunerCount
|
||
|
&& Objects.equals(mHdmiDeviceInfo, obj.mHdmiDeviceInfo)
|
||
|
&& mIsConnectedToHdmiSwitch == obj.mIsConnectedToHdmiSwitch
|
||
|
&& mHdmiConnectionRelativePosition == obj.mHdmiConnectionRelativePosition
|
||
|
&& TextUtils.equals(mParentId, obj.mParentId)
|
||
|
&& Objects.equals(mExtras, obj.mExtras);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public String toString() {
|
||
|
return "TvInputInfo{id=" + mId
|
||
|
+ ", pkg=" + mService.serviceInfo.packageName
|
||
|
+ ", service=" + mService.serviceInfo.name + "}";
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Used to package this object into a {@link Parcel}.
|
||
|
*
|
||
|
* @param dest The {@link Parcel} to be written.
|
||
|
* @param flags The flags used for parceling.
|
||
|
*/
|
||
|
@Override
|
||
|
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||
|
mService.writeToParcel(dest, flags);
|
||
|
dest.writeString(mId);
|
||
|
dest.writeInt(mType);
|
||
|
dest.writeByte(mIsHardwareInput ? (byte) 1 : 0);
|
||
|
TextUtils.writeToParcel(mLabel, dest, flags);
|
||
|
dest.writeParcelable(mIconUri, flags);
|
||
|
dest.writeInt(mLabelResId);
|
||
|
dest.writeParcelable(mIcon, flags);
|
||
|
dest.writeParcelable(mIconStandby, flags);
|
||
|
dest.writeParcelable(mIconDisconnected, flags);
|
||
|
dest.writeString(mSetupActivity);
|
||
|
dest.writeByte(mCanRecord ? (byte) 1 : 0);
|
||
|
dest.writeByte(mCanPauseRecording ? (byte) 1 : 0);
|
||
|
dest.writeInt(mTunerCount);
|
||
|
dest.writeParcelable(mHdmiDeviceInfo, flags);
|
||
|
dest.writeByte(mIsConnectedToHdmiSwitch ? (byte) 1 : 0);
|
||
|
dest.writeInt(mHdmiConnectionRelativePosition);
|
||
|
dest.writeString(mParentId);
|
||
|
dest.writeBundle(mExtras);
|
||
|
}
|
||
|
|
||
|
private Drawable loadServiceIcon(Context context) {
|
||
|
if (mService.serviceInfo.icon == 0
|
||
|
&& mService.serviceInfo.applicationInfo.icon == 0) {
|
||
|
return null;
|
||
|
}
|
||
|
return mService.serviceInfo.loadIcon(context.getPackageManager());
|
||
|
}
|
||
|
|
||
|
public static final @android.annotation.NonNull Parcelable.Creator<TvInputInfo> CREATOR =
|
||
|
new Parcelable.Creator<TvInputInfo>() {
|
||
|
@Override
|
||
|
public TvInputInfo createFromParcel(Parcel in) {
|
||
|
return new TvInputInfo(in);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public TvInputInfo[] newArray(int size) {
|
||
|
return new TvInputInfo[size];
|
||
|
}
|
||
|
};
|
||
|
|
||
|
private TvInputInfo(Parcel in) {
|
||
|
mService = ResolveInfo.CREATOR.createFromParcel(in);
|
||
|
mId = in.readString();
|
||
|
mType = in.readInt();
|
||
|
mIsHardwareInput = in.readByte() == 1;
|
||
|
mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
|
||
|
mIconUri = in.readParcelable(null, android.net.Uri.class);
|
||
|
mLabelResId = in.readInt();
|
||
|
mIcon = in.readParcelable(null, android.graphics.drawable.Icon.class);
|
||
|
mIconStandby = in.readParcelable(null, android.graphics.drawable.Icon.class);
|
||
|
mIconDisconnected = in.readParcelable(null, android.graphics.drawable.Icon.class);
|
||
|
mSetupActivity = in.readString();
|
||
|
mCanRecord = in.readByte() == 1;
|
||
|
mCanPauseRecording = in.readByte() == 1;
|
||
|
mTunerCount = in.readInt();
|
||
|
mHdmiDeviceInfo = in.readParcelable(null, android.hardware.hdmi.HdmiDeviceInfo.class);
|
||
|
mIsConnectedToHdmiSwitch = in.readByte() == 1;
|
||
|
mHdmiConnectionRelativePosition = in.readInt();
|
||
|
mParentId = in.readString();
|
||
|
mExtras = in.readBundle();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* A convenience builder for creating {@link TvInputInfo} objects.
|
||
|
*/
|
||
|
public static final class Builder {
|
||
|
private static final int LENGTH_HDMI_PHYSICAL_ADDRESS = 4;
|
||
|
private static final int LENGTH_HDMI_DEVICE_ID = 2;
|
||
|
|
||
|
private static final String XML_START_TAG_NAME = "tv-input";
|
||
|
private static final String DELIMITER_INFO_IN_ID = "/";
|
||
|
private static final String PREFIX_HDMI_DEVICE = "HDMI";
|
||
|
private static final String PREFIX_HARDWARE_DEVICE = "HW";
|
||
|
|
||
|
private static final SparseIntArray sHardwareTypeToTvInputType = new SparseIntArray();
|
||
|
static {
|
||
|
sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_OTHER_HARDWARE,
|
||
|
TYPE_OTHER);
|
||
|
sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_TUNER, TYPE_TUNER);
|
||
|
sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_COMPOSITE,
|
||
|
TYPE_COMPOSITE);
|
||
|
sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_SVIDEO, TYPE_SVIDEO);
|
||
|
sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_SCART, TYPE_SCART);
|
||
|
sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_COMPONENT,
|
||
|
TYPE_COMPONENT);
|
||
|
sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_VGA, TYPE_VGA);
|
||
|
sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_DVI, TYPE_DVI);
|
||
|
sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_HDMI, TYPE_HDMI);
|
||
|
sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_DISPLAY_PORT,
|
||
|
TYPE_DISPLAY_PORT);
|
||
|
}
|
||
|
|
||
|
private final Context mContext;
|
||
|
private final ResolveInfo mResolveInfo;
|
||
|
private CharSequence mLabel;
|
||
|
private int mLabelResId;
|
||
|
private Icon mIcon;
|
||
|
private Icon mIconStandby;
|
||
|
private Icon mIconDisconnected;
|
||
|
private String mSetupActivity;
|
||
|
private Boolean mCanRecord;
|
||
|
private Boolean mCanPauseRecording;
|
||
|
private Integer mTunerCount;
|
||
|
private TvInputHardwareInfo mTvInputHardwareInfo;
|
||
|
private HdmiDeviceInfo mHdmiDeviceInfo;
|
||
|
private String mParentId;
|
||
|
private Bundle mExtras;
|
||
|
|
||
|
/**
|
||
|
* Constructs a new builder for {@link TvInputInfo}.
|
||
|
*
|
||
|
* @param context A Context of the application package implementing this class.
|
||
|
* @param component The name of the application component to be used for the
|
||
|
* {@link TvInputService}.
|
||
|
*/
|
||
|
public Builder(Context context, ComponentName component) {
|
||
|
if (context == null) {
|
||
|
throw new IllegalArgumentException("context cannot be null.");
|
||
|
}
|
||
|
Intent intent = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(component);
|
||
|
mResolveInfo = context.getPackageManager().resolveService(intent,
|
||
|
PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
|
||
|
if (mResolveInfo == null) {
|
||
|
throw new IllegalArgumentException("Invalid component. Can't find the service.");
|
||
|
}
|
||
|
mContext = context;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Constructs a new builder for {@link TvInputInfo}.
|
||
|
*
|
||
|
* @param resolveInfo The ResolveInfo returned from the package manager about this TV input
|
||
|
* service.
|
||
|
* @hide
|
||
|
*/
|
||
|
public Builder(Context context, ResolveInfo resolveInfo) {
|
||
|
if (context == null) {
|
||
|
throw new IllegalArgumentException("context cannot be null");
|
||
|
}
|
||
|
if (resolveInfo == null) {
|
||
|
throw new IllegalArgumentException("resolveInfo cannot be null");
|
||
|
}
|
||
|
mContext = context;
|
||
|
mResolveInfo = resolveInfo;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets the icon.
|
||
|
*
|
||
|
* @param icon The icon that represents this TV input.
|
||
|
* @return This Builder object to allow for chaining of calls to builder methods.
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemApi
|
||
|
public Builder setIcon(Icon icon) {
|
||
|
this.mIcon = icon;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets the icon for a given input state.
|
||
|
*
|
||
|
* @param icon The icon that represents this TV input for the given state.
|
||
|
* @param state The input state. Should be one of the followings.
|
||
|
* {@link TvInputManager#INPUT_STATE_CONNECTED},
|
||
|
* {@link TvInputManager#INPUT_STATE_CONNECTED_STANDBY} and
|
||
|
* {@link TvInputManager#INPUT_STATE_DISCONNECTED}.
|
||
|
* @return This Builder object to allow for chaining of calls to builder methods.
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemApi
|
||
|
public Builder setIcon(Icon icon, int state) {
|
||
|
if (state == TvInputManager.INPUT_STATE_CONNECTED) {
|
||
|
this.mIcon = icon;
|
||
|
} else if (state == TvInputManager.INPUT_STATE_CONNECTED_STANDBY) {
|
||
|
this.mIconStandby = icon;
|
||
|
} else if (state == TvInputManager.INPUT_STATE_DISCONNECTED) {
|
||
|
this.mIconDisconnected = icon;
|
||
|
} else {
|
||
|
throw new IllegalArgumentException("Unknown state: " + state);
|
||
|
}
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets the label.
|
||
|
*
|
||
|
* @param label The text to be used as label.
|
||
|
* @return This Builder object to allow for chaining of calls to builder methods.
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemApi
|
||
|
public Builder setLabel(CharSequence label) {
|
||
|
if (mLabelResId != 0) {
|
||
|
throw new IllegalStateException("Resource ID for label is already set.");
|
||
|
}
|
||
|
this.mLabel = label;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets the label.
|
||
|
*
|
||
|
* @param resId The resource ID of the text to use.
|
||
|
* @return This Builder object to allow for chaining of calls to builder methods.
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemApi
|
||
|
public Builder setLabel(@StringRes int resId) {
|
||
|
if (mLabel != null) {
|
||
|
throw new IllegalStateException("Label text is already set.");
|
||
|
}
|
||
|
this.mLabelResId = resId;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets the HdmiDeviceInfo.
|
||
|
*
|
||
|
* @param hdmiDeviceInfo The HdmiDeviceInfo for a HDMI CEC logical device.
|
||
|
* @return This Builder object to allow for chaining of calls to builder methods.
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemApi
|
||
|
public Builder setHdmiDeviceInfo(HdmiDeviceInfo hdmiDeviceInfo) {
|
||
|
if (mTvInputHardwareInfo != null) {
|
||
|
Log.w(TAG, "TvInputHardwareInfo will not be used to build this TvInputInfo");
|
||
|
mTvInputHardwareInfo = null;
|
||
|
}
|
||
|
this.mHdmiDeviceInfo = hdmiDeviceInfo;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets the parent ID.
|
||
|
*
|
||
|
* @param parentId The parent ID.
|
||
|
* @return This Builder object to allow for chaining of calls to builder methods.
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemApi
|
||
|
public Builder setParentId(String parentId) {
|
||
|
this.mParentId = parentId;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets the TvInputHardwareInfo.
|
||
|
*
|
||
|
* @param tvInputHardwareInfo
|
||
|
* @return This Builder object to allow for chaining of calls to builder methods.
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemApi
|
||
|
public Builder setTvInputHardwareInfo(TvInputHardwareInfo tvInputHardwareInfo) {
|
||
|
if (mHdmiDeviceInfo != null) {
|
||
|
Log.w(TAG, "mHdmiDeviceInfo will not be used to build this TvInputInfo");
|
||
|
mHdmiDeviceInfo = null;
|
||
|
}
|
||
|
this.mTvInputHardwareInfo = tvInputHardwareInfo;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets the tuner count. Valid only for {@link #TYPE_TUNER}.
|
||
|
*
|
||
|
* @param tunerCount The number of tuners this TV input has.
|
||
|
* @return This Builder object to allow for chaining of calls to builder methods.
|
||
|
*/
|
||
|
public Builder setTunerCount(int tunerCount) {
|
||
|
this.mTunerCount = tunerCount;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets whether this TV input can record TV programs or not.
|
||
|
*
|
||
|
* @param canRecord Whether this TV input can record TV programs.
|
||
|
* @return This Builder object to allow for chaining of calls to builder methods.
|
||
|
*/
|
||
|
public Builder setCanRecord(boolean canRecord) {
|
||
|
this.mCanRecord = canRecord;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets whether this TV input can pause recording TV programs or not.
|
||
|
*
|
||
|
* @param canPauseRecording Whether this TV input can pause recording TV programs.
|
||
|
* @return This Builder object to allow for chaining of calls to builder methods.
|
||
|
*/
|
||
|
@NonNull
|
||
|
public Builder setCanPauseRecording(boolean canPauseRecording) {
|
||
|
this.mCanPauseRecording = canPauseRecording;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets domain-specific extras associated with this TV input.
|
||
|
*
|
||
|
* @param extras Domain-specific extras associated with this TV input. Keys <em>must</em> be
|
||
|
* a scoped name, i.e. prefixed with a package name you own, so that different
|
||
|
* developers will not create conflicting keys.
|
||
|
* @return This Builder object to allow for chaining of calls to builder methods.
|
||
|
*/
|
||
|
public Builder setExtras(Bundle extras) {
|
||
|
this.mExtras = extras;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Creates a {@link TvInputInfo} instance with the specified fields. Most of the information
|
||
|
* is obtained by parsing the AndroidManifest and {@link TvInputService#SERVICE_META_DATA}
|
||
|
* for the {@link TvInputService} this TV input implements.
|
||
|
*
|
||
|
* @return TvInputInfo containing information about this TV input.
|
||
|
*/
|
||
|
public TvInputInfo build() {
|
||
|
ComponentName componentName = new ComponentName(mResolveInfo.serviceInfo.packageName,
|
||
|
mResolveInfo.serviceInfo.name);
|
||
|
String id;
|
||
|
int type;
|
||
|
boolean isHardwareInput = false;
|
||
|
boolean isConnectedToHdmiSwitch = false;
|
||
|
@HdmiAddressRelativePosition
|
||
|
int hdmiConnectionRelativePosition = HdmiUtils.HDMI_RELATIVE_POSITION_UNKNOWN;
|
||
|
|
||
|
if (mHdmiDeviceInfo != null) {
|
||
|
id = generateInputId(componentName, mHdmiDeviceInfo);
|
||
|
type = TYPE_HDMI;
|
||
|
isHardwareInput = true;
|
||
|
hdmiConnectionRelativePosition = getRelativePosition(mContext, mHdmiDeviceInfo);
|
||
|
isConnectedToHdmiSwitch = hdmiConnectionRelativePosition
|
||
|
== HdmiUtils.HDMI_RELATIVE_POSITION_BELOW;
|
||
|
} else if (mTvInputHardwareInfo != null) {
|
||
|
id = generateInputId(componentName, mTvInputHardwareInfo);
|
||
|
type = sHardwareTypeToTvInputType.get(mTvInputHardwareInfo.getType(), TYPE_TUNER);
|
||
|
isHardwareInput = true;
|
||
|
if (mTvInputHardwareInfo.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
|
||
|
mHdmiDeviceInfo = HdmiDeviceInfo.hardwarePort(
|
||
|
HdmiDeviceInfo.PATH_INVALID, mTvInputHardwareInfo.getHdmiPortId());
|
||
|
}
|
||
|
} else {
|
||
|
id = generateInputId(componentName);
|
||
|
type = TYPE_TUNER;
|
||
|
}
|
||
|
parseServiceMetadata(type);
|
||
|
return new TvInputInfo(mResolveInfo, id, type, isHardwareInput, mLabel, mLabelResId,
|
||
|
mIcon, mIconStandby, mIconDisconnected, mSetupActivity,
|
||
|
mCanRecord == null ? false : mCanRecord,
|
||
|
mCanPauseRecording == null ? false : mCanPauseRecording,
|
||
|
mTunerCount == null ? 0 : mTunerCount,
|
||
|
mHdmiDeviceInfo, isConnectedToHdmiSwitch, hdmiConnectionRelativePosition,
|
||
|
mParentId, mExtras);
|
||
|
}
|
||
|
|
||
|
private static String generateInputId(ComponentName name) {
|
||
|
return name.flattenToShortString();
|
||
|
}
|
||
|
|
||
|
private static String generateInputId(ComponentName name, HdmiDeviceInfo hdmiDeviceInfo) {
|
||
|
// Example of the format : "/HDMI%04X%02X"
|
||
|
String format = DELIMITER_INFO_IN_ID + PREFIX_HDMI_DEVICE
|
||
|
+ "%0" + LENGTH_HDMI_PHYSICAL_ADDRESS + "X"
|
||
|
+ "%0" + LENGTH_HDMI_DEVICE_ID + "X";
|
||
|
return name.flattenToShortString() + String.format(Locale.ENGLISH, format,
|
||
|
hdmiDeviceInfo.getPhysicalAddress(), hdmiDeviceInfo.getId());
|
||
|
}
|
||
|
|
||
|
private static String generateInputId(ComponentName name,
|
||
|
TvInputHardwareInfo tvInputHardwareInfo) {
|
||
|
return name.flattenToShortString() + DELIMITER_INFO_IN_ID + PREFIX_HARDWARE_DEVICE
|
||
|
+ tvInputHardwareInfo.getDeviceId();
|
||
|
}
|
||
|
|
||
|
private static int getRelativePosition(Context context, HdmiDeviceInfo info) {
|
||
|
HdmiControlManager hcm =
|
||
|
(HdmiControlManager) context.getSystemService(Context.HDMI_CONTROL_SERVICE);
|
||
|
if (hcm == null) {
|
||
|
return HdmiUtils.HDMI_RELATIVE_POSITION_UNKNOWN;
|
||
|
}
|
||
|
return HdmiUtils.getHdmiAddressRelativePosition(
|
||
|
info.getPhysicalAddress(), hcm.getPhysicalAddress());
|
||
|
}
|
||
|
|
||
|
private void parseServiceMetadata(int inputType) {
|
||
|
ServiceInfo si = mResolveInfo.serviceInfo;
|
||
|
PackageManager pm = mContext.getPackageManager();
|
||
|
try (XmlResourceParser parser =
|
||
|
si.loadXmlMetaData(pm, TvInputService.SERVICE_META_DATA)) {
|
||
|
if (parser == null) {
|
||
|
throw new IllegalStateException("No " + TvInputService.SERVICE_META_DATA
|
||
|
+ " meta-data found for " + si.name);
|
||
|
}
|
||
|
|
||
|
Resources res = pm.getResourcesForApplication(si.applicationInfo);
|
||
|
AttributeSet attrs = Xml.asAttributeSet(parser);
|
||
|
|
||
|
int type;
|
||
|
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
|
||
|
&& type != XmlPullParser.START_TAG) {
|
||
|
}
|
||
|
|
||
|
String nodeName = parser.getName();
|
||
|
if (!XML_START_TAG_NAME.equals(nodeName)) {
|
||
|
throw new IllegalStateException("Meta-data does not start with "
|
||
|
+ XML_START_TAG_NAME + " tag for " + si.name);
|
||
|
}
|
||
|
|
||
|
TypedArray sa = res.obtainAttributes(attrs,
|
||
|
com.android.internal.R.styleable.TvInputService);
|
||
|
mSetupActivity = sa.getString(
|
||
|
com.android.internal.R.styleable.TvInputService_setupActivity);
|
||
|
if (mCanRecord == null) {
|
||
|
mCanRecord = sa.getBoolean(
|
||
|
com.android.internal.R.styleable.TvInputService_canRecord, false);
|
||
|
}
|
||
|
if (mTunerCount == null && inputType == TYPE_TUNER) {
|
||
|
mTunerCount = sa.getInt(
|
||
|
com.android.internal.R.styleable.TvInputService_tunerCount, 1);
|
||
|
}
|
||
|
if (mCanPauseRecording == null) {
|
||
|
mCanPauseRecording = sa.getBoolean(
|
||
|
com.android.internal.R.styleable.TvInputService_canPauseRecording,
|
||
|
false);
|
||
|
}
|
||
|
|
||
|
sa.recycle();
|
||
|
} catch (IOException | XmlPullParserException e) {
|
||
|
throw new IllegalStateException("Failed reading meta-data for " + si.packageName, e);
|
||
|
} catch (NameNotFoundException e) {
|
||
|
throw new IllegalStateException("No resources found for " + si.packageName, e);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Utility class for putting and getting settings for TV input.
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemApi
|
||
|
public static final class TvInputSettings {
|
||
|
private static final String TV_INPUT_SEPARATOR = ":";
|
||
|
private static final String CUSTOM_NAME_SEPARATOR = ",";
|
||
|
|
||
|
private TvInputSettings() { }
|
||
|
|
||
|
private static boolean isHidden(Context context, String inputId, int userId) {
|
||
|
return getHiddenTvInputIds(context, userId).contains(inputId);
|
||
|
}
|
||
|
|
||
|
private static String getCustomLabel(Context context, String inputId, int userId) {
|
||
|
return getCustomLabels(context, userId).get(inputId);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns a set of TV input IDs which are marked as hidden by user in the settings.
|
||
|
*
|
||
|
* @param context The application context
|
||
|
* @param userId The user ID for the stored hidden input set
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemApi
|
||
|
public static Set<String> getHiddenTvInputIds(Context context, int userId) {
|
||
|
String hiddenIdsString = Settings.Secure.getStringForUser(
|
||
|
context.getContentResolver(), Settings.Secure.TV_INPUT_HIDDEN_INPUTS, userId);
|
||
|
Set<String> set = new HashSet<>();
|
||
|
if (TextUtils.isEmpty(hiddenIdsString)) {
|
||
|
return set;
|
||
|
}
|
||
|
String[] ids = hiddenIdsString.split(TV_INPUT_SEPARATOR);
|
||
|
for (String id : ids) {
|
||
|
set.add(Uri.decode(id));
|
||
|
}
|
||
|
return set;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns a map of TV input ID/custom label pairs set by the user in the settings.
|
||
|
*
|
||
|
* @param context The application context
|
||
|
* @param userId The user ID for the stored hidden input map
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemApi
|
||
|
public static Map<String, String> getCustomLabels(Context context, int userId) {
|
||
|
String labelsString = Settings.Secure.getStringForUser(
|
||
|
context.getContentResolver(), Settings.Secure.TV_INPUT_CUSTOM_LABELS, userId);
|
||
|
Map<String, String> map = new HashMap<>();
|
||
|
if (TextUtils.isEmpty(labelsString)) {
|
||
|
return map;
|
||
|
}
|
||
|
String[] pairs = labelsString.split(TV_INPUT_SEPARATOR);
|
||
|
for (String pairString : pairs) {
|
||
|
String[] pair = pairString.split(CUSTOM_NAME_SEPARATOR);
|
||
|
map.put(Uri.decode(pair[0]), Uri.decode(pair[1]));
|
||
|
}
|
||
|
return map;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Stores a set of TV input IDs which are marked as hidden by user. This is expected to
|
||
|
* be called from the settings app.
|
||
|
*
|
||
|
* @param context The application context
|
||
|
* @param hiddenInputIds A set including all the hidden TV input IDs
|
||
|
* @param userId The user ID for the stored hidden input set
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemApi
|
||
|
public static void putHiddenTvInputs(Context context, Set<String> hiddenInputIds,
|
||
|
int userId) {
|
||
|
StringBuilder builder = new StringBuilder();
|
||
|
boolean firstItem = true;
|
||
|
for (String inputId : hiddenInputIds) {
|
||
|
ensureValidField(inputId);
|
||
|
if (firstItem) {
|
||
|
firstItem = false;
|
||
|
} else {
|
||
|
builder.append(TV_INPUT_SEPARATOR);
|
||
|
}
|
||
|
builder.append(Uri.encode(inputId));
|
||
|
}
|
||
|
Settings.Secure.putStringForUser(context.getContentResolver(),
|
||
|
Settings.Secure.TV_INPUT_HIDDEN_INPUTS, builder.toString(), userId);
|
||
|
|
||
|
// Notify of the TvInputInfo changes.
|
||
|
TvInputManager tm = (TvInputManager) context.getSystemService(Context.TV_INPUT_SERVICE);
|
||
|
for (String inputId : hiddenInputIds) {
|
||
|
TvInputInfo info = tm.getTvInputInfo(inputId);
|
||
|
if (info != null) {
|
||
|
tm.updateTvInputInfo(info);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Stores a map of TV input ID/custom label set by user. This is expected to be
|
||
|
* called from the settings app.
|
||
|
*
|
||
|
* @param context The application context.
|
||
|
* @param customLabels A map of TV input ID/custom label pairs
|
||
|
* @param userId The user ID for the stored hidden input map
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemApi
|
||
|
public static void putCustomLabels(Context context,
|
||
|
Map<String, String> customLabels, int userId) {
|
||
|
StringBuilder builder = new StringBuilder();
|
||
|
boolean firstItem = true;
|
||
|
for (Map.Entry<String, String> entry: customLabels.entrySet()) {
|
||
|
ensureValidField(entry.getKey());
|
||
|
ensureValidField(entry.getValue());
|
||
|
if (firstItem) {
|
||
|
firstItem = false;
|
||
|
} else {
|
||
|
builder.append(TV_INPUT_SEPARATOR);
|
||
|
}
|
||
|
builder.append(Uri.encode(entry.getKey()));
|
||
|
builder.append(CUSTOM_NAME_SEPARATOR);
|
||
|
builder.append(Uri.encode(entry.getValue()));
|
||
|
}
|
||
|
Settings.Secure.putStringForUser(context.getContentResolver(),
|
||
|
Settings.Secure.TV_INPUT_CUSTOM_LABELS, builder.toString(), userId);
|
||
|
|
||
|
// Notify of the TvInputInfo changes.
|
||
|
TvInputManager tm = (TvInputManager) context.getSystemService(Context.TV_INPUT_SERVICE);
|
||
|
for (String inputId : customLabels.keySet()) {
|
||
|
TvInputInfo info = tm.getTvInputInfo(inputId);
|
||
|
if (info != null) {
|
||
|
tm.updateTvInputInfo(info);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static void ensureValidField(String value) {
|
||
|
if (TextUtils.isEmpty(value)) {
|
||
|
throw new IllegalArgumentException(value + " should not empty ");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|