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