Home | History | Annotate | Download | only in list
      1 /*
      2  * Copyright (C) 2010 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 package com.android.contacts.common.list;
     17 
     18 import android.content.Context;
     19 import android.database.Cursor;
     20 import android.net.Uri;
     21 import android.provider.ContactsContract;
     22 import android.provider.ContactsContract.ContactCounts;
     23 import android.provider.ContactsContract.Contacts;
     24 import android.provider.ContactsContract.Directory;
     25 import android.provider.ContactsContract.SearchSnippetColumns;
     26 import android.text.TextUtils;
     27 import android.view.View;
     28 import android.view.ViewGroup;
     29 import android.widget.ListView;
     30 
     31 import com.android.contacts.common.R;
     32 
     33 /**
     34  * A cursor adapter for the {@link ContactsContract.Contacts#CONTENT_TYPE} content type.
     35  * Also includes support for including the {@link ContactsContract.Profile} record in the
     36  * list.
     37  */
     38 public abstract class ContactListAdapter extends ContactEntryListAdapter {
     39 
     40     protected static class ContactQuery {
     41         private static final String[] CONTACT_PROJECTION_PRIMARY = new String[] {
     42             Contacts._ID,                           // 0
     43             Contacts.DISPLAY_NAME_PRIMARY,          // 1
     44             Contacts.CONTACT_PRESENCE,              // 2
     45             Contacts.CONTACT_STATUS,                // 3
     46             Contacts.PHOTO_ID,                      // 4
     47             Contacts.PHOTO_THUMBNAIL_URI,           // 5
     48             Contacts.LOOKUP_KEY,                    // 6
     49             Contacts.IS_USER_PROFILE,               // 7
     50         };
     51 
     52         private static final String[] CONTACT_PROJECTION_ALTERNATIVE = new String[] {
     53             Contacts._ID,                           // 0
     54             Contacts.DISPLAY_NAME_ALTERNATIVE,      // 1
     55             Contacts.CONTACT_PRESENCE,              // 2
     56             Contacts.CONTACT_STATUS,                // 3
     57             Contacts.PHOTO_ID,                      // 4
     58             Contacts.PHOTO_THUMBNAIL_URI,           // 5
     59             Contacts.LOOKUP_KEY,                    // 6
     60             Contacts.IS_USER_PROFILE,               // 7
     61         };
     62 
     63         private static final String[] FILTER_PROJECTION_PRIMARY = new String[] {
     64             Contacts._ID,                           // 0
     65             Contacts.DISPLAY_NAME_PRIMARY,          // 1
     66             Contacts.CONTACT_PRESENCE,              // 2
     67             Contacts.CONTACT_STATUS,                // 3
     68             Contacts.PHOTO_ID,                      // 4
     69             Contacts.PHOTO_THUMBNAIL_URI,           // 5
     70             Contacts.LOOKUP_KEY,                    // 6
     71             Contacts.IS_USER_PROFILE,               // 7
     72             SearchSnippetColumns.SNIPPET,           // 8
     73         };
     74 
     75         private static final String[] FILTER_PROJECTION_ALTERNATIVE = new String[] {
     76             Contacts._ID,                           // 0
     77             Contacts.DISPLAY_NAME_ALTERNATIVE,      // 1
     78             Contacts.CONTACT_PRESENCE,              // 2
     79             Contacts.CONTACT_STATUS,                // 3
     80             Contacts.PHOTO_ID,                      // 4
     81             Contacts.PHOTO_THUMBNAIL_URI,           // 5
     82             Contacts.LOOKUP_KEY,                    // 6
     83             Contacts.IS_USER_PROFILE,               // 7
     84             SearchSnippetColumns.SNIPPET,           // 8
     85         };
     86 
     87         public static final int CONTACT_ID               = 0;
     88         public static final int CONTACT_DISPLAY_NAME     = 1;
     89         public static final int CONTACT_PRESENCE_STATUS  = 2;
     90         public static final int CONTACT_CONTACT_STATUS   = 3;
     91         public static final int CONTACT_PHOTO_ID         = 4;
     92         public static final int CONTACT_PHOTO_URI        = 5;
     93         public static final int CONTACT_LOOKUP_KEY       = 6;
     94         public static final int CONTACT_IS_USER_PROFILE  = 7;
     95         public static final int CONTACT_SNIPPET          = 8;
     96     }
     97 
     98     private CharSequence mUnknownNameText;
     99 
    100     private long mSelectedContactDirectoryId;
    101     private String mSelectedContactLookupKey;
    102     private long mSelectedContactId;
    103     private ContactListItemView.PhotoPosition mPhotoPosition;
    104 
    105     public ContactListAdapter(Context context) {
    106         super(context);
    107 
    108         mUnknownNameText = context.getText(R.string.missing_name);
    109     }
    110 
    111     public void setPhotoPosition(ContactListItemView.PhotoPosition photoPosition) {
    112         mPhotoPosition = photoPosition;
    113     }
    114 
    115     public ContactListItemView.PhotoPosition getPhotoPosition() {
    116         return mPhotoPosition;
    117     }
    118 
    119     public CharSequence getUnknownNameText() {
    120         return mUnknownNameText;
    121     }
    122 
    123     public long getSelectedContactDirectoryId() {
    124         return mSelectedContactDirectoryId;
    125     }
    126 
    127     public String getSelectedContactLookupKey() {
    128         return mSelectedContactLookupKey;
    129     }
    130 
    131     public long getSelectedContactId() {
    132         return mSelectedContactId;
    133     }
    134 
    135     public void setSelectedContact(long selectedDirectoryId, String lookupKey, long contactId) {
    136         mSelectedContactDirectoryId = selectedDirectoryId;
    137         mSelectedContactLookupKey = lookupKey;
    138         mSelectedContactId = contactId;
    139     }
    140 
    141     protected static Uri buildSectionIndexerUri(Uri uri) {
    142         return uri.buildUpon()
    143                 .appendQueryParameter(ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, "true").build();
    144     }
    145 
    146     @Override
    147     public String getContactDisplayName(int position) {
    148         return ((Cursor) getItem(position)).getString(ContactQuery.CONTACT_DISPLAY_NAME);
    149     }
    150 
    151     /**
    152      * Builds the {@link Contacts#CONTENT_LOOKUP_URI} for the given
    153      * {@link ListView} position.
    154      */
    155     public Uri getContactUri(int position) {
    156         int partitionIndex = getPartitionForPosition(position);
    157         Cursor item = (Cursor)getItem(position);
    158         return item != null ? getContactUri(partitionIndex, item) : null;
    159     }
    160 
    161     public Uri getContactUri(int partitionIndex, Cursor cursor) {
    162         long contactId = cursor.getLong(ContactQuery.CONTACT_ID);
    163         String lookupKey = cursor.getString(ContactQuery.CONTACT_LOOKUP_KEY);
    164         Uri uri = Contacts.getLookupUri(contactId, lookupKey);
    165         long directoryId = ((DirectoryPartition)getPartition(partitionIndex)).getDirectoryId();
    166         if (directoryId != Directory.DEFAULT) {
    167             uri = uri.buildUpon().appendQueryParameter(
    168                     ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(directoryId)).build();
    169         }
    170         return uri;
    171     }
    172 
    173     /**
    174      * Returns true if the specified contact is selected in the list. For a
    175      * contact to be shown as selected, we need both the directory and and the
    176      * lookup key to be the same. We are paying no attention to the contactId,
    177      * because it is volatile, especially in the case of directories.
    178      */
    179     public boolean isSelectedContact(int partitionIndex, Cursor cursor) {
    180         long directoryId = ((DirectoryPartition)getPartition(partitionIndex)).getDirectoryId();
    181         if (getSelectedContactDirectoryId() != directoryId) {
    182             return false;
    183         }
    184         String lookupKey = getSelectedContactLookupKey();
    185         if (lookupKey != null && TextUtils.equals(lookupKey,
    186                 cursor.getString(ContactQuery.CONTACT_LOOKUP_KEY))) {
    187             return true;
    188         }
    189 
    190         return directoryId != Directory.DEFAULT && directoryId != Directory.LOCAL_INVISIBLE
    191                 && getSelectedContactId() == cursor.getLong(ContactQuery.CONTACT_ID);
    192     }
    193 
    194     @Override
    195     protected View newView(Context context, int partition, Cursor cursor, int position,
    196             ViewGroup parent) {
    197         ContactListItemView view = new ContactListItemView(context, null);
    198         view.setUnknownNameText(mUnknownNameText);
    199         view.setQuickContactEnabled(isQuickContactEnabled());
    200         view.setActivatedStateSupported(isSelectionVisible());
    201         if (mPhotoPosition != null) {
    202             view.setPhotoPosition(mPhotoPosition);
    203         }
    204         return view;
    205     }
    206 
    207     protected void bindSectionHeaderAndDivider(ContactListItemView view, int position,
    208             Cursor cursor) {
    209         if (isSectionHeaderDisplayEnabled()) {
    210             Placement placement = getItemPlacementInSection(position);
    211 
    212             // First position, set the contacts number string
    213             if (position == 0 && cursor.getInt(ContactQuery.CONTACT_IS_USER_PROFILE) == 1) {
    214                 view.setCountView(getContactsCount());
    215             } else {
    216                 view.setCountView(null);
    217             }
    218             view.setSectionHeader(placement.sectionHeader);
    219             view.setDividerVisible(!placement.lastInSection);
    220         } else {
    221             view.setSectionHeader(null);
    222             view.setDividerVisible(true);
    223             view.setCountView(null);
    224         }
    225     }
    226 
    227     protected void bindPhoto(final ContactListItemView view, int partitionIndex, Cursor cursor) {
    228         if (!isPhotoSupported(partitionIndex)) {
    229             view.removePhotoView();
    230             return;
    231         }
    232 
    233         // Set the photo, if available
    234         long photoId = 0;
    235         if (!cursor.isNull(ContactQuery.CONTACT_PHOTO_ID)) {
    236             photoId = cursor.getLong(ContactQuery.CONTACT_PHOTO_ID);
    237         }
    238 
    239         if (photoId != 0) {
    240             getPhotoLoader().loadThumbnail(view.getPhotoView(), photoId, false);
    241         } else {
    242             final String photoUriString = cursor.getString(ContactQuery.CONTACT_PHOTO_URI);
    243             final Uri photoUri = photoUriString == null ? null : Uri.parse(photoUriString);
    244             getPhotoLoader().loadDirectoryPhoto(view.getPhotoView(), photoUri, false);
    245         }
    246     }
    247 
    248     protected void bindName(final ContactListItemView view, Cursor cursor) {
    249         view.showDisplayName(
    250                 cursor, ContactQuery.CONTACT_DISPLAY_NAME, getContactNameDisplayOrder());
    251         // Note: we don't show phonetic any more (See issue 5265330)
    252     }
    253 
    254     protected void bindPresenceAndStatusMessage(final ContactListItemView view, Cursor cursor) {
    255         view.showPresenceAndStatusMessage(cursor, ContactQuery.CONTACT_PRESENCE_STATUS,
    256                 ContactQuery.CONTACT_CONTACT_STATUS);
    257     }
    258 
    259     protected void bindSearchSnippet(final ContactListItemView view, Cursor cursor) {
    260         view.showSnippet(cursor, ContactQuery.CONTACT_SNIPPET);
    261     }
    262 
    263     public int getSelectedContactPosition() {
    264         if (mSelectedContactLookupKey == null && mSelectedContactId == 0) {
    265             return -1;
    266         }
    267 
    268         Cursor cursor = null;
    269         int partitionIndex = -1;
    270         int partitionCount = getPartitionCount();
    271         for (int i = 0; i < partitionCount; i++) {
    272             DirectoryPartition partition = (DirectoryPartition) getPartition(i);
    273             if (partition.getDirectoryId() == mSelectedContactDirectoryId) {
    274                 partitionIndex = i;
    275                 break;
    276             }
    277         }
    278         if (partitionIndex == -1) {
    279             return -1;
    280         }
    281 
    282         cursor = getCursor(partitionIndex);
    283         if (cursor == null) {
    284             return -1;
    285         }
    286 
    287         cursor.moveToPosition(-1);      // Reset cursor
    288         int offset = -1;
    289         while (cursor.moveToNext()) {
    290             if (mSelectedContactLookupKey != null) {
    291                 String lookupKey = cursor.getString(ContactQuery.CONTACT_LOOKUP_KEY);
    292                 if (mSelectedContactLookupKey.equals(lookupKey)) {
    293                     offset = cursor.getPosition();
    294                     break;
    295                 }
    296             }
    297             if (mSelectedContactId != 0 && (mSelectedContactDirectoryId == Directory.DEFAULT
    298                     || mSelectedContactDirectoryId == Directory.LOCAL_INVISIBLE)) {
    299                 long contactId = cursor.getLong(ContactQuery.CONTACT_ID);
    300                 if (contactId == mSelectedContactId) {
    301                     offset = cursor.getPosition();
    302                     break;
    303                 }
    304             }
    305         }
    306         if (offset == -1) {
    307             return -1;
    308         }
    309 
    310         int position = getPositionForPartition(partitionIndex) + offset;
    311         if (hasHeader(partitionIndex)) {
    312             position++;
    313         }
    314         return position;
    315     }
    316 
    317     public boolean hasValidSelection() {
    318         return getSelectedContactPosition() != -1;
    319     }
    320 
    321     public Uri getFirstContactUri() {
    322         int partitionCount = getPartitionCount();
    323         for (int i = 0; i < partitionCount; i++) {
    324             DirectoryPartition partition = (DirectoryPartition) getPartition(i);
    325             if (partition.isLoading()) {
    326                 continue;
    327             }
    328 
    329             Cursor cursor = getCursor(i);
    330             if (cursor == null) {
    331                 continue;
    332             }
    333 
    334             if (!cursor.moveToFirst()) {
    335                 continue;
    336             }
    337 
    338             return getContactUri(i, cursor);
    339         }
    340 
    341         return null;
    342     }
    343 
    344     @Override
    345     public void changeCursor(int partitionIndex, Cursor cursor) {
    346         super.changeCursor(partitionIndex, cursor);
    347 
    348         // Check if a profile exists
    349         if (cursor != null && cursor.getCount() > 0) {
    350             cursor.moveToFirst();
    351             setProfileExists(cursor.getInt(ContactQuery.CONTACT_IS_USER_PROFILE) == 1);
    352         }
    353     }
    354 
    355     /**
    356      * @return Projection useful for children.
    357      */
    358     protected final String[] getProjection(boolean forSearch) {
    359         final int sortOrder = getContactNameDisplayOrder();
    360         if (forSearch) {
    361             if (sortOrder == ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY) {
    362                 return ContactQuery.FILTER_PROJECTION_PRIMARY;
    363             } else {
    364                 return ContactQuery.FILTER_PROJECTION_ALTERNATIVE;
    365             }
    366         } else {
    367             if (sortOrder == ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY) {
    368                 return ContactQuery.CONTACT_PROJECTION_PRIMARY;
    369             } else {
    370                 return ContactQuery.CONTACT_PROJECTION_ALTERNATIVE;
    371             }
    372         }
    373     }
    374 }
    375