diff options
Diffstat (limited to 'partner_support/src/com/google/android/tv')
10 files changed, 946 insertions, 0 deletions
diff --git a/partner_support/src/com/google/android/tv/partner/support/AutoValue_EpgInput.java b/partner_support/src/com/google/android/tv/partner/support/AutoValue_EpgInput.java new file mode 100644 index 00000000..aad51c76 --- /dev/null +++ b/partner_support/src/com/google/android/tv/partner/support/AutoValue_EpgInput.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2018 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.google.android.tv.partner.support; + + + +/** + * Hand copy of generated Autovalue class. + * + * TODO get autovalue working + */ +final class AutoValue_EpgInput extends EpgInput { + + private final long id; + private final String inputId; + private final String lineupId; + + AutoValue_EpgInput( + long id, + String inputId, + String lineupId) { + this.id = id; + if (inputId == null) { + throw new NullPointerException("Null inputId"); + } + this.inputId = inputId; + if (lineupId == null) { + throw new NullPointerException("Null lineupId"); + } + this.lineupId = lineupId; + } + + @Override + public long getId() { + return id; + } + + @Override + public String getInputId() { + return inputId; + } + + @Override + public String getLineupId() { + return lineupId; + } + + @Override + public String toString() { + return "EpgInput{" + + "id=" + id + ", " + + "inputId=" + inputId + ", " + + "lineupId=" + lineupId + + "}"; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o instanceof EpgInput) { + EpgInput that = (EpgInput) o; + return (this.id == that.getId()) + && (this.inputId.equals(that.getInputId())) + && (this.lineupId.equals(that.getLineupId())); + } + return false; + } + + @Override + public int hashCode() { + int h$ = 1; + h$ *= 1000003; + h$ ^= (int) ((id >>> 32) ^ id); + h$ *= 1000003; + h$ ^= inputId.hashCode(); + h$ *= 1000003; + h$ ^= lineupId.hashCode(); + return h$; + } + +} diff --git a/partner_support/src/com/google/android/tv/partner/support/AutoValue_Lineup.java b/partner_support/src/com/google/android/tv/partner/support/AutoValue_Lineup.java new file mode 100644 index 00000000..076f8a2a --- /dev/null +++ b/partner_support/src/com/google/android/tv/partner/support/AutoValue_Lineup.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2018 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.google.android.tv.partner.support; + +import android.support.annotation.Nullable; +import java.util.List; + +/** + * Hand copy of generated Autovalue class. + * + * TODO get autovalue working + */ + +final class AutoValue_Lineup extends Lineup { + + private final String id; + private final int type; + private final String name; + private final List<String> channels; + + AutoValue_Lineup( + String id, + int type, + @Nullable String name, + List<String> channels) { + if (id == null) { + throw new NullPointerException("Null id"); + } + this.id = id; + this.type = type; + this.name = name; + if (channels == null) { + throw new NullPointerException("Null channels"); + } + this.channels = channels; + } + + @Override + public String getId() { + return id; + } + + @Override + public int getType() { + return type; + } + + @Nullable + @Override + public String getName() { + return name; + } + + @Override + public List<String> getChannels() { + return channels; + } + + @Override + public String toString() { + return "Lineup{" + + "id=" + id + ", " + + "type=" + type + ", " + + "name=" + name + ", " + + "channels=" + channels + + "}"; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o instanceof Lineup) { + Lineup that = (Lineup) o; + return (this.id.equals(that.getId())) + && (this.type == that.getType()) + && ((this.name == null) ? (that.getName() == null) : this.name.equals(that.getName())) + && (this.channels.equals(that.getChannels())); + } + return false; + } + + @Override + public int hashCode() { + int h$ = 1; + h$ *= 1000003; + h$ ^= id.hashCode(); + h$ *= 1000003; + h$ ^= type; + h$ *= 1000003; + h$ ^= (name == null) ? 0 : name.hashCode(); + h$ *= 1000003; + h$ ^= channels.hashCode(); + return h$; + } + +} diff --git a/partner_support/src/com/google/android/tv/partner/support/BaseCustomization.java b/partner_support/src/com/google/android/tv/partner/support/BaseCustomization.java new file mode 100644 index 00000000..e40d90d7 --- /dev/null +++ b/partner_support/src/com/google/android/tv/partner/support/BaseCustomization.java @@ -0,0 +1,93 @@ +/* + * 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.google.android.tv.partner.support; + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.os.Build; +import android.util.Log; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +/** + * Abstract class for TV Customization support. + * + * <p>Implement customization resources as needed in a child class. + */ +@TargetApi(Build.VERSION_CODES.N) +@SuppressWarnings("AndroidApiChecker") // TODO(b/32513850) remove when error prone is updated +public class BaseCustomization { + private static final boolean DEBUG = false; + private static final String TAG = "BaseCustomization"; + + // TODO move common aosp library + + // TODO cache resource if performance suffers. + + private static final String RES_TYPE_BOOLEAN = "bool"; + + private final String packageName; + + protected BaseCustomization(Context context, String[] permissions) { + packageName = getFirstPackageNameWithPermissions(context, permissions); + } + + public final String getPackageName() { + return packageName; + } + + private static String getFirstPackageNameWithPermissions( + Context context, String[] permissions) { + + List<PackageInfo> packageInfos = + context.getPackageManager().getPackagesHoldingPermissions(permissions, 0); + if (DEBUG) { + Log.d(TAG, "These packages have " + Arrays.toString(permissions) + ": " + packageInfos); + } + return packageInfos == null || packageInfos.size() == 0 + ? "" + : packageInfos.get(0).packageName; + } + + private static Resources getResourceForPackage(Context context, String packageName) + throws PackageManager.NameNotFoundException { + return context.getPackageManager().getResourcesForApplication(packageName); + } + + public final Optional<Boolean> getBooleanResource(Context context, String resourceName) { + if (resourceName.isEmpty()) { + return Optional.empty(); + } + try { + Resources res = getResourceForPackage(context, packageName); + int resId = + res == null + ? 0 + : res.getIdentifier(resourceName, RES_TYPE_BOOLEAN, packageName); + if (DEBUG) { + Log.d(TAG, "Boolean resource " + resourceName + " has " + resId); + } + return resId == 0 ? Optional.empty() : Optional.of(res.getBoolean(resId)); + } catch (PackageManager.NameNotFoundException e) { + return Optional.empty(); + } + } +} diff --git a/partner_support/src/com/google/android/tv/partner/support/EpgContract.java b/partner_support/src/com/google/android/tv/partner/support/EpgContract.java new file mode 100644 index 00000000..1f47b15b --- /dev/null +++ b/partner_support/src/com/google/android/tv/partner/support/EpgContract.java @@ -0,0 +1,201 @@ +/* + * 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.google.android.tv.partner.support; + +import android.content.ContentUris; +import android.net.Uri; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * The contract between the EPG provider and applications. Contains definitions for the supported + * URIs and columns. + * + * <h3>Overview</h3> + * + * <p> + * + * <p>EpgContract defines a basic database of EPG data for third party inputs. The information is + * stored in {@link Lineups} and {@link EpgInputs} tables. + * + * <p> + * + * <ul> + * <li>A row in the {@link Lineups} table represents information TV Lineups for postal codes. + * <li>A row in the {@link EpgInputs} table represents partner or 3rd party inputs with the lineup + * that input uses. + * </ul> + */ +public final class EpgContract { + + /** The authority for the EPG provider. */ + public static final String AUTHORITY = "com.google.android.tv.data.epg"; + + /** The delimiter between channel numbers. */ + public static final String CHANNEL_NUMBER_DELIMITER = ", "; + + /** + * A constant of the key for intent to indicate whether cloud EPG is used. + * + * <p>Value type: Boolean + */ + public static final String EXTRA_USE_CLOUD_EPG = "com.google.android.tv.extra.USE_CLOUD_EPG"; + + /** + * Returns the list of channels as a CSV string. + * + * <p>Any commas in the original channels are converted to periods. + */ + public static String toChannelString(List<String> channels) { + // TODO use a StringJoiner if we set the min SDK to N + StringBuilder result = new StringBuilder(); + for (String channel : channels) { + channel = channel.replace(",", "."); + if (result.length() > 0) { + result.append(CHANNEL_NUMBER_DELIMITER); + } + result.append(channel); + } + return result.toString(); + } + + /** Returns a list of channels. */ + public static List<String> toChannelList(String channelString) { + return channelString == null + ? Collections.EMPTY_LIST + : Arrays.asList(channelString.split(CHANNEL_NUMBER_DELIMITER)); + } + + /** Column definitions for the EPG lineups table. */ + public static final class Lineups { + + /** Base content:// style URI for all lines in a postal code. */ + private static final Uri LINEUPS_IN_POSTAL_CODE_URI = + Uri.parse("content://" + AUTHORITY + "/lineups/postal_code"); + + /** The MIME type of a directory of TV channels. */ + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/lineup"; + + /** The MIME type of a single TV channel. */ + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/lineup"; + + /** The content:// style URI all lineups in a postal code. */ + public static Uri uriForPostalCode(String postalCode) { + return LINEUPS_IN_POSTAL_CODE_URI.buildUpon().appendPath(postalCode).build(); + } + + /** + * The line up id. + * + * <p>This is a opaque string that should not be parsed. + * + * <p>Type: TEXT + */ + public static final String COLUMN_ID = "_ID"; + + /** + * The line up name that is displayed to the user. + * + * <p>Type: TEXT + */ + public static final String COLUMN_NAME = "NAME"; + + /** + * The channel numbers that are supported by this lineup that is displayed to the user. + * + * <p>Comma separated value of channel numbers + * + * <p>Type: TEXT + */ + public static final String COLUMN_CHANNELS = "CHANNELS"; + + /** + * The line up type. + * + * <p>Type: TEXT + */ + public static final String COLUMN_TYPE = "TYPE"; + + private Lineups() {} + } + + /** Column definitions for the EPG inputs table. */ + public static final class EpgInputs { + + /** The content:// style URI for this table. */ + public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/epg_input"); + + /** The MIME type of a directory of TV channels. */ + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/epg_input"; + + /** The MIME type of a single TV channel. */ + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/epg_input"; + + /** + * Builds a URI that points to a specific input. + * + * @param rowId The ID of the input to point to. + */ + public static final Uri buildUri(long rowId) { + return ContentUris.withAppendedId(CONTENT_URI, rowId); + } + + /** + * The unique ID for a row. + * + * <p>Type: INTEGER (long) + */ + public static final String COLUMN_ID = "_ID"; + + /** + * The name of the package that owns the current row. + * + * <p>The EPG provider fills in this column with the name of the package that provides the + * initial data of the row. If the package is later uninstalled, the rows it owns are + * automatically removed from the tables. + * + * <p>Type: TEXT + */ + public static final String COLUMN_PACKAGE_NAME = "PACKAGE_NAME"; + + /** + * The ID of the TV input service that provides this TV channel. + * + * <p>Use {@link android.media.tv.TvContract#buildInputId} to build the ID. + * + * <p>This is a required field. + * + * <p>Type: TEXT + */ + public static final String COLUMN_INPUT_ID = "INPUT_ID"; + /** + * The line up id. + * + * <p>This is a opaque string that should not be parsed. + * + * <p>This is a required field. + * + * <p>Type: TEXT + */ + public static final String COLUMN_LINEUP_ID = "LINEUP_ID"; + + private EpgInputs() {} + } + + private EpgContract() {} +} diff --git a/partner_support/src/com/google/android/tv/partner/support/EpgInput.java b/partner_support/src/com/google/android/tv/partner/support/EpgInput.java new file mode 100644 index 00000000..82cc463a --- /dev/null +++ b/partner_support/src/com/google/android/tv/partner/support/EpgInput.java @@ -0,0 +1,64 @@ +/* + * 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.google.android.tv.partner.support; + +import android.content.ContentValues; + +/** + * Value class representing a TV Input that uses Live TV EPG. + * + * @see {@link EpgContract.EpgInputs} + */ +// TODO(b/72052568): Get autovalue to work in aosp master +public abstract class EpgInput { + + public static EpgInput createEpgChannel(long id, String inputId, String lineupId) { + return new AutoValue_EpgInput(id, inputId, lineupId); + } + + public static EpgInput createEpgChannel(ContentValues contentValues) { + long id = contentValues.getAsLong(EpgContract.EpgInputs.COLUMN_ID); + String inputId = contentValues.getAsString(EpgContract.EpgInputs.COLUMN_INPUT_ID); + String lineupId = contentValues.getAsString(EpgContract.EpgInputs.COLUMN_LINEUP_ID); + return createEpgChannel(id, inputId, lineupId); + } + + /** The unique ID for a row. */ + public abstract long getId(); + + /** + * The ID of the TV input service that provides this TV channel. + * + * <p>Use {@link android.media.tv.TvContract#buildInputId} to build the ID. + */ + public abstract String getInputId(); + + /** + * The line up id. + * + * <p>This is a opaque string that should not be parsed. + */ + public abstract String getLineupId(); + + public final ContentValues toContentValues() { + ContentValues contentValues = new ContentValues(); + contentValues.put(EpgContract.EpgInputs.COLUMN_ID, getId()); + contentValues.put(EpgContract.EpgInputs.COLUMN_INPUT_ID, getInputId()); + contentValues.put(EpgContract.EpgInputs.COLUMN_LINEUP_ID, getLineupId()); + return contentValues; + } +} diff --git a/partner_support/src/com/google/android/tv/partner/support/EpgInputs.java b/partner_support/src/com/google/android/tv/partner/support/EpgInputs.java new file mode 100644 index 00000000..53485ec8 --- /dev/null +++ b/partner_support/src/com/google/android/tv/partner/support/EpgInputs.java @@ -0,0 +1,84 @@ +/* + * 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.google.android.tv.partner.support; + +import android.content.ContentResolver; +import android.content.ContentValues; +import android.database.Cursor; +import android.database.DatabaseUtils; +import android.net.Uri; +import android.support.annotation.Nullable; +import android.support.annotation.WorkerThread; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** Static utilities for {@link EpgInput}. */ +public final class EpgInputs { + // TODO create a fake EpgContentProvider for testing. + + /** Returns {@link EpgInput} for {@code inputId} or null if not found. */ + @WorkerThread + @Nullable + public static EpgInput queryEpgInput(ContentResolver contentResolver, String inputId) { + + for (EpgInput epgInput : queryEpgInputs(contentResolver)) { + if (inputId.equals(epgInput.getInputId())) { + return epgInput; + } + } + return null; + } + + /** Returns all {@link EpgInput}. */ + @WorkerThread + public static Set<EpgInput> queryEpgInputs(ContentResolver contentResolver) { + try (Cursor cursor = + contentResolver.query(EpgContract.EpgInputs.CONTENT_URI, null, null, null, null)) { + if (cursor == null) { + return Collections.emptySet(); + } + HashSet<EpgInput> result = new HashSet<>(cursor.getCount()); + while (cursor.moveToNext()) { + ContentValues contentValues = new ContentValues(); + DatabaseUtils.cursorRowToContentValues(cursor, contentValues); + result.add(EpgInput.createEpgChannel(contentValues)); + } + return result; + } + } + + /** Insert an {@link EpgInput}. */ + @WorkerThread + @Nullable + public static Uri insert(ContentResolver contentResolver, EpgInput epgInput) { + return contentResolver.insert( + EpgContract.EpgInputs.CONTENT_URI, epgInput.toContentValues()); + } + + /** Update an {@link EpgInput}. */ + @WorkerThread + public static int update(ContentResolver contentResolver, EpgInput epgInput) { + return contentResolver.update( + EpgContract.EpgInputs.buildUri(epgInput.getId()), + epgInput.toContentValues(), + null, + null); + } + + private EpgInputs() {} +} diff --git a/partner_support/src/com/google/android/tv/partner/support/Lineup.java b/partner_support/src/com/google/android/tv/partner/support/Lineup.java new file mode 100644 index 00000000..6123eebd --- /dev/null +++ b/partner_support/src/com/google/android/tv/partner/support/Lineup.java @@ -0,0 +1,81 @@ +/* + * 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.google.android.tv.partner.support; + +import android.content.ContentValues; +import android.support.annotation.Nullable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** Value class for {@link com.google.android.tv.partner.support.EpgContract.Lineups} */ +// TODO(b/72052568): Get autovalue to work in aosp master +public abstract class Lineup { + /** Lineup type for cable. */ + public static final int LINEUP_CABLE = 0; + + /** Lineup type for satelite. */ + public static final int LINEUP_SATELLITE = 1; + + /** Lineup type for broadcast digital. */ + public static final int LINEUP_BROADCAST_DIGITAL = 2; + + /** Lineup type for broadcast analog. */ + public static final int LINEUP_BROADCAST_ANALOG = 3; + + /** Lineup type for IPTV. */ + public static final int LINEUP_IPTV = 4; + + /** + * Indicates the lineup is either satellite, cable or IPTV but we are not sure which specific + * type. + */ + public static final int LINEUP_MVPD = 5; + + /** Lineup type for Internet. */ + public static final int LINEUP_INTERNET = 6; + + /** Lineup type for other. */ + public static final int LINEUP_OTHER = 7; + + public static Lineup createLineup(String id, int type, String name, List<String> channels) { + return new AutoValue_Lineup( + id, type, name, Collections.unmodifiableList(new ArrayList<>(channels))); + } + + public static Lineup createLineup(ContentValues contentValues) { + String id = contentValues.getAsString(EpgContract.Lineups.COLUMN_ID); + int type = contentValues.getAsInteger(EpgContract.Lineups.COLUMN_TYPE); + String name = contentValues.getAsString(EpgContract.Lineups.COLUMN_NAME); + String channels = contentValues.getAsString(EpgContract.Lineups.COLUMN_CHANNELS); + List<String> channelList = EpgContract.toChannelList(channels); + return new AutoValue_Lineup(id, type, name, Collections.unmodifiableList(channelList)); + } + + /** The ID of this lineup. */ + public abstract String getId(); + + /** The type associated with this lineup. */ + public abstract int getType(); + + /** The human readable name associated with this lineup. */ + @Nullable + public abstract String getName(); + + /** An unmodifiable list of channel numbers that this lineup has. */ + public abstract List<String> getChannels(); +} diff --git a/partner_support/src/com/google/android/tv/partner/support/Lineups.java b/partner_support/src/com/google/android/tv/partner/support/Lineups.java new file mode 100644 index 00000000..84f02bfb --- /dev/null +++ b/partner_support/src/com/google/android/tv/partner/support/Lineups.java @@ -0,0 +1,45 @@ +/* + * 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.google.android.tv.partner.support; + +import android.content.ContentResolver; +import android.content.ContentValues; +import android.database.Cursor; +import android.database.DatabaseUtils; +import java.util.HashSet; +import java.util.Set; + +/** Static utilities for {@link Lineup}. */ +public final class Lineups { + + public static Set<Lineup> query(ContentResolver contentResolver, String postalCode) { + Set<Lineup> result = new HashSet<>(); + try (Cursor cursor = + contentResolver.query( + EpgContract.Lineups.uriForPostalCode(postalCode), null, null, null, null)) { + ContentValues contentValues = new ContentValues(); + while (cursor.moveToNext()) { + contentValues.clear(); + DatabaseUtils.cursorRowToContentValues(cursor, contentValues); + result.add(Lineup.createLineup(contentValues)); + } + return result; + } + } + + private Lineups() {} +} diff --git a/partner_support/src/com/google/android/tv/partner/support/PartnerCustomizations.java b/partner_support/src/com/google/android/tv/partner/support/PartnerCustomizations.java new file mode 100644 index 00000000..7ff168e1 --- /dev/null +++ b/partner_support/src/com/google/android/tv/partner/support/PartnerCustomizations.java @@ -0,0 +1,43 @@ +/* + * 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.google.android.tv.partner.support; + +import android.annotation.TargetApi; +import android.content.Context; +import android.os.Build; + +/** Customizations available to partners */ +@TargetApi(Build.VERSION_CODES.N) +@SuppressWarnings("AndroidApiChecker") // TODO(b/32513850) remove when error prone is updated +public final class PartnerCustomizations extends BaseCustomization { + + private static final String[] CUSTOMIZE_PERMISSIONS = { + "com.google.android.tv.permission.CUSTOMIZE_TV_APP" + }; + + public static final String TVPROVIDER_ALLOWS_SYSTEM_INSERTS_TO_PROGRAM_TABLE = + "tvprovider_allows_system_inserts_to_program_table"; + + public PartnerCustomizations(Context context) { + super(context, CUSTOMIZE_PERMISSIONS); + } + + public boolean doesTvProviderAllowSystemInsertsToProgramTable(Context context) { + return getBooleanResource(context, TVPROVIDER_ALLOWS_SYSTEM_INSERTS_TO_PROGRAM_TABLE) + .orElse(false); + } +} diff --git a/partner_support/src/com/google/android/tv/partner/support/TunerSetupUtils.java b/partner_support/src/com/google/android/tv/partner/support/TunerSetupUtils.java new file mode 100644 index 00000000..e25d5836 --- /dev/null +++ b/partner_support/src/com/google/android/tv/partner/support/TunerSetupUtils.java @@ -0,0 +1,124 @@ +/* + * 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.google.android.tv.partner.support; + +import android.support.annotation.VisibleForTesting; +import android.text.TextUtils; +import android.util.Log; +import android.util.Pair; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.regex.Pattern; + +/** Utility class for providing tuner setup. */ +public class TunerSetupUtils { + private static final String TAG = "TunerSetupUtils"; + + private static final Pattern CHANNEL_NUMBER_DELIMITER = Pattern.compile("([ .-])"); + + public static List<Pair<Lineup, Integer>> lineupChannelMatchCount( + List<Lineup> lineups, List<String> localChannels) { + List<Pair<Lineup, Integer>> result = new ArrayList<>(); + List<List<String>> parsedLocalChannels = parseChannelNumbers(localChannels); + for (Lineup lineup : lineups) { + result.add( + new Pair<>(lineup, getMatchCount(lineup.getChannels(), parsedLocalChannels))); + } + // 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; + } + + @VisibleForTesting + static int getMatchCount(List<String> lineupChannels, List<List<String>> parsedLocalChannels) { + int count = 0; + List<List<String>> parsedLineupChannels = parseChannelNumbers(lineupChannels); + for (List<String> parsedLineupChannel : parsedLineupChannels) { + for (List<String> parsedLocalChannel : parsedLocalChannels) { + if (matchChannelNumber(parsedLineupChannel, parsedLocalChannel)) { + count++; + break; + } + } + } + return count; + } + + /** + * 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 + */ + @VisibleForTesting + static List<String> parseChannelNumber(String channelNumber) { + 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; + } + + /** + * Parses a list of channel numbers. See {@link #parseChannelNumber(String)}. + * + * @param channelNumbers a list of channel display numbers + */ + @VisibleForTesting + static List<List<String>> parseChannelNumbers(List<String> channelNumbers) { + List<List<String>> numbers = new ArrayList<>(channelNumbers.size()); + for (String channelNumber : channelNumbers) { + if (!TextUtils.isEmpty(channelNumber)) { + numbers.add(parseChannelNumber(channelNumber)); + } + } + return numbers; + } + + /** + * Checks whether two lists of channel numbers match or not. If the sizes are different, + * additional elements are ignore. + */ + @VisibleForTesting + 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; + } +} |