/* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.app.people; import android.annotation.NonNull; import android.app.Person; import android.content.Intent; import android.content.pm.LauncherApps; import android.content.pm.ShortcutInfo; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; import android.os.UserHandle; import java.util.ArrayList; import java.util.List; /** * The People Space tile contains all relevant information to render a tile in People Space: namely * the data of any visible conversation notification associated, associated statuses, and the last * interaction time. * * @hide */ public class PeopleSpaceTile implements Parcelable { public static final int SHOW_CONVERSATIONS = 1 << 0; public static final int BLOCK_CONVERSATIONS = 1 << 1; public static final int SHOW_IMPORTANT_CONVERSATIONS = 1 << 2; public static final int SHOW_STARRED_CONTACTS = 1 << 3; public static final int SHOW_CONTACTS = 1 << 4; private String mId; private CharSequence mUserName; private Icon mUserIcon; private UserHandle mUserHandle; private Uri mContactUri; private String mPackageName; private String mBirthdayText; private long mLastInteractionTimestamp; private boolean mIsImportantConversation; private String mNotificationKey; private CharSequence mNotificationContent; private CharSequence mNotificationSender; private String mNotificationCategory; private Uri mNotificationDataUri; private int mMessagesCount; private Intent mIntent; private long mNotificationTimestamp; private List mStatuses; private boolean mCanBypassDnd; private boolean mIsPackageSuspended; private boolean mIsUserQuieted; private int mNotificationPolicyState; private float mContactAffinity; private PeopleSpaceTile(Builder b) { mId = b.mId; mUserName = b.mUserName; mUserIcon = b.mUserIcon; mContactUri = b.mContactUri; mUserHandle = b.mUserHandle; mPackageName = b.mPackageName; mBirthdayText = b.mBirthdayText; mLastInteractionTimestamp = b.mLastInteractionTimestamp; mIsImportantConversation = b.mIsImportantConversation; mNotificationKey = b.mNotificationKey; mNotificationContent = b.mNotificationContent; mNotificationSender = b.mNotificationSender; mNotificationCategory = b.mNotificationCategory; mNotificationDataUri = b.mNotificationDataUri; mMessagesCount = b.mMessagesCount; mIntent = b.mIntent; mNotificationTimestamp = b.mNotificationTimestamp; mStatuses = b.mStatuses; mCanBypassDnd = b.mCanBypassDnd; mIsPackageSuspended = b.mIsPackageSuspended; mIsUserQuieted = b.mIsUserQuieted; mNotificationPolicyState = b.mNotificationPolicyState; mContactAffinity = b.mContactAffinity; } public String getId() { return mId; } public CharSequence getUserName() { return mUserName; } public Icon getUserIcon() { return mUserIcon; } /** Returns the Uri associated with the user in Android Contacts database. */ public Uri getContactUri() { return mContactUri; } public UserHandle getUserHandle() { return mUserHandle; } public String getPackageName() { return mPackageName; } public String getBirthdayText() { return mBirthdayText; } /** Returns the timestamp of the last interaction. */ public long getLastInteractionTimestamp() { return mLastInteractionTimestamp; } /** * Whether the conversation is important. */ public boolean isImportantConversation() { return mIsImportantConversation; } /** * If a notification is currently active that maps to the relevant shortcut ID, provides the * associated notification's key. */ public String getNotificationKey() { return mNotificationKey; } public CharSequence getNotificationContent() { return mNotificationContent; } public CharSequence getNotificationSender() { return mNotificationSender; } public String getNotificationCategory() { return mNotificationCategory; } public Uri getNotificationDataUri() { return mNotificationDataUri; } public int getMessagesCount() { return mMessagesCount; } /** * Provides an intent to launch. If present, we should manually launch the intent on tile * click, rather than calling {@link android.content.pm.LauncherApps} to launch the shortcut ID. * *

This field should only be used if manually constructing a tile without an associated * shortcut to launch (i.e. birthday tiles). */ public Intent getIntent() { return mIntent; } /** Returns the timestamp of the last notification. */ public long getNotificationTimestamp() { return mNotificationTimestamp; } /** Returns the statuses associated with the tile. */ public List getStatuses() { return mStatuses; } /** * Whether the app associated with the conversation can bypass DND. */ public boolean canBypassDnd() { return mCanBypassDnd; } /** * Whether the app associated with the conversation is suspended. */ public boolean isPackageSuspended() { return mIsPackageSuspended; } /** * Whether the user associated with the conversation is quieted. */ public boolean isUserQuieted() { return mIsUserQuieted; } /** * Returns the state of notifications for the conversation. */ public int getNotificationPolicyState() { return mNotificationPolicyState; } /** * Returns the contact affinity (whether the contact is starred). */ public float getContactAffinity() { return mContactAffinity; } /** Converts a {@link PeopleSpaceTile} into a {@link PeopleSpaceTile.Builder}. */ public Builder toBuilder() { Builder builder = new Builder(mId, mUserName, mUserIcon, mIntent); builder.setContactUri(mContactUri); builder.setUserHandle(mUserHandle); builder.setPackageName(mPackageName); builder.setBirthdayText(mBirthdayText); builder.setLastInteractionTimestamp(mLastInteractionTimestamp); builder.setIsImportantConversation(mIsImportantConversation); builder.setNotificationKey(mNotificationKey); builder.setNotificationContent(mNotificationContent); builder.setNotificationSender(mNotificationSender); builder.setNotificationCategory(mNotificationCategory); builder.setNotificationDataUri(mNotificationDataUri); builder.setMessagesCount(mMessagesCount); builder.setIntent(mIntent); builder.setNotificationTimestamp(mNotificationTimestamp); builder.setStatuses(mStatuses); builder.setCanBypassDnd(mCanBypassDnd); builder.setIsPackageSuspended(mIsPackageSuspended); builder.setIsUserQuieted(mIsUserQuieted); builder.setNotificationPolicyState(mNotificationPolicyState); builder.setContactAffinity(mContactAffinity); return builder; } /** Builder to create a {@link PeopleSpaceTile}. */ public static class Builder { private String mId; private CharSequence mUserName; private Icon mUserIcon; private Uri mContactUri; private UserHandle mUserHandle; private String mPackageName; private String mBirthdayText; private long mLastInteractionTimestamp; private boolean mIsImportantConversation; private String mNotificationKey; private CharSequence mNotificationContent; private CharSequence mNotificationSender; private String mNotificationCategory; private Uri mNotificationDataUri; private int mMessagesCount; private Intent mIntent; private long mNotificationTimestamp; private List mStatuses; private boolean mCanBypassDnd; private boolean mIsPackageSuspended; private boolean mIsUserQuieted; private int mNotificationPolicyState; private float mContactAffinity; /** Builder for use only if a shortcut is not available for the tile. */ public Builder(String id, CharSequence userName, Icon userIcon, Intent intent) { mId = id; mUserName = userName; mUserIcon = userIcon; mIntent = intent; mPackageName = intent == null ? null : intent.getPackage(); mNotificationPolicyState = SHOW_CONVERSATIONS; } public Builder(ShortcutInfo info, LauncherApps launcherApps) { mId = info.getId(); mUserName = info.getLabel(); mUserIcon = convertDrawableToIcon(launcherApps.getShortcutIconDrawable(info, 0)); mUserHandle = info.getUserHandle(); mPackageName = info.getPackage(); mContactUri = getContactUri(info); mNotificationPolicyState = SHOW_CONVERSATIONS; } public Builder(ConversationChannel channel, LauncherApps launcherApps) { ShortcutInfo info = channel.getShortcutInfo(); mId = info.getId(); mUserName = info.getLabel(); mUserIcon = convertDrawableToIcon(launcherApps.getShortcutIconDrawable(info, 0)); mUserHandle = info.getUserHandle(); mPackageName = info.getPackage(); mContactUri = getContactUri(info); mStatuses = channel.getStatuses(); mLastInteractionTimestamp = channel.getLastEventTimestamp(); mIsImportantConversation = channel.getNotificationChannel() != null && channel.getNotificationChannel().isImportantConversation(); mCanBypassDnd = channel.getNotificationChannel() != null && channel.getNotificationChannel().canBypassDnd(); mNotificationPolicyState = SHOW_CONVERSATIONS; } /** Returns the Contact's Uri if present. */ public Uri getContactUri(ShortcutInfo info) { if (info.getPersons() == null || info.getPersons().length != 1) { return null; } // TODO(b/175584929): Update to use the Uri from PeopleService directly Person person = info.getPersons()[0]; return person.getUri() == null ? null : Uri.parse(person.getUri()); } /** Sets the ID for the tile. */ public Builder setId(String id) { mId = id; return this; } /** Sets the user name. */ public Builder setUserName(CharSequence userName) { mUserName = userName; return this; } /** Sets the icon shown for the user. */ public Builder setUserIcon(Icon userIcon) { mUserIcon = userIcon; return this; } /** Sets the Uri associated with the user in Android Contacts database. */ public Builder setContactUri(Uri uri) { mContactUri = uri; return this; } /** Sets the associated {@code userHandle}. */ public Builder setUserHandle(UserHandle userHandle) { mUserHandle = userHandle; return this; } /** Sets the package shown that provided the information. */ public Builder setPackageName(String packageName) { mPackageName = packageName; return this; } /** Sets the status text. */ public Builder setBirthdayText(String birthdayText) { mBirthdayText = birthdayText; return this; } /** Sets the last interaction timestamp. */ public Builder setLastInteractionTimestamp(long lastInteractionTimestamp) { mLastInteractionTimestamp = lastInteractionTimestamp; return this; } /** Sets whether the conversation is important. */ public Builder setIsImportantConversation(boolean isImportantConversation) { mIsImportantConversation = isImportantConversation; return this; } /** Sets the associated notification's key. */ public Builder setNotificationKey(String notificationKey) { mNotificationKey = notificationKey; return this; } /** Sets the associated notification's content. */ public Builder setNotificationContent(CharSequence notificationContent) { mNotificationContent = notificationContent; return this; } /** Sets the associated notification's sender. */ public Builder setNotificationSender(CharSequence notificationSender) { mNotificationSender = notificationSender; return this; } /** Sets the associated notification's category. */ public Builder setNotificationCategory(String notificationCategory) { mNotificationCategory = notificationCategory; return this; } /** Sets the associated notification's data URI. */ public Builder setNotificationDataUri(Uri notificationDataUri) { mNotificationDataUri = notificationDataUri; return this; } /** Sets the number of messages associated with the Tile. */ public Builder setMessagesCount(int messagesCount) { mMessagesCount = messagesCount; return this; } /** Sets an intent to launch on click. */ public Builder setIntent(Intent intent) { mIntent = intent; return this; } /** Sets the notification timestamp. */ public Builder setNotificationTimestamp(long notificationTimestamp) { mNotificationTimestamp = notificationTimestamp; return this; } /** Sets the statuses. */ public Builder setStatuses(List statuses) { mStatuses = statuses; return this; } /** Sets whether the conversation channel can bypass DND. */ public Builder setCanBypassDnd(boolean canBypassDnd) { mCanBypassDnd = canBypassDnd; return this; } /** Sets whether the package is suspended. */ public Builder setIsPackageSuspended(boolean isPackageSuspended) { mIsPackageSuspended = isPackageSuspended; return this; } /** Sets whether the user has been quieted. */ public Builder setIsUserQuieted(boolean isUserQuieted) { mIsUserQuieted = isUserQuieted; return this; } /** Sets the state of blocked notifications for the conversation. */ public Builder setNotificationPolicyState(int notificationPolicyState) { mNotificationPolicyState = notificationPolicyState; return this; } /** Sets the contact's affinity. */ public Builder setContactAffinity(float contactAffinity) { mContactAffinity = contactAffinity; return this; } /** Builds a {@link PeopleSpaceTile}. */ @NonNull public PeopleSpaceTile build() { return new PeopleSpaceTile(this); } } public PeopleSpaceTile(Parcel in) { mId = in.readString(); mUserName = in.readCharSequence(); mUserIcon = in.readParcelable(Icon.class.getClassLoader(), android.graphics.drawable.Icon.class); mContactUri = in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class); mUserHandle = in.readParcelable(UserHandle.class.getClassLoader(), android.os.UserHandle.class); mPackageName = in.readString(); mBirthdayText = in.readString(); mLastInteractionTimestamp = in.readLong(); mIsImportantConversation = in.readBoolean(); mNotificationKey = in.readString(); mNotificationContent = in.readCharSequence(); mNotificationSender = in.readCharSequence(); mNotificationCategory = in.readString(); mNotificationDataUri = in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class); mMessagesCount = in.readInt(); mIntent = in.readParcelable(Intent.class.getClassLoader(), android.content.Intent.class); mNotificationTimestamp = in.readLong(); mStatuses = new ArrayList<>(); in.readParcelableList(mStatuses, ConversationStatus.class.getClassLoader(), android.app.people.ConversationStatus.class); mCanBypassDnd = in.readBoolean(); mIsPackageSuspended = in.readBoolean(); mIsUserQuieted = in.readBoolean(); mNotificationPolicyState = in.readInt(); mContactAffinity = in.readFloat(); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(mId); dest.writeCharSequence(mUserName); dest.writeParcelable(mUserIcon, flags); dest.writeParcelable(mContactUri, flags); dest.writeParcelable(mUserHandle, flags); dest.writeString(mPackageName); dest.writeString(mBirthdayText); dest.writeLong(mLastInteractionTimestamp); dest.writeBoolean(mIsImportantConversation); dest.writeString(mNotificationKey); dest.writeCharSequence(mNotificationContent); dest.writeCharSequence(mNotificationSender); dest.writeString(mNotificationCategory); dest.writeParcelable(mNotificationDataUri, flags); dest.writeInt(mMessagesCount); dest.writeParcelable(mIntent, flags); dest.writeLong(mNotificationTimestamp); dest.writeParcelableList(mStatuses, flags); dest.writeBoolean(mCanBypassDnd); dest.writeBoolean(mIsPackageSuspended); dest.writeBoolean(mIsUserQuieted); dest.writeInt(mNotificationPolicyState); dest.writeFloat(mContactAffinity); } public static final @android.annotation.NonNull Creator CREATOR = new Creator() { public PeopleSpaceTile createFromParcel(Parcel source) { return new PeopleSpaceTile(source); } public PeopleSpaceTile[] newArray(int size) { return new PeopleSpaceTile[size]; } }; /** Converts {@code drawable} to a {@link Icon}. */ public static Icon convertDrawableToIcon(Drawable drawable) { if (drawable == null) { return null; } if (drawable instanceof BitmapDrawable) { BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; if (bitmapDrawable.getBitmap() != null) { return Icon.createWithBitmap(bitmapDrawable.getBitmap()); } } Bitmap bitmap; if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) { bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); // Single color bitmap will be created of 1x1 pixel } else { bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); } Canvas canvas = new Canvas(bitmap); drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); drawable.draw(canvas); return Icon.createWithBitmap(bitmap); } }