• 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 
80     private boolean mIsValid;
81     /**
82      * This can be updated after this object being constructed, when the photo is fetched
83      * from remote directories.
84      */
85     private byte[] mPhotoBytes;
86 
87     @DrawableRes private int mIndicatorIconId;
88     private String mIndicatorText;
89 
90     /** See {@link android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} */
91     private final String mLookupKey;
92 
93     /** Should be used when type is {@link #ENTRY_TYPE_PERMISSION_REQUEST}. */
94     private final String[] mPermissions;
95 
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)96     protected RecipientEntry(int entryType, String displayName, String destination,
97             int destinationType, String destinationLabel, long contactId, Long directoryId,
98             long dataId, Uri photoThumbnailUri, boolean isFirstLevel, boolean isValid,
99             String lookupKey, String[] permissions) {
100         mEntryType = entryType;
101         mIsFirstLevel = isFirstLevel;
102         mDisplayName = displayName;
103         mDestination = destination;
104         mDestinationType = destinationType;
105         mDestinationLabel = destinationLabel;
106         mContactId = contactId;
107         mDirectoryId = directoryId;
108         mDataId = dataId;
109         mPhotoThumbnailUri = photoThumbnailUri;
110         mPhotoBytes = null;
111         mIsValid = isValid;
112         mLookupKey = lookupKey;
113         mIndicatorIconId = 0;
114         mIndicatorText = null;
115         mPermissions = permissions;
116     }
117 
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)118     protected RecipientEntry(int entryType, String displayName, String destination,
119             int destinationType, String destinationLabel, long contactId, Long directoryId,
120             long dataId, Uri photoThumbnailUri, boolean isFirstLevel, boolean isValid,
121             String lookupKey) {
122         this(entryType, displayName, destination, destinationType, destinationLabel,
123                 contactId, directoryId, dataId, photoThumbnailUri, isFirstLevel, isValid,
124                 lookupKey, null);
125     }
126 
isValid()127     public boolean isValid() {
128         return mIsValid;
129     }
130 
131     /**
132      * Determine if this was a RecipientEntry created from recipient info or
133      * an entry from contacts.
134      */
isCreatedRecipient(long id)135     public static boolean isCreatedRecipient(long id) {
136         return id == RecipientEntry.INVALID_CONTACT || id == RecipientEntry.GENERATED_CONTACT;
137     }
138 
139     /**
140      * Construct a RecipientEntry from just an address that has been entered.
141      * This address has not been resolved to a contact and therefore does not
142      * have a contact id or photo.
143      */
constructFakeEntry(final String address, final boolean isValid)144     public static RecipientEntry constructFakeEntry(final String address, final boolean isValid) {
145         final Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(address);
146         final String tokenizedAddress = tokens.length > 0 ? tokens[0].getAddress() : address;
147 
148         return new RecipientEntry(ENTRY_TYPE_PERSON, tokenizedAddress, tokenizedAddress,
149                 INVALID_DESTINATION_TYPE, null, INVALID_CONTACT, null /* directoryId */,
150                 INVALID_CONTACT, null, true, isValid, null /* lookupKey */, null /* permissions */);
151     }
152 
153     /**
154      * Construct a RecipientEntry from just a phone number.
155      */
constructFakePhoneEntry(final String phoneNumber, final boolean isValid)156     public static RecipientEntry constructFakePhoneEntry(final String phoneNumber,
157             final boolean isValid) {
158         return new RecipientEntry(ENTRY_TYPE_PERSON, phoneNumber, phoneNumber,
159                 INVALID_DESTINATION_TYPE, null, INVALID_CONTACT, null /* directoryId */,
160                 INVALID_CONTACT, null, true, isValid, null /* lookupKey */, null /* permissions */);
161     }
162 
163     /**
164      * Construct a RecipientEntry from just an address that has been entered
165      * with both an associated display name. This address has not been resolved
166      * to a contact and therefore does not have a contact id or photo.
167      */
constructGeneratedEntry(String display, String address, boolean isValid)168     public static RecipientEntry constructGeneratedEntry(String display, String address,
169             boolean isValid) {
170         return new RecipientEntry(ENTRY_TYPE_PERSON, display, address, INVALID_DESTINATION_TYPE,
171                 null, GENERATED_CONTACT, null /* directoryId */, GENERATED_CONTACT, null, true,
172                 isValid, null /* lookupKey */, null /* permissions */);
173     }
174 
constructTopLevelEntry(String displayName, int displayNameSource, String destination, int destinationType, String destinationLabel, long contactId, Long directoryId, long dataId, Uri photoThumbnailUri, boolean isValid, String lookupKey)175     public static RecipientEntry constructTopLevelEntry(String displayName, int displayNameSource,
176             String destination, int destinationType, String destinationLabel, long contactId,
177             Long directoryId, long dataId, Uri photoThumbnailUri, boolean isValid,
178             String lookupKey) {
179         return new RecipientEntry(ENTRY_TYPE_PERSON, pickDisplayName(displayNameSource,
180                 displayName, destination), destination, destinationType, destinationLabel,
181                 contactId, directoryId, dataId, photoThumbnailUri, true, isValid, lookupKey,
182                 null /* permissions */);
183     }
184 
constructTopLevelEntry(String displayName, int displayNameSource, String destination, int destinationType, String destinationLabel, long contactId, Long directoryId, long dataId, String thumbnailUriAsString, boolean isValid, String lookupKey)185     public static RecipientEntry constructTopLevelEntry(String displayName, int displayNameSource,
186             String destination, int destinationType, String destinationLabel, long contactId,
187             Long directoryId, long dataId, String thumbnailUriAsString, boolean isValid,
188             String lookupKey) {
189         return new RecipientEntry(ENTRY_TYPE_PERSON, pickDisplayName(displayNameSource,
190                 displayName, destination), destination, destinationType, destinationLabel,
191                 contactId, directoryId, dataId, (thumbnailUriAsString != null
192                 ? Uri.parse(thumbnailUriAsString) : null), true, isValid, lookupKey,
193                 null /* permissions */);
194     }
195 
constructSecondLevelEntry(String displayName, int displayNameSource, String destination, int destinationType, String destinationLabel, long contactId, Long directoryId, long dataId, String thumbnailUriAsString, boolean isValid, String lookupKey)196     public static RecipientEntry constructSecondLevelEntry(String displayName,
197             int displayNameSource, String destination, int destinationType,
198             String destinationLabel, long contactId, Long directoryId, long dataId,
199             String thumbnailUriAsString, boolean isValid, String lookupKey) {
200         return new RecipientEntry(ENTRY_TYPE_PERSON, pickDisplayName(displayNameSource,
201                 displayName, destination), destination, destinationType, destinationLabel,
202                 contactId, directoryId, dataId, (thumbnailUriAsString != null
203                 ? Uri.parse(thumbnailUriAsString) : null), false, isValid, lookupKey,
204                 null /* permissions */);
205     }
206 
constructPermissionEntry(String[] permissions)207     public static RecipientEntry constructPermissionEntry(String[] permissions) {
208         return new RecipientEntry(
209                 ENTRY_TYPE_PERMISSION_REQUEST,
210                 "" /* displayName */,
211                 "" /* destination */,
212                 Email.TYPE_CUSTOM,
213                 "" /* destinationLabel */,
214                 INVALID_CONTACT,
215                 null /* directoryId */,
216                 INVALID_CONTACT,
217                 null /* photoThumbnailUri */,
218                 true /* isFirstLevel*/,
219                 false /* isValid */,
220                 null /* lookupKey */,
221                 permissions);
222     }
223 
224     /**
225      * @return the display name for the entry.  If the display name source is larger than
226      * {@link DisplayNameSources#PHONE} we use the contact's display name, but if not,
227      * i.e. the display name came from an email address or a phone number, we don't use it
228      * to avoid confusion and just use the destination instead.
229      */
pickDisplayName(int displayNameSource, String displayName, String destination)230     private static String pickDisplayName(int displayNameSource, String displayName,
231             String destination) {
232         return (displayNameSource > DisplayNameSources.PHONE) ? displayName : destination;
233     }
234 
getEntryType()235     public int getEntryType() {
236         return mEntryType;
237     }
238 
getDisplayName()239     public String getDisplayName() {
240         return mDisplayName;
241     }
242 
getDestination()243     public String getDestination() {
244         return mDestination;
245     }
246 
getDestinationType()247     public int getDestinationType() {
248         return mDestinationType;
249     }
250 
getDestinationLabel()251     public String getDestinationLabel() {
252         return mDestinationLabel;
253     }
254 
getContactId()255     public long getContactId() {
256         return mContactId;
257     }
258 
getDirectoryId()259     public Long getDirectoryId() {
260         return mDirectoryId;
261     }
262 
getDataId()263     public long getDataId() {
264         return mDataId;
265     }
266 
isFirstLevel()267     public boolean isFirstLevel() {
268         return mIsFirstLevel;
269     }
270 
getPhotoThumbnailUri()271     public Uri getPhotoThumbnailUri() {
272         return mPhotoThumbnailUri;
273     }
274 
275     /** This can be called outside main Looper thread. */
setPhotoBytes(byte[] photoBytes)276     public synchronized void setPhotoBytes(byte[] photoBytes) {
277         mPhotoBytes = photoBytes;
278     }
279 
280     /** This can be called outside main Looper thread. */
getPhotoBytes()281     public synchronized byte[] getPhotoBytes() {
282         return mPhotoBytes;
283     }
284 
285     /**
286      * Used together with {@link #ENTRY_TYPE_PERMISSION_REQUEST} and indicates what permissions we
287      * need to ask user to grant.
288      */
getPermissions()289     public String[] getPermissions() {
290         return mPermissions;
291     }
292 
getLookupKey()293     public String getLookupKey() {
294         return mLookupKey;
295     }
296 
isSelectable()297     public boolean isSelectable() {
298         return mEntryType == ENTRY_TYPE_PERSON || mEntryType == ENTRY_TYPE_PERMISSION_REQUEST;
299     }
300 
301     @Override
toString()302     public String toString() {
303         return mDisplayName + " <" + mDestination + ">, isValid=" + mIsValid;
304     }
305 
306     /**
307      * Returns if entry represents the same person as this instance. The default implementation
308      * checks whether the contact ids are the same, and subclasses may opt to override this.
309      */
isSamePerson(final RecipientEntry entry)310     public boolean isSamePerson(final RecipientEntry entry) {
311         return entry != null && mContactId == entry.mContactId;
312     }
313 
314     /**
315      * Returns the resource ID for the indicator icon, or 0 if no icon should be displayed.
316      */
317     @DrawableRes
getIndicatorIconId()318     public int getIndicatorIconId() {
319         return mIndicatorIconId;
320     }
321 
322     /**
323      * Sets the indicator icon to the given resource ID.  Set to 0 to display no icon.
324      */
setIndicatorIconId(@rawableRes int indicatorIconId)325     public void setIndicatorIconId(@DrawableRes int indicatorIconId) {
326         mIndicatorIconId = indicatorIconId;
327     }
328 
329     /**
330      * Get the indicator text, or null if no text should be displayed.
331      */
getIndicatorText()332     public String getIndicatorText() {
333         return mIndicatorText;
334     }
335 
336     /**
337      * Set the indicator text.  Set to null for no text to be displayed.
338      */
setIndicatorText(String indicatorText)339     public void setIndicatorText(String indicatorText) {
340         mIndicatorText = indicatorText;
341     }
342 }
343