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