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.preference.ContactsPreferences; 19 20 import android.content.ContentUris; 21 import android.content.Context; 22 import android.content.CursorLoader; 23 import android.content.SharedPreferences; 24 import android.database.Cursor; 25 import android.net.Uri; 26 import android.net.Uri.Builder; 27 import android.preference.PreferenceManager; 28 import android.provider.ContactsContract; 29 import android.provider.ContactsContract.Contacts; 30 import android.provider.ContactsContract.Directory; 31 import android.provider.ContactsContract.RawContacts; 32 import android.provider.ContactsContract.SearchSnippetColumns; 33 import android.text.TextUtils; 34 import android.view.View; 35 36 import java.util.ArrayList; 37 import java.util.List; 38 39 /** 40 * A cursor adapter for the {@link ContactsContract.Contacts#CONTENT_TYPE} content type. 41 */ 42 public class DefaultContactListAdapter extends ContactListAdapter { 43 44 public static final char SNIPPET_START_MATCH = '\u0001'; 45 public static final char SNIPPET_END_MATCH = '\u0001'; 46 public static final String SNIPPET_ELLIPSIS = "\u2026"; 47 public static final int SNIPPET_MAX_TOKENS = 5; 48 49 public static final String SNIPPET_ARGS = SNIPPET_START_MATCH + "," + SNIPPET_END_MATCH + "," 50 + SNIPPET_ELLIPSIS + "," + SNIPPET_MAX_TOKENS; 51 52 public DefaultContactListAdapter(Context context) { 53 super(context); 54 } 55 56 @Override 57 public void configureLoader(CursorLoader loader, long directoryId) { 58 if (loader instanceof ProfileAndContactsLoader) { 59 ((ProfileAndContactsLoader) loader).setLoadProfile(shouldIncludeProfile()); 60 } 61 62 ContactListFilter filter = getFilter(); 63 if (isSearchMode()) { 64 String query = getQueryString(); 65 if (query == null) { 66 query = ""; 67 } 68 query = query.trim(); 69 if (TextUtils.isEmpty(query)) { 70 // Regardless of the directory, we don't want anything returned, 71 // so let's just send a "nothing" query to the local directory. 72 loader.setUri(Contacts.CONTENT_URI); 73 loader.setProjection(getProjection(false)); 74 loader.setSelection("0"); 75 } else { 76 Builder builder = Contacts.CONTENT_FILTER_URI.buildUpon(); 77 builder.appendPath(query); // Builder will encode the query 78 builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY, 79 String.valueOf(directoryId)); 80 if (directoryId != Directory.DEFAULT && directoryId != Directory.LOCAL_INVISIBLE) { 81 builder.appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, 82 String.valueOf(getDirectoryResultLimit())); 83 } 84 builder.appendQueryParameter(SearchSnippetColumns.SNIPPET_ARGS_PARAM_KEY, 85 SNIPPET_ARGS); 86 builder.appendQueryParameter(SearchSnippetColumns.DEFERRED_SNIPPETING_KEY,"1"); 87 loader.setUri(builder.build()); 88 loader.setProjection(getProjection(true)); 89 } 90 } else { 91 configureUri(loader, directoryId, filter); 92 loader.setProjection(getProjection(false)); 93 configureSelection(loader, directoryId, filter); 94 } 95 96 String sortOrder; 97 if (getSortOrder() == ContactsContract.Preferences.SORT_ORDER_PRIMARY) { 98 sortOrder = Contacts.SORT_KEY_PRIMARY; 99 } else { 100 sortOrder = Contacts.SORT_KEY_ALTERNATIVE; 101 } 102 103 loader.setSortOrder(sortOrder); 104 } 105 106 protected void configureUri(CursorLoader loader, long directoryId, ContactListFilter filter) { 107 Uri uri = Contacts.CONTENT_URI; 108 if (filter != null && filter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) { 109 String lookupKey = getSelectedContactLookupKey(); 110 if (lookupKey != null) { 111 uri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey); 112 } else { 113 uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, getSelectedContactId()); 114 } 115 } 116 117 if (directoryId == Directory.DEFAULT && isSectionHeaderDisplayEnabled()) { 118 uri = buildSectionIndexerUri(uri); 119 } 120 121 // The "All accounts" filter is the same as the entire contents of Directory.DEFAULT 122 if (filter != null 123 && filter.filterType != ContactListFilter.FILTER_TYPE_CUSTOM 124 && filter.filterType != ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) { 125 final Uri.Builder builder = uri.buildUpon(); 126 builder.appendQueryParameter( 127 ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(Directory.DEFAULT)); 128 if (filter.filterType == ContactListFilter.FILTER_TYPE_ACCOUNT) { 129 filter.addAccountQueryParameterToUrl(builder); 130 } 131 uri = builder.build(); 132 } 133 134 loader.setUri(uri); 135 } 136 137 private void configureSelection( 138 CursorLoader loader, long directoryId, ContactListFilter filter) { 139 if (filter == null) { 140 return; 141 } 142 143 if (directoryId != Directory.DEFAULT) { 144 return; 145 } 146 147 StringBuilder selection = new StringBuilder(); 148 List<String> selectionArgs = new ArrayList<String>(); 149 150 switch (filter.filterType) { 151 case ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS: { 152 // We have already added directory=0 to the URI, which takes care of this 153 // filter 154 break; 155 } 156 case ContactListFilter.FILTER_TYPE_SINGLE_CONTACT: { 157 // We have already added the lookup key to the URI, which takes care of this 158 // filter 159 break; 160 } 161 case ContactListFilter.FILTER_TYPE_STARRED: { 162 selection.append(Contacts.STARRED + "!=0"); 163 break; 164 } 165 case ContactListFilter.FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY: { 166 selection.append(Contacts.HAS_PHONE_NUMBER + "=1"); 167 break; 168 } 169 case ContactListFilter.FILTER_TYPE_CUSTOM: { 170 selection.append(Contacts.IN_VISIBLE_GROUP + "=1"); 171 if (isCustomFilterForPhoneNumbersOnly()) { 172 selection.append(" AND " + Contacts.HAS_PHONE_NUMBER + "=1"); 173 } 174 break; 175 } 176 case ContactListFilter.FILTER_TYPE_ACCOUNT: { 177 // We use query parameters for account filter, so no selection to add here. 178 break; 179 } 180 } 181 loader.setSelection(selection.toString()); 182 loader.setSelectionArgs(selectionArgs.toArray(new String[0])); 183 } 184 185 @Override 186 protected void bindView(View itemView, int partition, Cursor cursor, int position) { 187 final ContactListItemView view = (ContactListItemView)itemView; 188 189 view.setHighlightedPrefix(isSearchMode() ? getUpperCaseQueryString() : null); 190 191 if (isSelectionVisible()) { 192 view.setActivated(isSelectedContact(partition, cursor)); 193 } 194 195 bindSectionHeaderAndDivider(view, position, cursor); 196 197 if (isQuickContactEnabled()) { 198 bindQuickContact(view, partition, cursor, ContactQuery.CONTACT_PHOTO_ID, 199 ContactQuery.CONTACT_PHOTO_URI, ContactQuery.CONTACT_ID, 200 ContactQuery.CONTACT_LOOKUP_KEY); 201 } else { 202 if (getDisplayPhotos()) { 203 bindPhoto(view, partition, cursor); 204 } 205 } 206 207 bindName(view, cursor); 208 bindPresenceAndStatusMessage(view, cursor); 209 210 if (isSearchMode()) { 211 bindSearchSnippet(view, cursor); 212 } else { 213 view.setSnippet(null); 214 } 215 } 216 217 private boolean isCustomFilterForPhoneNumbersOnly() { 218 // TODO: this flag should not be stored in shared prefs. It needs to be in the db. 219 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); 220 return prefs.getBoolean(ContactsPreferences.PREF_DISPLAY_ONLY_PHONES, 221 ContactsPreferences.PREF_DISPLAY_ONLY_PHONES_DEFAULT); 222 } 223 } 224