1 /* 2 * Copyright (C) 2021 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 android.app.people; 18 19 import android.annotation.NonNull; 20 import android.app.Person; 21 import android.content.Intent; 22 import android.content.pm.LauncherApps; 23 import android.content.pm.ShortcutInfo; 24 import android.graphics.Bitmap; 25 import android.graphics.Canvas; 26 import android.graphics.drawable.BitmapDrawable; 27 import android.graphics.drawable.Drawable; 28 import android.graphics.drawable.Icon; 29 import android.net.Uri; 30 import android.os.Parcel; 31 import android.os.Parcelable; 32 import android.os.UserHandle; 33 34 import java.util.ArrayList; 35 import java.util.List; 36 37 /** 38 * The People Space tile contains all relevant information to render a tile in People Space: namely 39 * the data of any visible conversation notification associated, associated statuses, and the last 40 * interaction time. 41 * 42 * @hide 43 */ 44 public class PeopleSpaceTile implements Parcelable { 45 46 public static final int SHOW_CONVERSATIONS = 1 << 0; 47 public static final int BLOCK_CONVERSATIONS = 1 << 1; 48 public static final int SHOW_IMPORTANT_CONVERSATIONS = 1 << 2; 49 public static final int SHOW_STARRED_CONTACTS = 1 << 3; 50 public static final int SHOW_CONTACTS = 1 << 4; 51 52 private String mId; 53 private CharSequence mUserName; 54 private Icon mUserIcon; 55 private UserHandle mUserHandle; 56 private Uri mContactUri; 57 private String mPackageName; 58 private String mBirthdayText; 59 private long mLastInteractionTimestamp; 60 private boolean mIsImportantConversation; 61 private String mNotificationKey; 62 private CharSequence mNotificationContent; 63 private CharSequence mNotificationSender; 64 private String mNotificationCategory; 65 private Uri mNotificationDataUri; 66 private int mMessagesCount; 67 private Intent mIntent; 68 private long mNotificationTimestamp; 69 private List<ConversationStatus> mStatuses; 70 private boolean mCanBypassDnd; 71 private boolean mIsPackageSuspended; 72 private boolean mIsUserQuieted; 73 private int mNotificationPolicyState; 74 private float mContactAffinity; 75 PeopleSpaceTile(Builder b)76 private PeopleSpaceTile(Builder b) { 77 mId = b.mId; 78 mUserName = b.mUserName; 79 mUserIcon = b.mUserIcon; 80 mContactUri = b.mContactUri; 81 mUserHandle = b.mUserHandle; 82 mPackageName = b.mPackageName; 83 mBirthdayText = b.mBirthdayText; 84 mLastInteractionTimestamp = b.mLastInteractionTimestamp; 85 mIsImportantConversation = b.mIsImportantConversation; 86 mNotificationKey = b.mNotificationKey; 87 mNotificationContent = b.mNotificationContent; 88 mNotificationSender = b.mNotificationSender; 89 mNotificationCategory = b.mNotificationCategory; 90 mNotificationDataUri = b.mNotificationDataUri; 91 mMessagesCount = b.mMessagesCount; 92 mIntent = b.mIntent; 93 mNotificationTimestamp = b.mNotificationTimestamp; 94 mStatuses = b.mStatuses; 95 mCanBypassDnd = b.mCanBypassDnd; 96 mIsPackageSuspended = b.mIsPackageSuspended; 97 mIsUserQuieted = b.mIsUserQuieted; 98 mNotificationPolicyState = b.mNotificationPolicyState; 99 mContactAffinity = b.mContactAffinity; 100 } 101 getId()102 public String getId() { 103 return mId; 104 } 105 getUserName()106 public CharSequence getUserName() { 107 return mUserName; 108 } 109 getUserIcon()110 public Icon getUserIcon() { 111 return mUserIcon; 112 } 113 114 /** Returns the Uri associated with the user in Android Contacts database. */ getContactUri()115 public Uri getContactUri() { 116 return mContactUri; 117 } 118 getUserHandle()119 public UserHandle getUserHandle() { 120 return mUserHandle; 121 } 122 getPackageName()123 public String getPackageName() { 124 return mPackageName; 125 } 126 getBirthdayText()127 public String getBirthdayText() { 128 return mBirthdayText; 129 } 130 131 /** Returns the timestamp of the last interaction. */ getLastInteractionTimestamp()132 public long getLastInteractionTimestamp() { 133 return mLastInteractionTimestamp; 134 } 135 136 /** 137 * Whether the conversation is important. 138 */ isImportantConversation()139 public boolean isImportantConversation() { 140 return mIsImportantConversation; 141 } 142 143 /** 144 * If a notification is currently active that maps to the relevant shortcut ID, provides the 145 * associated notification's key. 146 */ getNotificationKey()147 public String getNotificationKey() { 148 return mNotificationKey; 149 } 150 getNotificationContent()151 public CharSequence getNotificationContent() { 152 return mNotificationContent; 153 } 154 getNotificationSender()155 public CharSequence getNotificationSender() { 156 return mNotificationSender; 157 } 158 getNotificationCategory()159 public String getNotificationCategory() { 160 return mNotificationCategory; 161 } 162 getNotificationDataUri()163 public Uri getNotificationDataUri() { 164 return mNotificationDataUri; 165 } 166 getMessagesCount()167 public int getMessagesCount() { 168 return mMessagesCount; 169 } 170 171 /** 172 * Provides an intent to launch. If present, we should manually launch the intent on tile 173 * click, rather than calling {@link android.content.pm.LauncherApps} to launch the shortcut ID. 174 * 175 * <p>This field should only be used if manually constructing a tile without an associated 176 * shortcut to launch (i.e. birthday tiles). 177 */ getIntent()178 public Intent getIntent() { 179 return mIntent; 180 } 181 182 /** Returns the timestamp of the last notification. */ getNotificationTimestamp()183 public long getNotificationTimestamp() { 184 return mNotificationTimestamp; 185 } 186 187 /** Returns the statuses associated with the tile. */ getStatuses()188 public List<ConversationStatus> getStatuses() { 189 return mStatuses; 190 } 191 192 /** 193 * Whether the app associated with the conversation can bypass DND. 194 */ canBypassDnd()195 public boolean canBypassDnd() { 196 return mCanBypassDnd; 197 } 198 199 /** 200 * Whether the app associated with the conversation is suspended. 201 */ isPackageSuspended()202 public boolean isPackageSuspended() { 203 return mIsPackageSuspended; 204 } 205 206 /** 207 * Whether the user associated with the conversation is quieted. 208 */ isUserQuieted()209 public boolean isUserQuieted() { 210 return mIsUserQuieted; 211 } 212 213 /** 214 * Returns the state of notifications for the conversation. 215 */ getNotificationPolicyState()216 public int getNotificationPolicyState() { 217 return mNotificationPolicyState; 218 } 219 220 /** 221 * Returns the contact affinity (whether the contact is starred). 222 */ getContactAffinity()223 public float getContactAffinity() { 224 return mContactAffinity; 225 } 226 227 /** Converts a {@link PeopleSpaceTile} into a {@link PeopleSpaceTile.Builder}. */ toBuilder()228 public Builder toBuilder() { 229 Builder builder = 230 new Builder(mId, mUserName, mUserIcon, mIntent); 231 builder.setContactUri(mContactUri); 232 builder.setUserHandle(mUserHandle); 233 builder.setPackageName(mPackageName); 234 builder.setBirthdayText(mBirthdayText); 235 builder.setLastInteractionTimestamp(mLastInteractionTimestamp); 236 builder.setIsImportantConversation(mIsImportantConversation); 237 builder.setNotificationKey(mNotificationKey); 238 builder.setNotificationContent(mNotificationContent); 239 builder.setNotificationSender(mNotificationSender); 240 builder.setNotificationCategory(mNotificationCategory); 241 builder.setNotificationDataUri(mNotificationDataUri); 242 builder.setMessagesCount(mMessagesCount); 243 builder.setIntent(mIntent); 244 builder.setNotificationTimestamp(mNotificationTimestamp); 245 builder.setStatuses(mStatuses); 246 builder.setCanBypassDnd(mCanBypassDnd); 247 builder.setIsPackageSuspended(mIsPackageSuspended); 248 builder.setIsUserQuieted(mIsUserQuieted); 249 builder.setNotificationPolicyState(mNotificationPolicyState); 250 builder.setContactAffinity(mContactAffinity); 251 return builder; 252 } 253 254 /** Builder to create a {@link PeopleSpaceTile}. */ 255 public static class Builder { 256 private String mId; 257 private CharSequence mUserName; 258 private Icon mUserIcon; 259 private Uri mContactUri; 260 private UserHandle mUserHandle; 261 private String mPackageName; 262 private String mBirthdayText; 263 private long mLastInteractionTimestamp; 264 private boolean mIsImportantConversation; 265 private String mNotificationKey; 266 private CharSequence mNotificationContent; 267 private CharSequence mNotificationSender; 268 private String mNotificationCategory; 269 private Uri mNotificationDataUri; 270 private int mMessagesCount; 271 private Intent mIntent; 272 private long mNotificationTimestamp; 273 private List<ConversationStatus> mStatuses; 274 private boolean mCanBypassDnd; 275 private boolean mIsPackageSuspended; 276 private boolean mIsUserQuieted; 277 private int mNotificationPolicyState; 278 private float mContactAffinity; 279 280 /** Builder for use only if a shortcut is not available for the tile. */ Builder(String id, CharSequence userName, Icon userIcon, Intent intent)281 public Builder(String id, CharSequence userName, Icon userIcon, Intent intent) { 282 mId = id; 283 mUserName = userName; 284 mUserIcon = userIcon; 285 mIntent = intent; 286 mPackageName = intent == null ? null : intent.getPackage(); 287 mNotificationPolicyState = SHOW_CONVERSATIONS; 288 } 289 Builder(ShortcutInfo info, LauncherApps launcherApps)290 public Builder(ShortcutInfo info, LauncherApps launcherApps) { 291 mId = info.getId(); 292 mUserName = info.getLabel(); 293 mUserIcon = convertDrawableToIcon(launcherApps.getShortcutIconDrawable(info, 0)); 294 mUserHandle = info.getUserHandle(); 295 mPackageName = info.getPackage(); 296 mContactUri = getContactUri(info); 297 mNotificationPolicyState = SHOW_CONVERSATIONS; 298 } 299 Builder(ConversationChannel channel, LauncherApps launcherApps)300 public Builder(ConversationChannel channel, LauncherApps launcherApps) { 301 ShortcutInfo info = channel.getShortcutInfo(); 302 mId = info.getId(); 303 mUserName = info.getLabel(); 304 mUserIcon = convertDrawableToIcon(launcherApps.getShortcutIconDrawable(info, 0)); 305 mUserHandle = info.getUserHandle(); 306 mPackageName = info.getPackage(); 307 mContactUri = getContactUri(info); 308 mStatuses = channel.getStatuses(); 309 mLastInteractionTimestamp = channel.getLastEventTimestamp(); 310 mIsImportantConversation = channel.getNotificationChannel() != null 311 && channel.getNotificationChannel().isImportantConversation(); 312 mCanBypassDnd = channel.getNotificationChannel() != null 313 && channel.getNotificationChannel().canBypassDnd(); 314 mNotificationPolicyState = SHOW_CONVERSATIONS; 315 } 316 317 /** Returns the Contact's Uri if present. */ getContactUri(ShortcutInfo info)318 public Uri getContactUri(ShortcutInfo info) { 319 if (info.getPersons() == null || info.getPersons().length != 1) { 320 return null; 321 } 322 // TODO(b/175584929): Update to use the Uri from PeopleService directly 323 Person person = info.getPersons()[0]; 324 return person.getUri() == null ? null : Uri.parse(person.getUri()); 325 } 326 327 /** Sets the ID for the tile. */ setId(String id)328 public Builder setId(String id) { 329 mId = id; 330 return this; 331 } 332 333 /** Sets the user name. */ setUserName(CharSequence userName)334 public Builder setUserName(CharSequence userName) { 335 mUserName = userName; 336 return this; 337 } 338 339 /** Sets the icon shown for the user. */ setUserIcon(Icon userIcon)340 public Builder setUserIcon(Icon userIcon) { 341 mUserIcon = userIcon; 342 return this; 343 } 344 345 /** Sets the Uri associated with the user in Android Contacts database. */ setContactUri(Uri uri)346 public Builder setContactUri(Uri uri) { 347 mContactUri = uri; 348 return this; 349 } 350 351 /** Sets the associated {@code userHandle}. */ setUserHandle(UserHandle userHandle)352 public Builder setUserHandle(UserHandle userHandle) { 353 mUserHandle = userHandle; 354 return this; 355 } 356 357 /** Sets the package shown that provided the information. */ setPackageName(String packageName)358 public Builder setPackageName(String packageName) { 359 mPackageName = packageName; 360 return this; 361 } 362 363 /** Sets the status text. */ setBirthdayText(String birthdayText)364 public Builder setBirthdayText(String birthdayText) { 365 mBirthdayText = birthdayText; 366 return this; 367 } 368 369 /** Sets the last interaction timestamp. */ setLastInteractionTimestamp(long lastInteractionTimestamp)370 public Builder setLastInteractionTimestamp(long lastInteractionTimestamp) { 371 mLastInteractionTimestamp = lastInteractionTimestamp; 372 return this; 373 } 374 375 /** Sets whether the conversation is important. */ setIsImportantConversation(boolean isImportantConversation)376 public Builder setIsImportantConversation(boolean isImportantConversation) { 377 mIsImportantConversation = isImportantConversation; 378 return this; 379 } 380 381 /** Sets the associated notification's key. */ setNotificationKey(String notificationKey)382 public Builder setNotificationKey(String notificationKey) { 383 mNotificationKey = notificationKey; 384 return this; 385 } 386 387 /** Sets the associated notification's content. */ setNotificationContent(CharSequence notificationContent)388 public Builder setNotificationContent(CharSequence notificationContent) { 389 mNotificationContent = notificationContent; 390 return this; 391 } 392 393 /** Sets the associated notification's sender. */ setNotificationSender(CharSequence notificationSender)394 public Builder setNotificationSender(CharSequence notificationSender) { 395 mNotificationSender = notificationSender; 396 return this; 397 } 398 399 /** Sets the associated notification's category. */ setNotificationCategory(String notificationCategory)400 public Builder setNotificationCategory(String notificationCategory) { 401 mNotificationCategory = notificationCategory; 402 return this; 403 } 404 405 /** Sets the associated notification's data URI. */ setNotificationDataUri(Uri notificationDataUri)406 public Builder setNotificationDataUri(Uri notificationDataUri) { 407 mNotificationDataUri = notificationDataUri; 408 return this; 409 } 410 411 /** Sets the number of messages associated with the Tile. */ setMessagesCount(int messagesCount)412 public Builder setMessagesCount(int messagesCount) { 413 mMessagesCount = messagesCount; 414 return this; 415 } 416 417 /** Sets an intent to launch on click. */ setIntent(Intent intent)418 public Builder setIntent(Intent intent) { 419 mIntent = intent; 420 return this; 421 } 422 423 /** Sets the notification timestamp. */ setNotificationTimestamp(long notificationTimestamp)424 public Builder setNotificationTimestamp(long notificationTimestamp) { 425 mNotificationTimestamp = notificationTimestamp; 426 return this; 427 } 428 429 /** Sets the statuses. */ setStatuses(List<ConversationStatus> statuses)430 public Builder setStatuses(List<ConversationStatus> statuses) { 431 mStatuses = statuses; 432 return this; 433 } 434 435 /** Sets whether the conversation channel can bypass DND. */ setCanBypassDnd(boolean canBypassDnd)436 public Builder setCanBypassDnd(boolean canBypassDnd) { 437 mCanBypassDnd = canBypassDnd; 438 return this; 439 } 440 441 /** Sets whether the package is suspended. */ setIsPackageSuspended(boolean isPackageSuspended)442 public Builder setIsPackageSuspended(boolean isPackageSuspended) { 443 mIsPackageSuspended = isPackageSuspended; 444 return this; 445 } 446 447 /** Sets whether the user has been quieted. */ setIsUserQuieted(boolean isUserQuieted)448 public Builder setIsUserQuieted(boolean isUserQuieted) { 449 mIsUserQuieted = isUserQuieted; 450 return this; 451 } 452 453 /** Sets the state of blocked notifications for the conversation. */ setNotificationPolicyState(int notificationPolicyState)454 public Builder setNotificationPolicyState(int notificationPolicyState) { 455 mNotificationPolicyState = notificationPolicyState; 456 return this; 457 } 458 459 /** Sets the contact's affinity. */ setContactAffinity(float contactAffinity)460 public Builder setContactAffinity(float contactAffinity) { 461 mContactAffinity = contactAffinity; 462 return this; 463 } 464 465 /** Builds a {@link PeopleSpaceTile}. */ 466 @NonNull build()467 public PeopleSpaceTile build() { 468 return new PeopleSpaceTile(this); 469 } 470 } 471 PeopleSpaceTile(Parcel in)472 public PeopleSpaceTile(Parcel in) { 473 mId = in.readString(); 474 mUserName = in.readCharSequence(); 475 mUserIcon = in.readParcelable(Icon.class.getClassLoader()); 476 mContactUri = in.readParcelable(Uri.class.getClassLoader()); 477 mUserHandle = in.readParcelable(UserHandle.class.getClassLoader()); 478 mPackageName = in.readString(); 479 mBirthdayText = in.readString(); 480 mLastInteractionTimestamp = in.readLong(); 481 mIsImportantConversation = in.readBoolean(); 482 mNotificationKey = in.readString(); 483 mNotificationContent = in.readCharSequence(); 484 mNotificationSender = in.readCharSequence(); 485 mNotificationCategory = in.readString(); 486 mNotificationDataUri = in.readParcelable(Uri.class.getClassLoader()); 487 mMessagesCount = in.readInt(); 488 mIntent = in.readParcelable(Intent.class.getClassLoader()); 489 mNotificationTimestamp = in.readLong(); 490 mStatuses = new ArrayList<>(); 491 in.readParcelableList(mStatuses, ConversationStatus.class.getClassLoader()); 492 mCanBypassDnd = in.readBoolean(); 493 mIsPackageSuspended = in.readBoolean(); 494 mIsUserQuieted = in.readBoolean(); 495 mNotificationPolicyState = in.readInt(); 496 mContactAffinity = in.readFloat(); 497 } 498 499 @Override describeContents()500 public int describeContents() { 501 return 0; 502 } 503 504 @Override writeToParcel(Parcel dest, int flags)505 public void writeToParcel(Parcel dest, int flags) { 506 dest.writeString(mId); 507 dest.writeCharSequence(mUserName); 508 dest.writeParcelable(mUserIcon, flags); 509 dest.writeParcelable(mContactUri, flags); 510 dest.writeParcelable(mUserHandle, flags); 511 dest.writeString(mPackageName); 512 dest.writeString(mBirthdayText); 513 dest.writeLong(mLastInteractionTimestamp); 514 dest.writeBoolean(mIsImportantConversation); 515 dest.writeString(mNotificationKey); 516 dest.writeCharSequence(mNotificationContent); 517 dest.writeCharSequence(mNotificationSender); 518 dest.writeString(mNotificationCategory); 519 dest.writeParcelable(mNotificationDataUri, flags); 520 dest.writeInt(mMessagesCount); 521 dest.writeParcelable(mIntent, flags); 522 dest.writeLong(mNotificationTimestamp); 523 dest.writeParcelableList(mStatuses, flags); 524 dest.writeBoolean(mCanBypassDnd); 525 dest.writeBoolean(mIsPackageSuspended); 526 dest.writeBoolean(mIsUserQuieted); 527 dest.writeInt(mNotificationPolicyState); 528 dest.writeFloat(mContactAffinity); 529 } 530 531 public static final @android.annotation.NonNull 532 Creator<PeopleSpaceTile> CREATOR = new Creator<PeopleSpaceTile>() { 533 public PeopleSpaceTile createFromParcel(Parcel source) { 534 return new PeopleSpaceTile(source); 535 } 536 public PeopleSpaceTile[] newArray(int size) { 537 return new PeopleSpaceTile[size]; 538 } 539 }; 540 541 /** Converts {@code drawable} to a {@link Icon}. */ convertDrawableToIcon(Drawable drawable)542 public static Icon convertDrawableToIcon(Drawable drawable) { 543 if (drawable == null) { 544 return null; 545 } 546 547 if (drawable instanceof BitmapDrawable) { 548 BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; 549 if (bitmapDrawable.getBitmap() != null) { 550 return Icon.createWithBitmap(bitmapDrawable.getBitmap()); 551 } 552 } 553 554 Bitmap bitmap; 555 if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) { 556 bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); 557 // Single color bitmap will be created of 1x1 pixel 558 } else { 559 bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), 560 drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); 561 } 562 563 Canvas canvas = new Canvas(bitmap); 564 drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); 565 drawable.draw(canvas); 566 return Icon.createWithBitmap(bitmap); 567 } 568 } 569