Changes to icon picker for reusability in "add mode" flow
* Don't finish the fragment from the controller (ugh!) instead just report the selected icon via a listener. * Highlight the selected icon in the list. * Cache the icon drawables (since we're using selectors for the colors, we don't need to swap them, one per icon resource id is enough). * Improved the tests a bit too. Bug: 333901673 Bug: 326442408 Test: ates Flag: android.app.modes_ui Change-Id: Ib2ec7a7e3ed99b13f9264aa6f7c209ee3f6967a0
This commit is contained in:
@@ -50,14 +50,16 @@ class IconUtil {
|
||||
|
||||
/**
|
||||
* Returns a variant of the supplied {@code icon} to be used in the icon picker. The inner icon
|
||||
* is 36x36dp and it's contained into a circle of diameter 54dp.
|
||||
* is 36x36dp and it's contained into a circle of diameter 54dp. It's also set up so that
|
||||
* selection and pressed states are represented in the color.
|
||||
*/
|
||||
static Drawable makeIconCircle(@NonNull Context context, @NonNull Drawable icon) {
|
||||
ShapeDrawable background = new ShapeDrawable(new OvalShape());
|
||||
background.getPaint().setColor(Utils.getColorAttrDefaultColor(context,
|
||||
com.android.internal.R.attr.materialColorSecondaryContainer));
|
||||
icon.setTint(Utils.getColorAttrDefaultColor(context,
|
||||
com.android.internal.R.attr.materialColorOnSecondaryContainer));
|
||||
background.setTintList(
|
||||
context.getColorStateList(R.color.modes_icon_picker_item_background));
|
||||
icon = icon.mutate();
|
||||
icon.setTintList(
|
||||
context.getColorStateList(R.color.modes_icon_picker_item_icon));
|
||||
|
||||
LayerDrawable layerDrawable = new LayerDrawable(new Drawable[] { background, icon });
|
||||
|
||||
|
@@ -18,13 +18,14 @@ package com.android.settings.notification.modes;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Application;
|
||||
import android.app.AutomaticZenRule;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settingslib.applications.ApplicationsState;
|
||||
import com.android.settingslib.core.AbstractPreferenceController;
|
||||
@@ -72,11 +73,9 @@ public class ZenModeFragment extends ZenModeFragmentBase {
|
||||
|
||||
// Set title for the entire screen
|
||||
ZenMode mode = getMode();
|
||||
AutomaticZenRule azr = getAZR();
|
||||
if (mode == null || azr == null) {
|
||||
return;
|
||||
if (mode != null) {
|
||||
requireActivity().setTitle(mode.getName());
|
||||
}
|
||||
getActivity().setTitle(azr.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -92,7 +91,7 @@ public class ZenModeFragment extends ZenModeFragmentBase {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onOptionsItemSelected(MenuItem item, ZenMode zenMode) {
|
||||
protected boolean onOptionsItemSelected(MenuItem item, @NonNull ZenMode zenMode) {
|
||||
switch (item.getItemId()) {
|
||||
case DELETE_MODE:
|
||||
new AlertDialog.Builder(mContext)
|
||||
|
@@ -18,7 +18,6 @@ package com.android.settings.notification.modes;
|
||||
|
||||
import static android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID;
|
||||
|
||||
import android.app.AutomaticZenRule;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
@@ -34,7 +33,10 @@ import com.android.settings.R;
|
||||
import com.android.settingslib.core.AbstractPreferenceController;
|
||||
import com.android.settingslib.notification.modes.ZenMode;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Base class for Settings pages used to configure individual modes.
|
||||
@@ -175,14 +177,15 @@ abstract class ZenModeFragmentBase extends ZenModesFragmentBase {
|
||||
return mZenMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get AutomaticZenRule associated with current mode data, or null if it doesn't exist.
|
||||
*/
|
||||
@Nullable
|
||||
public AutomaticZenRule getAZR() {
|
||||
if (mZenMode == null) {
|
||||
return null;
|
||||
protected final boolean saveMode(Consumer<ZenMode> updater) {
|
||||
Preconditions.checkState(mBackend != null);
|
||||
ZenMode mode = mZenMode;
|
||||
if (mode == null) {
|
||||
Log.wtf(TAG, "Cannot save mode, it hasn't been loaded (" + getClass() + ")");
|
||||
return false;
|
||||
}
|
||||
return mZenMode.getRule();
|
||||
updater.accept(mode);
|
||||
mBackend.updateMode(mode);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@@ -43,7 +43,16 @@ public class ZenModeIconPickerFragment extends ZenModeFragmentBase {
|
||||
return ImmutableList.of(
|
||||
new ZenModeIconPickerIconPreferenceController(context, "current_icon", this,
|
||||
mBackend),
|
||||
new ZenModeIconPickerListPreferenceController(context, "icon_list", this,
|
||||
new IconOptionsProviderImpl(mContext), mBackend));
|
||||
new ZenModeIconPickerListPreferenceController(context, "icon_list",
|
||||
mIconPickerListener, new IconOptionsProviderImpl(mContext), mBackend));
|
||||
}
|
||||
|
||||
private final ZenModeIconPickerListPreferenceController.IconPickerListener mIconPickerListener =
|
||||
new ZenModeIconPickerListPreferenceController.IconPickerListener() {
|
||||
@Override
|
||||
public void onIconSelected(int iconResId) {
|
||||
saveMode(mode -> mode.getRule().setIconResId(iconResId));
|
||||
finish();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@@ -17,6 +17,7 @@
|
||||
package com.android.settings.notification.modes;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -25,31 +26,35 @@ import android.widget.ImageView;
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.SimpleItemAnimator;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settingslib.notification.modes.ZenMode;
|
||||
import com.android.settingslib.notification.modes.ZenModesBackend;
|
||||
import com.android.settingslib.widget.LayoutPreference;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
class ZenModeIconPickerListPreferenceController extends AbstractZenModePreferenceController {
|
||||
|
||||
private final DashboardFragment mFragment;
|
||||
private final IconOptionsProvider mIconOptionsProvider;
|
||||
private final IconPickerListener mListener;
|
||||
@Nullable private IconAdapter mAdapter;
|
||||
private @DrawableRes int mCurrentIconResId;
|
||||
|
||||
ZenModeIconPickerListPreferenceController(@NonNull Context context, @NonNull String key,
|
||||
@NonNull DashboardFragment fragment, @NonNull IconOptionsProvider iconOptionsProvider,
|
||||
@NonNull IconPickerListener listener, @NonNull IconOptionsProvider iconOptionsProvider,
|
||||
@Nullable ZenModesBackend backend) {
|
||||
super(context, key, backend);
|
||||
mFragment = fragment;
|
||||
mListener = listener;
|
||||
mIconOptionsProvider = iconOptionsProvider;
|
||||
}
|
||||
|
||||
@@ -68,20 +73,34 @@ class ZenModeIconPickerListPreferenceController extends AbstractZenModePreferenc
|
||||
recyclerView.setLayoutManager(new AutoFitGridLayoutManager(mContext));
|
||||
recyclerView.setAdapter(mAdapter);
|
||||
recyclerView.setHasFixedSize(true);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void onIconSelected(@DrawableRes int resId) {
|
||||
saveMode(mode -> {
|
||||
mode.getRule().setIconResId(resId);
|
||||
return mode;
|
||||
});
|
||||
mFragment.finish();
|
||||
if (recyclerView.getItemAnimator() instanceof SimpleItemAnimator animator) {
|
||||
animator.setSupportsChangeAnimations(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void updateState(Preference preference, @NonNull ZenMode zenMode) {
|
||||
// Nothing to do, the current icon is shown in a different preference.
|
||||
updateIconSelection(zenMode.getRule().getIconResId());
|
||||
}
|
||||
|
||||
private void updateIconSelection(@DrawableRes int iconResId) {
|
||||
if (iconResId != mCurrentIconResId) {
|
||||
int oldIconResId = mCurrentIconResId;
|
||||
mCurrentIconResId = iconResId;
|
||||
if (mAdapter != null) {
|
||||
mAdapter.notifyIconChanged(oldIconResId);
|
||||
mAdapter.notifyIconChanged(mCurrentIconResId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onIconSelected(@DrawableRes int iconResId) {
|
||||
updateIconSelection(iconResId);
|
||||
mListener.onIconSelected(iconResId);
|
||||
}
|
||||
|
||||
interface IconPickerListener {
|
||||
void onIconSelected(@DrawableRes int iconResId);
|
||||
}
|
||||
|
||||
private class IconHolder extends RecyclerView.ViewHolder {
|
||||
@@ -93,20 +112,25 @@ class ZenModeIconPickerListPreferenceController extends AbstractZenModePreferenc
|
||||
mImageView = itemView.findViewById(R.id.icon_image_view);
|
||||
}
|
||||
|
||||
void bindIcon(IconOptionsProvider.IconInfo icon) {
|
||||
mImageView.setImageDrawable(
|
||||
IconUtil.makeIconCircle(itemView.getContext(), icon.resId()));
|
||||
void bindIcon(IconOptionsProvider.IconInfo icon, Drawable iconDrawable) {
|
||||
mImageView.setImageDrawable(iconDrawable);
|
||||
itemView.setContentDescription(icon.description());
|
||||
itemView.setOnClickListener(v -> onIconSelected(icon.resId()));
|
||||
itemView.setOnClickListener(v -> {
|
||||
itemView.setSelected(true); // Immediately, to avoid flicker until we rebind.
|
||||
onIconSelected(icon.resId());
|
||||
});
|
||||
itemView.setSelected(icon.resId() == mCurrentIconResId);
|
||||
}
|
||||
}
|
||||
|
||||
private class IconAdapter extends RecyclerView.Adapter<IconHolder> {
|
||||
|
||||
private final ImmutableList<IconOptionsProvider.IconInfo> mIconResources;
|
||||
private final Map<IconOptionsProvider.IconInfo, Drawable> mIconCache;
|
||||
|
||||
private IconAdapter(IconOptionsProvider iconOptionsProvider) {
|
||||
mIconResources = iconOptionsProvider.getIcons();
|
||||
mIconCache = new HashMap<>();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -119,13 +143,24 @@ class ZenModeIconPickerListPreferenceController extends AbstractZenModePreferenc
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull IconHolder holder, int position) {
|
||||
holder.bindIcon(mIconResources.get(position));
|
||||
IconOptionsProvider.IconInfo iconInfo = mIconResources.get(position);
|
||||
Drawable iconDrawable = mIconCache.computeIfAbsent(iconInfo,
|
||||
info -> IconUtil.makeIconCircle(mContext, info.resId()));
|
||||
holder.bindIcon(iconInfo, iconDrawable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mIconResources.size();
|
||||
}
|
||||
|
||||
private void notifyIconChanged(@DrawableRes int iconResId) {
|
||||
int position = Iterables.indexOf(mIconResources,
|
||||
iconInfo -> iconInfo.resId() == iconResId);
|
||||
if (position != -1) {
|
||||
notifyItemChanged(position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class AutoFitGridLayoutManager extends GridLayoutManager {
|
||||
|
Reference in New Issue
Block a user