diff options
| author | 2021-04-26 16:22:45 +0100 | |
|---|---|---|
| committer | 2021-05-03 17:23:28 +0100 | |
| commit | 1b12d3d47356c029f4605fd48c98c4ec80d26127 (patch) | |
| tree | bcaf777e4bd935eb3a76a6769e6def292e7f1400 | |
| parent | 6e729ee310a0c616adee340cc251182eeacb465f (diff) | |
Handle work profile, DND state, app pauses
Listen and respond to changes in state on background thread. Store in-memory to avoid
requerying frequently
Test: PeopleSpaceWidgetManagerTest, PeopleTileViewHelperTest,
PeopleSpaceUtilsTest
Bug: 182891880
Change-Id: Id0142b02bb549275c77d8940cda4eb1f0bf4e947
Change-Id: I65712a7a43d8956499533689fe77afbeeac5bd6e
14 files changed, 1243 insertions, 315 deletions
diff --git a/core/java/android/app/people/PeopleSpaceTile.java b/core/java/android/app/people/PeopleSpaceTile.java index de3eeee281ac..2dbbfdf660aa 100644 --- a/core/java/android/app/people/PeopleSpaceTile.java +++ b/core/java/android/app/people/PeopleSpaceTile.java @@ -43,6 +43,12 @@ import java.util.List; */ public class PeopleSpaceTile implements Parcelable { + public static final int SHOW_CONVERSATIONS = 1 << 0; + public static final int BLOCK_CONVERSATIONS = 1 << 1; + public static final int SHOW_IMPORTANT_CONVERSATIONS = 1 << 2; + public static final int SHOW_STARRED_CONTACTS = 1 << 3; + public static final int SHOW_CONTACTS = 1 << 4; + private String mId; private CharSequence mUserName; private Icon mUserIcon; @@ -61,6 +67,11 @@ public class PeopleSpaceTile implements Parcelable { private Intent mIntent; private long mNotificationTimestamp; private List<ConversationStatus> mStatuses; + private boolean mCanBypassDnd; + private boolean mIsPackageSuspended; + private boolean mIsUserQuieted; + private int mNotificationPolicyState; + private float mContactAffinity; private PeopleSpaceTile(Builder b) { mId = b.mId; @@ -81,6 +92,11 @@ public class PeopleSpaceTile implements Parcelable { mIntent = b.mIntent; mNotificationTimestamp = b.mNotificationTimestamp; mStatuses = b.mStatuses; + mCanBypassDnd = b.mCanBypassDnd; + mIsPackageSuspended = b.mIsPackageSuspended; + mIsUserQuieted = b.mIsUserQuieted; + mNotificationPolicyState = b.mNotificationPolicyState; + mContactAffinity = b.mContactAffinity; } public String getId() { @@ -173,6 +189,41 @@ public class PeopleSpaceTile implements Parcelable { return mStatuses; } + /** + * Whether the app associated with the conversation can bypass DND. + */ + public boolean canBypassDnd() { + return mCanBypassDnd; + } + + /** + * Whether the app associated with the conversation is suspended. + */ + public boolean isPackageSuspended() { + return mIsPackageSuspended; + } + + /** + * Whether the user associated with the conversation is quieted. + */ + public boolean isUserQuieted() { + return mIsUserQuieted; + } + + /** + * Returns the state of notifications for the conversation. + */ + public int getNotificationPolicyState() { + return mNotificationPolicyState; + } + + /** + * Returns the contact affinity (whether the contact is starred). + */ + public float getContactAffinity() { + return mContactAffinity; + } + /** Converts a {@link PeopleSpaceTile} into a {@link PeopleSpaceTile.Builder}. */ public Builder toBuilder() { Builder builder = @@ -192,6 +243,11 @@ public class PeopleSpaceTile implements Parcelable { builder.setIntent(mIntent); builder.setNotificationTimestamp(mNotificationTimestamp); builder.setStatuses(mStatuses); + builder.setCanBypassDnd(mCanBypassDnd); + builder.setIsPackageSuspended(mIsPackageSuspended); + builder.setIsUserQuieted(mIsUserQuieted); + builder.setNotificationPolicyState(mNotificationPolicyState); + builder.setContactAffinity(mContactAffinity); return builder; } @@ -215,6 +271,11 @@ public class PeopleSpaceTile implements Parcelable { private Intent mIntent; private long mNotificationTimestamp; private List<ConversationStatus> mStatuses; + private boolean mCanBypassDnd; + private boolean mIsPackageSuspended; + private boolean mIsUserQuieted; + private int mNotificationPolicyState; + private float mContactAffinity; /** Builder for use only if a shortcut is not available for the tile. */ public Builder(String id, CharSequence userName, Icon userIcon, Intent intent) { @@ -223,6 +284,7 @@ public class PeopleSpaceTile implements Parcelable { mUserIcon = userIcon; mIntent = intent; mPackageName = intent == null ? null : intent.getPackage(); + mNotificationPolicyState = SHOW_CONVERSATIONS; } public Builder(ShortcutInfo info, LauncherApps launcherApps) { @@ -232,6 +294,7 @@ public class PeopleSpaceTile implements Parcelable { mUserHandle = info.getUserHandle(); mPackageName = info.getPackage(); mContactUri = getContactUri(info); + mNotificationPolicyState = SHOW_CONVERSATIONS; } public Builder(ConversationChannel channel, LauncherApps launcherApps) { @@ -246,6 +309,9 @@ public class PeopleSpaceTile implements Parcelable { mLastInteractionTimestamp = channel.getLastEventTimestamp(); mIsImportantConversation = channel.getParentNotificationChannel() != null && channel.getParentNotificationChannel().isImportantConversation(); + mCanBypassDnd = channel.getParentNotificationChannel() != null + && channel.getParentNotificationChannel().canBypassDnd(); + mNotificationPolicyState = SHOW_CONVERSATIONS; } /** Returns the Contact's Uri if present. */ @@ -366,6 +432,36 @@ public class PeopleSpaceTile implements Parcelable { return this; } + /** Sets whether the conversation channel can bypass DND. */ + public Builder setCanBypassDnd(boolean canBypassDnd) { + mCanBypassDnd = canBypassDnd; + return this; + } + + /** Sets whether the package is suspended. */ + public Builder setIsPackageSuspended(boolean isPackageSuspended) { + mIsPackageSuspended = isPackageSuspended; + return this; + } + + /** Sets whether the user has been quieted. */ + public Builder setIsUserQuieted(boolean isUserQuieted) { + mIsUserQuieted = isUserQuieted; + return this; + } + + /** Sets the state of blocked notifications for the conversation. */ + public Builder setNotificationPolicyState(int notificationPolicyState) { + mNotificationPolicyState = notificationPolicyState; + return this; + } + + /** Sets the contact's affinity. */ + public Builder setContactAffinity(float contactAffinity) { + mContactAffinity = contactAffinity; + return this; + } + /** Builds a {@link PeopleSpaceTile}. */ @NonNull public PeopleSpaceTile build() { @@ -393,6 +489,11 @@ public class PeopleSpaceTile implements Parcelable { mNotificationTimestamp = in.readLong(); mStatuses = new ArrayList<>(); in.readParcelableList(mStatuses, ConversationStatus.class.getClassLoader()); + mCanBypassDnd = in.readBoolean(); + mIsPackageSuspended = in.readBoolean(); + mIsUserQuieted = in.readBoolean(); + mNotificationPolicyState = in.readInt(); + mContactAffinity = in.readFloat(); } @Override @@ -420,6 +521,11 @@ public class PeopleSpaceTile implements Parcelable { dest.writeParcelable(mIntent, flags); dest.writeLong(mNotificationTimestamp); dest.writeParcelableList(mStatuses, flags); + dest.writeBoolean(mCanBypassDnd); + dest.writeBoolean(mIsPackageSuspended); + dest.writeBoolean(mIsUserQuieted); + dest.writeInt(mNotificationPolicyState); + dest.writeFloat(mContactAffinity); } public static final @android.annotation.NonNull @@ -427,7 +533,6 @@ public class PeopleSpaceTile implements Parcelable { public PeopleSpaceTile createFromParcel(Parcel source) { return new PeopleSpaceTile(source); } - public PeopleSpaceTile[] newArray(int size) { return new PeopleSpaceTile[size]; } diff --git a/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java b/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java index 3e2c4e98e21a..5cee2c1389e4 100644 --- a/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java +++ b/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java @@ -16,6 +16,9 @@ package android.app.people; +import static android.app.people.PeopleSpaceTile.SHOW_CONVERSATIONS; +import static android.app.people.PeopleSpaceTile.SHOW_IMPORTANT_CONVERSATIONS; + import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertFalse; @@ -178,6 +181,71 @@ public class PeopleSpaceTileTest { } @Test + public void testUserQuieted() { + PeopleSpaceTile tile = new PeopleSpaceTile.Builder( + new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps).build(); + assertFalse(tile.isUserQuieted()); + + tile = new PeopleSpaceTile + .Builder(new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps) + .setIsUserQuieted(true) + .build(); + assertTrue(tile.isUserQuieted()); + } + + @Test + public void testCanBypassDnd() { + PeopleSpaceTile tile = new PeopleSpaceTile.Builder( + new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps).build(); + assertFalse(tile.canBypassDnd()); + + tile = new PeopleSpaceTile + .Builder(new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps) + .setCanBypassDnd(true) + .build(); + assertTrue(tile.canBypassDnd()); + } + + @Test + public void testNotificationPolicyState() { + PeopleSpaceTile tile = new PeopleSpaceTile.Builder( + new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps).build(); + assertThat(tile.getNotificationPolicyState()).isEqualTo(SHOW_CONVERSATIONS); + + tile = new PeopleSpaceTile + .Builder(new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps) + .setNotificationPolicyState(SHOW_IMPORTANT_CONVERSATIONS) + .build(); + assertThat(tile.getNotificationPolicyState()).isEqualTo(SHOW_IMPORTANT_CONVERSATIONS); + } + + @Test + public void testPackageSuspended() { + PeopleSpaceTile tile = new PeopleSpaceTile.Builder( + new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps).build(); + assertFalse(tile.isPackageSuspended()); + + tile = new PeopleSpaceTile + .Builder(new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps) + .setIsPackageSuspended(true) + .build(); + assertTrue(tile.isPackageSuspended()); + } + + @Test + public void testContactAffinity() { + PeopleSpaceTile tile = new PeopleSpaceTile.Builder( + new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps).build(); + assertThat(tile.getContactAffinity()).isEqualTo(0f); + + tile = new PeopleSpaceTile + .Builder(new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps) + .setContactAffinity(1f) + .build(); + assertThat(tile.getContactAffinity()).isEqualTo(1f); + } + + @Test public void testStatuses() { PeopleSpaceTile tile = new PeopleSpaceTile.Builder( new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps).build(); @@ -238,6 +306,11 @@ public class PeopleSpaceTileTest { .setNotificationDataUri(Uri.parse("data")) .setMessagesCount(2) .setIntent(new Intent()) + .setIsUserQuieted(true) + .setCanBypassDnd(false) + .setNotificationPolicyState(SHOW_IMPORTANT_CONVERSATIONS) + .setIsPackageSuspended(true) + .setContactAffinity(1f) .build(); Parcel parcel = Parcel.obtain(); @@ -261,6 +334,12 @@ public class PeopleSpaceTileTest { assertThat(readTile.getNotificationDataUri()).isEqualTo(tile.getNotificationDataUri()); assertThat(readTile.getMessagesCount()).isEqualTo(tile.getMessagesCount()); assertThat(readTile.getIntent().toString()).isEqualTo(tile.getIntent().toString()); + assertThat(readTile.isUserQuieted()).isEqualTo(tile.isUserQuieted()); + assertThat(readTile.canBypassDnd()).isEqualTo(tile.canBypassDnd()); + assertThat(readTile.getNotificationPolicyState()).isEqualTo( + tile.getNotificationPolicyState()); + assertThat(readTile.isPackageSuspended()).isEqualTo(tile.isPackageSuspended()); + assertThat(readTile.getContactAffinity()).isEqualTo(tile.getContactAffinity()); } @Test diff --git a/packages/SystemUI/res/layout/people_tile_empty_layout.xml b/packages/SystemUI/res/layout/people_tile_empty_layout.xml new file mode 100644 index 000000000000..7d3b919ce040 --- /dev/null +++ b/packages/SystemUI/res/layout/people_tile_empty_layout.xml @@ -0,0 +1,29 @@ +<!-- + ~ Copyright (C) 2021 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. + --> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:theme="@android:style/Theme.DeviceDefault.DayNight" + android:background="@drawable/people_space_tile_view_card" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <ImageView + android:id="@+id/item" + android:gravity="center" + android:layout_gravity="center" + android:padding="8dp" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> +</FrameLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/xml/people_space_widget_info.xml b/packages/SystemUI/res/xml/people_space_widget_info.xml index cce27e71dbd2..aa24bb49d868 100644 --- a/packages/SystemUI/res/xml/people_space_widget_info.xml +++ b/packages/SystemUI/res/xml/people_space_widget_info.xml @@ -26,5 +26,5 @@ android:previewLayout="@layout/people_space_placeholder_layout" android:resizeMode="horizontal|vertical" android:configure="com.android.systemui.people.PeopleSpaceActivity" - android:initialLayout="@layout/people_space_initial_layout"> + android:initialLayout="@layout/people_tile_empty_layout"> </appwidget-provider> diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java index 2a7023a58637..0cf3333d12a6 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java @@ -20,6 +20,7 @@ import android.content.BroadcastReceiver; import com.android.systemui.media.dialog.MediaOutputDialogReceiver; import com.android.systemui.people.widget.PeopleSpaceWidgetPinnedReceiver; +import com.android.systemui.people.widget.PeopleSpaceWidgetProvider; import com.android.systemui.screenshot.ActionProxyReceiver; import com.android.systemui.screenshot.DeleteScreenshotReceiver; import com.android.systemui.screenshot.SmartActionsReceiver; @@ -79,4 +80,13 @@ public abstract class DefaultBroadcastReceiverBinder { public abstract BroadcastReceiver bindPeopleSpaceWidgetPinnedReceiver( PeopleSpaceWidgetPinnedReceiver broadcastReceiver); + /** + * + */ + @Binds + @IntoMap + @ClassKey(PeopleSpaceWidgetProvider.class) + public abstract BroadcastReceiver bindPeopleSpaceWidgetProvider( + PeopleSpaceWidgetProvider broadcastReceiver); + } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java index 8e4e3081aff2..c04201caa182 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java @@ -23,6 +23,7 @@ import com.android.systemui.InitController; import com.android.systemui.SystemUIAppComponentFactory; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardSliceProvider; +import com.android.systemui.people.PeopleProvider; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.util.InjectionInflationController; import com.android.wm.shell.ShellCommandHandler; @@ -156,4 +157,9 @@ public interface SysUIComponent { * Member injection into the supplied argument. */ void inject(ClockOptionsProvider clockOptionsProvider); + + /** + * Member injection into the supplied argument. + */ + void inject(PeopleProvider peopleProvider); } diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java b/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java index a9640566d531..0f6645641dda 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java @@ -30,12 +30,15 @@ import android.widget.RemoteViews; import com.android.systemui.people.widget.PeopleSpaceWidgetManager; import com.android.systemui.shared.system.PeopleProviderUtils; +import javax.inject.Inject; + /** API that returns a People Tile preview. */ public class PeopleProvider extends ContentProvider { private static final String TAG = "PeopleProvider"; private static final boolean DEBUG = PeopleSpaceUtils.DEBUG; private static final String EMPTY_STRING = ""; + @Inject PeopleSpaceWidgetManager mPeopleSpaceWidgetManager; @Override @@ -76,7 +79,8 @@ public class PeopleProvider extends ContentProvider { } if (mPeopleSpaceWidgetManager == null) { - mPeopleSpaceWidgetManager = new PeopleSpaceWidgetManager(getContext()); + Log.e(TAG, "Could not initialize people widget manager"); + return null; } RemoteViews view = mPeopleSpaceWidgetManager.getPreview(shortcutId, userHandle, packageName, extras); diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java index 99a17ffbe77a..a6c6103ae4c5 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java @@ -23,11 +23,11 @@ import static com.android.systemui.people.NotificationHelper.hasReadContactsPerm import static com.android.systemui.people.NotificationHelper.isMissedCall; import static com.android.systemui.people.NotificationHelper.shouldMatchNotificationByUri; +import android.annotation.Nullable; import android.app.Notification; import android.app.people.ConversationChannel; import android.app.people.IPeopleManager; import android.app.people.PeopleSpaceTile; -import android.appwidget.AppWidgetManager; import android.content.Context; import android.content.SharedPreferences; import android.content.pm.LauncherApps; @@ -40,13 +40,11 @@ import android.graphics.Canvas; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; -import android.os.Bundle; import android.os.UserManager; import android.provider.ContactsContract; import android.service.notification.StatusBarNotification; import android.text.TextUtils; import android.util.Log; -import android.widget.RemoteViews; import androidx.preference.PreferenceManager; @@ -56,7 +54,7 @@ import com.android.internal.logging.UiEventLogger; import com.android.internal.util.ArrayUtils; import com.android.settingslib.utils.ThreadUtils; import com.android.systemui.R; -import com.android.systemui.people.widget.AppWidgetOptionsHelper; +import com.android.systemui.people.widget.PeopleSpaceWidgetManager; import com.android.systemui.people.widget.PeopleTileKey; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -76,45 +74,17 @@ import java.util.stream.Stream; public class PeopleSpaceUtils { /** Turns on debugging information about People Space. */ public static final boolean DEBUG = true; - private static final String TAG = "PeopleSpaceUtils"; public static final String PACKAGE_NAME = "package_name"; public static final String USER_ID = "user_id"; public static final String SHORTCUT_ID = "shortcut_id"; - public static final String EMPTY_STRING = ""; public static final int INVALID_USER_ID = -1; - public static final PeopleTileKey EMPTY_KEY = new PeopleTileKey(EMPTY_STRING, INVALID_USER_ID, EMPTY_STRING); - - /** Represents whether {@link StatusBarNotification} was posted or removed. */ - public enum NotificationAction { - POSTED, - REMOVED - } - - /** - * The UiEvent enums that this class can log. - */ - public enum PeopleSpaceWidgetEvent implements UiEventLogger.UiEventEnum { - @UiEvent(doc = "People space widget deleted") - PEOPLE_SPACE_WIDGET_DELETED(666), - @UiEvent(doc = "People space widget added") - PEOPLE_SPACE_WIDGET_ADDED(667), - @UiEvent(doc = "People space widget clicked to launch conversation") - PEOPLE_SPACE_WIDGET_CLICKED(668); - - private final int mId; - - PeopleSpaceWidgetEvent(int id) { - mId = id; - } - - @Override - public int getId() { - return mId; - } - } + static final float STARRED_CONTACT = 1f; + static final float VALID_CONTACT = .5f; + static final float DEFAULT_AFFINITY = 0f; + private static final String TAG = "PeopleSpaceUtils"; /** Returns stored widgets for the conversation specified. */ public static Set<String> getStoredWidgetIds(SharedPreferences sp, PeopleTileKey key) { @@ -127,6 +97,10 @@ public class PeopleSpaceUtils { /** Sets all relevant storage for {@code appWidgetId} association to {@code tile}. */ public static void setSharedPreferencesStorageForTile(Context context, PeopleTileKey key, int appWidgetId, Uri contactUri) { + if (!key.isValid()) { + Log.e(TAG, "Not storing for invalid key"); + return; + } // Write relevant persisted storage. SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(appWidgetId), Context.MODE_PRIVATE); @@ -201,7 +175,7 @@ public class PeopleSpaceUtils { .collect(Collectors.toList()); } - /** Returns the total messages in {@code notificationEntries}.*/ + /** Returns the total messages in {@code notificationEntries}. */ public static int getMessagesCount(Set<NotificationEntry> notificationEntries) { if (DEBUG) { Log.d(TAG, "Calculating messages count from " + notificationEntries.size() @@ -247,19 +221,34 @@ public class PeopleSpaceUtils { * {@code messagesCount}. */ public static PeopleSpaceTile augmentTileFromNotification(Context context, PeopleSpaceTile tile, - PeopleTileKey key, NotificationEntry notificationEntry, int messagesCount) { + PeopleTileKey key, NotificationEntry notificationEntry, int messagesCount, + Optional<Integer> appWidgetId) { if (notificationEntry == null || notificationEntry.getSbn().getNotification() == null) { if (DEBUG) Log.d(TAG, "Tile key: " + key.toString() + ". Notification is null"); return removeNotificationFields(tile); } Notification notification = notificationEntry.getSbn().getNotification(); + + PeopleSpaceTile.Builder updatedTile = tile.toBuilder(); + String uriFromNotification = getContactUri(notificationEntry.getSbn()); + if (appWidgetId.isPresent() && tile.getContactUri() == null && !TextUtils.isEmpty( + uriFromNotification)) { + if (DEBUG) Log.d(TAG, "Add uri from notification to tile: " + uriFromNotification); + Uri contactUri = Uri.parse(uriFromNotification); + // Update storage. + setSharedPreferencesStorageForTile(context, new PeopleTileKey(tile), appWidgetId.get(), + contactUri); + // Update cached tile in-memory. + updatedTile.setContactUri(contactUri); + } + boolean isMissedCall = isMissedCall(notification); List<Notification.MessagingStyle.Message> messages = getMessagingStyleMessages(notification); if (!isMissedCall && ArrayUtils.isEmpty(messages)) { if (DEBUG) Log.d(TAG, "Tile key: " + key.toString() + ". Notification has no content"); - return removeNotificationFields(tile); + return removeNotificationFields(updatedTile.build()); } // messages are in chronological order from most recent to least. @@ -276,8 +265,7 @@ public class PeopleSpaceUtils { } CharSequence sender = getSenderIfGroupConversation(notification, message); - return tile - .toBuilder() + return updatedTile .setNotificationKey(notificationEntry.getSbn().getKey()) .setNotificationCategory(notification.category) .setNotificationContent(content) @@ -378,17 +366,18 @@ public class PeopleSpaceUtils { context.getString(R.string.birthday_status)); } - /** Calls to retrieve birthdays on a background thread. */ - public static void getBirthdaysOnBackgroundThread(Context context, - AppWidgetManager appWidgetManager, + /** Calls to retrieve birthdays & contact affinity on a background thread. */ + public static void getDataFromContactsOnBackgroundThread(Context context, + PeopleSpaceWidgetManager manager, Map<Integer, PeopleSpaceTile> peopleSpaceTiles, int[] appWidgetIds) { ThreadUtils.postOnBackgroundThread( - () -> getBirthdays(context, appWidgetManager, peopleSpaceTiles, appWidgetIds)); + () -> getDataFromContacts(context, manager, peopleSpaceTiles, appWidgetIds)); } - /** Queries the Contacts DB for any birthdays today. */ + /** Queries the Contacts DB for any birthdays today & updates contact affinity. */ @VisibleForTesting - public static void getBirthdays(Context context, AppWidgetManager appWidgetManager, + public static void getDataFromContacts(Context context, + PeopleSpaceWidgetManager peopleSpaceWidgetManager, Map<Integer, PeopleSpaceTile> widgetIdToTile, int[] appWidgetIds) { if (DEBUG) Log.d(TAG, "Get birthdays"); if (appWidgetIds.length == 0) return; @@ -397,28 +386,37 @@ public class PeopleSpaceUtils { PeopleSpaceTile storedTile = widgetIdToTile.get(appWidgetId); if (storedTile == null || storedTile.getContactUri() == null) { if (DEBUG) Log.d(TAG, "No contact uri for: " + storedTile); - removeBirthdayStatusIfPresent(appWidgetManager, context, storedTile, appWidgetId); - continue; - } - if (lookupKeysWithBirthdaysToday.isEmpty()) { - if (DEBUG) Log.d(TAG, "No birthdays today"); - removeBirthdayStatusIfPresent(appWidgetManager, context, storedTile, appWidgetId); + updateTileContactFields(peopleSpaceWidgetManager, context, storedTile, + appWidgetId, DEFAULT_AFFINITY, /* birthdayString= */ null); continue; } - updateTileWithBirthday(context, appWidgetManager, lookupKeysWithBirthdaysToday, + updateTileWithBirthdayAndUpdateAffinity(context, peopleSpaceWidgetManager, + lookupKeysWithBirthdaysToday, storedTile, appWidgetId); } } - /** Removes the birthday status if present in {@code storedTile} and pushes the update. */ - private static void removeBirthdayStatusIfPresent(AppWidgetManager appWidgetManager, - Context context, PeopleSpaceTile storedTile, int appWidgetId) { - if (hasBirthdayStatus(storedTile, context)) { - if (DEBUG) Log.d(TAG, "Remove " + storedTile.getUserName() + "'s birthday"); - updateAppWidgetOptionsAndView(appWidgetManager, context, appWidgetId, + /** + * Updates the {@code storedTile} with {@code affinity} & {@code birthdayString} if + * necessary. + */ + private static void updateTileContactFields(PeopleSpaceWidgetManager manager, + Context context, PeopleSpaceTile storedTile, int appWidgetId, float affinity, + @Nullable String birthdayString) { + boolean outdatedBirthdayStatus = hasBirthdayStatus(storedTile, context) + && birthdayString == null; + boolean addBirthdayStatus = !hasBirthdayStatus(storedTile, context) + && birthdayString != null; + boolean shouldUpdate = + storedTile.getContactAffinity() != affinity || outdatedBirthdayStatus + || addBirthdayStatus; + if (shouldUpdate) { + if (DEBUG) Log.d(TAG, "Update " + storedTile.getUserName() + " from contacts"); + manager.updateAppWidgetOptionsAndView(appWidgetId, storedTile.toBuilder() - .setBirthdayText(null) + .setBirthdayText(birthdayString) + .setContactAffinity(affinity) .build()); } } @@ -427,7 +425,8 @@ public class PeopleSpaceUtils { * Update {@code storedTile} if the contact has a lookup key matched to any {@code * lookupKeysWithBirthdays}. */ - private static void updateTileWithBirthday(Context context, AppWidgetManager appWidgetManager, + private static void updateTileWithBirthdayAndUpdateAffinity(Context context, + PeopleSpaceWidgetManager manager, List<String> lookupKeysWithBirthdaysToday, PeopleSpaceTile storedTile, int appWidgetId) { Cursor cursor = null; @@ -437,14 +436,16 @@ public class PeopleSpaceUtils { while (cursor != null && cursor.moveToNext()) { String storedLookupKey = cursor.getString( cursor.getColumnIndex(ContactsContract.CommonDataKinds.Event.LOOKUP_KEY)); + float affinity = getContactAffinity(cursor); if (!storedLookupKey.isEmpty() && lookupKeysWithBirthdaysToday.contains( storedLookupKey)) { if (DEBUG) Log.d(TAG, storedTile.getUserName() + "'s birthday today!"); - updateAppWidgetOptionsAndView(appWidgetManager, context, appWidgetId, - storedTile.toBuilder() - .setBirthdayText(context.getString(R.string.birthday_status)) - .build()); - return; + updateTileContactFields(manager, context, storedTile, appWidgetId, + affinity, /* birthdayString= */ + context.getString(R.string.birthday_status)); + } else { + updateTileContactFields(manager, context, storedTile, appWidgetId, + affinity, /* birthdayString= */ null); } } } catch (SQLException e) { @@ -454,51 +455,20 @@ public class PeopleSpaceUtils { cursor.close(); } } - removeBirthdayStatusIfPresent(appWidgetManager, context, storedTile, appWidgetId); - } - - /** Updates the current widget view with provided {@link PeopleSpaceTile}. */ - public static void updateAppWidgetViews(AppWidgetManager appWidgetManager, - Context context, int appWidgetId, PeopleSpaceTile tile, Bundle options) { - if (tile == null) { - if (DEBUG) Log.d(TAG, "Widget: " + appWidgetId + ". Tile is null, skipping update"); - return; - } - if (DEBUG) { - Log.d(TAG, "Widget: " + appWidgetId + ", " + tile.getUserName() + ", " - + tile.getPackageName() + ". Updating app widget view."); - } - RemoteViews views = new PeopleTileViewHelper(context, tile, appWidgetId, - options).getViews(); - - // Tell the AppWidgetManager to perform an update on the current app widget. - appWidgetManager.updateAppWidget(appWidgetId, views); - } - - /** Updates tile in app widget options and the current view. */ - public static void updateAppWidgetOptionsAndView(AppWidgetManager appWidgetManager, - Context context, int appWidgetId, PeopleSpaceTile tile) { - if (tile == null) { - if (DEBUG) { - Log.w(TAG, "Widget: " + appWidgetId + "Tile is null, skipping storage and update."); - } - return; - } - Bundle options = AppWidgetOptionsHelper.setPeopleTile(appWidgetManager, appWidgetId, tile); - updateAppWidgetViews(appWidgetManager, context, appWidgetId, tile, options); } - /** Wrapper around {@link #updateAppWidgetOptionsAndView} with optional tile as a parameter. */ - public static void updateAppWidgetOptionsAndView(AppWidgetManager appWidgetManager, - Context context, int appWidgetId, Optional<PeopleSpaceTile> optionalTile) { - if (!optionalTile.isPresent()) { - if (DEBUG) { - Log.w(TAG, "Widget: " + appWidgetId - + "Optional tile is not present, skipping storage and update."); + /** Pulls the contact affinity from {@code cursor}. */ + private static float getContactAffinity(Cursor cursor) { + float affinity = VALID_CONTACT; + int starIdx = cursor.getColumnIndex(ContactsContract.Contacts.STARRED); + if (starIdx >= 0) { + boolean isStarred = cursor.getInt(starIdx) != 0; + if (isStarred) { + affinity = Math.max(affinity, STARRED_CONTACT); } - return; } - updateAppWidgetOptionsAndView(appWidgetManager, context, appWidgetId, optionalTile.get()); + if (DEBUG) Log.d(TAG, "Affinity is: " + affinity); + return affinity; } /** @@ -546,4 +516,33 @@ public class PeopleSpaceUtils { public static int getUserId(PeopleSpaceTile tile) { return tile.getUserHandle().getIdentifier(); } + + /** Represents whether {@link StatusBarNotification} was posted or removed. */ + public enum NotificationAction { + POSTED, + REMOVED + } + + /** + * The UiEvent enums that this class can log. + */ + public enum PeopleSpaceWidgetEvent implements UiEventLogger.UiEventEnum { + @UiEvent(doc = "People space widget deleted") + PEOPLE_SPACE_WIDGET_DELETED(666), + @UiEvent(doc = "People space widget added") + PEOPLE_SPACE_WIDGET_ADDED(667), + @UiEvent(doc = "People space widget clicked to launch conversation") + PEOPLE_SPACE_WIDGET_CLICKED(668); + + private final int mId; + + PeopleSpaceWidgetEvent(int id) { + mId = id; + } + + @Override + public int getId() { + return mId; + } + } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java index a23db63a70fc..6980d723583b 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java @@ -30,6 +30,8 @@ import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH; import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT; import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH; +import static com.android.systemui.people.PeopleSpaceUtils.STARRED_CONTACT; +import static com.android.systemui.people.PeopleSpaceUtils.VALID_CONTACT; import static com.android.systemui.people.PeopleSpaceUtils.convertDrawableToBitmap; import static com.android.systemui.people.PeopleSpaceUtils.getUserId; @@ -39,6 +41,8 @@ import android.app.people.ConversationStatus; import android.app.people.PeopleSpaceTile; import android.content.Context; import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; @@ -48,6 +52,7 @@ import android.icu.util.Measure; import android.icu.util.MeasureUnit; import android.net.Uri; import android.os.Bundle; +import android.os.UserHandle; import android.text.TextUtils; import android.util.IconDrawableFactory; import android.util.Log; @@ -57,6 +62,8 @@ import android.widget.RemoteViews; import android.widget.TextView; import com.android.internal.annotations.VisibleForTesting; +import com.android.launcher3.icons.FastBitmapDrawable; +import com.android.settingslib.Utils; import com.android.systemui.R; import com.android.systemui.people.widget.LaunchConversationActivity; import com.android.systemui.people.widget.PeopleSpaceWidgetProvider; @@ -142,7 +149,9 @@ public class PeopleTileViewHelper { private int mMediumVerticalPadding; private Context mContext; + @Nullable private PeopleSpaceTile mTile; + private PeopleTileKey mKey; private float mDensity; private int mAppWidgetId; private int mWidth; @@ -152,10 +161,11 @@ public class PeopleTileViewHelper { private Locale mLocale; private NumberFormat mIntegerFormat; - public PeopleTileViewHelper(Context context, PeopleSpaceTile tile, - int appWidgetId, Bundle options) { + public PeopleTileViewHelper(Context context, @Nullable PeopleSpaceTile tile, + int appWidgetId, Bundle options, PeopleTileKey key) { mContext = context; mTile = tile; + mKey = key; mAppWidgetId = appWidgetId; mDensity = mContext.getResources().getDisplayMetrics().density; int display = mContext.getResources().getConfiguration().orientation; @@ -184,8 +194,19 @@ public class PeopleTileViewHelper { * content, then birthdays, then the most recent status, and finally last interaction. */ private RemoteViews getViewForTile() { - PeopleTileKey key = new PeopleTileKey(mTile); - if (DEBUG) Log.d(TAG, "Creating view for tile key: " + key.toString()); + if (DEBUG) Log.d(TAG, "Creating view for tile key: " + mKey.toString()); + if (mTile == null || mTile.isPackageSuspended() || mTile.isUserQuieted()) { + if (DEBUG) Log.d(TAG, "Create empty view: " + mTile); + return createEmptyView(); + } + + boolean dndBlockingTileData = isDndBlockingTileData(mTile); + if (dndBlockingTileData) { + if (DEBUG) Log.d(TAG, "Create DND view: " + mTile.getNotificationPolicyState()); + // TODO: Create DND view. + return createEmptyView(); + } + if (Objects.equals(mTile.getNotificationCategory(), CATEGORY_MISSED_CALL)) { if (DEBUG) Log.d(TAG, "Create missed call view"); return createMissedCallRemoteViews(); @@ -217,6 +238,58 @@ public class PeopleTileViewHelper { return createLastInteractionRemoteViews(); } + private boolean isDndBlockingTileData(PeopleSpaceTile tile) { + int notificationPolicyState = tile.getNotificationPolicyState(); + if ((notificationPolicyState & PeopleSpaceTile.SHOW_CONVERSATIONS) != 0) { + // Not in DND, or all conversations + if (DEBUG) Log.d(TAG, "Tile can show all data: " + tile.getUserName()); + return false; + } + if ((notificationPolicyState & PeopleSpaceTile.SHOW_IMPORTANT_CONVERSATIONS) != 0 + && tile.isImportantConversation()) { + if (DEBUG) Log.d(TAG, "Tile can show important: " + tile.getUserName()); + return false; + } + if ((notificationPolicyState & PeopleSpaceTile.SHOW_STARRED_CONTACTS) != 0 + && tile.getContactAffinity() == STARRED_CONTACT) { + if (DEBUG) Log.d(TAG, "Tile can show starred: " + tile.getUserName()); + return false; + } + if ((notificationPolicyState & PeopleSpaceTile.SHOW_CONTACTS) != 0 + && (tile.getContactAffinity() == VALID_CONTACT + || tile.getContactAffinity() == STARRED_CONTACT)) { + if (DEBUG) Log.d(TAG, "Tile can show contacts: " + tile.getUserName()); + return false; + } + if (DEBUG) Log.d(TAG, "Tile can show if can bypass DND: " + tile.getUserName()); + return !tile.canBypassDnd(); + } + + private RemoteViews createEmptyView() { + RemoteViews views = new RemoteViews(mContext.getPackageName(), + R.layout.people_tile_empty_layout); + Drawable appIcon = getAppBadge(mKey.getPackageName(), mKey.getUserId()); + Bitmap appIconAsBitmap = convertDrawableToBitmap(appIcon); + FastBitmapDrawable drawable = new FastBitmapDrawable( + appIconAsBitmap); + drawable.setIsDisabled(true); + Bitmap convertedBitmap = convertDrawableToBitmap(drawable); + views.setImageViewBitmap(R.id.item, convertedBitmap); + return views; + } + + private Drawable getAppBadge(String packageName, int userId) { + Drawable badge = null; + try { + final ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfoAsUser( + packageName, PackageManager.GET_META_DATA, userId); + badge = Utils.getBadgedIcon(mContext, appInfo); + } catch (PackageManager.NameNotFoundException e) { + badge = mContext.getPackageManager().getDefaultActivityIcon(); + } + return badge; + } + private void setMaxLines(RemoteViews views, boolean showSender) { int textSize = mLayoutSize == LAYOUT_LARGE ? getSizeInDp( R.dimen.content_text_size_for_medium) @@ -337,6 +410,9 @@ public class PeopleTileViewHelper { private RemoteViews setCommonRemoteViewsFields(RemoteViews views, int maxAvatarSize) { try { + if (mTile == null) { + return views; + } boolean isAvailable = mTile.getStatuses() != null && mTile.getStatuses().stream().anyMatch( c -> c.getAvailability() == AVAILABILITY_AVAILABLE); @@ -367,13 +443,16 @@ public class PeopleTileViewHelper { | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); - activityIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_TILE_ID, mTile.getId()); + activityIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_TILE_ID, mKey.getShortcutId()); activityIntent.putExtra( - PeopleSpaceWidgetProvider.EXTRA_PACKAGE_NAME, mTile.getPackageName()); + PeopleSpaceWidgetProvider.EXTRA_PACKAGE_NAME, mKey.getPackageName()); activityIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_USER_HANDLE, - mTile.getUserHandle()); - activityIntent.putExtra( - PeopleSpaceWidgetProvider.EXTRA_NOTIFICATION_KEY, mTile.getNotificationKey()); + new UserHandle(mKey.getUserId())); + if (mTile != null) { + activityIntent.putExtra( + PeopleSpaceWidgetProvider.EXTRA_NOTIFICATION_KEY, + mTile.getNotificationKey()); + } views.setOnClickPendingIntent(R.id.item, PendingIntent.getActivity( mContext, mAppWidgetId, @@ -727,6 +806,9 @@ public class PeopleTileViewHelper { c -> c.getActivity() == ACTIVITY_NEW_STORY); Icon icon = tile.getUserIcon(); + if (icon == null) { + return null; + } PeopleStoryIconFactory storyIcon = new PeopleStoryIconFactory(context, context.getPackageManager(), IconDrawableFactory.newInstance(context, false), diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java index 64a6509ea0aa..ea1724f96669 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java @@ -17,6 +17,12 @@ package com.android.systemui.people.widget; import static android.Manifest.permission.READ_CONTACTS; +import static android.app.NotificationManager.INTERRUPTION_FILTER_ALARMS; +import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL; +import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE; +import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; +import static android.content.Intent.ACTION_BOOT_COMPLETED; +import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_ANYONE; import static com.android.systemui.people.NotificationHelper.getContactUri; import static com.android.systemui.people.NotificationHelper.getHighestPriorityNotification; @@ -30,13 +36,12 @@ import static com.android.systemui.people.PeopleSpaceUtils.augmentTileFromNotifi import static com.android.systemui.people.PeopleSpaceUtils.getMessagesCount; import static com.android.systemui.people.PeopleSpaceUtils.getNotificationsByUri; import static com.android.systemui.people.PeopleSpaceUtils.removeNotificationFields; -import static com.android.systemui.people.PeopleSpaceUtils.updateAppWidgetOptionsAndView; -import static com.android.systemui.people.PeopleSpaceUtils.updateAppWidgetViews; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.INotificationManager; import android.app.NotificationChannel; +import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Person; import android.app.people.ConversationChannel; @@ -44,8 +49,11 @@ import android.app.people.IPeopleManager; import android.app.people.PeopleManager; import android.app.people.PeopleSpaceTile; import android.appwidget.AppWidgetManager; +import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.pm.LauncherApps; import android.content.pm.PackageManager; @@ -60,6 +68,8 @@ import android.preference.PreferenceManager; import android.service.notification.ConversationChannelWrapper; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; +import android.service.notification.ZenModeConfig; +import android.text.TextUtils; import android.util.Log; import android.widget.RemoteViews; @@ -67,8 +77,9 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.UiEventLoggerImpl; -import com.android.settingslib.utils.ThreadUtils; -import com.android.systemui.Dependency; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.people.NotificationHelper; import com.android.systemui.people.PeopleSpaceUtils; import com.android.systemui.people.PeopleTileViewHelper; @@ -85,15 +96,15 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.concurrent.Executor; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.inject.Inject; -import javax.inject.Singleton; /** Manager for People Space widget. */ -@Singleton +@SysUISingleton public class PeopleSpaceWidgetManager { private static final String TAG = "PeopleSpaceWidgetMgr"; private static final boolean DEBUG = PeopleSpaceUtils.DEBUG; @@ -107,12 +118,15 @@ public class PeopleSpaceWidgetManager { private PeopleManager mPeopleManager; private NotificationEntryManager mNotificationEntryManager; private PackageManager mPackageManager; - private PeopleSpaceWidgetProvider mPeopleSpaceWidgetProvider; private INotificationManager mINotificationManager; private UserManager mUserManager; + private PeopleSpaceWidgetManager mManager; public UiEventLogger mUiEventLogger = new UiEventLoggerImpl(); + private NotificationManager mNotificationManager; + private BroadcastDispatcher mBroadcastDispatcher; + private Executor mBgExecutor; @GuardedBy("mLock") - public static Map<PeopleTileKey, PeopleSpaceWidgetProvider.TileConversationListener> + public static Map<PeopleTileKey, TileConversationListener> mListeners = new HashMap<>(); @GuardedBy("mLock") @@ -120,46 +134,91 @@ public class PeopleSpaceWidgetManager { // This is required because on notification removal, the contact Uri field is stripped and we // only have the notification key to determine which widget IDs should be updated. private Map<String, Set<String>> mNotificationKeyToWidgetIdsMatchedByUri = new HashMap<>(); - private boolean mIsForTesting; + private boolean mRegisteredReceivers; @Inject - public PeopleSpaceWidgetManager(Context context) { + public PeopleSpaceWidgetManager(Context context, LauncherApps launcherApps, + NotificationEntryManager notificationEntryManager, + PackageManager packageManager, UserManager userManager, + NotificationManager notificationManager, BroadcastDispatcher broadcastDispatcher, + @Background Executor bgExecutor) { if (DEBUG) Log.d(TAG, "constructor"); mContext = context; mAppWidgetManager = AppWidgetManager.getInstance(context); mIPeopleManager = IPeopleManager.Stub.asInterface( ServiceManager.getService(Context.PEOPLE_SERVICE)); - mLauncherApps = context.getSystemService(LauncherApps.class); + mLauncherApps = launcherApps; mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(mContext); - mPeopleManager = mContext.getSystemService(PeopleManager.class); - mNotificationEntryManager = Dependency.get(NotificationEntryManager.class); - mPackageManager = mContext.getPackageManager(); - mPeopleSpaceWidgetProvider = new PeopleSpaceWidgetProvider(); + mPeopleManager = context.getSystemService(PeopleManager.class); + mNotificationEntryManager = notificationEntryManager; + mPackageManager = packageManager; mINotificationManager = INotificationManager.Stub.asInterface( ServiceManager.getService(Context.NOTIFICATION_SERVICE)); - mUserManager = context.getSystemService(UserManager.class); + mUserManager = userManager; + mNotificationManager = notificationManager; + mManager = this; + mBroadcastDispatcher = broadcastDispatcher; + mBgExecutor = bgExecutor; + } + + /** Initializes {@PeopleSpaceWidgetManager}. */ + public void init() { + synchronized (mLock) { + if (!mRegisteredReceivers) { + IntentFilter filter = new IntentFilter(); + filter.addAction(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED); + filter.addAction(ACTION_BOOT_COMPLETED); + filter.addAction(Intent.ACTION_LOCALE_CHANGED); + filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE); + filter.addAction(Intent.ACTION_PACKAGES_SUSPENDED); + filter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED); + filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE); + filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE); + filter.addAction(Intent.ACTION_USER_UNLOCKED); + mBroadcastDispatcher.registerReceiver(mBaseBroadcastReceiver, filter, + null /* executor */, UserHandle.ALL); + mRegisteredReceivers = true; + } + } + } + + /** Listener for the shortcut data changes. */ + public class TileConversationListener implements PeopleManager.ConversationListener { + + @Override + public void onConversationUpdate(@NonNull ConversationChannel conversation) { + if (DEBUG) { + Log.d(TAG, + "Received updated conversation: " + + conversation.getShortcutInfo().getLabel()); + } + updateWidgetsWithConversationChanged(conversation); + } } /** - * AppWidgetManager setter used for testing. + * PeopleSpaceWidgetManager setter used for testing. */ @VisibleForTesting - public void setAppWidgetManager( + PeopleSpaceWidgetManager(Context context, AppWidgetManager appWidgetManager, IPeopleManager iPeopleManager, PeopleManager peopleManager, LauncherApps launcherApps, NotificationEntryManager notificationEntryManager, PackageManager packageManager, - boolean isForTesting, PeopleSpaceWidgetProvider peopleSpaceWidgetProvider, - UserManager userManager, INotificationManager notificationManager) { + UserManager userManager, INotificationManager iNotificationManager, + NotificationManager notificationManager, @Background Executor executor) { + mContext = context; mAppWidgetManager = appWidgetManager; mIPeopleManager = iPeopleManager; mPeopleManager = peopleManager; mLauncherApps = launcherApps; mNotificationEntryManager = notificationEntryManager; mPackageManager = packageManager; - mIsForTesting = isForTesting; - mPeopleSpaceWidgetProvider = peopleSpaceWidgetProvider; mUserManager = userManager; - mINotificationManager = notificationManager; + mINotificationManager = iNotificationManager; + mNotificationManager = notificationManager; + mManager = this; + mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); + mBgExecutor = executor; } /** @@ -173,7 +232,7 @@ public class PeopleSpaceWidgetManager { return; } - if (DEBUG) Log.d(TAG, "updating " + widgetIds.length + " widgets"); + if (DEBUG) Log.d(TAG, "updating " + widgetIds.length + " widgets: " + widgetIds); synchronized (mLock) { updateSingleConversationWidgets(widgetIds); } @@ -191,16 +250,47 @@ public class PeopleSpaceWidgetManager { for (int appWidgetId : appWidgetIds) { PeopleSpaceTile tile = getTileForExistingWidget(appWidgetId); if (tile == null) { - if (DEBUG) Log.d(TAG, "Matching conversation not found for shortcut ID"); - //TODO: Delete app widget id when crash is fixed (b/172932636) - continue; + Log.e(TAG, "Matching conversation not found for shortcut ID"); } Bundle options = mAppWidgetManager.getAppWidgetOptions(appWidgetId); - updateAppWidgetViews(mAppWidgetManager, mContext, appWidgetId, tile, options); + updateAppWidgetViews(appWidgetId, tile, options); widgetIdToTile.put(appWidgetId, tile); + if (tile != null) { + registerConversationListenerIfNeeded(appWidgetId, + new PeopleTileKey(tile)); + } + } + PeopleSpaceUtils.getDataFromContactsOnBackgroundThread( + mContext, mManager, widgetIdToTile, appWidgetIds); + } + + /** Updates the current widget view with provided {@link PeopleSpaceTile}. */ + private void updateAppWidgetViews(int appWidgetId, PeopleSpaceTile tile, Bundle options) { + PeopleTileKey key = getKeyFromStorageByWidgetId(appWidgetId); + if (DEBUG) Log.d(TAG, "Widget: " + appWidgetId + " for: " + key.toString()); + if (!key.isValid()) { + Log.e(TAG, "Cannot update invalid widget"); + return; + } + RemoteViews views = new PeopleTileViewHelper(mContext, tile, appWidgetId, + options, key).getViews(); + + // Tell the AppWidgetManager to perform an update on the current app widget. + mAppWidgetManager.updateAppWidget(appWidgetId, views); + } + + /** Updates tile in app widget options and the current view. */ + public void updateAppWidgetOptionsAndViewOptional(int appWidgetId, + Optional<PeopleSpaceTile> tile) { + if (tile.isPresent()) { + updateAppWidgetOptionsAndView(appWidgetId, tile.get()); } - PeopleSpaceUtils.getBirthdaysOnBackgroundThread( - mContext, mAppWidgetManager, widgetIdToTile, appWidgetIds); + } + + /** Updates tile in app widget options and the current view. */ + public void updateAppWidgetOptionsAndView(int appWidgetId, PeopleSpaceTile tile) { + Bundle options = AppWidgetOptionsHelper.setPeopleTile(mAppWidgetManager, appWidgetId, tile); + updateAppWidgetViews(appWidgetId, tile, options); } /** @@ -226,7 +316,7 @@ public class PeopleSpaceWidgetManager { widgetSp.getInt(USER_ID, INVALID_USER_ID), widgetSp.getString(PACKAGE_NAME, EMPTY_STRING)); - return getTileFromPersistentStorage(key); + return getTileFromPersistentStorage(key, appWidgetId); } /** @@ -234,7 +324,7 @@ public class PeopleSpaceWidgetManager { * If a {@link PeopleTileKey} is not provided, fetch one from {@link SharedPreferences}. */ @Nullable - public PeopleSpaceTile getTileFromPersistentStorage(PeopleTileKey key) { + public PeopleSpaceTile getTileFromPersistentStorage(PeopleTileKey key, int appWidgetId) { if (!key.isValid()) { Log.e(TAG, "PeopleTileKey invalid: " + key.toString()); return null; @@ -254,7 +344,22 @@ public class PeopleSpaceWidgetManager { return null; } - return new PeopleSpaceTile.Builder(channel, mLauncherApps).build(); + // Get tile from shortcut & conversation storage. + PeopleSpaceTile.Builder storedTile = new PeopleSpaceTile.Builder(channel, + mLauncherApps); + if (storedTile == null) { + return storedTile.build(); + } + + // Supplement with our storage. + String contactUri = mSharedPrefs.getString(String.valueOf(appWidgetId), null); + if (contactUri != null && storedTile.build().getContactUri() == null) { + if (DEBUG) Log.d(TAG, "Restore contact uri from storage: " + contactUri); + storedTile.setContactUri(Uri.parse(contactUri)); + } + + // Add current state. + return updateWithCurrentState(storedTile.build(), ACTION_BOOT_COMPLETED); } catch (Exception e) { Log.e(TAG, "Failed to retrieve conversation for tile: " + e); return null; @@ -275,11 +380,7 @@ public class PeopleSpaceWidgetManager { Log.d(TAG, "Notification removed, key: " + sbn.getKey()); } } - if (mIsForTesting) { - updateWidgetsWithNotificationChangedInBackground(sbn, notificationAction); - return; - } - ThreadUtils.postOnBackgroundThread( + mBgExecutor.execute( () -> updateWidgetsWithNotificationChangedInBackground(sbn, notificationAction)); } @@ -331,8 +432,7 @@ public class PeopleSpaceWidgetManager { .collect(Collectors.toMap( Function.identity(), id -> getAugmentedTileForExistingWidget(id, groupedNotifications))) - .forEach((id, tile) -> - updateAppWidgetOptionsAndView(mAppWidgetManager, mContext, id, tile)); + .forEach((id, tile) -> updateAppWidgetOptionsAndViewOptional(id, tile)); } catch (Exception e) { Log.e(TAG, "Exception updating widgets: " + e); } @@ -341,16 +441,20 @@ public class PeopleSpaceWidgetManager { /** * Augments {@code tile} based on notifications returned from {@code notificationEntryManager}. */ - public PeopleSpaceTile augmentTileFromNotificationEntryManager(PeopleSpaceTile tile) { + public PeopleSpaceTile augmentTileFromNotificationEntryManager(PeopleSpaceTile tile, + Optional<Integer> appWidgetId) { PeopleTileKey key = new PeopleTileKey(tile); - Log.d(TAG, "Augmenting tile from NotificationEntryManager widget: " + key.toString()); + if (DEBUG) { + Log.d(TAG, + "Augmenting tile from NotificationEntryManager widget: " + key.toString()); + } Map<PeopleTileKey, Set<NotificationEntry>> notifications = getGroupedConversationNotifications(); String contactUri = null; if (tile.getContactUri() != null) { contactUri = tile.getContactUri().toString(); } - return augmentTileFromNotifications(tile, key, contactUri, notifications); + return augmentTileFromNotifications(tile, key, contactUri, notifications, appWidgetId); } /** Returns active and pending notifications grouped by {@link PeopleTileKey}. */ @@ -380,9 +484,11 @@ public class PeopleSpaceWidgetManager { /** Augments {@code tile} based on {@code notifications}, matching {@code contactUri}. */ public PeopleSpaceTile augmentTileFromNotifications(PeopleSpaceTile tile, PeopleTileKey key, - String contactUri, Map<PeopleTileKey, Set<NotificationEntry>> notifications) { + String contactUri, + Map<PeopleTileKey, Set<NotificationEntry>> notifications, + Optional<Integer> appWidgetId) { if (DEBUG) Log.d(TAG, "Augmenting tile from notifications. Tile key: " + key.toString()); - boolean hasReadContactsPermission = mPackageManager.checkPermission(READ_CONTACTS, + boolean hasReadContactsPermission = mPackageManager.checkPermission(READ_CONTACTS, tile.getPackageName()) == PackageManager.PERMISSION_GRANTED; List<NotificationEntry> notificationsByUri = new ArrayList<>(); @@ -413,7 +519,8 @@ public class PeopleSpaceWidgetManager { NotificationEntry highestPriority = getHighestPriorityNotification(allNotifications); if (DEBUG) Log.d(TAG, "Augmenting tile from notification, key: " + key.toString()); - return augmentTileFromNotification(mContext, tile, key, highestPriority, messagesCount); + return augmentTileFromNotification(mContext, tile, key, highestPriority, messagesCount, + appWidgetId); } /** Returns an augmented tile for an existing widget. */ @@ -434,7 +541,8 @@ public class PeopleSpaceWidgetManager { PeopleTileKey key = new PeopleTileKey(tile); if (DEBUG) Log.d(TAG, "Existing widget: " + widgetId + ". Tile key: " + key.toString()); return Optional.ofNullable( - augmentTileFromNotifications(tile, key, contactUriString, notifications)); + augmentTileFromNotifications(tile, key, contactUriString, notifications, + Optional.of(widgetId))); } /** Returns stored widgets for the conversation specified. */ @@ -552,10 +660,8 @@ public class PeopleSpaceWidgetManager { .setStatuses(conversation.getStatuses()) .setLastInteractionTimestamp(conversation.getLastEventTimestamp()) .setIsImportantConversation(conversation.getParentNotificationChannel() != null - && conversation.getParentNotificationChannel().isImportantConversation()) - .build(); - updateAppWidgetOptionsAndView(mAppWidgetManager, mContext, appWidgetId, - updatedTile.build()); + && conversation.getParentNotificationChannel().isImportantConversation()); + updateAppWidgetOptionsAndView(appWidgetId, updatedTile.build()); } /** @@ -640,11 +746,11 @@ public class PeopleSpaceWidgetManager { /** Adds a widget based on {@code key} mapped to {@code appWidgetId}. */ public void addNewWidget(int appWidgetId, PeopleTileKey key) { if (DEBUG) Log.d(TAG, "addNewWidget called with key for appWidgetId: " + appWidgetId); - PeopleSpaceTile tile = getTileFromPersistentStorage(key); + PeopleSpaceTile tile = getTileFromPersistentStorage(key, appWidgetId); if (tile == null) { return; } - tile = augmentTileFromNotificationEntryManager(tile); + tile = augmentTileFromNotificationEntryManager(tile, Optional.of(appWidgetId)); PeopleTileKey existingKeyIfStored; synchronized (mLock) { @@ -665,6 +771,8 @@ public class PeopleSpaceWidgetManager { PeopleSpaceUtils.setSharedPreferencesStorageForTile(mContext, key, appWidgetId, tile.getContactUri()); } + if (DEBUG) Log.d(TAG, "Ensure listener is registered for widget: " + appWidgetId); + registerConversationListenerIfNeeded(appWidgetId, key); try { if (DEBUG) Log.d(TAG, "Caching shortcut for PeopleTile: " + key.toString()); mLauncherApps.cacheShortcuts(tile.getPackageName(), @@ -674,23 +782,17 @@ public class PeopleSpaceWidgetManager { Log.w(TAG, "Exception caching shortcut:" + e); } - PeopleSpaceUtils.updateAppWidgetOptionsAndView( - mAppWidgetManager, mContext, appWidgetId, tile); - mPeopleSpaceWidgetProvider.onUpdate(mContext, mAppWidgetManager, new int[]{appWidgetId}); + updateAppWidgetOptionsAndView(appWidgetId, tile); } /** Registers a conversation listener for {@code appWidgetId} if not already registered. */ - public void registerConversationListenerIfNeeded(int widgetId, - PeopleSpaceWidgetProvider.TileConversationListener newListener) { + public void registerConversationListenerIfNeeded(int widgetId, PeopleTileKey key) { // Retrieve storage needed for registration. - PeopleTileKey key; - synchronized (mLock) { - key = getKeyFromStorageByWidgetId(widgetId); - if (!key.isValid()) { - if (DEBUG) Log.w(TAG, "Could not register listener for widget: " + widgetId); - return; - } + if (!key.isValid()) { + if (DEBUG) Log.w(TAG, "Could not register listener for widget: " + widgetId); + return; } + TileConversationListener newListener = new TileConversationListener(); synchronized (mListeners) { if (mListeners.containsKey(key)) { if (DEBUG) Log.d(TAG, "Already registered listener"); @@ -760,7 +862,7 @@ public class PeopleSpaceWidgetManager { /** Unregisters the conversation listener for {@code appWidgetId}. */ private void unregisterConversationListener(PeopleTileKey key, int appWidgetId) { - PeopleSpaceWidgetProvider.TileConversationListener registeredListener; + TileConversationListener registeredListener; synchronized (mListeners) { registeredListener = mListeners.get(key); if (registeredListener == null) { @@ -875,9 +977,153 @@ public class PeopleSpaceWidgetManager { return null; } - PeopleSpaceTile augmentedTile = augmentTileFromNotificationEntryManager(tile); + PeopleSpaceTile augmentedTile = augmentTileFromNotificationEntryManager(tile, + Optional.empty()); if (DEBUG) Log.i(TAG, "Returning tile preview for shortcutId: " + shortcutId); - return new PeopleTileViewHelper(mContext, augmentedTile, 0, options).getViews(); + return new PeopleTileViewHelper(mContext, augmentedTile, 0, options, + new PeopleTileKey(augmentedTile)).getViews(); + } + + protected final BroadcastReceiver mBaseBroadcastReceiver = new BroadcastReceiver() { + + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (DEBUG) Log.d(TAG, "Update widgets from: " + action); + mBgExecutor.execute(() -> updateWidgetsOnStateChange(action)); + } + }; + + /** Updates any app widget based on the current state. */ + @VisibleForTesting + void updateWidgetsOnStateChange(String entryPoint) { + int[] appWidgetIds = mAppWidgetManager.getAppWidgetIds( + new ComponentName(mContext, PeopleSpaceWidgetProvider.class)); + if (appWidgetIds == null) { + return; + } + synchronized (mLock) { + for (int appWidgetId : appWidgetIds) { + PeopleSpaceTile tile = getTileForExistingWidget(appWidgetId); + if (tile == null) { + Log.e(TAG, "Matching conversation not found for shortcut ID"); + } else { + tile = updateWithCurrentState(tile, entryPoint); + } + updateAppWidgetOptionsAndView(appWidgetId, tile); + } + } + } + + /** Checks the current state of {@code tile} dependencies, updating fields as necessary. */ + @Nullable + private PeopleSpaceTile updateWithCurrentState(PeopleSpaceTile tile, + String entryPoint) { + PeopleSpaceTile.Builder updatedTile = tile.toBuilder(); + try { + switch (entryPoint) { + case NotificationManager + .ACTION_INTERRUPTION_FILTER_CHANGED: + updatedTile.setNotificationPolicyState(getNotificationPolicyState()); + break; + case Intent.ACTION_PACKAGES_SUSPENDED: + case Intent.ACTION_PACKAGES_UNSUSPENDED: + updatedTile.setIsPackageSuspended(getPackageSuspended(tile)); + break; + case Intent.ACTION_MANAGED_PROFILE_AVAILABLE: + case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE: + case Intent.ACTION_USER_UNLOCKED: + updatedTile.setIsUserQuieted(getUserQuieted(tile)); + break; + case Intent.ACTION_LOCALE_CHANGED: + break; + case ACTION_BOOT_COMPLETED: + default: + updatedTile.setIsUserQuieted(getUserQuieted(tile)).setIsPackageSuspended( + getPackageSuspended(tile)).setNotificationPolicyState( + getNotificationPolicyState()); + } + } catch (Exception e) { + Log.e(TAG, "Package no longer found for tile: " + tile.toString() + e); + return null; + } + return updatedTile.build(); + } + + private boolean getPackageSuspended(PeopleSpaceTile tile) throws Exception { + boolean packageSuspended = !TextUtils.isEmpty(tile.getPackageName()) + && mPackageManager.isPackageSuspended(tile.getPackageName()); + if (DEBUG) Log.d(TAG, "Package suspended: " + packageSuspended); + return packageSuspended; + } + + private boolean getUserQuieted(PeopleSpaceTile tile) { + boolean workProfileQuieted = + tile.getUserHandle() != null && mUserManager.isQuietModeEnabled( + tile.getUserHandle()); + if (DEBUG) Log.d(TAG, "Work profile quiet: " + workProfileQuieted); + return workProfileQuieted; + } + + private int getNotificationPolicyState() { + NotificationManager.Policy policy = mNotificationManager.getNotificationPolicy(); + boolean suppressVisualEffects = + NotificationManager.Policy.areAllVisualEffectsSuppressed( + policy.suppressedVisualEffects); + int notificationPolicyState = 0; + switch (mNotificationManager.getCurrentInterruptionFilter()) { + case INTERRUPTION_FILTER_ALL: + if (DEBUG) Log.d(TAG, "All interruptions allowed"); + return PeopleSpaceTile.SHOW_CONVERSATIONS; + case INTERRUPTION_FILTER_PRIORITY: + if (policy.allowConversations()) { + // If the user sees notifications in DND, show notifications in tiles in DND. + if (!suppressVisualEffects) { + if (DEBUG) Log.d(TAG, "Visual effects not suppressed."); + return PeopleSpaceTile.SHOW_CONVERSATIONS; + } + if (policy.priorityConversationSenders == CONVERSATION_SENDERS_ANYONE) { + if (DEBUG) Log.d(TAG, "All conversations allowed"); + // We only show conversations, so we can show everything. + return PeopleSpaceTile.SHOW_CONVERSATIONS; + } else if (policy.priorityConversationSenders + == NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT) { + if (DEBUG) Log.d(TAG, "Important conversations allowed"); + notificationPolicyState |= PeopleSpaceTile.SHOW_IMPORTANT_CONVERSATIONS; + } + } + if (policy.allowMessages()) { + switch (policy.allowMessagesFrom()) { + case ZenModeConfig.SOURCE_CONTACT: + if (DEBUG) Log.d(TAG, "All contacts allowed"); + notificationPolicyState |= PeopleSpaceTile.SHOW_CONTACTS; + return notificationPolicyState; + case ZenModeConfig.SOURCE_STAR: + if (DEBUG) Log.d(TAG, "Starred contacts allowed"); + notificationPolicyState |= PeopleSpaceTile.SHOW_STARRED_CONTACTS; + return notificationPolicyState; + case ZenModeConfig.SOURCE_ANYONE: + default: + if (DEBUG) Log.d(TAG, "All messages allowed"); + return PeopleSpaceTile.SHOW_CONVERSATIONS; + } + } + if (notificationPolicyState != 0) { + if (DEBUG) Log.d(TAG, "Return block state: " + notificationPolicyState); + return notificationPolicyState; + } + // If only alarms or nothing can bypass DND, the tile shouldn't show conversations. + case INTERRUPTION_FILTER_NONE: + case INTERRUPTION_FILTER_ALARMS: + default: + // If the user sees notifications in DND, show notifications in tiles in DND. + if (!suppressVisualEffects) { + if (DEBUG) Log.d(TAG, "Visual effects not suppressed."); + return PeopleSpaceTile.SHOW_CONVERSATIONS; + } + if (DEBUG) Log.d(TAG, "Block conversations"); + return PeopleSpaceTile.BLOCK_CONVERSATIONS; + } } } diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java index 3bc5b29bd05d..3522b76e6460 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java @@ -16,9 +16,6 @@ package com.android.systemui.people.widget; -import android.annotation.NonNull; -import android.app.people.ConversationChannel; -import android.app.people.PeopleManager; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProvider; import android.content.Context; @@ -28,6 +25,8 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.people.PeopleSpaceUtils; +import javax.inject.Inject; + /** People Space Widget Provider class. */ public class PeopleSpaceWidgetProvider extends AppWidgetProvider { private static final String TAG = "PeopleSpaceWidgetPvd"; @@ -38,25 +37,11 @@ public class PeopleSpaceWidgetProvider extends AppWidgetProvider { public static final String EXTRA_USER_HANDLE = "extra_user_handle"; public static final String EXTRA_NOTIFICATION_KEY = "extra_notification_key"; - public PeopleSpaceWidgetManager peopleSpaceWidgetManager; - - /** Listener for the shortcut data changes. */ - public class TileConversationListener implements PeopleManager.ConversationListener { + public PeopleSpaceWidgetManager mPeopleSpaceWidgetManager; - @Override - public void onConversationUpdate(@NonNull ConversationChannel conversation) { - if (DEBUG) { - Log.d(TAG, - "Received updated conversation: " - + conversation.getShortcutInfo().getLabel()); - } - if (peopleSpaceWidgetManager == null) { - // This shouldn't happen since onUpdate is called at reboot. - Log.e(TAG, "Skipping conversation update: WidgetManager uninitialized"); - return; - } - peopleSpaceWidgetManager.updateWidgetsWithConversationChanged(conversation); - } + @Inject + PeopleSpaceWidgetProvider(PeopleSpaceWidgetManager peopleSpaceWidgetManager) { + mPeopleSpaceWidgetManager = peopleSpaceWidgetManager; } /** Called when widget updates. */ @@ -65,15 +50,8 @@ public class PeopleSpaceWidgetProvider extends AppWidgetProvider { super.onUpdate(context, appWidgetManager, appWidgetIds); if (DEBUG) Log.d(TAG, "onUpdate called"); - ensurePeopleSpaceWidgetManagerInitialized(context); - peopleSpaceWidgetManager.updateWidgets(appWidgetIds); - for (int appWidgetId : appWidgetIds) { - if (DEBUG) Log.d(TAG, "Ensure listener is registered for widget: " + appWidgetId); - PeopleSpaceWidgetProvider.TileConversationListener - newListener = new PeopleSpaceWidgetProvider.TileConversationListener(); - peopleSpaceWidgetManager.registerConversationListenerIfNeeded(appWidgetId, - newListener); - } + ensurePeopleSpaceWidgetManagerInitialized(); + mPeopleSpaceWidgetManager.updateWidgets(appWidgetIds); } /** Called when widget updates. */ @@ -81,25 +59,23 @@ public class PeopleSpaceWidgetProvider extends AppWidgetProvider { public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) { super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions); - ensurePeopleSpaceWidgetManagerInitialized(context); - peopleSpaceWidgetManager.onAppWidgetOptionsChanged(appWidgetId, newOptions); + ensurePeopleSpaceWidgetManagerInitialized(); + mPeopleSpaceWidgetManager.onAppWidgetOptionsChanged(appWidgetId, newOptions); } @Override public void onDeleted(Context context, int[] appWidgetIds) { super.onDeleted(context, appWidgetIds); - ensurePeopleSpaceWidgetManagerInitialized(context); - peopleSpaceWidgetManager.deleteWidgets(appWidgetIds); + ensurePeopleSpaceWidgetManagerInitialized(); + mPeopleSpaceWidgetManager.deleteWidgets(appWidgetIds); } - private void ensurePeopleSpaceWidgetManagerInitialized(Context context) { - if (peopleSpaceWidgetManager == null) { - peopleSpaceWidgetManager = new PeopleSpaceWidgetManager(context); - } + private void ensurePeopleSpaceWidgetManagerInitialized() { + mPeopleSpaceWidgetManager.init(); } @VisibleForTesting public void setPeopleSpaceWidgetManager(PeopleSpaceWidgetManager manager) { - peopleSpaceWidgetManager = manager; + mPeopleSpaceWidgetManager = manager; } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java index cc322620eb57..0dd1f6816787 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java @@ -24,7 +24,6 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -59,6 +58,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.appwidget.IAppWidgetService; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.people.widget.PeopleSpaceWidgetManager; import com.android.systemui.people.widget.PeopleTileKey; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; @@ -73,6 +73,7 @@ import org.mockito.MockitoAnnotations; import java.util.List; import java.util.Map; +import java.util.Optional; @RunWith(AndroidTestingRunner.class) @SmallTest @@ -188,6 +189,8 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { private PackageManager mPackageManager; @Mock private NotificationEntryManager mNotificationEntryManager; + @Mock + private PeopleSpaceWidgetManager mPeopleSpaceWidgetManager; private Bundle mOptions; @@ -212,8 +215,8 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { when(resources.getConfiguration()).thenReturn(configuration); when(resources.getDisplayMetrics()).thenReturn(displayMetrics); when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver); - when(mMockContentResolver.query(any(Uri.class), any(), anyString(), any(), - isNull())).thenReturn(mMockCursor); + when(mMockContentResolver.query(any(Uri.class), any(), any(), any(), + any())).thenReturn(mMockCursor); when(mMockContext.getString(R.string.birthday_status)).thenReturn( mContext.getString(R.string.birthday_status)); when(mMockContext.getString(R.string.basic_status)).thenReturn( @@ -236,7 +239,8 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { .build(); PeopleTileKey key = new PeopleTileKey(tile); PeopleSpaceTile actual = PeopleSpaceUtils - .augmentTileFromNotification(mContext, tile, key, mNotificationEntry1, 0); + .augmentTileFromNotification(mContext, tile, key, mNotificationEntry1, 0, + Optional.empty()); assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_2); assertThat(actual.getNotificationSender()).isEqualTo(null); @@ -275,7 +279,8 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { .build(); PeopleTileKey key = new PeopleTileKey(tile); PeopleSpaceTile actual = PeopleSpaceUtils - .augmentTileFromNotification(mContext, tile, key, notificationEntry, 0); + .augmentTileFromNotification(mContext, tile, key, notificationEntry, 0, + Optional.empty()); assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_2); assertThat(actual.getNotificationSender().toString()).isEqualTo("name"); @@ -291,7 +296,8 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { .build(); PeopleTileKey key = new PeopleTileKey(tile); PeopleSpaceTile actual = PeopleSpaceUtils - .augmentTileFromNotification(mContext, tile, key, mNotificationEntry3, 0); + .augmentTileFromNotification(mContext, tile, key, mNotificationEntry3, 0, + Optional.empty()); assertThat(actual.getNotificationContent()).isEqualTo(null); } @@ -308,10 +314,11 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { Map<Integer, PeopleSpaceTile> widgetIdToTile = Map.of(WIDGET_ID_WITH_SHORTCUT, new PeopleSpaceTile.Builder(mShortcutInfoWithoutPerson, mContext.getSystemService(LauncherApps.class)).build()); - PeopleSpaceUtils.getBirthdays(mMockContext, mAppWidgetManager, + PeopleSpaceUtils.getDataFromContacts(mMockContext, mPeopleSpaceWidgetManager, widgetIdToTile, widgetIdsArray); - verify(mAppWidgetManager, never()).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT), + verify(mPeopleSpaceWidgetManager, never()).updateAppWidgetOptionsAndView( + eq(WIDGET_ID_WITH_SHORTCUT), any()); } @@ -328,10 +335,11 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { new PeopleSpaceTile.Builder(mShortcutInfoWithoutPerson, mContext.getSystemService(LauncherApps.class)).setBirthdayText( mContext.getString(R.string.birthday_status)).build()); - PeopleSpaceUtils.getBirthdays(mMockContext, mAppWidgetManager, + PeopleSpaceUtils.getDataFromContacts(mMockContext, mPeopleSpaceWidgetManager, widgetIdToTile, widgetIdsArray); - verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT), + verify(mPeopleSpaceWidgetManager, times(1)).updateAppWidgetOptionsAndView( + eq(WIDGET_ID_WITH_SHORTCUT), any()); } @@ -363,10 +371,11 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { new PeopleSpaceTile.Builder(mShortcutInfo, mContext.getSystemService(LauncherApps.class)).setBirthdayText( mContext.getString(R.string.birthday_status)).build()); - PeopleSpaceUtils.getBirthdays(mMockContext, mAppWidgetManager, + PeopleSpaceUtils.getDataFromContacts(mMockContext, mPeopleSpaceWidgetManager, widgetIdToTile, widgetIdsArray); - verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT), + verify(mPeopleSpaceWidgetManager, times(1)).updateAppWidgetOptionsAndView( + eq(WIDGET_ID_WITH_SHORTCUT), any()); } @@ -375,6 +384,9 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT}; when(mMockCursor.moveToNext()).thenReturn(true, false, true, false); when(mMockCursor.getString(eq(TEST_COLUMN_INDEX))).thenReturn(TEST_LOOKUP_KEY); + when(mMockCursor.getInt(eq(TEST_COLUMN_INDEX + 1))).thenReturn(1); + when(mMockCursor.getColumnIndex(eq(ContactsContract.Contacts.STARRED))).thenReturn( + TEST_COLUMN_INDEX + 1); when(mMockCursor.getColumnIndex(eq(ContactsContract.CommonDataKinds.Event.LOOKUP_KEY) )).thenReturn(TEST_COLUMN_INDEX); @@ -383,10 +395,11 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { new PeopleSpaceTile.Builder(mShortcutInfo, mContext.getSystemService(LauncherApps.class)).setBirthdayText( mContext.getString(R.string.birthday_status)).build()); - PeopleSpaceUtils.getBirthdays(mMockContext, mAppWidgetManager, + PeopleSpaceUtils.getDataFromContacts(mMockContext, mPeopleSpaceWidgetManager, widgetIdToTile, widgetIdsArray); - verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT), + verify(mPeopleSpaceWidgetManager, times(1)).updateAppWidgetOptionsAndView( + eq(WIDGET_ID_WITH_SHORTCUT), any()); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java index 764cdee7e36d..228e5e8d481d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java @@ -21,13 +21,20 @@ import static android.app.people.ConversationStatus.ACTIVITY_BIRTHDAY; import static android.app.people.ConversationStatus.ACTIVITY_GAME; import static android.app.people.ConversationStatus.ACTIVITY_NEW_STORY; import static android.app.people.ConversationStatus.AVAILABILITY_AVAILABLE; +import static android.app.people.PeopleSpaceTile.BLOCK_CONVERSATIONS; +import static android.app.people.PeopleSpaceTile.SHOW_CONTACTS; +import static android.app.people.PeopleSpaceTile.SHOW_IMPORTANT_CONVERSATIONS; +import static android.app.people.PeopleSpaceTile.SHOW_STARRED_CONTACTS; import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH; +import static com.android.systemui.people.PeopleSpaceUtils.STARRED_CONTACT; +import static com.android.systemui.people.PeopleSpaceUtils.VALID_CONTACT; import static com.android.systemui.people.widget.AppWidgetOptionsHelper.OPTIONS_PEOPLE_TILE; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -53,6 +60,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.people.widget.PeopleTileKey; import org.junit.Before; import org.junit.Test; @@ -148,14 +156,14 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { TextView textView = mock(TextView.class); when(textView.getLineHeight()).thenReturn(16); when(mPackageManager.getApplicationIcon(anyString())).thenReturn(null); - mPeopleTileViewHelper = new PeopleTileViewHelper(mContext, - PERSON_TILE, 0, mOptions); + mPeopleTileViewHelper = getPeopleTileViewHelper( + PERSON_TILE, mOptions); } @Test public void testCreateRemoteViewsWithLastInteractionTimeUnderOneDayHidden() { - RemoteViews views = new PeopleTileViewHelper(mContext, - PERSON_TILE_WITHOUT_NOTIFICATION, 0, mOptions).getViews(); + RemoteViews views = getPeopleTileViewHelper( + PERSON_TILE_WITHOUT_NOTIFICATION, mOptions).getViews(); View result = views.apply(mContext, null); // Not showing last interaction. @@ -165,8 +173,8 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { getSizeInDp(R.dimen.required_width_for_large)); mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, getSizeInDp(R.dimen.required_height_for_large)); - RemoteViews largeView = new PeopleTileViewHelper(mContext, - PERSON_TILE_WITHOUT_NOTIFICATION, 0, mOptions).getViews(); + RemoteViews largeView = getPeopleTileViewHelper( + PERSON_TILE_WITHOUT_NOTIFICATION, mOptions).getViews(); View largeResult = largeView.apply(mContext, null); // Not showing last interaction. @@ -178,8 +186,8 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { PeopleSpaceTile tileWithLastInteraction = PERSON_TILE_WITHOUT_NOTIFICATION.toBuilder().setLastInteractionTimestamp( 123445L).build(); - RemoteViews views = new PeopleTileViewHelper(mContext, - tileWithLastInteraction, 0, mOptions).getViews(); + RemoteViews views = getPeopleTileViewHelper( + tileWithLastInteraction, mOptions).getViews(); View result = views.apply(mContext, null); TextView name = (TextView) result.findViewById(R.id.name); @@ -197,8 +205,8 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, getSizeInDp(R.dimen.required_width_for_medium) - 1); - RemoteViews smallView = new PeopleTileViewHelper(mContext, - tileWithLastInteraction, 0, mOptions).getViews(); + RemoteViews smallView = getPeopleTileViewHelper( + tileWithLastInteraction, mOptions).getViews(); View smallResult = smallView.apply(mContext, null); // Show name over predefined icon. @@ -214,8 +222,8 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { getSizeInDp(R.dimen.required_width_for_large)); mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, getSizeInDp(R.dimen.required_height_for_large)); - RemoteViews largeView = new PeopleTileViewHelper(mContext, - tileWithLastInteraction, 0, mOptions).getViews(); + RemoteViews largeView = getPeopleTileViewHelper( + tileWithLastInteraction, mOptions).getViews(); View largeResult = largeView.apply(mContext, null); name = (TextView) largeResult.findViewById(R.id.name); @@ -240,8 +248,8 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { new ConversationStatus.Builder( PERSON_TILE_WITHOUT_NOTIFICATION.getId(), ACTIVITY_GAME).build())).build(); - RemoteViews views = new PeopleTileViewHelper(mContext, - tileWithAvailabilityAndNewStory, 0, mOptions).getViews(); + RemoteViews views = getPeopleTileViewHelper( + tileWithAvailabilityAndNewStory, mOptions).getViews(); View result = views.apply(mContext, null); TextView name = (TextView) result.findViewById(R.id.name); @@ -257,8 +265,8 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, getSizeInDp(R.dimen.required_width_for_medium) - 1); - RemoteViews smallView = new PeopleTileViewHelper(mContext, - tileWithAvailabilityAndNewStory, 0, mOptions).getViews(); + RemoteViews smallView = getPeopleTileViewHelper( + tileWithAvailabilityAndNewStory, mOptions).getViews(); View smallResult = smallView.apply(mContext, null); // Show name rather than game type. @@ -274,8 +282,8 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { getSizeInDp(R.dimen.required_width_for_large)); mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, getSizeInDp(R.dimen.required_height_for_large)); - RemoteViews largeView = new PeopleTileViewHelper(mContext, - tileWithAvailabilityAndNewStory, 0, mOptions).getViews(); + RemoteViews largeView = getPeopleTileViewHelper( + tileWithAvailabilityAndNewStory, mOptions).getViews(); View largeResult = largeView.apply(mContext, null); name = (TextView) largeResult.findViewById(R.id.name); @@ -298,8 +306,8 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { NEW_STORY_WITH_AVAILABILITY, new ConversationStatus.Builder( PERSON_TILE_WITHOUT_NOTIFICATION.getId(), ACTIVITY_BIRTHDAY).build())).build(); - RemoteViews views = new PeopleTileViewHelper(mContext, - tileWithStatusTemplate, 0, mOptions).getViews(); + RemoteViews views = getPeopleTileViewHelper( + tileWithStatusTemplate, mOptions).getViews(); View result = views.apply(mContext, null); TextView name = (TextView) result.findViewById(R.id.name); @@ -318,8 +326,8 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, getSizeInDp(R.dimen.required_width_for_medium) - 1); - RemoteViews smallView = new PeopleTileViewHelper(mContext, - tileWithStatusTemplate, 0, mOptions).getViews(); + RemoteViews smallView = getPeopleTileViewHelper( + tileWithStatusTemplate, mOptions).getViews(); View smallResult = smallView.apply(mContext, null); // Show icon instead of name. @@ -336,8 +344,8 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { getSizeInDp(R.dimen.required_width_for_large)); mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, getSizeInDp(R.dimen.required_height_for_large)); - RemoteViews largeView = new PeopleTileViewHelper(mContext, - tileWithStatusTemplate, 0, mOptions).getViews(); + RemoteViews largeView = getPeopleTileViewHelper( + tileWithStatusTemplate, mOptions).getViews(); View largeResult = largeView.apply(mContext, null); name = (TextView) largeResult.findViewById(R.id.name); @@ -362,8 +370,8 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { PERSON_TILE_WITHOUT_NOTIFICATION.toBuilder().setStatuses( Arrays.asList(GAME_STATUS, NEW_STORY_WITH_AVAILABILITY)).build(); - RemoteViews views = new PeopleTileViewHelper(mContext, - tileWithStatusTemplate, 0, mOptions).getViews(); + RemoteViews views = getPeopleTileViewHelper( + tileWithStatusTemplate, mOptions).getViews(); View result = views.apply(mContext, null); TextView name = (TextView) result.findViewById(R.id.name); @@ -381,8 +389,8 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, getSizeInDp(R.dimen.required_width_for_medium) - 1); - RemoteViews smallView = new PeopleTileViewHelper(mContext, - tileWithStatusTemplate, 0, mOptions).getViews(); + RemoteViews smallView = getPeopleTileViewHelper( + tileWithStatusTemplate, mOptions).getViews(); View smallResult = smallView.apply(mContext, null); // Show icon instead of name. @@ -399,8 +407,8 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { getSizeInDp(R.dimen.required_width_for_large)); mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, getSizeInDp(R.dimen.required_height_for_large)); - RemoteViews largeView = new PeopleTileViewHelper(mContext, - tileWithStatusTemplate, 0, mOptions).getViews(); + RemoteViews largeView = getPeopleTileViewHelper( + tileWithStatusTemplate, mOptions).getViews(); View largeResult = largeView.apply(mContext, null); name = (TextView) largeResult.findViewById(R.id.name); @@ -420,14 +428,128 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { } @Test + public void testCreateRemoteViewsWithPackageSuspended() { + PeopleSpaceTile tile = PERSON_TILE.toBuilder() + .setIsPackageSuspended(true) + .build(); + RemoteViews views = getPeopleTileViewHelper( + tile, mOptions).getViews(); + View result = views.apply(mContext, null); + + assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout); + } + + @Test + public void testCreateRemoteViewsWithUserQuieted() { + PeopleSpaceTile tile = PERSON_TILE.toBuilder() + .setIsUserQuieted(true) + .build(); + RemoteViews views = getPeopleTileViewHelper( + tile, mOptions).getViews(); + View result = views.apply(mContext, null); + + assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout); + } + + @Test + public void testCreateRemoteViewsWithDndBlocking() { + PeopleSpaceTile tileWithDndBlocking = PERSON_TILE.toBuilder() + .setNotificationPolicyState(BLOCK_CONVERSATIONS) + .build(); + RemoteViews views = getPeopleTileViewHelper( + tileWithDndBlocking, mOptions).getViews(); + View result = views.apply(mContext, null); + + assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout); + + tileWithDndBlocking = PERSON_TILE.toBuilder() + .setNotificationPolicyState(BLOCK_CONVERSATIONS) + .setCanBypassDnd(true) + .build(); + views = getPeopleTileViewHelper( + tileWithDndBlocking, mOptions).getViews(); + result = views.apply(mContext, null); + + assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout); + + tileWithDndBlocking = PERSON_TILE.toBuilder() + .setNotificationPolicyState(SHOW_IMPORTANT_CONVERSATIONS) + .build(); + views = getPeopleTileViewHelper( + tileWithDndBlocking, mOptions).getViews(); + result = views.apply(mContext, null); + + assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout); + + tileWithDndBlocking = PERSON_TILE.toBuilder() + .setNotificationPolicyState(SHOW_IMPORTANT_CONVERSATIONS) + .setIsImportantConversation(true) + .build(); + views = getPeopleTileViewHelper( + tileWithDndBlocking, mOptions).getViews(); + result = views.apply(mContext, null); + + assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout); + + tileWithDndBlocking = PERSON_TILE.toBuilder() + .setNotificationPolicyState(SHOW_STARRED_CONTACTS) + .setContactAffinity(VALID_CONTACT) + .build(); + views = getPeopleTileViewHelper( + tileWithDndBlocking, mOptions).getViews(); + result = views.apply(mContext, null); + + assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout); + + tileWithDndBlocking = PERSON_TILE.toBuilder() + .setNotificationPolicyState(SHOW_STARRED_CONTACTS) + .setContactAffinity(STARRED_CONTACT) + .build(); + views = getPeopleTileViewHelper( + tileWithDndBlocking, mOptions).getViews(); + result = views.apply(mContext, null); + + assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout); + + tileWithDndBlocking = PERSON_TILE.toBuilder() + .setNotificationPolicyState(SHOW_CONTACTS) + .setContactAffinity(STARRED_CONTACT) + .build(); + views = getPeopleTileViewHelper( + tileWithDndBlocking, mOptions).getViews(); + result = views.apply(mContext, null); + + assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout); + + tileWithDndBlocking = PERSON_TILE.toBuilder() + .setNotificationPolicyState(SHOW_CONTACTS) + .setContactAffinity(VALID_CONTACT) + .build(); + views = getPeopleTileViewHelper( + tileWithDndBlocking, mOptions).getViews(); + result = views.apply(mContext, null); + + assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout); + + tileWithDndBlocking = PERSON_TILE.toBuilder() + .setNotificationPolicyState(SHOW_CONTACTS) + .build(); + views = getPeopleTileViewHelper( + tileWithDndBlocking, mOptions).getViews(); + result = views.apply(mContext, null); + + assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout); + } + + @Test public void testCreateRemoteViewsWithMissedCallNotification() { PeopleSpaceTile tileWithMissedCallNotification = PERSON_TILE.toBuilder() .setNotificationDataUri(null) .setNotificationCategory(CATEGORY_MISSED_CALL) .setNotificationContent(MISSED_CALL) .build(); - RemoteViews views = new PeopleTileViewHelper(mContext, - tileWithMissedCallNotification, 0, mOptions).getViews(); + RemoteViews views = getPeopleTileViewHelper( + tileWithMissedCallNotification, mOptions).getViews(); View result = views.apply(mContext, null); TextView name = (TextView) result.findViewById(R.id.name); @@ -446,8 +568,8 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, getSizeInDp(R.dimen.required_width_for_medium) - 1); - RemoteViews smallView = new PeopleTileViewHelper(mContext, - tileWithMissedCallNotification, 0, mOptions).getViews(); + RemoteViews smallView = getPeopleTileViewHelper( + tileWithMissedCallNotification, mOptions).getViews(); View smallResult = smallView.apply(mContext, null); // Show icon instead of name. @@ -463,8 +585,8 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { getSizeInDp(R.dimen.required_width_for_large)); mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, getSizeInDp(R.dimen.required_height_for_large)); - RemoteViews largeView = new PeopleTileViewHelper(mContext, - tileWithMissedCallNotification, 0, mOptions).getViews(); + RemoteViews largeView = getPeopleTileViewHelper( + tileWithMissedCallNotification, mOptions).getViews(); View largeResult = largeView.apply(mContext, null); name = (TextView) largeResult.findViewById(R.id.name); @@ -489,8 +611,8 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { .setNotificationDataUri(null) .setStatuses(Arrays.asList(GAME_STATUS, NEW_STORY_WITH_AVAILABILITY)).build(); - RemoteViews views = new PeopleTileViewHelper(mContext, - tileWithStatusAndNotification, 0, mOptions).getViews(); + RemoteViews views = getPeopleTileViewHelper( + tileWithStatusAndNotification, mOptions).getViews(); View result = views.apply(mContext, null); TextView name = (TextView) result.findViewById(R.id.name); @@ -512,8 +634,8 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, getSizeInDp(R.dimen.required_width_for_medium) - 1); - RemoteViews smallView = new PeopleTileViewHelper(mContext, - tileWithStatusAndNotification, 0, mOptions).getViews(); + RemoteViews smallView = getPeopleTileViewHelper( + tileWithStatusAndNotification, mOptions).getViews(); View smallResult = smallView.apply(mContext, null); // Show icon instead of name. @@ -531,8 +653,8 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { getSizeInDp(R.dimen.required_width_for_large)); mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, getSizeInDp(R.dimen.required_height_for_large)); - RemoteViews largeView = new PeopleTileViewHelper(mContext, - tileWithStatusAndNotification, 0, mOptions).getViews(); + RemoteViews largeView = getPeopleTileViewHelper( + tileWithStatusAndNotification, mOptions).getViews(); View largeResult = largeView.apply(mContext, null); name = (TextView) largeResult.findViewById(R.id.name); @@ -561,8 +683,8 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { .setNotificationDataUri(null) .setStatuses(Arrays.asList(GAME_STATUS, NEW_STORY_WITH_AVAILABILITY)).build(); - RemoteViews views = new PeopleTileViewHelper(mContext, - tileWithStatusAndNotification, 0, mOptions).getViews(); + RemoteViews views = getPeopleTileViewHelper( + tileWithStatusAndNotification, mOptions).getViews(); View result = views.apply(mContext, null); TextView name = (TextView) result.findViewById(R.id.name); @@ -588,8 +710,8 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, getSizeInDp(R.dimen.required_width_for_medium) - 1); - RemoteViews smallView = new PeopleTileViewHelper(mContext, - tileWithStatusAndNotification, 0, mOptions).getViews(); + RemoteViews smallView = getPeopleTileViewHelper( + tileWithStatusAndNotification, mOptions).getViews(); View smallResult = smallView.apply(mContext, null); // Show icon instead of name. @@ -607,8 +729,8 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { getSizeInDp(R.dimen.required_width_for_large)); mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, getSizeInDp(R.dimen.required_height_for_large)); - RemoteViews largeView = new PeopleTileViewHelper(mContext, - tileWithStatusAndNotification, 0, mOptions).getViews(); + RemoteViews largeView = getPeopleTileViewHelper( + tileWithStatusAndNotification, mOptions).getViews(); View largeResult = largeView.apply(mContext, null); name = (TextView) largeResult.findViewById(R.id.name); @@ -642,8 +764,8 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { .setStatuses(Arrays.asList(GAME_STATUS, NEW_STORY_WITH_AVAILABILITY)) .setMessagesCount(2).build(); - RemoteViews views = new PeopleTileViewHelper(mContext, - tileWithStatusAndNotification, 0, mOptions).getViews(); + RemoteViews views = getPeopleTileViewHelper( + tileWithStatusAndNotification, mOptions).getViews(); View result = views.apply(mContext, null); TextView name = (TextView) result.findViewById(R.id.name); @@ -665,8 +787,8 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, getSizeInDp(R.dimen.required_width_for_medium) - 1); - RemoteViews smallView = new PeopleTileViewHelper(mContext, - tileWithStatusAndNotification, 0, mOptions).getViews(); + RemoteViews smallView = getPeopleTileViewHelper( + tileWithStatusAndNotification, mOptions).getViews(); View smallResult = smallView.apply(mContext, null); // Show icon instead of name. @@ -684,8 +806,8 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { getSizeInDp(R.dimen.required_width_for_large)); mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, getSizeInDp(R.dimen.required_height_for_large)); - RemoteViews largeView = new PeopleTileViewHelper(mContext, - tileWithStatusAndNotification, 0, mOptions).getViews(); + RemoteViews largeView = getPeopleTileViewHelper( + tileWithStatusAndNotification, mOptions).getViews(); View largeResult = largeView.apply(mContext, null); name = (TextView) largeResult.findViewById(R.id.name); @@ -858,4 +980,9 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { return (int) (mContext.getResources().getDimension(dimenResourceId) / mContext.getResources().getDisplayMetrics().density); } + + private PeopleTileViewHelper getPeopleTileViewHelper(PeopleSpaceTile tile, Bundle options) { + return new PeopleTileViewHelper(mContext, tile, 0, options, + new PeopleTileKey(tile.getId(), 0, tile.getPackageName())); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java index 411fb02ba87a..f31f326c0c45 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java @@ -20,11 +20,32 @@ import static android.app.Notification.CATEGORY_MISSED_CALL; import static android.app.Notification.EXTRA_PEOPLE_LIST; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_HIGH; +import static android.app.NotificationManager.INTERRUPTION_FILTER_ALARMS; +import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL; +import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; +import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR; import static android.app.people.ConversationStatus.ACTIVITY_ANNIVERSARY; import static android.app.people.ConversationStatus.ACTIVITY_BIRTHDAY; import static android.app.people.ConversationStatus.ACTIVITY_GAME; +import static android.app.people.PeopleSpaceTile.BLOCK_CONVERSATIONS; +import static android.app.people.PeopleSpaceTile.SHOW_CONTACTS; +import static android.app.people.PeopleSpaceTile.SHOW_CONVERSATIONS; +import static android.app.people.PeopleSpaceTile.SHOW_IMPORTANT_CONVERSATIONS; +import static android.app.people.PeopleSpaceTile.SHOW_STARRED_CONTACTS; +import static android.content.Intent.ACTION_BOOT_COMPLETED; +import static android.content.Intent.ACTION_PACKAGES_SUSPENDED; import static android.content.PermissionChecker.PERMISSION_GRANTED; import static android.content.PermissionChecker.PERMISSION_HARD_DENIED; +import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_ANYONE; import static com.android.systemui.people.PeopleSpaceUtils.EMPTY_STRING; import static com.android.systemui.people.PeopleSpaceUtils.INVALID_USER_ID; @@ -73,6 +94,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.service.notification.ConversationChannelWrapper; import android.service.notification.StatusBarNotification; +import android.service.notification.ZenModeConfig; import android.testing.AndroidTestingRunner; import androidx.preference.PreferenceManager; @@ -89,6 +111,7 @@ import com.android.systemui.statusbar.notification.collection.NoManSimulator; import com.android.systemui.statusbar.notification.collection.NoManSimulator.NotifEvent; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; +import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; import org.junit.Before; @@ -99,12 +122,14 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -158,6 +183,16 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { // Same contact uri. .setContactUri(URI) .build(); + private static final int ALL_SUPPRESSED_VISUAL_EFFECTS = SUPPRESSED_EFFECT_SCREEN_OFF + | SUPPRESSED_EFFECT_SCREEN_ON + | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT + | SUPPRESSED_EFFECT_AMBIENT + | SUPPRESSED_EFFECT_STATUS_BAR + | SUPPRESSED_EFFECT_BADGE + | SUPPRESSED_EFFECT_LIGHTS + | SUPPRESSED_EFFECT_PEEK + | SUPPRESSED_EFFECT_NOTIFICATION_LIST; + private ShortcutInfo mShortcutInfo; private NotificationEntry mNotificationEntry; @@ -182,9 +217,13 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { @Mock private PackageManager mPackageManager; @Mock - private INotificationManager mNotificationManager; + private INotificationManager mINotificationManager; @Mock private UserManager mUserManager; + @Mock + private NotificationManager mNotificationManager; + @Mock + private NotificationManager.Policy mNotificationPolicy; @Captor private ArgumentCaptor<NotificationHandler> mListenerCaptor; @@ -194,19 +233,16 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { private final NoManSimulator mNoMan = new NoManSimulator(); private final FakeSystemClock mClock = new FakeSystemClock(); - private PeopleSpaceWidgetProvider mProvider; + private final FakeExecutor mFakeExecutor = new FakeExecutor(mClock); @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mLauncherApps = mock(LauncherApps.class); mDependency.injectTestDependency(NotificationEntryManager.class, mNotificationEntryManager); - mManager = new PeopleSpaceWidgetManager(mContext); - mProvider = new PeopleSpaceWidgetProvider(); - mProvider.setPeopleSpaceWidgetManager(mManager); - mManager.setAppWidgetManager(mAppWidgetManager, mIPeopleManager, mPeopleManager, - mLauncherApps, mNotificationEntryManager, mPackageManager, true, mProvider, - mUserManager, mNotificationManager); + mManager = new PeopleSpaceWidgetManager(mContext, mAppWidgetManager, mIPeopleManager, + mPeopleManager, mLauncherApps, mNotificationEntryManager, mPackageManager, + mUserManager, mINotificationManager, mNotificationManager, mFakeExecutor); mManager.attach(mListenerService); verify(mListenerService).addNotificationHandler(mListenerCaptor.capture()); @@ -218,7 +254,19 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { addTileForWidget(PERSON_TILE_WITH_SAME_URI, WIDGET_ID_WITH_SAME_URI); when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITHOUT_SHORTCUT))) .thenReturn(new Bundle()); + when(mUserManager.isQuietModeEnabled(any())).thenReturn(false); + when(mPackageManager.isPackageSuspended(any())).thenReturn(false); + setFinalField("suppressedVisualEffects", ALL_SUPPRESSED_VISUAL_EFFECTS); + when(mNotificationPolicy.allowConversationsFrom()).thenReturn(CONVERSATION_SENDERS_ANYONE); + when(mNotificationPolicy.allowConversations()).thenReturn(false); + when(mNotificationPolicy.allowMessagesFrom()).thenReturn(ZenModeConfig.SOURCE_ANYONE); + when(mNotificationPolicy.allowMessages()).thenReturn(false); + when(mNotificationManager.getNotificationPolicy()).thenReturn(mNotificationPolicy); + when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn( + INTERRUPTION_FILTER_ALL); + int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT}; + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); when(mMockContext.getPackageName()).thenReturn(TEST_PACKAGE_A); when(mMockContext.getUserId()).thenReturn(0); @@ -242,7 +290,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { ConversationChannelWrapper olderImportantConversation = getConversationChannelWrapper( SHORTCUT_ID + 2, true, 1); - when(mNotificationManager.getConversations(anyBoolean())).thenReturn( + when(mINotificationManager.getConversations(anyBoolean())).thenReturn( new ParceledListSlice(Arrays.asList( newerNonImportantConversation, newerImportantConversation, olderImportantConversation))); @@ -280,7 +328,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { ConversationChannelWrapper olderImportantConversation = getConversationChannelWrapper( SHORTCUT_ID + 2, true, 1); - when(mNotificationManager.getConversations(anyBoolean())).thenReturn( + when(mINotificationManager.getConversations(anyBoolean())).thenReturn( new ParceledListSlice(Arrays.asList( newerNonImportantConversation, newerImportantConversation, olderImportantConversation))); @@ -306,7 +354,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { ConversationChannelWrapper olderImportantConversation = getConversationChannelWrapper( SHORTCUT_ID + 2, true, 1); - when(mNotificationManager.getConversations(anyBoolean())).thenReturn( + when(mINotificationManager.getConversations(anyBoolean())).thenReturn( new ParceledListSlice(Arrays.asList( newerNonImportantConversation, newerImportantConversation, olderImportantConversation))); @@ -1027,8 +1075,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { public void testDeleteAllWidgetsForConversationsUncachesShortcutAndRemovesListeners() throws Exception { addSecondWidgetForPersonTile(); - mProvider.onUpdate(mContext, mAppWidgetManager, - new int[]{WIDGET_ID_WITH_SHORTCUT, SECOND_WIDGET_ID_WITH_SHORTCUT}); + mManager.updateWidgets(new int[]{WIDGET_ID_WITH_SHORTCUT, SECOND_WIDGET_ID_WITH_SHORTCUT}); // Delete only one widget for the conversation. mManager.deleteWidgets(new int[]{WIDGET_ID_WITH_SHORTCUT}); @@ -1050,7 +1097,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { eq(LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS)); // Delete all widgets for the conversation. - mProvider.onDeleted(mContext, new int[]{SECOND_WIDGET_ID_WITH_SHORTCUT}); + mManager.deleteWidgets(new int[]{SECOND_WIDGET_ID_WITH_SHORTCUT}); // Check deleted storage. SharedPreferences secondWidgetSp = mContext.getSharedPreferences( @@ -1154,7 +1201,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { new PeopleTileKey(SHORTCUT_ID, 0, TEST_PACKAGE_A)); when(mIPeopleManager.getConversation(TEST_PACKAGE_A, 0, SHORTCUT_ID)).thenReturn(channel); PeopleTileKey key = new PeopleTileKey(SHORTCUT_ID, 0, TEST_PACKAGE_A); - PeopleSpaceTile tile = mManager.getTileFromPersistentStorage(key); + PeopleSpaceTile tile = mManager.getTileFromPersistentStorage(key, WIDGET_ID_WITH_SHORTCUT); assertThat(tile.getId()).isEqualTo(key.getShortcutId()); } @@ -1162,7 +1209,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { public void testGetPeopleTileFromPersistentStorageNoConversation() throws RemoteException { when(mIPeopleManager.getConversation(TEST_PACKAGE_A, 0, SHORTCUT_ID)).thenReturn(null); PeopleTileKey key = new PeopleTileKey(SHORTCUT_ID, 0, TEST_PACKAGE_A); - PeopleSpaceTile tile = mManager.getTileFromPersistentStorage(key); + PeopleSpaceTile tile = mManager.getTileFromPersistentStorage(key, WIDGET_ID_WITH_SHORTCUT); assertThat(tile).isNull(); } @@ -1195,18 +1242,25 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { @Test public void testAugmentTileFromNotifications() { + clearStorage(); + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); + assertThat(sp.getString(String.valueOf(WIDGET_ID_WITH_SHORTCUT), null)).isEqualTo(null); PeopleSpaceTile tile = new PeopleSpaceTile .Builder(SHORTCUT_ID, "userName", ICON, new Intent()) .setPackageName(TEST_PACKAGE_A) .setUserHandle(new UserHandle(0)) .build(); + PeopleTileKey key = new PeopleTileKey(tile); PeopleSpaceTile actual = mManager.augmentTileFromNotifications(tile, key, EMPTY_STRING, - Map.of(new PeopleTileKey(mNotificationEntry), - new HashSet<>(Collections.singleton(mNotificationEntry)))); + Map.of(new PeopleTileKey(mNotificationEntry), + new HashSet<>(Collections.singleton(mNotificationEntry))), + Optional.of(WIDGET_ID_WITH_SHORTCUT)); assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_CONTENT_1); + assertThat(sp.getString(String.valueOf(WIDGET_ID_WITH_SHORTCUT), null)).isEqualTo( + URI.toString()); } @Test @@ -1221,7 +1275,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { PeopleSpaceTile actual = mManager .augmentTileFromNotifications(tile, key, EMPTY_STRING, Map.of(new PeopleTileKey(mNotificationEntry), - new HashSet<>(Collections.singleton(mNotificationEntry)))); + new HashSet<>(Collections.singleton(mNotificationEntry))), + Optional.empty()); assertThat(actual.getNotificationContent()).isEqualTo(null); } @@ -1238,7 +1293,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { .thenReturn(List.of(mNotificationEntry)); PeopleSpaceTile actual = - mManager.augmentTileFromNotificationEntryManager(tile); + mManager.augmentTileFromNotificationEntryManager(tile, + Optional.of(WIDGET_ID_WITH_SHORTCUT)); assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_CONTENT_1); @@ -1246,6 +1302,202 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { .getVisibleNotifications(); } + @Test + public void testUpdateWidgetsOnStateChange() { + mManager.updateWidgetsOnStateChange(ACTION_BOOT_COMPLETED); + + verify(mAppWidgetManager, times(1)) + .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), + mBundleArgumentCaptor.capture()); + Bundle bundle = mBundleArgumentCaptor.getValue(); + PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_TILE); + assertThat(tile.isPackageSuspended()).isFalse(); + assertThat(tile.isUserQuieted()).isFalse(); + assertThat(tile.canBypassDnd()).isFalse(); + assertThat(tile.getNotificationPolicyState()).isEqualTo(SHOW_CONVERSATIONS); + verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT), + any()); + } + + @Test + public void testUpdateWidgetsOnStateChangeWithUserQuieted() { + when(mUserManager.isQuietModeEnabled(any())).thenReturn(true); + + mManager.updateWidgetsOnStateChange(ACTION_BOOT_COMPLETED); + + verify(mAppWidgetManager, times(1)) + .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), + mBundleArgumentCaptor.capture()); + Bundle bundle = mBundleArgumentCaptor.getValue(); + PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_TILE); + assertThat(tile.isPackageSuspended()).isFalse(); + assertThat(tile.isUserQuieted()).isTrue(); + assertThat(tile.getNotificationPolicyState()).isEqualTo(SHOW_CONVERSATIONS); + } + + @Test + public void testUpdateWidgetsOnStateChangeWithPackageSuspended() throws Exception { + when(mPackageManager.isPackageSuspended(any())).thenReturn(true); + + mManager.updateWidgetsOnStateChange(ACTION_PACKAGES_SUSPENDED); + + verify(mAppWidgetManager, times(1)) + .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), + mBundleArgumentCaptor.capture()); + Bundle bundle = mBundleArgumentCaptor.getValue(); + PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_TILE); + assertThat(tile.isPackageSuspended()).isTrue(); + assertThat(tile.isUserQuieted()).isFalse(); + assertThat(tile.getNotificationPolicyState()).isEqualTo(SHOW_CONVERSATIONS); + } + + @Test + public void testUpdateWidgetsOnStateChangeNotInDnd() { + int expected = 0; + mManager.updateWidgetsOnStateChange(NotificationManager + .ACTION_INTERRUPTION_FILTER_CHANGED); + + verify(mAppWidgetManager, times(1)) + .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), + mBundleArgumentCaptor.capture()); + PeopleSpaceTile tile = mBundleArgumentCaptor.getValue().getParcelable(OPTIONS_PEOPLE_TILE); + assertThat(tile.getNotificationPolicyState()).isEqualTo(expected | SHOW_CONVERSATIONS); + } + + @Test + public void testUpdateWidgetsOnStateChangeAllConversations() { + int expected = 0; + when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn( + INTERRUPTION_FILTER_PRIORITY); + when(mNotificationPolicy.allowConversations()).thenReturn(true); + setFinalField("priorityConversationSenders", CONVERSATION_SENDERS_ANYONE); + + mManager.updateWidgetsOnStateChange(NotificationManager + .ACTION_INTERRUPTION_FILTER_CHANGED); + + verify(mAppWidgetManager, times(1)) + .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), + mBundleArgumentCaptor.capture()); + PeopleSpaceTile tile = mBundleArgumentCaptor.getValue().getParcelable(OPTIONS_PEOPLE_TILE); + assertThat(tile.getNotificationPolicyState()).isEqualTo(expected | SHOW_CONVERSATIONS); + } + + @Test + public void testUpdateWidgetsOnStateChangeAllowOnlyImportantConversations() { + int expected = 0; + // Only allow important conversations. + when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn( + INTERRUPTION_FILTER_PRIORITY); + when(mNotificationPolicy.allowConversations()).thenReturn(true); + setFinalField("priorityConversationSenders", CONVERSATION_SENDERS_IMPORTANT); + + mManager.updateWidgetsOnStateChange(NotificationManager + .ACTION_INTERRUPTION_FILTER_CHANGED); + + verify(mAppWidgetManager, times(1)) + .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), + mBundleArgumentCaptor.capture()); + PeopleSpaceTile tile = mBundleArgumentCaptor.getValue().getParcelable(OPTIONS_PEOPLE_TILE); + assertThat(tile.getNotificationPolicyState()).isEqualTo( + expected | SHOW_IMPORTANT_CONVERSATIONS); + } + + @Test + public void testUpdateWidgetsOnStateChangeAllowNoConversations() { + int expected = 0; + when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn( + INTERRUPTION_FILTER_PRIORITY); + when(mNotificationPolicy.allowConversations()).thenReturn(false); + + mManager.updateWidgetsOnStateChange(NotificationManager + .ACTION_INTERRUPTION_FILTER_CHANGED); + + verify(mAppWidgetManager, times(1)) + .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), + mBundleArgumentCaptor.capture()); + PeopleSpaceTile tile = mBundleArgumentCaptor.getValue().getParcelable(OPTIONS_PEOPLE_TILE); + assertThat(tile.getNotificationPolicyState()).isEqualTo(expected | BLOCK_CONVERSATIONS); + } + + @Test + public void testUpdateWidgetsOnStateChangeAllowNoConversationsAllowContactMessages() { + int expected = 0; + when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn( + INTERRUPTION_FILTER_PRIORITY); + when(mNotificationPolicy.allowConversations()).thenReturn(false); + when(mNotificationPolicy.allowMessagesFrom()).thenReturn(ZenModeConfig.SOURCE_CONTACT); + when(mNotificationPolicy.allowMessages()).thenReturn(true); + + mManager.updateWidgetsOnStateChange(ACTION_BOOT_COMPLETED); + + verify(mAppWidgetManager, times(1)) + .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), + mBundleArgumentCaptor.capture()); + PeopleSpaceTile tile = mBundleArgumentCaptor.getValue().getParcelable(OPTIONS_PEOPLE_TILE); + assertThat(tile.getNotificationPolicyState()).isEqualTo(expected | SHOW_CONTACTS); + } + + @Test + public void testUpdateWidgetsOnStateChangeAllowNoConversationsAllowStarredContactMessages() { + int expected = 0; + when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn( + INTERRUPTION_FILTER_PRIORITY); + when(mNotificationPolicy.allowConversations()).thenReturn(false); + when(mNotificationPolicy.allowMessagesFrom()).thenReturn(ZenModeConfig.SOURCE_STAR); + when(mNotificationPolicy.allowMessages()).thenReturn(true); + + mManager.updateWidgetsOnStateChange(ACTION_BOOT_COMPLETED); + + verify(mAppWidgetManager, times(1)) + .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), + mBundleArgumentCaptor.capture()); + PeopleSpaceTile tile = mBundleArgumentCaptor.getValue().getParcelable(OPTIONS_PEOPLE_TILE); + assertThat(tile.getNotificationPolicyState()).isEqualTo(expected | SHOW_STARRED_CONTACTS); + } + + @Test + public void testUpdateWidgetsOnStateChangeAllowAlarmsOnly() { + int expected = 0; + when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn( + INTERRUPTION_FILTER_ALARMS); + + mManager.updateWidgetsOnStateChange(NotificationManager + .ACTION_INTERRUPTION_FILTER_CHANGED); + + verify(mAppWidgetManager, times(1)) + .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), + mBundleArgumentCaptor.capture()); + PeopleSpaceTile tile = mBundleArgumentCaptor.getValue().getParcelable(OPTIONS_PEOPLE_TILE); + assertThat(tile.getNotificationPolicyState()).isEqualTo(expected | BLOCK_CONVERSATIONS); + } + + @Test + public void testUpdateWidgetsOnStateChangeAllowVisualEffectsAndAllowAlarmsOnly() { + int expected = 0; + // If we show visuals, but just only make sounds for alarms, still show content in tiles. + when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn( + INTERRUPTION_FILTER_ALARMS); + setFinalField("suppressedVisualEffects", SUPPRESSED_EFFECT_FULL_SCREEN_INTENT + | SUPPRESSED_EFFECT_AMBIENT); + + mManager.updateWidgetsOnStateChange(ACTION_BOOT_COMPLETED); + + verify(mAppWidgetManager, times(1)) + .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), + mBundleArgumentCaptor.capture()); + PeopleSpaceTile tile = mBundleArgumentCaptor.getValue().getParcelable(OPTIONS_PEOPLE_TILE); + assertThat(tile.getNotificationPolicyState()).isEqualTo(expected | SHOW_CONVERSATIONS); + } + + private void setFinalField(String fieldName, int value) { + try { + Field field = NotificationManager.Policy.class.getDeclaredField(fieldName); + field.setAccessible(true); + field.set(mNotificationPolicy, value); + } catch (Exception e) { + } + } + /** * Adds another widget for {@code PERSON_TILE} with widget ID: {@code * SECOND_WIDGET_ID_WITH_SHORTCUT}. |