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 if (Flags.visitPersonUri()) { 193 if (mUri != null && !mUri.isEmpty()) { 194 visitor.accept(Uri.parse(mUri)); 195 } 196 } 197 } 198 199 /** Builder for the immutable {@link Person} class. */ 200 public static class Builder { 201 @Nullable private CharSequence mName; 202 @Nullable private Icon mIcon; 203 @Nullable private String mUri; 204 @Nullable private String mKey; 205 private boolean mIsBot; 206 private boolean mIsImportant; 207 208 /** Creates a new, empty {@link Builder}. */ Builder()209 public Builder() { 210 } 211 Builder(Person person)212 private Builder(Person person) { 213 mName = person.mName; 214 mIcon = person.mIcon; 215 mUri = person.mUri; 216 mKey = person.mKey; 217 mIsBot = person.mIsBot; 218 mIsImportant = person.mIsImportant; 219 } 220 221 /** 222 * Give this person a name. 223 * 224 * @param name the name of this person. 225 */ 226 @NonNull setName(@ullable CharSequence name)227 public Person.Builder setName(@Nullable CharSequence name) { 228 this.mName = name; 229 return this; 230 } 231 232 /** 233 * Add an icon for this person. 234 * <br /> 235 * The system will prefer this icon over any images that are resolved from the URI. 236 * 237 * @param icon the icon of the person. 238 */ 239 @NonNull setIcon(@ullable Icon icon)240 public Person.Builder setIcon(@Nullable Icon icon) { 241 this.mIcon = icon; 242 return this; 243 } 244 245 /** 246 * Set a URI associated with this person. 247 * 248 * <P> 249 * The person should be specified by the {@code String} representation of a 250 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}. 251 * </P> 252 * 253 * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema 254 * URIs. The path part of these URIs must exist in the contacts database, in the 255 * appropriate column, or the reference will be discarded as invalid. Telephone schema 256 * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}. 257 * </P> 258 * 259 * @param uri a URI for the person. 260 */ 261 @NonNull setUri(@ullable String uri)262 public Person.Builder setUri(@Nullable String uri) { 263 mUri = uri; 264 return this; 265 } 266 267 /** 268 * Add a key to this person in order to uniquely identify it. 269 * This is especially useful if the name doesn't uniquely identify this person or if the 270 * display name is a short handle of the actual name. 271 * 272 * <P>If no key is provided, the name serves as the key for the purpose of 273 * identification.</P> 274 * 275 * @param key the key that uniquely identifies this person. 276 */ 277 @NonNull setKey(@ullable String key)278 public Person.Builder setKey(@Nullable String key) { 279 mKey = key; 280 return this; 281 } 282 283 /** 284 * Sets whether this is an important person. Use this method to denote users who frequently 285 * interact with the user of this device when {@link #setUri(String)} isn't provided with 286 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}, and instead with 287 * the {@code mailto:} or {@code tel:} schemas. 288 * 289 * @param isImportant {@code true} if this is an important person, {@code false} otherwise. 290 */ 291 @NonNull setImportant(boolean isImportant)292 public Person.Builder setImportant(boolean isImportant) { 293 mIsImportant = isImportant; 294 return this; 295 } 296 297 /** 298 * Sets whether this person is a machine rather than a human. 299 * 300 * @param isBot {@code true} if this person is a machine, {@code false} otherwise. 301 */ 302 @NonNull setBot(boolean isBot)303 public Person.Builder setBot(boolean isBot) { 304 mIsBot = isBot; 305 return this; 306 } 307 308 /** Creates and returns the {@link Person} this builder represents. */ 309 @NonNull build()310 public Person build() { 311 return new Person(this); 312 } 313 } 314 315 public static final @android.annotation.NonNull Creator<Person> CREATOR = new Creator<Person>() { 316 @Override 317 public Person createFromParcel(Parcel in) { 318 return new Person(in); 319 } 320 321 @Override 322 public Person[] newArray(int size) { 323 return new Person[size]; 324 } 325 }; 326 } 327