diff options
author | Szu-An <szuanlu@google.com> | 2023-10-25 12:07:25 +0800 |
---|---|---|
committer | Szu-An <szuanlu@google.com> | 2023-10-27 15:42:59 +0800 |
commit | 9a1816574407747c987788ac003d771086a806c7 (patch) | |
tree | 5961a2bb0ec89c58cf81abbea3ddd4c7478255cf | |
parent | 6274b74f651f2ab57da0e10176973e60a0e02534 (diff) | |
download | TV-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.bp | 1 | ||||
-rw-r--r-- | common/src/com/android/tv/common/feature/ResourceConfigFeature.java | 39 | ||||
-rw-r--r-- | res/drawable/ic_empty_input_hdmi.xml | 10 | ||||
-rw-r--r-- | res/drawable/ic_empty_input_tuner.xml | 10 | ||||
-rw-r--r-- | res/layout/block_screen.xml | 7 | ||||
-rw-r--r-- | res/layout/empty_input_status_block.xml | 60 | ||||
-rw-r--r-- | res/values/colors.xml | 6 | ||||
-rw-r--r-- | res/values/configs.xml | 19 | ||||
-rw-r--r-- | res/values/dimens.xml | 9 | ||||
-rw-r--r-- | res/values/strings.xml | 7 | ||||
-rw-r--r-- | src/com/android/tv/features/TvFeatures.java | 7 | ||||
-rw-r--r-- | src/com/android/tv/ui/BlockScreenView.java | 15 | ||||
-rw-r--r-- | src/com/android/tv/ui/EmptyInputStatusBlockView.java | 68 | ||||
-rw-r--r-- | src/com/android/tv/ui/TunableTvView.java | 12 |
14 files changed, 270 insertions, 0 deletions
@@ -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 |