aboutsummaryrefslogtreecommitdiff
path: root/common/src/com/android/tv
diff options
context:
space:
mode:
Diffstat (limited to 'common/src/com/android/tv')
-rw-r--r--common/src/com/android/tv/common/BaseApplication.java34
-rw-r--r--common/src/com/android/tv/common/BaseSingletons.java13
-rw-r--r--common/src/com/android/tv/common/BuildConfig.java26
-rw-r--r--common/src/com/android/tv/common/CommonConstants.java10
-rw-r--r--common/src/com/android/tv/common/TvContentRatingCache.java46
-rw-r--r--common/src/com/android/tv/common/buildtype/AospBuildTypeProvider.java25
-rw-r--r--common/src/com/android/tv/common/buildtype/EngBuildTypeProvider.java25
-rw-r--r--common/src/com/android/tv/common/buildtype/HasBuildType.java34
-rw-r--r--common/src/com/android/tv/common/buildtype/NoJniTestBuildTypeProvider.java25
-rw-r--r--common/src/com/android/tv/common/buildtype/ProdBuildTypeProvider.java25
-rw-r--r--common/src/com/android/tv/common/compat/README.md7
-rw-r--r--common/src/com/android/tv/common/compat/RecordingSessionCompat.java69
-rw-r--r--common/src/com/android/tv/common/compat/TisSessionCompat.java77
-rw-r--r--common/src/com/android/tv/common/compat/TvInputConstantCompat.java46
-rw-r--r--common/src/com/android/tv/common/compat/TvInputInfoCompat.java117
-rw-r--r--common/src/com/android/tv/common/compat/TvRecordingClientCompat.java101
-rw-r--r--common/src/com/android/tv/common/compat/TvViewCompat.java111
-rw-r--r--common/src/com/android/tv/common/compat/api/PrivateCommandSender.java24
-rw-r--r--common/src/com/android/tv/common/compat/api/RecordingClientCallbackCompatEvents.java27
-rw-r--r--common/src/com/android/tv/common/compat/api/RecordingSessionCompatCommands.java22
-rw-r--r--common/src/com/android/tv/common/compat/api/RecordingSessionCompatEvents.java24
-rw-r--r--common/src/com/android/tv/common/compat/api/SessionCompatCommands.java22
-rw-r--r--common/src/com/android/tv/common/compat/api/SessionCompatEvents.java24
-rw-r--r--common/src/com/android/tv/common/compat/api/SessionEventNotifier.java24
-rw-r--r--common/src/com/android/tv/common/compat/api/TvInputCallbackCompatEvents.java26
-rw-r--r--common/src/com/android/tv/common/compat/api/TvRecordingClientCompatCommands.java22
-rw-r--r--common/src/com/android/tv/common/compat/api/TvViewCompatCommands.java22
-rw-r--r--common/src/com/android/tv/common/compat/internal/Constants.java28
-rw-r--r--common/src/com/android/tv/common/compat/internal/RecordingClientCompatProcessor.java85
-rw-r--r--common/src/com/android/tv/common/compat/internal/RecordingSessionCompatProcessor.java78
-rw-r--r--common/src/com/android/tv/common/compat/internal/SessionCompatProcessor.java75
-rw-r--r--common/src/com/android/tv/common/compat/internal/TifSessionCompatProcessor.java75
-rw-r--r--common/src/com/android/tv/common/compat/internal/TvViewCompatProcessor.java90
-rw-r--r--common/src/com/android/tv/common/compat/internal/ViewCompatProcessor.java90
-rw-r--r--common/src/com/android/tv/common/compat/internal/recording_commands.proto39
-rw-r--r--common/src/com/android/tv/common/compat/internal/recording_events.proto49
-rw-r--r--common/src/com/android/tv/common/compat/internal/tif_commands.proto39
-rw-r--r--common/src/com/android/tv/common/compat/internal/tif_events.proto47
-rw-r--r--common/src/com/android/tv/common/config/DefaultConfigManager.java60
-rw-r--r--common/src/com/android/tv/common/config/RemoteConfigFeature.java42
-rw-r--r--common/src/com/android/tv/common/config/api/RemoteConfig.java54
-rw-r--r--common/src/com/android/tv/common/config/api/RemoteConfigValue.java48
-rw-r--r--common/src/com/android/tv/common/dagger/ApplicationModule.java60
-rw-r--r--common/src/com/android/tv/common/dagger/annotations/ApplicationContext.java22
-rw-r--r--common/src/com/android/tv/common/dagger/annotations/MainLooper.java22
-rw-r--r--common/src/com/android/tv/common/data/RecordedProgramState.java14
-rw-r--r--common/src/com/android/tv/common/experiments/ExperimentFlag.java29
-rw-r--r--common/src/com/android/tv/common/experiments/Experiments.java9
-rw-r--r--common/src/com/android/tv/common/feature/BuildTypeFeature.java (renamed from common/src/com/android/tv/common/feature/EngOnlyFeature.java)15
-rw-r--r--common/src/com/android/tv/common/feature/CommonFeatures.java74
-rw-r--r--common/src/com/android/tv/common/feature/FeatureUtils.java41
-rw-r--r--common/src/com/android/tv/common/feature/FlagFeature.java (renamed from common/src/com/android/tv/common/feature/GServiceFeature.java)31
-rw-r--r--common/src/com/android/tv/common/feature/Sdk.java40
-rw-r--r--common/src/com/android/tv/common/flags/BackendKnobsFlags.java46
-rwxr-xr-xcommon/src/com/android/tv/common/flags/CloudEpgFlags.java34
-rwxr-xr-xcommon/src/com/android/tv/common/flags/ConcurrentDvrPlaybackFlags.java34
-rwxr-xr-xcommon/src/com/android/tv/common/flags/TunerFlags.java34
-rwxr-xr-xcommon/src/com/android/tv/common/flags/UiFlags.java41
-rw-r--r--common/src/com/android/tv/common/flags/has/HasCloudEpgFlags.java29
-rw-r--r--common/src/com/android/tv/common/flags/has/HasConcurrentDvrPlaybackFlags.java30
-rw-r--r--common/src/com/android/tv/common/flags/has/HasUiFlags.java24
-rw-r--r--common/src/com/android/tv/common/flags/has/HasUtils.java30
-rw-r--r--common/src/com/android/tv/common/flags/impl/DefaultBackendKnobsFlags.java56
-rw-r--r--common/src/com/android/tv/common/flags/impl/DefaultCloudEpgFlags.java46
-rw-r--r--common/src/com/android/tv/common/flags/impl/DefaultConcurrentDvrPlaybackFlags.java37
-rw-r--r--common/src/com/android/tv/common/flags/impl/DefaultFlagsModule.java60
-rw-r--r--common/src/com/android/tv/common/flags/impl/DefaultTunerFlags.java37
-rw-r--r--common/src/com/android/tv/common/flags/impl/DefaultUiFlags.java42
-rw-r--r--common/src/com/android/tv/common/recording/RecordingStorageStatusManager.java3
-rw-r--r--common/src/com/android/tv/common/singletons/HasSingletons.java33
-rw-r--r--common/src/com/android/tv/common/singletons/HasTvInputId.java27
-rw-r--r--common/src/com/android/tv/common/support/README.md8
-rw-r--r--common/src/com/android/tv/common/support/tis/BaseTvInputService.java90
-rw-r--r--common/src/com/android/tv/common/support/tis/SessionManager.java31
-rw-r--r--common/src/com/android/tv/common/support/tis/SimpleSessionManager.java62
-rw-r--r--common/src/com/android/tv/common/support/tis/TifSession.java203
-rw-r--r--common/src/com/android/tv/common/support/tis/WrappedSession.java148
-rw-r--r--common/src/com/android/tv/common/ui/setup/SetupActivity.java23
-rw-r--r--common/src/com/android/tv/common/util/CommonUtils.java19
-rw-r--r--common/src/com/android/tv/common/util/LocationUtils.java50
-rw-r--r--common/src/com/android/tv/common/util/NetworkTrafficTags.java23
-rw-r--r--common/src/com/android/tv/common/util/PermissionUtils.java5
-rw-r--r--common/src/com/android/tv/common/util/StringUtils.java5
-rw-r--r--common/src/com/android/tv/common/util/SystemProperties.java4
84 files changed, 3220 insertions, 378 deletions
diff --git a/common/src/com/android/tv/common/BaseApplication.java b/common/src/com/android/tv/common/BaseApplication.java
index 71c9b4d7..45c32567 100644
--- a/common/src/com/android/tv/common/BaseApplication.java
+++ b/common/src/com/android/tv/common/BaseApplication.java
@@ -17,10 +17,7 @@
package com.android.tv.common;
import android.annotation.TargetApi;
-import android.app.Application;
import android.content.Context;
-import android.content.Intent;
-import android.os.AsyncTask;
import android.os.Build;
import android.os.StrictMode;
import android.support.annotation.VisibleForTesting;
@@ -30,9 +27,10 @@ import com.android.tv.common.util.Clock;
import com.android.tv.common.util.CommonUtils;
import com.android.tv.common.util.Debug;
import com.android.tv.common.util.SystemProperties;
+import dagger.android.DaggerApplication;
/** The base application class for Live TV applications. */
-public abstract class BaseApplication extends Application implements BaseSingletons {
+public abstract class BaseApplication extends DaggerApplication implements BaseSingletons {
private RecordingStorageStatusManager mRecordingStorageStatusManager;
/**
@@ -41,7 +39,13 @@ public abstract class BaseApplication extends Application implements BaseSinglet
*/
@VisibleForTesting public static BaseSingletons sSingletons;
- /** Returns the {@link BaseSingletons} using the application context. */
+ /**
+ * Returns the {@link BaseSingletons} using the application context.
+ *
+ * @deprecated use {@link com.android.tv.common.singletons.HasSingletons#get(Class, Context)}
+ * instead
+ */
+ @Deprecated
public static BaseSingletons getSingletons(Context context) {
// STOP-SHIP: changing the method to protected once the Tuner application is created.
// No need to be "synchronized" because this doesn't create any instance.
@@ -65,8 +69,15 @@ public abstract class BaseApplication extends Application implements BaseSinglet
StrictMode.ThreadPolicy.Builder threadPolicyBuilder =
new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog();
// TODO(b/69565157): Turn penaltyDeath on for VMPolicy when tests are fixed.
+ // TODO(b/120840665): Restore detecting untagged network sockets
StrictMode.VmPolicy.Builder vmPolicyBuilder =
- new StrictMode.VmPolicy.Builder().detectAll().penaltyLog();
+ new StrictMode.VmPolicy.Builder()
+ .detectActivityLeaks()
+ .detectLeakedClosableObjects()
+ .detectLeakedRegistrationObjects()
+ .detectFileUriExposure()
+ .detectContentUriWithoutPermission()
+ .penaltyLog();
if (!CommonUtils.isRunningInTest()) {
threadPolicyBuilder.penaltyDialog();
@@ -77,14 +88,6 @@ public abstract class BaseApplication extends Application implements BaseSinglet
if (CommonFeatures.DVR.isEnabled(this)) {
getRecordingStorageStatusManager();
}
- new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... params) {
- // Fetch remote config
- getRemoteConfig().fetch(null);
- return null;
- }
- }.execute();
}
@Override
@@ -101,7 +104,4 @@ public abstract class BaseApplication extends Application implements BaseSinglet
}
return mRecordingStorageStatusManager;
}
-
- @Override
- public abstract Intent getTunerSetupIntent(Context context);
}
diff --git a/common/src/com/android/tv/common/BaseSingletons.java b/common/src/com/android/tv/common/BaseSingletons.java
index e735cdb4..10530617 100644
--- a/common/src/com/android/tv/common/BaseSingletons.java
+++ b/common/src/com/android/tv/common/BaseSingletons.java
@@ -16,20 +16,17 @@
package com.android.tv.common;
-import android.content.Context;
-import android.content.Intent;
-import com.android.tv.common.config.api.RemoteConfig.HasRemoteConfig;
+import com.android.tv.common.buildtype.HasBuildType;
+import com.android.tv.common.flags.has.HasCloudEpgFlags;
+import com.android.tv.common.flags.has.HasConcurrentDvrPlaybackFlags;
import com.android.tv.common.recording.RecordingStorageStatusManager;
import com.android.tv.common.util.Clock;
/** Injection point for the base app */
-public interface BaseSingletons extends HasRemoteConfig {
+public interface BaseSingletons
+ extends HasCloudEpgFlags, HasBuildType, HasConcurrentDvrPlaybackFlags {
Clock getClock();
RecordingStorageStatusManager getRecordingStorageStatusManager();
-
- Intent getTunerSetupIntent(Context context);
-
- String getEmbeddedTunerInputId();
}
diff --git a/common/src/com/android/tv/common/BuildConfig.java b/common/src/com/android/tv/common/BuildConfig.java
new file mode 100644
index 00000000..b3ad002b
--- /dev/null
+++ b/common/src/com/android/tv/common/BuildConfig.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2019 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.common;
+
+/* Hard coded BuildConfig. */
+public final class BuildConfig {
+ public static final boolean DEBUG = true;
+ public static final boolean ENG = false;
+ public static final boolean NO_JNI_TEST = false;
+ public static final boolean AOSP = true;
+
+ private BuildConfig() {}
+} \ No newline at end of file
diff --git a/common/src/com/android/tv/common/CommonConstants.java b/common/src/com/android/tv/common/CommonConstants.java
index ac379d18..43d9851b 100644
--- a/common/src/com/android/tv/common/CommonConstants.java
+++ b/common/src/com/android/tv/common/CommonConstants.java
@@ -19,10 +19,20 @@ package com.android.tv.common;
/** Constants for common use in apps and tests. */
public final class CommonConstants {
+ @Deprecated // TODO(b/121158110) refactor so this is not needed.
public static final String BASE_PACKAGE =
+// AOSP_Comment_Out !BuildConfig.AOSP
+// AOSP_Comment_Out ? "com.google.android.tv"
+// AOSP_Comment_Out :
"com.android.tv";
/** A constant for the key of the extra data for the app linking intent. */
public static final String EXTRA_APP_LINK_CHANNEL_URI = "app_link_channel_uri";
+ /**
+ * Video is unavailable because the source is not physically connected, for example the HDMI
+ * cable is not connected.
+ */
+ public static final int VIDEO_UNAVAILABLE_REASON_NOT_CONNECTED = 5;
+
private CommonConstants() {}
}
diff --git a/common/src/com/android/tv/common/TvContentRatingCache.java b/common/src/com/android/tv/common/TvContentRatingCache.java
index cfdb8e4d..f2fda69c 100644
--- a/common/src/com/android/tv/common/TvContentRatingCache.java
+++ b/common/src/com/android/tv/common/TvContentRatingCache.java
@@ -23,9 +23,8 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import com.android.tv.common.memory.MemoryManageable;
-import java.util.ArrayList;
+import com.google.common.collect.ImmutableList;
import java.util.Collections;
-import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
@@ -42,19 +41,19 @@ public final class TvContentRatingCache implements MemoryManageable {
}
// @GuardedBy("TvContentRatingCache.this")
- private final Map<String, TvContentRating[]> mRatingsMultiMap = new ArrayMap<>();
+ private final Map<String, ImmutableList<TvContentRating>> mRatingsMultiMap = new ArrayMap<>();
/**
* Returns an array TvContentRatings from a string of comma separated set of rating strings
- * creating each from {@link TvContentRating#unflattenFromString(String)} if needed. Returns
- * {@code null} if the string is empty or contains no valid ratings.
+ * creating each from {@link TvContentRating#unflattenFromString(String)} if needed or an empty
+ * list if the string is empty or contains no valid ratings.
*/
- @Nullable
- public synchronized TvContentRating[] getRatings(String commaSeparatedRatings) {
+ public synchronized ImmutableList<TvContentRating> getRatings(
+ @Nullable String commaSeparatedRatings) {
if (TextUtils.isEmpty(commaSeparatedRatings)) {
- return null;
+ return ImmutableList.of();
}
- TvContentRating[] tvContentRatings;
+ ImmutableList<TvContentRating> tvContentRatings;
if (mRatingsMultiMap.containsKey(commaSeparatedRatings)) {
tvContentRatings = mRatingsMultiMap.get(commaSeparatedRatings);
} else {
@@ -76,12 +75,13 @@ public final class TvContentRatingCache implements MemoryManageable {
/** Returns a sorted array of TvContentRatings from a comma separated string of ratings. */
@VisibleForTesting
- static TvContentRating[] stringToContentRatings(String commaSeparatedRatings) {
+ static ImmutableList<TvContentRating> stringToContentRatings(
+ @Nullable String commaSeparatedRatings) {
if (TextUtils.isEmpty(commaSeparatedRatings)) {
- return null;
+ return ImmutableList.of();
}
Set<String> ratingStrings = getSortedSetFromCsv(commaSeparatedRatings);
- List<TvContentRating> contentRatings = new ArrayList<>();
+ ImmutableList.Builder<TvContentRating> contentRatings = ImmutableList.builder();
for (String rating : ratingStrings) {
try {
contentRatings.add(TvContentRating.unflattenFromString(rating));
@@ -89,9 +89,7 @@ public final class TvContentRatingCache implements MemoryManageable {
Log.e(TAG, "Can't parse the content rating: '" + rating + "'", e);
}
}
- return contentRatings.size() == 0
- ? null
- : contentRatings.toArray(new TvContentRating[contentRatings.size()]);
+ return contentRatings.build();
}
private static Set<String> getSortedSetFromCsv(String commaSeparatedRatings) {
@@ -118,19 +116,17 @@ public final class TvContentRatingCache implements MemoryManageable {
* Returns a string of each flattened content rating, sorted and concatenated together with a
* comma.
*/
- public static String contentRatingsToString(TvContentRating[] contentRatings) {
- if (contentRatings == null || contentRatings.length == 0) {
+ @Nullable
+ public static String contentRatingsToString(
+ @Nullable ImmutableList<TvContentRating> contentRatings) {
+ if (contentRatings == null) {
return null;
}
- String[] ratingStrings = new String[contentRatings.length];
- for (int i = 0; i < contentRatings.length; i++) {
- ratingStrings[i] = contentRatings[i].flattenToString();
- }
- if (ratingStrings.length == 1) {
- return ratingStrings[0];
- } else {
- return TextUtils.join(",", toSortedSet(ratingStrings));
+ SortedSet<String> ratingStrings = new TreeSet<>();
+ for (TvContentRating rating : contentRatings) {
+ ratingStrings.add(rating.flattenToString());
}
+ return TextUtils.join(",", ratingStrings);
}
@Override
diff --git a/common/src/com/android/tv/common/buildtype/AospBuildTypeProvider.java b/common/src/com/android/tv/common/buildtype/AospBuildTypeProvider.java
new file mode 100644
index 00000000..8d39b3a6
--- /dev/null
+++ b/common/src/com/android/tv/common/buildtype/AospBuildTypeProvider.java
@@ -0,0 +1,25 @@
+/*
+ * 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.common.buildtype;
+
+/** {@code AOSP} {@link HasBuildType}. */
+public class AospBuildTypeProvider implements HasBuildType {
+
+ @Override
+ public BuildType getBuildType() {
+ return BuildType.AOSP;
+ }
+}
diff --git a/common/src/com/android/tv/common/buildtype/EngBuildTypeProvider.java b/common/src/com/android/tv/common/buildtype/EngBuildTypeProvider.java
new file mode 100644
index 00000000..5f18794c
--- /dev/null
+++ b/common/src/com/android/tv/common/buildtype/EngBuildTypeProvider.java
@@ -0,0 +1,25 @@
+/*
+ * 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.common.buildtype;
+
+/** {@code ENG} {@link HasBuildType}. */
+public class EngBuildTypeProvider implements HasBuildType {
+
+ @Override
+ public BuildType getBuildType() {
+ return BuildType.ENG;
+ }
+}
diff --git a/common/src/com/android/tv/common/buildtype/HasBuildType.java b/common/src/com/android/tv/common/buildtype/HasBuildType.java
new file mode 100644
index 00000000..7d5677c9
--- /dev/null
+++ b/common/src/com/android/tv/common/buildtype/HasBuildType.java
@@ -0,0 +1,34 @@
+/*
+ * 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.common.buildtype;
+
+/**
+ * The provides a {@link BuildType} for selecting features in code.
+ *
+ * <p>This is considered an anti-pattern and new usages should be discouraged.
+ */
+public interface HasBuildType {
+
+ /** Compile time constant for build type. */
+ enum BuildType {
+ AOSP,
+ ENG,
+ NO_JNI_TEST,
+ PROD
+ }
+
+ BuildType getBuildType();
+}
diff --git a/common/src/com/android/tv/common/buildtype/NoJniTestBuildTypeProvider.java b/common/src/com/android/tv/common/buildtype/NoJniTestBuildTypeProvider.java
new file mode 100644
index 00000000..1620af20
--- /dev/null
+++ b/common/src/com/android/tv/common/buildtype/NoJniTestBuildTypeProvider.java
@@ -0,0 +1,25 @@
+/*
+ * 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.common.buildtype;
+
+/** {@code NO_JNI_TEST} {@link HasBuildType}. */
+public class NoJniTestBuildTypeProvider implements HasBuildType {
+
+ @Override
+ public BuildType getBuildType() {
+ return BuildType.NO_JNI_TEST;
+ }
+}
diff --git a/common/src/com/android/tv/common/buildtype/ProdBuildTypeProvider.java b/common/src/com/android/tv/common/buildtype/ProdBuildTypeProvider.java
new file mode 100644
index 00000000..16db3263
--- /dev/null
+++ b/common/src/com/android/tv/common/buildtype/ProdBuildTypeProvider.java
@@ -0,0 +1,25 @@
+/*
+ * 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.common.buildtype;
+
+/** {@code Prod} {@link HasBuildType}. */
+public class ProdBuildTypeProvider implements HasBuildType {
+
+ @Override
+ public BuildType getBuildType() {
+ return BuildType.PROD;
+ }
+}
diff --git a/common/src/com/android/tv/common/compat/README.md b/common/src/com/android/tv/common/compat/README.md
new file mode 100644
index 00000000..8d87d83c
--- /dev/null
+++ b/common/src/com/android/tv/common/compat/README.md
@@ -0,0 +1,7 @@
+# TIF Compatibility Library
+
+This is the temporary location for the of Compatibility Library while it is under development.
+It will eventually move to a support library location.
+
+
+See go/tif-compat-proposal \ No newline at end of file
diff --git a/common/src/com/android/tv/common/compat/RecordingSessionCompat.java b/common/src/com/android/tv/common/compat/RecordingSessionCompat.java
new file mode 100644
index 00000000..6941e47b
--- /dev/null
+++ b/common/src/com/android/tv/common/compat/RecordingSessionCompat.java
@@ -0,0 +1,69 @@
+/*
+ * 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.common.compat;
+
+import android.content.Context;
+import android.media.tv.TvInputService.RecordingSession;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+import com.android.tv.common.compat.api.RecordingSessionCompatCommands;
+import com.android.tv.common.compat.api.RecordingSessionCompatEvents;
+import com.android.tv.common.compat.api.SessionEventNotifier;
+import com.android.tv.common.compat.internal.RecordingSessionCompatProcessor;
+
+/**
+ * TIF Compatibility for {@link RecordingSession}.
+ *
+ * <p>Extends {@code RecordingSession} in a backwards compatible way.
+ */
+@RequiresApi(api = VERSION_CODES.N)
+public abstract class RecordingSessionCompat extends RecordingSession
+ implements SessionEventNotifier,
+ RecordingSessionCompatCommands,
+ RecordingSessionCompatEvents {
+
+ private final RecordingSessionCompatProcessor mProcessor;
+
+ public RecordingSessionCompat(Context context) {
+ super(context);
+ mProcessor = new RecordingSessionCompatProcessor(this, this);
+ }
+
+ @Override
+ public void onAppPrivateCommand(String action, Bundle data) {
+ if (!mProcessor.handleAppPrivateCommand(action, data)) {
+ super.onAppPrivateCommand(action, data);
+ }
+ }
+
+ /** Display a debug message to the session for display on dev builds only */
+ @Override
+ public void onDevMessage(String message) {}
+
+ /** Notify the client to Display a message in the application as a toast on dev builds only. */
+ @Override
+ public void notifyDevToast(String message) {
+ mProcessor.notifyDevToast(message);
+ }
+
+ /** Notify the client Recording started. */
+ @Override
+ public void notifyRecordingStarted(String uri) {
+ mProcessor.notifyRecordingStarted(uri);
+ }
+}
diff --git a/common/src/com/android/tv/common/compat/TisSessionCompat.java b/common/src/com/android/tv/common/compat/TisSessionCompat.java
new file mode 100644
index 00000000..97f4fb32
--- /dev/null
+++ b/common/src/com/android/tv/common/compat/TisSessionCompat.java
@@ -0,0 +1,77 @@
+/*
+ * 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.common.compat;
+
+import android.content.Context;
+import android.media.tv.TvInputService.Session;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+import com.android.tv.common.compat.api.SessionCompatCommands;
+import com.android.tv.common.compat.api.SessionCompatEvents;
+import com.android.tv.common.compat.api.SessionEventNotifier;
+import com.android.tv.common.compat.internal.TifSessionCompatProcessor;
+
+/**
+ * TIF Compatibility for {@link Session}.
+ *
+ * <p>Extends {@code Session} in a backwards compatible way.
+ */
+@RequiresApi(api = VERSION_CODES.LOLLIPOP)
+public abstract class TisSessionCompat extends Session
+ implements SessionEventNotifier, SessionCompatCommands, SessionCompatEvents {
+
+ private final TifSessionCompatProcessor mTifCompatProcessor;
+
+ public TisSessionCompat(Context context) {
+ super(context);
+ mTifCompatProcessor = new TifSessionCompatProcessor(this, this);
+ }
+
+ @Override
+ public void onAppPrivateCommand(String action, Bundle data) {
+ if (!mTifCompatProcessor.handleAppPrivateCommand(action, data)) {
+ super.onAppPrivateCommand(action, data);
+ }
+ }
+
+ @Override
+ public void onDevMessage(String message) {}
+
+ @Override
+ public void notifyDevToast(String message) {
+ mTifCompatProcessor.notifyDevToast(message);
+ }
+
+ /**
+ * Notify the application with current signal strength.
+ *
+ * <p>At each {MainActivity#tune(boolean)}, the signal strength is implicitly reset to {@link
+ * TvInputConstantCompat#SIGNAL_STRENGTH_NOT_USED}. If a TV input supports reporting signal
+ * strength, it should set the signal strength to {@link
+ * TvInputConstantCompat#SIGNAL_STRENGTH_UNKNOWN} in
+ * {TunerSessionWorker#prepareTune(TunerChannel, String)}, until a valid strength is available.
+ *
+ * @param value The current signal strength. Valid values are {@link
+ * TvInputConstantCompat#SIGNAL_STRENGTH_NOT_USED}, {@link
+ * TvInputConstantCompat#SIGNAL_STRENGTH_UNKNOWN}, and 0 - 100 inclusive.
+ */
+ @Override
+ public void notifySignalStrength(int value) {
+ mTifCompatProcessor.notifySignalStrength(value);
+ }
+}
diff --git a/common/src/com/android/tv/common/compat/TvInputConstantCompat.java b/common/src/com/android/tv/common/compat/TvInputConstantCompat.java
new file mode 100644
index 00000000..251e8481
--- /dev/null
+++ b/common/src/com/android/tv/common/compat/TvInputConstantCompat.java
@@ -0,0 +1,46 @@
+/*
+ * 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.common.compat;
+
+/** Temp TIF Compatibility for {@link TvInputManager} constants. */
+public class TvInputConstantCompat {
+
+ /**
+ * Status for {@link TisSessionCompat#notifySignalStrength(int)} and
+ * {@link TvViewCompat.TvInputCallback#onTimeShiftStatusChanged(String, int)}:
+ *
+ * <p>SIGNAL_STRENGTH_NOT_USED means the TV Input does not report signal strength. Each onTune
+ * command implicitly resets the TV App's signal strength state to SIGNAL_STRENGTH_NOT_USED.
+ */
+ public static final int SIGNAL_STRENGTH_NOT_USED = -3;
+
+ /**
+ * Status for {@link TisSessionCompat#notifySignalStrength(int)} and
+ * {@link TvViewCompat.TvInputCallback#onTimeShiftStatusChanged(String, int)}:
+ *
+ * <p>SIGNAL_STRENGTH_ERROR means exception/error when handling signal strength.
+ */
+ public static final int SIGNAL_STRENGTH_ERROR = -2;
+
+ /**
+ * Status for {@link TisSessionCompat#notifySignalStrength(int)} and
+ * {@link TvViewCompat.TvInputCallback#onTimeShiftStatusChanged(String, int)}:
+ *
+ * <p>SIGNAL_STRENGTH_UNKNOWN means the TV Input supports signal strength, but does not
+ * currently know what the strength is.
+ */
+ public static final int SIGNAL_STRENGTH_UNKNOWN = -1;
+}
diff --git a/common/src/com/android/tv/common/compat/TvInputInfoCompat.java b/common/src/com/android/tv/common/compat/TvInputInfoCompat.java
new file mode 100644
index 00000000..685a3ed9
--- /dev/null
+++ b/common/src/com/android/tv/common/compat/TvInputInfoCompat.java
@@ -0,0 +1,117 @@
+/*
+ * 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.common.compat;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.media.tv.TvInputInfo;
+import android.media.tv.TvInputService;
+import android.support.annotation.VisibleForTesting;
+import android.util.Log;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * TIF Compatibility for {@link TvInputInfo}.
+ */
+public class TvInputInfoCompat {
+ private static final String TAG = "TvInputInfoCompat";
+ private static final String ATTRIBUTE_NAMESPACE_ANDROID =
+ "http://schemas.android.com/apk/res/android";
+ private static final String TV_INPUT_XML_START_TAG_NAME = "tv-input";
+ private static final String TV_INPUT_EXTRA_XML_START_TAG_NAME = "extra";
+ private static final String ATTRIBUTE_NAME = "name";
+ private static final String ATTRIBUTE_VALUE = "value";
+ private static final String ATTRIBUTE_NAME_AUDIO_ONLY =
+ "com.android.tv.common.compat.tvinputinfocompat.audioOnly";
+
+ private final Context mContext;
+ private final TvInputInfo mTvInputInfo;
+ private final boolean mAudioOnly;
+
+ public TvInputInfoCompat(Context context, TvInputInfo tvInputInfo) {
+ mContext = context;
+ mTvInputInfo = tvInputInfo;
+ // TODO(b/112938832): use tvInputInfo.isAudioOnly() when SDK is updated
+ mAudioOnly = Boolean.parseBoolean(getExtras().get(ATTRIBUTE_NAME_AUDIO_ONLY));
+ }
+
+ public TvInputInfo getTvInputInfo() {
+ return mTvInputInfo;
+ }
+
+ public boolean isAudioOnly() {
+ return mAudioOnly;
+ }
+
+ public int getType() {
+ return mTvInputInfo.getType();
+ }
+
+ @VisibleForTesting
+ public Map<String, String> getExtras() {
+ ServiceInfo si = mTvInputInfo.getServiceInfo();
+
+ try {
+ XmlPullParser parser = getXmlResourceParser();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.START_TAG) {
+ }
+
+ if (!TV_INPUT_XML_START_TAG_NAME.equals(parser.getName())) {
+ Log.w(TAG, "Meta-data does not start with " + TV_INPUT_XML_START_TAG_NAME
+ + " tag for " + si.name);
+ return Collections.emptyMap();
+ }
+ // <tv-input> start tag found
+ Map<String, String> extras = new HashMap<>();
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (type == XmlPullParser.END_TAG
+ && TV_INPUT_XML_START_TAG_NAME.equals(parser.getName())) {
+ // </tv-input> end tag found
+ return extras;
+ }
+ if (type == XmlPullParser.START_TAG
+ && TV_INPUT_EXTRA_XML_START_TAG_NAME.equals(parser.getName())) {
+ String extraName =
+ parser.getAttributeValue(ATTRIBUTE_NAMESPACE_ANDROID, ATTRIBUTE_NAME);
+ String extraValue =
+ parser.getAttributeValue(ATTRIBUTE_NAMESPACE_ANDROID, ATTRIBUTE_VALUE);
+ if (extraName != null && extraValue != null) {
+ extras.put(extraName, extraValue);
+ }
+ }
+ }
+
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to get extras of " + mTvInputInfo.getId() , e);
+ }
+ return Collections.emptyMap();
+ }
+
+ @VisibleForTesting
+ XmlPullParser getXmlResourceParser() {
+ ServiceInfo si = mTvInputInfo.getServiceInfo();
+ PackageManager pm = mContext.getPackageManager();
+ return si.loadXmlMetaData(pm, TvInputService.SERVICE_META_DATA);
+ }
+}
diff --git a/common/src/com/android/tv/common/compat/TvRecordingClientCompat.java b/common/src/com/android/tv/common/compat/TvRecordingClientCompat.java
new file mode 100644
index 00000000..143ff25c
--- /dev/null
+++ b/common/src/com/android/tv/common/compat/TvRecordingClientCompat.java
@@ -0,0 +1,101 @@
+/*
+ * 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.common.compat;
+
+import android.content.Context;
+import android.media.tv.TvRecordingClient;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.annotation.RequiresApi;
+import android.util.ArrayMap;
+import com.android.tv.common.compat.api.PrivateCommandSender;
+import com.android.tv.common.compat.api.RecordingClientCallbackCompatEvents;
+import com.android.tv.common.compat.api.TvRecordingClientCompatCommands;
+import com.android.tv.common.compat.internal.RecordingClientCompatProcessor;
+
+/**
+ * TIF Compatibility for {@link TvRecordingClient}.
+ *
+ * <p>Extends {@code TvRecordingClient} in a backwards compatible way.
+ */
+@RequiresApi(api = VERSION_CODES.N)
+public class TvRecordingClientCompat extends TvRecordingClient
+ implements TvRecordingClientCompatCommands, PrivateCommandSender {
+
+ private final RecordingClientCompatProcessor mProcessor;
+
+ /**
+ * Creates a new TvRecordingClient object.
+ *
+ * @param context The application context to create a TvRecordingClient with.
+ * @param tag A short name for debugging purposes.
+ * @param callback The callback to receive recording status changes.
+ * @param handler The handler to invoke the callback on.
+ */
+ public TvRecordingClientCompat(
+ Context context, String tag, RecordingCallback callback, Handler handler) {
+ super(context, tag, callback, handler);
+ RecordingCallbackCompat compatEvents =
+ callback instanceof RecordingCallbackCompat
+ ? (RecordingCallbackCompat) callback
+ : null;
+ mProcessor = new RecordingClientCompatProcessor(this, compatEvents);
+ if (compatEvents != null) {
+ compatEvents.mClientCompatProcessor = mProcessor;
+ }
+ }
+
+ /** Tell the session to Display a debug message dev builds only. */
+ @Override
+ public void devMessage(String message) {
+ mProcessor.devMessage(message);
+ }
+
+ /**
+ * TIF Compatibility for {@link RecordingCallback}.
+ *
+ * <p>Extends {@code RecordingCallback} in a backwards compatible way.
+ */
+ public static class RecordingCallbackCompat extends RecordingCallback
+ implements RecordingClientCallbackCompatEvents {
+ private final ArrayMap<String, Integer> inputCompatVersionMap = new ArrayMap<>();
+ private RecordingClientCompatProcessor mClientCompatProcessor;
+
+ @Override
+ public void onEvent(String inputId, String eventType, Bundle eventArgs) {
+ if (mClientCompatProcessor != null
+ && !mClientCompatProcessor.handleEvent(inputId, eventType, eventArgs)) {
+ super.onEvent(inputId, eventType, eventArgs);
+ }
+ }
+
+ public int getTifCompatVersionForInput(String inputId) {
+ return inputCompatVersionMap.containsKey(inputId)
+ ? inputCompatVersionMap.get(inputId)
+ : 0;
+ }
+
+ /** Display a message as a toast on dev builds only. */
+ @Override
+ public void onDevToast(String inputId, String message) {}
+
+ /** Recording started. */
+ @Override
+ public void onRecordingStarted(String inputId, String recUri) {}
+ }
+}
diff --git a/common/src/com/android/tv/common/compat/TvViewCompat.java b/common/src/com/android/tv/common/compat/TvViewCompat.java
new file mode 100644
index 00000000..f44564d3
--- /dev/null
+++ b/common/src/com/android/tv/common/compat/TvViewCompat.java
@@ -0,0 +1,111 @@
+/*
+ * 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.common.compat;
+
+import android.content.Context;
+import android.media.tv.TvView;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+import android.util.ArrayMap;
+import android.util.AttributeSet;
+import com.android.tv.common.compat.api.PrivateCommandSender;
+import com.android.tv.common.compat.api.TvInputCallbackCompatEvents;
+import com.android.tv.common.compat.api.TvViewCompatCommands;
+import com.android.tv.common.compat.internal.TvViewCompatProcessor;
+
+/**
+ * TIF Compatibility for {@link TvView}.
+ *
+ * <p>Extends {@code TvView} in a backwards compatible way.
+ */
+@RequiresApi(api = VERSION_CODES.LOLLIPOP)
+public class TvViewCompat extends TvView implements TvViewCompatCommands, PrivateCommandSender {
+
+ private final TvViewCompatProcessor mTvViewCompatProcessor;
+
+ public TvViewCompat(Context context) {
+ this(context, null);
+ }
+
+ public TvViewCompat(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public TvViewCompat(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mTvViewCompatProcessor = new TvViewCompatProcessor(this);
+ }
+
+ @Override
+ public void setCallback(TvInputCallback callback) {
+ super.setCallback(callback);
+ if (callback instanceof TvInputCallbackCompat) {
+ TvInputCallbackCompat compatEvents = (TvInputCallbackCompat) callback;
+ mTvViewCompatProcessor.setCallback(compatEvents);
+ compatEvents.mTvViewCompatProcessor = mTvViewCompatProcessor;
+ }
+ }
+
+ @Override
+ public void devMessage(String message) {
+ mTvViewCompatProcessor.devMessage(message);
+ }
+
+ /**
+ * TIF Compatibility for {@link TvInputCallback}.
+ *
+ * <p>Extends {@code TvInputCallback} in a backwards compatible way.
+ */
+ public static class TvInputCallbackCompat extends TvInputCallback
+ implements TvInputCallbackCompatEvents {
+ private final ArrayMap<String, Integer> inputCompatVersionMap = new ArrayMap<>();
+ private TvViewCompatProcessor mTvViewCompatProcessor;
+
+ @Override
+ public void onEvent(String inputId, String eventType, Bundle eventArgs) {
+ if (mTvViewCompatProcessor != null
+ && !mTvViewCompatProcessor.handleEvent(inputId, eventType, eventArgs)) {
+ super.onEvent(inputId, eventType, eventArgs);
+ }
+ }
+
+ public int getTifCompatVersionForInput(String inputId) {
+ return inputCompatVersionMap.containsKey(inputId)
+ ? inputCompatVersionMap.get(inputId)
+ : 0;
+ }
+
+ @Override
+ public void onDevToast(String inputId, String message) {}
+
+ /**
+ * This is called when the signal strength is notified.
+ *
+ * @param inputId The ID of the TV input bound to this view.
+ * @param value The current signal strength. Should be one of the followings.
+ * <ul>
+ * <li>{@link TvInputConstantCompat#SIGNAL_STRENGTH_NOT_USED}
+ * <li>{@link TvInputConstantCompat#SIGNAL_STRENGTH_ERROR}
+ * <li>{@link TvInputConstantCompat#SIGNAL_STRENGTH_UNKNOWN}
+ * <li>{int [0, 100]}
+ * </ul>
+ */
+ @Override
+ public void onSignalStrength(String inputId, int value) {}
+ }
+}
diff --git a/common/src/com/android/tv/common/compat/api/PrivateCommandSender.java b/common/src/com/android/tv/common/compat/api/PrivateCommandSender.java
new file mode 100644
index 00000000..11de970f
--- /dev/null
+++ b/common/src/com/android/tv/common/compat/api/PrivateCommandSender.java
@@ -0,0 +1,24 @@
+/*
+ * 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.common.compat.api;
+
+import android.os.Bundle;
+
+/** Sends a command from the TV App to a {@link android.media.tv.TvInputService.Session} */
+public interface PrivateCommandSender {
+
+ void sendAppPrivateCommand(String action, Bundle data);
+}
diff --git a/common/src/com/android/tv/common/compat/api/RecordingClientCallbackCompatEvents.java b/common/src/com/android/tv/common/compat/api/RecordingClientCallbackCompatEvents.java
new file mode 100644
index 00000000..753703c4
--- /dev/null
+++ b/common/src/com/android/tv/common/compat/api/RecordingClientCallbackCompatEvents.java
@@ -0,0 +1,27 @@
+/*
+ * 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.common.compat.api;
+
+/**
+ * {@link android.media.tv.TvRecordingClient} implements this to receive notification from a {@link
+ * android.media.tv.TvInputService.RecordingSession}
+ */
+public interface RecordingClientCallbackCompatEvents {
+ /** Display a message in the application as a toast on dev builds only */
+ void onDevToast(String inputId, String message);
+
+ void onRecordingStarted(String inputId, String recUri);
+}
diff --git a/common/src/com/android/tv/common/compat/api/RecordingSessionCompatCommands.java b/common/src/com/android/tv/common/compat/api/RecordingSessionCompatCommands.java
new file mode 100644
index 00000000..9deaa41f
--- /dev/null
+++ b/common/src/com/android/tv/common/compat/api/RecordingSessionCompatCommands.java
@@ -0,0 +1,22 @@
+/*
+ * 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.common.compat.api;
+
+/** Commands sent from the TV App to {@link android.media.tv.TvInputService.RecordingSession} */
+public interface RecordingSessionCompatCommands {
+
+ void onDevMessage(String message);
+}
diff --git a/common/src/com/android/tv/common/compat/api/RecordingSessionCompatEvents.java b/common/src/com/android/tv/common/compat/api/RecordingSessionCompatEvents.java
new file mode 100644
index 00000000..812bba62
--- /dev/null
+++ b/common/src/com/android/tv/common/compat/api/RecordingSessionCompatEvents.java
@@ -0,0 +1,24 @@
+/*
+ * 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.common.compat.api;
+
+/** Events sent from the {@link android.media.tv.TvInputService.RecordingSession} to the TV App. */
+public interface RecordingSessionCompatEvents {
+
+ void notifyDevToast(String message);
+
+ void notifyRecordingStarted(String value);
+}
diff --git a/common/src/com/android/tv/common/compat/api/SessionCompatCommands.java b/common/src/com/android/tv/common/compat/api/SessionCompatCommands.java
new file mode 100644
index 00000000..bef4ad27
--- /dev/null
+++ b/common/src/com/android/tv/common/compat/api/SessionCompatCommands.java
@@ -0,0 +1,22 @@
+/*
+ * 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.common.compat.api;
+
+/** Commands sent from the TV App to {@link android.media.tv.TvInputService.Session} */
+public interface SessionCompatCommands {
+
+ void onDevMessage(String message);
+}
diff --git a/common/src/com/android/tv/common/compat/api/SessionCompatEvents.java b/common/src/com/android/tv/common/compat/api/SessionCompatEvents.java
new file mode 100644
index 00000000..a3af8f3c
--- /dev/null
+++ b/common/src/com/android/tv/common/compat/api/SessionCompatEvents.java
@@ -0,0 +1,24 @@
+/*
+ * 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.common.compat.api;
+
+/** Events sent from the {@link android.media.tv.TvInputService.Session} to the TV App. */
+public interface SessionCompatEvents {
+
+ void notifyDevToast(String message);
+
+ void notifySignalStrength(int value);
+}
diff --git a/common/src/com/android/tv/common/compat/api/SessionEventNotifier.java b/common/src/com/android/tv/common/compat/api/SessionEventNotifier.java
new file mode 100644
index 00000000..66c5c3a8
--- /dev/null
+++ b/common/src/com/android/tv/common/compat/api/SessionEventNotifier.java
@@ -0,0 +1,24 @@
+/*
+ * 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.common.compat.api;
+
+import android.os.Bundle;
+
+/** Sends events from a {@link android.media.tv.TvInputService.Session} to the TV App. */
+public interface SessionEventNotifier {
+
+ void notifySessionEvent(String event, Bundle data);
+}
diff --git a/common/src/com/android/tv/common/compat/api/TvInputCallbackCompatEvents.java b/common/src/com/android/tv/common/compat/api/TvInputCallbackCompatEvents.java
new file mode 100644
index 00000000..e6b241b7
--- /dev/null
+++ b/common/src/com/android/tv/common/compat/api/TvInputCallbackCompatEvents.java
@@ -0,0 +1,26 @@
+/*
+ * 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.common.compat.api;
+
+/**
+ * {@link android.media.tv.TvView.TvInputCallback} implements this to receive notification from a
+ * {@link android.media.tv.TvInputService.Session}
+ */
+public interface TvInputCallbackCompatEvents {
+ void onDevToast(String inputId, String message);
+
+ void onSignalStrength(String inputId, int value);
+}
diff --git a/common/src/com/android/tv/common/compat/api/TvRecordingClientCompatCommands.java b/common/src/com/android/tv/common/compat/api/TvRecordingClientCompatCommands.java
new file mode 100644
index 00000000..c1852165
--- /dev/null
+++ b/common/src/com/android/tv/common/compat/api/TvRecordingClientCompatCommands.java
@@ -0,0 +1,22 @@
+/*
+ * 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.common.compat.api;
+
+/** Commands sent from the TV App to {@link android.media.tv.TvInputService.RecordingSession} */
+public interface TvRecordingClientCompatCommands {
+
+ void devMessage(String message);
+}
diff --git a/common/src/com/android/tv/common/compat/api/TvViewCompatCommands.java b/common/src/com/android/tv/common/compat/api/TvViewCompatCommands.java
new file mode 100644
index 00000000..5abc6bce
--- /dev/null
+++ b/common/src/com/android/tv/common/compat/api/TvViewCompatCommands.java
@@ -0,0 +1,22 @@
+/*
+ * 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.common.compat.api;
+
+/** Commands sent from the TV App to {@link android.media.tv.TvInputService.Session} */
+public interface TvViewCompatCommands {
+
+ void devMessage(String message);
+}
diff --git a/common/src/com/android/tv/common/compat/internal/Constants.java b/common/src/com/android/tv/common/compat/internal/Constants.java
new file mode 100644
index 00000000..993822c1
--- /dev/null
+++ b/common/src/com/android/tv/common/compat/internal/Constants.java
@@ -0,0 +1,28 @@
+/*
+ * 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.common.compat.internal;
+/** Static constants use by the TIF compat library */
+final class Constants {
+ static final String ACTION_GET_VERSION = "com.android.tv.common.compat.action.GET_VERSION";
+ static final String EVENT_GET_VERSION = "com.android.tv.common.compat.event.GET_VERSION";
+ static final String ACTION_COMPAT_ON = "com.android.tv.common.compat.action.COMPAT_ON";
+ static final String EVENT_COMPAT_NOTIFY = "com.android.tv.common.compat.event.COMPAT_NOTIFY";
+ static final String EVENT_COMPAT_NOTIFY_ERROR =
+ "com.android.tv.common.compat.event.COMPAT_NOTIFY_ERROR";
+ static final int TIF_COMPAT_VERSION = 1;
+
+ private Constants() {}
+}
diff --git a/common/src/com/android/tv/common/compat/internal/RecordingClientCompatProcessor.java b/common/src/com/android/tv/common/compat/internal/RecordingClientCompatProcessor.java
new file mode 100644
index 00000000..f83228c8
--- /dev/null
+++ b/common/src/com/android/tv/common/compat/internal/RecordingClientCompatProcessor.java
@@ -0,0 +1,85 @@
+/*
+ * 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.common.compat.internal;
+
+import android.support.annotation.Nullable;
+import android.util.Log;
+import com.android.tv.common.compat.api.PrivateCommandSender;
+import com.android.tv.common.compat.api.RecordingClientCallbackCompatEvents;
+import com.android.tv.common.compat.api.TvViewCompatCommands;
+import com.android.tv.common.compat.internal.Commands.OnDevMessage;
+import com.android.tv.common.compat.internal.Commands.PrivateCommand;
+import com.android.tv.common.compat.internal.RecordingEvents.NotifyDevToast;
+import com.android.tv.common.compat.internal.RecordingEvents.RecordingSessionEvent;
+
+/**
+ * Sends {@link RecordingCommands} to the {@link android.media.tv.TvInputService.RecordingSession}
+ * via {@link PrivateCommandSender} and receives notification events from the session forwarding
+ * them to {@link RecordingClientCallbackCompatEvents}
+ */
+public final class RecordingClientCompatProcessor
+ extends ViewCompatProcessor<PrivateCommand, RecordingSessionEvent>
+ implements TvViewCompatCommands {
+ private static final String TAG = "RecordingClientCompatProcessor";
+
+ @Nullable private final RecordingClientCallbackCompatEvents mCallback;
+
+ public RecordingClientCompatProcessor(
+ PrivateCommandSender commandSender,
+ @Nullable RecordingClientCallbackCompatEvents callback) {
+ super(commandSender, RecordingSessionEvent.parser());
+ mCallback = callback;
+ }
+
+ @Override
+ public void devMessage(String message) {
+ OnDevMessage devMessage = OnDevMessage.newBuilder().setMessage(message).build();
+ PrivateCommand privateCommand =
+ createPrivateCommandCommand().setOnDevMessage(devMessage).build();
+ sendCompatCommand(privateCommand);
+ }
+
+ private PrivateCommand.Builder createPrivateCommandCommand() {
+ return PrivateCommand.newBuilder().setCompatVersion(Constants.TIF_COMPAT_VERSION);
+ }
+
+ @Override
+ protected final void handleSessionEvent(String inputId, RecordingSessionEvent sessionEvent) {
+ switch (sessionEvent.getEventCase()) {
+ case NOTIFY_DEV_MESSAGE:
+ handle(inputId, sessionEvent.getNotifyDevMessage());
+ break;
+ case RECORDING_STARTED:
+ handle(inputId, sessionEvent.getRecordingStarted());
+ break;
+
+ case EVENT_NOT_SET:
+ Log.w(TAG, "Error event not set compat notify ");
+ }
+ }
+
+ private void handle(String inputId, NotifyDevToast devToast) {
+ if (devToast != null && mCallback != null) {
+ mCallback.onDevToast(inputId, devToast.getMessage());
+ }
+ }
+
+ private void handle(String inputId, RecordingEvents.RecordingStarted recStart) {
+ if (recStart != null && mCallback != null) {
+ mCallback.onRecordingStarted(inputId, recStart.getUri());
+ }
+ }
+}
diff --git a/common/src/com/android/tv/common/compat/internal/RecordingSessionCompatProcessor.java b/common/src/com/android/tv/common/compat/internal/RecordingSessionCompatProcessor.java
new file mode 100644
index 00000000..84ec5503
--- /dev/null
+++ b/common/src/com/android/tv/common/compat/internal/RecordingSessionCompatProcessor.java
@@ -0,0 +1,78 @@
+/*
+ * 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.common.compat.internal;
+
+import android.util.Log;
+import com.android.tv.common.compat.api.RecordingSessionCompatCommands;
+import com.android.tv.common.compat.api.RecordingSessionCompatEvents;
+import com.android.tv.common.compat.api.SessionEventNotifier;
+import com.android.tv.common.compat.internal.RecordingCommands.PrivateRecordingCommand;
+import com.android.tv.common.compat.internal.RecordingEvents.NotifyDevToast;
+import com.android.tv.common.compat.internal.RecordingEvents.RecordingSessionEvent;
+import com.android.tv.common.compat.internal.RecordingEvents.RecordingStarted;
+
+/**
+ * Sends {@link RecordingSessionCompatEvents} to the TV App via {@link SessionEventNotifier} and
+ * receives Commands from TV App forwarding them to {@link RecordingSessionCompatProcessor}
+ */
+public final class RecordingSessionCompatProcessor
+ extends SessionCompatProcessor<PrivateRecordingCommand, RecordingSessionEvent>
+ implements RecordingSessionCompatEvents {
+
+ private static final String TAG = "RecordingSessionCompatProc";
+
+ private final RecordingSessionCompatCommands mRecordingSessionOnCompat;
+
+ public RecordingSessionCompatProcessor(
+ SessionEventNotifier sessionEventNotifier,
+ RecordingSessionCompatCommands recordingSessionOnCompat) {
+ super(sessionEventNotifier, PrivateRecordingCommand.parser());
+ mRecordingSessionOnCompat = recordingSessionOnCompat;
+ }
+
+ @Override
+ protected void onCompat(PrivateRecordingCommand privateCommand) {
+ switch (privateCommand.getCommandCase()) {
+ case ON_DEV_MESSAGE:
+ mRecordingSessionOnCompat.onDevMessage(
+ privateCommand.getOnDevMessage().getMessage());
+ break;
+ case COMMAND_NOT_SET:
+ Log.w(TAG, "Command not set ");
+ }
+ }
+
+ @Override
+ public void notifyDevToast(String message) {
+ NotifyDevToast devMessage = NotifyDevToast.newBuilder().setMessage(message).build();
+ RecordingSessionEvent sessionEvent =
+ createSessionEvent().setNotifyDevMessage(devMessage).build();
+ notifyCompat(sessionEvent);
+ }
+
+ @Override
+ public void notifyRecordingStarted(String uri) {
+ RecordingStarted event = RecordingStarted.newBuilder().setUri(uri).build();
+ RecordingSessionEvent sessionEvent =
+ createSessionEvent().setRecordingStarted(event).build();
+ notifyCompat(sessionEvent);
+ }
+
+ private RecordingSessionEvent.Builder createSessionEvent() {
+
+ return RecordingSessionEvent.newBuilder().setCompatVersion(Constants.TIF_COMPAT_VERSION);
+ }
+}
diff --git a/common/src/com/android/tv/common/compat/internal/SessionCompatProcessor.java b/common/src/com/android/tv/common/compat/internal/SessionCompatProcessor.java
new file mode 100644
index 00000000..7f27a243
--- /dev/null
+++ b/common/src/com/android/tv/common/compat/internal/SessionCompatProcessor.java
@@ -0,0 +1,75 @@
+/*
+ * 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.common.compat.internal;
+
+import android.os.Bundle;
+import android.util.Log;
+import com.android.tv.common.compat.api.SessionEventNotifier;
+import com.google.protobuf.GeneratedMessageLite;
+import com.google.protobuf.InvalidProtocolBufferException;
+import com.google.protobuf.Parser;
+
+/**
+ * Sends {@code events} to the TV App via {@link SessionEventNotifier} and receives {@code commands}
+ * from TV App.
+ */
+abstract class SessionCompatProcessor<
+ C extends GeneratedMessageLite<C, ?>, E extends GeneratedMessageLite<E, ?>> {
+ private static final String TAG = "SessionCompatProcessor";
+ private final SessionEventNotifier mSessionEventNotifier;
+ private final Parser<C> mCommandParser;
+
+ SessionCompatProcessor(SessionEventNotifier sessionEventNotifier, Parser<C> commandParser) {
+ mSessionEventNotifier = sessionEventNotifier;
+ mCommandParser = commandParser;
+ }
+
+ public final boolean handleAppPrivateCommand(String action, Bundle data) {
+ switch (action) {
+ case Constants.ACTION_GET_VERSION:
+ Bundle response = new Bundle();
+ response.putInt(Constants.EVENT_GET_VERSION, Constants.TIF_COMPAT_VERSION);
+ mSessionEventNotifier.notifySessionEvent(Constants.EVENT_GET_VERSION, response);
+ return true;
+ case Constants.ACTION_COMPAT_ON:
+ byte[] bytes = data.getByteArray(Constants.ACTION_COMPAT_ON);
+ try {
+ C privateCommand = mCommandParser.parseFrom(bytes);
+ onCompat(privateCommand);
+ } catch (InvalidProtocolBufferException e) {
+ Log.w(TAG, "Error parsing compat data", e);
+ }
+
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ abstract void onCompat(C privateCommand);
+
+ final void notifyCompat(E event) {
+ Bundle response = new Bundle();
+ try {
+ byte[] bytes = event.toByteArray();
+ response.putByteArray(Constants.EVENT_COMPAT_NOTIFY, bytes);
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to send " + event, e);
+ response.putString(Constants.EVENT_COMPAT_NOTIFY_ERROR, e.getMessage());
+ }
+ mSessionEventNotifier.notifySessionEvent(Constants.EVENT_COMPAT_NOTIFY, response);
+ }
+}
diff --git a/common/src/com/android/tv/common/compat/internal/TifSessionCompatProcessor.java b/common/src/com/android/tv/common/compat/internal/TifSessionCompatProcessor.java
new file mode 100644
index 00000000..dd7a3b3e
--- /dev/null
+++ b/common/src/com/android/tv/common/compat/internal/TifSessionCompatProcessor.java
@@ -0,0 +1,75 @@
+/*
+ * 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.common.compat.internal;
+
+import android.util.Log;
+import com.android.tv.common.compat.api.SessionCompatCommands;
+import com.android.tv.common.compat.api.SessionCompatEvents;
+import com.android.tv.common.compat.api.SessionEventNotifier;
+import com.android.tv.common.compat.internal.Commands.PrivateCommand;
+import com.android.tv.common.compat.internal.Events.NotifyDevToast;
+import com.android.tv.common.compat.internal.Events.NotifySignalStrength;
+import com.android.tv.common.compat.internal.Events.SessionEvent;
+
+/**
+ * Sends {@link SessionCompatEvents} to the TV App via {@link SessionEventNotifier} and receives
+ * Commands from TV App forwarding them to {@link SessionCompatCommands}
+ */
+public final class TifSessionCompatProcessor
+ extends SessionCompatProcessor<PrivateCommand, SessionEvent>
+ implements SessionCompatEvents {
+
+ private static final String TAG = "TifSessionCompatProcessor";
+
+ private final SessionCompatCommands mSessionOnCompat;
+
+ public TifSessionCompatProcessor(
+ SessionEventNotifier sessionEventNotifier, SessionCompatCommands sessionOnCompat) {
+ super(sessionEventNotifier, PrivateCommand.parser());
+ mSessionOnCompat = sessionOnCompat;
+ }
+
+ @Override
+ protected void onCompat(Commands.PrivateCommand privateCommand) {
+ switch (privateCommand.getCommandCase()) {
+ case ON_DEV_MESSAGE:
+ mSessionOnCompat.onDevMessage(privateCommand.getOnDevMessage().getMessage());
+ break;
+ case COMMAND_NOT_SET:
+ Log.w(TAG, "Command not set ");
+ }
+ }
+
+ @Override
+ public void notifyDevToast(String message) {
+ NotifyDevToast devMessage = NotifyDevToast.newBuilder().setMessage(message).build();
+ SessionEvent sessionEvent = createSessionEvent().setNotifyDevMessage(devMessage).build();
+ notifyCompat(sessionEvent);
+ }
+
+ @Override
+ public void notifySignalStrength(int value) {
+ NotifySignalStrength signalStrength =
+ NotifySignalStrength.newBuilder().setSignalStrength(value).build();
+ SessionEvent sessionEvent =
+ createSessionEvent().setNotifySignalStrength(signalStrength).build();
+ notifyCompat(sessionEvent);
+ }
+
+ private SessionEvent.Builder createSessionEvent() {
+ return SessionEvent.newBuilder().setCompatVersion(Constants.TIF_COMPAT_VERSION);
+ }
+}
diff --git a/common/src/com/android/tv/common/compat/internal/TvViewCompatProcessor.java b/common/src/com/android/tv/common/compat/internal/TvViewCompatProcessor.java
new file mode 100644
index 00000000..382f8d8a
--- /dev/null
+++ b/common/src/com/android/tv/common/compat/internal/TvViewCompatProcessor.java
@@ -0,0 +1,90 @@
+/*
+ * 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.common.compat.internal;
+
+import android.support.annotation.NonNull;
+import android.util.Log;
+import com.android.tv.common.compat.api.PrivateCommandSender;
+import com.android.tv.common.compat.api.TvInputCallbackCompatEvents;
+import com.android.tv.common.compat.api.TvViewCompatCommands;
+import com.android.tv.common.compat.internal.Commands.OnDevMessage;
+import com.android.tv.common.compat.internal.Commands.PrivateCommand;
+import com.android.tv.common.compat.internal.Events.NotifyDevToast;
+import com.android.tv.common.compat.internal.Events.NotifySignalStrength;
+import com.android.tv.common.compat.internal.Events.SessionEvent;
+
+/**
+ * Sends {@link TvViewCompatCommands} to the {@link android.media.tv.TvInputService.Session} via
+ * {@link PrivateCommandSender} and receives notification events from the session forwarding them to
+ * {@link TvInputCallbackCompatEvents}
+ */
+public final class TvViewCompatProcessor extends ViewCompatProcessor<PrivateCommand, SessionEvent>
+ implements TvViewCompatCommands {
+ private static final String TAG = "TvViewCompatProcessor";
+
+ private TvInputCallbackCompatEvents mCallback;
+
+ public TvViewCompatProcessor(PrivateCommandSender commandSender) {
+ super(commandSender, SessionEvent.parser());
+ }
+
+ @Override
+ public void devMessage(String message) {
+ OnDevMessage devMessage = Commands.OnDevMessage.newBuilder().setMessage(message).build();
+ Commands.PrivateCommand privateCommand =
+ createPrivateCommandCommand().setOnDevMessage(devMessage).build();
+ sendCompatCommand(privateCommand);
+ }
+
+ @NonNull
+ private PrivateCommand.Builder createPrivateCommandCommand() {
+ return PrivateCommand.newBuilder().setCompatVersion(Constants.TIF_COMPAT_VERSION);
+ }
+
+ public void onDevToast(String inputId, String message) {}
+
+ public void onSignalStrength(String inputId, int value) {}
+
+ @Override
+ protected final void handleSessionEvent(String inputId, Events.SessionEvent sessionEvent) {
+ switch (sessionEvent.getEventCase()) {
+ case NOTIFY_DEV_MESSAGE:
+ handle(inputId, sessionEvent.getNotifyDevMessage());
+ break;
+ case NOTIFY_SIGNAL_STRENGTH:
+ handle(inputId, sessionEvent.getNotifySignalStrength());
+ break;
+ case EVENT_NOT_SET:
+ Log.w(TAG, "Error event not set compat notify ");
+ }
+ }
+
+ private void handle(String inputId, NotifyDevToast devToast) {
+ if (devToast != null && mCallback != null) {
+ mCallback.onDevToast(inputId, devToast.getMessage());
+ }
+ }
+
+ private void handle(String inputId, NotifySignalStrength signalStrength) {
+ if (signalStrength != null && mCallback != null) {
+ mCallback.onSignalStrength(inputId, signalStrength.getSignalStrength());
+ }
+ }
+
+ public void setCallback(TvInputCallbackCompatEvents callback) {
+ this.mCallback = callback;
+ }
+}
diff --git a/common/src/com/android/tv/common/compat/internal/ViewCompatProcessor.java b/common/src/com/android/tv/common/compat/internal/ViewCompatProcessor.java
new file mode 100644
index 00000000..dc6bbfa5
--- /dev/null
+++ b/common/src/com/android/tv/common/compat/internal/ViewCompatProcessor.java
@@ -0,0 +1,90 @@
+/*
+ * 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.common.compat.internal;
+
+import android.os.Bundle;
+import android.util.ArrayMap;
+import android.util.Log;
+import com.android.tv.common.compat.api.PrivateCommandSender;
+import com.google.protobuf.GeneratedMessageLite;
+import com.google.protobuf.InvalidProtocolBufferException;
+import com.google.protobuf.Parser;
+
+/**
+ * Sends {@code commands} to the {@code session} via {@link PrivateCommandSender} and receives
+ * notification events from the session forwarding them to {@link
+ * com.android.tv.common.compat.api.TvInputCallbackCompatEvents}
+ */
+abstract class ViewCompatProcessor<
+ C extends GeneratedMessageLite<C, ?>, E extends GeneratedMessageLite<E, ?>> {
+ private static final String TAG = "ViewCompatProcessor";
+ private final ArrayMap<String, Integer> inputCompatVersionMap = new ArrayMap<>();
+
+ private final Parser<E> mEventParser;
+ private final PrivateCommandSender mCommandSender;
+
+ ViewCompatProcessor(PrivateCommandSender commandSender, Parser<E> eventParser) {
+ mCommandSender = commandSender;
+ mEventParser = eventParser;
+ }
+
+ private final E sessionEventFromBundle(Bundle eventArgs) throws InvalidProtocolBufferException {
+
+ byte[] protoBytes = eventArgs.getByteArray(Constants.EVENT_COMPAT_NOTIFY);
+ return protoBytes == null || protoBytes.length == 0
+ ? null
+ : mEventParser.parseFrom(protoBytes);
+ }
+
+ final void sendCompatCommand(C privateCommand) {
+ try {
+ Bundle data = new Bundle();
+ data.putByteArray(Constants.ACTION_COMPAT_ON, privateCommand.toByteArray());
+ mCommandSender.sendAppPrivateCommand(Constants.ACTION_COMPAT_ON, data);
+ } catch (Exception e) {
+ Log.w(TAG, "Error sending compat action " + privateCommand, e);
+ }
+ }
+
+ public boolean handleEvent(String inputId, String eventType, Bundle eventArgs) {
+ switch (eventType) {
+ case Constants.EVENT_GET_VERSION:
+ int version = eventArgs.getInt(Constants.EVENT_GET_VERSION, 0);
+ inputCompatVersionMap.put(inputId, version);
+ return true;
+ case Constants.EVENT_COMPAT_NOTIFY:
+ try {
+ E sessionEvent = sessionEventFromBundle(eventArgs);
+ if (sessionEvent != null) {
+ handleSessionEvent(inputId, sessionEvent);
+ } else {
+ String errorMessage =
+ eventArgs.getString(Constants.EVENT_COMPAT_NOTIFY_ERROR);
+ Log.w(TAG, "Error sent in compat notify " + errorMessage);
+ }
+
+ } catch (InvalidProtocolBufferException e) {
+ Log.w(TAG, "Error parsing in compat notify for " + inputId);
+ }
+
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ protected abstract void handleSessionEvent(String inputId, E sessionEvent);
+}
diff --git a/common/src/com/android/tv/common/compat/internal/recording_commands.proto b/common/src/com/android/tv/common/compat/internal/recording_commands.proto
new file mode 100644
index 00000000..ce59bfa0
--- /dev/null
+++ b/common/src/com/android/tv/common/compat/internal/recording_commands.proto
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+// A set of Private Commands to send to a TVInputService.Session, in particular
+// support new features on older devices. NOTE: this proto is internal to this
+// package and should not be used outside it.
+
+syntax = "proto3";
+package android.tv.common.compat.internal;
+
+option java_outer_classname = "RecordingCommands";
+option java_package = "com.android.tv.common.compat.internal";
+
+// Wraps messages for sending to a session a private command.
+message PrivateRecordingCommand {
+ uint32 compat_version = 1;
+
+ oneof command {
+ OnDevMessage on_dev_message = 2;
+ }
+}
+
+// Display a debug message dev builds only.
+message OnDevMessage {
+ string message = 1;
+}
diff --git a/common/src/com/android/tv/common/compat/internal/recording_events.proto b/common/src/com/android/tv/common/compat/internal/recording_events.proto
new file mode 100644
index 00000000..68db5ddf
--- /dev/null
+++ b/common/src/com/android/tv/common/compat/internal/recording_events.proto
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+// A set of Session events to send from a TVInputService.Session, in particular
+// support new features on older devices. NOTE: this proto is internal to this
+// package and should not be used outside it.
+syntax = "proto3";
+package android.tv.common.compat.internal;
+
+option java_outer_classname = "RecordingEvents";
+option java_package = "com.android.tv.common.compat.internal";
+
+// Wraps messages for sending from a session as an Event.
+// RecordingSessionCompat will have a notify{EventMessageName} for each event
+// TvRecordingClientCompat will have a on{EventMessageName} for each event
+
+message RecordingSessionEvent {
+ uint32 compat_version = 1;
+
+ oneof event {
+ NotifyDevToast notify_dev_message = 2;
+ RecordingStarted recording_started = 3;
+ }
+}
+
+// Display a message as a toast on dev builds only
+message NotifyDevToast {
+ string message = 1;
+}
+
+// Recording started.
+message RecordingStarted {
+ // Recording URI.
+ string uri = 1;
+}
+
diff --git a/common/src/com/android/tv/common/compat/internal/tif_commands.proto b/common/src/com/android/tv/common/compat/internal/tif_commands.proto
new file mode 100644
index 00000000..d5867703
--- /dev/null
+++ b/common/src/com/android/tv/common/compat/internal/tif_commands.proto
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+// A set of Private Commands to send to a TVInputService.Session, in particular
+// support new features on older devices. NOTE: this proto is internal to this
+// package and should not be used outside it.
+
+syntax = "proto3";
+package android.tv.common.compat.internal;
+
+option java_outer_classname = "Commands";
+option java_package = "com.android.tv.common.compat.internal";
+
+// Wraps messages for sending to a session a private command.
+message PrivateCommand {
+ uint32 compat_version = 1;
+
+ oneof command {
+ OnDevMessage on_dev_message = 2;
+ }
+}
+
+// Sends a debug message to the session for display on dev builds only
+message OnDevMessage {
+ string message = 1;
+}
diff --git a/common/src/com/android/tv/common/compat/internal/tif_events.proto b/common/src/com/android/tv/common/compat/internal/tif_events.proto
new file mode 100644
index 00000000..6e71ae11
--- /dev/null
+++ b/common/src/com/android/tv/common/compat/internal/tif_events.proto
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+// A set of Session events to send from a TVInputService.Session, in particular
+// support new features on older devices. NOTE: this proto is internal to this
+// package and should not be used outside it.
+syntax = "proto3";
+package android.tv.common.compat.internal;
+
+option java_outer_classname = "Events";
+option java_package = "com.android.tv.common.compat.internal";
+
+// Wraps messages for sending from a session as an Event.
+message SessionEvent {
+ uint32 compat_version = 1;
+
+ oneof event {
+ NotifyDevToast notify_dev_message = 2;
+ NotifySignalStrength notify_signal_strength = 3;
+ }
+}
+
+// Send a message to the application to be displayed as a toast on dev builds
+// only
+message NotifyDevToast {
+ string message = 1;
+}
+
+// Notifies the TV Application the current signal strength.
+message NotifySignalStrength {
+ // The signal strength as a percent (0 to 100),
+ // with -1 meaning unknown, -2 meaning not used.
+ int32 signal_strength = 1;
+}
diff --git a/common/src/com/android/tv/common/config/DefaultConfigManager.java b/common/src/com/android/tv/common/config/DefaultConfigManager.java
deleted file mode 100644
index ae240855..00000000
--- a/common/src/com/android/tv/common/config/DefaultConfigManager.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2016 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.common.config;
-
-import android.content.Context;
-import com.android.tv.common.config.api.RemoteConfig;
-
-/** Stub Remote Config. */
-public class DefaultConfigManager {
- public static final long DEFAULT_LONG_VALUE = 0;
-
- public static DefaultConfigManager createInstance(Context context) {
- return new DefaultConfigManager();
- }
-
- private StubRemoteConfig mRemoteConfig = new StubRemoteConfig();
-
- public RemoteConfig getRemoteConfig() {
- return mRemoteConfig;
- }
-
- private static class StubRemoteConfig implements RemoteConfig {
- @Override
- public void fetch(OnRemoteConfigUpdatedListener listener) {}
-
- @Override
- public String getString(String key) {
- return null;
- }
-
- @Override
- public boolean getBoolean(String key) {
- return false;
- }
-
- @Override
- public long getLong(String key) {
- return DEFAULT_LONG_VALUE;
- }
-
- @Override
- public long getLong(String key, long defaultValue) {
- return defaultValue;
- }
- }
-}
diff --git a/common/src/com/android/tv/common/config/RemoteConfigFeature.java b/common/src/com/android/tv/common/config/RemoteConfigFeature.java
deleted file mode 100644
index 2ea381f0..00000000
--- a/common/src/com/android/tv/common/config/RemoteConfigFeature.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2016 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.common.config;
-
-import android.content.Context;
-import com.android.tv.common.BaseApplication;
-import com.android.tv.common.feature.Feature;
-
-/**
- * A {@link Feature} controlled by a {@link com.android.tv.common.config.api.RemoteConfig} boolean.
- */
-public class RemoteConfigFeature implements Feature {
- private final String mKey;
-
- /** Creates a {@link RemoteConfigFeature for the {@code key}. */
- public static RemoteConfigFeature fromKey(String key) {
- return new RemoteConfigFeature(key);
- }
-
- private RemoteConfigFeature(String key) {
- mKey = key;
- }
-
- @Override
- public boolean isEnabled(Context context) {
- return BaseApplication.getSingletons(context).getRemoteConfig().getBoolean(mKey);
- }
-}
diff --git a/common/src/com/android/tv/common/config/api/RemoteConfig.java b/common/src/com/android/tv/common/config/api/RemoteConfig.java
deleted file mode 100644
index 74597f9d..00000000
--- a/common/src/com/android/tv/common/config/api/RemoteConfig.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2016 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.common.config.api;
-
-/**
- * Manages Live TV Configuration, allowing remote updates.
- *
- * <p>This is a thin wrapper around <a
- * href="https://firebase.google.com/docs/remote-config/"></a>Firebase Remote Config</a>
- */
-public interface RemoteConfig {
-
- /** Used to inject a remote config */
- interface HasRemoteConfig {
- RemoteConfig getRemoteConfig();
- }
-
- /** Notified on successful completion of a {@link #fetch)} */
- interface OnRemoteConfigUpdatedListener {
- void onRemoteConfigUpdated();
- }
-
- /** Starts a fetch and notifies {@code listener} on successful completion. */
- void fetch(OnRemoteConfigUpdatedListener listener);
-
- /** Gets value as a string corresponding to the specified key. */
- String getString(String key);
-
- /** Gets value as a boolean corresponding to the specified key. */
- boolean getBoolean(String key);
-
- /** Gets value as a long corresponding to the specified key. */
- long getLong(String key);
-
- /**
- * Gets value as a long corresponding to the specified key. Returns the defaultValue if no value
- * is found.
- */
- long getLong(String key, long defaultValue);
-}
diff --git a/common/src/com/android/tv/common/config/api/RemoteConfigValue.java b/common/src/com/android/tv/common/config/api/RemoteConfigValue.java
deleted file mode 100644
index 6da89fb9..00000000
--- a/common/src/com/android/tv/common/config/api/RemoteConfigValue.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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.common.config.api;
-
-/** Wrapper for a RemoteConfig key and default value. */
-public abstract class RemoteConfigValue<T> {
- private final T defaultValue;
- private final String key;
-
- private RemoteConfigValue(String key, T defaultValue) {
- this.defaultValue = defaultValue;
- this.key = key;
- }
-
- /** Create with the given key and default value */
- public static RemoteConfigValue<Long> create(String key, long defaultValue) {
- return new RemoteConfigValue<Long>(key, defaultValue) {
- @Override
- public Long get(RemoteConfig remoteConfig) {
- return remoteConfig.getLong(key, defaultValue);
- }
- };
- }
-
- public abstract T get(RemoteConfig remoteConfig);
-
- public final T getDefaultValue() {
- return defaultValue;
- }
-
- @Override
- public final String toString() {
- return "RemoteConfigValue(key=" + key + ", defalutValue=" + defaultValue + "]";
- }
-}
diff --git a/common/src/com/android/tv/common/dagger/ApplicationModule.java b/common/src/com/android/tv/common/dagger/ApplicationModule.java
new file mode 100644
index 00000000..4655f777
--- /dev/null
+++ b/common/src/com/android/tv/common/dagger/ApplicationModule.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 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.common.dagger;
+
+import android.app.Application;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.os.Looper;
+import com.android.tv.common.dagger.annotations.ApplicationContext;
+import com.android.tv.common.dagger.annotations.MainLooper;
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Provides application-scope qualifiers for the {@link Application}, the application context, and
+ * the application's main looper.
+ */
+@Module
+public final class ApplicationModule {
+ private final Application mApplication;
+
+ public ApplicationModule(Application application) {
+ mApplication = application;
+ }
+
+ @Provides
+ Application provideApplication() {
+ return mApplication;
+ }
+
+ @Provides
+ @ApplicationContext
+ Context provideContext() {
+ return mApplication.getApplicationContext();
+ }
+
+ @Provides
+ @MainLooper
+ static Looper provideMainLooper() {
+ return Looper.getMainLooper();
+ }
+
+ @Provides
+ ContentResolver provideContentResolver() {
+ return mApplication.getContentResolver();
+ }
+}
diff --git a/common/src/com/android/tv/common/dagger/annotations/ApplicationContext.java b/common/src/com/android/tv/common/dagger/annotations/ApplicationContext.java
new file mode 100644
index 00000000..86318156
--- /dev/null
+++ b/common/src/com/android/tv/common/dagger/annotations/ApplicationContext.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2019 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.common.dagger.annotations;
+
+import javax.inject.Qualifier;
+
+/** Annotation for requesting the application's context. */
+@Qualifier
+public @interface ApplicationContext {}
diff --git a/common/src/com/android/tv/common/dagger/annotations/MainLooper.java b/common/src/com/android/tv/common/dagger/annotations/MainLooper.java
new file mode 100644
index 00000000..a8b4100f
--- /dev/null
+++ b/common/src/com/android/tv/common/dagger/annotations/MainLooper.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2019 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.common.dagger.annotations;
+
+import javax.inject.Qualifier;
+
+/** Annotation for requesting a Looper that is on the UI thread. */
+@Qualifier
+public @interface MainLooper {}
diff --git a/common/src/com/android/tv/common/data/RecordedProgramState.java b/common/src/com/android/tv/common/data/RecordedProgramState.java
new file mode 100644
index 00000000..3bfad88d
--- /dev/null
+++ b/common/src/com/android/tv/common/data/RecordedProgramState.java
@@ -0,0 +1,14 @@
+package com.android.tv.common.data;
+
+/** The recording state. */
+// TODO(b/25023911): Use @SimpleEnum when it is supported by AutoValue
+public enum RecordedProgramState {
+ // TODO(b/71717809): Document each state.
+ NOT_SET,
+ STARTED,
+ FINISHED,
+ PARTIAL,
+ FAILED,
+ DELETE,
+ DELETED,
+}
diff --git a/common/src/com/android/tv/common/experiments/ExperimentFlag.java b/common/src/com/android/tv/common/experiments/ExperimentFlag.java
index c9bacac4..b8370ad6 100644
--- a/common/src/com/android/tv/common/experiments/ExperimentFlag.java
+++ b/common/src/com/android/tv/common/experiments/ExperimentFlag.java
@@ -19,41 +19,63 @@ package com.android.tv.common.experiments;
import android.support.annotation.VisibleForTesting;
import com.android.tv.common.BuildConfig;
+import com.google.common.base.Supplier;
/** Experiments return values based on user, device and other criteria. */
public final class ExperimentFlag<T> {
+ // NOTE: sAllowOverrides IS NEVER USED in the non AOSP version.
private static boolean sAllowOverrides = false;
@VisibleForTesting
public static void initForTest() {
+ /* Begin_AOSP_Comment_Out
+ if (!BuildConfig.AOSP) {
+ PhenotypeFlag.initForTest();
+ return;
+ }
+ End_AOSP_Comment_Out */
sAllowOverrides = true;
}
/** Returns a boolean experiment */
public static ExperimentFlag<Boolean> createFlag(
+// AOSP_Comment_Out Supplier<Boolean> phenotypeFlag,
boolean defaultValue) {
return new ExperimentFlag<>(
+// AOSP_Comment_Out phenotypeFlag,
defaultValue);
}
private final T mDefaultValue;
+// AOSP_Comment_Out private final Supplier<T> mPhenotypeFlag;
+// AOSP_Comment_Out // NOTE: mOverrideValue IS NEVER USED in the non AOSP version.
private T mOverrideValue = null;
+ // mOverridden IS NEVER USED in the non AOSP version.
private boolean mOverridden = false;
private ExperimentFlag(
+// AOSP_Comment_Out Supplier<T> phenotypeFlag,
+ // NOTE: defaultValue IS NEVER USED in the non AOSP version.
T defaultValue) {
mDefaultValue = defaultValue;
+// AOSP_Comment_Out mPhenotypeFlag = phenotypeFlag;
}
/** Returns value for this experiment */
public T get() {
+ /* Begin_AOSP_Comment_Out
+ if (!BuildConfig.AOSP) {
+ return mPhenotypeFlag.get();
+ }
+ End_AOSP_Comment_Out */
return sAllowOverrides && mOverridden ? mOverrideValue : mDefaultValue;
}
@VisibleForTesting
public void override(T t) {
+
if (sAllowOverrides) {
mOverridden = true;
mOverrideValue = t;
@@ -64,4 +86,11 @@ public final class ExperimentFlag<T> {
public void resetOverride() {
mOverridden = false;
}
+
+ /* Begin_AOSP_Comment_Out
+ @VisibleForTesting
+ T getAospDefaultValueForTesting() {
+ return mDefaultValue;
+ }
+ End_AOSP_Comment_Out */
}
diff --git a/common/src/com/android/tv/common/experiments/Experiments.java b/common/src/com/android/tv/common/experiments/Experiments.java
index 96b15e53..9bfdb547 100644
--- a/common/src/com/android/tv/common/experiments/Experiments.java
+++ b/common/src/com/android/tv/common/experiments/Experiments.java
@@ -19,6 +19,7 @@ package com.android.tv.common.experiments;
import static com.android.tv.common.experiments.ExperimentFlag.createFlag;
import com.android.tv.common.BuildConfig;
+// AOSP_Comment_Out import com.android.tv.common.flags.LiveChannels;
/**
* Set of experiments visible in AOSP.
@@ -26,17 +27,15 @@ import com.android.tv.common.BuildConfig;
* <p>This file is maintained by hand.
*/
public final class Experiments {
- public static final ExperimentFlag<Boolean> CLOUD_EPG =
- ExperimentFlag.createFlag(
- true);
-
public static final ExperimentFlag<Boolean> ENABLE_UNRATED_CONTENT_SETTINGS =
ExperimentFlag.createFlag(
+// AOSP_Comment_Out LiveChannels::enableUnratedContentSettings,
false);
/** Turn analytics on or off based on the System Checkbox for logging. */
public static final ExperimentFlag<Boolean> ENABLE_ANALYTICS_VIA_CHECKBOX =
createFlag(
+// AOSP_Comment_Out LiveChannels::enableAnalyticsViaCheckbox,
false);
/**
@@ -46,6 +45,7 @@ public final class Experiments {
*/
public static final ExperimentFlag<Boolean> ENABLE_DEVELOPER_FEATURES =
ExperimentFlag.createFlag(
+// AOSP_Comment_Out LiveChannels::enableDeveloperFeatures,
BuildConfig.ENG);
/**
@@ -57,6 +57,7 @@ public final class Experiments {
*/
public static final ExperimentFlag<Boolean> ENABLE_QA_FEATURES =
ExperimentFlag.createFlag(
+// AOSP_Comment_Out LiveChannels::enableQaFeatures,
false);
private Experiments() {}
diff --git a/common/src/com/android/tv/common/feature/EngOnlyFeature.java b/common/src/com/android/tv/common/feature/BuildTypeFeature.java
index 5feb5481..9e1704e8 100644
--- a/common/src/com/android/tv/common/feature/EngOnlyFeature.java
+++ b/common/src/com/android/tv/common/feature/BuildTypeFeature.java
@@ -20,18 +20,23 @@ import android.content.Context;
import com.android.tv.common.BuildConfig;
/** A feature that is only available on {@link BuildConfig#ENG} builds. */
-public final class EngOnlyFeature implements Feature {
- public static final Feature ENG_ONLY_FEATURE = new EngOnlyFeature();
+public final class BuildTypeFeature implements Feature {
+ public static final Feature ENG_ONLY_FEATURE = new BuildTypeFeature(BuildConfig.ENG);
+ public static final Feature ASOP_FEATURE = new BuildTypeFeature(BuildConfig.AOSP);
- private EngOnlyFeature() {}
+ private final boolean mIsBuildType;
+
+ private BuildTypeFeature(boolean isBuildType) {
+ mIsBuildType = isBuildType;
+ }
@Override
public boolean isEnabled(Context context) {
- return BuildConfig.ENG;
+ return mIsBuildType;
}
@Override
public String toString() {
- return "EngOnlyFeature(" + BuildConfig.ENG + ")";
+ return getClass().getSimpleName() + "(" + mIsBuildType + ")";
}
}
diff --git a/common/src/com/android/tv/common/feature/CommonFeatures.java b/common/src/com/android/tv/common/feature/CommonFeatures.java
index 1fceabb3..04052a7c 100644
--- a/common/src/com/android/tv/common/feature/CommonFeatures.java
+++ b/common/src/com/android/tv/common/feature/CommonFeatures.java
@@ -16,18 +16,16 @@
package com.android.tv.common.feature;
-import static com.android.tv.common.feature.FeatureUtils.AND;
+import static com.android.tv.common.feature.BuildTypeFeature.ENG_ONLY_FEATURE;
+import static com.android.tv.common.feature.FeatureUtils.and;
+import static com.android.tv.common.feature.FeatureUtils.or;
import static com.android.tv.common.feature.TestableFeature.createTestableFeature;
import android.content.Context;
-import android.os.Build;
-import android.text.TextUtils;
import android.util.Log;
-import com.android.tv.common.config.api.RemoteConfig.HasRemoteConfig;
-import com.android.tv.common.experiments.Experiments;
-
-import com.android.tv.common.util.CommonUtils;
+import com.android.tv.common.flags.has.HasCloudEpgFlags;
import com.android.tv.common.util.LocationUtils;
+import com.android.tv.common.flags.CloudEpgFlags;
/**
* List of {@link Feature} that affect more than just the Live TV app.
@@ -46,7 +44,7 @@ public class CommonFeatures {
* <p>DVR API is introduced in N, it only works when app runs as a system app.
*/
public static final TestableFeature DVR =
- createTestableFeature(AND(Sdk.AT_LEAST_N, SystemAppFeature.SYSTEM_APP_FEATURE));
+ createTestableFeature(and(Sdk.AT_LEAST_N, SystemAppFeature.SYSTEM_APP_FEATURE));
/**
* ENABLE_RECORDING_REGARDLESS_OF_STORAGE_STATUS
@@ -56,44 +54,32 @@ public class CommonFeatures {
public static final Feature FORCE_RECORDING_UNTIL_NO_SPACE =
PropertyFeature.create("force_recording_until_no_space", false);
- public static final Feature TUNER =
- new Feature() {
- @Override
- public boolean isEnabled(Context context) {
-
- if (CommonUtils.isDeveloper()) {
- // we enable tuner for developers to test tuner in any platform.
- return true;
- }
-
- // This is special handling just for USB Tuner.
- // It does not require any N API's but relies on a improvements in N for AC3
- // support
- return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
- }
- };
-
/** Show postal code fragment before channel scan. */
public static final Feature ENABLE_CLOUD_EPG_REGION =
- new Feature() {
- private final String[] supportedRegions = {
- };
+ or(
+ FlagFeature.from(HasCloudEpgFlags::fromContext, CloudEpgFlags::supportedRegion),
+ new Feature() {
+ private final String[] supportedRegions = {
+// AOSP_Comment_Out "US", "GB"
+ };
-
- @Override
- public boolean isEnabled(Context context) {
- if (!Experiments.CLOUD_EPG.get()) {
- if (DEBUG) Log.d(TAG, "Experiments.CLOUD_EPG is false");
- return false;
- }
- String country = LocationUtils.getCurrentCountry(context);
- for (int i = 0; i < supportedRegions.length; i++) {
- if (supportedRegions[i].equalsIgnoreCase(country)) {
- return true;
+ @Override
+ public boolean isEnabled(Context context) {
+ String country = LocationUtils.getCurrentCountry(context);
+ for (int i = 0; i < supportedRegions.length; i++) {
+ if (supportedRegions[i].equalsIgnoreCase(country)) {
+ return true;
+ }
+ }
+ if (DEBUG) Log.d(TAG, "EPG flag false after country check");
+ return false;
}
- }
- if (DEBUG) Log.d(TAG, "EPG flag false after country check");
- return false;
- }
- };
+ });
+
+ // TODO(b/74197177): remove when UI and API finalized.
+ /** Show channel signal strength. */
+ public static final Feature TUNER_SIGNAL_STRENGTH = ENG_ONLY_FEATURE;
+
+ /** Use AudioOnlyTvService for audio-only inputs. */
+ public static final Feature ENABLE_TV_SERVICE = ENG_ONLY_FEATURE;
}
diff --git a/common/src/com/android/tv/common/feature/FeatureUtils.java b/common/src/com/android/tv/common/feature/FeatureUtils.java
index 8650d151..aaed6c82 100644
--- a/common/src/com/android/tv/common/feature/FeatureUtils.java
+++ b/common/src/com/android/tv/common/feature/FeatureUtils.java
@@ -17,6 +17,7 @@
package com.android.tv.common.feature;
import android.content.Context;
+import com.android.tv.common.BuildConfig;
import com.android.tv.common.util.CommonUtils;
import java.util.Arrays;
@@ -28,7 +29,7 @@ public class FeatureUtils {
*
* @param features the features to or
*/
- public static Feature OR(final Feature... features) {
+ public static Feature or(final Feature... features) {
return new Feature() {
@Override
public boolean isEnabled(Context context) {
@@ -52,7 +53,7 @@ public class FeatureUtils {
*
* @param features the features to and
*/
- public static Feature AND(final Feature... features) {
+ public static Feature and(final Feature... features) {
return new Feature() {
@Override
public boolean isEnabled(Context context) {
@@ -70,6 +71,42 @@ public class FeatureUtils {
}
};
}
+ /**
+ * A feature available in AOSP.
+ *
+ * @param googleFeature the feature used in non AOSP builds
+ * @param aospFeature the feature used in AOSP builds
+ */
+ public static Feature aospFeature(
+// AOSP_Comment_Out final Feature googleFeature,
+ final Feature aospFeature) {
+ /* Begin_AOSP_Comment_Out
+ if (!BuildConfig.AOSP) {
+ return googleFeature;
+ } else {
+ End_AOSP_Comment_Out */
+ return aospFeature;
+// AOSP_Comment_Out }
+ }
+
+ /**
+ * Returns a feature that is opposite of the given {@code feature}.
+ *
+ * @param feature the feature to invert
+ */
+ public static Feature not(final Feature feature) {
+ return new Feature() {
+ @Override
+ public boolean isEnabled(Context context) {
+ return !feature.isEnabled(context);
+ }
+
+ @Override
+ public String toString() {
+ return "not(" + feature + ")";
+ }
+ };
+ }
/** A feature that is always enabled. */
public static final Feature ON =
diff --git a/common/src/com/android/tv/common/feature/GServiceFeature.java b/common/src/com/android/tv/common/feature/FlagFeature.java
index 1d7d1156..8da470ef 100644
--- a/common/src/com/android/tv/common/feature/GServiceFeature.java
+++ b/common/src/com/android/tv/common/feature/FlagFeature.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -13,31 +13,34 @@
* See the License for the specific language governing permissions and
* limitations under the License
*/
-
package com.android.tv.common.feature;
import android.content.Context;
+import com.google.common.base.Function;
+
+/** Feature from a Flag */
+public class FlagFeature<T> implements Feature {
-/** A feature controlled by a GServices flag. */
-public class GServiceFeature implements Feature {
- private static final String LIVECHANNELS_PREFIX = "livechannels:";
- private final String mKey;
- private final boolean mDefaultValue;
+ private final Function<Context, T> mToFlag;
+ private final Function<T, Boolean> mToBoolean;
- public GServiceFeature(String key, boolean defaultValue) {
- mKey = LIVECHANNELS_PREFIX + key;
- mDefaultValue = defaultValue;
+ public static <T> FlagFeature<T> from(
+ Function<Context, T> toFlag, Function<T, Boolean> toBoolean) {
+ return new FlagFeature<T>(toFlag, toBoolean);
+ }
+
+ private FlagFeature(Function<Context, T> toFlag, Function<T, Boolean> toBoolean) {
+ mToFlag = toFlag;
+ mToBoolean = toBoolean;
}
@Override
public boolean isEnabled(Context context) {
-
- // GServices is not available outside of Google.
- return mDefaultValue;
+ return mToBoolean.apply(mToFlag.apply(context));
}
@Override
public String toString() {
- return "GService[hash=" + mKey.hashCode() + "]";
+ return mToBoolean.toString();
}
}
diff --git a/common/src/com/android/tv/common/feature/Sdk.java b/common/src/com/android/tv/common/feature/Sdk.java
index 155b391d..4b0a925f 100644
--- a/common/src/com/android/tv/common/feature/Sdk.java
+++ b/common/src/com/android/tv/common/feature/Sdk.java
@@ -17,25 +17,33 @@
package com.android.tv.common.feature;
import android.content.Context;
-import android.os.Build;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
/** Holder for SDK version features */
public final class Sdk {
- public static final Feature AT_LEAST_N =
- new Feature() {
- @Override
- public boolean isEnabled(Context context) {
- return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
- }
- };
-
- public static final Feature AT_LEAST_O =
- new Feature() {
- @Override
- public boolean isEnabled(Context context) {
- return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
- }
- };
+
+ public static final Feature AT_LEAST_M = new AtLeast(VERSION_CODES.M);
+
+ public static final Feature AT_LEAST_N = new AtLeast(VERSION_CODES.N);
+
+ public static final Feature AT_LEAST_O = new AtLeast(VERSION_CODES.O);
+
+ public static final Feature AT_LEAST_P = new AtLeast(VERSION_CODES.P); // AOSP_OC:strip_line
+
+ private static final class AtLeast implements Feature {
+
+ private final int versionCode;
+
+ private AtLeast(int versionCode) {
+ this.versionCode = versionCode;
+ }
+
+ @Override
+ public boolean isEnabled(Context unused) {
+ return VERSION.SDK_INT >= versionCode;
+ }
+ }
private Sdk() {}
}
diff --git a/common/src/com/android/tv/common/flags/BackendKnobsFlags.java b/common/src/com/android/tv/common/flags/BackendKnobsFlags.java
new file mode 100644
index 00000000..69bac7a0
--- /dev/null
+++ b/common/src/com/android/tv/common/flags/BackendKnobsFlags.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 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.common.flags;
+
+/** Flags for tuning non ui behavior */
+public interface BackendKnobsFlags {
+
+ /**
+ * Whether or not this feature is compiled into this build.
+ *
+ * <p>This returns true by default, unless the is_compiled_selector parameter was set during
+ * code generation.
+ */
+ boolean compiled();
+
+ /** Enable fetching only part of the program data. */
+ boolean enablePartialProgramFetch();
+
+ /** EPG fetcher interval in hours */
+ long epgFetcherIntervalHour();
+
+ /** Target channel count for EPG. It is used to adjust the EPG length */
+ long epgTargetChannelCount();
+
+ /** Enables fetching a few hours of programs only when the epg is scrolled to that time. */
+ boolean fetchProgramsAsNeeded();
+
+ /** How many hours of programs are loaded in the program guide for during the initial fetch */
+ long programGuideInitialFetchHours();
+
+ /** How many hours of programs are loaded in the program guide */
+ long programGuideMaxHours();
+}
diff --git a/common/src/com/android/tv/common/flags/CloudEpgFlags.java b/common/src/com/android/tv/common/flags/CloudEpgFlags.java
new file mode 100755
index 00000000..ab4c6a17
--- /dev/null
+++ b/common/src/com/android/tv/common/flags/CloudEpgFlags.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 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.common.flags;
+
+/** Flags for Cloud EPG */
+public interface CloudEpgFlags {
+
+ /**
+ * Whether or not this feature is compiled into this build.
+ *
+ * <p>This returns true by default, unless the is_compiled_selector parameter was set during
+ * code generation.
+ */
+ boolean compiled();
+
+ /** Is the device in a region supported by Cloud Epg */
+ boolean supportedRegion();
+
+ /** List of input ids that Live TV will update their EPG. */
+ String thirdPartyEpgInputsCsv();
+}
diff --git a/common/src/com/android/tv/common/flags/ConcurrentDvrPlaybackFlags.java b/common/src/com/android/tv/common/flags/ConcurrentDvrPlaybackFlags.java
new file mode 100755
index 00000000..1afff793
--- /dev/null
+++ b/common/src/com/android/tv/common/flags/ConcurrentDvrPlaybackFlags.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 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.common.flags;
+
+/** Flags allowing concurrent DVR playback */
+public interface ConcurrentDvrPlaybackFlags {
+
+ /**
+ * Whether or not this feature is compiled into this build.
+ *
+ * <p>This returns true by default, unless the is_compiled_selector parameter was set during
+ * code generation.
+ */
+ boolean compiled();
+
+ /** Enable playback of DVR playback during recording */
+ boolean enabled();
+
+ /** Enable tuner using recording data for playback in onTune */
+ boolean onTuneUsesRecording();
+}
diff --git a/common/src/com/android/tv/common/flags/TunerFlags.java b/common/src/com/android/tv/common/flags/TunerFlags.java
new file mode 100755
index 00000000..5f899b90
--- /dev/null
+++ b/common/src/com/android/tv/common/flags/TunerFlags.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 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.common.flags;
+
+/** Flags for tuner */
+public interface TunerFlags {
+
+ /**
+ * Whether or not this feature is compiled into this build.
+ *
+ * <p>This returns true by default, unless the is_compiled_selector parameter was set during
+ * code generation.
+ */
+ boolean compiled();
+
+ /** Tune using current recording if available. */
+ boolean tuneUsingRecording();
+
+ /** Enable using exoplayer V2 */
+ boolean useExoplayerV2();
+}
diff --git a/common/src/com/android/tv/common/flags/UiFlags.java b/common/src/com/android/tv/common/flags/UiFlags.java
new file mode 100755
index 00000000..4c88d08a
--- /dev/null
+++ b/common/src/com/android/tv/common/flags/UiFlags.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 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.common.flags;
+
+/** Flags for Live TV UI */
+public interface UiFlags {
+
+ /**
+ * Whether or not this feature is compiled into this build.
+ *
+ * <p>This returns true by default, unless the is_compiled_selector parameter was set during
+ * code generation.
+ */
+ boolean compiled();
+
+ /**
+ * Number of days to be shown by Recording History.
+ *
+ * <p>Set to 0 for all recordings.
+ */
+ long maxHistoryDays();
+
+ /** Unhide the launcher all the time */
+ boolean uhideLauncher();
+
+ /** Use the Leanback Pin Picker */
+ boolean useLeanbackPinPicker();
+}
diff --git a/common/src/com/android/tv/common/flags/has/HasCloudEpgFlags.java b/common/src/com/android/tv/common/flags/has/HasCloudEpgFlags.java
new file mode 100644
index 00000000..c33c5528
--- /dev/null
+++ b/common/src/com/android/tv/common/flags/has/HasCloudEpgFlags.java
@@ -0,0 +1,29 @@
+/*
+ * 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.common.flags.has;
+
+import android.content.Context;
+import com.android.tv.common.flags.CloudEpgFlags;
+
+/** Has {@link CloudEpgFlags} */
+public interface HasCloudEpgFlags {
+
+ static CloudEpgFlags fromContext(Context context) {
+ return ((HasCloudEpgFlags) HasUtils.getApplicationContext(context)).getCloudEpgFlags();
+ }
+
+ CloudEpgFlags getCloudEpgFlags();
+}
diff --git a/common/src/com/android/tv/common/flags/has/HasConcurrentDvrPlaybackFlags.java b/common/src/com/android/tv/common/flags/has/HasConcurrentDvrPlaybackFlags.java
new file mode 100644
index 00000000..b4710875
--- /dev/null
+++ b/common/src/com/android/tv/common/flags/has/HasConcurrentDvrPlaybackFlags.java
@@ -0,0 +1,30 @@
+/*
+ * 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.common.flags.has;
+
+import android.content.Context;
+import com.android.tv.common.flags.ConcurrentDvrPlaybackFlags;
+
+/** Has {@link ConcurrentDvrPlaybackFlags} */
+public interface HasConcurrentDvrPlaybackFlags {
+
+ static ConcurrentDvrPlaybackFlags fromContext(Context context) {
+ return ((HasConcurrentDvrPlaybackFlags) HasUtils.getApplicationContext(context))
+ .getConcurrentDvrPlaybackFlags();
+ }
+
+ ConcurrentDvrPlaybackFlags getConcurrentDvrPlaybackFlags();
+}
diff --git a/common/src/com/android/tv/common/flags/has/HasUiFlags.java b/common/src/com/android/tv/common/flags/has/HasUiFlags.java
new file mode 100644
index 00000000..72cc84f2
--- /dev/null
+++ b/common/src/com/android/tv/common/flags/has/HasUiFlags.java
@@ -0,0 +1,24 @@
+/*
+ * 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.common.flags.has;
+
+import com.android.tv.common.flags.UiFlags;
+
+/** Has {@link UiFlags} */
+public interface HasUiFlags {
+
+ UiFlags getUiFlags();
+}
diff --git a/common/src/com/android/tv/common/flags/has/HasUtils.java b/common/src/com/android/tv/common/flags/has/HasUtils.java
new file mode 100644
index 00000000..1c6126dc
--- /dev/null
+++ b/common/src/com/android/tv/common/flags/has/HasUtils.java
@@ -0,0 +1,30 @@
+/*
+ * 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.common.flags.has;
+
+import android.content.Context;
+
+/** Static utilities for Has interfaces. */
+public final class HasUtils {
+
+ /** Returns the application context. */
+ public static Context getApplicationContext(Context context) {
+ Context appContext = context.getApplicationContext();
+ return appContext != null ? appContext : context;
+ }
+
+ private HasUtils() {}
+}
diff --git a/common/src/com/android/tv/common/flags/impl/DefaultBackendKnobsFlags.java b/common/src/com/android/tv/common/flags/impl/DefaultBackendKnobsFlags.java
new file mode 100644
index 00000000..a189e473
--- /dev/null
+++ b/common/src/com/android/tv/common/flags/impl/DefaultBackendKnobsFlags.java
@@ -0,0 +1,56 @@
+/*
+ * 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.common.flags.impl;
+
+/** Flags for tuning non ui behavior. */
+public final class DefaultBackendKnobsFlags
+ implements com.android.tv.common.flags.BackendKnobsFlags {
+
+ @Override
+ public boolean compiled() {
+ return true;
+ }
+
+ @Override
+ public boolean enablePartialProgramFetch() {
+ return false;
+ }
+
+ @Override
+ public long epgFetcherIntervalHour() {
+ return 25;
+ }
+
+ @Override
+ public boolean fetchProgramsAsNeeded() {
+ return false;
+ }
+
+ @Override
+ public long programGuideInitialFetchHours() {
+ return 8;
+ }
+
+ @Override
+ public long programGuideMaxHours() {
+ return 336;
+ }
+
+ @Override
+ public long epgTargetChannelCount() {
+ return 100;
+ }
+}
diff --git a/common/src/com/android/tv/common/flags/impl/DefaultCloudEpgFlags.java b/common/src/com/android/tv/common/flags/impl/DefaultCloudEpgFlags.java
new file mode 100644
index 00000000..34c4fc4b
--- /dev/null
+++ b/common/src/com/android/tv/common/flags/impl/DefaultCloudEpgFlags.java
@@ -0,0 +1,46 @@
+/*
+ * 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.common.flags.impl;
+
+import com.android.tv.common.flags.CloudEpgFlags;
+
+/** Default flags for Cloud EPG */
+public final class DefaultCloudEpgFlags implements CloudEpgFlags {
+
+ private String mThirdPartyEpgInputCsv =
+ "com.google.android.tv/.tuner.tvinput.TunerTvInputService,"
+ + "com.technicolor.skipper.tuner/.tvinput.TunerTvInputService,"
+ + "com.silicondust.view/.tif.SDTvInputService";
+
+ @Override
+ public boolean compiled() {
+ return true;
+ }
+
+ @Override
+ public boolean supportedRegion() {
+ return false;
+ }
+
+ public void setThirdPartyEpgInputCsv(String value) {
+ mThirdPartyEpgInputCsv = value;
+ }
+
+ @Override
+ public String thirdPartyEpgInputsCsv() {
+ return mThirdPartyEpgInputCsv;
+ }
+}
diff --git a/common/src/com/android/tv/common/flags/impl/DefaultConcurrentDvrPlaybackFlags.java b/common/src/com/android/tv/common/flags/impl/DefaultConcurrentDvrPlaybackFlags.java
new file mode 100644
index 00000000..8d8c584a
--- /dev/null
+++ b/common/src/com/android/tv/common/flags/impl/DefaultConcurrentDvrPlaybackFlags.java
@@ -0,0 +1,37 @@
+/*
+ * 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.common.flags.impl;
+
+import com.android.tv.common.flags.ConcurrentDvrPlaybackFlags;
+
+/** Default flags for Concurrent DVR Playback */
+public final class DefaultConcurrentDvrPlaybackFlags implements ConcurrentDvrPlaybackFlags {
+
+ @Override
+ public boolean compiled() {
+ return true;
+ }
+
+ @Override
+ public boolean enabled() {
+ return false;
+ }
+
+ @Override
+ public boolean onTuneUsesRecording() {
+ return false;
+ }
+}
diff --git a/common/src/com/android/tv/common/flags/impl/DefaultFlagsModule.java b/common/src/com/android/tv/common/flags/impl/DefaultFlagsModule.java
new file mode 100644
index 00000000..49352364
--- /dev/null
+++ b/common/src/com/android/tv/common/flags/impl/DefaultFlagsModule.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 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.common.flags.impl;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.Reusable;
+import com.android.tv.common.flags.BackendKnobsFlags;
+import com.android.tv.common.flags.CloudEpgFlags;
+import com.android.tv.common.flags.ConcurrentDvrPlaybackFlags;
+import com.android.tv.common.flags.TunerFlags;
+import com.android.tv.common.flags.UiFlags;
+
+/** Provides default flags. */
+@Module
+public class DefaultFlagsModule {
+
+ @Provides
+ @Reusable
+ BackendKnobsFlags provideBackendKnobsFlags() {
+ return new DefaultBackendKnobsFlags();
+ }
+
+ @Provides
+ @Reusable
+ CloudEpgFlags provideCloudEpgFlags() {
+ return new DefaultCloudEpgFlags();
+ }
+
+ @Provides
+ @Reusable
+ ConcurrentDvrPlaybackFlags provideConcurrentDvrPlaybackFlags() {
+ return new DefaultConcurrentDvrPlaybackFlags();
+ }
+
+ @Provides
+ @Reusable
+ TunerFlags provideTunerFlags() {
+ return new DefaultTunerFlags();
+ }
+
+ @Provides
+ @Reusable
+ UiFlags provideUiFlags() {
+ return new DefaultUiFlags();
+ }
+}
diff --git a/common/src/com/android/tv/common/flags/impl/DefaultTunerFlags.java b/common/src/com/android/tv/common/flags/impl/DefaultTunerFlags.java
new file mode 100644
index 00000000..195953bc
--- /dev/null
+++ b/common/src/com/android/tv/common/flags/impl/DefaultTunerFlags.java
@@ -0,0 +1,37 @@
+/*
+ * 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.common.flags.impl;
+
+import com.android.tv.common.flags.TunerFlags;
+
+/** Default Flags for Tuner */
+public class DefaultTunerFlags implements TunerFlags {
+
+ @Override
+ public boolean compiled() {
+ return true;
+ }
+
+ @Override
+ public boolean tuneUsingRecording() {
+ return false;
+ }
+
+ @Override
+ public boolean useExoplayerV2() {
+ return false;
+ }
+}
diff --git a/common/src/com/android/tv/common/flags/impl/DefaultUiFlags.java b/common/src/com/android/tv/common/flags/impl/DefaultUiFlags.java
new file mode 100644
index 00000000..fce45853
--- /dev/null
+++ b/common/src/com/android/tv/common/flags/impl/DefaultUiFlags.java
@@ -0,0 +1,42 @@
+/*
+ * 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.common.flags.impl;
+
+import com.android.tv.common.flags.UiFlags;
+
+/** Default Flags for Live TV UI */
+public class DefaultUiFlags implements UiFlags {
+
+ @Override
+ public boolean compiled() {
+ return true;
+ }
+
+ @Override
+ public boolean uhideLauncher() {
+ return false;
+ }
+
+ @Override
+ public boolean useLeanbackPinPicker() {
+ return false;
+ }
+
+ @Override
+ public long maxHistoryDays() {
+ return 7;
+ }
+}
diff --git a/common/src/com/android/tv/common/recording/RecordingStorageStatusManager.java b/common/src/com/android/tv/common/recording/RecordingStorageStatusManager.java
index 8b45a730..0fb864bd 100644
--- a/common/src/com/android/tv/common/recording/RecordingStorageStatusManager.java
+++ b/common/src/com/android/tv/common/recording/RecordingStorageStatusManager.java
@@ -217,6 +217,7 @@ public class RecordingStorageStatusManager {
}
} catch (IllegalArgumentException e) {
// In rare cases, storage status change was not notified yet.
+ Log.w(TAG, "Error getting Dvr Storage Status.", e);
SoftPreconditions.checkState(false);
return STORAGE_STATUS_FREE_SPACE_INSUFFICIENT;
}
@@ -246,7 +247,7 @@ public class RecordingStorageStatusManager {
StatFs statFs = new StatFs(storageMountedDir.toString());
storageMountedCapacity = statFs.getTotalBytes();
} catch (IllegalArgumentException e) {
- Log.e(TAG, "Storage mount status was changed.");
+ Log.w(TAG, "Storage mount status was changed.", e);
storageMounted = false;
storageMountedDir = null;
}
diff --git a/common/src/com/android/tv/common/singletons/HasSingletons.java b/common/src/com/android/tv/common/singletons/HasSingletons.java
new file mode 100644
index 00000000..193aed3a
--- /dev/null
+++ b/common/src/com/android/tv/common/singletons/HasSingletons.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 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.common.singletons;
+
+import android.content.Context;
+
+/**
+ * A type that can know about and supply a singleton, typically a type t such as an android activity
+ * or application.
+ */
+public interface HasSingletons<C> {
+
+ @SuppressWarnings("unchecked") // injection
+ static <C> C get(Class<C> clazz, Context context) {
+ return ((HasSingletons<C>) context).singletons();
+ }
+
+ /** Returns the strongly typed singleton. */
+ C singletons();
+}
diff --git a/common/src/com/android/tv/common/singletons/HasTvInputId.java b/common/src/com/android/tv/common/singletons/HasTvInputId.java
new file mode 100644
index 00000000..4bc0a21c
--- /dev/null
+++ b/common/src/com/android/tv/common/singletons/HasTvInputId.java
@@ -0,0 +1,27 @@
+/*
+ * 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.common.singletons;
+
+/**
+ * Has TunerInputId.
+ *
+ * <p>This is used buy both the tuner to get its input id and by the Live TV to get the
+ * embedded tuner input id.
+ */
+public interface HasTvInputId {
+
+ String getEmbeddedTunerInputId();
+}
diff --git a/common/src/com/android/tv/common/support/README.md b/common/src/com/android/tv/common/support/README.md
new file mode 100644
index 00000000..67993f37
--- /dev/null
+++ b/common/src/com/android/tv/common/support/README.md
@@ -0,0 +1,8 @@
+# Support Libraries
+
+Packages here are destined to become support libraries.
+
+Each package should be self contained and only have dependencies on public libraries.
+
+It if becomes clear a package should not or will not be part of a support library move it to a
+different location. \ No newline at end of file
diff --git a/common/src/com/android/tv/common/support/tis/BaseTvInputService.java b/common/src/com/android/tv/common/support/tis/BaseTvInputService.java
new file mode 100644
index 00000000..7791550b
--- /dev/null
+++ b/common/src/com/android/tv/common/support/tis/BaseTvInputService.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 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.common.support.tis;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.tv.TvInputManager;
+import android.media.tv.TvInputService;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import com.android.tv.common.support.tis.TifSession.TifSessionFactory;
+
+/** Abstract TVInputService. */
+public abstract class BaseTvInputService extends TvInputService {
+
+ private static final IntentFilter INTENT_FILTER = new IntentFilter();
+
+ static {
+ INTENT_FILTER.addAction(TvInputManager.ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED);
+ INTENT_FILTER.addAction(TvInputManager.ACTION_BLOCKED_RATINGS_CHANGED);
+ }
+
+ @VisibleForTesting
+ protected final BroadcastReceiver broadcastReceiver =
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ switch (intent.getAction()) {
+ case TvInputManager.ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED:
+ case TvInputManager.ACTION_BLOCKED_RATINGS_CHANGED:
+ for (Session session : getSessionManager().getSessions()) {
+ if (session instanceof WrappedSession) {
+ ((WrappedSession) session).onParentalControlsChanged();
+ }
+ }
+ break;
+ default:
+ // do nothing
+ }
+ }
+ };
+
+ @Nullable
+ @Override
+ public final WrappedSession onCreateSession(String inputId) {
+ SessionManager sessionManager = getSessionManager();
+ if (sessionManager.canCreateNewSession()) {
+ WrappedSession session =
+ new WrappedSession(
+ getApplicationContext(),
+ sessionManager,
+ getTifSessionFactory(),
+ inputId);
+ sessionManager.addSession(session);
+ return session;
+ }
+ return null;
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ registerReceiver(broadcastReceiver, INTENT_FILTER);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ unregisterReceiver(broadcastReceiver);
+ }
+
+ protected abstract TifSessionFactory getTifSessionFactory();
+
+ protected abstract SessionManager getSessionManager();
+}
diff --git a/common/src/com/android/tv/common/support/tis/SessionManager.java b/common/src/com/android/tv/common/support/tis/SessionManager.java
new file mode 100644
index 00000000..5eeebc80
--- /dev/null
+++ b/common/src/com/android/tv/common/support/tis/SessionManager.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 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.common.support.tis;
+
+import android.media.tv.TvInputService.Session;
+import com.google.common.collect.ImmutableSet;
+
+/** Manages the number of concurrent sessions, keeping track of when sessions are released. */
+public interface SessionManager {
+
+ void removeSession(Session session);
+
+ void addSession(Session session);
+
+ boolean canCreateNewSession();
+
+ ImmutableSet<Session> getSessions();
+}
diff --git a/common/src/com/android/tv/common/support/tis/SimpleSessionManager.java b/common/src/com/android/tv/common/support/tis/SimpleSessionManager.java
new file mode 100644
index 00000000..f0636ccc
--- /dev/null
+++ b/common/src/com/android/tv/common/support/tis/SimpleSessionManager.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 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.common.support.tis;
+
+import android.media.tv.TvInputService.Session;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.support.annotation.VisibleForTesting;
+import android.util.ArraySet;
+import com.google.common.collect.ImmutableSet;
+import java.util.HashSet;
+import java.util.Set;
+
+/** A simple session manager that allows a maximum number of concurrent session. */
+public final class SimpleSessionManager implements SessionManager {
+
+ private final Set<Session> sessions;
+ private final int max;
+
+ public SimpleSessionManager(int max) {
+ this.max = max;
+ sessions = VERSION.SDK_INT >= VERSION_CODES.M ? new ArraySet<>() : new HashSet<>();
+ }
+
+ @Override
+ public void removeSession(Session session) {
+ sessions.remove(session);
+ }
+
+ @Override
+ public void addSession(Session session) {
+ sessions.add(session);
+ }
+
+ @Override
+ public boolean canCreateNewSession() {
+ return sessions.size() < max;
+ }
+
+ @Override
+ public ImmutableSet<Session> getSessions() {
+ return ImmutableSet.copyOf(sessions);
+ }
+
+ @VisibleForTesting
+ int getSessionCount() {
+ return sessions.size();
+ }
+}
diff --git a/common/src/com/android/tv/common/support/tis/TifSession.java b/common/src/com/android/tv/common/support/tis/TifSession.java
new file mode 100644
index 00000000..61cfe767
--- /dev/null
+++ b/common/src/com/android/tv/common/support/tis/TifSession.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright 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.common.support.tis;
+
+import android.annotation.TargetApi;
+import android.media.PlaybackParams;
+import android.media.tv.TvContentRating;
+import android.media.tv.TvInputManager;
+import android.media.tv.TvInputService.Session;
+import android.media.tv.TvTrackInfo;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Build.VERSION_CODES;
+import android.support.annotation.FloatRange;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.view.Surface;
+import android.view.View;
+import java.util.List;
+
+/**
+ * Custom {@link android.media.tv.TvInputService.Session} class that uses delegation and a callback
+ * to separate it from the TvInputService for easier testing.
+ */
+public abstract class TifSession {
+
+ private final TifSessionCallbacks callback;
+
+ /**
+ * Creates TV Input Framework Session with the given callback.
+ *
+ * <p>The callback is used to pass notification to the actual {@link
+ * android.media.tv.TvInputService.Session}.
+ *
+ * <p>Pass a mock callback for tests.
+ */
+ protected TifSession(TifSessionCallbacks callback) {
+ this.callback = callback;
+ }
+
+ /**
+ * Called after this session had been created and the callback is attached.
+ *
+ * <p>Do not call notify methods in the constructor, instead call them here if needed at
+ * creation time. eg @{@link Session#notifyTimeShiftStatusChanged(int)}.
+ */
+ public void onSessionCreated() {}
+
+ /** @see Session#onRelease() */
+ public void onRelease() {}
+
+ /** @see Session#onSetSurface(Surface) */
+ public abstract boolean onSetSurface(@Nullable Surface surface);
+
+ /** @see Session#onSurfaceChanged(int, int, int) */
+ public abstract void onSurfaceChanged(int format, int width, int height);
+
+ /** @see Session#onSetStreamVolume(float) */
+ public abstract void onSetStreamVolume(@FloatRange(from = 0.0, to = 1.0) float volume);
+
+ /** @see Session#onTune(Uri) */
+ public abstract boolean onTune(Uri channelUri);
+
+ /** @see Session#onSetCaptionEnabled(boolean) */
+ public abstract void onSetCaptionEnabled(boolean enabled);
+
+ /** @see Session#onUnblockContent(TvContentRating) */
+ public abstract void onUnblockContent(TvContentRating unblockedRating);
+
+ /** @see Session#onTimeShiftGetCurrentPosition() */
+ @TargetApi(Build.VERSION_CODES.M)
+ public long onTimeShiftGetCurrentPosition() {
+ return TvInputManager.TIME_SHIFT_INVALID_TIME;
+ }
+
+ /** @see Session#onTimeShiftGetStartPosition() */
+ @TargetApi(Build.VERSION_CODES.M)
+ public long onTimeShiftGetStartPosition() {
+ return TvInputManager.TIME_SHIFT_INVALID_TIME;
+ }
+
+ /** @see Session#onTimeShiftPause() */
+ @TargetApi(Build.VERSION_CODES.M)
+ public void onTimeShiftPause() {}
+
+ /** @see Session#onTimeShiftResume() */
+ @TargetApi(Build.VERSION_CODES.M)
+ public void onTimeShiftResume() {}
+
+ /** @see Session#onTimeShiftSeekTo(long) */
+ @TargetApi(Build.VERSION_CODES.M)
+ public void onTimeShiftSeekTo(long timeMs) {}
+
+ /** @see Session#onTimeShiftSetPlaybackParams(PlaybackParams) */
+ @TargetApi(Build.VERSION_CODES.M)
+ public void onTimeShiftSetPlaybackParams(PlaybackParams params) {}
+
+ public void onParentalControlsChanged() {}
+
+ /** @see Session#notifyChannelRetuned(Uri) */
+ public final void notifyChannelRetuned(final Uri channelUri) {
+ callback.notifyChannelRetuned(channelUri);
+ }
+
+ /** @see Session#notifyTracksChanged(List) */
+ public final void notifyTracksChanged(final List<TvTrackInfo> tracks) {
+ callback.notifyTracksChanged(tracks);
+ }
+
+ /** @see Session#notifyTrackSelected(int, String) */
+ public final void notifyTrackSelected(final int type, final String trackId) {
+ callback.notifyTrackSelected(type, trackId);
+ }
+
+ /** @see Session#notifyVideoAvailable() */
+ public final void notifyVideoAvailable() {
+ callback.notifyVideoAvailable();
+ }
+
+ /** @see Session#notifyVideoUnavailable(int) */
+ public final void notifyVideoUnavailable(final int reason) {
+ callback.notifyVideoUnavailable(reason);
+ }
+
+ /** @see Session#notifyContentAllowed() */
+ public final void notifyContentAllowed() {
+ callback.notifyContentAllowed();
+ }
+
+ /** @see Session#notifyContentBlocked(TvContentRating) */
+ public final void notifyContentBlocked(@NonNull final TvContentRating rating) {
+ callback.notifyContentBlocked(rating);
+ }
+
+ /** @see Session#notifyTimeShiftStatusChanged(int) */
+ @TargetApi(VERSION_CODES.M)
+ public final void notifyTimeShiftStatusChanged(final int status) {
+ callback.notifyTimeShiftStatusChanged(status);
+ }
+
+ /** @see Session#setOverlayViewEnabled(boolean) */
+ public void setOverlayViewEnabled(boolean enabled) {
+ callback.setOverlayViewEnabled(enabled);
+ }
+
+ /** @see Session#onCreateOverlayView() */
+ public View onCreateOverlayView() {
+ return null;
+ }
+
+ /** @see Session#onOverlayViewSizeChanged(int, int) */
+ public void onOverlayViewSizeChanged(int width, int height) {}
+
+ /**
+ * Callbacks used to notify the {@link android.media.tv.TvInputService.Session}.
+ *
+ * <p>This is implemented internally by {@link WrappedSession}, and can be mocked for tests.
+ */
+ public interface TifSessionCallbacks {
+ /** @see Session#notifyChannelRetuned(Uri) */
+ void notifyChannelRetuned(final Uri channelUri);
+ /** @see Session#notifyTracksChanged(List) */
+ void notifyTracksChanged(final List<TvTrackInfo> tracks);
+ /** @see Session#notifyTrackSelected(int, String) */
+ void notifyTrackSelected(final int type, final String trackId);
+ /** @see Session#notifyVideoAvailable() */
+ void notifyVideoAvailable();
+ /** @see Session#notifyVideoUnavailable(int) */
+ void notifyVideoUnavailable(final int reason);
+ /** @see Session#notifyContentAllowed() */
+ void notifyContentAllowed();
+ /** @see Session#notifyContentBlocked(TvContentRating) */
+ void notifyContentBlocked(@NonNull final TvContentRating rating);
+ /** @see Session#notifyTimeShiftStatusChanged(int) */
+ @TargetApi(VERSION_CODES.M)
+ void notifyTimeShiftStatusChanged(final int status);
+ /** @see Session#setOverlayViewEnabled(boolean) */
+ void setOverlayViewEnabled(boolean enabled);
+ }
+
+ /**
+ * Creates a {@link TifSession}.
+ *
+ * <p>This is used by {@link WrappedSession} to create the desired {@code TifSession}. Should be
+ * used with <a href="http://go/autofactory">go/autofactory</a>.
+ */
+ public interface TifSessionFactory {
+ TifSession create(TifSessionCallbacks callbacks, String inputId);
+ }
+}
diff --git a/common/src/com/android/tv/common/support/tis/WrappedSession.java b/common/src/com/android/tv/common/support/tis/WrappedSession.java
new file mode 100644
index 00000000..f4a71dda
--- /dev/null
+++ b/common/src/com/android/tv/common/support/tis/WrappedSession.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 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.common.support.tis;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.media.PlaybackParams;
+import android.media.tv.TvContentRating;
+import android.media.tv.TvInputService.Session;
+import android.net.Uri;
+import android.os.Build;
+import android.support.annotation.FloatRange;
+import android.support.annotation.Nullable;
+import android.view.Surface;
+import android.view.View;
+import com.android.tv.common.support.tis.TifSession.TifSessionCallbacks;
+import com.android.tv.common.support.tis.TifSession.TifSessionFactory;
+
+/**
+ * Delegates all call to a {@link TifSession} and removes the session from the {@link
+ * SessionManager} when {@link Session#onRelease()} is called.
+ */
+final class WrappedSession extends Session implements TifSessionCallbacks {
+
+ private final SessionManager listener;
+ private final TifSession delegate;
+
+ WrappedSession(
+ Context context,
+ SessionManager sessionManager,
+ TifSessionFactory sessionFactory,
+ String inputId) {
+ super(context);
+ this.listener = sessionManager;
+ this.delegate = sessionFactory.create(this, inputId);
+ }
+
+ @Override
+ public void onRelease() {
+ delegate.onRelease();
+ listener.removeSession(this);
+ }
+
+ @Override
+ public boolean onSetSurface(@Nullable Surface surface) {
+ return delegate.onSetSurface(surface);
+ }
+
+ @Override
+ public void onSurfaceChanged(int format, int width, int height) {
+ delegate.onSurfaceChanged(format, width, height);
+ }
+
+ @Override
+ public void onSetStreamVolume(@FloatRange(from = 0.0, to = 1.0) float volume) {
+ delegate.onSetStreamVolume(volume);
+ }
+
+ @Override
+ public boolean onTune(Uri channelUri) {
+ return delegate.onTune(channelUri);
+ }
+
+ @Override
+ public void onSetCaptionEnabled(boolean enabled) {
+ delegate.onSetCaptionEnabled(enabled);
+ }
+
+ @Override
+ public void onUnblockContent(TvContentRating unblockedRating) {
+ delegate.onUnblockContent(unblockedRating);
+ }
+
+ @Override
+ @TargetApi(Build.VERSION_CODES.M)
+ public long onTimeShiftGetCurrentPosition() {
+ return delegate.onTimeShiftGetCurrentPosition();
+ }
+
+ @Override
+ @TargetApi(Build.VERSION_CODES.M)
+ public long onTimeShiftGetStartPosition() {
+ return delegate.onTimeShiftGetStartPosition();
+ }
+
+ @Override
+ @TargetApi(Build.VERSION_CODES.M)
+ public void onTimeShiftPause() {
+ delegate.onTimeShiftPause();
+ }
+
+ @Override
+ @TargetApi(Build.VERSION_CODES.M)
+ public void onTimeShiftResume() {
+ delegate.onTimeShiftResume();
+ }
+
+ @Override
+ @TargetApi(Build.VERSION_CODES.M)
+ public void onTimeShiftSeekTo(long timeMs) {
+ delegate.onTimeShiftSeekTo(timeMs);
+ }
+
+ @Override
+ @TargetApi(Build.VERSION_CODES.M)
+ public void onTimeShiftSetPlaybackParams(PlaybackParams params) {
+ delegate.onTimeShiftSetPlaybackParams(params);
+ }
+
+ public void onParentalControlsChanged() {
+ delegate.onParentalControlsChanged();
+ }
+
+ @Override
+ @TargetApi(Build.VERSION_CODES.M)
+ public void notifyTimeShiftStatusChanged(int status) {
+ // TODO(nchalko): why is the required for call from TisSession.onSessionCreated to work
+ super.notifyTimeShiftStatusChanged(status);
+ }
+
+ @Override
+ public void setOverlayViewEnabled(boolean enabled) {
+ super.setOverlayViewEnabled(enabled);
+ }
+
+ @Override
+ public View onCreateOverlayView() {
+ return delegate.onCreateOverlayView();
+ }
+
+ @Override
+ public void onOverlayViewSizeChanged(int width, int height) {
+ delegate.onOverlayViewSizeChanged(width, height);
+ }
+}
diff --git a/common/src/com/android/tv/common/ui/setup/SetupActivity.java b/common/src/com/android/tv/common/ui/setup/SetupActivity.java
index 67418ce0..1a3ddbda 100644
--- a/common/src/com/android/tv/common/ui/setup/SetupActivity.java
+++ b/common/src/com/android/tv/common/ui/setup/SetupActivity.java
@@ -16,7 +16,6 @@
package com.android.tv.common.ui.setup;
-import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.os.Bundle;
@@ -27,10 +26,10 @@ import android.support.annotation.NonNull;
import android.transition.Transition;
import android.transition.TransitionInflater;
import android.view.View;
-import android.view.ViewTreeObserver.OnPreDrawListener;
import com.android.tv.common.R;
import com.android.tv.common.WeakHandler;
import com.android.tv.common.ui.setup.animation.SetupAnimationHelper;
+import dagger.android.DaggerActivity;
/**
* Setup activity for onboarding screens or TIS.
@@ -38,7 +37,7 @@ import com.android.tv.common.ui.setup.animation.SetupAnimationHelper;
* <p>The inherited class should add theme {@code Theme.Setup.GuidedStep} to its definition in
* AndroidManifest.xml.
*/
-public abstract class SetupActivity extends Activity implements OnActionClickListener {
+public abstract class SetupActivity extends DaggerActivity implements OnActionClickListener {
private static final int MSG_EXECUTE_ACTION = 1;
private boolean mShowInitialFragment = true;
@@ -55,23 +54,7 @@ public abstract class SetupActivity extends Activity implements OnActionClickLis
// Show initial fragment only when the saved state is not restored, because the last
// fragment is restored if savesInstanceState is not null.
if (savedInstanceState == null) {
- // This is the workaround to show the first fragment with delay to show the fragment
- // enter transition. See http://b/26255145
- getWindow()
- .getDecorView()
- .getViewTreeObserver()
- .addOnPreDrawListener(
- new OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- getWindow()
- .getDecorView()
- .getViewTreeObserver()
- .removeOnPreDrawListener(this);
- showInitialFragment();
- return true;
- }
- });
+ showInitialFragment();
} else {
mShowInitialFragment = false;
}
diff --git a/common/src/com/android/tv/common/util/CommonUtils.java b/common/src/com/android/tv/common/util/CommonUtils.java
index 305431d3..4513a879 100644
--- a/common/src/com/android/tv/common/util/CommonUtils.java
+++ b/common/src/com/android/tv/common/util/CommonUtils.java
@@ -138,14 +138,23 @@ public final class CommonUtils {
return ISO_8601.get().format(new Date(timeMillis));
}
- /** Deletes a file or a directory. */
- public static void deleteDirOrFile(File fileOrDirectory) {
+ /**
+ * Deletes a file or a directory.
+ *
+ * @return <code>true</code> if and only if the file or directory is successfully deleted;
+ * <code>false</code> otherwise
+ */
+ public static boolean deleteDirOrFile(File fileOrDirectory) {
if (fileOrDirectory.isDirectory()) {
- for (File child : fileOrDirectory.listFiles()) {
- deleteDirOrFile(child);
+ File[] files = fileOrDirectory.listFiles();
+ if (files != null) {
+ for (File child : files) {
+ deleteDirOrFile(child);
+ }
}
}
- fileOrDirectory.delete();
+ // If earlier deletes failed this will also
+ return fileOrDirectory.delete();
}
public static boolean isRoboTest() {
diff --git a/common/src/com/android/tv/common/util/LocationUtils.java b/common/src/com/android/tv/common/util/LocationUtils.java
index 53155298..ee5119eb 100644
--- a/common/src/com/android/tv/common/util/LocationUtils.java
+++ b/common/src/com/android/tv/common/util/LocationUtils.java
@@ -34,14 +34,20 @@ import com.android.tv.common.BuildConfig;
import java.io.IOException;
+import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Locale;
+import java.util.Set;
/** A utility class to get the current location. */
public class LocationUtils {
private static final String TAG = "LocationUtils";
private static final boolean DEBUG = false;
+ private static final Set<OnUpdateAddressListener> sOnUpdateAddressListeners =
+ Collections.synchronizedSet(new HashSet<>());
+
private static Context sApplicationContext;
private static Address sAddress;
private static String sCountry;
@@ -63,6 +69,39 @@ public class LocationUtils {
return null;
}
+ /** The listener used when address is updated. */
+ public interface OnUpdateAddressListener {
+ /**
+ * Called when address is updated.
+ *
+ * This listener is removed when this method returns true.
+ *
+ * @return {@code true} if the job has been finished and the listener needs to be removed;
+ * {@code false} otherwise.
+ */
+ boolean onUpdateAddress(Address address);
+ }
+
+ /**
+ * Add an {@link OnUpdateAddressListener} instance.
+ *
+ * Note that the listener is removed automatically when
+ * {@link OnUpdateAddressListener#onUpdateAddress(Address)} is called and returns {@code true}.
+ */
+ public static void addOnUpdateAddressListener(OnUpdateAddressListener listener) {
+ sOnUpdateAddressListeners.add(listener);
+ }
+
+ /**
+ * Remove an {@link OnUpdateAddressListener} instance if it exists.
+ *
+ * Note that the listener will be removed automatically when
+ * {@link OnUpdateAddressListener#onUpdateAddress(Address)} is called and returns {@code true}.
+ */
+ public static void removeOnUpdateAddressListener(OnUpdateAddressListener listener) {
+ sOnUpdateAddressListeners.remove(listener);
+ }
+
/** Returns the current country. */
@NonNull
public static synchronized String getCurrentCountry(Context context) {
@@ -92,6 +131,17 @@ public class LocationUtils {
} catch (Exception e) {
// Do nothing
}
+ Set<OnUpdateAddressListener> listenersToRemove = new HashSet<>();
+ synchronized (sOnUpdateAddressListeners) {
+ for (OnUpdateAddressListener listener : sOnUpdateAddressListeners) {
+ if (listener.onUpdateAddress(sAddress)) {
+ listenersToRemove.add(listener);
+ }
+ }
+ for (OnUpdateAddressListener listener : listenersToRemove) {
+ removeOnUpdateAddressListener(listener);
+ }
+ }
} else {
if (DEBUG) Log.d(TAG, "No address returned");
}
diff --git a/common/src/com/android/tv/common/util/NetworkTrafficTags.java b/common/src/com/android/tv/common/util/NetworkTrafficTags.java
index 91f2bcd1..3c94aed6 100644
--- a/common/src/com/android/tv/common/util/NetworkTrafficTags.java
+++ b/common/src/com/android/tv/common/util/NetworkTrafficTags.java
@@ -43,19 +43,16 @@ public final class NetworkTrafficTags {
@Override
public void execute(final @NonNull Runnable command) {
- // TODO(b/62038127): robolectric does not support lamdas in unbundled apps
- delegateExecutor.execute(
- new Runnable() {
- @Override
- public void run() {
- TrafficStats.setThreadStatsTag(tag);
- try {
- command.run();
- } finally {
- TrafficStats.clearThreadStatsTag();
- }
- }
- });
+ // TODO(b/62038127): robolectric does not support lamdas in unbundled apps
+ delegateExecutor.execute(
+ () -> {
+ TrafficStats.setThreadStatsTag(tag);
+ try {
+ command.run();
+ } finally {
+ TrafficStats.clearThreadStatsTag();
+ }
+ });
}
}
diff --git a/common/src/com/android/tv/common/util/PermissionUtils.java b/common/src/com/android/tv/common/util/PermissionUtils.java
index 8d409e50..ca1abdc4 100644
--- a/common/src/com/android/tv/common/util/PermissionUtils.java
+++ b/common/src/com/android/tv/common/util/PermissionUtils.java
@@ -65,4 +65,9 @@ public class PermissionUtils {
return context.checkSelfPermission("android.permission.INTERNET")
== PackageManager.PERMISSION_GRANTED;
}
+
+ public static boolean hasWriteExternalStorage(Context context) {
+ return context.checkSelfPermission("android.permission.WRITE_EXTERNAL_STORAGE")
+ == PackageManager.PERMISSION_GRANTED;
+ }
}
diff --git a/common/src/com/android/tv/common/util/StringUtils.java b/common/src/com/android/tv/common/util/StringUtils.java
index b9461426..bc826208 100644
--- a/common/src/com/android/tv/common/util/StringUtils.java
+++ b/common/src/com/android/tv/common/util/StringUtils.java
@@ -31,4 +31,9 @@ public final class StringUtils {
}
return a.compareTo(b);
}
+
+ /** Returns {@code s} or {@code ""} if {@code s} is {@code null} */
+ public static final String nullToEmpty(String s) {
+ return s == null ? "" : s;
+ }
}
diff --git a/common/src/com/android/tv/common/util/SystemProperties.java b/common/src/com/android/tv/common/util/SystemProperties.java
index a9f18d4b..6ac2907b 100644
--- a/common/src/com/android/tv/common/util/SystemProperties.java
+++ b/common/src/com/android/tv/common/util/SystemProperties.java
@@ -40,6 +40,10 @@ public final class SystemProperties {
public static final BooleanSystemProperty USE_TRACKER =
new BooleanSystemProperty("tv_use_tracker", true);
+ /** Allow third party inputs. */
+ public static final BooleanSystemProperty ALLOW_THIRD_PARTY_INPUTS =
+ new BooleanSystemProperty("ro.tv_allow_third_party_inputs", true);
+
static {
updateSystemProperties();
}