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