/* * Copyright (C) 2018 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; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; import java.util.Objects; import java.util.function.Consumer; /** * Provides an immutable reference to an entity that appears repeatedly on different surfaces of the * platform. For example, this could represent the sender of a message. */ public final class Person implements Parcelable { @Nullable private CharSequence mName; @Nullable private Icon mIcon; @Nullable private String mUri; @Nullable private String mKey; private boolean mIsBot; private boolean mIsImportant; private Person(Parcel in) { mName = in.readCharSequence(); if (in.readInt() != 0) { mIcon = Icon.CREATOR.createFromParcel(in); } mUri = in.readString(); mKey = in.readString(); mIsImportant = in.readBoolean(); mIsBot = in.readBoolean(); } private Person(Builder builder) { mName = builder.mName; mIcon = builder.mIcon; mUri = builder.mUri; mKey = builder.mKey; mIsBot = builder.mIsBot; mIsImportant = builder.mIsImportant; } /** Creates and returns a new {@link Builder} initialized with this Person's data. */ public Builder toBuilder() { return new Builder(this); } /** * @return the uri provided for this person or {@code null} if no Uri was provided. */ @Nullable public String getUri() { return mUri; } /** * @return the name provided for this person or {@code null} if no name was provided. */ @Nullable public CharSequence getName() { return mName; } /** * @return the icon provided for this person or {@code null} if no icon was provided. */ @Nullable public Icon getIcon() { return mIcon; } /** * @return the key provided for this person or {@code null} if no key was provided. */ @Nullable public String getKey() { return mKey; } /** * @return whether this Person is a machine. */ public boolean isBot() { return mIsBot; } /** * @return whether this Person is important. */ public boolean isImportant() { return mIsImportant; } /** * @return the URI associated with this person, or "name:mName" otherwise * @hide */ public String resolveToLegacyUri() { if (mUri != null) { return mUri; } if (mName != null) { return "name:" + mName; } return ""; } /** * @return the URI associated with the {@link #getIcon()} for this person, iff the icon exists * and is URI based. * @hide */ @Nullable public Uri getIconUri() { if (mIcon != null && (mIcon.getType() == Icon.TYPE_URI || mIcon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP)) { return mIcon.getUri(); } return null; } @Override public boolean equals(@Nullable Object obj) { if (obj instanceof Person) { final Person other = (Person) obj; return Objects.equals(mName, other.mName) && (mIcon == null ? other.mIcon == null : (other.mIcon != null && mIcon.sameAs(other.mIcon))) && Objects.equals(mUri, other.mUri) && Objects.equals(mKey, other.mKey) && mIsBot == other.mIsBot && mIsImportant == other.mIsImportant; } return false; } @Override public int hashCode() { return Objects.hash(mName, mIcon, mUri, mKey, mIsBot, mIsImportant); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, @WriteFlags int flags) { dest.writeCharSequence(mName); if (mIcon != null) { dest.writeInt(1); mIcon.writeToParcel(dest, 0); } else { dest.writeInt(0); } dest.writeString(mUri); dest.writeString(mKey); dest.writeBoolean(mIsImportant); dest.writeBoolean(mIsBot); } /** * Note all {@link Uri} that are referenced internally, with the expectation that Uri permission * grants will need to be issued to ensure the recipient of this object is able to render its * contents. * See b/281044385 for more context and examples about what happens when this isn't done * correctly. * * @hide */ public void visitUris(@NonNull Consumer visitor) { visitor.accept(getIconUri()); if (Flags.visitPersonUri()) { if (mUri != null && !mUri.isEmpty()) { visitor.accept(Uri.parse(mUri)); } } } /** Builder for the immutable {@link Person} class. */ public static class Builder { @Nullable private CharSequence mName; @Nullable private Icon mIcon; @Nullable private String mUri; @Nullable private String mKey; private boolean mIsBot; private boolean mIsImportant; /** Creates a new, empty {@link Builder}. */ public Builder() { } private Builder(Person person) { mName = person.mName; mIcon = person.mIcon; mUri = person.mUri; mKey = person.mKey; mIsBot = person.mIsBot; mIsImportant = person.mIsImportant; } /** * Give this person a name. * * @param name the name of this person. */ @NonNull public Person.Builder setName(@Nullable CharSequence name) { this.mName = name; return this; } /** * Add an icon for this person. *
* The system will prefer this icon over any images that are resolved from the URI. * * @param icon the icon of the person. */ @NonNull public Person.Builder setIcon(@Nullable Icon icon) { this.mIcon = icon; return this; } /** * Set a URI associated with this person. * *

* The person should be specified by the {@code String} representation of a * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}. *

* *

The system will also attempt to resolve {@code mailto:} and {@code tel:} schema * URIs. The path part of these URIs must exist in the contacts database, in the * appropriate column, or the reference will be discarded as invalid. Telephone schema * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}. *

* * @param uri a URI for the person. */ @NonNull public Person.Builder setUri(@Nullable String uri) { mUri = uri; return this; } /** * Add a key to this person in order to uniquely identify it. * This is especially useful if the name doesn't uniquely identify this person or if the * display name is a short handle of the actual name. * *

If no key is provided, the name serves as the key for the purpose of * identification.

* * @param key the key that uniquely identifies this person. */ @NonNull public Person.Builder setKey(@Nullable String key) { mKey = key; return this; } /** * Sets whether this is an important person. Use this method to denote users who frequently * interact with the user of this device when {@link #setUri(String)} isn't provided with * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}, and instead with * the {@code mailto:} or {@code tel:} schemas. * * @param isImportant {@code true} if this is an important person, {@code false} otherwise. */ @NonNull public Person.Builder setImportant(boolean isImportant) { mIsImportant = isImportant; return this; } /** * Sets whether this person is a machine rather than a human. * * @param isBot {@code true} if this person is a machine, {@code false} otherwise. */ @NonNull public Person.Builder setBot(boolean isBot) { mIsBot = isBot; return this; } /** Creates and returns the {@link Person} this builder represents. */ @NonNull public Person build() { return new Person(this); } } public static final @android.annotation.NonNull Creator CREATOR = new Creator() { @Override public Person createFromParcel(Parcel in) { return new Person(in); } @Override public Person[] newArray(int size) { return new Person[size]; } }; }