Merge "Fix several issues related to CircularIconsPreference" into main
This commit is contained in:
committed by
Android (Google) Code Review
commit
0a5c7530ed
@@ -58,8 +58,8 @@
|
||||
android:lineBreakWordStyle="phrase"
|
||||
android:maxLines="10"/>
|
||||
|
||||
<!-- Circular icons (32dp) will be ImageViews under this LinearLayout -->
|
||||
<LinearLayout
|
||||
<!-- Circular icons (32dp) will be ImageViews under this container -->
|
||||
<com.android.settings.notification.modes.CircularIconsView
|
||||
android:id="@+id/circles_container"
|
||||
android:importantForAccessibility="noHideDescendants"
|
||||
android:orientation="horizontal"
|
||||
|
@@ -61,13 +61,6 @@ abstract class AbstractZenModeHeaderController extends AbstractZenModePreference
|
||||
LayoutPreference preference = checkNotNull(screen.findPreference(getPreferenceKey()));
|
||||
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));
|
||||
ViewGroup.LayoutParams layoutParams = iconView.getLayoutParams();
|
||||
if (layoutParams.width != iconSizePx || layoutParams.height != iconSizePx) {
|
||||
@@ -75,6 +68,14 @@ abstract class AbstractZenModeHeaderController extends AbstractZenModePreference
|
||||
layoutParams.height = iconSizePx;
|
||||
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,
|
||||
|
@@ -16,236 +16,72 @@
|
||||
|
||||
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 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.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.VisibleForTesting;
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settingslib.RestrictedPreference;
|
||||
|
||||
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 {
|
||||
|
||||
private static final float DISABLED_ITEM_ALPHA = 0.3f;
|
||||
|
||||
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;
|
||||
private CircularIconSet<?> mIconSet = CircularIconSet.EMPTY;
|
||||
|
||||
public CircularIconsPreference(Context context) {
|
||||
super(context);
|
||||
init(context);
|
||||
}
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
|
||||
public CircularIconsPreference(Context context, Executor uiExecutor) {
|
||||
this(context);
|
||||
mUiExecutor = uiExecutor;
|
||||
init();
|
||||
}
|
||||
|
||||
public CircularIconsPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public CircularIconsPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public CircularIconsPreference(Context context, AttributeSet attrs, int defStyleAttr,
|
||||
int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
init(context);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init(Context context) {
|
||||
mUiExecutor = context.getMainExecutor();
|
||||
private void init() {
|
||||
setLayoutResource(R.layout.preference_circular_icons);
|
||||
}
|
||||
|
||||
<T> void displayIcons(CircularIconSet<T> iconSet) {
|
||||
displayIcons(iconSet, null);
|
||||
<T> void setIcons(CircularIconSet<T> iconSet) {
|
||||
setIcons(iconSet, null);
|
||||
}
|
||||
|
||||
<T> void displayIcons(CircularIconSet<T> iconSet, @Nullable Equivalence<T> itemEquivalence) {
|
||||
if (mIconSet != null && mIconSet.hasSameItemsAs(iconSet, itemEquivalence)) {
|
||||
<T> void setIcons(CircularIconSet<T> iconSet, @Nullable Equivalence<T> itemEquivalence) {
|
||||
if (mIconSet.hasSameItemsAs(iconSet, itemEquivalence)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mIconSet = iconSet;
|
||||
|
||||
mLoadedIcons = null;
|
||||
if (mPendingLoadIconsFuture != null) {
|
||||
mPendingLoadIconsFuture.cancel(true);
|
||||
mPendingLoadIconsFuture = null;
|
||||
}
|
||||
|
||||
notifyChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(PreferenceViewHolder holder) {
|
||||
super.onBindViewHolder(holder);
|
||||
CircularIconsView iconContainer = checkNotNull(
|
||||
(CircularIconsView) holder.findViewById(R.id.circles_container));
|
||||
|
||||
LinearLayout iconContainer = checkNotNull(
|
||||
(LinearLayout) holder.findViewById(R.id.circles_container));
|
||||
bindIconContainer(iconContainer);
|
||||
}
|
||||
|
||||
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;
|
||||
iconContainer.setVisibility(mIconSet != null && mIconSet.size() == 0 ? GONE : VISIBLE);
|
||||
iconContainer.setEnabled(isEnabled());
|
||||
iconContainer.setIcons(mIconSet);
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -79,6 +79,7 @@ class IconUtil {
|
||||
@Px int innerSizePx = res.getDimensionPixelSize(R.dimen.zen_mode_header_inner_icon_size);
|
||||
|
||||
Drawable base = composeIcons(
|
||||
context.getResources(),
|
||||
background,
|
||||
Utils.getColorAttr(context,
|
||||
com.android.internal.R.attr.materialColorSecondaryContainer),
|
||||
@@ -89,6 +90,7 @@ class IconUtil {
|
||||
innerSizePx);
|
||||
|
||||
Drawable selected = composeIcons(
|
||||
context.getResources(),
|
||||
background,
|
||||
Utils.getColorAttr(context, com.android.internal.R.attr.materialColorPrimary),
|
||||
outerSizePx,
|
||||
@@ -111,6 +113,7 @@ class IconUtil {
|
||||
*/
|
||||
static Drawable makeIconPickerHeader(@NonNull Context context, Drawable icon) {
|
||||
return composeIconCircle(
|
||||
context.getResources(),
|
||||
Utils.getColorAttr(context,
|
||||
com.android.internal.R.attr.materialColorSecondaryContainer),
|
||||
context.getResources().getDimensionPixelSize(
|
||||
@@ -129,6 +132,7 @@ class IconUtil {
|
||||
*/
|
||||
static Drawable makeIconPickerItem(@NonNull Context context, @DrawableRes int iconResId) {
|
||||
return composeIconCircle(
|
||||
context.getResources(),
|
||||
context.getColorStateList(R.color.modes_icon_selectable_background),
|
||||
context.getResources().getDimensionPixelSize(
|
||||
R.dimen.zen_mode_icon_list_item_circle_diameter),
|
||||
@@ -146,6 +150,7 @@ class IconUtil {
|
||||
static Drawable makeCircularIconPreferenceItem(@NonNull Context context,
|
||||
@DrawableRes int iconResId) {
|
||||
return composeIconCircle(
|
||||
context.getResources(),
|
||||
Utils.getColorAttr(context,
|
||||
com.android.internal.R.attr.materialColorSecondaryContainer),
|
||||
context.getResources().getDimensionPixelSize(
|
||||
@@ -166,6 +171,7 @@ class IconUtil {
|
||||
Resources res = context.getResources();
|
||||
if (Strings.isNullOrEmpty(displayName)) {
|
||||
return composeIconCircle(
|
||||
context.getResources(),
|
||||
Utils.getColorAttr(context,
|
||||
com.android.internal.R.attr.materialColorTertiaryContainer),
|
||||
res.getDimensionPixelSize(R.dimen.zen_mode_circular_icon_diameter),
|
||||
@@ -204,17 +210,17 @@ class IconUtil {
|
||||
return new BitmapDrawable(context.getResources(), bitmap);
|
||||
}
|
||||
|
||||
private static Drawable composeIconCircle(ColorStateList circleColor, @Px int circleDiameterPx,
|
||||
Drawable icon, ColorStateList iconColor, @Px int iconSizePx) {
|
||||
return composeIcons(new ShapeDrawable(new OvalShape()), circleColor, circleDiameterPx, icon,
|
||||
iconColor, iconSizePx);
|
||||
private static Drawable composeIconCircle(Resources res, ColorStateList circleColor,
|
||||
@Px int circleDiameterPx, Drawable icon, ColorStateList iconColor, @Px int iconSizePx) {
|
||||
return composeIcons(res, new ShapeDrawable(new OvalShape()), circleColor, circleDiameterPx,
|
||||
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) {
|
||||
Drawable background = checkNotNull(outer.getConstantState()).newDrawable().mutate();
|
||||
Drawable background = checkNotNull(outer.getConstantState()).newDrawable(res).mutate();
|
||||
background.setTintList(outerColor);
|
||||
Drawable foreground = checkNotNull(icon.getConstantState()).newDrawable().mutate();
|
||||
Drawable foreground = checkNotNull(icon.getConstantState()).newDrawable(res).mutate();
|
||||
foreground.setTintList(iconColor);
|
||||
|
||||
LayerDrawable layerDrawable = new LayerDrawable(new Drawable[] { background, foreground });
|
||||
|
@@ -109,7 +109,7 @@ class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceContr
|
||||
|
||||
if (zenMode.getPolicy().getAllowedChannels() == ZenPolicy.CHANNEL_POLICY_NONE) {
|
||||
mPreference.setSummary(R.string.zen_mode_apps_none_apps);
|
||||
mPreference.displayIcons(CircularIconSet.EMPTY);
|
||||
mPreference.setIcons(CircularIconSet.EMPTY);
|
||||
if (mAppSession != null) {
|
||||
mAppSession.deactivateSession();
|
||||
}
|
||||
@@ -151,7 +151,7 @@ class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceContr
|
||||
|
||||
ImmutableList<AppEntry> apps = getAppsBypassingDndSortedByName(allApps);
|
||||
mPreference.setSummary(mSummaryHelper.getAppsSummary(mZenMode, apps));
|
||||
mPreference.displayIcons(new CircularIconSet<>(apps,
|
||||
mPreference.setIcons(new CircularIconSet<>(apps,
|
||||
app -> mAppIconRetriever.apply(app.info)),
|
||||
APP_ENTRY_EQUIVALENCE);
|
||||
}
|
||||
|
@@ -72,7 +72,7 @@ class ZenModeOtherLinkPreferenceController extends AbstractZenModePreferenceCont
|
||||
|
||||
preference.setEnabled(zenMode.isEnabled());
|
||||
preference.setSummary(mSummaryHelper.getOtherSoundCategoriesSummary(zenMode));
|
||||
((CircularIconsPreference) preference).displayIcons(getSoundIcons(zenMode.getPolicy()));
|
||||
((CircularIconsPreference) preference).setIcons(getSoundIcons(zenMode.getPolicy()));
|
||||
}
|
||||
|
||||
private CircularIconSet<Integer> getSoundIcons(ZenPolicy policy) {
|
||||
|
@@ -96,7 +96,7 @@ class ZenModePeopleLinkPreferenceController extends AbstractZenModePreferenceCon
|
||||
|
||||
preference.setEnabled(zenMode.isEnabled());
|
||||
preference.setSummary(mSummaryHelper.getPeopleSummary(zenMode.getPolicy()));
|
||||
((CircularIconsPreference) preference).displayIcons(getPeopleIcons(zenMode.getPolicy()),
|
||||
((CircularIconsPreference) preference).setIcons(getPeopleIcons(zenMode.getPolicy()),
|
||||
PEOPLE_ITEM_EQUIVALENCE);
|
||||
}
|
||||
|
||||
|
@@ -62,8 +62,7 @@ public class CircularIconsPreferenceTest {
|
||||
|
||||
private Context mContext;
|
||||
private CircularIconsPreference mPreference;
|
||||
private PreferenceViewHolder mViewHolder;
|
||||
private ViewGroup mContainer;
|
||||
private CircularIconsView mContainer;
|
||||
|
||||
private int mOneIconWidth;
|
||||
|
||||
@@ -73,179 +72,211 @@ public class CircularIconsPreferenceTest {
|
||||
mContext = RuntimeEnvironment.application;
|
||||
CircularIconSet.sExecutorService = MoreExecutors.newDirectExecutorService();
|
||||
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();
|
||||
mOneIconWidth = res.getDimensionPixelSize(R.dimen.zen_mode_circular_icon_diameter)
|
||||
+ res.getDimensionPixelSize(R.dimen.zen_mode_circular_icon_margin_between);
|
||||
}
|
||||
|
||||
private void bindAndMeasureViewHolder(int viewWidth) {
|
||||
private void bindAndLayoutViewHolder(int viewWidth) {
|
||||
bindViewHolder();
|
||||
measureViewHolder(viewWidth);
|
||||
layoutViewHolder(viewWidth);
|
||||
}
|
||||
|
||||
private void bindViewHolder() {
|
||||
View preferenceView = LayoutInflater.from(mContext).inflate(mPreference.getLayoutResource(),
|
||||
null);
|
||||
mContainer = checkNotNull(preferenceView.findViewById(R.id.circles_container));
|
||||
mViewHolder = PreferenceViewHolder.createInstanceForTests(preferenceView);
|
||||
mPreference.onBindViewHolder(mViewHolder);
|
||||
mContainer.setUiExecutor(MoreExecutors.directExecutor());
|
||||
PreferenceViewHolder viewHolder = PreferenceViewHolder.createInstanceForTests(
|
||||
preferenceView);
|
||||
mPreference.onBindViewHolder(viewHolder);
|
||||
}
|
||||
|
||||
private void measureViewHolder(int viewWidth) {
|
||||
private void layoutViewHolder(int viewWidth) {
|
||||
checkState(mContainer != null, "Call bindViewHolder() first!");
|
||||
mContainer.measure(makeMeasureSpec(viewWidth, View.MeasureSpec.EXACTLY),
|
||||
makeMeasureSpec(1000, View.MeasureSpec.EXACTLY));
|
||||
mContainer.getViewTreeObserver().dispatchOnGlobalLayout();
|
||||
mContainer.layout(0, 0, viewWidth, 1000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void displayIcons_loadsIcons() {
|
||||
public void setIcons_loadsIcons() {
|
||||
CircularIconSet<Integer> iconSet = new CircularIconSet<>(ImmutableList.of(1, 2),
|
||||
ColorDrawable::new);
|
||||
|
||||
bindAndMeasureViewHolder(VIEW_WIDTH);
|
||||
mPreference.displayIcons(iconSet);
|
||||
bindAndLayoutViewHolder(VIEW_WIDTH);
|
||||
mPreference.setIcons(iconSet);
|
||||
|
||||
assertThat(getIcons(mContainer)).hasSize(2);
|
||||
assertThat(((ColorDrawable) getIcons(mContainer).get(0)).getColor()).isEqualTo(1);
|
||||
assertThat(((ColorDrawable) getIcons(mContainer).get(1)).getColor()).isEqualTo(2);
|
||||
assertThat(getDrawables(mContainer)).hasSize(2);
|
||||
assertThat(((ColorDrawable) getDrawables(mContainer).get(0)).getColor()).isEqualTo(1);
|
||||
assertThat(((ColorDrawable) getDrawables(mContainer).get(1)).getColor()).isEqualTo(2);
|
||||
assertThat(getPlusText(mContainer)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void displayIcons_noIcons_hidesRow() {
|
||||
public void setIcons_noIcons_hidesRow() {
|
||||
CircularIconSet<Integer> iconSet = new CircularIconSet<>(ImmutableList.of(),
|
||||
ColorDrawable::new);
|
||||
|
||||
bindAndMeasureViewHolder(VIEW_WIDTH);
|
||||
mPreference.displayIcons(iconSet);
|
||||
bindAndLayoutViewHolder(VIEW_WIDTH);
|
||||
mPreference.setIcons(iconSet);
|
||||
|
||||
assertThat(mContainer.getVisibility()).isEqualTo(View.GONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void displayIcons_exactlyMaxIcons_loadsAllIcons() throws Exception {
|
||||
public void setIcons_exactlyMaxIcons_loadsAllIcons() throws Exception {
|
||||
int width = 300;
|
||||
int fittingCircles = width / mOneIconWidth;
|
||||
CircularIconSet<Integer> iconSet = new CircularIconSet<>(
|
||||
IntStream.range(0, fittingCircles).boxed().toList(),
|
||||
ColorDrawable::new);
|
||||
|
||||
bindAndMeasureViewHolder(width);
|
||||
mPreference.displayIcons(iconSet);
|
||||
bindAndLayoutViewHolder(width);
|
||||
mPreference.setIcons(iconSet);
|
||||
|
||||
assertThat(getIcons(mContainer)).hasSize(fittingCircles);
|
||||
assertThat(getIcons(mContainer)).containsExactlyElementsIn(
|
||||
assertThat(getDrawables(mContainer)).hasSize(fittingCircles);
|
||||
assertThat(getDrawables(mContainer)).containsExactlyElementsIn(
|
||||
Futures.allAsList(iconSet.getIcons()).get()).inOrder();
|
||||
assertThat(getPlusText(mContainer)).isNull();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void displayIcons_tooManyIcons_loadsFirstNAndPlusIcon() throws Exception {
|
||||
public void setIcons_tooManyIcons_loadsFirstNAndPlusIcon() throws Exception {
|
||||
int width = 300;
|
||||
int fittingCircles = width / mOneIconWidth;
|
||||
CircularIconSet<Integer> iconSet = new CircularIconSet<>(
|
||||
IntStream.range(0, fittingCircles + 5).boxed().toList(),
|
||||
ColorDrawable::new);
|
||||
|
||||
bindAndMeasureViewHolder(width);
|
||||
mPreference.displayIcons(iconSet);
|
||||
bindAndLayoutViewHolder(width);
|
||||
mPreference.setIcons(iconSet);
|
||||
|
||||
// N-1 icons, plus (+6) text.
|
||||
assertThat(getIcons(mContainer)).hasSize(fittingCircles - 1);
|
||||
assertThat(getIcons(mContainer)).containsExactlyElementsIn(
|
||||
assertThat(getDrawables(mContainer)).hasSize(fittingCircles - 1);
|
||||
assertThat(getDrawables(mContainer)).containsExactlyElementsIn(
|
||||
Futures.allAsList(iconSet.getIcons(fittingCircles - 1)).get())
|
||||
.inOrder();
|
||||
assertThat(getPlusText(mContainer)).isEqualTo("+6");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void displayIcons_teenyTinySpace_showsPlusIcon_noCrash() {
|
||||
public void setIcons_teenyTinySpace_showsPlusIcon_noCrash() {
|
||||
CircularIconSet<Integer> iconSet = new CircularIconSet<>(ImmutableList.of(1, 2),
|
||||
ColorDrawable::new);
|
||||
|
||||
bindAndMeasureViewHolder(1);
|
||||
mPreference.displayIcons(iconSet);
|
||||
bindAndLayoutViewHolder(1);
|
||||
mPreference.setIcons(iconSet);
|
||||
|
||||
assertThat(getIcons(mContainer)).isEmpty();
|
||||
assertThat(getDrawables(mContainer)).isEmpty();
|
||||
assertThat(getPlusText(mContainer)).isEqualTo("+2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void displayIcons_beforeBind_loadsIconsOnBindAndMeasure() {
|
||||
public void setIcons_beforeBind_loadsIconsOnBindAndMeasure() {
|
||||
CircularIconSet<Integer> iconSet = new CircularIconSet<>(ImmutableList.of(1, 2, 3),
|
||||
ColorDrawable::new);
|
||||
|
||||
mPreference.displayIcons(iconSet);
|
||||
assertThat(mPreference.getLoadedIcons()).isNull(); // Hold...
|
||||
mPreference.setIcons(iconSet);
|
||||
assertThat(mContainer).isNull(); // Hold...
|
||||
|
||||
bindViewHolder();
|
||||
assertThat(mPreference.getLoadedIcons()).isNull(); // Hooooold...
|
||||
assertThat(getDrawables(mContainer)).hasSize(0); // Hooooold...
|
||||
|
||||
measureViewHolder(VIEW_WIDTH);
|
||||
assertThat(mPreference.getLoadedIcons().icons()).hasSize(3);
|
||||
assertThat(getIcons(mContainer)).hasSize(3);
|
||||
layoutViewHolder(VIEW_WIDTH);
|
||||
assertThat(getDrawables(mContainer)).hasSize(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void displayIcons_beforeMeasure_loadsIconsOnMeasure() {
|
||||
public void setIcons_beforeMeasure_loadsIconsOnMeasure() {
|
||||
CircularIconSet<Integer> iconSet = new CircularIconSet<>(ImmutableList.of(1, 2, 3),
|
||||
ColorDrawable::new);
|
||||
bindViewHolder();
|
||||
|
||||
mPreference.displayIcons(iconSet);
|
||||
assertThat(mPreference.getLoadedIcons()).isNull();
|
||||
mPreference.setIcons(iconSet);
|
||||
assertThat(getDrawables(mContainer)).hasSize(0);
|
||||
|
||||
measureViewHolder(VIEW_WIDTH);
|
||||
assertThat(getIcons(mContainer)).hasSize(3);
|
||||
layoutViewHolder(VIEW_WIDTH);
|
||||
assertThat(getDrawables(mContainer)).hasSize(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void displayIcons_calledAgain_reloadsIcons() {
|
||||
public void setIcons_calledAgain_reloadsIcons() {
|
||||
CircularIconSet<Integer> threeIcons = new CircularIconSet<>(ImmutableList.of(1, 2, 3),
|
||||
ColorDrawable::new);
|
||||
CircularIconSet<Integer> twoIcons = new CircularIconSet<>(ImmutableList.of(1, 2),
|
||||
ColorDrawable::new);
|
||||
CircularIconSet<Integer> fourIcons = new CircularIconSet<>(ImmutableList.of(1, 2, 3, 4),
|
||||
ColorDrawable::new);
|
||||
bindAndMeasureViewHolder(VIEW_WIDTH);
|
||||
bindAndLayoutViewHolder(VIEW_WIDTH);
|
||||
|
||||
mPreference.displayIcons(threeIcons);
|
||||
assertThat(mPreference.getLoadedIcons()).isNotNull();
|
||||
assertThat(getIcons(mContainer)).hasSize(3);
|
||||
mPreference.setIcons(threeIcons);
|
||||
assertThat(getDrawables(mContainer)).hasSize(3);
|
||||
|
||||
mPreference.displayIcons(twoIcons);
|
||||
assertThat(mPreference.getLoadedIcons()).isNotNull();
|
||||
assertThat(getIcons(mContainer)).hasSize(2);
|
||||
mPreference.setIcons(twoIcons);
|
||||
assertThat(getDrawables(mContainer)).hasSize(2);
|
||||
|
||||
mPreference.displayIcons(fourIcons);
|
||||
assertThat(mPreference.getLoadedIcons()).isNotNull();
|
||||
assertThat(getIcons(mContainer)).hasSize(4);
|
||||
mPreference.setIcons(fourIcons);
|
||||
assertThat(getDrawables(mContainer)).hasSize(4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void displayIcons_sameSet_doesNotReloadIcons() {
|
||||
public void setIcons_sameSet_doesNotReloadIcons() {
|
||||
CircularIconSet<Integer> one = new CircularIconSet<>(ImmutableList.of(1, 2, 3),
|
||||
ColorDrawable::new);
|
||||
CircularIconSet<Integer> same = Mockito.spy(new CircularIconSet<>(ImmutableList.of(1, 2, 3),
|
||||
ColorDrawable::new));
|
||||
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
|
||||
public void onBindViewHolder_withDifferentView_reloadsIconsCorrectly() {
|
||||
View preferenceViewOne = LayoutInflater.from(mContext).inflate(
|
||||
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(
|
||||
preferenceViewOne);
|
||||
containerOne.measure(makeMeasureSpec(1000, View.MeasureSpec.EXACTLY),
|
||||
@@ -253,7 +284,8 @@ public class CircularIconsPreferenceTest {
|
||||
|
||||
View preferenceViewTwo = LayoutInflater.from(mContext).inflate(
|
||||
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(
|
||||
preferenceViewTwo);
|
||||
containerTwo.measure(makeMeasureSpec(1000, View.MeasureSpec.EXACTLY),
|
||||
@@ -265,25 +297,25 @@ public class CircularIconsPreferenceTest {
|
||||
ColorDrawable::new);
|
||||
|
||||
mPreference.onBindViewHolder(viewHolderOne);
|
||||
mPreference.displayIcons(iconSetOne);
|
||||
assertThat(getIcons(containerOne)).hasSize(3);
|
||||
mPreference.setIcons(iconSetOne);
|
||||
assertThat(getDrawables(containerOne)).hasSize(3);
|
||||
|
||||
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.
|
||||
assertThat(getIcons(containerTwo)).hasSize(2);
|
||||
assertThat(getIcons(containerOne)).hasSize(3);
|
||||
assertThat(getDrawables(containerTwo)).hasSize(2);
|
||||
assertThat(getDrawables(containerOne)).hasSize(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setEnabled_afterDisplayIcons_showsEnabledOrDisabledImages() {
|
||||
public void setEnabled_afterSetIcons_showsEnabledOrDisabledImages() {
|
||||
CircularIconSet<Integer> iconSet = new CircularIconSet<>(ImmutableList.of(1, 2),
|
||||
ColorDrawable::new);
|
||||
bindAndMeasureViewHolder(VIEW_WIDTH);
|
||||
mPreference.displayIcons(iconSet);
|
||||
bindAndLayoutViewHolder(VIEW_WIDTH);
|
||||
mPreference.setIcons(iconSet);
|
||||
assertThat(getViews(mContainer)).hasSize(2);
|
||||
|
||||
mPreference.setEnabled(false);
|
||||
@@ -294,13 +326,13 @@ public class CircularIconsPreferenceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setEnabled_beforeDisplayIcons_showsEnabledOrDisabledImages() {
|
||||
public void setEnabled_beforeSetIcons_showsEnabledOrDisabledImages() {
|
||||
CircularIconSet<Integer> iconSet = new CircularIconSet<>(ImmutableList.of(1, 2),
|
||||
ColorDrawable::new);
|
||||
|
||||
mPreference.setEnabled(false);
|
||||
bindAndMeasureViewHolder(VIEW_WIDTH);
|
||||
mPreference.displayIcons(iconSet);
|
||||
bindAndLayoutViewHolder(VIEW_WIDTH);
|
||||
mPreference.setIcons(iconSet);
|
||||
|
||||
assertThat(getViews(mContainer)).hasSize(2);
|
||||
assertThat(getViews(mContainer).get(0).getAlpha()).isLessThan(1f);
|
||||
@@ -314,7 +346,7 @@ public class CircularIconsPreferenceTest {
|
||||
return views;
|
||||
}
|
||||
|
||||
private static List<Drawable> getIcons(ViewGroup container) {
|
||||
private static List<Drawable> getDrawables(ViewGroup container) {
|
||||
ArrayList<Drawable> drawables = new ArrayList<>();
|
||||
for (int i = 0; i < container.getChildCount(); i++) {
|
||||
if (container.getChildAt(i) instanceof ImageView imageView) {
|
||||
|
@@ -20,14 +20,12 @@ import android.content.Context;
|
||||
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
|
||||
class TestableCircularIconsPreference extends CircularIconsPreference {
|
||||
|
||||
private PreferenceViewHolder mLastViewHolder;
|
||||
|
||||
TestableCircularIconsPreference(Context context) {
|
||||
super(context, MoreExecutors.directExecutor());
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -19,6 +19,7 @@ package com.android.settings.notification.modes;
|
||||
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
|
||||
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 org.mockito.ArgumentMatchers.any;
|
||||
@@ -52,6 +53,7 @@ import android.view.View;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsActivity;
|
||||
import com.android.settingslib.applications.ApplicationsState;
|
||||
import com.android.settingslib.applications.ApplicationsState.AppEntry;
|
||||
@@ -82,6 +84,7 @@ public final class ZenModeAppsLinkPreferenceControllerTest {
|
||||
|
||||
private ZenModeAppsLinkPreferenceController mController;
|
||||
private CircularIconsPreference mPreference;
|
||||
private CircularIconsView mIconsView;
|
||||
|
||||
private Context mContext;
|
||||
@Mock
|
||||
@@ -114,6 +117,8 @@ public final class ZenModeAppsLinkPreferenceControllerTest {
|
||||
// Ensure the preference view is bound & measured (needed to add child ImageViews).
|
||||
View preferenceView = LayoutInflater.from(mContext).inflate(mPreference.getLayoutResource(),
|
||||
null);
|
||||
mIconsView = checkNotNull(preferenceView.findViewById(R.id.circles_container));
|
||||
mIconsView.setUiExecutor(MoreExecutors.directExecutor());
|
||||
preferenceView.measure(View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY),
|
||||
View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY));
|
||||
PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(preferenceView);
|
||||
@@ -273,7 +278,7 @@ public final class ZenModeAppsLinkPreferenceControllerTest {
|
||||
appEntries.add(createAppEntry("test2", mContext.getUserId()));
|
||||
mController.mAppSessionCallbacks.onRebuildComplete(appEntries);
|
||||
|
||||
assertThat(mPreference.getLoadedIcons().icons()).hasSize(2);
|
||||
assertThat(mIconsView.getDisplayedIcons().icons()).hasSize(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -313,7 +318,7 @@ public final class ZenModeAppsLinkPreferenceControllerTest {
|
||||
|
||||
mController.updateState(mPreference, zenModeWithNone);
|
||||
|
||||
assertThat(mPreference.getLoadedIcons().icons()).hasSize(0);
|
||||
assertThat(mIconsView.getDisplayedIcons().icons()).hasSize(0);
|
||||
verifyNoMoreInteractions(mApplicationsState);
|
||||
verifyNoMoreInteractions(mSession);
|
||||
|
||||
@@ -322,7 +327,7 @@ public final class ZenModeAppsLinkPreferenceControllerTest {
|
||||
verify(mApplicationsState).newSession(any(), any());
|
||||
verify(mSession).rebuild(any(), any(), anyBoolean());
|
||||
mController.mAppSessionCallbacks.onRebuildComplete(appEntries);
|
||||
assertThat(mPreference.getLoadedIcons().icons()).hasSize(1);
|
||||
assertThat(mIconsView.getDisplayedIcons().icons()).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -343,11 +348,11 @@ public final class ZenModeAppsLinkPreferenceControllerTest {
|
||||
verify(mApplicationsState).newSession(any(), any());
|
||||
verify(mSession).rebuild(any(), any(), anyBoolean());
|
||||
mController.mAppSessionCallbacks.onRebuildComplete(appEntries);
|
||||
assertThat(mPreference.getLoadedIcons().icons()).hasSize(1);
|
||||
assertThat(mIconsView.getDisplayedIcons().icons()).hasSize(1);
|
||||
|
||||
mController.updateState(mPreference, zenModeWithNone);
|
||||
|
||||
assertThat(mPreference.getLoadedIcons().icons()).hasSize(0);
|
||||
assertThat(mIconsView.getDisplayedIcons().icons()).hasSize(0);
|
||||
verify(mSession).deactivateSession();
|
||||
verifyNoMoreInteractions(mSession);
|
||||
verifyNoMoreInteractions(mApplicationsState);
|
||||
@@ -356,7 +361,7 @@ public final class ZenModeAppsLinkPreferenceControllerTest {
|
||||
// updateState()) is ignored.
|
||||
mController.mAppSessionCallbacks.onRebuildComplete(appEntries);
|
||||
|
||||
assertThat(mPreference.getLoadedIcons().icons()).hasSize(0);
|
||||
assertThat(mIconsView.getDisplayedIcons().icons()).hasSize(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@@ -95,7 +95,7 @@ public final class ZenModeOtherLinkPreferenceControllerTest {
|
||||
|
||||
mController.updateState(pref, mode);
|
||||
|
||||
verify(pref).displayIcons(argThat(iconSet -> iconSet.size() == 3));
|
||||
verify(pref).setIcons(argThat(iconSet -> iconSet.size() == 3));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -107,7 +107,7 @@ public final class ZenModeOtherLinkPreferenceControllerTest {
|
||||
|
||||
mController.updateState(pref, mode);
|
||||
|
||||
verify(pref).displayIcons(argThat(iconSet ->
|
||||
verify(pref).setIcons(argThat(iconSet ->
|
||||
iconSet.size() == ZenModeSummaryHelper.OTHER_SOUND_CATEGORIES.size()));
|
||||
}
|
||||
}
|
@@ -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_STARRED;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
@@ -50,6 +51,7 @@ import android.view.View;
|
||||
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.notification.modes.ZenHelperBackend.Contact;
|
||||
import com.android.settingslib.notification.ConversationIconFactory;
|
||||
import com.android.settingslib.notification.modes.TestModeBuilder;
|
||||
@@ -76,6 +78,7 @@ public final class ZenModePeopleLinkPreferenceControllerTest {
|
||||
|
||||
private ZenModePeopleLinkPreferenceController mController;
|
||||
private CircularIconsPreference mPreference;
|
||||
private CircularIconsView mIconsView;
|
||||
|
||||
@Rule
|
||||
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).
|
||||
View preferenceView = LayoutInflater.from(mContext).inflate(mPreference.getLayoutResource(),
|
||||
null);
|
||||
mIconsView = checkNotNull(preferenceView.findViewById(R.id.circles_container));
|
||||
mIconsView.setUiExecutor(MoreExecutors.directExecutor());
|
||||
preferenceView.measure(View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY),
|
||||
View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY));
|
||||
PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(preferenceView);
|
||||
@@ -142,9 +147,9 @@ public final class ZenModePeopleLinkPreferenceControllerTest {
|
||||
|
||||
mController.updateState(mPreference, mode);
|
||||
|
||||
assertThat(mPreference.getLoadedIcons()).isNotNull();
|
||||
assertThat(mPreference.getLoadedIcons().icons()).hasSize(2);
|
||||
assertThat(mPreference.getLoadedIcons().icons().stream()
|
||||
assertThat(mIconsView.getDisplayedIcons()).isNotNull();
|
||||
assertThat(mIconsView.getDisplayedIcons().icons()).hasSize(2);
|
||||
assertThat(mIconsView.getDisplayedIcons().icons().stream()
|
||||
.map(ColorDrawable.class::cast)
|
||||
.map(d -> d.getColor()).toList())
|
||||
.containsExactly(2, 3).inOrder();
|
||||
@@ -162,9 +167,9 @@ public final class ZenModePeopleLinkPreferenceControllerTest {
|
||||
|
||||
mController.updateState(mPreference, mode);
|
||||
|
||||
assertThat(mPreference.getLoadedIcons()).isNotNull();
|
||||
assertThat(mPreference.getLoadedIcons().icons()).hasSize(4);
|
||||
assertThat(mPreference.getLoadedIcons().icons().stream()
|
||||
assertThat(mIconsView.getDisplayedIcons()).isNotNull();
|
||||
assertThat(mIconsView.getDisplayedIcons().icons()).hasSize(4);
|
||||
assertThat(mIconsView.getDisplayedIcons().icons().stream()
|
||||
.map(ColorDrawable.class::cast)
|
||||
.map(d -> d.getColor()).toList())
|
||||
.containsExactly(1, 2, 3, 4).inOrder();
|
||||
@@ -182,8 +187,8 @@ public final class ZenModePeopleLinkPreferenceControllerTest {
|
||||
|
||||
mController.updateState(mPreference, mode);
|
||||
|
||||
assertThat(mPreference.getLoadedIcons()).isNotNull();
|
||||
assertThat(mPreference.getLoadedIcons().icons()).hasSize(1);
|
||||
assertThat(mIconsView.getDisplayedIcons()).isNotNull();
|
||||
assertThat(mIconsView.getDisplayedIcons().icons()).hasSize(1);
|
||||
verify(mHelperBackend, never()).getContactPhoto(any());
|
||||
}
|
||||
|
||||
@@ -201,8 +206,8 @@ public final class ZenModePeopleLinkPreferenceControllerTest {
|
||||
|
||||
mController.updateState(mPreference, mode);
|
||||
|
||||
assertThat(mPreference.getLoadedIcons()).isNotNull();
|
||||
assertThat(mPreference.getLoadedIcons().icons()).hasSize(3);
|
||||
assertThat(mIconsView.getDisplayedIcons()).isNotNull();
|
||||
assertThat(mIconsView.getDisplayedIcons().icons()).hasSize(3);
|
||||
verify(mConversationIconFactory, times(3)).getConversationDrawable((ShortcutInfo) any(),
|
||||
any(), anyInt(), anyBoolean());
|
||||
}
|
||||
|
Reference in New Issue
Block a user