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
This commit is contained in:
Matías Hernández
2024-08-13 19:13:46 +02:00
parent f1cd68ebd3
commit 115d29e851
4 changed files with 88 additions and 43 deletions

View File

@@ -15,6 +15,8 @@
*/ */
package com.android.settings.notification.modes; package com.android.settings.notification.modes;
import static com.google.common.base.Preconditions.checkNotNull;
import android.app.Flags; import android.app.Flags;
import android.content.Context; import android.content.Context;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
@@ -22,8 +24,8 @@ import android.view.ViewGroup;
import android.widget.ImageView; import android.widget.ImageView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment; 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.notification.modes.ZenMode;
import com.android.settingslib.widget.LayoutPreference; import com.android.settingslib.widget.LayoutPreference;
import java.util.function.Consumer; import com.google.common.base.Objects;
import java.util.function.Function; import java.util.function.Function;
abstract class AbstractZenModeHeaderController extends AbstractZenModePreferenceController { abstract class AbstractZenModeHeaderController extends AbstractZenModePreferenceController {
private final DashboardFragment mFragment; private final DashboardFragment mFragment;
private EntityHeaderController mHeaderController; private EntityHeaderController mHeaderController;
private String mCurrentIconKey;
AbstractZenModeHeaderController( AbstractZenModeHeaderController(
@NonNull Context context, @NonNull Context context,
@@ -53,27 +57,18 @@ abstract class AbstractZenModeHeaderController extends AbstractZenModePreference
return Flags.modesApi() && Flags.modesUi(); return Flags.modesApi() && Flags.modesUi();
} }
protected void updateIcon(Preference preference, @NonNull ZenMode zenMode, int iconSizePx, protected void setUpHeader(PreferenceScreen screen, int iconSizePx) {
Function<Drawable, Drawable> modeIconStylist, LayoutPreference preference = checkNotNull(screen.findPreference(getPreferenceKey()));
@Nullable Consumer<ImageView> iconViewCustomizer) {
if (mFragment == null) {
return;
}
preference.setSelectable(false); preference.setSelectable(false);
if (mHeaderController == null) { if (mHeaderController == null) {
final LayoutPreference pref = (LayoutPreference) preference;
mHeaderController = EntityHeaderController.newInstance( mHeaderController = EntityHeaderController.newInstance(
mFragment.getActivity(), mFragment.getActivity(),
mFragment, mFragment,
pref.findViewById(R.id.entity_header)); preference.findViewById(R.id.entity_header));
} }
ImageView iconView = ((LayoutPreference) preference).findViewById(R.id.entity_header_icon); ImageView iconView = checkNotNull(preference.findViewById(R.id.entity_header_icon));
if (iconView != null) {
if (iconViewCustomizer != null) {
iconViewCustomizer.accept(iconView);
}
ViewGroup.LayoutParams layoutParams = iconView.getLayoutParams(); ViewGroup.LayoutParams layoutParams = iconView.getLayoutParams();
if (layoutParams.width != iconSizePx || layoutParams.height != iconSizePx) { if (layoutParams.width != iconSizePx || layoutParams.height != iconSizePx) {
layoutParams.width = iconSizePx; layoutParams.width = iconSizePx;
@@ -82,11 +77,24 @@ abstract class AbstractZenModeHeaderController extends AbstractZenModePreference
} }
} }
protected void updateIcon(Preference preference, @NonNull ZenMode zenMode,
Function<Drawable, Drawable> 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( FutureUtil.whenDone(
zenMode.getIcon(mContext, ZenIconLoader.getInstance()), zenMode.getIcon(mContext, ZenIconLoader.getInstance()),
icon -> mHeaderController icon -> {
.setIcon(modeIconStylist.apply(icon)) checkNotNull(mHeaderController)
.done(/* rebindActions= */ false), .setIcon(iconStylist.apply(icon))
.done(/* rebindActions= */ false);
iconView.jumpDrawablesToCurrentState(); // Skip animation on first load.
},
mContext.getMainExecutor()); mContext.getMainExecutor());
} }
}
} }

View File

@@ -30,7 +30,9 @@ import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable; import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.StateListDrawable;
import android.graphics.drawable.shapes.OvalShape; import android.graphics.drawable.shapes.OvalShape;
import android.util.StateSet;
import android.view.Gravity; import android.view.Gravity;
import androidx.annotation.AttrRes; 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 * 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 * mode icon is contained in a 12-sided-cookie. The color combination is "material secondary"
* tinted with the "material secondary" color combination and the "selected" color variant * when unselected and "material primary" when selected; the switch between these two color sets
* should be used for modes currently active. * 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) { static Drawable makeModeHeader(@NonNull Context context, Drawable modeIcon) {
return composeIcons( Resources res = context.getResources();
checkNotNull(context.getDrawable(R.drawable.ic_zen_mode_icon_cookie)), Drawable background = checkNotNull(context.getDrawable(R.drawable.ic_zen_mode_icon_cookie));
context.getColorStateList(R.color.modes_icon_selectable_background), @Px int outerSizePx = res.getDimensionPixelSize(R.dimen.zen_mode_header_size);
context.getResources().getDimensionPixelSize( @Px int innerSizePx = res.getDimensionPixelSize(R.dimen.zen_mode_header_inner_icon_size);
R.dimen.zen_mode_header_size),
Drawable base = composeIcons(
background,
Utils.getColorAttr(context,
com.android.internal.R.attr.materialColorSecondaryContainer),
outerSizePx,
modeIcon, modeIcon,
context.getColorStateList(R.color.modes_icon_selectable_icon), Utils.getColorAttr(context,
context.getResources().getDimensionPixelSize( com.android.internal.R.attr.materialColorOnSecondaryContainer),
R.dimen.zen_mode_header_inner_icon_size)); 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;
} }
/** /**

View File

@@ -19,6 +19,7 @@ import android.content.Context;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment; import com.android.settings.dashboard.DashboardFragment;
@@ -33,11 +34,17 @@ class ZenModeHeaderController extends AbstractZenModeHeaderController {
super(context, key, fragment); 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 @Override
public void updateState(Preference preference, @NonNull ZenMode zenMode) { public void updateState(Preference preference, @NonNull ZenMode zenMode) {
updateIcon(preference, zenMode, updateIcon(preference, zenMode,
mContext.getResources().getDimensionPixelSize(R.dimen.zen_mode_header_size),
icon -> IconUtil.makeModeHeader(mContext, icon), icon -> IconUtil.makeModeHeader(mContext, icon),
iconView -> iconView.setSelected(zenMode.isActive())); /* isSelected= */ zenMode.isActive());
} }
} }

View File

@@ -20,6 +20,7 @@ import android.content.Context;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment; import com.android.settings.dashboard.DashboardFragment;
@@ -33,12 +34,17 @@ class ZenModeIconPickerIconPreferenceController extends AbstractZenModeHeaderCon
super(context, key, fragment); 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 @Override
void updateState(Preference preference, @NonNull ZenMode zenMode) { void updateState(Preference preference, @NonNull ZenMode zenMode) {
updateIcon(preference, zenMode, updateIcon(preference, zenMode,
mContext.getResources().getDimensionPixelSize(
R.dimen.zen_mode_icon_list_header_circle_diameter),
icon -> IconUtil.makeIconPickerHeader(mContext, icon), icon -> IconUtil.makeIconPickerHeader(mContext, icon),
null); /* isSelected= */ false);
} }
} }