1 /* 2 * Copyright (C) 2018 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; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.graphics.drawable.Icon; 22 import android.net.Uri; 23 import android.os.Parcel; 24 import android.os.Parcelable; 25 26 import java.util.Objects; 27 import java.util.function.Consumer; 28 29 /** 30 * Provides an immutable reference to an entity that appears repeatedly on different surfaces of the 31 * platform. For example, this could represent the sender of a message. 32 */ 33 public final class Person implements Parcelable { 34 35 @Nullable private CharSequence mName; 36 @Nullable private Icon mIcon; 37 @Nullable private String mUri; 38 @Nullable private String mKey; 39 private boolean mIsBot; 40 private boolean mIsImportant; 41 Person(Parcel in)42 private Person(Parcel in) { 43 mName = in.readCharSequence(); 44 if (in.readInt() != 0) { 45 mIcon = Icon.CREATOR.createFromParcel(in); 46 } 47 mUri = in.readString(); 48 mKey = in.readString(); 49 mIsImportant = in.readBoolean(); 50 mIsBot = in.readBoolean(); 51 } 52 Person(Builder builder)53 private Person(Builder builder) { 54 mName = builder.mName; 55 mIcon = builder.mIcon; 56 mUri = builder.mUri; 57 mKey = builder.mKey; 58 mIsBot = builder.mIsBot; 59 mIsImportant = builder.mIsImportant; 60 } 61 62 /** Creates and returns a new {@link Builder} initialized with this Person's data. */ toBuilder()63 public Builder toBuilder() { 64 return new Builder(this); 65 } 66 67 /** 68 * @return the uri provided for this person or {@code null} if no Uri was provided. 69 */ 70 @Nullable getUri()71 public String getUri() { 72 return mUri; 73 } 74 75 /** 76 * @return the name provided for this person or {@code null} if no name was provided. 77 */ 78 @Nullable getName()79 public CharSequence getName() { 80 return mName; 81 } 82 83 /** 84 * @return the icon provided for this person or {@code null} if no icon was provided. 85 */ 86 @Nullable getIcon()87 public Icon getIcon() { 88 return mIcon; 89 } 90 91 /** 92 * @return the key provided for this person or {@code null} if no key was provided. 93 */ 94 @Nullable getKey()95 public String getKey() { 96 return mKey; 97 } 98 99 /** 100 * @return whether this Person is a machine. 101 */ isBot()102 public boolean isBot() { 103 return mIsBot; 104 } 105 106 /** 107 * @return whether this Person is important. 108 */ isImportant()109 public boolean isImportant() { 110 return mIsImportant; 111 } 112 113 /** 114 * @return the URI associated with this person, or "name:mName" otherwise 115 * @hide 116 */ resolveToLegacyUri()117 public String resolveToLegacyUri() { 118 if (mUri != null) { 119 return mUri; 120 } 121 if (mName != null) { 122 return "name:" + mName; 123 } 124 return ""; 125 } 126 127 /** 128 * @return the URI associated with the {@link #getIcon()} for this person, iff the icon exists 129 * and is URI based. 130 * @hide 131 */ 132 @Nullable getIconUri()133 public Uri getIconUri() { 134 if (mIcon != null && (mIcon.getType() == Icon.TYPE_URI 135 || mIcon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP)) { 136 return mIcon.getUri(); 137 } 138 return null; 139 } 140 141 @Override equals(@ullable Object obj)142 public boolean equals(@Nullable Object obj) { 143 if (obj instanceof Person) { 144 final Person other = (Person) obj; 145 return Objects.equals(mName, other.mName) 146 && (mIcon == null ? other.mIcon == null : 147 (other.mIcon != null && mIcon.sameAs(other.mIcon))) 148 && Objects.equals(mUri, other.mUri) 149 && Objects.equals(mKey, other.mKey) 150 && mIsBot == other.mIsBot 151 && mIsImportant == other.mIsImportant; 152 } 153 return false; 154 } 155 156 @Override hashCode()157 public int hashCode() { 158 return Objects.hash(mName, mIcon, mUri, mKey, mIsBot, mIsImportant); 159 } 160 161 @Override describeContents()162 public int describeContents() { 163 return 0; 164 } 165 166 @Override writeToParcel(Parcel dest, @WriteFlags int flags)167 public void writeToParcel(Parcel dest, @WriteFlags int flags) { 168 dest.writeCharSequence(mName); 169 if (mIcon != null) { 170 dest.writeInt(1); 171 mIcon.writeToParcel(dest, 0); 172 } else { 173 dest.writeInt(0); 174 } 175 dest.writeString(mUri); 176 dest.writeString(mKey); 177 dest.writeBoolean(mIsImportant); 178 dest.writeBoolean(mIsBot); 179 } 180 181 /** 182 * Note all {@link Uri} that are referenced internally, with the expectation that Uri permission 183 * grants will need to be issued to ensure the recipient of this object is able to render its 184 * contents. 185 * See b/281044385 for more context and examples about what happens when this isn't done 186 * correctly. 187 * 188 * @hide 189 */ visitUris(@onNull Consumer<Uri> visitor)190 public void visitUris(@NonNull Consumer<Uri> visitor) { 191 visitor.accept(getIconUri()); 192 } 193 194 /** Builder for the immutable {@link Person} class. */ 195 public static class Builder { 196 @Nullable private CharSequence mName; 197 @Nullable private Icon mIcon; 198 @Nullable private String mUri; 199 @Nullable private String mKey; 200 private boolean mIsBot; 201 private boolean mIsImportant; 202 203 /** Creates a new, empty {@link Builder}. */ Builder()204 public Builder() { 205 } 206 Builder(Person person)207 private Builder(Person person) { 208 mName = person.mName; 209 mIcon = person.mIcon; 210 mUri = person.mUri; 211 mKey = person.mKey; 212 mIsBot = person.mIsBot; 213 mIsImportant = person.mIsImportant; 214 } 215 216 /** 217 * Give this person a name. 218 * 219 * @param name the name of this person. 220 */ 221 @NonNull setName(@ullable CharSequence name)222 public Person.Builder setName(@Nullable CharSequence name) { 223 this.mName = name; 224 return this; 225 } 226 227 /** 228 * Add an icon for this person. 229 * <br /> 230 * The system will prefer this icon over any images that are resolved from the URI. 231 * 232 * @param icon the icon of the person. 233 */ 234 @NonNull setIcon(@ullable Icon icon)235 public Person.Builder setIcon(@Nullable Icon icon) { 236 this.mIcon = icon; 237 return this; 238 } 239 240 /** 241 * Set a URI associated with this person. 242 * 243 * <P> 244 * The person should be specified by the {@code String} representation of a 245 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}. 246 * </P> 247 * 248 * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema 249 * URIs. The path part of these URIs must exist in the contacts database, in the 250 * appropriate column, or the reference will be discarded as invalid. Telephone schema 251 * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}. 252 * </P> 253 * 254 * @param uri a URI for the person. 255 */ 256 @NonNull setUri(@ullable String uri)257 public Person.Builder setUri(@Nullable String uri) { 258 mUri = uri; 259 return this; 260 } 261 262 /** 263 * Add a key to this person in order to uniquely identify it. 264 * This is especially useful if the name doesn't uniquely identify this person or if the 265 * display name is a short handle of the actual name. 266 * 267 * <P>If no key is provided, the name serves as the key for the purpose of 268 * identification.</P> 269 * 270 * @param key the key that uniquely identifies this person. 271 */ 272 @NonNull setKey(@ullable String key)273 public Person.Builder setKey(@Nullable String key) { 274 mKey = key; 275 return this; 276 } 277 278 /** 279 * Sets whether this is an important person. Use this method to denote users who frequently 280 * interact with the user of this device when {@link #setUri(String)} isn't provided with 281 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}, and instead with 282 * the {@code mailto:} or {@code tel:} schemas. 283 * 284 * @param isImportant {@code true} if this is an important person, {@code false} otherwise. 285 */ 286 @NonNull setImportant(boolean isImportant)287 public Person.Builder setImportant(boolean isImportant) { 288 mIsImportant = isImportant; 289 return this; 290 } 291 292 /** 293 * Sets whether this person is a machine rather than a human. 294 * 295 * @param isBot {@code true} if this person is a machine, {@code false} otherwise. 296 */ 297 @NonNull setBot(boolean isBot)298 public Person.Builder setBot(boolean isBot) { 299 mIsBot = isBot; 300 return this; 301 } 302 303 /** Creates and returns the {@link Person} this builder represents. */ 304 @NonNull build()305 public Person build() { 306 return new Person(this); 307 } 308 } 309 310 public static final @android.annotation.NonNull Creator<Person> CREATOR = new Creator<Person>() { 311 @Override 312 public Person createFromParcel(Parcel in) { 313 return new Person(in); 314 } 315 316 @Override 317 public Person[] newArray(int size) { 318 return new Person[size]; 319 } 320 }; 321 } 322