diff options
| author | 2021-05-05 17:02:31 +0000 | |
|---|---|---|
| committer | 2021-05-18 15:29:41 +0000 | |
| commit | 11ec9ba02f64f6fde8858aa4363c167302d3e22b (patch) | |
| tree | 7e1579a3ac390e7a77adae70e24f27d464fffb22 | |
| parent | 6fb4729ecf0bac375f8ef5d2260c4efeb0e802a7 (diff) | |
Updates BannerMessagePreference to new style
Updates the style of the BannerMessagePreference in v31 and adds support
for the following new functionality (all optional):
- Use one of three attention level themes: high, medium, and low
(default: high)
- Add an icon (default: alert icon)
- Subtitle (default: hidden)
- Dismiss button (default: hidden)
Icon, subtitle, and theme can also be set via xml attrs.
Adds support for creating a BannerMessage without a title.
Test: make -j80 RunSettingsLibRoboTests
Test: Manually tested all configurations
Bug: 181764215
Change-Id: I73bfc5225d83057e423b6a9d77a6277a656d3b90
14 files changed, 919 insertions, 86 deletions
diff --git a/packages/SettingsLib/BannerMessagePreference/Android.bp b/packages/SettingsLib/BannerMessagePreference/Android.bp index 82e837bcd3ac..c6a95625a414 100644 --- a/packages/SettingsLib/BannerMessagePreference/Android.bp +++ b/packages/SettingsLib/BannerMessagePreference/Android.bp @@ -14,9 +14,10 @@ android_library { resource_dirs: ["res"], static_libs: [ - "androidx.preference_preference", + "androidx.preference_preference", + "SettingsLibSettingsTheme", ], sdk_version: "system_current", - min_sdk_version: "21", + min_sdk_version: "28", } diff --git a/packages/SettingsLib/BannerMessagePreference/AndroidManifest.xml b/packages/SettingsLib/BannerMessagePreference/AndroidManifest.xml index 56b886f7efb1..dd51ea38a1fe 100644 --- a/packages/SettingsLib/BannerMessagePreference/AndroidManifest.xml +++ b/packages/SettingsLib/BannerMessagePreference/AndroidManifest.xml @@ -18,6 +18,6 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.settingslib.widget"> - <uses-sdk android:minSdkVersion="21"/> + <uses-sdk android:minSdkVersion="28"/> </manifest> diff --git a/packages/SettingsLib/BannerMessagePreference/lint-baseline.xml b/packages/SettingsLib/BannerMessagePreference/lint-baseline.xml deleted file mode 100644 index ba02a1fae9e0..000000000000 --- a/packages/SettingsLib/BannerMessagePreference/lint-baseline.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0"> - - <issue - id="NewApi" - message="`@android:style/Widget.DeviceDefault.Button.Borderless.Colored` requires API level 28 (current min is 21)" - errorLine1=" style="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"/>" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SettingsLib/BannerMessagePreference/res/layout/banner_message.xml" - line="65" - column="13"/> - </issue> - - <issue - id="NewApi" - message="`@android:style/Widget.DeviceDefault.Button.Borderless.Colored` requires API level 28 (current min is 21)" - errorLine1=" style="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"/>" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SettingsLib/BannerMessagePreference/res/layout/banner_message.xml" - line="71" - column="13"/> - </issue> - - <issue - id="NewApi" - message="`?android:attr/colorError` requires API level 26 (current min is 21)" - errorLine1=" android:fillColor="?android:attr/colorError"" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/packages/SettingsLib/BannerMessagePreference/res/drawable/ic_warning.xml" - line="24" - column="9"/> - </issue> - -</issues> diff --git a/packages/SettingsLib/BannerMessagePreference/res/drawable-v31/settingslib_card_background.xml b/packages/SettingsLib/BannerMessagePreference/res/drawable-v31/settingslib_card_background.xml new file mode 100644 index 000000000000..072eb5873ce5 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/drawable-v31/settingslib_card_background.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 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. + --> + +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <solid android:color="?android:attr/background" /> + <corners android:radius="28dp"/> +</shape>
\ No newline at end of file diff --git a/packages/SettingsLib/BannerMessagePreference/res/drawable-v31/settingslib_ic_cross.xml b/packages/SettingsLib/BannerMessagePreference/res/drawable-v31/settingslib_ic_cross.xml new file mode 100644 index 000000000000..d926cc6e5a58 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/drawable-v31/settingslib_ic_cross.xml @@ -0,0 +1,25 @@ +<!-- + Copyright (C) 2021 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. + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="?android:attr/textColorSecondary" + android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12 19,6.41z"/> +</vector> diff --git a/packages/SettingsLib/BannerMessagePreference/res/layout-v31/settingslib_banner_message.xml b/packages/SettingsLib/BannerMessagePreference/res/layout-v31/settingslib_banner_message.xml new file mode 100644 index 000000000000..904b78c7f165 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/layout-v31/settingslib_banner_message.xml @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 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. + --> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + style="@style/Banner.Preference.SettingsLib"> + + <RelativeLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingBottom="8dp" + android:orientation="horizontal"> + + <ImageView + android:id="@+id/banner_icon" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_alignParentStart="true" + android:importantForAccessibility="no" /> + + <ImageButton + android:id="@+id/banner_dismiss_btn" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/settingslib_ic_cross" + android:layout_alignParentEnd="true" + android:contentDescription="@string/accessibility_banner_message_dismiss" + style="@style/Banner.Dismiss.SettingsLib" /> + </RelativeLayout> + + <TextView + android:id="@+id/banner_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingBottom="4dp" + android:textAppearance="@style/Banner.Title.SettingsLib"/> + + <TextView + android:id="@+id/banner_subtitle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingBottom="4dp" + android:textAppearance="@style/Banner.Subtitle.SettingsLib" + android:visibility="gone"/> + + <TextView + android:id="@+id/banner_summary" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingTop="4dp" + android:paddingBottom="8dp" + android:textAppearance="@style/Banner.Summary.SettingsLib"/> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:minHeight="8dp" + android:gravity="end"> + + <Button + android:id="@+id/banner_negative_btn" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + style="@style/Banner.ButtonText.SettingsLib"/> + + <Button + android:id="@+id/banner_positive_btn" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + style="@style/Banner.ButtonText.SettingsLib"/> + </LinearLayout> +</LinearLayout>
\ No newline at end of file diff --git a/packages/SettingsLib/BannerMessagePreference/res/layout/banner_message.xml b/packages/SettingsLib/BannerMessagePreference/res/layout/settingslib_banner_message.xml index 977e19687960..80cb1d5f244a 100644 --- a/packages/SettingsLib/BannerMessagePreference/res/layout/banner_message.xml +++ b/packages/SettingsLib/BannerMessagePreference/res/layout/settingslib_banner_message.xml @@ -30,8 +30,10 @@ android:orientation="horizontal"> <ImageView + android:id="@+id/banner_icon" android:layout_width="24dp" android:layout_height="24dp" + android:importantForAccessibility="no" android:src="@drawable/ic_warning"/> <LinearLayout @@ -70,4 +72,19 @@ android:layout_height="wrap_content" style="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"/> </LinearLayout> + + <!-- Not supported before v31 --> + <TextView + android:id="@+id/banner_subtitle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:visibility="gone"/> + + <!-- Not supported before v31 --> + <ImageButton + android:id="@+id/banner_dismiss_btn" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@string/accessibility_banner_message_dismiss" + android:visibility="gone"/> </LinearLayout>
\ No newline at end of file diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-night/colors.xml b/packages/SettingsLib/BannerMessagePreference/res/values-night/colors.xml new file mode 100644 index 000000000000..50141a8b63bd --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-night/colors.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 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> + <color name="banner_background_attention_high">#3B2D2C</color> <!-- red card background --> + <color name="banner_background_attention_medium">#333121</color> <!-- yellow card background --> + <color name="banner_background_attention_low">#2B3328</color> <!-- green card background --> + <color name="banner_accent_attention_high">#F28B82</color> <!-- red accent color --> + <color name="banner_accent_attention_medium">#FDD663</color> <!-- yellow accent color --> + <color name="banner_accent_attention_low">#81C995</color> <!-- green accent color --> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-v31/styles.xml b/packages/SettingsLib/BannerMessagePreference/res/values-v31/styles.xml new file mode 100644 index 000000000000..a39e77969c40 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-v31/styles.xml @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2020 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> + + <style name="Banner.Preference.SettingsLib" + parent="android:Widget.DeviceDefault"> + <item name="android:paddingStart">20dp</item> + <item name="android:paddingEnd">20dp</item> + <item name="android:paddingTop">20dp</item> + <item name="android:paddingBottom">8dp</item> + <item name="android:layout_marginTop">8dp</item> + <item name="android:layout_marginStart">16dp</item> + <item name="android:layout_marginBottom">8dp</item> + <item name="android:layout_marginEnd">16dp</item> + <item name="android:background">@drawable/settingslib_card_background</item> + </style> + + <style name="Banner.Title.SettingsLib" + parent="@android:style/TextAppearance.Material.Subhead"> + <item name="android:textSize">20sp</item> + <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item> + <item name="android:textColor">?android:attr/textColorPrimary</item> + </style> + + <style name="Banner.Subtitle.SettingsLib" + parent="@*android:style/TextAppearance.DeviceDefault.Body1"> + <item name="android:textColor">?android:attr/textColorSecondary</item> + <item name="android:textSize">14sp</item> + </style> + + <style name="Banner.Summary.SettingsLib" + parent="@*android:style/TextAppearance.DeviceDefault.Body1"> + <item name="android:textColor">?android:attr/textColorSecondary</item> + <item name="android:textSize">14sp</item> + </style> + + <style name="Banner.Dismiss.SettingsLib" + parent="android:Widget.DeviceDefault.ImageButton"> + <item name="android:background">@android:color/transparent</item> + </style> + + <style name="Banner.ButtonText.SettingsLib" + parent="android:Widget.DeviceDefault.Button.Borderless.Colored"> + <item name="android:textColor">?android:attr/colorAccent</item> + </style> +</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/BannerMessagePreference/res/values/attrs.xml b/packages/SettingsLib/BannerMessagePreference/res/values/attrs.xml new file mode 100644 index 000000000000..96634a51de56 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values/attrs.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 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> + <declare-styleable name="BannerMessagePreference"> + <attr format="enum" name="attentionLevel"> + <enum name="high" value="0"/> + <enum name="medium" value="1"/> + <enum name="low" value="2"/> + </attr> + <attr format="string" name="subtitle" /> + </declare-styleable> +</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/BannerMessagePreference/res/values/colors.xml b/packages/SettingsLib/BannerMessagePreference/res/values/colors.xml new file mode 100644 index 000000000000..53d72d1eeff7 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values/colors.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 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> + <color name="banner_background_attention_high">#FFDAD5</color> <!-- red card background --> + <color name="banner_background_attention_medium">#F0E3A8</color> <!-- yellow card background --> + <color name="banner_background_attention_low">#CFEBC0</color> <!-- green card background --> + <color name="banner_accent_attention_high">#BB3322</color> <!-- red accent color --> + <color name="banner_accent_attention_medium">#895900</color> <!-- yellow accent color --> + <color name="banner_accent_attention_low">#1D7233</color> <!-- green accent color --> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values/strings.xml new file mode 100644 index 000000000000..a1af38a2bd38 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 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"> + + <!-- Content description of the dismiss button on a banner message for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_banner_message_dismiss">Dismiss</string> +</resources> + + diff --git a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java index 5352552d7c66..5bedd7a18890 100644 --- a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java +++ b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java @@ -17,13 +17,25 @@ package com.android.settingslib.widget; import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.drawable.Drawable; +import android.os.Build; import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; import android.widget.Button; +import android.widget.ImageButton; +import android.widget.ImageView; import android.widget.TextView; +import androidx.annotation.ColorInt; +import androidx.annotation.ColorRes; +import androidx.annotation.RequiresApi; import androidx.annotation.StringRes; +import androidx.core.os.BuildCompat; import androidx.preference.Preference; import androidx.preference.PreferenceViewHolder; @@ -33,55 +45,151 @@ import androidx.preference.PreferenceViewHolder; */ public class BannerMessagePreference extends Preference { + public enum AttentionLevel { + HIGH(0, R.color.banner_background_attention_high, R.color.banner_accent_attention_high), + MEDIUM(1, + R.color.banner_background_attention_medium, + R.color.banner_accent_attention_medium), + LOW(2, R.color.banner_background_attention_low, R.color.banner_accent_attention_low); + + // Corresponds to the enum valye of R.attr.attentionLevel + private final int mAttrValue; + @ColorRes private final int mBackgroundColorResId; + @ColorRes private final int mAccentColorResId; + + AttentionLevel(int attrValue, @ColorRes int backgroundColorResId, + @ColorRes int accentColorResId) { + mAttrValue = attrValue; + mBackgroundColorResId = backgroundColorResId; + mAccentColorResId = accentColorResId; + } + + static AttentionLevel fromAttr(int attrValue) { + for (AttentionLevel level : values()) { + if (level.mAttrValue == attrValue) { + return level; + } + } + throw new IllegalArgumentException(); + } + + @ColorRes int getAccentColorResId() { + return mAccentColorResId; + } + + @ColorRes int getBackgroundColorResId() { + return mBackgroundColorResId; + } + } + private static final String TAG = "BannerPreference"; - private BannerMessagePreference.ButtonInfo mPositiveButtonInfo; - private BannerMessagePreference.ButtonInfo mNegativeButtonInfo; + private static final boolean IS_AT_LEAST_S = BuildCompat.isAtLeastS(); + + private final BannerMessagePreference.ButtonInfo mPositiveButtonInfo = + new BannerMessagePreference.ButtonInfo(); + private final BannerMessagePreference.ButtonInfo mNegativeButtonInfo = + new BannerMessagePreference.ButtonInfo(); + private final BannerMessagePreference.DismissButtonInfo mDismissButtonInfo = + new BannerMessagePreference.DismissButtonInfo(); + + // Default attention level is High. + private AttentionLevel mAttentionLevel = AttentionLevel.HIGH; + private String mSubtitle; public BannerMessagePreference(Context context) { super(context); - init(); + init(context, null /* attrs */); } public BannerMessagePreference(Context context, AttributeSet attrs) { super(context, attrs); - init(); + init(context, attrs); } public BannerMessagePreference(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - init(); + init(context, attrs); } public BannerMessagePreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - init(); + init(context, attrs); + } + + private void init(Context context, AttributeSet attrs) { + setSelectable(false); + setLayoutResource(R.layout.settingslib_banner_message); + + if (IS_AT_LEAST_S) { + if (attrs != null) { + // Get attention level and subtitle from layout XML + TypedArray a = + context.obtainStyledAttributes(attrs, R.styleable.BannerMessagePreference); + int mAttentionLevelValue = + a.getInt(R.styleable.BannerMessagePreference_attentionLevel, 0); + mAttentionLevel = AttentionLevel.fromAttr(mAttentionLevelValue); + mSubtitle = a.getString(R.styleable.BannerMessagePreference_subtitle); + a.recycle(); + } + } } @Override public void onBindViewHolder(PreferenceViewHolder holder) { super.onBindViewHolder(holder); - holder.setDividerAllowedAbove(true); - holder.setDividerAllowedBelow(true); + final Context context = getContext(); + + final TextView titleView = (TextView) holder.findViewById(R.id.banner_title); + CharSequence title = getTitle(); + titleView.setText(title); + titleView.setVisibility(title == null ? View.GONE : View.VISIBLE); + + final TextView summaryView = (TextView) holder.findViewById(R.id.banner_summary); + summaryView.setText(getSummary()); mPositiveButtonInfo.mButton = (Button) holder.findViewById(R.id.banner_positive_btn); mNegativeButtonInfo.mButton = (Button) holder.findViewById(R.id.banner_negative_btn); - mPositiveButtonInfo.setUpButton(); - mNegativeButtonInfo.setUpButton(); + if (IS_AT_LEAST_S) { + final Resources.Theme theme = context.getTheme(); + @ColorInt final int accentColor = + context.getResources().getColor(mAttentionLevel.getAccentColorResId(), theme); + @ColorInt final int backgroundColor = + context.getResources().getColor( + mAttentionLevel.getBackgroundColorResId(), theme); - final TextView titleView = (TextView) holder.findViewById(R.id.banner_title); - final TextView summaryView = (TextView) holder.findViewById(R.id.banner_summary); + holder.setDividerAllowedAbove(false); + holder.setDividerAllowedBelow(false); + holder.itemView.getBackground().setTint(backgroundColor); - titleView.setText(getTitle()); - summaryView.setText(getSummary()); - } + mPositiveButtonInfo.mColor = accentColor; + mNegativeButtonInfo.mColor = accentColor; - private void init() { - mPositiveButtonInfo = new BannerMessagePreference.ButtonInfo(); - mNegativeButtonInfo = new BannerMessagePreference.ButtonInfo(); - setSelectable(false); - setLayoutResource(R.layout.banner_message); + mDismissButtonInfo.mButton = (ImageButton) holder.findViewById(R.id.banner_dismiss_btn); + mDismissButtonInfo.setUpButton(); + + final TextView subtitleView = (TextView) holder.findViewById(R.id.banner_subtitle); + subtitleView.setText(mSubtitle); + subtitleView.setVisibility(mSubtitle == null ? View.GONE : View.VISIBLE); + + final ImageView iconView = (ImageView) holder.findViewById(R.id.banner_icon); + if (iconView != null) { + Drawable icon = getIcon(); + iconView.setImageDrawable( + icon == null + ? getContext().getDrawable(R.drawable.ic_warning) + : icon); + iconView.setColorFilter( + new PorterDuffColorFilter(accentColor, PorterDuff.Mode.SRC_IN)); + } + } else { + holder.setDividerAllowedAbove(true); + holder.setDividerAllowedBelow(true); + } + + mPositiveButtonInfo.setUpButton(); + mNegativeButtonInfo.setUpButton(); } /** @@ -107,6 +215,18 @@ public class BannerMessagePreference extends Preference { } /** + * Set the visibility state of dismiss button. + */ + @RequiresApi(Build.VERSION_CODES.S) + public BannerMessagePreference setDismissButtonVisible(boolean isVisible) { + if (isVisible != mDismissButtonInfo.mIsVisible) { + mDismissButtonInfo.mIsVisible = isVisible; + notifyChanged(); + } + return this; + } + + /** * Register a callback to be invoked when positive button is clicked. */ public BannerMessagePreference setPositiveButtonOnClickListener( @@ -131,12 +251,31 @@ public class BannerMessagePreference extends Preference { } /** + * Register a callback to be invoked when the dismiss button is clicked. + */ + @RequiresApi(Build.VERSION_CODES.S) + public BannerMessagePreference setDismissButtonOnClickListener( + View.OnClickListener listener) { + if (listener != mDismissButtonInfo.mListener) { + mDismissButtonInfo.mListener = listener; + notifyChanged(); + } + return this; + } + + /** * Sets the text to be displayed in positive button. */ public BannerMessagePreference setPositiveButtonText(@StringRes int textResId) { - final String newText = getContext().getString(textResId); - if (!TextUtils.equals(newText, mPositiveButtonInfo.mText)) { - mPositiveButtonInfo.mText = newText; + return setPositiveButtonText(getContext().getString(textResId)); + } + + /** + * Sets the text to be displayed in positive button. + */ + public BannerMessagePreference setPositiveButtonText(String positiveButtonText) { + if (!TextUtils.equals(positiveButtonText, mPositiveButtonInfo.mText)) { + mPositiveButtonInfo.mText = positiveButtonText; notifyChanged(); } return this; @@ -146,9 +285,51 @@ public class BannerMessagePreference extends Preference { * Sets the text to be displayed in negative button. */ public BannerMessagePreference setNegativeButtonText(@StringRes int textResId) { - final String newText = getContext().getString(textResId); - if (!TextUtils.equals(newText, mNegativeButtonInfo.mText)) { - mNegativeButtonInfo.mText = newText; + return setNegativeButtonText(getContext().getString(textResId)); + } + + /** + * Sets the text to be displayed in negative button. + */ + public BannerMessagePreference setNegativeButtonText(String negativeButtonText) { + if (!TextUtils.equals(negativeButtonText, mNegativeButtonInfo.mText)) { + mNegativeButtonInfo.mText = negativeButtonText; + notifyChanged(); + } + return this; + } + + /** + * Sets the subtitle. + */ + @RequiresApi(Build.VERSION_CODES.S) + public BannerMessagePreference setSubtitle(@StringRes int textResId) { + return setSubtitle(getContext().getString(textResId)); + } + + /** + * Sets the subtitle. + */ + @RequiresApi(Build.VERSION_CODES.S) + public BannerMessagePreference setSubtitle(String subtitle) { + if (!TextUtils.equals(subtitle, mSubtitle)) { + mSubtitle = subtitle; + notifyChanged(); + } + return this; + } + + /** + * Sets the attention level. This will update the color theme of the preference. + */ + @RequiresApi(Build.VERSION_CODES.S) + public BannerMessagePreference setAttentionLevel(AttentionLevel attentionLevel) { + if (attentionLevel == mAttentionLevel) { + return this; + } + + if (attentionLevel != null) { + mAttentionLevel = attentionLevel; notifyChanged(); } return this; @@ -159,11 +340,16 @@ public class BannerMessagePreference extends Preference { private CharSequence mText; private View.OnClickListener mListener; private boolean mIsVisible = true; + @ColorInt private int mColor; void setUpButton() { mButton.setText(mText); mButton.setOnClickListener(mListener); + if (IS_AT_LEAST_S) { + mButton.setTextColor(mColor); + } + if (shouldBeVisible()) { mButton.setVisibility(View.VISIBLE); } else { @@ -179,4 +365,26 @@ public class BannerMessagePreference extends Preference { return mIsVisible && (!TextUtils.isEmpty(mText)); } } + + static class DismissButtonInfo { + private ImageButton mButton; + private View.OnClickListener mListener; + private boolean mIsVisible = true; + + void setUpButton() { + mButton.setOnClickListener(mListener); + if (shouldBeVisible()) { + mButton.setVisibility(View.VISIBLE); + } else { + mButton.setVisibility(View.GONE); + } + } + + /** + * By default, dismiss button is visible if it has a click listener. + */ + private boolean shouldBeVisible() { + return mIsVisible && (mListener != null); + } + } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java index 8ab02983bfc2..6670ed307816 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java @@ -18,18 +18,35 @@ package com.android.settingslib.widget; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + import android.content.Context; +import android.graphics.ColorFilter; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.util.AttributeSet; import android.view.View; import android.widget.Button; +import android.widget.ImageButton; +import android.widget.ImageView; import android.widget.TextView; +import androidx.annotation.ColorRes; import androidx.preference.PreferenceViewHolder; +import androidx.preference.R; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.Shadows; +import org.robolectric.shadows.ShadowDrawable; +import org.robolectric.util.ReflectionHelpers; @RunWith(RobolectricTestRunner.class) public class BannerMessagePreferenceTest { @@ -39,16 +56,22 @@ public class BannerMessagePreferenceTest { private BannerMessagePreference mBannerPreference; private PreferenceViewHolder mHolder; + private boolean mClickListenerCalled = false; + private final View.OnClickListener mClickListener = v -> mClickListenerCalled = true; + + private static final int TEST_STRING_RES_ID = + R.string.accessibility_banner_message_dismiss; + @Before public void setUp() { mContext = RuntimeEnvironment.application; - mRootView = View.inflate(mContext, R.layout.banner_message, null /* parent */); - mHolder = PreferenceViewHolder.createInstanceForTests(mRootView); + mClickListenerCalled = false; mBannerPreference = new BannerMessagePreference(mContext); + setUpViewHolder(); } @Test - public void onBindViewHolder_shouldSetTitle() { + public void onBindViewHolder_whenTitleSet_shouldSetTitle() { mBannerPreference.setTitle("test"); mBannerPreference.onBindViewHolder(mHolder); @@ -58,7 +81,7 @@ public class BannerMessagePreferenceTest { } @Test - public void onBindViewHolder_shouldSetSummary() { + public void onBindViewHolder_whenSummarySet_shouldSetSummary() { mBannerPreference.setSummary("test"); mBannerPreference.onBindViewHolder(mHolder); @@ -68,30 +91,114 @@ public class BannerMessagePreferenceTest { } @Test + public void onBindViewHolder_whenPreS_shouldBindView() { + assumeAndroidR(); + mBannerPreference.setSummary("test"); + + mBannerPreference.onBindViewHolder(mHolder); + + assertThat(((TextView) mRootView.findViewById(R.id.banner_summary)).getText()) + .isEqualTo("test"); + } + + @Test + public void onBindViewHolder_whenAtLeastS_whenSubtitleSetByString_shouldSetSubtitle() { + assumeAndroidS(); + mBannerPreference.setSubtitle("test"); + + mBannerPreference.onBindViewHolder(mHolder); + + TextView mSubtitleView = mRootView.findViewById(R.id.banner_subtitle); + assertThat(mSubtitleView.getText()).isEqualTo("test"); + assertThat(mSubtitleView.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void onBindViewHolder_whenAtLeastS_whenSubtitleSetByResId_shouldSetSubtitle() { + assumeAndroidS(); + mBannerPreference.setSubtitle(TEST_STRING_RES_ID); + + mBannerPreference.onBindViewHolder(mHolder); + + TextView mSubtitleView = mRootView.findViewById(R.id.banner_subtitle); + assertThat(mSubtitleView.getText()).isEqualTo(mContext.getString(TEST_STRING_RES_ID)); + assertThat(mSubtitleView.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void onBindViewHolder_whenAtLeastS_whenSubtitleXmlAttribute_shouldSetSubtitle() { + assumeAndroidS(); + AttributeSet mAttributeSet = Robolectric.buildAttributeSet() + .addAttribute(R.attr.subtitle, "Test") + .build(); + mBannerPreference = new BannerMessagePreference(mContext, mAttributeSet); + + mBannerPreference.onBindViewHolder(mHolder); + + TextView mSubtitleView = mRootView.findViewById(R.id.banner_subtitle); + assertThat(mSubtitleView.getText()).isEqualTo("Test"); + assertThat(mSubtitleView.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void onBindViewHolder_whenAtLeastS_shouldNotShowSubtitleIfUnset() { + assumeAndroidS(); + mBannerPreference.onBindViewHolder(mHolder); + + TextView mSubtitleView = mRootView.findViewById(R.id.banner_subtitle); + assertThat(mSubtitleView.getVisibility()).isEqualTo(View.GONE); + } + + @Test + public void onBindViewHolder_whenAtLeastS_whenIconSet_shouldSetIcon() { + assumeAndroidS(); + mBannerPreference.setIcon(R.drawable.settingslib_ic_cross); + + mBannerPreference.onBindViewHolder(mHolder); + + ImageView mIcon = mRootView.findViewById(R.id.banner_icon); + ShadowDrawable shadowDrawable = Shadows.shadowOf(mIcon.getDrawable()); + assertThat(shadowDrawable.getCreatedFromResId()) + .isEqualTo(R.drawable.settingslib_ic_cross); + } + + @Test + public void onBindViewHolder_whenAtLeastS_whenNoIconSet_shouldSetIconToDefault() { + assumeAndroidS(); + mBannerPreference.onBindViewHolder(mHolder); + + ImageView mIcon = mRootView.findViewById(R.id.banner_icon); + ShadowDrawable shadowDrawable = Shadows.shadowOf(mIcon.getDrawable()); + assertThat(shadowDrawable.getCreatedFromResId()).isEqualTo(R.drawable.ic_warning); + } + + @Test public void setPositiveButtonText_shouldShowPositiveButton() { - mBannerPreference.setPositiveButtonText(R.string.tts_settings_title); + mBannerPreference.setPositiveButtonText(TEST_STRING_RES_ID); mBannerPreference.onBindViewHolder(mHolder); - assertThat(((Button) mRootView.findViewById(R.id.banner_positive_btn)).getVisibility()) - .isEqualTo(View.VISIBLE); + Button mPositiveButton = mRootView.findViewById(R.id.banner_positive_btn); + assertThat(mPositiveButton.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mPositiveButton.getText()).isEqualTo(mContext.getString(TEST_STRING_RES_ID)); } @Test public void setNegativeButtonText_shouldShowNegativeButton() { - mBannerPreference.setNegativeButtonText(R.string.tts_settings_title); + mBannerPreference.setNegativeButtonText(TEST_STRING_RES_ID); mBannerPreference.onBindViewHolder(mHolder); - assertThat(((Button) mRootView.findViewById(R.id.banner_negative_btn)).getVisibility()) - .isEqualTo(View.VISIBLE); + Button mNegativeButton = mRootView.findViewById(R.id.banner_negative_btn); + assertThat(mNegativeButton.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mNegativeButton.getText()).isEqualTo(mContext.getString(TEST_STRING_RES_ID)); } @Test public void withoutSetPositiveButtonText_shouldHidePositiveButton() { mBannerPreference.onBindViewHolder(mHolder); - assertThat(((Button) mRootView.findViewById(R.id.banner_positive_btn)).getVisibility()) + assertThat(mRootView.findViewById(R.id.banner_positive_btn).getVisibility()) .isEqualTo(View.GONE); } @@ -99,51 +206,290 @@ public class BannerMessagePreferenceTest { public void withoutSetNegativeButtonText_shouldHideNegativeButton() { mBannerPreference.onBindViewHolder(mHolder); - assertThat(((Button) mRootView.findViewById(R.id.banner_negative_btn)).getVisibility()) + assertThat(mRootView.findViewById(R.id.banner_negative_btn).getVisibility()) .isEqualTo(View.GONE); } @Test public void setPositiveButtonVisible_withTrue_shouldShowPositiveButton() { - mBannerPreference.setPositiveButtonText(R.string.tts_settings_title); + mBannerPreference.setPositiveButtonText(TEST_STRING_RES_ID); mBannerPreference.setPositiveButtonVisible(true); mBannerPreference.onBindViewHolder(mHolder); - assertThat(((Button) mRootView.findViewById(R.id.banner_positive_btn)).getVisibility()) + assertThat((mRootView.findViewById(R.id.banner_positive_btn)).getVisibility()) .isEqualTo(View.VISIBLE); } @Test public void setPositiveButtonVisible_withFalse_shouldHidePositiveButton() { - mBannerPreference.setPositiveButtonText(R.string.tts_settings_title); + mBannerPreference.setPositiveButtonText(TEST_STRING_RES_ID); mBannerPreference.setPositiveButtonVisible(false); mBannerPreference.onBindViewHolder(mHolder); - assertThat(((Button) mRootView.findViewById(R.id.banner_positive_btn)).getVisibility()) + assertThat(mRootView.findViewById(R.id.banner_positive_btn).getVisibility()) .isEqualTo(View.GONE); } @Test public void setNegativeButtonVisible_withTrue_shouldShowNegativeButton() { - mBannerPreference.setNegativeButtonText(R.string.tts_settings_title); + mBannerPreference.setNegativeButtonText(TEST_STRING_RES_ID); mBannerPreference.setNegativeButtonVisible(true); mBannerPreference.onBindViewHolder(mHolder); - assertThat(((Button) mRootView.findViewById(R.id.banner_negative_btn)).getVisibility()) + assertThat(mRootView.findViewById(R.id.banner_negative_btn).getVisibility()) .isEqualTo(View.VISIBLE); } @Test public void setNegativeButtonVisible_withFalse_shouldHideNegativeButton() { - mBannerPreference.setNegativeButtonText(R.string.tts_settings_title); + mBannerPreference.setNegativeButtonText(TEST_STRING_RES_ID); mBannerPreference.setNegativeButtonVisible(false); mBannerPreference.onBindViewHolder(mHolder); - assertThat(((Button) mRootView.findViewById(R.id.banner_negative_btn)).getVisibility()) + assertThat(mRootView.findViewById(R.id.banner_negative_btn).getVisibility()) .isEqualTo(View.GONE); } + + @Test + public void setPositiveButtonOnClickListener_setsClickListener() { + mBannerPreference.setPositiveButtonOnClickListener(mClickListener); + + mBannerPreference.onBindViewHolder(mHolder); + mRootView.findViewById(R.id.banner_positive_btn).callOnClick(); + + assertThat(mClickListenerCalled).isTrue(); + } + + @Test + public void setNegativeButtonOnClickListener_setsClickListener() { + mBannerPreference.setNegativeButtonOnClickListener(mClickListener); + + mBannerPreference.onBindViewHolder(mHolder); + mRootView.findViewById(R.id.banner_negative_btn).callOnClick(); + + assertThat(mClickListenerCalled).isTrue(); + } + + @Test + public void setDismissButtonOnClickListener_whenAtLeastS_setsClickListener() { + assumeAndroidS(); + mBannerPreference.setDismissButtonOnClickListener(mClickListener); + + mBannerPreference.onBindViewHolder(mHolder); + mRootView.findViewById(R.id.banner_dismiss_btn).callOnClick(); + + assertThat(mClickListenerCalled).isTrue(); + } + + @Test + public void onBindViewHolder_whenAtLeastS_withDismissOnClickListener_dismissIsVisible() { + assumeAndroidS(); + mBannerPreference.setDismissButtonOnClickListener(mClickListener); + + mBannerPreference.onBindViewHolder(mHolder); + + assertThat((mRootView.findViewById(R.id.banner_dismiss_btn)).getVisibility()) + .isEqualTo(View.VISIBLE); + } + + @Test + public void onBindViewHolder_whenAtLeastS_withNoDismissClickListener_dimissButtonIsGone() { + assumeAndroidS(); + + mBannerPreference.onBindViewHolder(mHolder); + + assertThat((mRootView.findViewById(R.id.banner_dismiss_btn)).getVisibility()) + .isEqualTo(View.GONE); + } + + @Test + public void onBindViewHolder_whenAtLeastS_withNoClickListenerAndVisible_dimissButtonIsGone() { + assumeAndroidS(); + mBannerPreference.setDismissButtonVisible(true); + + mBannerPreference.onBindViewHolder(mHolder); + + assertThat(mRootView.findViewById(R.id.banner_dismiss_btn).getVisibility()) + .isEqualTo(View.GONE); + } + + @Test + public void onBindViewHolder_whenAtLeastS_withClickListenerAndNotVisible_dimissButtonIsGone() { + assumeAndroidS(); + mBannerPreference.setDismissButtonOnClickListener(mClickListener); + mBannerPreference.setDismissButtonVisible(false); + + mBannerPreference.onBindViewHolder(mHolder); + + ImageButton mDismissButton = mRootView.findViewById(R.id.banner_dismiss_btn); + assertThat(mDismissButton.getVisibility()).isEqualTo(View.GONE); + } + + @Test + public void onBindViewHolder_whenAtLeastS_whenAttentionUnset_setsHighTheme() { + assumeAndroidS(); + Drawable mCardBackgroundSpy = spy(mRootView.getBackground()); + mRootView.setBackground(mCardBackgroundSpy); + + mBannerPreference.onBindViewHolder(mHolder); + + assertThat(((ImageView) mHolder.findViewById(R.id.banner_icon)).getColorFilter()) + .isEqualTo(getColorFilter(R.color.banner_accent_attention_high)); + assertThat(getButtonColor(R.id.banner_positive_btn)) + .isEqualTo(getColorId(R.color.banner_accent_attention_high)); + assertThat(getButtonColor(R.id.banner_negative_btn)) + .isEqualTo(getColorId(R.color.banner_accent_attention_high)); + verify(mCardBackgroundSpy).setTint(getColorId(R.color.banner_background_attention_high)); + } + + @Test + public void onBindViewHolder_whenAtLeastS_whenAttentionHighByXML_setsHighTheme() { + assumeAndroidS(); + Drawable mCardBackgroundSpy = spy(mRootView.getBackground()); + mRootView.setBackground(mCardBackgroundSpy); + AttributeSet mAttributeSet = Robolectric.buildAttributeSet() + .addAttribute(R.attr.attentionLevel, "high") + .build(); + mBannerPreference = new BannerMessagePreference(mContext, mAttributeSet); + + mBannerPreference.onBindViewHolder(mHolder); + + assertThat(((ImageView) mHolder.findViewById(R.id.banner_icon)).getColorFilter()) + .isEqualTo(getColorFilter(R.color.banner_accent_attention_high)); + assertThat(getButtonColor(R.id.banner_positive_btn)) + .isEqualTo(getColorId(R.color.banner_accent_attention_high)); + assertThat(getButtonColor(R.id.banner_negative_btn)) + .isEqualTo(getColorId(R.color.banner_accent_attention_high)); + verify(mCardBackgroundSpy).setTint(getColorId(R.color.banner_background_attention_high)); + } + + @Test + public void onBindViewHolder_whenAtLeastS_whenAttentionMediumByXML_setsMediumTheme() { + assumeAndroidS(); + Drawable mCardBackgroundSpy = spy(mRootView.getBackground()); + mRootView.setBackground(mCardBackgroundSpy); + AttributeSet mAttributeSet = Robolectric.buildAttributeSet() + .addAttribute(R.attr.attentionLevel, "medium") + .build(); + mBannerPreference = new BannerMessagePreference(mContext, mAttributeSet); + + mBannerPreference.onBindViewHolder(mHolder); + + assertThat(((ImageView) mHolder.findViewById(R.id.banner_icon)).getColorFilter()) + .isEqualTo(getColorFilter(R.color.banner_accent_attention_medium)); + assertThat(getButtonColor(R.id.banner_positive_btn)) + .isEqualTo(getColorId(R.color.banner_accent_attention_medium)); + assertThat(getButtonColor(R.id.banner_negative_btn)) + .isEqualTo(getColorId(R.color.banner_accent_attention_medium)); + verify(mCardBackgroundSpy).setTint(getColorId(R.color.banner_background_attention_medium)); + } + + @Test + public void onBindViewHolder_whenAtLeastS_whenAttentionLowByXML_setsLowTheme() { + assumeAndroidS(); + Drawable mCardBackgroundSpy = spy(mRootView.getBackground()); + mRootView.setBackground(mCardBackgroundSpy); + AttributeSet mAttributeSet = Robolectric.buildAttributeSet() + .addAttribute(R.attr.attentionLevel, "low") + .build(); + mBannerPreference = new BannerMessagePreference(mContext, mAttributeSet); + + mBannerPreference.onBindViewHolder(mHolder); + + assertThat(((ImageView) mHolder.findViewById(R.id.banner_icon)).getColorFilter()) + .isEqualTo(getColorFilter(R.color.banner_accent_attention_low)); + assertThat(getButtonColor(R.id.banner_positive_btn)) + .isEqualTo(getColorId(R.color.banner_accent_attention_low)); + assertThat(getButtonColor(R.id.banner_negative_btn)) + .isEqualTo(getColorId(R.color.banner_accent_attention_low)); + verify(mCardBackgroundSpy).setTint(getColorId(R.color.banner_background_attention_low)); + } + + @Test + public void setAttentionLevel_whenAtLeastS_whenHighAttention_setsHighTheme() { + assumeAndroidS(); + Drawable mCardBackgroundSpy = spy(mRootView.getBackground()); + mRootView.setBackground(mCardBackgroundSpy); + mBannerPreference.setAttentionLevel(BannerMessagePreference.AttentionLevel.HIGH); + + mBannerPreference.onBindViewHolder(mHolder); + + assertThat(((ImageView) mHolder.findViewById(R.id.banner_icon)).getColorFilter()) + .isEqualTo(getColorFilter(R.color.banner_accent_attention_high)); + assertThat(getButtonColor(R.id.banner_positive_btn)) + .isEqualTo(getColorId(R.color.banner_accent_attention_high)); + assertThat(getButtonColor(R.id.banner_negative_btn)) + .isEqualTo(getColorId(R.color.banner_accent_attention_high)); + verify(mCardBackgroundSpy).setTint(getColorId(R.color.banner_background_attention_high)); + } + + @Test + public void setAttentionLevel_whenAtLeastS_whenMedAttention_setsMediumTheme() { + assumeAndroidS(); + Drawable mCardBackgroundSpy = spy(mRootView.getBackground()); + mRootView.setBackground(mCardBackgroundSpy); + mBannerPreference.setAttentionLevel(BannerMessagePreference.AttentionLevel.MEDIUM); + + mBannerPreference.onBindViewHolder(mHolder); + + assertThat(((ImageView) mHolder.findViewById(R.id.banner_icon)).getColorFilter()) + .isEqualTo(getColorFilter(R.color.banner_accent_attention_medium)); + assertThat(getButtonColor(R.id.banner_positive_btn)) + .isEqualTo(getColorId(R.color.banner_accent_attention_medium)); + assertThat(getButtonColor(R.id.banner_negative_btn)) + .isEqualTo(getColorId(R.color.banner_accent_attention_medium)); + verify(mCardBackgroundSpy).setTint(getColorId(R.color.banner_background_attention_medium)); + } + + @Test + public void setAttentionLevel_whenAtLeastS_whenLowAttention_setsLowTheme() { + assumeAndroidS(); + Drawable mCardBackgroundSpy = spy(mRootView.getBackground()); + mRootView.setBackground(mCardBackgroundSpy); + mBannerPreference.setAttentionLevel(BannerMessagePreference.AttentionLevel.LOW); + + mBannerPreference.onBindViewHolder(mHolder); + + assertThat(((ImageView) mHolder.findViewById(R.id.banner_icon)).getColorFilter()) + .isEqualTo(getColorFilter(R.color.banner_accent_attention_low)); + assertThat(getButtonColor(R.id.banner_positive_btn)) + .isEqualTo(getColorId(R.color.banner_accent_attention_low)); + assertThat(getButtonColor(R.id.banner_negative_btn)) + .isEqualTo(getColorId(R.color.banner_accent_attention_low)); + verify(mCardBackgroundSpy).setTint(getColorId(R.color.banner_background_attention_low)); + } + + private int getButtonColor(int buttonResId) { + Button mButton = mRootView.findViewById(buttonResId); + return mButton.getTextColors().getDefaultColor(); + } + + private ColorFilter getColorFilter(@ColorRes int colorResId) { + return new PorterDuffColorFilter(getColorId(colorResId), PorterDuff.Mode.SRC_IN); + } + + private int getColorId(@ColorRes int colorResId) { + return mContext.getResources().getColor(colorResId, mContext.getTheme()); + } + + private void assumeAndroidR() { + ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", 30); + // Reset view holder to use correct layout. + } + + private void assumeAndroidS() { + ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", 31); + // Re-inflate view to update layout. + setUpViewHolder(); + } + + private void setUpViewHolder() { + mRootView = + View.inflate(mContext, mBannerPreference.getLayoutResource(), null /* parent */); + mHolder = PreferenceViewHolder.createInstanceForTests(mRootView); + } } |