Home | History | Annotate | Download | only in chips
      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 
     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 
    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 
    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 
    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      */
    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      */
    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      */
    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      */
    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 
    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 
    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 
    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 
    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      */
    242     private static String pickDisplayName(int displayNameSource, String displayName,
    243             String destination) {
    244         return (displayNameSource > DisplayNameSources.PHONE) ? displayName : destination;
    245     }
    246 
    247     public int getEntryType() {
    248         return mEntryType;
    249     }
    250 
    251     public String getDisplayName() {
    252         return mDisplayName;
    253     }
    254 
    255     public String getDestination() {
    256         return mDestination;
    257     }
    258 
    259     public int getDestinationType() {
    260         return mDestinationType;
    261     }
    262 
    263     public String getDestinationLabel() {
    264         return mDestinationLabel;
    265     }
    266 
    267     public long getContactId() {
    268         return mContactId;
    269     }
    270 
    271     public Long getDirectoryId() {
    272         return mDirectoryId;
    273     }
    274 
    275     public long getDataId() {
    276         return mDataId;
    277     }
    278 
    279     public boolean isFirstLevel() {
    280         return mIsFirstLevel;
    281     }
    282 
    283     public Uri getPhotoThumbnailUri() {
    284         return mPhotoThumbnailUri;
    285     }
    286 
    287     /** Indicates whether the icon in the chip is displayed or not. */
    288     public boolean shouldDisplayIcon() {
    289         return mShouldDisplayIcon;
    290     }
    291 
    292     /** This can be called outside main Looper thread. */
    293     public synchronized void setPhotoBytes(byte[] photoBytes) {
    294         mPhotoBytes = photoBytes;
    295     }
    296 
    297     /** This can be called outside main Looper thread. */
    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      */
    306     public String[] getPermissions() {
    307         return mPermissions;
    308     }
    309 
    310     public String getLookupKey() {
    311         return mLookupKey;
    312     }
    313 
    314     public boolean isSelectable() {
    315         return mEntryType == ENTRY_TYPE_PERSON || mEntryType == ENTRY_TYPE_PERMISSION_REQUEST;
    316     }
    317 
    318     @Override
    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      */
    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
    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      */
    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      */
    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      */
    356     public void setIndicatorText(String indicatorText) {
    357         mIndicatorText = indicatorText;
    358     }
    359 }
    360