• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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 com.android.ex.chips;
18 
19 import android.net.Uri;
20 import android.provider.ContactsContract.CommonDataKinds.Email;
21 import android.provider.ContactsContract.DisplayNameSources;
22 import android.support.annotation.DrawableRes;
23 import android.text.util.Rfc822Token;
24 import android.text.util.Rfc822Tokenizer;
25 
26 /**
27  * Represents one entry inside recipient auto-complete list.
28  */
29 public class RecipientEntry {
30     /* package */ static final int INVALID_CONTACT = -1;
31     /**
32      * A GENERATED_CONTACT is one that was created based entirely on
33      * information passed in to the RecipientEntry from an external source
34      * that is not a real contact.
35      */
36     /* package */ static final int GENERATED_CONTACT = -2;
37 
38     /** Used when {@link #mDestinationType} is invalid and thus shouldn't be used for display. */
39     public static final int INVALID_DESTINATION_TYPE = -1;
40 
41     public static final int ENTRY_TYPE_PERSON = 0;
42 
43     /**
44      * Entry of this type represents the item in auto-complete that asks user to grant permissions
45      * to the app. This permission model is introduced in M platform.
46      *
47      * <p>Entries of this type should have {@link #mPermissions} set as well.
48      */
49     public static final int ENTRY_TYPE_PERMISSION_REQUEST = 1;
50 
51     public static final int ENTRY_TYPE_SIZE = 2;
52 
53     private final int mEntryType;
54 
55     /**
56      * True when this entry is the first entry in a group, which should have a photo and display
57      * name, while the second or later entries won't.
58      */
59     private boolean mIsFirstLevel;
60     private final String mDisplayName;
61 
62     /** Destination for this contact entry. Would be an email address or a phone number. */
63     private final String mDestination;
64     /** Type of the destination like {@link Email#TYPE_HOME} */
65     private final int mDestinationType;
66     /**
67      * Label of the destination which will be used when type was {@link Email#TYPE_CUSTOM}.
68      * Can be null when {@link #mDestinationType} is {@link #INVALID_DESTINATION_TYPE}.
69      */
70     private final String mDestinationLabel;
71     /** ID for the person */
72     private final long mContactId;
73     /** ID for the directory this contact came from, or <code>null</code> */
74     private final Long mDirectoryId;
75     /** ID for the destination */
76     private final long mDataId;
77 
78     private final Uri mPhotoThumbnailUri;
79     /** Configures showing the icon in the chip */
80     private final boolean mShouldDisplayIcon;
81 
82     private boolean mIsValid;
83     /**
84      * This can be updated after this object being constructed, when the photo is fetched
85      * from remote directories.
86      */
87     private byte[] mPhotoBytes;
88 
89     @DrawableRes private int mIndicatorIconId;
90     private String mIndicatorText;
91 
92     /** See {@link android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} */
93     private final String mLookupKey;
94 
95     /** Should be used when type is {@link #ENTRY_TYPE_PERMISSION_REQUEST}. */
96     private final String[] mPermissions;
97 
RecipientEntry(int entryType, String displayName, String destination, int destinationType, String destinationLabel, long contactId, Long directoryId, long dataId, Uri photoThumbnailUri, boolean isFirstLevel, boolean isValid, String lookupKey, String[] permissions)98     protected RecipientEntry(int entryType, String displayName, String destination,
99         int destinationType, String destinationLabel, long contactId, Long directoryId,
100         long dataId, Uri photoThumbnailUri, boolean isFirstLevel, boolean isValid,
101         String lookupKey, String[] permissions) {
102         this(entryType, displayName, destination, destinationType,
103             destinationLabel, contactId, directoryId, dataId, photoThumbnailUri,
104             true /* shouldDisplayIcon */, isFirstLevel, isValid, lookupKey, permissions);
105     }
106 
RecipientEntry(int entryType, String displayName, String destination, int destinationType, String destinationLabel, long contactId, Long directoryId, long dataId, Uri photoThumbnailUri, boolean shouldDisplayIcon, boolean isFirstLevel, boolean isValid, String lookupKey, String[] permissions)107     protected RecipientEntry(int entryType, String displayName, String destination,
108             int destinationType, String destinationLabel, long contactId, Long directoryId,
109             long dataId, Uri photoThumbnailUri, boolean shouldDisplayIcon,
110             boolean isFirstLevel, boolean isValid, String lookupKey, String[] permissions) {
111         mEntryType = entryType;
112         mIsFirstLevel = isFirstLevel;
113         mDisplayName = displayName;
114         mDestination = destination;
115         mDestinationType = destinationType;
116         mDestinationLabel = destinationLabel;
117         mContactId = contactId;
118         mDirectoryId = directoryId;
119         mDataId = dataId;
120         mPhotoThumbnailUri = photoThumbnailUri;
121         mShouldDisplayIcon = shouldDisplayIcon;
122         mPhotoBytes = null;
123         mIsValid = isValid;
124         mLookupKey = lookupKey;
125         mIndicatorIconId = 0;
126         mIndicatorText = null;
127         mPermissions = permissions;
128     }
129 
RecipientEntry(int entryType, String displayName, String destination, int destinationType, String destinationLabel, long contactId, Long directoryId, long dataId, Uri photoThumbnailUri, boolean isFirstLevel, boolean isValid, String lookupKey)130     protected RecipientEntry(int entryType, String displayName, String destination,
131             int destinationType, String destinationLabel, long contactId, Long directoryId,
132             long dataId, Uri photoThumbnailUri, boolean isFirstLevel, boolean isValid,
133             String lookupKey) {
134         this(entryType, displayName, destination, destinationType, destinationLabel,
135                 contactId, directoryId, dataId, photoThumbnailUri, isFirstLevel, isValid,
136                 lookupKey, null);
137     }
138 
isValid()139     public boolean isValid() {
140         return mIsValid;
141     }
142 
143     /**
144      * Determine if this was a RecipientEntry created from recipient info or
145      * an entry from contacts.
146      */
isCreatedRecipient(long id)147     public static boolean isCreatedRecipient(long id) {
148         return id == RecipientEntry.INVALID_CONTACT || id == RecipientEntry.GENERATED_CONTACT;
149     }
150 
151     /**
152      * Construct a RecipientEntry from just an address that has been entered.
153      * This address has not been resolved to a contact and therefore does not
154      * have a contact id or photo.
155      */
constructFakeEntry(final String address, final boolean isValid)156     public static RecipientEntry constructFakeEntry(final String address, final boolean isValid) {
157         final Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(address);
158         final String tokenizedAddress = tokens.length > 0 ? tokens[0].getAddress() : address;
159 
160         return new RecipientEntry(ENTRY_TYPE_PERSON, tokenizedAddress, tokenizedAddress,
161                 INVALID_DESTINATION_TYPE, null, INVALID_CONTACT, null /* directoryId */,
162                 INVALID_CONTACT, null, true, isValid, null /* lookupKey */, null /* permissions */);
163     }
164 
165     /**
166      * Construct a RecipientEntry from just a phone number.
167      */
constructFakePhoneEntry(final String phoneNumber, final boolean isValid)168     public static RecipientEntry constructFakePhoneEntry(final String phoneNumber,
169             final boolean isValid) {
170         return new RecipientEntry(ENTRY_TYPE_PERSON, phoneNumber, phoneNumber,
171                 INVALID_DESTINATION_TYPE, null, INVALID_CONTACT, null /* directoryId */,
172                 INVALID_CONTACT, null, true, isValid, null /* lookupKey */, null /* permissions */);
173     }
174 
175     /**
176      * Construct a RecipientEntry from just an address that has been entered
177      * with both an associated display name. This address has not been resolved
178      * to a contact and therefore does not have a contact id or photo.
179      */
constructGeneratedEntry(String display, String address, boolean isValid)180     public static RecipientEntry constructGeneratedEntry(String display, String address,
181             boolean isValid) {
182         return new RecipientEntry(ENTRY_TYPE_PERSON, display, address, INVALID_DESTINATION_TYPE,
183                 null, GENERATED_CONTACT, null /* directoryId */, GENERATED_CONTACT, null, true,
184                 isValid, null /* lookupKey */, null /* permissions */);
185     }
186 
constructTopLevelEntry(String displayName, int displayNameSource, String destination, int destinationType, String destinationLabel, long contactId, Long directoryId, long dataId, Uri photoThumbnailUri, boolean isValid, String lookupKey)187     public static RecipientEntry constructTopLevelEntry(String displayName, int displayNameSource,
188             String destination, int destinationType, String destinationLabel, long contactId,
189             Long directoryId, long dataId, Uri photoThumbnailUri, boolean isValid,
190             String lookupKey) {
191         return new RecipientEntry(ENTRY_TYPE_PERSON, pickDisplayName(displayNameSource,
192                 displayName, destination), destination, destinationType, destinationLabel,
193                 contactId, directoryId, dataId, photoThumbnailUri, true, isValid, lookupKey,
194                 null /* permissions */);
195     }
196 
constructTopLevelEntry(String displayName, int displayNameSource, String destination, int destinationType, String destinationLabel, long contactId, Long directoryId, long dataId, String thumbnailUriAsString, boolean isValid, String lookupKey)197     public static RecipientEntry constructTopLevelEntry(String displayName, int displayNameSource,
198             String destination, int destinationType, String destinationLabel, long contactId,
199             Long directoryId, long dataId, String thumbnailUriAsString, boolean isValid,
200             String lookupKey) {
201         return new RecipientEntry(ENTRY_TYPE_PERSON, pickDisplayName(displayNameSource,
202                 displayName, destination), destination, destinationType, destinationLabel,
203                 contactId, directoryId, dataId, (thumbnailUriAsString != null
204                 ? Uri.parse(thumbnailUriAsString) : null), true, isValid, lookupKey,
205                 null /* permissions */);
206     }
207 
constructSecondLevelEntry(String displayName, int displayNameSource, String destination, int destinationType, String destinationLabel, long contactId, Long directoryId, long dataId, String thumbnailUriAsString, boolean isValid, String lookupKey)208     public static RecipientEntry constructSecondLevelEntry(String displayName,
209             int displayNameSource, String destination, int destinationType,
210             String destinationLabel, long contactId, Long directoryId, long dataId,
211             String thumbnailUriAsString, boolean isValid, String lookupKey) {
212         return new RecipientEntry(ENTRY_TYPE_PERSON, pickDisplayName(displayNameSource,
213                 displayName, destination), destination, destinationType, destinationLabel,
214                 contactId, directoryId, dataId, (thumbnailUriAsString != null
215                 ? Uri.parse(thumbnailUriAsString) : null), false, isValid, lookupKey,
216                 null /* permissions */);
217     }
218 
constructPermissionEntry(String[] permissions)219     public static RecipientEntry constructPermissionEntry(String[] permissions) {
220         return new RecipientEntry(
221                 ENTRY_TYPE_PERMISSION_REQUEST,
222                 "" /* displayName */,
223                 "" /* destination */,
224                 Email.TYPE_CUSTOM,
225                 "" /* destinationLabel */,
226                 INVALID_CONTACT,
227                 null /* directoryId */,
228                 INVALID_CONTACT,
229                 null /* photoThumbnailUri */,
230                 true /* isFirstLevel*/,
231                 false /* isValid */,
232                 null /* lookupKey */,
233                 permissions);
234     }
235 
236     /**
237      * @return the display name for the entry.  If the display name source is larger than
238      * {@link DisplayNameSources#PHONE} we use the contact's display name, but if not,
239      * i.e. the display name came from an email address or a phone number, we don't use it
240      * to avoid confusion and just use the destination instead.
241      */
pickDisplayName(int displayNameSource, String displayName, String destination)242     private static String pickDisplayName(int displayNameSource, String displayName,
243             String destination) {
244         return (displayNameSource > DisplayNameSources.PHONE) ? displayName : destination;
245     }
246 
getEntryType()247     public int getEntryType() {
248         return mEntryType;
249     }
250 
getDisplayName()251     public String getDisplayName() {
252         return mDisplayName;
253     }
254 
getDestination()255     public String getDestination() {
256         return mDestination;
257     }
258 
getDestinationType()259     public int getDestinationType() {
260         return mDestinationType;
261     }
262 
getDestinationLabel()263     public String getDestinationLabel() {
264         return mDestinationLabel;
265     }
266 
getContactId()267     public long getContactId() {
268         return mContactId;
269     }
270 
getDirectoryId()271     public Long getDirectoryId() {
272         return mDirectoryId;
273     }
274 
getDataId()275     public long getDataId() {
276         return mDataId;
277     }
278 
isFirstLevel()279     public boolean isFirstLevel() {
280         return mIsFirstLevel;
281     }
282 
getPhotoThumbnailUri()283     public Uri getPhotoThumbnailUri() {
284         return mPhotoThumbnailUri;
285     }
286 
287     /** Indicates whether the icon in the chip is displayed or not. */
shouldDisplayIcon()288     public boolean shouldDisplayIcon() {
289         return mShouldDisplayIcon;
290     }
291 
292     /** This can be called outside main Looper thread. */
setPhotoBytes(byte[] photoBytes)293     public synchronized void setPhotoBytes(byte[] photoBytes) {
294         mPhotoBytes = photoBytes;
295     }
296 
297     /** This can be called outside main Looper thread. */
getPhotoBytes()298     public synchronized byte[] getPhotoBytes() {
299         return mPhotoBytes;
300     }
301 
302     /**
303      * Used together with {@link #ENTRY_TYPE_PERMISSION_REQUEST} and indicates what permissions we
304      * need to ask user to grant.
305      */
getPermissions()306     public String[] getPermissions() {
307         return mPermissions;
308     }
309 
getLookupKey()310     public String getLookupKey() {
311         return mLookupKey;
312     }
313 
isSelectable()314     public boolean isSelectable() {
315         return mEntryType == ENTRY_TYPE_PERSON || mEntryType == ENTRY_TYPE_PERMISSION_REQUEST;
316     }
317 
318     @Override
toString()319     public String toString() {
320         return mDisplayName + " <" + mDestination + ">, isValid=" + mIsValid;
321     }
322 
323     /**
324      * Returns if entry represents the same person as this instance. The default implementation
325      * checks whether the contact ids are the same, and subclasses may opt to override this.
326      */
isSamePerson(final RecipientEntry entry)327     public boolean isSamePerson(final RecipientEntry entry) {
328         return entry != null && mContactId == entry.mContactId;
329     }
330 
331     /**
332      * Returns the resource ID for the indicator icon, or 0 if no icon should be displayed.
333      */
334     @DrawableRes
getIndicatorIconId()335     public int getIndicatorIconId() {
336         return mIndicatorIconId;
337     }
338 
339     /**
340      * Sets the indicator icon to the given resource ID.  Set to 0 to display no icon.
341      */
setIndicatorIconId(@rawableRes int indicatorIconId)342     public void setIndicatorIconId(@DrawableRes int indicatorIconId) {
343         mIndicatorIconId = indicatorIconId;
344     }
345 
346     /**
347      * Get the indicator text, or null if no text should be displayed.
348      */
getIndicatorText()349     public String getIndicatorText() {
350         return mIndicatorText;
351     }
352 
353     /**
354      * Set the indicator text.  Set to null for no text to be displayed.
355      */
setIndicatorText(String indicatorText)356     public void setIndicatorText(String indicatorText) {
357         mIndicatorText = indicatorText;
358     }
359 }
360