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