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;
18 
19 import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME;
20 import static com.android.systemui.people.PeopleSpaceUtils.getContactLookupKeysWithBirthdaysToday;
21 
22 import static com.google.common.truth.Truth.assertThat;
23 
24 import static org.mockito.ArgumentMatchers.any;
25 import static org.mockito.ArgumentMatchers.anyString;
26 import static org.mockito.ArgumentMatchers.eq;
27 import static org.mockito.Mockito.mock;
28 import static org.mockito.Mockito.never;
29 import static org.mockito.Mockito.times;
30 import static org.mockito.Mockito.verify;
31 import static org.mockito.Mockito.when;
32 
33 import android.app.INotificationManager;
34 import android.app.Notification;
35 import android.app.Person;
36 import android.app.backup.BackupManager;
37 import android.app.people.IPeopleManager;
38 import android.app.people.PeopleSpaceTile;
39 import android.appwidget.AppWidgetManager;
40 import android.content.ContentProviderOperation;
41 import android.content.ContentResolver;
42 import android.content.Context;
43 import android.content.Intent;
44 import android.content.pm.LauncherApps;
45 import android.content.pm.PackageManager;
46 import android.content.pm.ShortcutInfo;
47 import android.content.res.Configuration;
48 import android.content.res.Resources;
49 import android.database.Cursor;
50 import android.graphics.drawable.Icon;
51 import android.net.Uri;
52 import android.os.Bundle;
53 import android.os.UserHandle;
54 import android.provider.ContactsContract;
55 import android.util.DisplayMetrics;
56 
57 import androidx.test.ext.junit.runners.AndroidJUnit4;
58 import androidx.test.filters.SmallTest;
59 
60 import com.android.internal.appwidget.IAppWidgetService;
61 import com.android.systemui.SysuiTestCase;
62 import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
63 import com.android.systemui.people.widget.PeopleTileKey;
64 import com.android.systemui.res.R;
65 import com.android.systemui.statusbar.NotificationListener;
66 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
67 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
68 
69 import org.junit.After;
70 import org.junit.Before;
71 import org.junit.Test;
72 import org.junit.runner.RunWith;
73 import org.mockito.Mock;
74 import org.mockito.MockitoAnnotations;
75 
76 import java.text.SimpleDateFormat;
77 import java.time.Duration;
78 import java.util.ArrayList;
79 import java.util.Date;
80 import java.util.List;
81 import java.util.Map;
82 import java.util.Optional;
83 
84 @RunWith(AndroidJUnit4.class)
85 @SmallTest
86 public class PeopleSpaceUtilsTest extends SysuiTestCase {
87 
88     private static final int WIDGET_ID_WITH_SHORTCUT = 1;
89     private static final int WIDGET_ID_WITHOUT_SHORTCUT = 2;
90     private static final String SHORTCUT_ID_1 = "101";
91     private static final String SHORTCUT_ID_2 = "202";
92     private static final String SHORTCUT_ID_3 = "303";
93     private static final String SHORTCUT_ID_4 = "404";
94     private static final String NOTIFICATION_KEY = "notification_key";
95     private static final String NOTIFICATION_CONTENT = "notification_content";
96     private static final String TEST_LOOKUP_KEY = "lookup_key";
97     private static final String NOTIFICATION_TEXT_1 = "notification_text_1";
98     private static final String NOTIFICATION_TEXT_2 = "notification_text_2";
99     private static final String NOTIFICATION_TEXT_3 = "notification_text_3";
100     private static final String NOTIFICATION_TEXT_4 = "notification_text_4";
101     private static final int TEST_COLUMN_INDEX = 1;
102     private static final Uri URI = Uri.parse("fake_uri");
103     private static final Icon ICON = Icon.createWithResource("package", R.drawable.ic_android);
104     private static final String NAME = "username";
105     private static final UserHandle USER = new UserHandle(0);
106     private static final Person PERSON = new Person.Builder()
107             .setName("name")
108             .setKey("abc")
109             .setUri(URI.toString())
110             .setBot(false)
111             .build();
112     private static final PeopleSpaceTile PERSON_TILE =
113             new PeopleSpaceTile
114                     .Builder(SHORTCUT_ID_1, NAME, ICON, new Intent())
115                     .setUserHandle(USER)
116                     .setLastInteractionTimestamp(123L)
117                     .setNotificationKey(NOTIFICATION_KEY)
118                     .setNotificationContent(NOTIFICATION_CONTENT)
119                     .setNotificationDataUri(URI)
120                     .setMessagesCount(1)
121                     .build();
122     private static final String TEST_DISPLAY_NAME = "Display Name";
123 
124     private final ShortcutInfo mShortcutInfo = new ShortcutInfo.Builder(mContext,
125             SHORTCUT_ID_1).setLongLabel(
126             NAME).setPerson(PERSON)
127             .build();
128     private final ShortcutInfo mShortcutInfoWithoutPerson = new ShortcutInfo.Builder(mContext,
129             SHORTCUT_ID_1).setLongLabel(
130             NAME)
131             .build();
132     private final Notification mNotification1 = new Notification.Builder(mContext, "test")
133             .setContentTitle("TEST_TITLE")
134             .setContentText("TEST_TEXT")
135             .setShortcutId(SHORTCUT_ID_1)
136             .setStyle(new Notification.MessagingStyle(PERSON)
137                     .addMessage(new Notification.MessagingStyle.Message(
138                             NOTIFICATION_TEXT_1, 0, PERSON))
139                     .addMessage(new Notification.MessagingStyle.Message(
140                             NOTIFICATION_TEXT_2, 20, PERSON))
141                     .addMessage(new Notification.MessagingStyle.Message(
142                             NOTIFICATION_TEXT_3, 10, PERSON))
143             )
144             .build();
145     private final Notification mNotification2 = new Notification.Builder(mContext, "test2")
146             .setContentTitle("TEST_TITLE")
147             .setContentText("OTHER_TEXT")
148             .setShortcutId(SHORTCUT_ID_2)
149             .setStyle(new Notification.MessagingStyle(PERSON)
150                     .addMessage(new Notification.MessagingStyle.Message(
151                             NOTIFICATION_TEXT_4, 0, PERSON))
152             )
153             .build();
154     private final Notification mNotification3 = new Notification.Builder(mContext, "test2")
155             .setContentTitle("TEST_TITLE")
156             .setContentText("OTHER_TEXT")
157             .setShortcutId(SHORTCUT_ID_3)
158             .setStyle(new Notification.MessagingStyle(PERSON))
159             .build();
160     private final NotificationEntry mNotificationEntry1 = new NotificationEntryBuilder()
161             .setNotification(mNotification1)
162             .setShortcutInfo(new ShortcutInfo.Builder(mContext, SHORTCUT_ID_1).build())
163             .setUser(UserHandle.of(0))
164             .setPkg(PACKAGE_NAME)
165             .build();
166     private final NotificationEntry mNotificationEntry2 = new NotificationEntryBuilder()
167             .setNotification(mNotification2)
168             .setShortcutInfo(new ShortcutInfo.Builder(mContext, SHORTCUT_ID_2).build())
169             .setUser(UserHandle.of(0))
170             .setPkg(PACKAGE_NAME)
171             .build();
172     private final NotificationEntry mNotificationEntry3 = new NotificationEntryBuilder()
173             .setNotification(mNotification3)
174             .setShortcutInfo(new ShortcutInfo.Builder(mContext, SHORTCUT_ID_3).build())
175             .setUser(UserHandle.of(0))
176             .setPkg(PACKAGE_NAME)
177             .build();
178 
179     @Mock
180     private NotificationListener mListenerService;
181     @Mock
182     private INotificationManager mNotificationManager;
183     @Mock
184     private IPeopleManager mPeopleManager;
185     @Mock
186     private IAppWidgetService mIAppWidgetService;
187     @Mock
188     private AppWidgetManager mAppWidgetManager;
189     @Mock
190     private Cursor mMockCursor;
191     @Mock
192     private ContentResolver mMockContentResolver;
193     @Mock
194     private Context mMockContext;
195     @Mock
196     private PackageManager mPackageManager;
197     @Mock
198     private PeopleSpaceWidgetManager mPeopleSpaceWidgetManager;
199     @Mock
200     private BackupManager mBackupManager;
201 
202     private Bundle mOptions;
203 
204     @Before
setUp()205     public void setUp() throws Exception {
206         MockitoAnnotations.initMocks(this);
207 
208         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
209         mOptions = new Bundle();
210 
211         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
212         when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT)))
213                 .thenReturn(mOptions);
214         when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITHOUT_SHORTCUT)))
215                 .thenReturn(new Bundle());
216 
217         Configuration configuration = mock(Configuration.class);
218         DisplayMetrics displayMetrics = mock(DisplayMetrics.class);
219         Resources resources = mock(Resources.class);
220         when(mMockContext.getResources()).thenReturn(resources);
221         when(resources.getConfiguration()).thenReturn(configuration);
222         when(resources.getDisplayMetrics()).thenReturn(displayMetrics);
223         when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver);
224         when(mMockContentResolver.query(any(Uri.class), any(), any(), any(),
225                 any())).thenReturn(mMockCursor);
226         when(mMockContext.getString(R.string.birthday_status)).thenReturn(
227                 mContext.getString(R.string.birthday_status));
228         when(mMockContext.getString(R.string.basic_status)).thenReturn(
229                 mContext.getString(R.string.basic_status));
230         when(mMockContext.getPackageManager()).thenReturn(mPackageManager);
231         when(mMockContext.getString(R.string.over_two_weeks_timestamp)).thenReturn(
232                 mContext.getString(R.string.over_two_weeks_timestamp));
233         when(mPackageManager.getApplicationIcon(anyString())).thenReturn(null);
234     }
235 
236     @After
tearDown()237     public void tearDown() {
238         cleanupTestContactFromContactProvider();
239     }
240 
241 
242     @Test
testAugmentTileFromNotification()243     public void testAugmentTileFromNotification() {
244         PeopleSpaceTile tile =
245                 new PeopleSpaceTile
246                         .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent())
247                         .setPackageName(PACKAGE_NAME)
248                         .setUserHandle(new UserHandle(0))
249                         .build();
250         PeopleTileKey key = new PeopleTileKey(tile);
251         PeopleSpaceTile actual = PeopleSpaceUtils
252                 .augmentTileFromNotification(mContext, tile, key, mNotificationEntry1, 0,
253                         Optional.empty(), mBackupManager);
254 
255         assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_2);
256         assertThat(actual.getNotificationSender()).isEqualTo(null);
257     }
258 
259     @Test
testAugmentTileFromNotificationGroupWithSender()260     public void testAugmentTileFromNotificationGroupWithSender() {
261         Bundle extras = new Bundle();
262         extras.putBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION, true);
263         Notification notification = new Notification.Builder(mContext, "test")
264                 .setContentTitle("TEST_TITLE")
265                 .setContentText("TEST_TEXT")
266                 .setShortcutId(SHORTCUT_ID_1)
267                 .setStyle(new Notification.MessagingStyle(PERSON)
268                         .setGroupConversation(true)
269                         .addMessage(new Notification.MessagingStyle.Message(
270                                 NOTIFICATION_TEXT_1, 0, PERSON))
271                         .addMessage(new Notification.MessagingStyle.Message(
272                                 NOTIFICATION_TEXT_2, 20, PERSON))
273                         .addMessage(new Notification.MessagingStyle.Message(
274                                 NOTIFICATION_TEXT_3, 10, PERSON))
275                 )
276                 .setExtras(extras)
277                 .build();
278         NotificationEntry notificationEntry = new NotificationEntryBuilder()
279                 .setNotification(notification)
280                 .setShortcutInfo(new ShortcutInfo.Builder(mContext, SHORTCUT_ID_1).build())
281                 .setUser(UserHandle.of(0))
282                 .setPkg(PACKAGE_NAME)
283                 .build();
284         PeopleSpaceTile tile =
285                 new PeopleSpaceTile
286                         .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent())
287                         .setPackageName(PACKAGE_NAME)
288                         .setUserHandle(new UserHandle(0))
289                         .build();
290         PeopleTileKey key = new PeopleTileKey(tile);
291         PeopleSpaceTile actual = PeopleSpaceUtils
292                 .augmentTileFromNotification(mContext, tile, key, notificationEntry, 0,
293                         Optional.empty(), mBackupManager);
294 
295         assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_2);
296         assertThat(actual.getNotificationSender().toString()).isEqualTo("name");
297     }
298 
299     @Test
testAugmentTileFromNotificationGroupWithImageUri()300     public void testAugmentTileFromNotificationGroupWithImageUri() {
301         Notification notification = new Notification.Builder(mContext, "test")
302                 .setContentTitle("TEST_TITLE")
303                 .setContentText("TEST_TEXT")
304                 .setShortcutId(SHORTCUT_ID_1)
305                 .setStyle(new Notification.MessagingStyle(PERSON)
306                         .addMessage(new Notification.MessagingStyle.Message(
307                                 NOTIFICATION_TEXT_1, 0, PERSON)
308                                 .setData("image/jpeg", URI))
309                 )
310                 .build();
311         NotificationEntry notificationEntry = new NotificationEntryBuilder()
312                 .setNotification(notification)
313                 .setShortcutInfo(new ShortcutInfo.Builder(mContext, SHORTCUT_ID_1).build())
314                 .setUser(UserHandle.of(0))
315                 .setPkg(PACKAGE_NAME)
316                 .build();
317         PeopleSpaceTile tile =
318                 new PeopleSpaceTile
319                         .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent())
320                         .setPackageName(PACKAGE_NAME)
321                         .setUserHandle(new UserHandle(0))
322                         .build();
323         PeopleTileKey key = new PeopleTileKey(tile);
324         PeopleSpaceTile actual = PeopleSpaceUtils
325                 .augmentTileFromNotification(mContext, tile, key, notificationEntry, 0,
326                         Optional.empty(), mBackupManager);
327 
328         assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_1);
329         assertThat(actual.getNotificationDataUri()).isEqualTo(URI);
330     }
331 
332     @Test
testAugmentTileFromNotificationGroupWithAudioUri()333     public void testAugmentTileFromNotificationGroupWithAudioUri() {
334         Notification notification = new Notification.Builder(mContext, "test")
335                 .setContentTitle("TEST_TITLE")
336                 .setContentText("TEST_TEXT")
337                 .setShortcutId(SHORTCUT_ID_1)
338                 .setStyle(new Notification.MessagingStyle(PERSON)
339                         .addMessage(new Notification.MessagingStyle.Message(
340                                 NOTIFICATION_TEXT_1, 0, PERSON)
341                                 .setData("audio/ogg", URI))
342                 )
343                 .build();
344         NotificationEntry notificationEntry = new NotificationEntryBuilder()
345                 .setNotification(notification)
346                 .setShortcutInfo(new ShortcutInfo.Builder(mContext, SHORTCUT_ID_1).build())
347                 .setUser(UserHandle.of(0))
348                 .setPkg(PACKAGE_NAME)
349                 .build();
350         PeopleSpaceTile tile =
351                 new PeopleSpaceTile
352                         .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent())
353                         .setPackageName(PACKAGE_NAME)
354                         .setUserHandle(new UserHandle(0))
355                         .build();
356         PeopleTileKey key = new PeopleTileKey(tile);
357         PeopleSpaceTile actual = PeopleSpaceUtils
358                 .augmentTileFromNotification(mContext, tile, key, notificationEntry, 0,
359                         Optional.empty(), mBackupManager);
360 
361         assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_1);
362         assertThat(actual.getNotificationDataUri()).isNull();
363     }
364 
365 
366     @Test
testAugmentTileFromNotificationNoContent()367     public void testAugmentTileFromNotificationNoContent() {
368         PeopleSpaceTile tile =
369                 new PeopleSpaceTile
370                         .Builder(SHORTCUT_ID_3, "userName", ICON, new Intent())
371                         .setPackageName(PACKAGE_NAME)
372                         .setUserHandle(new UserHandle(0))
373                         .build();
374         PeopleTileKey key = new PeopleTileKey(tile);
375         PeopleSpaceTile actual = PeopleSpaceUtils
376                 .augmentTileFromNotification(mContext, tile, key, mNotificationEntry3, 0,
377                         Optional.empty(), mBackupManager);
378 
379         assertThat(actual.getNotificationContent()).isEqualTo(null);
380     }
381 
382     @Test
testDoNotUpdateSingleConversationAppWidgetWhenNotBirthday()383     public void testDoNotUpdateSingleConversationAppWidgetWhenNotBirthday() {
384         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT};
385         when(mMockCursor.moveToNext()).thenReturn(true, false);
386         when(mMockCursor.getString(eq(TEST_COLUMN_INDEX))).thenReturn(TEST_LOOKUP_KEY);
387         when(mMockCursor.getColumnIndex(eq(ContactsContract.CommonDataKinds.Event.LOOKUP_KEY)
388         )).thenReturn(TEST_COLUMN_INDEX);
389 
390         // Existing tile does not have birthday status.
391         Map<Integer, PeopleSpaceTile> widgetIdToTile = Map.of(WIDGET_ID_WITH_SHORTCUT,
392                 new PeopleSpaceTile.Builder(mShortcutInfoWithoutPerson,
393                         mContext.getSystemService(LauncherApps.class)).build());
394         PeopleSpaceUtils.getDataFromContacts(mMockContext, mPeopleSpaceWidgetManager,
395                 widgetIdToTile, widgetIdsArray);
396 
397         verify(mPeopleSpaceWidgetManager, never()).updateAppWidgetOptionsAndView(
398                 eq(WIDGET_ID_WITH_SHORTCUT),
399                 any());
400     }
401 
402     @Test
testUpdateSingleConversationAppWidgetWithoutPersonContactUriToRemoveBirthday()403     public void testUpdateSingleConversationAppWidgetWithoutPersonContactUriToRemoveBirthday() {
404         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT};
405         when(mMockCursor.moveToNext()).thenReturn(true, false);
406         when(mMockCursor.getString(eq(TEST_COLUMN_INDEX))).thenReturn(TEST_LOOKUP_KEY);
407         when(mMockCursor.getColumnIndex(eq(ContactsContract.CommonDataKinds.Event.LOOKUP_KEY)
408         )).thenReturn(TEST_COLUMN_INDEX);
409 
410         // Existing tile has a birthday status.
411         Map<Integer, PeopleSpaceTile> widgetIdToTile = Map.of(WIDGET_ID_WITH_SHORTCUT,
412                 new PeopleSpaceTile.Builder(mShortcutInfoWithoutPerson,
413                         mContext.getSystemService(LauncherApps.class)).setBirthdayText(
414                         mContext.getString(R.string.birthday_status)).build());
415         PeopleSpaceUtils.getDataFromContacts(mMockContext, mPeopleSpaceWidgetManager,
416                 widgetIdToTile, widgetIdsArray);
417 
418         verify(mPeopleSpaceWidgetManager, times(1)).updateAppWidgetOptionsAndView(
419                 eq(WIDGET_ID_WITH_SHORTCUT),
420                 any());
421     }
422 
423     @Test
testUpdateSingleConversationAppWidgetToRemoveBirthdayWhenNoLongerBirthday()424     public void testUpdateSingleConversationAppWidgetToRemoveBirthdayWhenNoLongerBirthday() {
425         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT};
426         Cursor mockPersonUriCursor = mock(Cursor.class);
427         Cursor mockBirthdaysUriCursor = mock(Cursor.class);
428         when(mockPersonUriCursor.moveToNext()).thenReturn(true, false);
429         when(mockBirthdaysUriCursor.moveToNext()).thenReturn(true, false);
430         when(mockBirthdaysUriCursor.getColumnIndex(
431                 eq(ContactsContract.CommonDataKinds.Event.LOOKUP_KEY)
432         )).thenReturn(TEST_COLUMN_INDEX);
433         when(mockPersonUriCursor.getColumnIndex(
434                 eq(ContactsContract.CommonDataKinds.Event.LOOKUP_KEY)
435         )).thenReturn(TEST_COLUMN_INDEX);
436         // Return different cursors based on the Uri queried.
437         when(mMockContentResolver.query(eq(URI), any(), any(), any(),
438                 any())).thenReturn(mockPersonUriCursor);
439         when(mMockContentResolver.query(eq(ContactsContract.Data.CONTENT_URI), any(), any(), any(),
440                 any())).thenReturn(mockBirthdaysUriCursor);
441         // Each cursor returns a different lookup key.
442         when(mockBirthdaysUriCursor.getString(eq(TEST_COLUMN_INDEX))).thenReturn(TEST_LOOKUP_KEY);
443         when(mockPersonUriCursor.getString(eq(TEST_COLUMN_INDEX))).thenReturn(
444                 TEST_LOOKUP_KEY + "differentlookup");
445 
446         // Existing tile has a birthday status.
447         Map<Integer, PeopleSpaceTile> widgetIdToTile = Map.of(WIDGET_ID_WITH_SHORTCUT,
448                 new PeopleSpaceTile.Builder(mShortcutInfo,
449                         mContext.getSystemService(LauncherApps.class)).setBirthdayText(
450                         mContext.getString(R.string.birthday_status)).build());
451         PeopleSpaceUtils.getDataFromContacts(mMockContext, mPeopleSpaceWidgetManager,
452                 widgetIdToTile, widgetIdsArray);
453 
454         verify(mPeopleSpaceWidgetManager, times(1)).updateAppWidgetOptionsAndView(
455                 eq(WIDGET_ID_WITH_SHORTCUT),
456                 any());
457     }
458 
459     @Test
testUpdateSingleConversationAppWidgetWhenBirthday()460     public void testUpdateSingleConversationAppWidgetWhenBirthday() {
461         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT};
462         when(mMockCursor.moveToNext()).thenReturn(true, false, true, false);
463         when(mMockCursor.getString(eq(TEST_COLUMN_INDEX))).thenReturn(TEST_LOOKUP_KEY);
464         when(mMockCursor.getInt(eq(TEST_COLUMN_INDEX + 1))).thenReturn(1);
465         when(mMockCursor.getColumnIndex(eq(ContactsContract.Contacts.STARRED))).thenReturn(
466                 TEST_COLUMN_INDEX + 1);
467         when(mMockCursor.getColumnIndex(eq(ContactsContract.CommonDataKinds.Event.LOOKUP_KEY)
468         )).thenReturn(TEST_COLUMN_INDEX);
469 
470         // Existing tile has a birthday status.
471         Map<Integer, PeopleSpaceTile> widgetIdToTile = Map.of(WIDGET_ID_WITH_SHORTCUT,
472                 new PeopleSpaceTile.Builder(mShortcutInfo,
473                         mContext.getSystemService(LauncherApps.class)).setBirthdayText(
474                         mContext.getString(R.string.birthday_status)).build());
475         PeopleSpaceUtils.getDataFromContacts(mMockContext, mPeopleSpaceWidgetManager,
476                 widgetIdToTile, widgetIdsArray);
477 
478         verify(mPeopleSpaceWidgetManager, times(1)).updateAppWidgetOptionsAndView(
479                 eq(WIDGET_ID_WITH_SHORTCUT),
480                 any());
481     }
482 
483     @Test
testBirthdayQueriesWithYear()484     public void testBirthdayQueriesWithYear() throws Exception {
485         String birthdayToday = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
486         addBirthdayToContactsDatabase(birthdayToday);
487 
488         List<String> lookupKeys = getContactLookupKeysWithBirthdaysToday(mContext);
489 
490         assertThat(lookupKeys).hasSize(1);
491     }
492 
493     @Test
testBirthdayQueriesWithoutYear()494     public void testBirthdayQueriesWithoutYear() throws Exception {
495         String birthdayToday = new SimpleDateFormat("--MM-dd").format(new Date());
496         addBirthdayToContactsDatabase(birthdayToday);
497 
498         List<String> lookupKeys = getContactLookupKeysWithBirthdaysToday(mContext);
499 
500         assertThat(lookupKeys).hasSize(1);
501     }
502 
503     @Test
testBirthdayQueriesWithDifferentDates()504     public void testBirthdayQueriesWithDifferentDates() throws Exception {
505         Date yesterday = new Date(System.currentTimeMillis() - Duration.ofDays(1).toMillis());
506         String birthdayYesterday = new SimpleDateFormat("--MM-dd").format(yesterday);
507         addBirthdayToContactsDatabase(birthdayYesterday);
508 
509         List<String> lookupKeys = getContactLookupKeysWithBirthdaysToday(mContext);
510 
511         assertThat(lookupKeys).isEmpty();
512     }
513 
addBirthdayToContactsDatabase(String birthdayDate)514     private void addBirthdayToContactsDatabase(String birthdayDate) throws Exception {
515         ContentResolver resolver = mContext.getContentResolver();
516         ArrayList<ContentProviderOperation> ops = new ArrayList<>(3);
517         ops.add(ContentProviderOperation
518                 .newInsert(ContactsContract.RawContacts.CONTENT_URI)
519                 .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, "com.google")
520                 .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, "fakeAccountName")
521                 .build());
522         ops.add(ContentProviderOperation
523                 .newInsert(ContactsContract.Data.CONTENT_URI)
524                 .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
525                 .withValue(ContactsContract.Data.MIMETYPE,
526                         ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
527                 .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME,
528                         TEST_DISPLAY_NAME)
529                 .build());
530         ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
531                 .withValueBackReference(
532                         ContactsContract.Data.RAW_CONTACT_ID, 0)
533                 .withValue(
534                         ContactsContract.Data.MIMETYPE,
535                         ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE)
536                 .withValue(
537                         ContactsContract.CommonDataKinds.Event.TYPE,
538                         ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY)
539                 .withValue(
540                         ContactsContract.CommonDataKinds.Event.START_DATE, birthdayDate)
541                 .build());
542         resolver.applyBatch(ContactsContract.AUTHORITY, ops);
543     }
544 
cleanupTestContactFromContactProvider()545     private void cleanupTestContactFromContactProvider() {
546         Cursor cursor = mContext.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,
547                 null,
548                 ContactsContract.Contacts.DISPLAY_NAME_PRIMARY + "=?",
549                 new String[]{TEST_DISPLAY_NAME},
550                 null);
551         while (cursor.moveToNext()) {
552             String contactId = cursor.getString(cursor.getColumnIndex(
553                     ContactsContract.Contacts.NAME_RAW_CONTACT_ID));
554             mContext.getContentResolver().delete(ContactsContract.Data.CONTENT_URI,
555                     ContactsContract.Data.RAW_CONTACT_ID + "=?",
556                     new String[]{contactId});
557         }
558         cursor.close();
559     }
560 }
561