From 115d29e851d6c4f6b593591a578bf5653d1f7e35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Hern=C3=A1ndez?= Date: Tue, 13 Aug 2024 19:13:46 +0200 Subject: [PATCH] Animate the color transition (active<->inactive) in the mode header icon Also, don't apply the layout params, etc on each call to updateState - once per displayPreference is enough. Fixes: 356399449 Bug: 357861830 Test: manual Flag: android.app.modes_ui Change-Id: I6967ea1745377d0f514ca0f68101043f017a8fd7 --- .../AbstractZenModeHeaderController.java | 62 +++++++++++-------- .../settings/notification/modes/IconUtil.java | 46 ++++++++++---- .../modes/ZenModeHeaderController.java | 11 +++- ...odeIconPickerIconPreferenceController.java | 12 +++- 4 files changed, 88 insertions(+), 43 deletions(-) diff --git a/src/com/android/settings/notification/modes/AbstractZenModeHeaderController.java b/src/com/android/settings/notification/modes/AbstractZenModeHeaderController.java index 06a30fa30f5..81b53cc3d3e 100644 --- a/src/com/android/settings/notification/modes/AbstractZenModeHeaderController.java +++ b/src/com/android/settings/notification/modes/AbstractZenModeHeaderController.java @@ -15,6 +15,8 @@ */ package com.android.settings.notification.modes; +import static com.google.common.base.Preconditions.checkNotNull; + import android.app.Flags; import android.content.Context; import android.graphics.drawable.Drawable; @@ -22,8 +24,8 @@ import android.view.ViewGroup; import android.widget.ImageView; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.dashboard.DashboardFragment; @@ -32,13 +34,15 @@ import com.android.settingslib.notification.modes.ZenIconLoader; import com.android.settingslib.notification.modes.ZenMode; import com.android.settingslib.widget.LayoutPreference; -import java.util.function.Consumer; +import com.google.common.base.Objects; + import java.util.function.Function; abstract class AbstractZenModeHeaderController extends AbstractZenModePreferenceController { private final DashboardFragment mFragment; private EntityHeaderController mHeaderController; + private String mCurrentIconKey; AbstractZenModeHeaderController( @NonNull Context context, @@ -53,40 +57,44 @@ abstract class AbstractZenModeHeaderController extends AbstractZenModePreference return Flags.modesApi() && Flags.modesUi(); } - protected void updateIcon(Preference preference, @NonNull ZenMode zenMode, int iconSizePx, - Function modeIconStylist, - @Nullable Consumer iconViewCustomizer) { - if (mFragment == null) { - return; - } + protected void setUpHeader(PreferenceScreen screen, int iconSizePx) { + LayoutPreference preference = checkNotNull(screen.findPreference(getPreferenceKey())); preference.setSelectable(false); if (mHeaderController == null) { - final LayoutPreference pref = (LayoutPreference) preference; mHeaderController = EntityHeaderController.newInstance( mFragment.getActivity(), mFragment, - pref.findViewById(R.id.entity_header)); + preference.findViewById(R.id.entity_header)); } - ImageView iconView = ((LayoutPreference) preference).findViewById(R.id.entity_header_icon); - if (iconView != null) { - if (iconViewCustomizer != null) { - iconViewCustomizer.accept(iconView); - } - ViewGroup.LayoutParams layoutParams = iconView.getLayoutParams(); - if (layoutParams.width != iconSizePx || layoutParams.height != iconSizePx) { - layoutParams.width = iconSizePx; - layoutParams.height = iconSizePx; - iconView.setLayoutParams(layoutParams); - } + ImageView iconView = checkNotNull(preference.findViewById(R.id.entity_header_icon)); + ViewGroup.LayoutParams layoutParams = iconView.getLayoutParams(); + if (layoutParams.width != iconSizePx || layoutParams.height != iconSizePx) { + layoutParams.width = iconSizePx; + layoutParams.height = iconSizePx; + iconView.setLayoutParams(layoutParams); } + } - FutureUtil.whenDone( - zenMode.getIcon(mContext, ZenIconLoader.getInstance()), - icon -> mHeaderController - .setIcon(modeIconStylist.apply(icon)) - .done(/* rebindActions= */ false), - mContext.getMainExecutor()); + protected void updateIcon(Preference preference, @NonNull ZenMode zenMode, + Function iconStylist, boolean isSelected) { + + ImageView iconView = checkNotNull( + ((LayoutPreference) preference).findViewById(R.id.entity_header_icon)); + iconView.setSelected(isSelected); + + if (!Objects.equal(mCurrentIconKey, zenMode.getIconKey())) { + mCurrentIconKey = zenMode.getIconKey(); + FutureUtil.whenDone( + zenMode.getIcon(mContext, ZenIconLoader.getInstance()), + icon -> { + checkNotNull(mHeaderController) + .setIcon(iconStylist.apply(icon)) + .done(/* rebindActions= */ false); + iconView.jumpDrawablesToCurrentState(); // Skip animation on first load. + }, + mContext.getMainExecutor()); + } } } diff --git a/src/com/android/settings/notification/modes/IconUtil.java b/src/com/android/settings/notification/modes/IconUtil.java index 43161ce085d..dc4d875869e 100644 --- a/src/com/android/settings/notification/modes/IconUtil.java +++ b/src/com/android/settings/notification/modes/IconUtil.java @@ -30,7 +30,9 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.graphics.drawable.ShapeDrawable; +import android.graphics.drawable.StateListDrawable; import android.graphics.drawable.shapes.OvalShape; +import android.util.StateSet; import android.view.Gravity; import androidx.annotation.AttrRes; @@ -65,20 +67,42 @@ class IconUtil { /** * Returns a variant of the supplied mode icon to be used as the header in the mode page. The - * inner icon is 64x64 dp and it's contained in a 12-sided-cookie of 136dp diameter. It's - * tinted with the "material secondary" color combination and the "selected" color variant - * should be used for modes currently active. + * mode icon is contained in a 12-sided-cookie. The color combination is "material secondary" + * when unselected and "material primary" when selected; the switch between these two color sets + * is animated with a cross-fade. The selected colors should be used when the mode is currently + * active. */ static Drawable makeModeHeader(@NonNull Context context, Drawable modeIcon) { - return composeIcons( - checkNotNull(context.getDrawable(R.drawable.ic_zen_mode_icon_cookie)), - context.getColorStateList(R.color.modes_icon_selectable_background), - context.getResources().getDimensionPixelSize( - R.dimen.zen_mode_header_size), + Resources res = context.getResources(); + Drawable background = checkNotNull(context.getDrawable(R.drawable.ic_zen_mode_icon_cookie)); + @Px int outerSizePx = res.getDimensionPixelSize(R.dimen.zen_mode_header_size); + @Px int innerSizePx = res.getDimensionPixelSize(R.dimen.zen_mode_header_inner_icon_size); + + Drawable base = composeIcons( + background, + Utils.getColorAttr(context, + com.android.internal.R.attr.materialColorSecondaryContainer), + outerSizePx, modeIcon, - context.getColorStateList(R.color.modes_icon_selectable_icon), - context.getResources().getDimensionPixelSize( - R.dimen.zen_mode_header_inner_icon_size)); + Utils.getColorAttr(context, + com.android.internal.R.attr.materialColorOnSecondaryContainer), + innerSizePx); + + Drawable selected = composeIcons( + background, + Utils.getColorAttr(context, com.android.internal.R.attr.materialColorPrimary), + outerSizePx, + modeIcon, + Utils.getColorAttr(context, com.android.internal.R.attr.materialColorOnPrimary), + innerSizePx); + + StateListDrawable result = new StateListDrawable(); + result.setEnterFadeDuration(res.getInteger(android.R.integer.config_mediumAnimTime)); + result.setExitFadeDuration(res.getInteger(android.R.integer.config_mediumAnimTime)); + result.addState(new int[] { android.R.attr.state_selected }, selected); + result.addState(StateSet.WILD_CARD, base); + result.setBounds(0, 0, outerSizePx, outerSizePx); + return result; } /** diff --git a/src/com/android/settings/notification/modes/ZenModeHeaderController.java b/src/com/android/settings/notification/modes/ZenModeHeaderController.java index c4f3dd188ba..ae6eacc52da 100644 --- a/src/com/android/settings/notification/modes/ZenModeHeaderController.java +++ b/src/com/android/settings/notification/modes/ZenModeHeaderController.java @@ -19,6 +19,7 @@ import android.content.Context; import androidx.annotation.NonNull; import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.dashboard.DashboardFragment; @@ -33,11 +34,17 @@ class ZenModeHeaderController extends AbstractZenModeHeaderController { super(context, key, fragment); } + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + setUpHeader(screen, + mContext.getResources().getDimensionPixelSize(R.dimen.zen_mode_header_size)); + } + @Override public void updateState(Preference preference, @NonNull ZenMode zenMode) { updateIcon(preference, zenMode, - mContext.getResources().getDimensionPixelSize(R.dimen.zen_mode_header_size), icon -> IconUtil.makeModeHeader(mContext, icon), - iconView -> iconView.setSelected(zenMode.isActive())); + /* isSelected= */ zenMode.isActive()); } } diff --git a/src/com/android/settings/notification/modes/ZenModeIconPickerIconPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeIconPickerIconPreferenceController.java index a7adf6c27f9..6c8d41f591a 100644 --- a/src/com/android/settings/notification/modes/ZenModeIconPickerIconPreferenceController.java +++ b/src/com/android/settings/notification/modes/ZenModeIconPickerIconPreferenceController.java @@ -20,6 +20,7 @@ import android.content.Context; import androidx.annotation.NonNull; import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.dashboard.DashboardFragment; @@ -33,12 +34,17 @@ class ZenModeIconPickerIconPreferenceController extends AbstractZenModeHeaderCon super(context, key, fragment); } + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + setUpHeader(screen, mContext.getResources().getDimensionPixelSize( + R.dimen.zen_mode_icon_list_header_circle_diameter)); + } + @Override void updateState(Preference preference, @NonNull ZenMode zenMode) { updateIcon(preference, zenMode, - mContext.getResources().getDimensionPixelSize( - R.dimen.zen_mode_icon_list_header_circle_diameter), icon -> IconUtil.makeIconPickerHeader(mContext, icon), - null); + /* isSelected= */ false); } }