diff --git a/res/drawable/ic_zen_mode_sound_alarms.xml b/res/drawable/ic_zen_mode_sound_alarms.xml new file mode 100644 index 00000000000..b2761ca3a08 --- /dev/null +++ b/res/drawable/ic_zen_mode_sound_alarms.xml @@ -0,0 +1,25 @@ + + + + \ No newline at end of file diff --git a/res/drawable/ic_zen_mode_sound_events.xml b/res/drawable/ic_zen_mode_sound_events.xml new file mode 100644 index 00000000000..15035e4fa2d --- /dev/null +++ b/res/drawable/ic_zen_mode_sound_events.xml @@ -0,0 +1,25 @@ + + + + \ No newline at end of file diff --git a/res/drawable/ic_zen_mode_sound_media.xml b/res/drawable/ic_zen_mode_sound_media.xml new file mode 100644 index 00000000000..f2bedcb9c8e --- /dev/null +++ b/res/drawable/ic_zen_mode_sound_media.xml @@ -0,0 +1,25 @@ + + + + \ No newline at end of file diff --git a/res/drawable/ic_zen_mode_sound_reminders.xml b/res/drawable/ic_zen_mode_sound_reminders.xml new file mode 100644 index 00000000000..cd9490a988f --- /dev/null +++ b/res/drawable/ic_zen_mode_sound_reminders.xml @@ -0,0 +1,25 @@ + + + + \ No newline at end of file diff --git a/res/drawable/ic_zen_mode_sound_system.xml b/res/drawable/ic_zen_mode_sound_system.xml new file mode 100644 index 00000000000..2da05d8f38f --- /dev/null +++ b/res/drawable/ic_zen_mode_sound_system.xml @@ -0,0 +1,25 @@ + + + + \ No newline at end of file diff --git a/res/drawable/preference_circular_icons_plus_item_background.xml b/res/drawable/preference_circular_icons_plus_item_background.xml index 8200a9b12eb..faaa1fd11fe 100644 --- a/res/drawable/preference_circular_icons_plus_item_background.xml +++ b/res/drawable/preference_circular_icons_plus_item_background.xml @@ -22,7 +22,4 @@ android:width="@dimen/zen_mode_circular_icon_diameter" android:height="@dimen/zen_mode_circular_icon_diameter" /> - \ No newline at end of file diff --git a/src/com/android/settings/notification/modes/CircularIconSet.java b/src/com/android/settings/notification/modes/CircularIconSet.java index 18f82d97de0..eb7289aabe1 100644 --- a/src/com/android/settings/notification/modes/CircularIconSet.java +++ b/src/com/android/settings/notification/modes/CircularIconSet.java @@ -20,8 +20,10 @@ import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import com.google.common.base.Equivalence; import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.ListenableFuture; @@ -29,6 +31,7 @@ import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; import java.util.List; +import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -67,8 +70,25 @@ class CircularIconSet { return MoreObjects.toStringHelper(this).add("items", mItems).toString(); } - boolean hasSameItemsAs(CircularIconSet other) { - return other != null && this.mItems.equals(other.mItems); + @SuppressWarnings("unchecked") + boolean hasSameItemsAs(CircularIconSet other, + @Nullable Equivalence equivalence) { + if (other == null) { + return false; + } + if (other == this) { + return true; + } + if (equivalence == null) { + return mItems.equals(other.mItems); + } + // Check that types match before applying equivalence (statically unsafe). :( + Optional> thisItemClass = this.mItems.stream().findFirst().map(T::getClass); + Optional> otherItemClass = other.mItems.stream().findFirst().map(OtherT::getClass); + if (!thisItemClass.equals(otherItemClass)) { + return false; + } + return equivalence.pairwise().equivalent((Iterable) this.mItems, other.mItems); } int size() { diff --git a/src/com/android/settings/notification/modes/CircularIconsPreference.java b/src/com/android/settings/notification/modes/CircularIconsPreference.java index 5e8f720c83c..1ce34766fdc 100644 --- a/src/com/android/settings/notification/modes/CircularIconsPreference.java +++ b/src/com/android/settings/notification/modes/CircularIconsPreference.java @@ -41,6 +41,7 @@ import com.android.settings.R; import com.android.settingslib.RestrictedPreference; import com.android.settingslib.Utils; +import com.google.common.base.Equivalence; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @@ -105,8 +106,12 @@ public class CircularIconsPreference extends RestrictedPreference { } } - void displayIcons(CircularIconSet iconSet) { - if (mIconSet != null && mIconSet.hasSameItemsAs(iconSet)) { + void displayIcons(CircularIconSet iconSet) { + displayIcons(iconSet, null); + } + + void displayIcons(CircularIconSet iconSet, @Nullable Equivalence itemEquivalence) { + if (mIconSet != null && mIconSet.hasSameItemsAs(iconSet, itemEquivalence)) { return; } mIconSet = iconSet; @@ -189,7 +194,6 @@ public class CircularIconsPreference extends RestrictedPreference { } // ... plus 0/1 TextViews at the end. if (extraItems > 0 && !(getLastChild(mIconContainer) instanceof TextView)) { - // TODO: b/346551087 - Check TODO in preference_circular_icons_plus_item_background TextView plusView = (TextView) inflater.inflate( R.layout.preference_circular_icons_plus_item, mIconContainer, false); mIconContainer.addView(plusView); diff --git a/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java index 9bff2bbb558..962e0162310 100644 --- a/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java +++ b/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java @@ -40,6 +40,7 @@ import com.android.settingslib.applications.ApplicationsState.AppEntry; import com.android.settingslib.notification.modes.ZenMode; import com.android.settingslib.notification.modes.ZenModesBackend; +import com.google.common.base.Equivalence; import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableList; import com.google.common.collect.Multimap; @@ -47,6 +48,7 @@ import com.google.common.collect.Multimap; import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import java.util.Objects; /** * Preference with a link and summary about what apps can break through the mode @@ -137,7 +139,8 @@ class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceContr mPreference.setSummary(mSummaryHelper.getAppsSummary(mZenMode, apps)); mPreference.displayIcons(new CircularIconSet<>(apps, - app -> Utils.getBadgedIcon(mContext, app.info))); + app -> Utils.getBadgedIcon(mContext, app.info)), + APP_ENTRY_EQUIVALENCE); } @VisibleForTesting @@ -158,6 +161,19 @@ class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceContr .toList()); } + private static final Equivalence APP_ENTRY_EQUIVALENCE = new Equivalence<>() { + @Override + protected boolean doEquivalent(@NonNull AppEntry a, @NonNull AppEntry b) { + return a.info.uid == b.info.uid + && Objects.equals(a.info.packageName, b.info.packageName); + } + + @Override + protected int doHash(@NonNull AppEntry entry) { + return Objects.hash(entry.info.uid, entry.info.packageName); + } + }; + @VisibleForTesting final ApplicationsState.Callbacks mAppSessionCallbacks = new ApplicationsState.Callbacks() { diff --git a/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceController.java index 206ad42bc7b..d7bd5177aaf 100644 --- a/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceController.java +++ b/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceController.java @@ -29,6 +29,7 @@ import android.service.notification.ZenPolicy; import androidx.annotation.NonNull; import androidx.preference.Preference; +import com.android.settings.R; import com.android.settingslib.notification.modes.ZenMode; import com.google.common.collect.ImmutableList; @@ -41,19 +42,13 @@ import java.util.Map; */ class ZenModeOtherLinkPreferenceController extends AbstractZenModePreferenceController { - // TODO: b/346551087 - Use proper icons private static final ImmutableMap PRIORITIES_TO_ICONS = ImmutableMap.of( - PRIORITY_CATEGORY_ALARMS, - com.android.internal.R.drawable.ic_audio_alarm, - PRIORITY_CATEGORY_MEDIA, - com.android.settings.R.drawable.ic_media_stream, - PRIORITY_CATEGORY_SYSTEM, - com.android.settings.R.drawable.ic_settings_keyboards, - PRIORITY_CATEGORY_REMINDERS, - com.android.internal.R.drawable.ic_popup_reminder, - PRIORITY_CATEGORY_EVENTS, - com.android.internal.R.drawable.ic_zen_mode_type_schedule_calendar); + PRIORITY_CATEGORY_ALARMS, R.drawable.ic_zen_mode_sound_alarms, + PRIORITY_CATEGORY_MEDIA, R.drawable.ic_zen_mode_sound_media, + PRIORITY_CATEGORY_SYSTEM, R.drawable.ic_zen_mode_sound_system, + PRIORITY_CATEGORY_REMINDERS, R.drawable.ic_zen_mode_sound_reminders, + PRIORITY_CATEGORY_EVENTS, R.drawable.ic_zen_mode_sound_events); private final ZenModeSummaryHelper mSummaryHelper; diff --git a/tests/robotests/src/com/android/settings/notification/modes/CircularIconSetTest.java b/tests/robotests/src/com/android/settings/notification/modes/CircularIconSetTest.java index 9e8524395e5..826c9df4cee 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/CircularIconSetTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/CircularIconSetTest.java @@ -27,6 +27,7 @@ import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import com.google.common.base.Equivalence; import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; @@ -54,33 +55,111 @@ public class CircularIconSetTest { } @Test - public void equals_sameItems_true() { + public void hasSameItemsAs_sameItems_true() { CircularIconSet items1 = new CircularIconSet<>(ImmutableList.of(1, 2), num -> new ColorDrawable(Color.BLUE)); CircularIconSet items2 = new CircularIconSet<>(ImmutableList.of(1, 2), num -> new ColorDrawable(Color.GREEN)); - assertThat(items1.hasSameItemsAs(items2)).isTrue(); + assertThat(items1.hasSameItemsAs(items2, null)).isTrue(); } @Test - public void equals_differentTypes_false() { + public void hasSameItemsAs_differentTypes_false() { CircularIconSet items1 = new CircularIconSet<>(ImmutableList.of(1, 2), num -> new ColorDrawable(Color.BLUE)); CircularIconSet items2 = new CircularIconSet<>(ImmutableList.of("a", "b"), str -> new ColorDrawable(Color.GREEN)); - assertThat(items1.hasSameItemsAs(items2)).isFalse(); + assertThat(items1.hasSameItemsAs(items2, null)).isFalse(); } @Test - public void equals_differentItems_false() { + public void hasSameItemsAs_differentItems_false() { CircularIconSet items1 = new CircularIconSet<>(ImmutableList.of("a", "b"), str -> new ColorDrawable(Color.GREEN)); CircularIconSet items2 = new CircularIconSet<>(ImmutableList.of("a", "b", "c"), str -> new ColorDrawable(Color.GREEN)); - assertThat(items1.hasSameItemsAs(items2)).isFalse(); + assertThat(items1.hasSameItemsAs(items2, null)).isFalse(); + } + + private static class WrapperWithoutEquals { + private final T mValue; + private WrapperWithoutEquals(T value) { + mValue = value; + } + } + + @Test + public void hasSameItemsAs_withEquivalence_trueIfEquivalentItems() { + CircularIconSet> items1 = new CircularIconSet<>( + ImmutableList.of( + new WrapperWithoutEquals<>(1), + new WrapperWithoutEquals<>(2)), + unused -> new ColorDrawable(Color.BLACK)); + CircularIconSet> items2 = new CircularIconSet<>( + ImmutableList.of( + new WrapperWithoutEquals<>(1), + new WrapperWithoutEquals<>(2)), + unused -> new ColorDrawable(Color.BLACK)); + CircularIconSet> items3 = new CircularIconSet<>( + ImmutableList.of( + new WrapperWithoutEquals<>(2), + new WrapperWithoutEquals<>(3)), + unused -> new ColorDrawable(Color.BLACK)); + // Needs special equivalence, equals is not enough. + assertThat(items1.hasSameItemsAs(items2, null)).isFalse(); + + Equivalence> equivalence = new Equivalence<>() { + @Override + protected boolean doEquivalent(WrapperWithoutEquals a, + WrapperWithoutEquals b) { + return a.mValue.equals(b.mValue); + } + + @Override + protected int doHash(WrapperWithoutEquals t) { + return t.mValue; + } + }; + + assertThat(items1.hasSameItemsAs(items2, equivalence)).isTrue(); + assertThat(items1.hasSameItemsAs(items3, equivalence)).isFalse(); + } + + @Test + public void hasSameItemsAs_withEquivalenceButDifferentTypes_falseAndNoClassCastExceptions() { + CircularIconSet items1 = new CircularIconSet<>(ImmutableList.of(1, 2), + num -> new ColorDrawable(Color.BLUE)); + CircularIconSet items2 = new CircularIconSet<>(ImmutableList.of("one", "two"), + num -> new ColorDrawable(Color.GREEN)); + + Equivalence stringEquivalence = new Equivalence() { + @Override + protected boolean doEquivalent(String a, String b) { + return a.equals(b); + } + + @Override + protected int doHash(String t) { + return t.hashCode(); + } + }; + Equivalence integerEquivalence = new Equivalence() { + @Override + protected boolean doEquivalent(Integer a, Integer b) { + return a.equals(b); + } + + @Override + protected int doHash(Integer t) { + return t.hashCode(); + } + }; + + assertThat(items1.hasSameItemsAs(items2, stringEquivalence)).isFalse(); + assertThat(items2.hasSameItemsAs(items1, integerEquivalence)).isFalse(); } @Test