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