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