/* * 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.android.tv.util; import static java.lang.Boolean.TRUE; import android.content.Context; import android.media.tv.TvContract; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.support.annotation.StringDef; import android.support.annotation.VisibleForTesting; import android.support.annotation.WorkerThread; import android.util.Log; import com.android.tv.data.api.BaseProgram; import com.android.tv.features.PartnerFeatures; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; /** A utility class related to TvProvider. */ public final class TvProviderUtils { private static final String TAG = "TvProviderUtils"; public static final String EXTRA_PROGRAM_COLUMN_SERIES_ID = BaseProgram.COLUMN_SERIES_ID; public static final String EXTRA_PROGRAM_COLUMN_STATE = BaseProgram.COLUMN_STATE; /** Possible extra columns in TV provider. */ @Retention(RetentionPolicy.SOURCE) @StringDef({EXTRA_PROGRAM_COLUMN_SERIES_ID, EXTRA_PROGRAM_COLUMN_STATE}) public @interface TvProviderExtraColumn {} private static boolean sProgramHasSeriesIdColumn; private static boolean sRecordedProgramHasSeriesIdColumn; private static boolean sRecordedProgramHasStateColumn; /** * Checks whether a table contains a series ID column. * *
This method is different from {@link #getProgramHasSeriesIdColumn()} and {@link * #getRecordedProgramHasSeriesIdColumn()} because it may access to database, so it should be * run in worker thread. * * @return {@code true} if the corresponding table contains a series ID column; {@code false} * otherwise. */ @WorkerThread public static synchronized boolean checkSeriesIdColumn(Context context, Uri uri) { boolean canCreateColumn = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O); canCreateColumn = (canCreateColumn || PartnerFeatures.TVPROVIDER_ALLOWS_COLUMN_CREATION.isEnabled(context)); if (!canCreateColumn) { return false; } return (Utils.isRecordedProgramsUri(uri) && checkRecordedProgramTableSeriesIdColumn(context, uri)) || (Utils.isProgramsUri(uri) && checkProgramTableSeriesIdColumn(context, uri)); } @WorkerThread private static synchronized boolean checkProgramTableSeriesIdColumn(Context context, Uri uri) { if (!sProgramHasSeriesIdColumn) { if (getExistingColumns(context, uri).contains(EXTRA_PROGRAM_COLUMN_SERIES_ID)) { sProgramHasSeriesIdColumn = true; } else if (addColumnToTable(context, uri, EXTRA_PROGRAM_COLUMN_SERIES_ID)) { sProgramHasSeriesIdColumn = true; } } return sProgramHasSeriesIdColumn; } @WorkerThread private static synchronized boolean checkRecordedProgramTableSeriesIdColumn( Context context, Uri uri) { if (!sRecordedProgramHasSeriesIdColumn) { if (getExistingColumns(context, uri).contains(EXTRA_PROGRAM_COLUMN_SERIES_ID)) { sRecordedProgramHasSeriesIdColumn = true; } else if (addColumnToTable(context, uri, EXTRA_PROGRAM_COLUMN_SERIES_ID)) { sRecordedProgramHasSeriesIdColumn = true; } } return sRecordedProgramHasSeriesIdColumn; } /** * Checks whether a table contains a state column. * *
This method is different from {@link #getRecordedProgramHasStateColumn()} because it may
* access to database, so it should be run in worker thread.
*
* @return {@code true} if the corresponding table contains a state column; {@code false}
* otherwise.
*/
@WorkerThread
public static synchronized boolean checkStateColumn(Context context, Uri uri) {
boolean canCreateColumn = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O);
canCreateColumn =
(canCreateColumn
|| PartnerFeatures.TVPROVIDER_ALLOWS_COLUMN_CREATION.isEnabled(context));
if (!canCreateColumn) {
return false;
}
return (Utils.isRecordedProgramsUri(uri)
&& checkRecordedProgramTableStateColumn(context, uri));
}
@WorkerThread
private static synchronized boolean checkRecordedProgramTableStateColumn(
Context context, Uri uri) {
if (!sRecordedProgramHasStateColumn) {
if (getExistingColumns(context, uri).contains(EXTRA_PROGRAM_COLUMN_STATE)) {
sRecordedProgramHasStateColumn = true;
} else if (addColumnToTable(context, uri, EXTRA_PROGRAM_COLUMN_STATE)) {
sRecordedProgramHasStateColumn = true;
}
}
return sRecordedProgramHasStateColumn;
}
public static synchronized boolean getProgramHasSeriesIdColumn() {
return TRUE.equals(sProgramHasSeriesIdColumn);
}
public static synchronized boolean getRecordedProgramHasSeriesIdColumn() {
return TRUE.equals(sRecordedProgramHasSeriesIdColumn);
}
public static synchronized boolean getRecordedProgramHasStateColumn() {
return TRUE.equals(sRecordedProgramHasStateColumn);
}
public static String[] addExtraColumnsToProjection(
String[] projection, @TvProviderExtraColumn String column) {
List