Merge changes Iceadf4a1,If2b6b06b into main

* changes:
  Final (?) touches to people/apps/sound circles
  Show icons for allowed contacts
This commit is contained in:
Matías Hernández
2024-07-22 17:27:01 +00:00
committed by Android (Google) Code Review
23 changed files with 821 additions and 123 deletions

View File

@@ -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<T> {
return MoreObjects.toStringHelper(this).add("items", mItems).toString();
}
boolean hasSameItemsAs(CircularIconSet<?> other) {
return other != null && this.mItems.equals(other.mItems);
@SuppressWarnings("unchecked")
<OtherT> boolean hasSameItemsAs(CircularIconSet<OtherT> other,
@Nullable Equivalence<OtherT> 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<Class<?>> thisItemClass = this.mItems.stream().findFirst().map(T::getClass);
Optional<Class<?>> otherItemClass = other.mItems.stream().findFirst().map(OtherT::getClass);
if (!thisItemClass.equals(otherItemClass)) {
return false;
}
return equivalence.pairwise().equivalent((Iterable<OtherT>) this.mItems, other.mItems);
}
int size() {

View File

@@ -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)) {
<T> void displayIcons(CircularIconSet<T> iconSet) {
displayIcons(iconSet, null);
}
<T> void displayIcons(CircularIconSet<T> iconSet, @Nullable Equivalence<T> 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);

View File

@@ -20,6 +20,13 @@ import static com.google.common.base.Preconditions.checkNotNull;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.ShapeDrawable;
@@ -29,11 +36,16 @@ import android.view.Gravity;
import androidx.annotation.AttrRes;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Px;
import com.android.settings.R;
import com.android.settingslib.Utils;
import com.google.common.base.Strings;
import java.util.Locale;
class IconUtil {
static Drawable applyNormalTint(@NonNull Context context, @NonNull Drawable icon) {
@@ -87,9 +99,10 @@ class IconUtil {
/**
* Returns a variant of the supplied icon to be used in a {@link CircularIconsPreference}. The
* inner icon is 20x20 dp and it's contained in a circle of diameter 32dp, and is tinted
* with the "material secondary container" color combination.
* with the "material secondary" color combination.
*/
static Drawable makeSoundIcon(@NonNull Context context, @DrawableRes int iconResId) {
static Drawable makeCircularIconPreferenceItem(@NonNull Context context,
@DrawableRes int iconResId) {
return composeIconCircle(
Utils.getColorAttr(context,
com.android.internal.R.attr.materialColorSecondaryContainer),
@@ -102,6 +115,53 @@ class IconUtil {
R.dimen.zen_mode_circular_icon_inner_icon_size));
}
/**
* Returns an icon representing a contact that doesn't have an associated photo, to be used in
* a {@link CircularIconsPreference}, tinted with the "material tertiary". If the contact's
* display name is not empty, it's the contact's monogram, otherwise it's a generic icon.
*/
static Drawable makeContactMonogram(@NonNull Context context, @Nullable String displayName) {
Resources res = context.getResources();
if (Strings.isNullOrEmpty(displayName)) {
return composeIconCircle(
Utils.getColorAttr(context,
com.android.internal.R.attr.materialColorTertiaryContainer),
res.getDimensionPixelSize(R.dimen.zen_mode_circular_icon_diameter),
checkNotNull(context.getDrawable(R.drawable.ic_zen_mode_generic_contact)),
Utils.getColorAttr(context,
com.android.internal.R.attr.materialColorOnTertiaryContainer),
res.getDimensionPixelSize(R.dimen.zen_mode_circular_icon_inner_icon_size));
}
float diameter = res.getDimensionPixelSize(R.dimen.zen_mode_circular_icon_diameter);
Bitmap bitmap = Bitmap.createBitmap((int) diameter, (int) diameter,
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint circlePaint = new Paint();
circlePaint.setColor(Utils.getColorAttrDefaultColor(context,
com.android.internal.R.attr.materialColorTertiaryContainer));
circlePaint.setFlags(Paint.ANTI_ALIAS_FLAG);
canvas.drawCircle(diameter / 2f, diameter / 2f, diameter / 2f, circlePaint);
Paint textPaint = new Paint();
textPaint.setColor(Utils.getColorAttrDefaultColor(context,
com.android.internal.R.attr.materialColorOnTertiaryContainer));
textPaint.setTypeface(Typeface.create("sans-serif", Typeface.NORMAL));
textPaint.setTextAlign(Paint.Align.LEFT);
textPaint.setTextSize(res.getDimensionPixelSize(R.dimen.zen_mode_circular_icon_text_size));
String text = displayName.substring(0, 1).toUpperCase(Locale.getDefault());
Rect textRect = new Rect();
textPaint.getTextBounds(text, 0, text.length(), textRect);
float textX = diameter / 2f - textRect.width() / 2f - textRect.left;
float textY = diameter / 2f + textRect.height() / 2f - textRect.bottom;
canvas.drawText(text, textX, textY, textPaint);
return new BitmapDrawable(context.getResources(), bitmap);
}
private static Drawable composeIconCircle(ColorStateList circleColor, @Px int circleDiameterPx,
Drawable icon, ColorStateList iconColor, @Px int iconSizePx) {
ShapeDrawable background = new ShapeDrawable(new OvalShape());

View File

@@ -21,15 +21,22 @@ import android.app.INotificationManager;
import android.content.Context;
import android.content.pm.ParceledListSlice;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.ServiceManager;
import android.provider.ContactsContract;
import android.service.notification.ConversationChannelWrapper;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.annotation.NonNull;
import androidx.core.graphics.drawable.RoundedBitmapDrawable;
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
import com.android.settings.R;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
@@ -75,45 +82,99 @@ class ZenHelperBackend {
}
@SuppressWarnings("unchecked")
ParceledListSlice<ConversationChannelWrapper> getConversations(boolean onlyImportant) {
ImmutableList<ConversationChannelWrapper> getImportantConversations() {
try {
return mInm.getConversations(onlyImportant);
ImmutableList.Builder<ConversationChannelWrapper> list = new ImmutableList.Builder<>();
ParceledListSlice<ConversationChannelWrapper> parceledList = mInm.getConversations(
/* onlyImportant= */ true);
if (parceledList != null) {
for (ConversationChannelWrapper conversation : parceledList.getList()) {
if (!conversation.getNotificationChannel().isDemoted()) {
list.add(conversation);
}
}
}
return list.build();
} catch (Exception e) {
Log.w(TAG, "Error calling NoMan", e);
return ParceledListSlice.emptyList();
return ImmutableList.of();
}
}
List<String> getStarredContacts() {
record Contact(long id, @Nullable String displayName, @Nullable Uri photoUri) { }
ImmutableList<Contact> getAllContacts() {
try (Cursor cursor = queryAllContactsData()) {
return getContactsFromCursor(cursor);
}
}
ImmutableList<Contact> getStarredContacts() {
try (Cursor cursor = queryStarredContactsData()) {
return getStarredContacts(cursor);
return getContactsFromCursor(cursor);
}
}
@VisibleForTesting
List<String> getStarredContacts(Cursor cursor) {
List<String> starredContacts = new ArrayList<>();
private ImmutableList<Contact> getContactsFromCursor(Cursor cursor) {
ImmutableList.Builder<Contact> list = new ImmutableList.Builder<>();
if (cursor != null && cursor.moveToFirst()) {
do {
String contact = cursor.getString(0);
starredContacts.add(contact != null ? contact :
mContext.getString(R.string.zen_mode_starred_contacts_empty_name));
long id = cursor.getLong(0);
String name = Strings.emptyToNull(cursor.getString(1));
String photoUriStr = cursor.getString(2);
Uri photoUri = !Strings.isNullOrEmpty(photoUriStr) ? Uri.parse(photoUriStr) : null;
list.add(new Contact(id, name, photoUri));
} while (cursor.moveToNext());
}
return starredContacts;
return list.build();
}
int getAllContactsCount() {
try (Cursor cursor = queryAllContactsData()) {
return cursor != null ? cursor.getCount() : 0;
}
}
private static final String[] CONTACTS_PROJECTION = new String[] {
ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME_PRIMARY,
ContactsContract.Contacts.PHOTO_THUMBNAIL_URI
};
private Cursor queryStarredContactsData() {
return mContext.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,
new String[]{ContactsContract.Contacts.DISPLAY_NAME_PRIMARY},
ContactsContract.Data.STARRED + "=1", null,
ContactsContract.Data.TIMES_CONTACTED);
return mContext.getContentResolver().query(
ContactsContract.Contacts.CONTENT_URI,
CONTACTS_PROJECTION,
/* selection= */ ContactsContract.Data.STARRED + "=1", /* selectionArgs= */ null,
/* sortOrder= */ ContactsContract.Contacts.DISPLAY_NAME_PRIMARY);
}
Cursor queryAllContactsData() {
return mContext.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,
new String[]{ContactsContract.Contacts.DISPLAY_NAME_PRIMARY},
null, null, null);
private Cursor queryAllContactsData() {
return mContext.getContentResolver().query(
ContactsContract.Contacts.CONTENT_URI,
CONTACTS_PROJECTION,
/* selection= */ null, /* selectionArgs= */ null,
/* sortOrder= */ ContactsContract.Contacts.DISPLAY_NAME_PRIMARY);
}
@NonNull
Drawable getContactPhoto(Contact contact) {
if (contact.photoUri != null) {
try (InputStream is = mContext.getContentResolver().openInputStream(contact.photoUri)) {
if (is != null) {
RoundedBitmapDrawable rbd = RoundedBitmapDrawableFactory.create(
mContext.getResources(), is);
if (rbd != null && rbd.getBitmap() != null) {
rbd.setCircular(true);
return rbd;
}
}
} catch (IOException e) {
Log.w(TAG, "Couldn't load photo for " + contact, e);
}
}
// Fall back to a monogram if no picture.
return IconUtil.makeContactMonogram(mContext, contact.displayName);
}
}

View File

@@ -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<AppEntry> 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() {

View File

@@ -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</* @PriorityCategory */ Integer, /* @DrawableRes */ Integer>
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;
@@ -87,6 +82,6 @@ class ZenModeOtherLinkPreferenceController extends AbstractZenModePreferenceCont
}
}
return new CircularIconSet<>(icons.build(),
iconResId -> IconUtil.makeSoundIcon(mContext, iconResId));
iconResId -> IconUtil.makeCircularIconPreferenceItem(mContext, iconResId));
}
}

View File

@@ -17,25 +17,67 @@
package com.android.settings.notification.modes;
import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_ANYONE;
import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_NONE;
import static android.service.notification.ZenPolicy.PEOPLE_TYPE_ANYONE;
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 android.service.notification.ZenPolicy.STATE_ALLOW;
import android.content.Context;
import android.content.pm.LauncherApps;
import android.graphics.drawable.Drawable;
import android.service.notification.ConversationChannelWrapper;
import android.service.notification.ZenPolicy;
import android.service.notification.ZenPolicy.ConversationSenders;
import android.service.notification.ZenPolicy.PeopleType;
import android.util.IconDrawableFactory;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.annotation.WorkerThread;
import androidx.preference.Preference;
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.ZenMode;
import com.google.common.collect.ImmutableList;
import java.util.function.Function;
/**
* Preference with a link and summary about what calls and messages can break through the mode
* Preference with a link and summary about what calls and messages can break through the mode,
* and icons representing those people.
*/
class ZenModePeopleLinkPreferenceController extends AbstractZenModePreferenceController {
private final ZenModeSummaryHelper mSummaryHelper;
private final ZenHelperBackend mHelperBackend;
private final ConversationIconFactory mConversationIconFactory;
public ZenModePeopleLinkPreferenceController(Context context, String key,
ZenModePeopleLinkPreferenceController(Context context, String key,
ZenHelperBackend helperBackend) {
this(context, key, helperBackend,
new ConversationIconFactory(context,
context.getSystemService(LauncherApps.class),
context.getPackageManager(),
IconDrawableFactory.newInstance(context, false),
context.getResources().getDimensionPixelSize(
R.dimen.zen_mode_circular_icon_diameter)));
}
@VisibleForTesting
ZenModePeopleLinkPreferenceController(Context context, String key,
ZenHelperBackend helperBackend, ConversationIconFactory conversationIconFactory) {
super(context, key);
mSummaryHelper = new ZenModeSummaryHelper(mContext, helperBackend);
mHelperBackend = helperBackend;
mConversationIconFactory = conversationIconFactory;
}
@Override
@@ -50,8 +92,104 @@ class ZenModePeopleLinkPreferenceController extends AbstractZenModePreferenceCon
ZenSubSettingLauncher.forModeFragment(mContext, ZenModePeopleFragment.class,
zenMode.getId(), 0).toIntent());
preference.setSummary(mSummaryHelper.getPeopleSummary(zenMode));
// TODO: b/346551087 - Show people icons
((CircularIconsPreference) preference).displayIcons(CircularIconSet.EMPTY);
preference.setSummary(mSummaryHelper.getPeopleSummary(zenMode.getPolicy()));
((CircularIconsPreference) preference).displayIcons(getPeopleIcons(zenMode.getPolicy()));
}
// Represents "Either<Contact, ConversationChannelWrapper>".
record PeopleItem(@Nullable Contact contact,
@Nullable ConversationChannelWrapper conversation) {
PeopleItem(@NonNull Contact contact) {
this(contact, null);
}
PeopleItem(@NonNull ConversationChannelWrapper conversation) {
this(null, conversation);
}
}
private CircularIconSet<?> getPeopleIcons(ZenPolicy policy) {
if (getCallersOrMessagesAllowed(policy) == PEOPLE_TYPE_ANYONE) {
return new CircularIconSet<>(
ImmutableList.of(IconUtil.makeCircularIconPreferenceItem(mContext,
R.drawable.ic_zen_mode_people_all)),
Function.identity());
}
ImmutableList.Builder<PeopleItem> peopleItems = ImmutableList.builder();
fetchContactsAllowed(policy, peopleItems);
fetchConversationsAllowed(policy, peopleItems);
return new CircularIconSet<>(peopleItems.build(), this::loadPeopleIcon);
}
/**
* Adds {@link PeopleItem} entries corresponding to the set of people (contacts) who can
* break through via either call OR message.
*/
private void fetchContactsAllowed(ZenPolicy policy,
ImmutableList.Builder<PeopleItem> peopleItems) {
@PeopleType int peopleAllowed = getCallersOrMessagesAllowed(policy);
ImmutableList<Contact> contactsAllowed = ImmutableList.of();
if (peopleAllowed == PEOPLE_TYPE_CONTACTS) {
contactsAllowed = mHelperBackend.getAllContacts();
} else if (peopleAllowed == PEOPLE_TYPE_STARRED) {
contactsAllowed = mHelperBackend.getStarredContacts();
}
for (Contact contact : contactsAllowed) {
peopleItems.add(new PeopleItem(contact));
}
}
/**
* Adds {@link PeopleItem} entries corresponding to the set of conversation channels that can
* break through.
*/
private void fetchConversationsAllowed(ZenPolicy policy,
ImmutableList.Builder<PeopleItem> peopleItems) {
@ConversationSenders int conversationSendersAllowed =
policy.getPriorityCategoryConversations() == STATE_ALLOW
? policy.getPriorityConversationSenders()
: CONVERSATION_SENDERS_NONE;
ImmutableList<ConversationChannelWrapper> conversationsAllowed = ImmutableList.of();
if (conversationSendersAllowed == CONVERSATION_SENDERS_ANYONE) {
// TODO: b/354658240 - Need to handle CONVERSATION_SENDERS_ANYONE?
return;
} else if (conversationSendersAllowed == CONVERSATION_SENDERS_IMPORTANT) {
conversationsAllowed = mHelperBackend.getImportantConversations();
}
for (ConversationChannelWrapper conversation : conversationsAllowed) {
peopleItems.add(new PeopleItem(conversation));
}
}
/** Returns the broadest set of people who can call OR message. */
private @PeopleType int getCallersOrMessagesAllowed(ZenPolicy policy) {
@PeopleType int callersAllowed = policy.getPriorityCategoryCalls() == STATE_ALLOW
? policy.getPriorityCallSenders() : PEOPLE_TYPE_NONE;
@PeopleType int messagesAllowed = policy.getPriorityCategoryMessages() == STATE_ALLOW
? policy.getPriorityMessageSenders() : PEOPLE_TYPE_NONE;
// Order is ANYONE -> CONTACTS -> STARRED -> NONE, so just taking the minimum works.
return Math.min(callersAllowed, messagesAllowed);
}
@WorkerThread
private Drawable loadPeopleIcon(PeopleItem peopleItem) {
if (peopleItem.contact != null) {
return mHelperBackend.getContactPhoto(peopleItem.contact);
} else if (peopleItem.conversation != null) {
return mConversationIconFactory.getConversationDrawable(
peopleItem.conversation.getShortcutInfo(),
peopleItem.conversation.getPkg(),
peopleItem.conversation.getUid(),
/* important= */ true);
} else {
throw new IllegalArgumentException("Neither contact nor conversation!");
}
}
}

View File

@@ -30,10 +30,8 @@ import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.icu.text.MessageFormat;
import android.provider.Contacts;
import android.service.notification.ConversationChannelWrapper;
import android.service.notification.ZenPolicy;
import android.view.View;
@@ -167,17 +165,7 @@ class ZenModePrioritySendersPreferenceController
}
private void updateChannelCounts() {
ParceledListSlice<ConversationChannelWrapper> impConversations =
mHelperBackend.getConversations(true);
int numImportantConversations = 0;
if (impConversations != null) {
for (ConversationChannelWrapper conversation : impConversations.getList()) {
if (!conversation.getNotificationChannel().isDemoted()) {
numImportantConversations++;
}
}
}
mNumImportantConversations = numImportantConversations;
mNumImportantConversations = mHelperBackend.getImportantConversations().size();
}
private int getPrioritySenders(ZenPolicy policy) {

View File

@@ -31,6 +31,7 @@ import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_MESSAGES;
import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_REMINDERS;
import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS;
import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_SYSTEM;
import static android.service.notification.ZenPolicy.STATE_ALLOW;
import static android.service.notification.ZenPolicy.VISUAL_EFFECT_AMBIENT;
import static android.service.notification.ZenPolicy.VISUAL_EFFECT_BADGE;
import static android.service.notification.ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT;
@@ -45,6 +46,8 @@ import android.provider.Settings;
import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenPolicy;
import android.service.notification.ZenPolicy.ConversationSenders;
import android.service.notification.ZenPolicy.PeopleType;
import android.util.ArrayMap;
import androidx.annotation.NonNull;
@@ -56,6 +59,7 @@ import com.android.settings.R;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.notification.modes.ZenMode;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
@@ -365,7 +369,12 @@ class ZenModeSummaryHelper {
}
public String getStarredContactsSummary() {
List<String> starredContacts = mBackend.getStarredContacts();
List<String> starredContacts = mBackend.getStarredContacts().stream()
.map(ZenHelperBackend.Contact::displayName)
.map(name -> Strings.isNullOrEmpty(name)
? mContext.getString(R.string.zen_mode_starred_contacts_empty_name)
: name)
.toList();
int numStarredContacts = starredContacts.size();
MessageFormat msgFormat = new MessageFormat(
mContext.getString(R.string.zen_mode_starred_contacts_summary_contacts),
@@ -389,26 +398,32 @@ class ZenModeSummaryHelper {
mContext.getString(R.string.zen_mode_contacts_count),
Locale.getDefault());
Map<String, Object> args = new HashMap<>();
args.put("count", mBackend.queryAllContactsData().getCount());
args.put("count", mBackend.getAllContactsCount());
return msgFormat.format(args);
}
public String getPeopleSummary(ZenMode zenMode) {
final int callersAllowed = zenMode.getPolicy().getPriorityCallSenders();
final int messagesAllowed = zenMode.getPolicy().getPriorityMessageSenders();
final int conversationsAllowed = zenMode.getPolicy().getPriorityConversationSenders();
public String getPeopleSummary(ZenPolicy policy) {
@PeopleType int callersAllowed = policy.getPriorityCategoryCalls() == STATE_ALLOW
? policy.getPriorityCallSenders() : PEOPLE_TYPE_NONE;
@PeopleType int messagesAllowed = policy.getPriorityCategoryMessages() == STATE_ALLOW
? policy.getPriorityMessageSenders() : PEOPLE_TYPE_NONE;
@ConversationSenders int conversationsAllowed =
policy.getPriorityCategoryConversations() == STATE_ALLOW
? policy.getPriorityConversationSenders()
: CONVERSATION_SENDERS_NONE;
final boolean areRepeatCallersAllowed =
zenMode.getPolicy().isCategoryAllowed(PRIORITY_CATEGORY_REPEAT_CALLERS, false);
policy.isCategoryAllowed(PRIORITY_CATEGORY_REPEAT_CALLERS, false);
if (callersAllowed == PEOPLE_TYPE_ANYONE
&& messagesAllowed == PEOPLE_TYPE_ANYONE
&& conversationsAllowed == CONVERSATION_SENDERS_ANYONE) {
return mContext.getResources().getString(R.string.zen_mode_people_all);
return mContext.getString(R.string.zen_mode_people_all);
} else if (callersAllowed == PEOPLE_TYPE_NONE
&& messagesAllowed == PEOPLE_TYPE_NONE
&& conversationsAllowed == CONVERSATION_SENDERS_NONE
&& !areRepeatCallersAllowed) {
return mContext.getResources().getString(R.string.zen_mode_people_none);
&& conversationsAllowed == CONVERSATION_SENDERS_NONE) {
return mContext.getString(
areRepeatCallersAllowed ? R.string.zen_mode_people_repeat_callers
: R.string.zen_mode_people_none);
} else {
return mContext.getResources().getString(R.string.zen_mode_people_some);
}