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