1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.people.widget;
18 
19 import static android.app.Notification.CATEGORY_MISSED_CALL;
20 import static android.app.Notification.EXTRA_PEOPLE_LIST;
21 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
22 import static android.app.NotificationManager.IMPORTANCE_HIGH;
23 import static android.app.NotificationManager.INTERRUPTION_FILTER_ALARMS;
24 import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
25 import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
26 import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT;
27 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
28 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE;
29 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
30 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
31 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
32 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
33 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
34 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON;
35 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
36 import static android.app.people.ConversationStatus.ACTIVITY_ANNIVERSARY;
37 import static android.app.people.ConversationStatus.ACTIVITY_BIRTHDAY;
38 import static android.app.people.ConversationStatus.ACTIVITY_GAME;
39 import static android.app.people.PeopleSpaceTile.BLOCK_CONVERSATIONS;
40 import static android.app.people.PeopleSpaceTile.SHOW_CONTACTS;
41 import static android.app.people.PeopleSpaceTile.SHOW_CONVERSATIONS;
42 import static android.app.people.PeopleSpaceTile.SHOW_IMPORTANT_CONVERSATIONS;
43 import static android.app.people.PeopleSpaceTile.SHOW_STARRED_CONTACTS;
44 import static android.content.Intent.ACTION_BOOT_COMPLETED;
45 import static android.content.Intent.ACTION_PACKAGES_SUSPENDED;
46 import static android.content.Intent.ACTION_PACKAGE_REMOVED;
47 import static android.content.PermissionChecker.PERMISSION_GRANTED;
48 import static android.content.PermissionChecker.PERMISSION_HARD_DENIED;
49 import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_ANYONE;
50 
51 import static com.android.systemui.people.PeopleSpaceUtils.EMPTY_STRING;
52 import static com.android.systemui.people.PeopleSpaceUtils.INVALID_USER_ID;
53 import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME;
54 import static com.android.systemui.people.PeopleSpaceUtils.USER_ID;
55 
56 import static com.google.common.truth.Truth.assertThat;
57 
58 import static org.mockito.ArgumentMatchers.any;
59 import static org.mockito.ArgumentMatchers.anyBoolean;
60 import static org.mockito.ArgumentMatchers.anyInt;
61 import static org.mockito.ArgumentMatchers.anyString;
62 import static org.mockito.ArgumentMatchers.eq;
63 import static org.mockito.Mockito.mock;
64 import static org.mockito.Mockito.never;
65 import static org.mockito.Mockito.times;
66 import static org.mockito.Mockito.verify;
67 import static org.mockito.Mockito.when;
68 
69 import static java.util.Objects.requireNonNull;
70 
71 import android.app.INotificationManager;
72 import android.app.Notification;
73 import android.app.NotificationChannel;
74 import android.app.NotificationManager;
75 import android.app.Person;
76 import android.app.backup.BackupManager;
77 import android.app.people.ConversationChannel;
78 import android.app.people.ConversationStatus;
79 import android.app.people.IPeopleManager;
80 import android.app.people.PeopleManager;
81 import android.app.people.PeopleSpaceTile;
82 import android.appwidget.AppWidgetManager;
83 import android.appwidget.AppWidgetProviderInfo;
84 import android.content.ComponentName;
85 import android.content.Context;
86 import android.content.Intent;
87 import android.content.SharedPreferences;
88 import android.content.pm.LauncherApps;
89 import android.content.pm.PackageManager;
90 import android.content.pm.ParceledListSlice;
91 import android.content.pm.ShortcutInfo;
92 import android.graphics.drawable.Icon;
93 import android.net.Uri;
94 import android.os.Bundle;
95 import android.os.UserHandle;
96 import android.os.UserManager;
97 import android.platform.test.annotations.DisableFlags;
98 import android.platform.test.annotations.EnableFlags;
99 import android.service.notification.ConversationChannelWrapper;
100 import android.service.notification.StatusBarNotification;
101 import android.service.notification.ZenModeConfig;
102 import android.text.TextUtils;
103 
104 import androidx.preference.PreferenceManager;
105 import androidx.test.ext.junit.runners.AndroidJUnit4;
106 import androidx.test.filters.SmallTest;
107 
108 import com.android.systemui.SysuiTestCase;
109 import com.android.systemui.people.PeopleBackupFollowUpJob;
110 import com.android.systemui.people.PeopleSpaceUtils;
111 import com.android.systemui.people.SharedPreferencesHelper;
112 import com.android.systemui.res.R;
113 import com.android.systemui.settings.FakeUserTracker;
114 import com.android.systemui.statusbar.NotificationListener;
115 import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
116 import com.android.systemui.statusbar.SbnBuilder;
117 import com.android.systemui.statusbar.notification.collection.NoManSimulator;
118 import com.android.systemui.statusbar.notification.collection.NoManSimulator.NotifEvent;
119 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
120 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
121 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
122 import com.android.systemui.util.concurrency.FakeExecutor;
123 import com.android.systemui.util.time.FakeSystemClock;
124 import com.android.wm.shell.bubbles.Bubbles;
125 
126 import org.junit.Before;
127 import org.junit.Test;
128 import org.junit.runner.RunWith;
129 import org.mockito.ArgumentCaptor;
130 import org.mockito.Captor;
131 import org.mockito.Mock;
132 import org.mockito.MockitoAnnotations;
133 
134 import java.lang.reflect.Field;
135 import java.util.ArrayList;
136 import java.util.Arrays;
137 import java.util.Collections;
138 import java.util.HashSet;
139 import java.util.List;
140 import java.util.Map;
141 import java.util.Optional;
142 import java.util.Set;
143 import java.util.stream.Collectors;
144 
145 @SmallTest
146 @RunWith(AndroidJUnit4.class)
147 public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
148     private static final long MIN_LINGER_DURATION = 5;
149 
150     private static final String TEST_PACKAGE_A = "com.android.systemui.tests";
151     private static final String TEST_PACKAGE_B = "com.test.package_b";
152     private static final String TEST_PACKAGE_C = "com.test.package_c";
153     private static final String TEST_CHANNEL_ID = "channel_id";
154     private static final String TEST_CHANNEL_NAME = "channel_name";
155     private static final String TEST_PARENT_CHANNEL_ID = "parent_channel_id";
156     private static final String TEST_CONVERSATION_ID = "conversation_id";
157     private static final int WIDGET_ID_WITH_SHORTCUT = 1;
158     private static final int SECOND_WIDGET_ID_WITH_SHORTCUT = 3;
159     private static final int WIDGET_ID_WITHOUT_SHORTCUT = 2;
160     private static final int WIDGET_ID_WITH_KEY_IN_OPTIONS = 4;
161     private static final int WIDGET_ID_WITH_SAME_URI = 5;
162     private static final int WIDGET_ID_WITH_DIFFERENT_URI = 6;
163     private static final int WIDGET_ID_8 = 8;
164     private static final int WIDGET_ID_9 = 9;
165     private static final int WIDGET_ID_11 = 11;
166     private static final int WIDGET_ID_14 = 14;
167     private static final int WIDGET_ID_15 = 15;
168     private static final String SHORTCUT_ID = "101";
169     private static final String OTHER_SHORTCUT_ID = "102";
170     private static final String THIRD_SHORTCUT_ID = "103";
171     private static final String NOTIFICATION_KEY = "0|com.android.systemui.tests|0|null|0";
172     private static final String NOTIFICATION_CONTENT_1 = "message text 1";
173     private static final Uri URI = Uri.parse("fake_uri");
174     private static final Icon ICON = Icon.createWithResource("package", R.drawable.ic_android);
175     private static final PeopleTileKey KEY = new PeopleTileKey(SHORTCUT_ID, 0, TEST_PACKAGE_A);
176     private static final Person PERSON = new Person.Builder()
177             .setName("name")
178             .setKey("abc")
179             .setUri(URI.toString())
180             .setBot(false)
181             .build();
182     private static final PeopleSpaceTile PERSON_TILE =
183             new PeopleSpaceTile
184                     .Builder(SHORTCUT_ID, "username", ICON, new Intent())
185                     .setPackageName(TEST_PACKAGE_A)
186                     .setUserHandle(new UserHandle(0))
187                     .setNotificationKey(NOTIFICATION_KEY + "1")
188                     .setNotificationContent(NOTIFICATION_CONTENT_1)
189                     .setNotificationDataUri(URI)
190                     .setContactUri(URI)
191                     .build();
192     private static final PeopleSpaceTile PERSON_TILE_WITH_SAME_URI =
193             new PeopleSpaceTile
194                     // Different shortcut ID
195                     .Builder(OTHER_SHORTCUT_ID, "username", ICON, new Intent())
196                     // Different package name
197                     .setPackageName(TEST_PACKAGE_B)
198                     .setUserHandle(new UserHandle(0))
199                     // Same contact uri.
200                     .setContactUri(URI)
201                     .build();
202     private static final int ALL_SUPPRESSED_VISUAL_EFFECTS = SUPPRESSED_EFFECT_SCREEN_OFF
203             | SUPPRESSED_EFFECT_SCREEN_ON
204             | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
205             | SUPPRESSED_EFFECT_AMBIENT
206             | SUPPRESSED_EFFECT_STATUS_BAR
207             | SUPPRESSED_EFFECT_BADGE
208             | SUPPRESSED_EFFECT_LIGHTS
209             | SUPPRESSED_EFFECT_PEEK
210             | SUPPRESSED_EFFECT_NOTIFICATION_LIST;
211     private static final long SBN_POST_TIME = 567L;
212 
213     private static final Map<String, String> WIDGETS_MAPPING = Map.of(
214             String.valueOf(WIDGET_ID_8), String.valueOf(WIDGET_ID_WITH_SHORTCUT),
215             String.valueOf(WIDGET_ID_9), String.valueOf(WIDGET_ID_WITHOUT_SHORTCUT),
216             String.valueOf(WIDGET_ID_11), String.valueOf(WIDGET_ID_WITH_KEY_IN_OPTIONS),
217             String.valueOf(WIDGET_ID_14), String.valueOf(WIDGET_ID_WITH_SAME_URI),
218             String.valueOf(WIDGET_ID_15), String.valueOf(WIDGET_ID_WITH_DIFFERENT_URI)
219     );
220 
221     private static final Map<String, String> WIDGETS_MAPPING_CROSS_MAPPING = Map.of(
222             String.valueOf(WIDGET_ID_WITH_SHORTCUT), String.valueOf(WIDGET_ID_WITH_KEY_IN_OPTIONS),
223             String.valueOf(WIDGET_ID_WITHOUT_SHORTCUT), String.valueOf(WIDGET_ID_WITHOUT_SHORTCUT),
224             String.valueOf(WIDGET_ID_WITH_KEY_IN_OPTIONS), String.valueOf(WIDGET_ID_WITH_SHORTCUT)
225     );
226 
227     private ShortcutInfo mShortcutInfo;
228     private NotificationEntry mNotificationEntry;
229 
230     private PeopleSpaceWidgetManager mManager;
231 
232     @Mock
233     private Context mMockContext;
234 
235     @Mock
236     private NotificationListener mListenerService;
237 
238     @Mock
239     private AppWidgetManager mAppWidgetManager;
240     @Mock
241     private IPeopleManager mIPeopleManager;
242     @Mock
243     private PeopleManager mPeopleManager;
244     @Mock
245     private LauncherApps mLauncherApps;
246     @Mock
247     private CommonNotifCollection mNotifCollection;
248     @Mock
249     private PackageManager mPackageManager;
250     @Mock
251     private INotificationManager mINotificationManager;
252     @Mock
253     private UserManager mUserManager;
254     @Mock
255     private NotificationManager mNotificationManager;
256     @Mock
257     private NotificationManager.Policy mNotificationPolicy;
258     @Mock
259     private Bubbles mBubbles;
260     @Mock
261     private BackupManager mBackupManager;
262 
263     @Captor
264     private ArgumentCaptor<NotificationHandler> mListenerCaptor;
265     @Captor
266     private ArgumentCaptor<Bundle> mBundleArgumentCaptor;
267 
268     private final NoManSimulator mNoMan = new NoManSimulator();
269     private final FakeSystemClock mClock = new FakeSystemClock();
270 
271     private final FakeExecutor mFakeExecutor = new FakeExecutor(mClock);
272 
273     private final FakeUserTracker mUserTracker = new FakeUserTracker();
274 
275     @Before
setUp()276     public void setUp() throws Exception {
277         MockitoAnnotations.initMocks(this);
278         mLauncherApps = mock(LauncherApps.class);
279         mManager = new PeopleSpaceWidgetManager(mContext, mAppWidgetManager, mIPeopleManager,
280                 mPeopleManager, mLauncherApps, mNotifCollection, mPackageManager,
281                 Optional.of(mBubbles), mUserManager, mBackupManager, mINotificationManager,
282                 mNotificationManager, mFakeExecutor, mUserTracker);
283         mManager.attach(mListenerService);
284 
285         verify(mListenerService).addNotificationHandler(mListenerCaptor.capture());
286         NotificationHandler serviceListener = requireNonNull(mListenerCaptor.getValue());
287         mNoMan.addListener(serviceListener);
288 
289         clearStorage();
290         addTileForWidget(PERSON_TILE, WIDGET_ID_WITH_SHORTCUT);
291         addTileForWidget(PERSON_TILE_WITH_SAME_URI, WIDGET_ID_WITH_SAME_URI);
292         when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITHOUT_SHORTCUT)))
293                 .thenReturn(new Bundle());
294 
295         when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
296         when(mPackageManager.isPackageSuspended(any())).thenReturn(false);
297         setFinalField("suppressedVisualEffects", ALL_SUPPRESSED_VISUAL_EFFECTS);
298         when(mNotificationPolicy.allowConversationsFrom()).thenReturn(CONVERSATION_SENDERS_ANYONE);
299         when(mNotificationPolicy.allowConversations()).thenReturn(false);
300         when(mNotificationPolicy.allowMessagesFrom()).thenReturn(ZenModeConfig.SOURCE_ANYONE);
301         when(mNotificationPolicy.allowMessages()).thenReturn(false);
302         when(mNotificationManager.getNotificationPolicy()).thenReturn(mNotificationPolicy);
303         when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn(
304                 INTERRUPTION_FILTER_ALL);
305         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT};
306         when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
307         when(mBubbles.isBubbleNotificationSuppressedFromShade(any(), any())).thenReturn(false);
308 
309         when(mMockContext.getPackageName()).thenReturn(TEST_PACKAGE_A);
310         when(mMockContext.getUserId()).thenReturn(0);
311         mShortcutInfo = new ShortcutInfo.Builder(mMockContext,
312                 SHORTCUT_ID).setLongLabel("name").build();
313         mNotificationEntry = new NotificationEntryBuilder()
314                 .setSbn(createNotification(
315                         SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false))
316                 .setId(1)
317                 .setShortcutInfo(mShortcutInfo)
318                 .build();
319 
320         AppWidgetProviderInfo providerInfo = new AppWidgetProviderInfo();
321         providerInfo.provider = new ComponentName("com.android.systemui.tests",
322                 "com.android.systemui.people.widget.PeopleSpaceWidgetProvider");
323         when(mAppWidgetManager.getInstalledProvidersForPackage(anyString(), any()))
324                 .thenReturn(List.of(providerInfo));
325     }
326 
327     @Test
testGetRecentTilesReturnsSortedListWithOnlyRecentConversations()328     public void testGetRecentTilesReturnsSortedListWithOnlyRecentConversations() throws Exception {
329         // Ensure the less-recent Important conversation is before more recent conversations.
330         ConversationChannelWrapper newerNonImportantConversation = getConversationChannelWrapper(
331                 SHORTCUT_ID, false, 3);
332         ConversationChannelWrapper newerImportantConversation = getConversationChannelWrapper(
333                 SHORTCUT_ID + 1, true, 3);
334         ConversationChannelWrapper olderImportantConversation = getConversationChannelWrapper(
335                 SHORTCUT_ID + 2,
336                 true, 1);
337         when(mINotificationManager.getConversations(anyBoolean())).thenReturn(
338                 new ParceledListSlice(Arrays.asList(
339                         newerNonImportantConversation, newerImportantConversation,
340                         olderImportantConversation)));
341 
342         // Ensure the non-Important conversation is sorted between these recent conversations.
343         ConversationChannel recentConversationBeforeNonImportantConversation =
344                 getConversationChannel(
345                         SHORTCUT_ID + 3, 4);
346         ConversationChannel recentConversationAfterNonImportantConversation =
347                 getConversationChannel(SHORTCUT_ID + 4,
348                         2);
349         when(mIPeopleManager.getRecentConversations()).thenReturn(
350                 new ParceledListSlice(Arrays.asList(recentConversationAfterNonImportantConversation,
351                         recentConversationBeforeNonImportantConversation)));
352 
353         List<String> orderedShortcutIds = mManager.getRecentTiles()
354                 .stream().map(tile -> tile.getId()).collect(Collectors.toList());
355 
356         // Check for sorted recent conversations.
357         assertThat(orderedShortcutIds).containsExactly(
358                 recentConversationBeforeNonImportantConversation.getShortcutInfo().getId(),
359                 newerNonImportantConversation.getShortcutInfo().getId(),
360                 recentConversationAfterNonImportantConversation.getShortcutInfo().getId())
361                 .inOrder();
362     }
363 
364     @Test
testGetPriorityTilesReturnsSortedListWithOnlyImportantConversations()365     public void testGetPriorityTilesReturnsSortedListWithOnlyImportantConversations()
366             throws Exception {
367         // Ensure the less-recent Important conversation is before more recent conversations.
368         ConversationChannelWrapper newerNonImportantConversation = getConversationChannelWrapper(
369                 SHORTCUT_ID, false, 3);
370         ConversationChannelWrapper newerImportantConversation = getConversationChannelWrapper(
371                 SHORTCUT_ID + 1, true, 3);
372         ConversationChannelWrapper olderImportantConversation = getConversationChannelWrapper(
373                 SHORTCUT_ID + 2,
374                 true, 1);
375         when(mINotificationManager.getConversations(anyBoolean())).thenReturn(
376                 new ParceledListSlice(Arrays.asList(
377                         newerNonImportantConversation, newerImportantConversation,
378                         olderImportantConversation)));
379 
380         List<String> orderedShortcutIds = mManager.getPriorityTiles()
381                 .stream().map(tile -> tile.getId()).collect(Collectors.toList());
382 
383         // Check for sorted priority conversations.
384         assertThat(orderedShortcutIds).containsExactly(
385                 newerImportantConversation.getShortcutInfo().getId(),
386                 olderImportantConversation.getShortcutInfo().getId())
387                 .inOrder();
388     }
389 
390     @Test
testGetTilesReturnsNothingInQuietMode()391     public void testGetTilesReturnsNothingInQuietMode()
392             throws Exception {
393         // Ensure the less-recent Important conversation is before more recent conversations.
394         ConversationChannelWrapper newerNonImportantConversation = getConversationChannelWrapper(
395                 SHORTCUT_ID, false, 3);
396         ConversationChannelWrapper newerImportantConversation = getConversationChannelWrapper(
397                 SHORTCUT_ID + 1, true, 3);
398         ConversationChannelWrapper olderImportantConversation = getConversationChannelWrapper(
399                 SHORTCUT_ID + 2,
400                 true, 1);
401         when(mINotificationManager.getConversations(anyBoolean())).thenReturn(
402                 new ParceledListSlice(Arrays.asList(
403                         newerNonImportantConversation, newerImportantConversation,
404                         olderImportantConversation)));
405         ConversationChannel recentConversation =
406                 getConversationChannel(
407                         SHORTCUT_ID + 3, 4);
408         when(mIPeopleManager.getRecentConversations()).thenReturn(
409                 new ParceledListSlice(Arrays.asList(recentConversation)));
410 
411         when(mUserManager.isQuietModeEnabled(any())).thenReturn(true);
412 
413         // Check nothing returned.
414         assertThat(mManager.getPriorityTiles()).isEmpty();
415         assertThat(mManager.getRecentTiles()).isEmpty();
416     }
417 
418     @Test
testDoNotUpdateAppWidgetIfNoWidgets()419     public void testDoNotUpdateAppWidgetIfNoWidgets() throws Exception {
420         int[] widgetIdsArray = {};
421         when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
422 
423         StatusBarNotification sbn = createNotification(
424                 OTHER_SHORTCUT_ID, /* isMessagingStyle = */ false, /* isMissedCall = */ false);
425         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
426                 .setSbn(sbn)
427                 .setId(1));
428         mClock.advanceTime(MIN_LINGER_DURATION);
429 
430         verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
431                 any());
432     }
433 
434     @Test
testDoNotUpdateAppWidgetIfNoShortcutInfo()435     public void testDoNotUpdateAppWidgetIfNoShortcutInfo() throws Exception {
436         int[] widgetIdsArray = {};
437         when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
438 
439         Notification notificationWithoutShortcut = new Notification.Builder(mContext)
440                 .setContentTitle("TEST_TITLE")
441                 .setContentText("TEST_TEXT")
442                 .setStyle(new Notification.MessagingStyle(PERSON)
443                         .addMessage(new Notification.MessagingStyle.Message("text3", 10, PERSON))
444                 )
445                 .build();
446         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
447                 .setSbn(new SbnBuilder()
448                         .setNotification(notificationWithoutShortcut)
449                         .setPkg(TEST_PACKAGE_A)
450                         .build())
451                 .setId(1));
452         mClock.advanceTime(MIN_LINGER_DURATION);
453 
454         verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
455                 any());
456     }
457 
458     @Test
testDoNotUpdateAppWidgetIfNoPackage()459     public void testDoNotUpdateAppWidgetIfNoPackage() throws Exception {
460         int[] widgetIdsArray = {};
461         when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
462 
463         StatusBarNotification sbnWithoutPackageName = new SbnBuilder()
464                 .setNotification(createMessagingStyleNotification(
465                         SHORTCUT_ID, /* isMessagingStyle = */ false, /* isMissedCall = */ false))
466                 .build();
467         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
468                 .setSbn(sbnWithoutPackageName)
469                 .setId(1));
470         mClock.advanceTime(MIN_LINGER_DURATION);
471 
472         verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
473                 any());
474     }
475 
476     @Test
testDoNotUpdateAppWidgetIfNonConversationChannelModified()477     public void testDoNotUpdateAppWidgetIfNonConversationChannelModified() throws Exception {
478         int[] widgetIdsArray = {1};
479         when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
480 
481         NotificationChannel channel =
482                 new NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT);
483 
484         mNoMan.issueChannelModification(TEST_PACKAGE_A,
485                 UserHandle.getUserHandleForUid(0), channel, IMPORTANCE_HIGH);
486         mClock.advanceTime(MIN_LINGER_DURATION);
487 
488         verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
489                 any());
490     }
491 
492     @Test
testUpdateAppWidgetIfConversationChannelModified()493     public void testUpdateAppWidgetIfConversationChannelModified() throws Exception {
494         int[] widgetIdsArray = {1};
495         when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
496 
497         when(mUserManager.isUserUnlocked(any())).thenReturn(true);
498 
499         NotificationChannel channel =
500                 new NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT);
501         channel.setConversationId(TEST_PARENT_CHANNEL_ID, TEST_CONVERSATION_ID);
502 
503         mNoMan.issueChannelModification(TEST_PACKAGE_A,
504                 UserHandle.getUserHandleForUid(0), channel, IMPORTANCE_HIGH);
505         mClock.advanceTime(MIN_LINGER_DURATION);
506 
507         verify(mAppWidgetManager, times(1)).updateAppWidget(anyInt(), any());
508     }
509 
510     @Test
testOnNotificationChannelModified_userLocked()511     public void testOnNotificationChannelModified_userLocked() {
512         int[] widgetIdsArray = {1};
513         when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
514 
515         when(mUserManager.isUserUnlocked(any())).thenReturn(false);
516 
517         NotificationChannel channel =
518                 new NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT);
519         channel.setConversationId(TEST_PARENT_CHANNEL_ID, TEST_CONVERSATION_ID);
520 
521         mNoMan.issueChannelModification(TEST_PACKAGE_A,
522                 UserHandle.getUserHandleForUid(0), channel, IMPORTANCE_HIGH);
523         mClock.advanceTime(MIN_LINGER_DURATION);
524 
525         verify(mAppWidgetManager, never()).updateAppWidget(anyInt(), any());
526     }
527 
528     @Test
testDoNotUpdateNotificationPostedIfDifferentShortcutId()529     public void testDoNotUpdateNotificationPostedIfDifferentShortcutId() throws Exception {
530         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
531         when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
532 
533         StatusBarNotification sbn = createNotification(
534                 OTHER_SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false);
535         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
536                 .setSbn(sbn)
537                 .setId(1));
538         mClock.advanceTime(MIN_LINGER_DURATION);
539 
540         verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
541                 any());
542     }
543 
544     @Test
testDoNotUpdateNotificationPostedIfDifferentPackageName()545     public void testDoNotUpdateNotificationPostedIfDifferentPackageName() throws Exception {
546         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
547         when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
548 
549         StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder()
550                 .setNotification(createMessagingStyleNotification(
551                         SHORTCUT_ID, /* isMessagingStyle = */ false, /* isMissedCall = */ false))
552                 .setPkg(TEST_PACKAGE_B)
553                 .build();
554         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
555                 .setSbn(sbnWithDifferentPackageName)
556                 .setId(1));
557         mClock.advanceTime(MIN_LINGER_DURATION);
558 
559         verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
560                 any());
561     }
562 
563     @Test
testDoNotUpdateNotificationRemovedIfDifferentShortcutId()564     public void testDoNotUpdateNotificationRemovedIfDifferentShortcutId() throws Exception {
565         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
566         when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
567 
568         StatusBarNotification sbn = createNotification(
569                 OTHER_SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false);
570         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
571                 .setSbn(sbn)
572                 .setId(1));
573         mClock.advanceTime(4);
574         NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0);
575         mClock.advanceTime(MIN_LINGER_DURATION);
576 
577         verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
578                 any());
579     }
580 
581     @Test
testDoNotUpdateNotificationRemovedIfDifferentPackageName()582     public void testDoNotUpdateNotificationRemovedIfDifferentPackageName() throws Exception {
583         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
584         when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
585 
586         StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder()
587                 .setNotification(createMessagingStyleNotification(
588                         SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false))
589                 .setPkg(TEST_PACKAGE_B)
590                 .build();
591         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
592                 .setSbn(sbnWithDifferentPackageName)
593                 .setId(1));
594         mClock.advanceTime(4);
595         NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0);
596         mClock.advanceTime(MIN_LINGER_DURATION);
597 
598         verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
599                 any());
600     }
601 
602     @Test
testDoNotUpdateStatusPostedIfDifferentShortcutId()603     public void testDoNotUpdateStatusPostedIfDifferentShortcutId() throws Exception {
604         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
605         when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
606 
607         ConversationStatus status1 = new ConversationStatus.Builder(OTHER_SHORTCUT_ID,
608                 ACTIVITY_GAME).setDescription("Playing a game!").build();
609         ConversationStatus status2 = new ConversationStatus.Builder(OTHER_SHORTCUT_ID,
610                 ACTIVITY_BIRTHDAY).build();
611         ConversationChannel conversationChannel = getConversationWithShortcutId(
612                 new PeopleTileKey(OTHER_SHORTCUT_ID, 0, TEST_PACKAGE_A),
613                 Arrays.asList(status1, status2));
614         mManager.updateWidgetsWithConversationChanged(conversationChannel);
615         mClock.advanceTime(MIN_LINGER_DURATION);
616 
617         verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
618                 any());
619     }
620 
621     @Test
testUpdateStatusPostedIfExistingTile()622     public void testUpdateStatusPostedIfExistingTile() throws Exception {
623         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
624         when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
625 
626         ConversationStatus status = new ConversationStatus.Builder(SHORTCUT_ID,
627                 ACTIVITY_GAME).setDescription("Playing a game!").build();
628         ConversationChannel conversationChannel = getConversationWithShortcutId(
629                 new PeopleTileKey(SHORTCUT_ID, 0, TEST_PACKAGE_A),
630                 Arrays.asList(status));
631         mManager.updateWidgetsWithConversationChanged(conversationChannel);
632         mClock.advanceTime(MIN_LINGER_DURATION);
633 
634         PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
635         assertThat(tile.getStatuses()).containsExactly(status);
636         verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
637                 any());
638     }
639 
640     @Test
testUpdateStatusPostedOnTwoExistingTiles()641     public void testUpdateStatusPostedOnTwoExistingTiles() throws Exception {
642         addSecondWidgetForPersonTile();
643 
644         ConversationStatus status = new ConversationStatus.Builder(SHORTCUT_ID,
645                 ACTIVITY_ANNIVERSARY).build();
646         ConversationChannel conversationChannel = getConversationWithShortcutId(
647                 new PeopleTileKey(SHORTCUT_ID, 0, TEST_PACKAGE_A), Arrays.asList(status));
648         mManager.updateWidgetsWithConversationChanged(conversationChannel);
649         mClock.advanceTime(MIN_LINGER_DURATION);
650 
651         verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
652                 any());
653         verify(mAppWidgetManager, times(1)).updateAppWidget(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
654                 any());
655     }
656 
657     @Test
testUpdateNotificationPostedIfExistingTile()658     public void testUpdateNotificationPostedIfExistingTile() throws Exception {
659         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
660         when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
661         NotificationEntryBuilder builder = new NotificationEntryBuilder()
662                 .setSbn(createNotification(
663                         SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false))
664                 .setShortcutInfo(mShortcutInfo)
665                 .setId(1);
666         NotificationEntry entry = builder.build();
667         when(mNotifCollection.getAllNotifs()).thenReturn(List.of(entry));
668 
669         NotifEvent notif1 = mNoMan.postNotif(builder);
670         mClock.advanceTime(MIN_LINGER_DURATION);
671 
672         PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
673         assertThat(tile.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
674         assertThat(tile.getLastInteractionTimestamp()).isEqualTo(SBN_POST_TIME);
675         assertThat(tile.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT_1);
676         verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
677                 any());
678     }
679 
680     @Test
testUpdateNotificationPostedOnTwoExistingTiles()681     public void testUpdateNotificationPostedOnTwoExistingTiles() throws Exception {
682         addSecondWidgetForPersonTile();
683 
684         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
685                 .setSbn(createNotification(
686                         SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false))
687                 .setId(1));
688         mClock.advanceTime(MIN_LINGER_DURATION);
689 
690         verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
691                 any());
692         verify(mAppWidgetManager, times(1)).updateAppWidget(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
693                 any());
694     }
695 
696     @Test
testUpdateNotificationOnExistingTileAfterRemovingTileForSamePerson()697     public void testUpdateNotificationOnExistingTileAfterRemovingTileForSamePerson()
698             throws Exception {
699         addSecondWidgetForPersonTile();
700 
701         PeopleSpaceUtils.removeSharedPreferencesStorageForTile(
702                 mContext, KEY, SECOND_WIDGET_ID_WITH_SHORTCUT, EMPTY_STRING);
703         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
704                 .setSbn(createNotification(
705                         SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false))
706                 .setId(1));
707         mClock.advanceTime(MIN_LINGER_DURATION);
708 
709         verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
710                 any());
711         verify(mAppWidgetManager, never()).updateAppWidget(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
712                 any());
713     }
714 
715     @Test
testUpdateMissedCallNotificationWithoutContentPostedIfExistingTile()716     public void testUpdateMissedCallNotificationWithoutContentPostedIfExistingTile()
717             throws Exception {
718         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
719         when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
720         NotificationEntryBuilder builder = new NotificationEntryBuilder()
721                 .setSbn(createNotification(
722                         SHORTCUT_ID, /* isMessagingStyle = */ false, /* isMissedCall = */ true))
723                 .setShortcutInfo(mShortcutInfo)
724                 .setId(1);
725         NotificationEntry entry = builder.build();
726         when(mNotifCollection.getAllNotifs()).thenReturn(List.of(entry));
727 
728         NotifEvent notif1 = mNoMan.postNotif(builder);
729         mClock.advanceTime(MIN_LINGER_DURATION);
730 
731         PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
732         assertThat(tile.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
733         assertThat(tile.getNotificationContent())
734                 .isEqualTo(mContext.getString(R.string.missed_call));
735         verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
736                 any());
737     }
738 
739     @Test
testUpdateMissedCallNotificationWithContentPostedIfExistingTile()740     public void testUpdateMissedCallNotificationWithContentPostedIfExistingTile()
741             throws Exception {
742         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
743         when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
744         NotificationEntryBuilder builder = new NotificationEntryBuilder()
745                 .setSbn(createNotification(
746                         SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ true))
747                 .setShortcutInfo(mShortcutInfo)
748                 .setId(1);
749         NotificationEntry entry = builder.build();
750         when(mNotifCollection.getAllNotifs()).thenReturn(List.of(entry));
751 
752         NotifEvent notif1 = mNoMan.postNotif(builder);
753         mClock.advanceTime(MIN_LINGER_DURATION);
754 
755         PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
756         assertThat(tile.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
757         assertThat(tile.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT_1);
758         verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
759                 any());
760     }
761 
762     @Test
testUpdateMissedCallNotificationWithContentPostedIfMatchingUriTile()763     public void testUpdateMissedCallNotificationWithContentPostedIfMatchingUriTile()
764             throws Exception {
765         int[] widgetIdsArray =
766                 {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT, WIDGET_ID_WITH_SAME_URI};
767         when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
768         NotificationEntryBuilder builder = new NotificationEntryBuilder()
769                 .setSbn(createNotification(
770                         SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ true))
771                 .setShortcutInfo(mShortcutInfo)
772                 .setId(1);
773         NotificationEntry entry = builder.build();
774         when(mNotifCollection.getAllNotifs()).thenReturn(List.of(entry));
775 
776         NotifEvent notif1 = mNoMan.postNotif(builder);
777         mClock.advanceTime(MIN_LINGER_DURATION);
778 
779         PeopleSpaceTile tileWithMissedCallOrigin = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
780         assertThat(tileWithMissedCallOrigin.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
781         assertThat(tileWithMissedCallOrigin.getNotificationContent()).isEqualTo(
782                 NOTIFICATION_CONTENT_1);
783         verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
784                 any());
785         PeopleSpaceTile tileWithSameUri = mManager.mTiles.get(WIDGET_ID_WITH_SAME_URI);
786         assertThat(tileWithSameUri.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
787         assertThat(tileWithSameUri.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT_1);
788         verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SAME_URI),
789                 any());
790     }
791 
792     @Test
testRemoveMissedCallNotificationWithContentPostedIfMatchingUriTile()793     public void testRemoveMissedCallNotificationWithContentPostedIfMatchingUriTile()
794             throws Exception {
795         int[] widgetIdsArray =
796                 {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT, WIDGET_ID_WITH_SAME_URI};
797         when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
798         NotificationEntryBuilder builder = new NotificationEntryBuilder()
799                 .setSbn(createNotification(
800                         SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ true))
801                 .setShortcutInfo(mShortcutInfo)
802                 .setId(1);
803 
804         NotificationEntry entry = builder.build();
805         when(mNotifCollection.getAllNotifs()).thenReturn(List.of(entry));
806 
807         NotifEvent notif1 = mNoMan.postNotif(builder);
808         mClock.advanceTime(MIN_LINGER_DURATION);
809 
810         when(mNotifCollection.getAllNotifs()).thenReturn(List.of());
811         NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn.cloneLight(), 0);
812         mClock.advanceTime(MIN_LINGER_DURATION);
813 
814         PeopleSpaceTile tileWithMissedCallOrigin = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
815         assertThat(tileWithMissedCallOrigin.getNotificationKey()).isEqualTo(null);
816         assertThat(tileWithMissedCallOrigin.getNotificationContent()).isEqualTo(null);
817         verify(mAppWidgetManager, times(2)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
818                 any());
819         PeopleSpaceTile tileWithSameUri = mManager.mTiles.get(WIDGET_ID_WITH_SAME_URI);
820         assertThat(tileWithSameUri.getNotificationKey()).isEqualTo(null);
821         assertThat(tileWithSameUri.getNotificationContent()).isEqualTo(null);
822         verify(mAppWidgetManager, times(2)).updateAppWidget(eq(WIDGET_ID_WITH_SAME_URI),
823                 any());
824     }
825 
826     @Test
testUpdateMissedCallNotificationWithContentPostedIfMatchingUriTileFromSender()827     public void testUpdateMissedCallNotificationWithContentPostedIfMatchingUriTileFromSender()
828             throws Exception {
829         int[] widgetIdsArray =
830                 {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT, WIDGET_ID_WITH_SAME_URI};
831         when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
832         Notification notificationWithPersonOnlyInSender =
833                 createMessagingStyleNotificationWithoutExtras(
834                         SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */
835                         true).build();
836         StatusBarNotification sbn = new SbnBuilder()
837                 .setNotification(notificationWithPersonOnlyInSender)
838                 .setPkg(TEST_PACKAGE_A)
839                 .setUid(0)
840                 .setUser(new UserHandle(0))
841                 .build();
842         NotificationEntryBuilder builder = new NotificationEntryBuilder()
843                 .setRank(1)
844                 .setShortcutInfo(mShortcutInfo)
845                 .setSbn(sbn)
846                 .setId(1);
847         NotificationEntry entry = builder.build();
848         when(mNotifCollection.getAllNotifs()).thenReturn(List.of(entry));
849 
850         NotifEvent notif1 = mNoMan.postNotif(builder);
851         mClock.advanceTime(MIN_LINGER_DURATION);
852 
853         PeopleSpaceTile tileWithMissedCallOrigin = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
854         assertThat(tileWithMissedCallOrigin.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
855         assertThat(tileWithMissedCallOrigin.getNotificationContent()).isEqualTo(
856                 NOTIFICATION_CONTENT_1);
857         verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
858                 any());
859         PeopleSpaceTile tileWithSameUri = mManager.mTiles.get(WIDGET_ID_WITH_SAME_URI);
860         assertThat(tileWithSameUri.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
861         assertThat(tileWithSameUri.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT_1);
862         verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SAME_URI),
863                 any());
864     }
865 
866     @Test
testDoNotUpdateMissedCallNotificationWithContentPostedIfNoPersonsAttached()867     public void testDoNotUpdateMissedCallNotificationWithContentPostedIfNoPersonsAttached()
868             throws Exception {
869         int[] widgetIdsArray =
870                 {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT, WIDGET_ID_WITH_SAME_URI};
871         when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
872         // Notification posted without any Person attached.
873         Notification notificationWithoutPersonObject =
874                 createMessagingStyleNotificationWithoutExtras(
875                         SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */
876                         true).setStyle(new Notification.MessagingStyle("sender")
877                         .addMessage(
878                                 new Notification.MessagingStyle.Message(NOTIFICATION_CONTENT_1, 10,
879                                         "sender"))
880                 ).build();
881         StatusBarNotification sbn = new SbnBuilder()
882                 .setNotification(notificationWithoutPersonObject)
883                 .setPkg(TEST_PACKAGE_A)
884                 .setUid(0)
885                 .setUser(new UserHandle(0))
886                 .build();
887         NotificationEntryBuilder builder = new NotificationEntryBuilder()
888                 .setSbn(sbn)
889                 .setShortcutInfo(mShortcutInfo)
890                 .setId(1);
891         NotificationEntry entry = builder.build();
892         when(mNotifCollection.getAllNotifs()).thenReturn(List.of(entry));
893 
894         NotifEvent notif1 = mNoMan.postNotif(builder);
895 
896         mClock.advanceTime(MIN_LINGER_DURATION);
897 
898         PeopleSpaceTile tileWithMissedCallOrigin = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
899         assertThat(tileWithMissedCallOrigin.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
900         assertThat(tileWithMissedCallOrigin.getNotificationContent()).isEqualTo(
901                 NOTIFICATION_CONTENT_1);
902         verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
903                 any());
904         // Do not update since notification doesn't include a Person reference.
905         verify(mAppWidgetManager, times(0)).updateAppWidget(eq(WIDGET_ID_WITH_SAME_URI),
906                 any());
907     }
908 
909     @Test
testDoNotUpdateMissedCallNotificationWithContentPostedIfNotMatchingUriTile()910     public void testDoNotUpdateMissedCallNotificationWithContentPostedIfNotMatchingUriTile()
911             throws Exception {
912         clearStorage();
913         addTileForWidget(PERSON_TILE, WIDGET_ID_WITH_SHORTCUT);
914         addTileForWidget(PERSON_TILE_WITH_SAME_URI.toBuilder().setContactUri(
915                 Uri.parse("different_uri")).build(), WIDGET_ID_WITH_DIFFERENT_URI);
916         int[] widgetIdsArray =
917                 {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT, WIDGET_ID_WITH_DIFFERENT_URI};
918         when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
919         NotificationEntryBuilder builder = new NotificationEntryBuilder()
920                 .setSbn(createNotification(
921                         SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ true))
922                 .setShortcutInfo(mShortcutInfo)
923                 .setId(1);
924         NotificationEntry entry = builder.build();
925         when(mNotifCollection.getAllNotifs()).thenReturn(List.of(entry));
926 
927         NotifEvent notif1 = mNoMan.postNotif(builder);
928         mClock.advanceTime(MIN_LINGER_DURATION);
929 
930         PeopleSpaceTile tileWithMissedCallOrigin = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
931         assertThat(tileWithMissedCallOrigin.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
932         assertThat(tileWithMissedCallOrigin.getNotificationContent()).isEqualTo(
933                 NOTIFICATION_CONTENT_1);
934         verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
935                 any());
936         // Do not update since missing permission to read contacts.
937         verify(mAppWidgetManager, times(0)).updateAppWidget(eq(WIDGET_ID_WITH_DIFFERENT_URI),
938                 any());
939     }
940 
941     @Test
testDoNotUpdateMissedCallIfMatchingUriTileMissingReadContactsPermission()942     public void testDoNotUpdateMissedCallIfMatchingUriTileMissingReadContactsPermission()
943             throws Exception {
944         when(mPackageManager.checkPermission(any(),
945                 eq(PERSON_TILE_WITH_SAME_URI.getPackageName()))).thenReturn(
946                 PERMISSION_HARD_DENIED);
947         int[] widgetIdsArray =
948                 {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT, WIDGET_ID_WITH_SAME_URI};
949         when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
950         NotificationEntryBuilder builder = new NotificationEntryBuilder()
951                 .setSbn(createNotification(
952                         SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ true))
953                 .setShortcutInfo(mShortcutInfo)
954                 .setId(1);
955         NotificationEntry entry = builder.build();
956         when(mNotifCollection.getAllNotifs()).thenReturn(List.of(entry));
957 
958         NotifEvent notif1 = mNoMan.postNotif(builder);
959         mClock.advanceTime(MIN_LINGER_DURATION);
960 
961         PeopleSpaceTile tileWithMissedCallOrigin = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
962         assertThat(tileWithMissedCallOrigin.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
963         assertThat(tileWithMissedCallOrigin.getNotificationContent()).isEqualTo(
964                 NOTIFICATION_CONTENT_1);
965         verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
966                 any());
967         // Do not update since missing permission to read contacts.
968         PeopleSpaceTile tileNoNotification = mManager.mTiles.get(WIDGET_ID_WITH_SAME_URI);
969         assertThat(tileNoNotification.getNotificationKey()).isNull();
970         verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SAME_URI),
971                 any());
972     }
973 
974     @Test
testUpdateNotificationRemovedIfExistingTile()975     public void testUpdateNotificationRemovedIfExistingTile() throws Exception {
976         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
977         when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
978 
979         StatusBarNotification sbn = createNotification(
980                 SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false);
981         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
982                 .setSbn(sbn)
983                 .setId(1));
984         mClock.advanceTime(MIN_LINGER_DURATION);
985         long timestampBeforeNotificationClear = System.currentTimeMillis();
986         NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0);
987         mClock.advanceTime(MIN_LINGER_DURATION);
988 
989         PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
990         assertThat(tile.getNotificationKey()).isEqualTo(null);
991         assertThat(tile.getLastInteractionTimestamp()).isLessThan(
992                 timestampBeforeNotificationClear);
993         assertThat(tile.getNotificationContent()).isEqualTo(null);
994         assertThat(tile.getNotificationDataUri()).isEqualTo(null);
995         verify(mAppWidgetManager, times(2)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
996                 any());
997     }
998 
999     @Test
testAddThenReconfigureWidgetsUpdatesStorageCacheAndListeners()1000     public void testAddThenReconfigureWidgetsUpdatesStorageCacheAndListeners()
1001             throws Exception {
1002         clearStorage();
1003         mManager.addNewWidget(WIDGET_ID_WITH_SHORTCUT, new PeopleTileKey(PERSON_TILE));
1004         // Check storage.
1005         SharedPreferences widgetSp = mContext.getSharedPreferences(
1006                 String.valueOf(WIDGET_ID_WITH_SHORTCUT),
1007                 Context.MODE_PRIVATE);
1008         assertThat(widgetSp.getString(PACKAGE_NAME, null)).isEqualTo(TEST_PACKAGE_A);
1009         assertThat(widgetSp.getString(PeopleSpaceUtils.SHORTCUT_ID, null)).isEqualTo(
1010                 PERSON_TILE.getId());
1011         assertThat(widgetSp.getInt(USER_ID, INVALID_USER_ID)).isEqualTo(0);
1012         // Check listener and caching.
1013         verify(mPeopleManager).registerConversationListener(eq(TEST_PACKAGE_A), anyInt(),
1014                 eq(SHORTCUT_ID), any(),
1015                 any());
1016         verify(mLauncherApps, times(1)).cacheShortcuts(
1017                 eq(TEST_PACKAGE_A),
1018                 eq(Arrays.asList(SHORTCUT_ID)), eq(UserHandle.of(0)),
1019                 eq(LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS));
1020 
1021         // Reconfigure WIDGET_ID_WITH_SHORTCUT from PERSON_TILE to PERSON_TILE_WITH_SAME_URI
1022         mManager.addNewWidget(
1023                 WIDGET_ID_WITH_SHORTCUT, new PeopleTileKey(PERSON_TILE_WITH_SAME_URI));
1024 
1025         // Check listener is removed and shortcut is uncached.
1026         verify(mPeopleManager).unregisterConversationListener(any());
1027         verify(mLauncherApps).uncacheShortcuts(eq(TEST_PACKAGE_A),
1028                 eq(Arrays.asList(PERSON_TILE.getId())), eq(UserHandle.of(0)),
1029                 eq(LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS));
1030         // Check reconfigured storage from TEST_PACKAGE_A to B and SHORTCUT_ID to OTHER_SHORTCUT_ID.
1031         assertThat(widgetSp.getString(PACKAGE_NAME, null)).isEqualTo(TEST_PACKAGE_B);
1032         assertThat(widgetSp.getString(PeopleSpaceUtils.SHORTCUT_ID, null)).isEqualTo(
1033                 OTHER_SHORTCUT_ID);
1034         assertThat(widgetSp.getInt(USER_ID, INVALID_USER_ID)).isEqualTo(0);
1035         // Check listener & caching are reconfigured to TEST_PACKAGE_B and OTHER_SHORTCUT_ID.
1036         verify(mPeopleManager, times(1)).registerConversationListener(eq(TEST_PACKAGE_B), anyInt(),
1037                 eq(OTHER_SHORTCUT_ID), any(),
1038                 any());
1039         verify(mLauncherApps, times(1)).cacheShortcuts(
1040                 eq(TEST_PACKAGE_B),
1041                 eq(Arrays.asList(OTHER_SHORTCUT_ID)), eq(UserHandle.of(0)),
1042                 eq(LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS));
1043     }
1044 
1045     @Test
testDeleteAllWidgetsForConversationsUncachesShortcutAndRemovesListeners()1046     public void testDeleteAllWidgetsForConversationsUncachesShortcutAndRemovesListeners()
1047             throws Exception {
1048         addSecondWidgetForPersonTile();
1049         mManager.updateWidgets(new int[]{WIDGET_ID_WITH_SHORTCUT, SECOND_WIDGET_ID_WITH_SHORTCUT});
1050 
1051         // Delete only one widget for the conversation in background.
1052         mManager.deleteWidgets(new int[]{WIDGET_ID_WITH_SHORTCUT});
1053         mClock.advanceTime(MIN_LINGER_DURATION);
1054 
1055         // Check deleted storage.
1056         SharedPreferences widgetSp = mContext.getSharedPreferences(
1057                 String.valueOf(WIDGET_ID_WITH_SHORTCUT),
1058                 Context.MODE_PRIVATE);
1059         assertThat(widgetSp.getString(PACKAGE_NAME, null)).isNull();
1060         assertThat(widgetSp.getString(SHORTCUT_ID, null)).isNull();
1061         assertThat(widgetSp.getInt(USER_ID, INVALID_USER_ID)).isEqualTo(INVALID_USER_ID);
1062         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
1063         assertThat(sp.getStringSet(KEY.toString(), new HashSet<>())).containsExactly(
1064                 String.valueOf(SECOND_WIDGET_ID_WITH_SHORTCUT));
1065         // Check listener & shortcut caching remain for other widget.
1066         verify(mPeopleManager, never()).unregisterConversationListener(any());
1067         verify(mLauncherApps, never()).uncacheShortcuts(eq(TEST_PACKAGE_A),
1068                 eq(Arrays.asList(SHORTCUT_ID)), eq(UserHandle.of(0)),
1069                 eq(LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS));
1070 
1071         // Delete all widgets for the conversation in background.
1072         mManager.deleteWidgets(new int[]{SECOND_WIDGET_ID_WITH_SHORTCUT});
1073         mClock.advanceTime(MIN_LINGER_DURATION);
1074 
1075         // Check deleted storage.
1076         SharedPreferences secondWidgetSp = mContext.getSharedPreferences(
1077                 String.valueOf(SECOND_WIDGET_ID_WITH_SHORTCUT),
1078                 Context.MODE_PRIVATE);
1079         assertThat(secondWidgetSp.getString(PACKAGE_NAME, null)).isNull();
1080         assertThat(secondWidgetSp.getString(SHORTCUT_ID, null)).isNull();
1081         assertThat(secondWidgetSp.getInt(USER_ID, INVALID_USER_ID)).isEqualTo(INVALID_USER_ID);
1082         assertThat(sp.getStringSet(KEY.toString(), new HashSet<>())).isEmpty();
1083         // Check listener is removed and shortcut is uncached.
1084         verify(mPeopleManager, times(1)).unregisterConversationListener(any());
1085         verify(mLauncherApps, times(1)).uncacheShortcuts(eq(TEST_PACKAGE_A),
1086                 eq(Arrays.asList(SHORTCUT_ID)), eq(UserHandle.of(0)),
1087                 eq(LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS));
1088     }
1089 
1090     @Test
testUpdateWidgetsWithEmptyOptionsAddsPeopleTileToOptions()1091     public void testUpdateWidgetsWithEmptyOptionsAddsPeopleTileToOptions() throws Exception {
1092         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT};
1093         when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
1094         when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT)))
1095                 .thenReturn(new Bundle());
1096 
1097         mManager.updateWidgets(widgetIdsArray);
1098         mClock.advanceTime(MIN_LINGER_DURATION);
1099 
1100         verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
1101                 any());
1102     }
1103 
1104     @Test
testOnAppWidgetOptionsChangedNoWidgetAdded()1105     public void testOnAppWidgetOptionsChangedNoWidgetAdded() {
1106         Bundle newOptions = new Bundle();
1107         mManager.onAppWidgetOptionsChanged(SECOND_WIDGET_ID_WITH_SHORTCUT, newOptions);
1108 
1109         // Check that options is not modified
1110         verify(mAppWidgetManager, never()).updateAppWidgetOptions(
1111                 eq(SECOND_WIDGET_ID_WITH_SHORTCUT), any());
1112         // Check listener is not added and shortcut is not cached.
1113         verify(mPeopleManager, never()).registerConversationListener(any(), anyInt(), any(), any(),
1114                 any());
1115         verify(mLauncherApps, never()).cacheShortcuts(any(), any(), any(), anyInt());
1116         // Check no added storage.
1117         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
1118         assertThat(sp.getStringSet(KEY.toString(), new HashSet<>()))
1119                 .doesNotContain(SECOND_WIDGET_ID_WITH_SHORTCUT);
1120         SharedPreferences widgetSp = mContext.getSharedPreferences(
1121                 String.valueOf(SECOND_WIDGET_ID_WITH_SHORTCUT),
1122                 Context.MODE_PRIVATE);
1123         assertThat(widgetSp.getString(PACKAGE_NAME, EMPTY_STRING)).isEqualTo(EMPTY_STRING);
1124         assertThat(widgetSp.getString(SHORTCUT_ID, EMPTY_STRING)).isEqualTo(EMPTY_STRING);
1125         assertThat(widgetSp.getInt(USER_ID, INVALID_USER_ID)).isEqualTo(INVALID_USER_ID);
1126 
1127     }
1128 
1129     @Test
testOnAppWidgetOptionsChangedWidgetAdded()1130     public void testOnAppWidgetOptionsChangedWidgetAdded() {
1131         Bundle newOptions = new Bundle();
1132         newOptions.putString(PeopleSpaceUtils.SHORTCUT_ID, SHORTCUT_ID);
1133         newOptions.putInt(USER_ID, 0);
1134         newOptions.putString(PACKAGE_NAME, TEST_PACKAGE_A);
1135         when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT)))
1136                 .thenReturn(newOptions);
1137 
1138         mManager.onAppWidgetOptionsChanged(SECOND_WIDGET_ID_WITH_SHORTCUT, newOptions);
1139 
1140         verify(mAppWidgetManager, times(1)).updateAppWidgetOptions(
1141                 eq(SECOND_WIDGET_ID_WITH_SHORTCUT), mBundleArgumentCaptor.capture());
1142         Bundle first = mBundleArgumentCaptor.getValue();
1143         assertThat(first.getString(PeopleSpaceUtils.SHORTCUT_ID, EMPTY_STRING))
1144                 .isEqualTo(EMPTY_STRING);
1145         assertThat(first.getInt(USER_ID, INVALID_USER_ID)).isEqualTo(INVALID_USER_ID);
1146         assertThat(first.getString(PACKAGE_NAME, EMPTY_STRING)).isEqualTo(EMPTY_STRING);
1147         verify(mLauncherApps, times(1)).cacheShortcuts(eq(TEST_PACKAGE_A),
1148                 eq(Arrays.asList(SHORTCUT_ID)), eq(UserHandle.of(0)),
1149                 eq(LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS));
1150     }
1151 
1152     @Test
testGetPeopleTileFromPersistentStorageExistingConversation()1153     public void testGetPeopleTileFromPersistentStorageExistingConversation()
1154             throws Exception {
1155         ConversationChannel channel = getConversationWithShortcutId(
1156                 new PeopleTileKey(SHORTCUT_ID, 0, TEST_PACKAGE_A));
1157         when(mIPeopleManager.getConversation(TEST_PACKAGE_A, 0, SHORTCUT_ID)).thenReturn(channel);
1158         PeopleTileKey key = new PeopleTileKey(SHORTCUT_ID, 0, TEST_PACKAGE_A);
1159         PeopleSpaceTile tile = mManager
1160                 .getTileFromPersistentStorage(key, WIDGET_ID_WITH_SHORTCUT, true);
1161         assertThat(tile.getId()).isEqualTo(key.getShortcutId());
1162     }
1163 
1164     @Test
testGetPeopleTileFromPersistentStorageNoConversation()1165     public void testGetPeopleTileFromPersistentStorageNoConversation() throws Exception {
1166         when(mIPeopleManager.getConversation(TEST_PACKAGE_A, 0, SHORTCUT_ID)).thenReturn(null);
1167         PeopleTileKey key = new PeopleTileKey(SHORTCUT_ID, 0, TEST_PACKAGE_A);
1168         PeopleSpaceTile tile = mManager
1169                 .getTileFromPersistentStorage(key, WIDGET_ID_WITH_SHORTCUT, false);
1170         assertThat(tile).isNull();
1171     }
1172 
1173     @Test
testRequestPinAppWidgetExistingConversation()1174     public void testRequestPinAppWidgetExistingConversation() throws Exception {
1175         ConversationChannel channel = getConversationWithShortcutId(
1176                 new PeopleTileKey(SHORTCUT_ID, 0, TEST_PACKAGE_A));
1177         when(mIPeopleManager.getConversation(TEST_PACKAGE_A, 0, SHORTCUT_ID))
1178                 .thenReturn(channel);
1179         when(mAppWidgetManager.requestPinAppWidget(any(), any(), any())).thenReturn(true);
1180 
1181         ShortcutInfo info = new ShortcutInfo.Builder(mMockContext, SHORTCUT_ID).build();
1182         boolean valid = mManager.requestPinAppWidget(info, new Bundle());
1183 
1184         assertThat(valid).isTrue();
1185         verify(mAppWidgetManager, times(1)).requestPinAppWidget(
1186                 any(), any(), any());
1187     }
1188 
1189     @Test
testRequestPinAppWidgetNoConversation()1190     public void testRequestPinAppWidgetNoConversation() throws Exception {
1191         when(mIPeopleManager.getConversation(TEST_PACKAGE_A, 0, SHORTCUT_ID)).thenReturn(null);
1192 
1193         ShortcutInfo info = new ShortcutInfo.Builder(mMockContext, SHORTCUT_ID).build();
1194         boolean valid = mManager.requestPinAppWidget(info, new Bundle());
1195 
1196         assertThat(valid).isFalse();
1197         verify(mAppWidgetManager, never()).requestPinAppWidget(any(), any(), any());
1198     }
1199 
1200     @Test
testAugmentTileFromNotifications()1201     public void testAugmentTileFromNotifications() {
1202         clearStorage();
1203         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
1204         assertThat(sp.getString(String.valueOf(WIDGET_ID_WITH_SHORTCUT), null)).isEqualTo(null);
1205         PeopleSpaceTile tile =
1206                 new PeopleSpaceTile
1207                         .Builder(SHORTCUT_ID, "userName", ICON, new Intent())
1208                         .setPackageName(TEST_PACKAGE_A)
1209                         .setUserHandle(new UserHandle(0))
1210                         .build();
1211 
1212         PeopleTileKey key = new PeopleTileKey(tile);
1213         PeopleSpaceTile actual = mManager.augmentTileFromNotifications(tile, key, EMPTY_STRING,
1214                 Map.of(new PeopleTileKey(mNotificationEntry),
1215                         new HashSet<>(Collections.singleton(mNotificationEntry))),
1216                 Optional.of(WIDGET_ID_WITH_SHORTCUT));
1217 
1218         assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_CONTENT_1);
1219         assertThat(sp.getString(String.valueOf(WIDGET_ID_WITH_SHORTCUT), null)).isEqualTo(
1220                 URI.toString());
1221     }
1222 
1223     @Test
testAugmentTileFromNotificationsDifferentShortcutId()1224     public void testAugmentTileFromNotificationsDifferentShortcutId() {
1225         PeopleSpaceTile tile =
1226                 new PeopleSpaceTile
1227                         .Builder(OTHER_SHORTCUT_ID, "userName", ICON, new Intent())
1228                         .setPackageName(TEST_PACKAGE_A)
1229                         .setUserHandle(new UserHandle(0))
1230                         .build();
1231         PeopleTileKey key = new PeopleTileKey(tile);
1232         PeopleSpaceTile actual = mManager
1233                 .augmentTileFromNotifications(tile, key, EMPTY_STRING,
1234                         Map.of(new PeopleTileKey(mNotificationEntry),
1235                                 new HashSet<>(Collections.singleton(mNotificationEntry))),
1236                         Optional.empty());
1237 
1238         assertThat(actual.getNotificationContent()).isEqualTo(null);
1239     }
1240 
1241     @Test
testAugmentTileFromNotificationEntryManager()1242     public void testAugmentTileFromNotificationEntryManager() {
1243         PeopleSpaceTile tile =
1244                 new PeopleSpaceTile
1245                         .Builder(SHORTCUT_ID, "userName", ICON, new Intent())
1246                         .setPackageName(TEST_PACKAGE_A)
1247                         .setUserHandle(new UserHandle(0))
1248                         .build();
1249         when(mNotifCollection.getAllNotifs()).thenReturn(List.of(mNotificationEntry));
1250 
1251         PeopleSpaceTile actual =
1252                 mManager.augmentTileFromNotificationEntryManager(tile,
1253                         Optional.of(WIDGET_ID_WITH_SHORTCUT));
1254 
1255         assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_CONTENT_1);
1256 
1257         verify(mNotifCollection, times(1)).getAllNotifs();
1258     }
1259 
1260     @Test
testAugmentTileFromNotificationEntryManager_notificationHidden()1261     public void testAugmentTileFromNotificationEntryManager_notificationHidden() {
1262         when(mBubbles.isBubbleNotificationSuppressedFromShade(any(), any())).thenReturn(true);
1263         PeopleSpaceTile tile =
1264                 new PeopleSpaceTile
1265                         .Builder(SHORTCUT_ID, "userName", ICON, new Intent())
1266                         .setPackageName(TEST_PACKAGE_A)
1267                         .setUserHandle(new UserHandle(0))
1268                         .build();
1269         when(mNotifCollection.getAllNotifs()).thenReturn(List.of(mNotificationEntry));
1270 
1271         PeopleSpaceTile actual =
1272                 mManager.augmentTileFromNotificationEntryManager(tile,
1273                         Optional.of(WIDGET_ID_WITH_SHORTCUT));
1274 
1275         assertThat(TextUtils.isEmpty(actual.getNotificationContent())).isTrue();
1276 
1277         verify(mNotifCollection, times(1)).getAllNotifs();
1278     }
1279 
1280     @Test
testUpdateWidgetsFromBroadcastInBackgroundBootCompleteWithPackageUninstalled()1281     public void testUpdateWidgetsFromBroadcastInBackgroundBootCompleteWithPackageUninstalled()
1282             throws Exception {
1283         when(mPackageManager.getApplicationInfoAsUser(any(), anyInt(), anyInt())).thenThrow(
1284                 PackageManager.NameNotFoundException.class);
1285 
1286         // We should remove widgets if the package is uninstalled at next reboot if we missed the
1287         // package removed broadcast.
1288         mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED);
1289 
1290         PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
1291         assertThat(tile).isNull();
1292         verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
1293                 any());
1294     }
1295 
1296     @Test
testUpdateWidgetsFromBroadcastInBackgroundPackageRemovedWithPackageUninstalled()1297     public void testUpdateWidgetsFromBroadcastInBackgroundPackageRemovedWithPackageUninstalled()
1298             throws Exception {
1299         when(mPackageManager.getApplicationInfoAsUser(any(), anyInt(), anyInt())).thenThrow(
1300                 PackageManager.NameNotFoundException.class);
1301 
1302         mManager.updateWidgetsFromBroadcastInBackground(ACTION_PACKAGE_REMOVED);
1303 
1304         PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
1305         assertThat(tile).isNull();
1306         verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
1307                 any());
1308     }
1309 
1310     @Test
testUpdateWidgetsFromBroadcastInBackground()1311     public void testUpdateWidgetsFromBroadcastInBackground() {
1312         mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED);
1313 
1314         PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
1315         assertThat(tile.isPackageSuspended()).isFalse();
1316         assertThat(tile.isUserQuieted()).isFalse();
1317         assertThat(tile.canBypassDnd()).isFalse();
1318         assertThat(tile.getNotificationPolicyState()).isEqualTo(SHOW_CONVERSATIONS);
1319         verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
1320                 any());
1321     }
1322 
1323     @Test
testUpdateWidgetsFromBroadcastInBackgroundWithUserQuieted()1324     public void testUpdateWidgetsFromBroadcastInBackgroundWithUserQuieted() {
1325         when(mUserManager.isQuietModeEnabled(any())).thenReturn(true);
1326 
1327         mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED);
1328 
1329         PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
1330         assertThat(tile.isPackageSuspended()).isFalse();
1331         assertThat(tile.isUserQuieted()).isTrue();
1332         assertThat(tile.getNotificationPolicyState()).isEqualTo(SHOW_CONVERSATIONS);
1333     }
1334 
1335     @Test
testUpdateWidgetsFromBroadcastInBackgroundWithPackageSuspended()1336     public void testUpdateWidgetsFromBroadcastInBackgroundWithPackageSuspended() throws Exception {
1337         when(mPackageManager.isPackageSuspended(any())).thenReturn(true);
1338 
1339         mManager.updateWidgetsFromBroadcastInBackground(ACTION_PACKAGES_SUSPENDED);
1340 
1341         PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
1342         assertThat(tile.isPackageSuspended()).isTrue();
1343         assertThat(tile.isUserQuieted()).isFalse();
1344         assertThat(tile.getNotificationPolicyState()).isEqualTo(SHOW_CONVERSATIONS);
1345     }
1346 
1347     @Test
testUpdateWidgetsFromBroadcastInBackgroundNotInDnd()1348     public void testUpdateWidgetsFromBroadcastInBackgroundNotInDnd() {
1349         int expected = 0;
1350         mManager.updateWidgetsFromBroadcastInBackground(NotificationManager
1351                 .ACTION_INTERRUPTION_FILTER_CHANGED);
1352 
1353         PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
1354         assertThat(tile.getNotificationPolicyState()).isEqualTo(expected | SHOW_CONVERSATIONS);
1355     }
1356 
1357     @Test
testUpdateWidgetsFromBroadcastInBackgroundAllConversations()1358     public void testUpdateWidgetsFromBroadcastInBackgroundAllConversations() {
1359         int expected = 0;
1360         when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn(
1361                 INTERRUPTION_FILTER_PRIORITY);
1362         when(mNotificationPolicy.allowConversations()).thenReturn(true);
1363         setFinalField("priorityConversationSenders", CONVERSATION_SENDERS_ANYONE);
1364 
1365         mManager.updateWidgetsFromBroadcastInBackground(NotificationManager
1366                 .ACTION_INTERRUPTION_FILTER_CHANGED);
1367 
1368         PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
1369         assertThat(tile.getNotificationPolicyState()).isEqualTo(expected | SHOW_CONVERSATIONS);
1370     }
1371 
1372     @Test
testUpdateWidgetsFromBroadcastInBackgroundAllowOnlyImportantConversations()1373     public void testUpdateWidgetsFromBroadcastInBackgroundAllowOnlyImportantConversations() {
1374         int expected = 0;
1375         // Only allow important conversations.
1376         when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn(
1377                 INTERRUPTION_FILTER_PRIORITY);
1378         when(mNotificationPolicy.allowConversations()).thenReturn(true);
1379         setFinalField("priorityConversationSenders", CONVERSATION_SENDERS_IMPORTANT);
1380 
1381         mManager.updateWidgetsFromBroadcastInBackground(NotificationManager
1382                 .ACTION_INTERRUPTION_FILTER_CHANGED);
1383 
1384         PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
1385         assertThat(tile.getNotificationPolicyState()).isEqualTo(
1386                 expected | SHOW_IMPORTANT_CONVERSATIONS);
1387     }
1388 
1389     @Test
testUpdateWidgetsFromBroadcastInBackgroundAllowNoConversations()1390     public void testUpdateWidgetsFromBroadcastInBackgroundAllowNoConversations() {
1391         int expected = 0;
1392         when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn(
1393                 INTERRUPTION_FILTER_PRIORITY);
1394         when(mNotificationPolicy.allowConversations()).thenReturn(false);
1395 
1396         mManager.updateWidgetsFromBroadcastInBackground(NotificationManager
1397                 .ACTION_INTERRUPTION_FILTER_CHANGED);
1398 
1399         PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
1400         assertThat(tile.getNotificationPolicyState()).isEqualTo(expected | BLOCK_CONVERSATIONS);
1401     }
1402 
1403     @Test
testUpdateWidgetsFromBroadcastInBackgroundAllowNoConversationsAllowContactMessages()1404     public void testUpdateWidgetsFromBroadcastInBackgroundAllowNoConversationsAllowContactMessages() {
1405         int expected = 0;
1406         when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn(
1407                 INTERRUPTION_FILTER_PRIORITY);
1408         when(mNotificationPolicy.allowConversations()).thenReturn(false);
1409         when(mNotificationPolicy.allowMessagesFrom()).thenReturn(ZenModeConfig.SOURCE_CONTACT);
1410         when(mNotificationPolicy.allowMessages()).thenReturn(true);
1411 
1412         mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED);
1413 
1414         PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
1415         assertThat(tile.getNotificationPolicyState()).isEqualTo(expected | SHOW_CONTACTS);
1416     }
1417 
1418     @Test
testUpdateWidgetsFromBroadcastInBackgroundAllowNoConversationsAllowStarredContactMessages()1419     public void testUpdateWidgetsFromBroadcastInBackgroundAllowNoConversationsAllowStarredContactMessages() {
1420         int expected = 0;
1421         when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn(
1422                 INTERRUPTION_FILTER_PRIORITY);
1423         when(mNotificationPolicy.allowConversations()).thenReturn(false);
1424         when(mNotificationPolicy.allowMessagesFrom()).thenReturn(ZenModeConfig.SOURCE_STAR);
1425         when(mNotificationPolicy.allowMessages()).thenReturn(true);
1426 
1427         mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED);
1428 
1429         PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
1430         assertThat(tile.getNotificationPolicyState()).isEqualTo(expected | SHOW_STARRED_CONTACTS);
1431 
1432         setFinalField("suppressedVisualEffects", SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
1433                 | SUPPRESSED_EFFECT_AMBIENT);
1434         mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED);
1435 
1436         tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
1437         assertThat(tile.getNotificationPolicyState()).isEqualTo(expected | SHOW_CONVERSATIONS);
1438     }
1439 
1440     @Test
testUpdateWidgetsFromBroadcastInBackgroundAllowAlarmsOnly()1441     public void testUpdateWidgetsFromBroadcastInBackgroundAllowAlarmsOnly() {
1442         int expected = 0;
1443         when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn(
1444                 INTERRUPTION_FILTER_ALARMS);
1445 
1446         mManager.updateWidgetsFromBroadcastInBackground(NotificationManager
1447                 .ACTION_INTERRUPTION_FILTER_CHANGED);
1448 
1449         PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
1450         assertThat(tile.getNotificationPolicyState()).isEqualTo(expected | BLOCK_CONVERSATIONS);
1451     }
1452 
1453     @Test
testUpdateWidgetsFromBroadcastInBackgroundAllowVisualEffectsAndAllowAlarmsOnly()1454     public void testUpdateWidgetsFromBroadcastInBackgroundAllowVisualEffectsAndAllowAlarmsOnly() {
1455         int expected = 0;
1456         // If we show visuals, but just only make sounds for alarms, still show content in tiles.
1457         when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn(
1458                 INTERRUPTION_FILTER_ALARMS);
1459         setFinalField("suppressedVisualEffects", SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
1460                 | SUPPRESSED_EFFECT_AMBIENT);
1461 
1462         mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED);
1463 
1464         PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
1465         assertThat(tile.getNotificationPolicyState()).isEqualTo(expected | SHOW_CONVERSATIONS);
1466     }
1467 
1468     @Test
testRemapWidgetFiles()1469     public void testRemapWidgetFiles() {
1470         setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_8, URI);
1471         setStorageForTile(OTHER_SHORTCUT_ID, TEST_PACKAGE_B, WIDGET_ID_11, URI);
1472 
1473         mManager.remapWidgetFiles(WIDGETS_MAPPING);
1474 
1475         SharedPreferences sp1 = mContext.getSharedPreferences(
1476                 String.valueOf(WIDGET_ID_WITH_SHORTCUT), Context.MODE_PRIVATE);
1477         PeopleTileKey key1 = SharedPreferencesHelper.getPeopleTileKey(sp1);
1478         assertThat(key1.getShortcutId()).isEqualTo(SHORTCUT_ID);
1479         assertThat(key1.getPackageName()).isEqualTo(TEST_PACKAGE_A);
1480 
1481         SharedPreferences sp4 = mContext.getSharedPreferences(
1482                 String.valueOf(WIDGET_ID_WITH_KEY_IN_OPTIONS), Context.MODE_PRIVATE);
1483         PeopleTileKey key4 = SharedPreferencesHelper.getPeopleTileKey(sp4);
1484         assertThat(key4.getShortcutId()).isEqualTo(OTHER_SHORTCUT_ID);
1485         assertThat(key4.getPackageName()).isEqualTo(TEST_PACKAGE_B);
1486 
1487         SharedPreferences sp8 = mContext.getSharedPreferences(
1488                 String.valueOf(WIDGET_ID_8), Context.MODE_PRIVATE);
1489         PeopleTileKey key8 = SharedPreferencesHelper.getPeopleTileKey(sp8);
1490         assertThat(key8.getShortcutId()).isNull();
1491         assertThat(key8.getPackageName()).isNull();
1492 
1493         SharedPreferences sp11 = mContext.getSharedPreferences(
1494                 String.valueOf(WIDGET_ID_11), Context.MODE_PRIVATE);
1495         PeopleTileKey key11 = SharedPreferencesHelper.getPeopleTileKey(sp11);
1496         assertThat(key11.getShortcutId()).isNull();
1497         assertThat(key11.getPackageName()).isNull();
1498     }
1499 
1500     @Test
testRemapWidgetFiles_crossMapping()1501     public void testRemapWidgetFiles_crossMapping() {
1502         setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT, URI);
1503         setStorageForTile(OTHER_SHORTCUT_ID, TEST_PACKAGE_B, WIDGET_ID_WITHOUT_SHORTCUT, URI);
1504         setStorageForTile(THIRD_SHORTCUT_ID, TEST_PACKAGE_C, WIDGET_ID_WITH_KEY_IN_OPTIONS, URI);
1505 
1506         mManager.remapWidgetFiles(WIDGETS_MAPPING_CROSS_MAPPING);
1507 
1508         SharedPreferences sp1 = mContext.getSharedPreferences(
1509                 String.valueOf(WIDGET_ID_WITH_SHORTCUT), Context.MODE_PRIVATE);
1510         PeopleTileKey key1 = SharedPreferencesHelper.getPeopleTileKey(sp1);
1511         assertThat(key1.getShortcutId()).isEqualTo(THIRD_SHORTCUT_ID);
1512         assertThat(key1.getPackageName()).isEqualTo(TEST_PACKAGE_C);
1513 
1514         SharedPreferences sp2 = mContext.getSharedPreferences(
1515                 String.valueOf(WIDGET_ID_WITHOUT_SHORTCUT), Context.MODE_PRIVATE);
1516         PeopleTileKey key2 = SharedPreferencesHelper.getPeopleTileKey(sp2);
1517         assertThat(key2.getShortcutId()).isEqualTo(OTHER_SHORTCUT_ID);
1518         assertThat(key2.getPackageName()).isEqualTo(TEST_PACKAGE_B);
1519 
1520         SharedPreferences sp4 = mContext.getSharedPreferences(
1521                 String.valueOf(WIDGET_ID_WITH_KEY_IN_OPTIONS), Context.MODE_PRIVATE);
1522         PeopleTileKey key4 = SharedPreferencesHelper.getPeopleTileKey(sp4);
1523         assertThat(key4.getShortcutId()).isEqualTo(SHORTCUT_ID);
1524         assertThat(key4.getPackageName()).isEqualTo(TEST_PACKAGE_A);
1525     }
1526 
1527     @Test
testRemapSharedFile()1528     public void testRemapSharedFile() {
1529         setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_8, URI);
1530         setStorageForTile(OTHER_SHORTCUT_ID, TEST_PACKAGE_B, WIDGET_ID_11, URI);
1531 
1532         mManager.remapSharedFile(WIDGETS_MAPPING);
1533 
1534         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
1535 
1536         assertThat(sp.getString(String.valueOf(WIDGET_ID_8), null)).isNull();
1537         assertThat(sp.getString(String.valueOf(WIDGET_ID_11), null)).isNull();
1538         assertThat(sp.getString(String.valueOf(WIDGET_ID_WITH_SHORTCUT), null))
1539                 .isEqualTo(URI.toString());
1540         assertThat(sp.getString(String.valueOf(WIDGET_ID_WITH_KEY_IN_OPTIONS), null))
1541                 .isEqualTo(URI.toString());
1542 
1543         assertThat(sp.getStringSet(URI.toString(), new HashSet<>())).containsExactly(
1544                 String.valueOf(WIDGET_ID_WITH_SHORTCUT),
1545                 String.valueOf(WIDGET_ID_WITH_KEY_IN_OPTIONS));
1546 
1547         PeopleTileKey key8 = new PeopleTileKey(SHORTCUT_ID, 0, TEST_PACKAGE_A);
1548         assertThat(sp.getStringSet(key8.toString(), new HashSet<>())).containsExactly(
1549                 String.valueOf(WIDGET_ID_WITH_SHORTCUT));
1550 
1551         PeopleTileKey key11 = new PeopleTileKey(OTHER_SHORTCUT_ID, 0, TEST_PACKAGE_B);
1552         assertThat(sp.getStringSet(key11.toString(), new HashSet<>())).containsExactly(
1553                 String.valueOf(WIDGET_ID_WITH_KEY_IN_OPTIONS));
1554     }
1555 
1556     @Test
testRemapFollowupFile()1557     public void testRemapFollowupFile() {
1558         PeopleTileKey key8 = new PeopleTileKey(SHORTCUT_ID, 0, TEST_PACKAGE_A);
1559         PeopleTileKey key11 = new PeopleTileKey(OTHER_SHORTCUT_ID, 0, TEST_PACKAGE_B);
1560         Set<String> set8 = new HashSet<>(Collections.singleton(String.valueOf(WIDGET_ID_8)));
1561         Set<String> set11 = new HashSet<>(Collections.singleton(String.valueOf(WIDGET_ID_11)));
1562 
1563         SharedPreferences followUp = mContext.getSharedPreferences(
1564                 PeopleBackupFollowUpJob.SHARED_FOLLOW_UP, Context.MODE_PRIVATE);
1565         SharedPreferences.Editor followUpEditor = followUp.edit();
1566         followUpEditor.putStringSet(key8.toString(), set8);
1567         followUpEditor.putStringSet(key11.toString(), set11);
1568         followUpEditor.apply();
1569 
1570         mManager.remapFollowupFile(WIDGETS_MAPPING);
1571 
1572         assertThat(followUp.getStringSet(key8.toString(), new HashSet<>())).containsExactly(
1573                 String.valueOf(WIDGET_ID_WITH_SHORTCUT));
1574         assertThat(followUp.getStringSet(key11.toString(), new HashSet<>())).containsExactly(
1575                 String.valueOf(WIDGET_ID_WITH_KEY_IN_OPTIONS));
1576     }
1577 
1578     @Test
1579     @DisableFlags({
1580         android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS,
1581         android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL
1582     })
testUpdateGeneratedPreview_flagDisabled()1583     public void testUpdateGeneratedPreview_flagDisabled() {
1584         mManager.updateGeneratedPreviewForUser(mUserTracker.getUserHandle());
1585         verify(mAppWidgetManager, times(0)).setWidgetPreview(any(), anyInt(), any());
1586     }
1587 
1588     @Test
1589     @EnableFlags(android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS)
1590     @DisableFlags(android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL)
testUpdateGeneratedPreview_userLocked()1591     public void testUpdateGeneratedPreview_userLocked() {
1592         when(mUserManager.isUserUnlocked(mUserTracker.getUserHandle())).thenReturn(false);
1593 
1594         mManager.updateGeneratedPreviewForUser(mUserTracker.getUserHandle());
1595         verify(mAppWidgetManager, times(0)).setWidgetPreview(any(), anyInt(), any());
1596     }
1597 
1598     @Test
1599     @EnableFlags(android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS)
1600     @DisableFlags(android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL)
testUpdateGeneratedPreview_userUnlocked()1601     public void testUpdateGeneratedPreview_userUnlocked() {
1602         when(mUserManager.isUserUnlocked(mUserTracker.getUserHandle())).thenReturn(true);
1603         when(mAppWidgetManager.setWidgetPreview(any(), anyInt(), any())).thenReturn(true);
1604 
1605         mManager.updateGeneratedPreviewForUser(mUserTracker.getUserHandle());
1606         verify(mAppWidgetManager, times(1)).setWidgetPreview(any(), anyInt(), any());
1607     }
1608 
1609     @Test
1610     @EnableFlags(android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS)
1611     @DisableFlags(android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL)
testUpdateGeneratedPreview_doesNotSetTwice()1612     public void testUpdateGeneratedPreview_doesNotSetTwice() {
1613         when(mUserManager.isUserUnlocked(mUserTracker.getUserHandle())).thenReturn(true);
1614         when(mAppWidgetManager.setWidgetPreview(any(), anyInt(), any())).thenReturn(true);
1615 
1616         mManager.updateGeneratedPreviewForUser(mUserTracker.getUserHandle());
1617         mManager.updateGeneratedPreviewForUser(mUserTracker.getUserHandle());
1618         verify(mAppWidgetManager, times(1)).setWidgetPreview(any(), anyInt(), any());
1619     }
1620 
setFinalField(String fieldName, int value)1621     private void setFinalField(String fieldName, int value) {
1622         try {
1623             Field field = NotificationManager.Policy.class.getDeclaredField(fieldName);
1624             field.setAccessible(true);
1625             field.set(mNotificationPolicy, value);
1626         } catch (Exception e) {
1627         }
1628     }
1629 
1630     /**
1631      * Adds another widget for {@code PERSON_TILE} with widget ID: {@code
1632      * SECOND_WIDGET_ID_WITH_SHORTCUT}.
1633      */
addSecondWidgetForPersonTile()1634     private void addSecondWidgetForPersonTile() throws Exception {
1635         // Set the same Person associated on another People Tile widget ID.
1636         addTileForWidget(PERSON_TILE, SECOND_WIDGET_ID_WITH_SHORTCUT);
1637         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT,
1638                 SECOND_WIDGET_ID_WITH_SHORTCUT};
1639         when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
1640     }
1641 
addTileForWidget(PeopleSpaceTile tile, int widgetId)1642     private void addTileForWidget(PeopleSpaceTile tile, int widgetId) throws Exception {
1643         setStorageForTile(tile.getId(), tile.getPackageName(), widgetId, tile.getContactUri());
1644         Bundle options = new Bundle();
1645         ConversationChannel channel = getConversationWithShortcutId(new PeopleTileKey(tile));
1646         when(mAppWidgetManager.getAppWidgetOptions(eq(widgetId)))
1647                 .thenReturn(options);
1648         when(mIPeopleManager.getConversation(tile.getPackageName(), 0, tile.getId()))
1649                 .thenReturn(channel);
1650         when(mPackageManager.checkPermission(any(), eq(tile.getPackageName()))).thenReturn(
1651                 PERMISSION_GRANTED);
1652     }
1653 
1654     /**
1655      * Returns a single conversation associated with {@code shortcutId}.
1656      */
getConversationWithShortcutId(PeopleTileKey key)1657     private ConversationChannel getConversationWithShortcutId(PeopleTileKey key) throws Exception {
1658         return getConversationWithShortcutId(key, Arrays.asList());
1659     }
1660 
1661     /**
1662      * Returns a single conversation associated with {@code shortcutId} and {@code statuses}.
1663      */
getConversationWithShortcutId(PeopleTileKey key, List<ConversationStatus> statuses)1664     private ConversationChannel getConversationWithShortcutId(PeopleTileKey key,
1665             List<ConversationStatus> statuses) throws Exception {
1666         when(mMockContext.getPackageName()).thenReturn(key.getPackageName());
1667         when(mMockContext.getUserId()).thenReturn(key.getUserId());
1668         ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(mMockContext, key.getShortcutId())
1669                 .setLongLabel("name").setPerson(PERSON).build();
1670         ConversationChannel convo = new ConversationChannel(shortcutInfo, 0, null, null,
1671                 0L, false, false, statuses);
1672         return convo;
1673     }
1674 
createMessagingStyleNotification(String shortcutId, boolean isMessagingStyle, boolean isMissedCall)1675     private Notification createMessagingStyleNotification(String shortcutId,
1676             boolean isMessagingStyle, boolean isMissedCall) {
1677         Bundle extras = new Bundle();
1678         ArrayList<Person> person = new ArrayList<Person>();
1679         person.add(PERSON);
1680         extras.putParcelableArrayList(EXTRA_PEOPLE_LIST, person);
1681         Notification.Builder builder = new Notification.Builder(mContext)
1682                 .setContentTitle("TEST_TITLE")
1683                 .setContentText("TEST_TEXT")
1684                 .setExtras(extras)
1685                 .setShortcutId(shortcutId);
1686         if (isMessagingStyle) {
1687             builder.setStyle(new Notification.MessagingStyle(PERSON)
1688                     .addMessage(
1689                             new Notification.MessagingStyle.Message(NOTIFICATION_CONTENT_1, 10,
1690                                     PERSON))
1691             );
1692         }
1693         if (isMissedCall) {
1694             builder.setCategory(CATEGORY_MISSED_CALL);
1695         }
1696         return builder.build();
1697     }
1698 
createMessagingStyleNotificationWithoutExtras(String shortcutId, boolean isMessagingStyle, boolean isMissedCall)1699     private Notification.Builder createMessagingStyleNotificationWithoutExtras(String shortcutId,
1700             boolean isMessagingStyle, boolean isMissedCall) {
1701         Notification.Builder builder = new Notification.Builder(mContext)
1702                 .setContentTitle("TEST_TITLE")
1703                 .setContentText("TEST_TEXT")
1704                 .setShortcutId(shortcutId);
1705         if (isMessagingStyle) {
1706             builder.setStyle(new Notification.MessagingStyle(PERSON)
1707                     .addMessage(
1708                             new Notification.MessagingStyle.Message(NOTIFICATION_CONTENT_1, 10,
1709                                     PERSON))
1710             );
1711         }
1712         if (isMissedCall) {
1713             builder.setCategory(CATEGORY_MISSED_CALL);
1714         }
1715         return builder;
1716     }
1717 
1718 
createNotification(String shortcutId, boolean isMessagingStyle, boolean isMissedCall)1719     private StatusBarNotification createNotification(String shortcutId,
1720             boolean isMessagingStyle, boolean isMissedCall) {
1721         Notification notification = createMessagingStyleNotification(
1722                 shortcutId, isMessagingStyle, isMissedCall);
1723         return new SbnBuilder()
1724                 .setNotification(notification)
1725                 .setPkg(TEST_PACKAGE_A)
1726                 .setUid(0)
1727                 .setPostTime(SBN_POST_TIME)
1728                 .setUser(new UserHandle(0))
1729                 .build();
1730     }
1731 
clearStorage()1732     private void clearStorage() {
1733         SharedPreferences widgetSp1 = mContext.getSharedPreferences(
1734                 String.valueOf(WIDGET_ID_WITH_SHORTCUT),
1735                 Context.MODE_PRIVATE);
1736         widgetSp1.edit().clear().commit();
1737         SharedPreferences widgetSp2 = mContext.getSharedPreferences(
1738                 String.valueOf(WIDGET_ID_WITHOUT_SHORTCUT),
1739                 Context.MODE_PRIVATE);
1740         widgetSp2.edit().clear().commit();
1741         SharedPreferences widgetSp3 = mContext.getSharedPreferences(
1742                 String.valueOf(SECOND_WIDGET_ID_WITH_SHORTCUT),
1743                 Context.MODE_PRIVATE);
1744         widgetSp3.edit().clear().commit();
1745         SharedPreferences widgetSp4 = mContext.getSharedPreferences(
1746                 String.valueOf(WIDGET_ID_WITH_KEY_IN_OPTIONS),
1747                 Context.MODE_PRIVATE);
1748         widgetSp4.edit().clear().commit();
1749         SharedPreferences widgetSp5 = mContext.getSharedPreferences(
1750                 String.valueOf(WIDGET_ID_WITH_SAME_URI),
1751                 Context.MODE_PRIVATE);
1752         widgetSp5.edit().clear().commit();
1753         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
1754         sp.edit().clear().commit();
1755         mManager.mListeners.clear();
1756         mManager.mTiles.clear();
1757     }
1758 
setStorageForTile(String shortcutId, String packageName, int widgetId, Uri contactUri)1759     private void setStorageForTile(String shortcutId, String packageName, int widgetId,
1760             Uri contactUri) {
1761         SharedPreferences widgetSp = mContext.getSharedPreferences(
1762                 String.valueOf(widgetId),
1763                 Context.MODE_PRIVATE);
1764         SharedPreferences.Editor widgetEditor = widgetSp.edit();
1765         widgetEditor.putString(PeopleSpaceUtils.PACKAGE_NAME, packageName);
1766         widgetEditor.putString(PeopleSpaceUtils.SHORTCUT_ID, shortcutId);
1767         widgetEditor.putInt(PeopleSpaceUtils.USER_ID, 0);
1768         widgetEditor.apply();
1769 
1770         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
1771         SharedPreferences.Editor editor = sp.edit();
1772         editor.putString(String.valueOf(widgetId), contactUri.toString());
1773 
1774         String key = new PeopleTileKey(shortcutId, 0, packageName).toString();
1775         Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
1776         storedWidgetIds.add(String.valueOf(widgetId));
1777         editor.putStringSet(key, storedWidgetIds);
1778 
1779         Set<String> storedWidgetIdsByUri = new HashSet<>(
1780                 sp.getStringSet(contactUri.toString(), new HashSet<>()));
1781         storedWidgetIdsByUri.add(String.valueOf(widgetId));
1782         editor.putStringSet(contactUri.toString(), storedWidgetIdsByUri);
1783         editor.apply();
1784     }
1785 
getConversationChannelWrapper(String shortcutId, boolean importantConversation, long lastInteractionTimestamp)1786     private ConversationChannelWrapper getConversationChannelWrapper(String shortcutId,
1787             boolean importantConversation, long lastInteractionTimestamp) throws Exception {
1788         ConversationChannelWrapper convo = new ConversationChannelWrapper();
1789         NotificationChannel notificationChannel = new NotificationChannel(shortcutId,
1790                 "channel" + shortcutId,
1791                 NotificationManager.IMPORTANCE_DEFAULT);
1792         notificationChannel.setImportantConversation(importantConversation);
1793         convo.setNotificationChannel(notificationChannel);
1794         convo.setShortcutInfo(new ShortcutInfo.Builder(mContext, shortcutId).setLongLabel(
1795                 "name").build());
1796         when(mIPeopleManager.getLastInteraction(anyString(), anyInt(),
1797                 eq(shortcutId))).thenReturn(lastInteractionTimestamp);
1798         return convo;
1799     }
1800 
getConversationChannel(String shortcutId, long lastInteractionTimestamp)1801     private ConversationChannel getConversationChannel(String shortcutId,
1802             long lastInteractionTimestamp) throws Exception {
1803         ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(mContext, shortcutId).setLongLabel(
1804                 "name").build();
1805         ConversationChannel convo = new ConversationChannel(shortcutInfo, 0, null, null,
1806                 lastInteractionTimestamp, false);
1807         when(mIPeopleManager.getLastInteraction(anyString(), anyInt(),
1808                 eq(shortcutId))).thenReturn(lastInteractionTimestamp);
1809         return convo;
1810     }
1811 }
1812