aboutsummaryrefslogtreecommitdiff
path: root/partner_support/src/com/google
diff options
context:
space:
mode:
Diffstat (limited to 'partner_support/src/com/google')
-rw-r--r--partner_support/src/com/google/android/tv/partner/support/AutoValue_EpgInput.java97
-rw-r--r--partner_support/src/com/google/android/tv/partner/support/AutoValue_Lineup.java114
-rw-r--r--partner_support/src/com/google/android/tv/partner/support/BaseCustomization.java93
-rw-r--r--partner_support/src/com/google/android/tv/partner/support/EpgContract.java201
-rw-r--r--partner_support/src/com/google/android/tv/partner/support/EpgInput.java64
-rw-r--r--partner_support/src/com/google/android/tv/partner/support/EpgInputs.java84
-rw-r--r--partner_support/src/com/google/android/tv/partner/support/Lineup.java81
-rw-r--r--partner_support/src/com/google/android/tv/partner/support/Lineups.java45
-rw-r--r--partner_support/src/com/google/android/tv/partner/support/PartnerCustomizations.java43
-rw-r--r--partner_support/src/com/google/android/tv/partner/support/TunerSetupUtils.java124
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;
+ }
+}