280 lines
9.8 KiB
Java
280 lines
9.8 KiB
Java
/*
|
|
* Copyright 2021 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.interactive;
|
|
|
|
import android.annotation.IntDef;
|
|
import android.annotation.NonNull;
|
|
import android.annotation.Nullable;
|
|
import android.content.ComponentName;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.pm.PackageManager;
|
|
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.os.Parcel;
|
|
import android.os.Parcelable;
|
|
import android.util.AttributeSet;
|
|
import android.util.Xml;
|
|
|
|
import org.xmlpull.v1.XmlPullParser;
|
|
import org.xmlpull.v1.XmlPullParserException;
|
|
|
|
import java.io.IOException;
|
|
import java.lang.annotation.Retention;
|
|
import java.lang.annotation.RetentionPolicy;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* This class is used to specify meta information of a TV interactive app.
|
|
*/
|
|
public final class TvInteractiveAppServiceInfo implements Parcelable {
|
|
private static final boolean DEBUG = false;
|
|
private static final String TAG = "TvInteractiveAppServiceInfo";
|
|
|
|
private static final String XML_START_TAG_NAME = "tv-interactive-app";
|
|
|
|
/** @hide */
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@IntDef(prefix = { "INTERACTIVE_APP_TYPE_" }, value = {
|
|
INTERACTIVE_APP_TYPE_HBBTV,
|
|
INTERACTIVE_APP_TYPE_ATSC,
|
|
INTERACTIVE_APP_TYPE_GINGA,
|
|
INTERACTIVE_APP_TYPE_TARGETED_AD,
|
|
INTERACTIVE_APP_TYPE_OTHER
|
|
})
|
|
public @interface InteractiveAppType {}
|
|
|
|
/** HbbTV interactive app type */
|
|
public static final int INTERACTIVE_APP_TYPE_HBBTV = 0x1;
|
|
/** ATSC interactive app type */
|
|
public static final int INTERACTIVE_APP_TYPE_ATSC = 0x2;
|
|
/** Ginga interactive app type */
|
|
public static final int INTERACTIVE_APP_TYPE_GINGA = 0x4;
|
|
/** Targeted Advertisement interactive app type */
|
|
public static final int INTERACTIVE_APP_TYPE_TARGETED_AD = 0x8;
|
|
/** Other interactive app type */
|
|
public static final int INTERACTIVE_APP_TYPE_OTHER = 0x80000000;
|
|
|
|
private final ResolveInfo mService;
|
|
private final String mId;
|
|
private int mTypes;
|
|
private final List<String> mExtraTypes = new ArrayList<>();
|
|
|
|
/**
|
|
* Constructs a TvInteractiveAppServiceInfo object.
|
|
*
|
|
* @param context the application context
|
|
* @param component the component name of the TvInteractiveAppService
|
|
*/
|
|
public TvInteractiveAppServiceInfo(@NonNull Context context, @NonNull ComponentName component) {
|
|
if (context == null) {
|
|
throw new IllegalArgumentException("context cannot be null.");
|
|
}
|
|
Intent intent =
|
|
new Intent(TvInteractiveAppService.SERVICE_INTERFACE).setComponent(component);
|
|
ResolveInfo resolveInfo = context.getPackageManager().resolveService(intent,
|
|
PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
|
|
if (resolveInfo == null) {
|
|
throw new IllegalArgumentException("Invalid component. Can't find the service.");
|
|
}
|
|
|
|
ComponentName componentName = new ComponentName(resolveInfo.serviceInfo.packageName,
|
|
resolveInfo.serviceInfo.name);
|
|
String id;
|
|
id = generateInteractiveAppServiceId(componentName);
|
|
List<String> types = new ArrayList<>();
|
|
parseServiceMetadata(resolveInfo, context, types);
|
|
|
|
mService = resolveInfo;
|
|
mId = id;
|
|
toTypesFlag(types);
|
|
}
|
|
private TvInteractiveAppServiceInfo(
|
|
ResolveInfo service, String id, int types, List<String> extraTypes) {
|
|
mService = service;
|
|
mId = id;
|
|
mTypes = types;
|
|
mExtraTypes.addAll(extraTypes);
|
|
}
|
|
|
|
private TvInteractiveAppServiceInfo(@NonNull Parcel in) {
|
|
mService = ResolveInfo.CREATOR.createFromParcel(in);
|
|
mId = in.readString();
|
|
mTypes = in.readInt();
|
|
in.readStringList(mExtraTypes);
|
|
}
|
|
|
|
public static final @NonNull Creator<TvInteractiveAppServiceInfo> CREATOR =
|
|
new Creator<TvInteractiveAppServiceInfo>() {
|
|
@Override
|
|
public TvInteractiveAppServiceInfo createFromParcel(Parcel in) {
|
|
return new TvInteractiveAppServiceInfo(in);
|
|
}
|
|
|
|
@Override
|
|
public TvInteractiveAppServiceInfo[] newArray(int size) {
|
|
return new TvInteractiveAppServiceInfo[size];
|
|
}
|
|
};
|
|
|
|
@Override
|
|
public int describeContents() {
|
|
return 0;
|
|
}
|
|
|
|
@Override
|
|
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
|
mService.writeToParcel(dest, flags);
|
|
dest.writeString(mId);
|
|
dest.writeInt(mTypes);
|
|
dest.writeStringList(mExtraTypes);
|
|
}
|
|
|
|
/**
|
|
* Returns a unique ID for this TV interactive app service. The ID is generated from the package
|
|
* and class name implementing the TV interactive app service.
|
|
*/
|
|
@NonNull
|
|
public String getId() {
|
|
return mId;
|
|
}
|
|
|
|
/**
|
|
* Returns the component of the TV Interactive App service.
|
|
* @hide
|
|
*/
|
|
public ComponentName getComponent() {
|
|
return new ComponentName(mService.serviceInfo.packageName, mService.serviceInfo.name);
|
|
}
|
|
|
|
/**
|
|
* Returns the information of the service that implements this TV Interactive App service.
|
|
*/
|
|
@Nullable
|
|
public ServiceInfo getServiceInfo() {
|
|
return mService.serviceInfo;
|
|
}
|
|
|
|
/**
|
|
* Gets supported interactive app types.
|
|
*
|
|
* <p>The supported interactive app types is in a bit map format. For example:
|
|
* <pre><code>
|
|
* int types = tvInteractiveAppInfo.getSupportedTypes();
|
|
* if (types & TvInteractiveAppInfo.INTERACTIVE_APP_TYPE_HBBTV != 0) {
|
|
* // HbbTV type is supported. Do something...
|
|
* }
|
|
* if (types & TvInteractiveAppInfo.INTERACTIVE_APP_TYPE_ATSC == 0) {
|
|
* // ATSC type is not supported. Do something...
|
|
* }
|
|
* </code></pre>
|
|
*
|
|
* @return An int bit map representing supported types.
|
|
*/
|
|
@InteractiveAppType
|
|
@NonNull
|
|
public int getSupportedTypes() {
|
|
return mTypes;
|
|
}
|
|
|
|
/**
|
|
* Gets custom supported interactive app types which are not listed.
|
|
*
|
|
* @see #getSupportedTypes()
|
|
*/
|
|
@NonNull
|
|
public List<String> getCustomSupportedTypes() {
|
|
return mExtraTypes;
|
|
}
|
|
|
|
private static String generateInteractiveAppServiceId(ComponentName name) {
|
|
return name.flattenToShortString();
|
|
}
|
|
|
|
private static void parseServiceMetadata(
|
|
ResolveInfo resolveInfo, Context context, List<String> types) {
|
|
ServiceInfo si = resolveInfo.serviceInfo;
|
|
PackageManager pm = context.getPackageManager();
|
|
try (XmlResourceParser parser =
|
|
si.loadXmlMetaData(pm, TvInteractiveAppService.SERVICE_META_DATA)) {
|
|
if (parser == null) {
|
|
throw new IllegalStateException(
|
|
"No " + TvInteractiveAppService.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) {
|
|
// move to the 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.TvInteractiveAppService);
|
|
CharSequence[] textArr = sa.getTextArray(
|
|
com.android.internal.R.styleable.TvInteractiveAppService_supportedTypes);
|
|
for (CharSequence cs : textArr) {
|
|
types.add(cs.toString().toLowerCase());
|
|
}
|
|
|
|
sa.recycle();
|
|
} catch (IOException | XmlPullParserException e) {
|
|
throw new IllegalStateException(
|
|
"Failed reading meta-data for " + si.packageName, e);
|
|
} catch (PackageManager.NameNotFoundException e) {
|
|
throw new IllegalStateException("No resources found for " + si.packageName, e);
|
|
}
|
|
}
|
|
|
|
private void toTypesFlag(List<String> types) {
|
|
mTypes = 0;
|
|
mExtraTypes.clear();
|
|
for (String type : types) {
|
|
switch (type) {
|
|
case "hbbtv":
|
|
mTypes |= INTERACTIVE_APP_TYPE_HBBTV;
|
|
break;
|
|
case "atsc":
|
|
mTypes |= INTERACTIVE_APP_TYPE_ATSC;
|
|
break;
|
|
case "ginga":
|
|
mTypes |= INTERACTIVE_APP_TYPE_GINGA;
|
|
break;
|
|
case "targeted_ad":
|
|
mTypes |= INTERACTIVE_APP_TYPE_TARGETED_AD;
|
|
default:
|
|
mTypes |= INTERACTIVE_APP_TYPE_OTHER;
|
|
mExtraTypes.add(type);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|