Merge "Fix several issues related to CircularIconsPreference" into main

This commit is contained in:
Matías Hernández
2024-08-16 16:44:24 +00:00
committed by Android (Google) Code Review
13 changed files with 414 additions and 299 deletions

View File

@@ -58,8 +58,8 @@
android:lineBreakWordStyle="phrase" android:lineBreakWordStyle="phrase"
android:maxLines="10"/> android:maxLines="10"/>
<!-- Circular icons (32dp) will be ImageViews under this LinearLayout --> <!-- Circular icons (32dp) will be ImageViews under this container -->
<LinearLayout <com.android.settings.notification.modes.CircularIconsView
android:id="@+id/circles_container" android:id="@+id/circles_container"
android:importantForAccessibility="noHideDescendants" android:importantForAccessibility="noHideDescendants"
android:orientation="horizontal" android:orientation="horizontal"

View File

@@ -61,13 +61,6 @@ abstract class AbstractZenModeHeaderController extends AbstractZenModePreference
LayoutPreference preference = checkNotNull(screen.findPreference(getPreferenceKey())); LayoutPreference preference = checkNotNull(screen.findPreference(getPreferenceKey()));
preference.setSelectable(false); preference.setSelectable(false);
if (mHeaderController == null) {
mHeaderController = EntityHeaderController.newInstance(
mFragment.getActivity(),
mFragment,
preference.findViewById(R.id.entity_header));
}
ImageView iconView = checkNotNull(preference.findViewById(R.id.entity_header_icon)); ImageView iconView = checkNotNull(preference.findViewById(R.id.entity_header_icon));
ViewGroup.LayoutParams layoutParams = iconView.getLayoutParams(); ViewGroup.LayoutParams layoutParams = iconView.getLayoutParams();
if (layoutParams.width != iconSizePx || layoutParams.height != iconSizePx) { if (layoutParams.width != iconSizePx || layoutParams.height != iconSizePx) {
@@ -75,6 +68,14 @@ abstract class AbstractZenModeHeaderController extends AbstractZenModePreference
layoutParams.height = iconSizePx; layoutParams.height = iconSizePx;
iconView.setLayoutParams(layoutParams); iconView.setLayoutParams(layoutParams);
} }
if (mHeaderController == null) {
mHeaderController = EntityHeaderController.newInstance(
mFragment.getActivity(),
mFragment,
preference.findViewById(R.id.entity_header));
mHeaderController.done(false); // Make the space for the (unused) name go away.
}
} }
protected void updateIcon(Preference preference, @NonNull ZenMode zenMode, protected void updateIcon(Preference preference, @NonNull ZenMode zenMode,

View File

@@ -16,236 +16,72 @@
package com.android.settings.notification.modes; package com.android.settings.notification.modes;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import android.content.Context; import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceViewHolder; import androidx.preference.PreferenceViewHolder;
import com.android.settings.R; import com.android.settings.R;
import com.android.settingslib.RestrictedPreference; import com.android.settingslib.RestrictedPreference;
import com.google.common.base.Equivalence; import com.google.common.base.Equivalence;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.List;
import java.util.concurrent.Executor;
public class CircularIconsPreference extends RestrictedPreference { public class CircularIconsPreference extends RestrictedPreference {
private static final float DISABLED_ITEM_ALPHA = 0.3f; private CircularIconSet<?> mIconSet = CircularIconSet.EMPTY;
record LoadedIcons(ImmutableList<Drawable> icons, int extraItems) {
static final LoadedIcons EMPTY = new LoadedIcons(ImmutableList.of(), 0);
}
private Executor mUiExecutor;
// Chronologically, fields will be set top-to-bottom.
@Nullable private CircularIconSet<?> mIconSet;
@Nullable private ListenableFuture<List<Drawable>> mPendingLoadIconsFuture;
@Nullable private LoadedIcons mLoadedIcons;
public CircularIconsPreference(Context context) { public CircularIconsPreference(Context context) {
super(context); super(context);
init(context); init();
}
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
public CircularIconsPreference(Context context, Executor uiExecutor) {
this(context);
mUiExecutor = uiExecutor;
} }
public CircularIconsPreference(Context context, AttributeSet attrs) { public CircularIconsPreference(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);
init(context); init();
} }
public CircularIconsPreference(Context context, AttributeSet attrs, int defStyleAttr) { public CircularIconsPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr); super(context, attrs, defStyleAttr);
init(context); init();
} }
public CircularIconsPreference(Context context, AttributeSet attrs, int defStyleAttr, public CircularIconsPreference(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) { int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes); super(context, attrs, defStyleAttr, defStyleRes);
init(context); init();
} }
private void init(Context context) { private void init() {
mUiExecutor = context.getMainExecutor();
setLayoutResource(R.layout.preference_circular_icons); setLayoutResource(R.layout.preference_circular_icons);
} }
<T> void displayIcons(CircularIconSet<T> iconSet) { <T> void setIcons(CircularIconSet<T> iconSet) {
displayIcons(iconSet, null); setIcons(iconSet, null);
} }
<T> void displayIcons(CircularIconSet<T> iconSet, @Nullable Equivalence<T> itemEquivalence) { <T> void setIcons(CircularIconSet<T> iconSet, @Nullable Equivalence<T> itemEquivalence) {
if (mIconSet != null && mIconSet.hasSameItemsAs(iconSet, itemEquivalence)) { if (mIconSet.hasSameItemsAs(iconSet, itemEquivalence)) {
return; return;
} }
mIconSet = iconSet; mIconSet = iconSet;
mLoadedIcons = null;
if (mPendingLoadIconsFuture != null) {
mPendingLoadIconsFuture.cancel(true);
mPendingLoadIconsFuture = null;
}
notifyChanged(); notifyChanged();
} }
@Override @Override
public void onBindViewHolder(PreferenceViewHolder holder) { public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder); super.onBindViewHolder(holder);
CircularIconsView iconContainer = checkNotNull(
(CircularIconsView) holder.findViewById(R.id.circles_container));
LinearLayout iconContainer = checkNotNull( iconContainer.setVisibility(mIconSet != null && mIconSet.size() == 0 ? GONE : VISIBLE);
(LinearLayout) holder.findViewById(R.id.circles_container)); iconContainer.setEnabled(isEnabled());
bindIconContainer(iconContainer); iconContainer.setIcons(mIconSet);
}
private void bindIconContainer(LinearLayout container) {
if (mLoadedIcons != null) {
// We have the icons ready to display already, show them.
setDrawables(container, mLoadedIcons);
} else if (mIconSet != null) {
// We know what icons we want, but haven't yet loaded them.
if (mIconSet.size() == 0) {
container.setVisibility(View.GONE);
mLoadedIcons = LoadedIcons.EMPTY;
return;
}
container.setVisibility(View.VISIBLE);
if (container.getMeasuredWidth() != 0) {
startLoadingIcons(container, mIconSet);
} else {
container.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
container.getViewTreeObserver().removeOnGlobalLayoutListener(this);
notifyChanged();
}
}
);
}
}
}
private void startLoadingIcons(LinearLayout container, CircularIconSet<?> iconSet) {
Resources res = getContext().getResources();
int availableSpace = container.getMeasuredWidth();
int iconHorizontalSpace = res.getDimensionPixelSize(R.dimen.zen_mode_circular_icon_diameter)
+ res.getDimensionPixelSize(R.dimen.zen_mode_circular_icon_margin_between);
int numIconsThatFit = availableSpace / iconHorizontalSpace;
List<ListenableFuture<Drawable>> iconFutures;
int extraItems;
if (iconSet.size() > numIconsThatFit) {
// Reserve one space for the (+xx) textview.
int numIconsToShow = numIconsThatFit - 1;
if (numIconsToShow < 0) {
numIconsToShow = 0;
}
iconFutures = iconSet.getIcons(numIconsToShow);
extraItems = iconSet.size() - numIconsToShow;
} else {
// Fit exactly or with remaining space.
iconFutures = iconSet.getIcons();
extraItems = 0;
}
// Display icons when all are ready (more consistent than randomly loading).
mPendingLoadIconsFuture = Futures.allAsList(iconFutures);
FutureUtil.whenDone(
mPendingLoadIconsFuture,
icons -> {
mLoadedIcons = new LoadedIcons(ImmutableList.copyOf(icons), extraItems);
notifyChanged(); // So that view is rebound and icons actually shown.
},
mUiExecutor);
}
private void setDrawables(LinearLayout container, LoadedIcons loadedIcons) {
// Rearrange child views until we have <numImages> ImageViews...
LayoutInflater inflater = LayoutInflater.from(getContext());
int numImages = loadedIcons.icons.size();
int numImageViews = getChildCount(container, ImageView.class);
if (numImages > numImageViews) {
for (int i = 0; i < numImages - numImageViews; i++) {
ImageView imageView = (ImageView) inflater.inflate(
R.layout.preference_circular_icons_item, container, false);
container.addView(imageView, 0);
}
} else if (numImageViews > numImages) {
for (int i = 0; i < numImageViews - numImages; i++) {
container.removeViewAt(0);
}
}
// ... plus 0/1 TextViews at the end.
if (loadedIcons.extraItems > 0 && !(getLastChild(container) instanceof TextView)) {
TextView plusView = (TextView) inflater.inflate(
R.layout.preference_circular_icons_plus_item, container, false);
container.addView(plusView);
} else if (loadedIcons.extraItems == 0 && (getLastChild(container) instanceof TextView)) {
container.removeViewAt(container.getChildCount() - 1);
}
// Show images (and +n if needed).
for (int i = 0; i < numImages; i++) {
ImageView imageView = (ImageView) container.getChildAt(i);
imageView.setImageDrawable(loadedIcons.icons.get(i));
}
if (loadedIcons.extraItems > 0) {
TextView textView = (TextView) checkNotNull(getLastChild(container));
textView.setText(getContext().getString(R.string.zen_mode_plus_n_items,
loadedIcons.extraItems));
}
// Apply enabled/disabled style.
for (int i = 0; i < container.getChildCount(); i++) {
View child = container.getChildAt(i);
child.setAlpha(isEnabled() ? 1.0f : DISABLED_ITEM_ALPHA);
}
}
private static int getChildCount(ViewGroup parent, Class<? extends View> childClass) {
int count = 0;
for (int i = 0; i < parent.getChildCount(); i++) {
if (childClass.isInstance(parent.getChildAt(i))) {
count++;
}
}
return count;
}
@Nullable
private static View getLastChild(ViewGroup parent) {
if (parent.getChildCount() == 0) {
return null;
}
return parent.getChildAt(parent.getChildCount() - 1);
}
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
@Nullable
LoadedIcons getLoadedIcons() {
return mLoadedIcons;
} }
} }

View File

@@ -0,0 +1,232 @@
/*
* Copyright (C) 2024 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.settings.notification.modes;
import static com.google.common.base.Preconditions.checkNotNull;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.settings.R;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.List;
import java.util.concurrent.Executor;
public class CircularIconsView extends LinearLayout {
private static final float DISABLED_ITEM_ALPHA = 0.3f;
record Icons(ImmutableList<Drawable> icons, int extraItems) { }
private Executor mUiExecutor;
private int mNumberOfCirclesThatFit;
// Chronologically, fields will be set top-to-bottom.
@Nullable private CircularIconSet<?> mIconSet;
@Nullable private ListenableFuture<List<Drawable>> mPendingLoadIconsFuture;
@Nullable private Icons mDisplayedIcons;
public CircularIconsView(Context context) {
super(context);
setUiExecutor(context.getMainExecutor());
}
public CircularIconsView(Context context, AttributeSet attrs) {
super(context, attrs);
setUiExecutor(context.getMainExecutor());
}
public CircularIconsView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setUiExecutor(context.getMainExecutor());
}
public CircularIconsView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
setUiExecutor(context.getMainExecutor());
}
@VisibleForTesting
void setUiExecutor(Executor uiExecutor) {
mUiExecutor = uiExecutor;
}
<T> void setIcons(CircularIconSet<T> iconSet) {
if (mIconSet != null && mIconSet.equals(iconSet)) {
return;
}
mIconSet = checkNotNull(iconSet);
cancelPendingTasks();
if (getMeasuredWidth() != 0) {
startLoadingIcons(iconSet);
}
}
private void cancelPendingTasks() {
mDisplayedIcons = null;
if (mPendingLoadIconsFuture != null) {
mPendingLoadIconsFuture.cancel(true);
mPendingLoadIconsFuture = null;
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
int numFitting = getNumberOfCirclesThatFit();
if (mNumberOfCirclesThatFit != numFitting) {
// View has been measured for the first time OR its dimensions have changed since then.
// Keep track, because we want to reload stuff if more (or less) items fit.
mNumberOfCirclesThatFit = numFitting;
if (mIconSet != null) {
cancelPendingTasks();
startLoadingIcons(mIconSet);
}
}
}
private int getNumberOfCirclesThatFit() {
Resources res = getContext().getResources();
int availableSpace = getMeasuredWidth();
int iconHorizontalSpace = res.getDimensionPixelSize(R.dimen.zen_mode_circular_icon_diameter)
+ res.getDimensionPixelSize(R.dimen.zen_mode_circular_icon_margin_between);
return availableSpace / iconHorizontalSpace;
}
private void startLoadingIcons(CircularIconSet<?> iconSet) {
int numCirclesThatFit = getNumberOfCirclesThatFit();
List<ListenableFuture<Drawable>> iconFutures;
int extraItems;
if (iconSet.size() > numCirclesThatFit) {
// Reserve one space for the (+xx) textview.
int numIconsToShow = numCirclesThatFit - 1;
if (numIconsToShow < 0) {
numIconsToShow = 0;
}
iconFutures = iconSet.getIcons(numIconsToShow);
extraItems = iconSet.size() - numIconsToShow;
} else {
// Fit exactly or with remaining space.
iconFutures = iconSet.getIcons();
extraItems = 0;
}
// Display icons when all are ready (more consistent than randomly loading).
mPendingLoadIconsFuture = Futures.allAsList(iconFutures);
FutureUtil.whenDone(
mPendingLoadIconsFuture,
icons -> setDrawables(new Icons(ImmutableList.copyOf(icons), extraItems)),
mUiExecutor);
}
private void setDrawables(Icons icons) {
mDisplayedIcons = icons;
// Rearrange child views until we have <numImages> ImageViews...
LayoutInflater inflater = LayoutInflater.from(getContext());
int numImages = icons.icons.size();
int numImageViews = getChildCount(ImageView.class);
if (numImages > numImageViews) {
for (int i = 0; i < numImages - numImageViews; i++) {
ImageView imageView = (ImageView) inflater.inflate(
R.layout.preference_circular_icons_item, this, false);
addView(imageView, 0);
}
} else if (numImageViews > numImages) {
for (int i = 0; i < numImageViews - numImages; i++) {
removeViewAt(0);
}
}
// ... plus 0/1 TextViews at the end.
if (icons.extraItems > 0 && !(getLastChild() instanceof TextView)) {
TextView plusView = (TextView) inflater.inflate(
R.layout.preference_circular_icons_plus_item, this, false);
this.addView(plusView);
} else if (icons.extraItems == 0 && (getLastChild() instanceof TextView)) {
removeViewAt(getChildCount() - 1);
}
// Show images (and +n if needed).
for (int i = 0; i < numImages; i++) {
ImageView imageView = (ImageView) getChildAt(i);
imageView.setImageDrawable(icons.icons.get(i));
}
if (icons.extraItems > 0) {
TextView textView = (TextView) checkNotNull(getLastChild());
textView.setText(getContext().getString(R.string.zen_mode_plus_n_items,
icons.extraItems));
}
applyEnabledDisabledAppearance(isEnabled());
}
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
applyEnabledDisabledAppearance(isEnabled());
}
private void applyEnabledDisabledAppearance(boolean enabled) {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
child.setAlpha(enabled ? 1.0f : DISABLED_ITEM_ALPHA);
}
}
private int getChildCount(Class<? extends View> childClass) {
int count = 0;
for (int i = 0; i < getChildCount(); i++) {
if (childClass.isInstance(getChildAt(i))) {
count++;
}
}
return count;
}
@Nullable
private View getLastChild() {
if (getChildCount() == 0) {
return null;
}
return getChildAt(getChildCount() - 1);
}
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
@Nullable
Icons getDisplayedIcons() {
return mDisplayedIcons;
}
}

View File

@@ -79,6 +79,7 @@ class IconUtil {
@Px int innerSizePx = res.getDimensionPixelSize(R.dimen.zen_mode_header_inner_icon_size); @Px int innerSizePx = res.getDimensionPixelSize(R.dimen.zen_mode_header_inner_icon_size);
Drawable base = composeIcons( Drawable base = composeIcons(
context.getResources(),
background, background,
Utils.getColorAttr(context, Utils.getColorAttr(context,
com.android.internal.R.attr.materialColorSecondaryContainer), com.android.internal.R.attr.materialColorSecondaryContainer),
@@ -89,6 +90,7 @@ class IconUtil {
innerSizePx); innerSizePx);
Drawable selected = composeIcons( Drawable selected = composeIcons(
context.getResources(),
background, background,
Utils.getColorAttr(context, com.android.internal.R.attr.materialColorPrimary), Utils.getColorAttr(context, com.android.internal.R.attr.materialColorPrimary),
outerSizePx, outerSizePx,
@@ -111,6 +113,7 @@ class IconUtil {
*/ */
static Drawable makeIconPickerHeader(@NonNull Context context, Drawable icon) { static Drawable makeIconPickerHeader(@NonNull Context context, Drawable icon) {
return composeIconCircle( return composeIconCircle(
context.getResources(),
Utils.getColorAttr(context, Utils.getColorAttr(context,
com.android.internal.R.attr.materialColorSecondaryContainer), com.android.internal.R.attr.materialColorSecondaryContainer),
context.getResources().getDimensionPixelSize( context.getResources().getDimensionPixelSize(
@@ -129,6 +132,7 @@ class IconUtil {
*/ */
static Drawable makeIconPickerItem(@NonNull Context context, @DrawableRes int iconResId) { static Drawable makeIconPickerItem(@NonNull Context context, @DrawableRes int iconResId) {
return composeIconCircle( return composeIconCircle(
context.getResources(),
context.getColorStateList(R.color.modes_icon_selectable_background), context.getColorStateList(R.color.modes_icon_selectable_background),
context.getResources().getDimensionPixelSize( context.getResources().getDimensionPixelSize(
R.dimen.zen_mode_icon_list_item_circle_diameter), R.dimen.zen_mode_icon_list_item_circle_diameter),
@@ -146,6 +150,7 @@ class IconUtil {
static Drawable makeCircularIconPreferenceItem(@NonNull Context context, static Drawable makeCircularIconPreferenceItem(@NonNull Context context,
@DrawableRes int iconResId) { @DrawableRes int iconResId) {
return composeIconCircle( return composeIconCircle(
context.getResources(),
Utils.getColorAttr(context, Utils.getColorAttr(context,
com.android.internal.R.attr.materialColorSecondaryContainer), com.android.internal.R.attr.materialColorSecondaryContainer),
context.getResources().getDimensionPixelSize( context.getResources().getDimensionPixelSize(
@@ -166,6 +171,7 @@ class IconUtil {
Resources res = context.getResources(); Resources res = context.getResources();
if (Strings.isNullOrEmpty(displayName)) { if (Strings.isNullOrEmpty(displayName)) {
return composeIconCircle( return composeIconCircle(
context.getResources(),
Utils.getColorAttr(context, Utils.getColorAttr(context,
com.android.internal.R.attr.materialColorTertiaryContainer), com.android.internal.R.attr.materialColorTertiaryContainer),
res.getDimensionPixelSize(R.dimen.zen_mode_circular_icon_diameter), res.getDimensionPixelSize(R.dimen.zen_mode_circular_icon_diameter),
@@ -204,17 +210,17 @@ class IconUtil {
return new BitmapDrawable(context.getResources(), bitmap); return new BitmapDrawable(context.getResources(), bitmap);
} }
private static Drawable composeIconCircle(ColorStateList circleColor, @Px int circleDiameterPx, private static Drawable composeIconCircle(Resources res, ColorStateList circleColor,
Drawable icon, ColorStateList iconColor, @Px int iconSizePx) { @Px int circleDiameterPx, Drawable icon, ColorStateList iconColor, @Px int iconSizePx) {
return composeIcons(new ShapeDrawable(new OvalShape()), circleColor, circleDiameterPx, icon, return composeIcons(res, new ShapeDrawable(new OvalShape()), circleColor, circleDiameterPx,
iconColor, iconSizePx); icon, iconColor, iconSizePx);
} }
private static Drawable composeIcons(Drawable outer, ColorStateList outerColor, private static Drawable composeIcons(Resources res, Drawable outer, ColorStateList outerColor,
@Px int outerSizePx, Drawable icon, ColorStateList iconColor, @Px int iconSizePx) { @Px int outerSizePx, Drawable icon, ColorStateList iconColor, @Px int iconSizePx) {
Drawable background = checkNotNull(outer.getConstantState()).newDrawable().mutate(); Drawable background = checkNotNull(outer.getConstantState()).newDrawable(res).mutate();
background.setTintList(outerColor); background.setTintList(outerColor);
Drawable foreground = checkNotNull(icon.getConstantState()).newDrawable().mutate(); Drawable foreground = checkNotNull(icon.getConstantState()).newDrawable(res).mutate();
foreground.setTintList(iconColor); foreground.setTintList(iconColor);
LayerDrawable layerDrawable = new LayerDrawable(new Drawable[] { background, foreground }); LayerDrawable layerDrawable = new LayerDrawable(new Drawable[] { background, foreground });

View File

@@ -109,7 +109,7 @@ class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceContr
if (zenMode.getPolicy().getAllowedChannels() == ZenPolicy.CHANNEL_POLICY_NONE) { if (zenMode.getPolicy().getAllowedChannels() == ZenPolicy.CHANNEL_POLICY_NONE) {
mPreference.setSummary(R.string.zen_mode_apps_none_apps); mPreference.setSummary(R.string.zen_mode_apps_none_apps);
mPreference.displayIcons(CircularIconSet.EMPTY); mPreference.setIcons(CircularIconSet.EMPTY);
if (mAppSession != null) { if (mAppSession != null) {
mAppSession.deactivateSession(); mAppSession.deactivateSession();
} }
@@ -151,7 +151,7 @@ class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceContr
ImmutableList<AppEntry> apps = getAppsBypassingDndSortedByName(allApps); ImmutableList<AppEntry> apps = getAppsBypassingDndSortedByName(allApps);
mPreference.setSummary(mSummaryHelper.getAppsSummary(mZenMode, apps)); mPreference.setSummary(mSummaryHelper.getAppsSummary(mZenMode, apps));
mPreference.displayIcons(new CircularIconSet<>(apps, mPreference.setIcons(new CircularIconSet<>(apps,
app -> mAppIconRetriever.apply(app.info)), app -> mAppIconRetriever.apply(app.info)),
APP_ENTRY_EQUIVALENCE); APP_ENTRY_EQUIVALENCE);
} }

View File

@@ -72,7 +72,7 @@ class ZenModeOtherLinkPreferenceController extends AbstractZenModePreferenceCont
preference.setEnabled(zenMode.isEnabled()); preference.setEnabled(zenMode.isEnabled());
preference.setSummary(mSummaryHelper.getOtherSoundCategoriesSummary(zenMode)); preference.setSummary(mSummaryHelper.getOtherSoundCategoriesSummary(zenMode));
((CircularIconsPreference) preference).displayIcons(getSoundIcons(zenMode.getPolicy())); ((CircularIconsPreference) preference).setIcons(getSoundIcons(zenMode.getPolicy()));
} }
private CircularIconSet<Integer> getSoundIcons(ZenPolicy policy) { private CircularIconSet<Integer> getSoundIcons(ZenPolicy policy) {

View File

@@ -96,7 +96,7 @@ class ZenModePeopleLinkPreferenceController extends AbstractZenModePreferenceCon
preference.setEnabled(zenMode.isEnabled()); preference.setEnabled(zenMode.isEnabled());
preference.setSummary(mSummaryHelper.getPeopleSummary(zenMode.getPolicy())); preference.setSummary(mSummaryHelper.getPeopleSummary(zenMode.getPolicy()));
((CircularIconsPreference) preference).displayIcons(getPeopleIcons(zenMode.getPolicy()), ((CircularIconsPreference) preference).setIcons(getPeopleIcons(zenMode.getPolicy()),
PEOPLE_ITEM_EQUIVALENCE); PEOPLE_ITEM_EQUIVALENCE);
} }

View File

@@ -62,8 +62,7 @@ public class CircularIconsPreferenceTest {
private Context mContext; private Context mContext;
private CircularIconsPreference mPreference; private CircularIconsPreference mPreference;
private PreferenceViewHolder mViewHolder; private CircularIconsView mContainer;
private ViewGroup mContainer;
private int mOneIconWidth; private int mOneIconWidth;
@@ -73,179 +72,211 @@ public class CircularIconsPreferenceTest {
mContext = RuntimeEnvironment.application; mContext = RuntimeEnvironment.application;
CircularIconSet.sExecutorService = MoreExecutors.newDirectExecutorService(); CircularIconSet.sExecutorService = MoreExecutors.newDirectExecutorService();
mPreference = new TestableCircularIconsPreference(mContext); mPreference = new TestableCircularIconsPreference(mContext);
// Tests should call bindAndMeasureViewHolder() so that icons can be added. // Tests should call bindAndLayoutViewHolder() so that icons can be added.
Resources res = mContext.getResources(); Resources res = mContext.getResources();
mOneIconWidth = res.getDimensionPixelSize(R.dimen.zen_mode_circular_icon_diameter) mOneIconWidth = res.getDimensionPixelSize(R.dimen.zen_mode_circular_icon_diameter)
+ res.getDimensionPixelSize(R.dimen.zen_mode_circular_icon_margin_between); + res.getDimensionPixelSize(R.dimen.zen_mode_circular_icon_margin_between);
} }
private void bindAndMeasureViewHolder(int viewWidth) { private void bindAndLayoutViewHolder(int viewWidth) {
bindViewHolder(); bindViewHolder();
measureViewHolder(viewWidth); layoutViewHolder(viewWidth);
} }
private void bindViewHolder() { private void bindViewHolder() {
View preferenceView = LayoutInflater.from(mContext).inflate(mPreference.getLayoutResource(), View preferenceView = LayoutInflater.from(mContext).inflate(mPreference.getLayoutResource(),
null); null);
mContainer = checkNotNull(preferenceView.findViewById(R.id.circles_container)); mContainer = checkNotNull(preferenceView.findViewById(R.id.circles_container));
mViewHolder = PreferenceViewHolder.createInstanceForTests(preferenceView); mContainer.setUiExecutor(MoreExecutors.directExecutor());
mPreference.onBindViewHolder(mViewHolder); PreferenceViewHolder viewHolder = PreferenceViewHolder.createInstanceForTests(
preferenceView);
mPreference.onBindViewHolder(viewHolder);
} }
private void measureViewHolder(int viewWidth) { private void layoutViewHolder(int viewWidth) {
checkState(mContainer != null, "Call bindViewHolder() first!"); checkState(mContainer != null, "Call bindViewHolder() first!");
mContainer.measure(makeMeasureSpec(viewWidth, View.MeasureSpec.EXACTLY), mContainer.measure(makeMeasureSpec(viewWidth, View.MeasureSpec.EXACTLY),
makeMeasureSpec(1000, View.MeasureSpec.EXACTLY)); makeMeasureSpec(1000, View.MeasureSpec.EXACTLY));
mContainer.getViewTreeObserver().dispatchOnGlobalLayout(); mContainer.layout(0, 0, viewWidth, 1000);
} }
@Test @Test
public void displayIcons_loadsIcons() { public void setIcons_loadsIcons() {
CircularIconSet<Integer> iconSet = new CircularIconSet<>(ImmutableList.of(1, 2), CircularIconSet<Integer> iconSet = new CircularIconSet<>(ImmutableList.of(1, 2),
ColorDrawable::new); ColorDrawable::new);
bindAndMeasureViewHolder(VIEW_WIDTH); bindAndLayoutViewHolder(VIEW_WIDTH);
mPreference.displayIcons(iconSet); mPreference.setIcons(iconSet);
assertThat(getIcons(mContainer)).hasSize(2); assertThat(getDrawables(mContainer)).hasSize(2);
assertThat(((ColorDrawable) getIcons(mContainer).get(0)).getColor()).isEqualTo(1); assertThat(((ColorDrawable) getDrawables(mContainer).get(0)).getColor()).isEqualTo(1);
assertThat(((ColorDrawable) getIcons(mContainer).get(1)).getColor()).isEqualTo(2); assertThat(((ColorDrawable) getDrawables(mContainer).get(1)).getColor()).isEqualTo(2);
assertThat(getPlusText(mContainer)).isNull(); assertThat(getPlusText(mContainer)).isNull();
} }
@Test @Test
public void displayIcons_noIcons_hidesRow() { public void setIcons_noIcons_hidesRow() {
CircularIconSet<Integer> iconSet = new CircularIconSet<>(ImmutableList.of(), CircularIconSet<Integer> iconSet = new CircularIconSet<>(ImmutableList.of(),
ColorDrawable::new); ColorDrawable::new);
bindAndMeasureViewHolder(VIEW_WIDTH); bindAndLayoutViewHolder(VIEW_WIDTH);
mPreference.displayIcons(iconSet); mPreference.setIcons(iconSet);
assertThat(mContainer.getVisibility()).isEqualTo(View.GONE); assertThat(mContainer.getVisibility()).isEqualTo(View.GONE);
} }
@Test @Test
public void displayIcons_exactlyMaxIcons_loadsAllIcons() throws Exception { public void setIcons_exactlyMaxIcons_loadsAllIcons() throws Exception {
int width = 300; int width = 300;
int fittingCircles = width / mOneIconWidth; int fittingCircles = width / mOneIconWidth;
CircularIconSet<Integer> iconSet = new CircularIconSet<>( CircularIconSet<Integer> iconSet = new CircularIconSet<>(
IntStream.range(0, fittingCircles).boxed().toList(), IntStream.range(0, fittingCircles).boxed().toList(),
ColorDrawable::new); ColorDrawable::new);
bindAndMeasureViewHolder(width); bindAndLayoutViewHolder(width);
mPreference.displayIcons(iconSet); mPreference.setIcons(iconSet);
assertThat(getIcons(mContainer)).hasSize(fittingCircles); assertThat(getDrawables(mContainer)).hasSize(fittingCircles);
assertThat(getIcons(mContainer)).containsExactlyElementsIn( assertThat(getDrawables(mContainer)).containsExactlyElementsIn(
Futures.allAsList(iconSet.getIcons()).get()).inOrder(); Futures.allAsList(iconSet.getIcons()).get()).inOrder();
assertThat(getPlusText(mContainer)).isNull(); assertThat(getPlusText(mContainer)).isNull();
} }
@Test @Test
public void displayIcons_tooManyIcons_loadsFirstNAndPlusIcon() throws Exception { public void setIcons_tooManyIcons_loadsFirstNAndPlusIcon() throws Exception {
int width = 300; int width = 300;
int fittingCircles = width / mOneIconWidth; int fittingCircles = width / mOneIconWidth;
CircularIconSet<Integer> iconSet = new CircularIconSet<>( CircularIconSet<Integer> iconSet = new CircularIconSet<>(
IntStream.range(0, fittingCircles + 5).boxed().toList(), IntStream.range(0, fittingCircles + 5).boxed().toList(),
ColorDrawable::new); ColorDrawable::new);
bindAndMeasureViewHolder(width); bindAndLayoutViewHolder(width);
mPreference.displayIcons(iconSet); mPreference.setIcons(iconSet);
// N-1 icons, plus (+6) text. // N-1 icons, plus (+6) text.
assertThat(getIcons(mContainer)).hasSize(fittingCircles - 1); assertThat(getDrawables(mContainer)).hasSize(fittingCircles - 1);
assertThat(getIcons(mContainer)).containsExactlyElementsIn( assertThat(getDrawables(mContainer)).containsExactlyElementsIn(
Futures.allAsList(iconSet.getIcons(fittingCircles - 1)).get()) Futures.allAsList(iconSet.getIcons(fittingCircles - 1)).get())
.inOrder(); .inOrder();
assertThat(getPlusText(mContainer)).isEqualTo("+6"); assertThat(getPlusText(mContainer)).isEqualTo("+6");
} }
@Test @Test
public void displayIcons_teenyTinySpace_showsPlusIcon_noCrash() { public void setIcons_teenyTinySpace_showsPlusIcon_noCrash() {
CircularIconSet<Integer> iconSet = new CircularIconSet<>(ImmutableList.of(1, 2), CircularIconSet<Integer> iconSet = new CircularIconSet<>(ImmutableList.of(1, 2),
ColorDrawable::new); ColorDrawable::new);
bindAndMeasureViewHolder(1); bindAndLayoutViewHolder(1);
mPreference.displayIcons(iconSet); mPreference.setIcons(iconSet);
assertThat(getIcons(mContainer)).isEmpty(); assertThat(getDrawables(mContainer)).isEmpty();
assertThat(getPlusText(mContainer)).isEqualTo("+2"); assertThat(getPlusText(mContainer)).isEqualTo("+2");
} }
@Test @Test
public void displayIcons_beforeBind_loadsIconsOnBindAndMeasure() { public void setIcons_beforeBind_loadsIconsOnBindAndMeasure() {
CircularIconSet<Integer> iconSet = new CircularIconSet<>(ImmutableList.of(1, 2, 3), CircularIconSet<Integer> iconSet = new CircularIconSet<>(ImmutableList.of(1, 2, 3),
ColorDrawable::new); ColorDrawable::new);
mPreference.displayIcons(iconSet); mPreference.setIcons(iconSet);
assertThat(mPreference.getLoadedIcons()).isNull(); // Hold... assertThat(mContainer).isNull(); // Hold...
bindViewHolder(); bindViewHolder();
assertThat(mPreference.getLoadedIcons()).isNull(); // Hooooold... assertThat(getDrawables(mContainer)).hasSize(0); // Hooooold...
measureViewHolder(VIEW_WIDTH); layoutViewHolder(VIEW_WIDTH);
assertThat(mPreference.getLoadedIcons().icons()).hasSize(3); assertThat(getDrawables(mContainer)).hasSize(3);
assertThat(getIcons(mContainer)).hasSize(3);
} }
@Test @Test
public void displayIcons_beforeMeasure_loadsIconsOnMeasure() { public void setIcons_beforeMeasure_loadsIconsOnMeasure() {
CircularIconSet<Integer> iconSet = new CircularIconSet<>(ImmutableList.of(1, 2, 3), CircularIconSet<Integer> iconSet = new CircularIconSet<>(ImmutableList.of(1, 2, 3),
ColorDrawable::new); ColorDrawable::new);
bindViewHolder(); bindViewHolder();
mPreference.displayIcons(iconSet); mPreference.setIcons(iconSet);
assertThat(mPreference.getLoadedIcons()).isNull(); assertThat(getDrawables(mContainer)).hasSize(0);
measureViewHolder(VIEW_WIDTH); layoutViewHolder(VIEW_WIDTH);
assertThat(getIcons(mContainer)).hasSize(3); assertThat(getDrawables(mContainer)).hasSize(3);
} }
@Test @Test
public void displayIcons_calledAgain_reloadsIcons() { public void setIcons_calledAgain_reloadsIcons() {
CircularIconSet<Integer> threeIcons = new CircularIconSet<>(ImmutableList.of(1, 2, 3), CircularIconSet<Integer> threeIcons = new CircularIconSet<>(ImmutableList.of(1, 2, 3),
ColorDrawable::new); ColorDrawable::new);
CircularIconSet<Integer> twoIcons = new CircularIconSet<>(ImmutableList.of(1, 2), CircularIconSet<Integer> twoIcons = new CircularIconSet<>(ImmutableList.of(1, 2),
ColorDrawable::new); ColorDrawable::new);
CircularIconSet<Integer> fourIcons = new CircularIconSet<>(ImmutableList.of(1, 2, 3, 4), CircularIconSet<Integer> fourIcons = new CircularIconSet<>(ImmutableList.of(1, 2, 3, 4),
ColorDrawable::new); ColorDrawable::new);
bindAndMeasureViewHolder(VIEW_WIDTH); bindAndLayoutViewHolder(VIEW_WIDTH);
mPreference.displayIcons(threeIcons); mPreference.setIcons(threeIcons);
assertThat(mPreference.getLoadedIcons()).isNotNull(); assertThat(getDrawables(mContainer)).hasSize(3);
assertThat(getIcons(mContainer)).hasSize(3);
mPreference.displayIcons(twoIcons); mPreference.setIcons(twoIcons);
assertThat(mPreference.getLoadedIcons()).isNotNull(); assertThat(getDrawables(mContainer)).hasSize(2);
assertThat(getIcons(mContainer)).hasSize(2);
mPreference.displayIcons(fourIcons); mPreference.setIcons(fourIcons);
assertThat(mPreference.getLoadedIcons()).isNotNull(); assertThat(getDrawables(mContainer)).hasSize(4);
assertThat(getIcons(mContainer)).hasSize(4);
} }
@Test @Test
public void displayIcons_sameSet_doesNotReloadIcons() { public void setIcons_sameSet_doesNotReloadIcons() {
CircularIconSet<Integer> one = new CircularIconSet<>(ImmutableList.of(1, 2, 3), CircularIconSet<Integer> one = new CircularIconSet<>(ImmutableList.of(1, 2, 3),
ColorDrawable::new); ColorDrawable::new);
CircularIconSet<Integer> same = Mockito.spy(new CircularIconSet<>(ImmutableList.of(1, 2, 3), CircularIconSet<Integer> same = Mockito.spy(new CircularIconSet<>(ImmutableList.of(1, 2, 3),
ColorDrawable::new)); ColorDrawable::new));
when(same.getIcons()).thenThrow(new RuntimeException("Shouldn't be called!")); when(same.getIcons()).thenThrow(new RuntimeException("Shouldn't be called!"));
bindAndMeasureViewHolder(VIEW_WIDTH); bindAndLayoutViewHolder(VIEW_WIDTH);
mPreference.displayIcons(one); mPreference.setIcons(one);
mPreference.displayIcons(same); // if no exception, wasn't called. mPreference.setIcons(same); // if no exception, wasn't called.
} }
@Test
public void sizeChanged_reloadsIconsIfDifferentFit() {
CircularIconSet<Integer> largeIconSet = new CircularIconSet<>(
IntStream.range(0, 100).boxed().toList(),
ColorDrawable::new);
mPreference.setIcons(largeIconSet);
// Base space -> some icons
int firstWidth = 600;
int firstFittingCircles = firstWidth / mOneIconWidth;
bindAndLayoutViewHolder(firstWidth);
assertThat(getDrawables(mContainer)).hasSize(firstFittingCircles - 1);
assertThat(getPlusText(mContainer)).isEqualTo("+" + (100 - (firstFittingCircles - 1)));
// More space -> more icons
int secondWidth = 1000;
int secondFittingCircles = secondWidth / mOneIconWidth;
assertThat(secondFittingCircles).isGreaterThan(firstFittingCircles);
bindAndLayoutViewHolder(secondWidth);
assertThat(getDrawables(mContainer)).hasSize(secondFittingCircles - 1);
assertThat(getPlusText(mContainer)).isEqualTo("+" + (100 - (secondFittingCircles - 1)));
// Less space -> fewer icons
int thirdWidth = 600;
int thirdFittingCircles = thirdWidth / mOneIconWidth;
bindAndLayoutViewHolder(thirdWidth);
assertThat(getDrawables(mContainer)).hasSize(thirdFittingCircles - 1);
assertThat(getPlusText(mContainer)).isEqualTo("+" + (100 - (thirdFittingCircles - 1)));
}
@Test @Test
public void onBindViewHolder_withDifferentView_reloadsIconsCorrectly() { public void onBindViewHolder_withDifferentView_reloadsIconsCorrectly() {
View preferenceViewOne = LayoutInflater.from(mContext).inflate( View preferenceViewOne = LayoutInflater.from(mContext).inflate(
mPreference.getLayoutResource(), null); mPreference.getLayoutResource(), null);
ViewGroup containerOne = preferenceViewOne.findViewById(R.id.circles_container); CircularIconsView containerOne = preferenceViewOne.findViewById(R.id.circles_container);
containerOne.setUiExecutor(MoreExecutors.directExecutor());
PreferenceViewHolder viewHolderOne = PreferenceViewHolder.createInstanceForTests( PreferenceViewHolder viewHolderOne = PreferenceViewHolder.createInstanceForTests(
preferenceViewOne); preferenceViewOne);
containerOne.measure(makeMeasureSpec(1000, View.MeasureSpec.EXACTLY), containerOne.measure(makeMeasureSpec(1000, View.MeasureSpec.EXACTLY),
@@ -253,7 +284,8 @@ public class CircularIconsPreferenceTest {
View preferenceViewTwo = LayoutInflater.from(mContext).inflate( View preferenceViewTwo = LayoutInflater.from(mContext).inflate(
mPreference.getLayoutResource(), null); mPreference.getLayoutResource(), null);
ViewGroup containerTwo = preferenceViewTwo.findViewById(R.id.circles_container); CircularIconsView containerTwo = preferenceViewTwo.findViewById(R.id.circles_container);
containerTwo.setUiExecutor(MoreExecutors.directExecutor());
PreferenceViewHolder viewHolderTwo = PreferenceViewHolder.createInstanceForTests( PreferenceViewHolder viewHolderTwo = PreferenceViewHolder.createInstanceForTests(
preferenceViewTwo); preferenceViewTwo);
containerTwo.measure(makeMeasureSpec(1000, View.MeasureSpec.EXACTLY), containerTwo.measure(makeMeasureSpec(1000, View.MeasureSpec.EXACTLY),
@@ -265,25 +297,25 @@ public class CircularIconsPreferenceTest {
ColorDrawable::new); ColorDrawable::new);
mPreference.onBindViewHolder(viewHolderOne); mPreference.onBindViewHolder(viewHolderOne);
mPreference.displayIcons(iconSetOne); mPreference.setIcons(iconSetOne);
assertThat(getIcons(containerOne)).hasSize(3); assertThat(getDrawables(containerOne)).hasSize(3);
mPreference.onBindViewHolder(viewHolderTwo); mPreference.onBindViewHolder(viewHolderTwo);
assertThat(getIcons(containerTwo)).hasSize(3); assertThat(getDrawables(containerTwo)).hasSize(3);
mPreference.displayIcons(iconSetTwo); mPreference.setIcons(iconSetTwo);
// The second view is updated and the first view is unaffected. // The second view is updated and the first view is unaffected.
assertThat(getIcons(containerTwo)).hasSize(2); assertThat(getDrawables(containerTwo)).hasSize(2);
assertThat(getIcons(containerOne)).hasSize(3); assertThat(getDrawables(containerOne)).hasSize(3);
} }
@Test @Test
public void setEnabled_afterDisplayIcons_showsEnabledOrDisabledImages() { public void setEnabled_afterSetIcons_showsEnabledOrDisabledImages() {
CircularIconSet<Integer> iconSet = new CircularIconSet<>(ImmutableList.of(1, 2), CircularIconSet<Integer> iconSet = new CircularIconSet<>(ImmutableList.of(1, 2),
ColorDrawable::new); ColorDrawable::new);
bindAndMeasureViewHolder(VIEW_WIDTH); bindAndLayoutViewHolder(VIEW_WIDTH);
mPreference.displayIcons(iconSet); mPreference.setIcons(iconSet);
assertThat(getViews(mContainer)).hasSize(2); assertThat(getViews(mContainer)).hasSize(2);
mPreference.setEnabled(false); mPreference.setEnabled(false);
@@ -294,13 +326,13 @@ public class CircularIconsPreferenceTest {
} }
@Test @Test
public void setEnabled_beforeDisplayIcons_showsEnabledOrDisabledImages() { public void setEnabled_beforeSetIcons_showsEnabledOrDisabledImages() {
CircularIconSet<Integer> iconSet = new CircularIconSet<>(ImmutableList.of(1, 2), CircularIconSet<Integer> iconSet = new CircularIconSet<>(ImmutableList.of(1, 2),
ColorDrawable::new); ColorDrawable::new);
mPreference.setEnabled(false); mPreference.setEnabled(false);
bindAndMeasureViewHolder(VIEW_WIDTH); bindAndLayoutViewHolder(VIEW_WIDTH);
mPreference.displayIcons(iconSet); mPreference.setIcons(iconSet);
assertThat(getViews(mContainer)).hasSize(2); assertThat(getViews(mContainer)).hasSize(2);
assertThat(getViews(mContainer).get(0).getAlpha()).isLessThan(1f); assertThat(getViews(mContainer).get(0).getAlpha()).isLessThan(1f);
@@ -314,7 +346,7 @@ public class CircularIconsPreferenceTest {
return views; return views;
} }
private static List<Drawable> getIcons(ViewGroup container) { private static List<Drawable> getDrawables(ViewGroup container) {
ArrayList<Drawable> drawables = new ArrayList<>(); ArrayList<Drawable> drawables = new ArrayList<>();
for (int i = 0; i < container.getChildCount(); i++) { for (int i = 0; i < container.getChildCount(); i++) {
if (container.getChildAt(i) instanceof ImageView imageView) { if (container.getChildAt(i) instanceof ImageView imageView) {

View File

@@ -20,14 +20,12 @@ import android.content.Context;
import androidx.preference.PreferenceViewHolder; import androidx.preference.PreferenceViewHolder;
import com.google.common.util.concurrent.MoreExecutors;
class TestableCircularIconsPreference extends CircularIconsPreference { class TestableCircularIconsPreference extends CircularIconsPreference {
private PreferenceViewHolder mLastViewHolder; private PreferenceViewHolder mLastViewHolder;
TestableCircularIconsPreference(Context context) { TestableCircularIconsPreference(Context context) {
super(context, MoreExecutors.directExecutor()); super(context);
} }
@Override @Override

View File

@@ -19,6 +19,7 @@ package com.android.settings.notification.modes;
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
import static android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID; import static android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
@@ -52,6 +53,7 @@ import android.view.View;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.preference.PreferenceViewHolder; import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
import com.android.settings.SettingsActivity; import com.android.settings.SettingsActivity;
import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry; import com.android.settingslib.applications.ApplicationsState.AppEntry;
@@ -82,6 +84,7 @@ public final class ZenModeAppsLinkPreferenceControllerTest {
private ZenModeAppsLinkPreferenceController mController; private ZenModeAppsLinkPreferenceController mController;
private CircularIconsPreference mPreference; private CircularIconsPreference mPreference;
private CircularIconsView mIconsView;
private Context mContext; private Context mContext;
@Mock @Mock
@@ -114,6 +117,8 @@ public final class ZenModeAppsLinkPreferenceControllerTest {
// Ensure the preference view is bound & measured (needed to add child ImageViews). // Ensure the preference view is bound & measured (needed to add child ImageViews).
View preferenceView = LayoutInflater.from(mContext).inflate(mPreference.getLayoutResource(), View preferenceView = LayoutInflater.from(mContext).inflate(mPreference.getLayoutResource(),
null); null);
mIconsView = checkNotNull(preferenceView.findViewById(R.id.circles_container));
mIconsView.setUiExecutor(MoreExecutors.directExecutor());
preferenceView.measure(View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY), preferenceView.measure(View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY)); View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY));
PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(preferenceView); PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(preferenceView);
@@ -273,7 +278,7 @@ public final class ZenModeAppsLinkPreferenceControllerTest {
appEntries.add(createAppEntry("test2", mContext.getUserId())); appEntries.add(createAppEntry("test2", mContext.getUserId()));
mController.mAppSessionCallbacks.onRebuildComplete(appEntries); mController.mAppSessionCallbacks.onRebuildComplete(appEntries);
assertThat(mPreference.getLoadedIcons().icons()).hasSize(2); assertThat(mIconsView.getDisplayedIcons().icons()).hasSize(2);
} }
@Test @Test
@@ -313,7 +318,7 @@ public final class ZenModeAppsLinkPreferenceControllerTest {
mController.updateState(mPreference, zenModeWithNone); mController.updateState(mPreference, zenModeWithNone);
assertThat(mPreference.getLoadedIcons().icons()).hasSize(0); assertThat(mIconsView.getDisplayedIcons().icons()).hasSize(0);
verifyNoMoreInteractions(mApplicationsState); verifyNoMoreInteractions(mApplicationsState);
verifyNoMoreInteractions(mSession); verifyNoMoreInteractions(mSession);
@@ -322,7 +327,7 @@ public final class ZenModeAppsLinkPreferenceControllerTest {
verify(mApplicationsState).newSession(any(), any()); verify(mApplicationsState).newSession(any(), any());
verify(mSession).rebuild(any(), any(), anyBoolean()); verify(mSession).rebuild(any(), any(), anyBoolean());
mController.mAppSessionCallbacks.onRebuildComplete(appEntries); mController.mAppSessionCallbacks.onRebuildComplete(appEntries);
assertThat(mPreference.getLoadedIcons().icons()).hasSize(1); assertThat(mIconsView.getDisplayedIcons().icons()).hasSize(1);
} }
@Test @Test
@@ -343,11 +348,11 @@ public final class ZenModeAppsLinkPreferenceControllerTest {
verify(mApplicationsState).newSession(any(), any()); verify(mApplicationsState).newSession(any(), any());
verify(mSession).rebuild(any(), any(), anyBoolean()); verify(mSession).rebuild(any(), any(), anyBoolean());
mController.mAppSessionCallbacks.onRebuildComplete(appEntries); mController.mAppSessionCallbacks.onRebuildComplete(appEntries);
assertThat(mPreference.getLoadedIcons().icons()).hasSize(1); assertThat(mIconsView.getDisplayedIcons().icons()).hasSize(1);
mController.updateState(mPreference, zenModeWithNone); mController.updateState(mPreference, zenModeWithNone);
assertThat(mPreference.getLoadedIcons().icons()).hasSize(0); assertThat(mIconsView.getDisplayedIcons().icons()).hasSize(0);
verify(mSession).deactivateSession(); verify(mSession).deactivateSession();
verifyNoMoreInteractions(mSession); verifyNoMoreInteractions(mSession);
verifyNoMoreInteractions(mApplicationsState); verifyNoMoreInteractions(mApplicationsState);
@@ -356,7 +361,7 @@ public final class ZenModeAppsLinkPreferenceControllerTest {
// updateState()) is ignored. // updateState()) is ignored.
mController.mAppSessionCallbacks.onRebuildComplete(appEntries); mController.mAppSessionCallbacks.onRebuildComplete(appEntries);
assertThat(mPreference.getLoadedIcons().icons()).hasSize(0); assertThat(mIconsView.getDisplayedIcons().icons()).hasSize(0);
} }
@Test @Test

View File

@@ -95,7 +95,7 @@ public final class ZenModeOtherLinkPreferenceControllerTest {
mController.updateState(pref, mode); mController.updateState(pref, mode);
verify(pref).displayIcons(argThat(iconSet -> iconSet.size() == 3)); verify(pref).setIcons(argThat(iconSet -> iconSet.size() == 3));
} }
@Test @Test
@@ -107,7 +107,7 @@ public final class ZenModeOtherLinkPreferenceControllerTest {
mController.updateState(pref, mode); mController.updateState(pref, mode);
verify(pref).displayIcons(argThat(iconSet -> verify(pref).setIcons(argThat(iconSet ->
iconSet.size() == ZenModeSummaryHelper.OTHER_SOUND_CATEGORIES.size())); iconSet.size() == ZenModeSummaryHelper.OTHER_SOUND_CATEGORIES.size()));
} }
} }

View File

@@ -22,6 +22,7 @@ import static android.service.notification.ZenPolicy.PEOPLE_TYPE_CONTACTS;
import static android.service.notification.ZenPolicy.PEOPLE_TYPE_NONE; import static android.service.notification.ZenPolicy.PEOPLE_TYPE_NONE;
import static android.service.notification.ZenPolicy.PEOPLE_TYPE_STARRED; import static android.service.notification.ZenPolicy.PEOPLE_TYPE_STARRED;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
@@ -50,6 +51,7 @@ import android.view.View;
import androidx.preference.PreferenceViewHolder; import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
import com.android.settings.notification.modes.ZenHelperBackend.Contact; import com.android.settings.notification.modes.ZenHelperBackend.Contact;
import com.android.settingslib.notification.ConversationIconFactory; import com.android.settingslib.notification.ConversationIconFactory;
import com.android.settingslib.notification.modes.TestModeBuilder; import com.android.settingslib.notification.modes.TestModeBuilder;
@@ -76,6 +78,7 @@ public final class ZenModePeopleLinkPreferenceControllerTest {
private ZenModePeopleLinkPreferenceController mController; private ZenModePeopleLinkPreferenceController mController;
private CircularIconsPreference mPreference; private CircularIconsPreference mPreference;
private CircularIconsView mIconsView;
@Rule @Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -94,6 +97,8 @@ public final class ZenModePeopleLinkPreferenceControllerTest {
// Ensure the preference view is bound & measured (needed to add icons). // Ensure the preference view is bound & measured (needed to add icons).
View preferenceView = LayoutInflater.from(mContext).inflate(mPreference.getLayoutResource(), View preferenceView = LayoutInflater.from(mContext).inflate(mPreference.getLayoutResource(),
null); null);
mIconsView = checkNotNull(preferenceView.findViewById(R.id.circles_container));
mIconsView.setUiExecutor(MoreExecutors.directExecutor());
preferenceView.measure(View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY), preferenceView.measure(View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY)); View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY));
PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(preferenceView); PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(preferenceView);
@@ -142,9 +147,9 @@ public final class ZenModePeopleLinkPreferenceControllerTest {
mController.updateState(mPreference, mode); mController.updateState(mPreference, mode);
assertThat(mPreference.getLoadedIcons()).isNotNull(); assertThat(mIconsView.getDisplayedIcons()).isNotNull();
assertThat(mPreference.getLoadedIcons().icons()).hasSize(2); assertThat(mIconsView.getDisplayedIcons().icons()).hasSize(2);
assertThat(mPreference.getLoadedIcons().icons().stream() assertThat(mIconsView.getDisplayedIcons().icons().stream()
.map(ColorDrawable.class::cast) .map(ColorDrawable.class::cast)
.map(d -> d.getColor()).toList()) .map(d -> d.getColor()).toList())
.containsExactly(2, 3).inOrder(); .containsExactly(2, 3).inOrder();
@@ -162,9 +167,9 @@ public final class ZenModePeopleLinkPreferenceControllerTest {
mController.updateState(mPreference, mode); mController.updateState(mPreference, mode);
assertThat(mPreference.getLoadedIcons()).isNotNull(); assertThat(mIconsView.getDisplayedIcons()).isNotNull();
assertThat(mPreference.getLoadedIcons().icons()).hasSize(4); assertThat(mIconsView.getDisplayedIcons().icons()).hasSize(4);
assertThat(mPreference.getLoadedIcons().icons().stream() assertThat(mIconsView.getDisplayedIcons().icons().stream()
.map(ColorDrawable.class::cast) .map(ColorDrawable.class::cast)
.map(d -> d.getColor()).toList()) .map(d -> d.getColor()).toList())
.containsExactly(1, 2, 3, 4).inOrder(); .containsExactly(1, 2, 3, 4).inOrder();
@@ -182,8 +187,8 @@ public final class ZenModePeopleLinkPreferenceControllerTest {
mController.updateState(mPreference, mode); mController.updateState(mPreference, mode);
assertThat(mPreference.getLoadedIcons()).isNotNull(); assertThat(mIconsView.getDisplayedIcons()).isNotNull();
assertThat(mPreference.getLoadedIcons().icons()).hasSize(1); assertThat(mIconsView.getDisplayedIcons().icons()).hasSize(1);
verify(mHelperBackend, never()).getContactPhoto(any()); verify(mHelperBackend, never()).getContactPhoto(any());
} }
@@ -201,8 +206,8 @@ public final class ZenModePeopleLinkPreferenceControllerTest {
mController.updateState(mPreference, mode); mController.updateState(mPreference, mode);
assertThat(mPreference.getLoadedIcons()).isNotNull(); assertThat(mIconsView.getDisplayedIcons()).isNotNull();
assertThat(mPreference.getLoadedIcons().icons()).hasSize(3); assertThat(mIconsView.getDisplayedIcons().icons()).hasSize(3);
verify(mConversationIconFactory, times(3)).getConversationDrawable((ShortcutInfo) any(), verify(mConversationIconFactory, times(3)).getConversationDrawable((ShortcutInfo) any(),
any(), anyInt(), anyBoolean()); any(), anyInt(), anyBoolean());
} }