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