aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSzu-An <szuanlu@google.com>2023-10-25 12:07:25 +0800
committerSzu-An <szuanlu@google.com>2023-10-27 15:42:59 +0800
commit9a1816574407747c987788ac003d771086a806c7 (patch)
tree5961a2bb0ec89c58cf81abbea3ddd4c7478255cf
parent6274b74f651f2ab57da0e10176973e60a0e02534 (diff)
downloadTV-9a1816574407747c987788ac003d771086a806c7.tar.gz
introduce new empty input status block view
It's a new UX design to notify users that inputs are not connected or weak signal. The new block view is controlled by an overlayable resource config to coexist with existing design. Tuner: https://screenshot.googleplex.com/8xJohZxo2V8nt7i.png Passthrough: https://screenshot.googleplex.com/992cRDCj3A5maw8.png Bug: 302992748 Test: Switch to HDMI inputs without power-on devices plugged-in Test: Switch to tuner input that done channel scan but has no signal Test: Switch to active tuner and HDMI inputs to play Test: Switch (power-on / off devices) between active and empty inputs Change-Id: Iffd8de124b7084053ab7b2eb2946c35b83cb4b1f
-rw-r--r--Android.bp1
-rw-r--r--common/src/com/android/tv/common/feature/ResourceConfigFeature.java39
-rw-r--r--res/drawable/ic_empty_input_hdmi.xml10
-rw-r--r--res/drawable/ic_empty_input_tuner.xml10
-rw-r--r--res/layout/block_screen.xml7
-rw-r--r--res/layout/empty_input_status_block.xml60
-rw-r--r--res/values/colors.xml6
-rw-r--r--res/values/configs.xml19
-rw-r--r--res/values/dimens.xml9
-rw-r--r--res/values/strings.xml7
-rw-r--r--src/com/android/tv/features/TvFeatures.java7
-rw-r--r--src/com/android/tv/ui/BlockScreenView.java15
-rw-r--r--src/com/android/tv/ui/EmptyInputStatusBlockView.java68
-rw-r--r--src/com/android/tv/ui/TunableTvView.java12
14 files changed, 270 insertions, 0 deletions
diff --git a/Android.bp b/Android.bp
index 951d6b0e..44eff1f0 100644
--- a/Android.bp
+++ b/Android.bp
@@ -62,6 +62,7 @@ android_app {
libs: ["tv-guava-android-jar"],
static_libs: [
+ "androidx-constraintlayout_constraintlayout",
"android-support-annotations",
"android-support-compat",
"android-support-v7-recyclerview",
diff --git a/common/src/com/android/tv/common/feature/ResourceConfigFeature.java b/common/src/com/android/tv/common/feature/ResourceConfigFeature.java
new file mode 100644
index 00000000..7519a350
--- /dev/null
+++ b/common/src/com/android/tv/common/feature/ResourceConfigFeature.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 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.feature;
+
+import android.content.Context;
+import android.content.res.Resources;
+
+public class ResourceConfigFeature implements Feature{
+
+ private final int mResId;
+ private final boolean mDefaultValue;
+
+ public ResourceConfigFeature(int resId, boolean defaultValue) {
+ mResId = resId;
+ mDefaultValue = defaultValue;
+ }
+ @Override
+ public boolean isEnabled(Context context) {
+ try {
+ return context.getResources().getBoolean(mResId);
+ } catch (Resources.NotFoundException e) {
+ return mDefaultValue;
+ }
+ }
+}
diff --git a/res/drawable/ic_empty_input_hdmi.xml b/res/drawable/ic_empty_input_hdmi.xml
new file mode 100644
index 00000000..b2150a19
--- /dev/null
+++ b/res/drawable/ic_empty_input_hdmi.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="@color/empty_input_status_icon_tint_color">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M306,896L306,767L184,530L184,275L224,275L224,136Q224,108.05 245.84,86.03Q267.69,64 297,64L663,64Q692.05,64 714.03,86.03Q736,108.05 736,136L736,275L776,275L776,530L654,767L654,896L306,896ZM297,275L395,275L395,194L432,194L432,275L528,275L528,194L565,194L565,275L663,275L663,137Q663,137 663,137Q663,137 663,137L297,137Q297,137 297,137Q297,137 297,137L297,275ZM379,823L581,823L581,749L703,510L703,348L257,348L257,510L379,749L379,823ZM480,510L480,510L480,510L480,510L480,510L480,510L480,510L480,510L480,510Z"/>
+</vector> \ No newline at end of file
diff --git a/res/drawable/ic_empty_input_tuner.xml b/res/drawable/ic_empty_input_tuner.xml
new file mode 100644
index 00000000..9f694e98
--- /dev/null
+++ b/res/drawable/ic_empty_input_tuner.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="@color/empty_input_status_icon_tint_color">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M199,689Q138,630 106.5,552.5Q75,475 75,400Q75,325 105,248.5Q135,172 199,111L242,154Q191,205 163.5,271.5Q136,338 136,400Q136,462 163.5,528.5Q191,595 242,646L199,689ZM289,599Q247,560 227,506.5Q207,453 207,400Q207,351 226.5,296Q246,241 289,201L332,244Q302,274 285,318Q268,362 268,400Q268,434 285.5,479Q303,524 332,556L289,599ZM285,886L420,477Q403,464 393.5,444.5Q384,425 384,400Q384,359 411.5,331Q439,303 480,303Q521,303 549,331Q577,359 577,400Q577,425 567,444.5Q557,464 541,477L675,886L602,886L573,796L388,796L358,886L285,886ZM411,723L549,723L480,512L411,723ZM671,599L628,556Q658,526 675.5,482Q693,438 693,400Q693,366 675,321Q657,276 628,244L671,201Q714,241 734.5,296.5Q755,352 754,400Q754,448 733.5,503.5Q713,559 671,599ZM761,689L718,646Q769,595 797,528.5Q825,462 825,400Q825,338 797,271.5Q769,205 718,154L761,111Q823,171 854.5,248Q886,325 886,400Q886,476 856,552Q826,628 761,689Z"/>
+</vector> \ No newline at end of file
diff --git a/res/layout/block_screen.xml b/res/layout/block_screen.xml
index c564efd0..7edc9839 100644
--- a/res/layout/block_screen.xml
+++ b/res/layout/block_screen.xml
@@ -71,4 +71,11 @@
android:lineSpacingExtra="@dimen/tvview_block_line_spacing_extra"
android:textColor="@color/tvview_block_text_color" />
</LinearLayout>
+
+ <!-- b/302992748 new config-controlled no signal page -->
+ <com.android.tv.ui.EmptyInputStatusBlockView
+ android:id="@+id/empty_input_status_block_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="gone"/>
</com.android.tv.ui.BlockScreenView>
diff --git a/res/layout/empty_input_status_block.xml b/res/layout/empty_input_status_block.xml
new file mode 100644
index 00000000..2f5d8d26
--- /dev/null
+++ b/res/layout/empty_input_status_block.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/block_screen_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/empty_input_status_background_color">
+
+ <ImageView
+ android:id="@+id/empty_input_status_icon"
+ android:layout_width="@dimen/empty_input_status_block_icon_width"
+ android:layout_height="@dimen/empty_input_status_block_icon_height"
+ android:contentDescription="@null"
+ android:src="@drawable/ic_empty_input_hdmi"
+ android:layout_marginTop="@dimen/empty_input_status_block_icon_margin_top"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+ <TextView
+ android:id="@+id/empty_input_status_title_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:fontFamily="google-sans-regular"
+ android:textColor="@color/empty_input_status_title_text_color"
+ android:textSize="@dimen/empty_input_status_block_title_text_size"
+ android:text="@string/empty_input_status_title_format"
+ android:layout_marginTop="@dimen/empty_input_status_block_title_margin_top"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/empty_input_status_icon"/>
+
+ <TextView
+ android:id="@+id/empty_input_status_info_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@color/empty_input_status_info_text_color"
+ android:textSize="@dimen/empty_input_status_block_info_text_size"
+ android:text="@string/empty_input_status_info"
+ android:layout_marginTop="@dimen/empty_input_status_block_info_margin_top"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/empty_input_status_title_text" />
+</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file
diff --git a/res/values/colors.xml b/res/values/colors.xml
index f46d7b9b..e8c75bca 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -111,6 +111,12 @@
<color name="tvview_block_text_color">#80EEEEEE</color>
<color name="tvview_block_image_color_filter">#99000000</color>
+ <!-- Empty input status -->
+ <color name="empty_input_status_background_color">#FF0E0E0F</color>
+ <color name="empty_input_status_icon_tint_color">#FF445664</color>
+ <color name="empty_input_status_title_text_color">#FFE8F0FE</color>
+ <color name="empty_input_status_info_text_color">#99D2E3FC</color>
+
<!-- Channel banner -->
<color name="channel_banner_text_color">#FFEEEEEE</color>
<color name="channel_banner_episode_text_color">#B3EEEEEE</color>
diff --git a/res/values/configs.xml b/res/values/configs.xml
new file mode 100644
index 00000000..15481b03
--- /dev/null
+++ b/res/values/configs.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <bool name="use_gtv_livetv_v2">false</bool>
+</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 9d8941fa..8b1e9e25 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -248,6 +248,15 @@
<dimen name="shrunken_tvview_margin_start">56dp</dimen>
<dimen name="shrunken_tvview_margin_end">32dp</dimen>
+ <!-- Empty input status -->
+ <dimen name="empty_input_status_block_title_text_size">28sp</dimen>
+ <dimen name="empty_input_status_block_info_text_size">16sp</dimen>
+ <dimen name="empty_input_status_block_icon_width">170dp</dimen>
+ <dimen name="empty_input_status_block_icon_height">170dp</dimen>
+ <dimen name="empty_input_status_block_icon_margin_top">100dp</dimen>
+ <dimen name="empty_input_status_block_title_margin_top">44dp</dimen>
+ <dimen name="empty_input_status_block_info_margin_top">16dp</dimen>
+
<!-- Channel banner -->
<dimen name="channel_banner_width">696dp</dimen>
<dimen name="channel_banner_channel_number_large_text_size">54sp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index b36827d1..ba5cb7dd 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -420,6 +420,13 @@
\n\nPress Right to adjust recording schedule.</item>
</plurals>
+ <!-- Empty input status -->
+
+ <!-- The text used when external input source is weak signal or not connected [CHAR LIMIT=NONE] -->
+ <string name="empty_input_status_title_format"><xliff:g id="input_label" example="HDMI 1">%1$s</xliff:g> - No signal</string>
+ <!-- The text used to indicate the action when external input source is weak signal or not connected [CHAR LIMIT=NONE] -->
+ <string name="empty_input_status_info">Check the external input or select another input.</string>
+
<!-- Channel Banner -->
<eat-comment />
<!-- The text used when there is no program title. [CHAR LIMIT=NONE] -->
diff --git a/src/com/android/tv/features/TvFeatures.java b/src/com/android/tv/features/TvFeatures.java
index ebd7cb9a..c4e388a3 100644
--- a/src/com/android/tv/features/TvFeatures.java
+++ b/src/com/android/tv/features/TvFeatures.java
@@ -28,10 +28,13 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
+import androidx.core.R;
+
import com.android.tv.common.feature.CommonFeatures;
import com.android.tv.common.feature.Feature;
import com.android.tv.common.feature.FeatureUtils;
import com.android.tv.common.feature.FlagFeature;
+import com.android.tv.common.feature.ResourceConfigFeature;
import com.android.tv.common.feature.Sdk;
import com.android.tv.common.feature.TestableFeature;
import com.android.tv.common.flags.has.HasUiFlags;
@@ -104,5 +107,9 @@ public final class TvFeatures extends CommonFeatures {
/** Support for interactive applications using the TIAF **/
public static final Feature HAS_TIAF = Sdk.AT_LEAST_T;
+ /** Use new components that are consistent with other Google TV styles b/302992748 */
+ public static final Feature USE_GTV_LIVETV_V2 =
+ new ResourceConfigFeature(R.bool.use_gtv_livetv_v2, false);
+
private TvFeatures() {}
}
diff --git a/src/com/android/tv/ui/BlockScreenView.java b/src/com/android/tv/ui/BlockScreenView.java
index b7a2dd95..9f61bf02 100644
--- a/src/com/android/tv/ui/BlockScreenView.java
+++ b/src/com/android/tv/ui/BlockScreenView.java
@@ -16,12 +16,15 @@
package com.android.tv.ui;
+import static com.android.tv.R.animator.tvview_block_screen_fade_out;
+
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorInflater;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.graphics.drawable.Drawable;
+import android.media.tv.TvInputInfo;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.AttributeSet;
@@ -30,6 +33,7 @@ import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.TextView;
+
import com.android.tv.R;
import com.android.tv.ui.TunableTvView.BlockScreenType;
@@ -42,6 +46,8 @@ public class BlockScreenView extends FrameLayout {
private TextView mBlockingInfoTextView;
private ImageView mBackgroundImageView;
+ private EmptyInputStatusBlockView mEmptyInputStatusBlockView;
+
private final int mSpacingNormal;
private final int mSpacingShrunken;
@@ -79,6 +85,7 @@ public class BlockScreenView extends FrameLayout {
mSpace = findViewById(R.id.space);
mBlockingInfoTextView = (TextView) findViewById(R.id.block_screen_text);
mBackgroundImageView = (ImageView) findViewById(R.id.background_image);
+ mEmptyInputStatusBlockView = findViewById(R.id.empty_input_status_block_view);
mFadeOut =
AnimatorInflater.loadAnimator(
getContext(), R.animator.tvview_block_screen_fade_out);
@@ -261,4 +268,12 @@ public class BlockScreenView extends FrameLayout {
public void setInfoTextClickable(boolean clickable) {
mBlockingInfoTextView.setClickable(clickable);
}
+
+ public void setEmptyInputStatusInputInfo(TvInputInfo inputInfo) {
+ mEmptyInputStatusBlockView.setIconAndLabelByInputInfo(inputInfo);
+ }
+
+ public void setEmptyInputStatusBlockVisibility(boolean visible) {
+ mEmptyInputStatusBlockView.setVisibility(visible ? View.VISIBLE : View.GONE);
+ }
}
diff --git a/src/com/android/tv/ui/EmptyInputStatusBlockView.java b/src/com/android/tv/ui/EmptyInputStatusBlockView.java
new file mode 100644
index 00000000..a7f91661
--- /dev/null
+++ b/src/com/android/tv/ui/EmptyInputStatusBlockView.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2023 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.ui;
+
+import android.content.Context;
+import android.media.tv.TvInputInfo;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.tv.R;
+
+public class EmptyInputStatusBlockView extends FrameLayout {
+ private ImageView mEmptyInputStatusIcon;
+ private TextView mEmptyInputTitleTextView;
+
+ public EmptyInputStatusBlockView(Context context) {
+ this(context, null, 0);
+ }
+
+ public EmptyInputStatusBlockView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public EmptyInputStatusBlockView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ inflate(context, R.layout.empty_input_status_block, this);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mEmptyInputStatusIcon = findViewById(R.id.empty_input_status_icon);
+ mEmptyInputTitleTextView = findViewById(R.id.empty_input_status_title_text);
+ }
+
+ public void setIconAndLabelByInputInfo(TvInputInfo inputInfo) {
+ CharSequence label = inputInfo.loadLabel(getContext());
+ String title = getResources().getString(R.string.empty_input_status_title_format, label);
+ mEmptyInputTitleTextView.setText(title);
+
+ if (inputInfo.isPassthroughInput()) {
+ mEmptyInputStatusIcon.setImageDrawable(
+ getResources().getDrawable(R.drawable.ic_empty_input_hdmi, null)
+ );
+ } else {
+ mEmptyInputStatusIcon.setImageDrawable(
+ getResources().getDrawable(R.drawable.ic_empty_input_tuner, null)
+ );
+ }
+ }
+}
diff --git a/src/com/android/tv/ui/TunableTvView.java b/src/com/android/tv/ui/TunableTvView.java
index 3ac841c2..75f0f8c6 100644
--- a/src/com/android/tv/ui/TunableTvView.java
+++ b/src/com/android/tv/ui/TunableTvView.java
@@ -1017,6 +1017,11 @@ public class TunableTvView extends FrameLayout implements StreamInfo, TunableTvV
return;
}
mBlockScreenView.setVisibility(VISIBLE);
+ if (shouldShowEmptyInputStatusBlock()){
+ // It will fade out along with mBlockScreenView when contents available
+ mBlockScreenView.setEmptyInputStatusInputInfo(mInputInfo);
+ mBlockScreenView.setEmptyInputStatusBlockVisibility(true);
+ }
if (mTvIAppView != null) {
mTvIAppView.setVisibility(INVISIBLE);
}
@@ -1255,6 +1260,13 @@ public class TunableTvView extends FrameLayout implements StreamInfo, TunableTvV
}
}
+ private boolean shouldShowEmptyInputStatusBlock() {
+ return TvFeatures.USE_GTV_LIVETV_V2.isEnabled(getContext()) &&
+ (mVideoUnavailableReason == TvInputManager.VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL ||
+ mVideoUnavailableReason ==
+ CommonConstants.VIDEO_UNAVAILABLE_REASON_NOT_CONNECTED);
+ }
+
private boolean isBundledInput() {
return mInputInfo != null
&& mInputInfo.getType() == TvInputInfo.TYPE_TUNER