blob: 8a6e081c114cfd1e287a7d897b736f22fa13176b [file] [log] [blame]
Dmitri Plotnikov732f6522010-04-27 10:30:59 -07001/*
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
Dmitri Plotnikovd9591042010-11-23 09:25:03 -080018import android.app.Activity;
Dmitri Plotnikov892fb512010-12-15 16:25:16 -080019import android.content.ContentResolver;
Dmitri Plotnikov7d402242011-01-09 16:52:08 -080020import android.content.ContentUris;
Dmitri Plotnikovcfc64892010-07-09 17:55:09 -070021import android.content.Loader;
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -070022import android.content.SharedPreferences;
Dmitri Plotnikovd9591042010-11-23 09:25:03 -080023import android.content.SharedPreferences.Editor;
Dmitri Plotnikovcfc64892010-07-09 17:55:09 -070024import android.database.Cursor;
Dmitri Plotnikov732f6522010-04-27 10:30:59 -070025import android.net.Uri;
Katherine Kuan6cd5b0a2011-09-16 11:46:01 -070026import android.os.AsyncTask;
Dmitri Plotnikovcfc64892010-07-09 17:55:09 -070027import android.os.Bundle;
Dmitri Plotnikovd9591042010-11-23 09:25:03 -080028import android.os.Handler;
29import android.os.Message;
30import android.preference.PreferenceManager;
Dmitri Plotnikov7ccdc1f2010-08-02 18:18:01 -070031import android.provider.ContactsContract;
Dmitri Plotnikovcfc64892010-07-09 17:55:09 -070032import android.provider.ContactsContract.Contacts;
Dmitri Plotnikov7ccdc1f2010-08-02 18:18:01 -070033import android.provider.ContactsContract.Directory;
34import android.text.TextUtils;
Dmitri Plotnikov264023c2010-12-15 12:04:13 -080035import android.util.Log;
Dmitri Plotnikov732f6522010-04-27 10:30:59 -070036
Chiao Chenge0b2f1e2012-06-12 13:07:56 -070037import com.android.common.widget.CompositeCursorAdapter.Partition;
Gary Mai69c182a2016-12-05 13:07:03 -080038import com.android.contacts.util.ContactLoaderUtils;
Chiao Chenge0b2f1e2012-06-12 13:07:56 -070039
Dmitri Plotnikov892fb512010-12-15 16:25:16 -080040import java.util.List;
41
Dmitri Plotnikov732f6522010-04-27 10:30:59 -070042/**
Dmitri Plotnikov416b5692010-04-27 18:14:58 -070043 * Fragment containing a contact list used for browsing (as compared to
44 * picking a contact with one of the PICK intents).
Dmitri Plotnikov732f6522010-04-27 10:30:59 -070045 */
Dmitri Plotnikov238d9842010-05-04 14:14:57 -070046public abstract class ContactBrowseListFragment extends
Walter Jange9ea4f02016-05-10 09:39:46 -070047 MultiSelectContactsListFragment<ContactListAdapter> {
Dmitri Plotnikov732f6522010-04-27 10:30:59 -070048
Dmitri Plotnikov264023c2010-12-15 12:04:13 -080049 private static final String TAG = "ContactList";
50
Dmitri Plotnikovcfc64892010-07-09 17:55:09 -070051 private static final String KEY_SELECTED_URI = "selectedUri";
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -070052 private static final String KEY_SELECTION_VERIFIED = "selectionVerified";
Dmitri Plotnikov264023c2010-12-15 12:04:13 -080053 private static final String KEY_FILTER = "filter";
Dmitri Plotnikov682e1522011-01-16 17:04:32 -080054 private static final String KEY_LAST_SELECTED_POSITION = "lastSelected";
Dmitri Plotnikovd9591042010-11-23 09:25:03 -080055
Dmitri Plotnikovd9591042010-11-23 09:25:03 -080056 private static final String PERSISTENT_SELECTION_PREFIX = "defaultContactBrowserSelection";
57
58 /**
59 * The id for a delayed message that triggers automatic selection of the first
60 * found contact in search mode.
61 */
62 private static final int MESSAGE_AUTOSELECT_FIRST_FOUND_CONTACT = 1;
63
64 /**
65 * The delay that is used for automatically selecting the first found contact.
66 */
67 private static final int DELAY_AUTOSELECT_FIRST_FOUND_CONTACT_MILLIS = 500;
68
69 /**
70 * The minimum number of characters in the search query that is required
71 * before we automatically select the first found contact.
72 */
73 private static final int AUTOSELECT_FIRST_FOUND_CONTACT_MIN_QUERY_LENGTH = 2;
74
Dmitri Plotnikovd9591042010-11-23 09:25:03 -080075 private SharedPreferences mPrefs;
76 private Handler mHandler;
Dmitri Plotnikov3c69e212010-09-27 11:43:39 -070077
Dmitri Plotnikovd9591042010-11-23 09:25:03 -080078 private boolean mStartedLoading;
79 private boolean mSelectionRequired;
80 private boolean mSelectionToScreenRequested;
81 private boolean mSmoothScrollRequested;
82 private boolean mSelectionPersistenceRequested;
Dmitri Plotnikovcfc64892010-07-09 17:55:09 -070083 private Uri mSelectedContactUri;
Dmitri Plotnikov7ccdc1f2010-08-02 18:18:01 -070084 private long mSelectedContactDirectoryId;
85 private String mSelectedContactLookupKey;
Dmitri Plotnikov7d402242011-01-09 16:52:08 -080086 private long mSelectedContactId;
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -070087 private boolean mSelectionVerified;
Dmitri Plotnikov682e1522011-01-16 17:04:32 -080088 private int mLastSelectedPosition = -1;
Dmitri Plotnikov892fb512010-12-15 16:25:16 -080089 private boolean mRefreshingContactUri;
yaolu9d998592016-10-14 23:21:00 -070090 private ContactListFilter mFilter;
Dmitri Plotnikovd9591042010-11-23 09:25:03 -080091 private String mPersistentSelectionPrefix = PERSISTENT_SELECTION_PREFIX;
Dmitri Plotnikovcfc64892010-07-09 17:55:09 -070092
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -070093 protected OnContactBrowserActionListener mListener;
Katherine Kuan6cd5b0a2011-09-16 11:46:01 -070094 private ContactLookupTask mContactLookupTask;
Dmitri Plotnikov732f6522010-04-27 10:30:59 -070095
Katherine Kuan6cd5b0a2011-09-16 11:46:01 -070096 private final class ContactLookupTask extends AsyncTask<Void, Void, Uri> {
Dmitri Plotnikov5ffd9292010-07-12 17:04:20 -070097
Katherine Kuan6cd5b0a2011-09-16 11:46:01 -070098 private final Uri mUri;
99 private boolean mIsCancelled;
Dmitri Plotnikov892fb512010-12-15 16:25:16 -0800100
Katherine Kuan6cd5b0a2011-09-16 11:46:01 -0700101 public ContactLookupTask(Uri uri) {
102 mUri = uri;
Dmitri Plotnikov5ffd9292010-07-12 17:04:20 -0700103 }
104
105 @Override
Katherine Kuan6cd5b0a2011-09-16 11:46:01 -0700106 protected Uri doInBackground(Void... args) {
107 Cursor cursor = null;
108 try {
109 final ContentResolver resolver = getContext().getContentResolver();
110 final Uri uriCurrentFormat = ContactLoaderUtils.ensureIsContactUri(resolver, mUri);
111 cursor = resolver.query(uriCurrentFormat,
112 new String[] { Contacts._ID, Contacts.LOOKUP_KEY }, null, null, null);
Dmitri Plotnikov892fb512010-12-15 16:25:16 -0800113
Katherine Kuan6cd5b0a2011-09-16 11:46:01 -0700114 if (cursor != null && cursor.moveToFirst()) {
115 final long contactId = cursor.getLong(0);
116 final String lookupKey = cursor.getString(1);
117 if (contactId != 0 && !TextUtils.isEmpty(lookupKey)) {
118 return Contacts.getLookupUri(contactId, lookupKey);
119 }
120 }
121
122 Log.e(TAG, "Error: No contact ID or lookup key for contact " + mUri);
123 return null;
Jay Shrauner02ecc3f2014-12-12 11:29:16 -0800124 } catch (Exception e) {
125 Log.e(TAG, "Error loading the contact: " + mUri, e);
126 return null;
Katherine Kuan6cd5b0a2011-09-16 11:46:01 -0700127 } finally {
128 if (cursor != null) {
129 cursor.close();
130 }
131 }
132 }
133
134 public void cancel() {
135 super.cancel(true);
136 // Use a flag to keep track of whether the {@link AsyncTask} was cancelled or not in
137 // order to ensure onPostExecute() is not executed after the cancel request. The flag is
138 // necessary because {@link AsyncTask} still calls onPostExecute() if the cancel request
139 // came after the worker thread was finished.
140 mIsCancelled = true;
141 }
142
143 @Override
144 protected void onPostExecute(Uri uri) {
145 // Make sure the {@link Fragment} is at least still attached to the {@link Activity}
Katherine Kuandb90ba12011-11-10 20:57:26 -0800146 // before continuing. Null URIs should still be allowed so that the list can be
147 // refreshed and a default contact can be selected (i.e. the case of deleted
148 // contacts).
149 if (mIsCancelled || !isAdded()) {
Dmitri Plotnikov892fb512010-12-15 16:25:16 -0800150 return;
151 }
Katherine Kuandb90ba12011-11-10 20:57:26 -0800152 onContactUriQueryFinished(uri);
Dmitri Plotnikov5ffd9292010-07-12 17:04:20 -0700153 }
Dmitri Plotnikov892fb512010-12-15 16:25:16 -0800154 }
155
Dmitri Plotnikov52518842011-01-19 10:39:58 -0800156 private boolean mDelaySelection;
157
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800158 private Handler getHandler() {
159 if (mHandler == null) {
160 mHandler = new Handler() {
161 @Override
162 public void handleMessage(Message msg) {
163 switch (msg.what) {
164 case MESSAGE_AUTOSELECT_FIRST_FOUND_CONTACT:
165 selectDefaultContact();
166 break;
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800167 }
168 }
169 };
170 }
171 return mHandler;
172 }
173
174 @Override
175 public void onAttach(Activity activity) {
176 super.onAttach(activity);
177 mPrefs = PreferenceManager.getDefaultSharedPreferences(activity);
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800178 restoreFilter();
179 restoreSelectedUri(false);
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800180 }
181
Dmitri Plotnikovfa49a662011-01-13 11:04:56 -0800182 @Override
Makoto Onuki1db00f62011-10-19 12:40:12 -0700183 protected void setSearchMode(boolean flag) {
Dmitri Plotnikovfa49a662011-01-13 11:04:56 -0800184 if (isSearchMode() != flag) {
185 if (!flag) {
186 restoreSelectedUri(true);
187 }
188 super.setSearchMode(flag);
189 }
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800190 }
191
Wenyi Wangdf3e8492016-09-09 11:10:07 -0700192 public void updateListFilter(ContactListFilter filter, boolean restoreSelectedUri) {
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800193 if (mFilter == null && filter == null) {
194 return;
195 }
196
197 if (mFilter != null && mFilter.equals(filter)) {
Wenyi Wang2b943992016-05-20 17:21:35 -0700198 setLogListEvents(false);
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800199 return;
200 }
201
Dmitri Plotnikov8daa1792010-12-20 17:50:57 -0800202 Log.v(TAG, "New filter: " + filter);
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800203
Wenyi Wangdf3e8492016-09-09 11:10:07 -0700204 setListType(filter.toListType());
Wenyi Wang2b943992016-05-20 17:21:35 -0700205 setLogListEvents(true);
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800206 mFilter = filter;
Dmitri Plotnikov682e1522011-01-16 17:04:32 -0800207 mLastSelectedPosition = -1;
Wenyi Wang40e79102016-06-01 20:43:56 -0700208
Dmitri Plotnikova5a27442010-12-27 21:06:38 -0800209 if (restoreSelectedUri) {
210 mSelectedContactUri = null;
211 restoreSelectedUri(true);
212 }
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800213 reloadData();
214 }
215
216 public ContactListFilter getFilter() {
217 return mFilter;
218 }
219
Dmitri Plotnikov68e32a32010-05-06 16:15:14 -0700220 @Override
Dmitri Plotnikovcfc64892010-07-09 17:55:09 -0700221 public void restoreSavedState(Bundle savedState) {
222 super.restoreSavedState(savedState);
223
224 if (savedState == null) {
225 return;
226 }
227
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800228 mFilter = savedState.getParcelable(KEY_FILTER);
Dmitri Plotnikovcfc64892010-07-09 17:55:09 -0700229 mSelectedContactUri = savedState.getParcelable(KEY_SELECTED_URI);
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -0700230 mSelectionVerified = savedState.getBoolean(KEY_SELECTION_VERIFIED);
Dmitri Plotnikov682e1522011-01-16 17:04:32 -0800231 mLastSelectedPosition = savedState.getInt(KEY_LAST_SELECTED_POSITION);
Dmitri Plotnikov580286b2010-08-14 18:06:18 -0700232 parseSelectedContactUri();
Dmitri Plotnikovcfc64892010-07-09 17:55:09 -0700233 }
234
235 @Override
236 public void onSaveInstanceState(Bundle outState) {
237 super.onSaveInstanceState(outState);
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800238 outState.putParcelable(KEY_FILTER, mFilter);
Dmitri Plotnikovcfc64892010-07-09 17:55:09 -0700239 outState.putParcelable(KEY_SELECTED_URI, mSelectedContactUri);
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -0700240 outState.putBoolean(KEY_SELECTION_VERIFIED, mSelectionVerified);
Dmitri Plotnikov682e1522011-01-16 17:04:32 -0800241 outState.putInt(KEY_LAST_SELECTED_POSITION, mLastSelectedPosition);
Dmitri Plotnikovcfc64892010-07-09 17:55:09 -0700242 }
243
Dmitri Plotnikov892fb512010-12-15 16:25:16 -0800244 protected void refreshSelectedContactUri() {
Katherine Kuan6cd5b0a2011-09-16 11:46:01 -0700245 if (mContactLookupTask != null) {
246 mContactLookupTask.cancel();
Dmitri Plotnikov892fb512010-12-15 16:25:16 -0800247 }
Dmitri Plotnikovcfc64892010-07-09 17:55:09 -0700248
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800249 if (!isSelectionVisible()) {
250 return;
Dmitri Plotnikov7ccdc1f2010-08-02 18:18:01 -0700251 }
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800252
Dmitri Plotnikov892fb512010-12-15 16:25:16 -0800253 mRefreshingContactUri = true;
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800254
255 if (mSelectedContactUri == null) {
Dmitri Plotnikov892fb512010-12-15 16:25:16 -0800256 onContactUriQueryFinished(null);
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800257 return;
258 }
259
260 if (mSelectedContactDirectoryId != Directory.DEFAULT
261 && mSelectedContactDirectoryId != Directory.LOCAL_INVISIBLE) {
Dmitri Plotnikov892fb512010-12-15 16:25:16 -0800262 onContactUriQueryFinished(mSelectedContactUri);
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800263 } else {
Katherine Kuan6cd5b0a2011-09-16 11:46:01 -0700264 mContactLookupTask = new ContactLookupTask(mSelectedContactUri);
265 mContactLookupTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null);
Dmitri Plotnikov892fb512010-12-15 16:25:16 -0800266 }
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800267 }
268
Dmitri Plotnikov892fb512010-12-15 16:25:16 -0800269 protected void onContactUriQueryFinished(Uri uri) {
270 mRefreshingContactUri = false;
271 mSelectedContactUri = uri;
272 parseSelectedContactUri();
273 checkSelection();
Dmitri Plotnikov7ccdc1f2010-08-02 18:18:01 -0700274 }
275
Dmitri Plotnikovcfc64892010-07-09 17:55:09 -0700276 public Uri getSelectedContactUri() {
277 return mSelectedContactUri;
278 }
279
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800280 /**
281 * Sets the new selection for the list.
282 */
Dmitri Plotnikovcfc64892010-07-09 17:55:09 -0700283 public void setSelectedContactUri(Uri uri) {
Katherine Kuan0b3af922011-12-28 14:35:21 -0800284 setSelectedContactUri(uri, true, false /* no smooth scroll */, true, false);
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800285 }
286
Dmitri Plotnikov52518842011-01-19 10:39:58 -0800287 @Override
288 public void setQueryString(String queryString, boolean delaySelection) {
289 mDelaySelection = delaySelection;
290 super.setQueryString(queryString, delaySelection);
291 }
292
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800293 /**
Katherine Kuane1cabcd2011-11-11 20:40:20 -0800294 * Sets whether or not a contact selection must be made.
295 * @param required if true, we need to check if the selection is present in
296 * the list and if not notify the listener so that it can load a
297 * different list.
298 * TODO: Figure out how to reconcile this with {@link #setSelectedContactUri},
299 * without causing unnecessary loading of the list if the selected contact URI is
300 * the same as before.
301 */
302 public void setSelectionRequired(boolean required) {
303 mSelectionRequired = required;
304 }
305
306 /**
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800307 * Sets the new contact selection.
308 *
309 * @param uri the new selection
310 * @param required if true, we need to check if the selection is present in
311 * the list and if not notify the listener so that it can load a
312 * different list
313 * @param smoothScroll if true, the UI will roll smoothly to the new
314 * selection
315 * @param persistent if true, the selection will be stored in shared
316 * preferences.
317 * @param willReloadData if true, the selection will be remembered but not
318 * actually shown, because we are expecting that the data will be
319 * reloaded momentarily
320 */
321 private void setSelectedContactUri(Uri uri, boolean required, boolean smoothScroll,
322 boolean persistent, boolean willReloadData) {
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800323 mSmoothScrollRequested = smoothScroll;
324 mSelectionToScreenRequested = true;
325
Dmitri Plotnikov174fdc22010-10-29 13:55:01 -0700326 if ((mSelectedContactUri == null && uri != null)
Dmitri Plotnikovcfc64892010-07-09 17:55:09 -0700327 || (mSelectedContactUri != null && !mSelectedContactUri.equals(uri))) {
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800328 mSelectionVerified = false;
329 mSelectionRequired = required;
330 mSelectionPersistenceRequested = persistent;
Dmitri Plotnikov7ccdc1f2010-08-02 18:18:01 -0700331 mSelectedContactUri = uri;
Dmitri Plotnikov580286b2010-08-14 18:06:18 -0700332 parseSelectedContactUri();
Dmitri Plotnikov7ccdc1f2010-08-02 18:18:01 -0700333
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800334 if (!willReloadData) {
335 // Configure the adapter to show the selection based on the
336 // lookup key extracted from the URI
337 ContactListAdapter adapter = getAdapter();
338 if (adapter != null) {
Dmitri Plotnikov7d402242011-01-09 16:52:08 -0800339 adapter.setSelectedContact(mSelectedContactDirectoryId,
340 mSelectedContactLookupKey, mSelectedContactId);
Dmitri Plotnikov2f28eda2010-12-15 16:49:01 -0800341 getListView().invalidateViews();
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800342 }
343 }
344
Dmitri Plotnikov892fb512010-12-15 16:25:16 -0800345 // Also, launch a loader to pick up a new lookup URI in case it has changed
346 refreshSelectedContactUri();
Dmitri Plotnikovcfc64892010-07-09 17:55:09 -0700347 }
348 }
349
Dmitri Plotnikov580286b2010-08-14 18:06:18 -0700350 private void parseSelectedContactUri() {
351 if (mSelectedContactUri != null) {
Dmitri Plotnikov90715b32010-11-23 10:57:23 -0800352 String directoryParam =
Dmitri Plotnikov892fb512010-12-15 16:25:16 -0800353 mSelectedContactUri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY);
Dmitri Plotnikov90715b32010-11-23 10:57:23 -0800354 mSelectedContactDirectoryId = TextUtils.isEmpty(directoryParam) ? Directory.DEFAULT
355 : Long.parseLong(directoryParam);
356 if (mSelectedContactUri.toString().startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) {
Dmitri Plotnikov892fb512010-12-15 16:25:16 -0800357 List<String> pathSegments = mSelectedContactUri.getPathSegments();
358 mSelectedContactLookupKey = Uri.encode(pathSegments.get(2));
Dmitri Plotnikovb906cbe2011-01-18 11:14:05 -0800359 if (pathSegments.size() == 4) {
Dmitri Plotnikov0c708092011-01-16 16:40:34 -0800360 mSelectedContactId = ContentUris.parseId(mSelectedContactUri);
361 }
Dmitri Plotnikovfa49a662011-01-13 11:04:56 -0800362 } else if (mSelectedContactUri.toString().startsWith(Contacts.CONTENT_URI.toString()) &&
363 mSelectedContactUri.getPathSegments().size() >= 2) {
Dmitri Plotnikov90715b32010-11-23 10:57:23 -0800364 mSelectedContactLookupKey = null;
Dmitri Plotnikov7d402242011-01-09 16:52:08 -0800365 mSelectedContactId = ContentUris.parseId(mSelectedContactUri);
366 } else {
367 Log.e(TAG, "Unsupported contact URI: " + mSelectedContactUri);
368 mSelectedContactLookupKey = null;
369 mSelectedContactId = 0;
Dmitri Plotnikov580286b2010-08-14 18:06:18 -0700370 }
371
Dmitri Plotnikov580286b2010-08-14 18:06:18 -0700372 } else {
373 mSelectedContactDirectoryId = Directory.DEFAULT;
374 mSelectedContactLookupKey = null;
Dmitri Plotnikov7d402242011-01-09 16:52:08 -0800375 mSelectedContactId = 0;
Dmitri Plotnikov580286b2010-08-14 18:06:18 -0700376 }
377 }
378
Dmitri Plotnikov7ccdc1f2010-08-02 18:18:01 -0700379 @Override
Walter Jange9ea4f02016-05-10 09:39:46 -0700380 public ContactListAdapter getAdapter() {
381 return (ContactListAdapter) super.getAdapter();
382 }
383
384 @Override
Dmitri Plotnikov7ccdc1f2010-08-02 18:18:01 -0700385 protected void configureAdapter() {
386 super.configureAdapter();
Dmitri Plotnikov7ccdc1f2010-08-02 18:18:01 -0700387
Dmitri Plotnikov7ccdc1f2010-08-02 18:18:01 -0700388 ContactListAdapter adapter = getAdapter();
389 if (adapter == null) {
390 return;
391 }
392
Isaac Katzenelson8fdf8902011-08-10 21:00:54 -0700393 boolean searchMode = isSearchMode();
394 if (!searchMode && mFilter != null) {
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800395 adapter.setFilter(mFilter);
Dmitri Plotnikov3c8c3e82010-12-22 17:09:21 -0800396 if (mSelectionRequired
397 || mFilter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) {
Dmitri Plotnikov7d402242011-01-09 16:52:08 -0800398 adapter.setSelectedContact(
399 mSelectedContactDirectoryId, mSelectedContactLookupKey, mSelectedContactId);
Dmitri Plotnikov93a05db2010-12-22 15:23:54 -0800400 }
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800401 }
Dave Santoro0a4d2252011-05-20 11:40:34 -0700402
Wenyi Wang65751292016-09-01 15:23:25 -0700403 adapter.setIncludeFavorites(!searchMode && mFilter.isContactsFilterType());
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -0700404 }
405
406 @Override
Dmitri Plotnikov174fdc22010-10-29 13:55:01 -0700407 public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
408 super.onLoadFinished(loader, data);
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800409 mSelectionVerified = false;
Dmitri Plotnikov892fb512010-12-15 16:25:16 -0800410
411 // Refresh the currently selected lookup in case it changed while we were sleeping
412 refreshSelectedContactUri();
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -0700413 }
414
Dmitri Plotnikov0cd01302010-12-17 18:41:57 -0800415 @Override
Dianne Hackborn4ef95cc2010-12-16 00:44:33 -0800416 public void onLoaderReset(Loader<Cursor> loader) {
417 }
418
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -0700419 private void checkSelection() {
Dmitri Plotnikov174fdc22010-10-29 13:55:01 -0700420 if (mSelectionVerified) {
421 return;
422 }
423
Dmitri Plotnikov93a05db2010-12-22 15:23:54 -0800424 if (mRefreshingContactUri) {
425 return;
426 }
427
428 if (isLoadingDirectoryList()) {
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -0700429 return;
430 }
431
432 ContactListAdapter adapter = getAdapter();
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800433 if (adapter == null) {
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800434 return;
435 }
436
Dmitri Plotnikov93a05db2010-12-22 15:23:54 -0800437 boolean directoryLoading = true;
438 int count = adapter.getPartitionCount();
439 for (int i = 0; i < count; i++) {
440 Partition partition = adapter.getPartition(i);
441 if (partition instanceof DirectoryPartition) {
442 DirectoryPartition directory = (DirectoryPartition) partition;
443 if (directory.getDirectoryId() == mSelectedContactDirectoryId) {
444 directoryLoading = directory.isLoading();
445 break;
446 }
447 }
448 }
449
450 if (directoryLoading) {
451 return;
452 }
453
Dmitri Plotnikov7d402242011-01-09 16:52:08 -0800454 adapter.setSelectedContact(
455 mSelectedContactDirectoryId, mSelectedContactLookupKey, mSelectedContactId);
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800456
Katherine Kuane1cabcd2011-11-11 20:40:20 -0800457 final int selectedPosition = adapter.getSelectedContactPosition();
Dmitri Plotnikov682e1522011-01-16 17:04:32 -0800458 if (selectedPosition != -1) {
459 mLastSelectedPosition = selectedPosition;
460 } else {
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800461 if (isSearchMode()) {
Dmitri Plotnikov52518842011-01-19 10:39:58 -0800462 if (mDelaySelection) {
463 selectFirstFoundContactAfterDelay();
464 if (mListener != null) {
465 mListener.onSelectionChange();
466 }
467 return;
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800468 }
Dmitri Plotnikov52518842011-01-19 10:39:58 -0800469 } else if (mSelectionRequired) {
Dmitri Plotnikov8fe78212011-01-16 17:58:42 -0800470 // A specific contact was requested, but it's not in the loaded list.
471
472 // Try reconfiguring and reloading the list that will hopefully contain
473 // the requested contact. Only take one attempt to avoid an infinite loop
474 // in case the contact cannot be found at all.
475 mSelectionRequired = false;
476
477 // If we were looking at a different specific contact, just reload
Chiao Cheng3af2d482012-11-28 15:11:36 -0800478 // FILTER_TYPE_ALL_ACCOUNTS is needed for the case where a new contact is added
479 // on a tablet and the loader is returning a stale list. In this case, the contact
480 // will not be found until the next load. b/7621855 This will only fix the most
481 // common case where all accounts are shown. It will not fix the one account case.
482 // TODO: we may want to add more FILTER_TYPEs or relax this check to fix all other
483 // FILTER_TYPE cases.
Dmitri Plotnikov8fe78212011-01-16 17:58:42 -0800484 if (mFilter != null
Chiao Cheng3af2d482012-11-28 15:11:36 -0800485 && (mFilter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT
486 || mFilter.filterType == ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS)) {
Dmitri Plotnikov8fe78212011-01-16 17:58:42 -0800487 reloadData();
488 } else {
489 // Otherwise, call the listener, which will adjust the filter.
490 notifyInvalidSelection();
491 }
492 return;
Dmitri Plotnikov52518842011-01-19 10:39:58 -0800493 } else if (mFilter != null
Dmitri Plotnikov8fe78212011-01-16 17:58:42 -0800494 && mFilter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) {
Dmitri Plotnikov52518842011-01-19 10:39:58 -0800495 // If we were trying to load a specific contact, but that contact no longer
496 // exists, call the listener, which will adjust the filter.
Dmitri Plotnikov8fe78212011-01-16 17:58:42 -0800497 notifyInvalidSelection();
498 return;
499 }
500
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800501 saveSelectedUri(null);
502 selectDefaultContact();
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800503 }
504
505 mSelectionRequired = false;
506 mSelectionVerified = true;
507
508 if (mSelectionPersistenceRequested) {
509 saveSelectedUri(mSelectedContactUri);
510 mSelectionPersistenceRequested = false;
511 }
512
513 if (mSelectionToScreenRequested) {
Katherine Kuane1cabcd2011-11-11 20:40:20 -0800514 requestSelectionToScreen(selectedPosition);
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800515 }
516
Dmitri Plotnikov2f28eda2010-12-15 16:49:01 -0800517 getListView().invalidateViews();
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800518
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800519 if (mListener != null) {
520 mListener.onSelectionChange();
521 }
522 }
523
524 /**
525 * Automatically selects the first found contact in search mode. The selection
526 * is updated after a delay to allow the user to type without to much UI churn
527 * and to save bandwidth on directory queries.
528 */
529 public void selectFirstFoundContactAfterDelay() {
530 Handler handler = getHandler();
531 handler.removeMessages(MESSAGE_AUTOSELECT_FIRST_FOUND_CONTACT);
532
533 String queryString = getQueryString();
534 if (queryString != null
535 && queryString.length() >= AUTOSELECT_FIRST_FOUND_CONTACT_MIN_QUERY_LENGTH) {
536 handler.sendEmptyMessageDelayed(MESSAGE_AUTOSELECT_FIRST_FOUND_CONTACT,
537 DELAY_AUTOSELECT_FIRST_FOUND_CONTACT_MILLIS);
538 } else {
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800539 setSelectedContactUri(null, false, false, false, false);
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800540 }
541 }
542
543 protected void selectDefaultContact() {
Dmitri Plotnikov682e1522011-01-16 17:04:32 -0800544 Uri contactUri = null;
Dmitri Plotnikov07bf52d2011-02-22 18:46:40 -0800545 ContactListAdapter adapter = getAdapter();
Dmitri Plotnikov682e1522011-01-16 17:04:32 -0800546 if (mLastSelectedPosition != -1) {
Dmitri Plotnikov07bf52d2011-02-22 18:46:40 -0800547 int count = adapter.getCount();
548 int pos = mLastSelectedPosition;
549 if (pos >= count && count > 0) {
550 pos = count - 1;
551 }
552 contactUri = adapter.getContactUri(pos);
Dmitri Plotnikov682e1522011-01-16 17:04:32 -0800553 }
554
555 if (contactUri == null) {
Dmitri Plotnikov07bf52d2011-02-22 18:46:40 -0800556 contactUri = adapter.getFirstContactUri();
Dmitri Plotnikov682e1522011-01-16 17:04:32 -0800557 }
558
559 setSelectedContactUri(contactUri, false, mSmoothScrollRequested, false, false);
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800560 }
561
Katherine Kuane1cabcd2011-11-11 20:40:20 -0800562 protected void requestSelectionToScreen(int selectedPosition) {
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800563 if (selectedPosition != -1) {
Dmitri Plotnikov753f8f62010-12-15 11:10:27 -0800564 AutoScrollListView listView = (AutoScrollListView)getListView();
565 listView.requestPositionToScreen(
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800566 selectedPosition + listView.getHeaderViewsCount(), mSmoothScrollRequested);
567 mSelectionToScreenRequested = false;
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -0700568 }
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -0700569 }
570
Dmitri Plotnikov174fdc22010-10-29 13:55:01 -0700571 @Override
572 public boolean isLoading() {
Dmitri Plotnikov892fb512010-12-15 16:25:16 -0800573 return mRefreshingContactUri || super.isLoading();
Dmitri Plotnikov174fdc22010-10-29 13:55:01 -0700574 }
575
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -0700576 @Override
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800577 protected void startLoading() {
578 mStartedLoading = true;
579 mSelectionVerified = false;
580 super.startLoading();
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -0700581 }
582
Dmitri Plotnikova5a27442010-12-27 21:06:38 -0800583 public void reloadDataAndSetSelectedUri(Uri uri) {
584 setSelectedContactUri(uri, true, true, true, true);
585 reloadData();
586 }
587
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -0700588 @Override
589 public void reloadData() {
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800590 if (mStartedLoading) {
591 mSelectionVerified = false;
Dmitri Plotnikov04bad512011-01-17 12:43:01 -0800592 mLastSelectedPosition = -1;
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800593 super.reloadData();
594 }
Dmitri Plotnikov7ccdc1f2010-08-02 18:18:01 -0700595 }
596
Dmitri Plotnikov416b5692010-04-27 18:14:58 -0700597 public void setOnContactListActionListener(OnContactBrowserActionListener listener) {
Dmitri Plotnikov732f6522010-04-27 10:30:59 -0700598 mListener = listener;
599 }
600
Wenyi Wang2b943992016-05-20 17:21:35 -0700601 public void viewContact(int position, Uri contactUri, boolean isEnterpriseContact) {
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800602 setSelectedContactUri(contactUri, false, false, true, false);
Wenyi Wang2b943992016-05-20 17:21:35 -0700603 if (mListener != null) mListener.onViewContactAction(position, contactUri,
604 isEnterpriseContact);
Dmitri Plotnikov1323d692010-04-27 14:54:27 -0700605 }
Dmitri Plotnikov732f6522010-04-27 10:30:59 -0700606
Dmitri Plotnikov1323d692010-04-27 14:54:27 -0700607 public void deleteContact(Uri contactUri) {
Dmitri Plotnikov71521242010-11-17 18:47:14 -0800608 if (mListener != null) mListener.onDeleteContactAction(contactUri);
Dmitri Plotnikov1323d692010-04-27 14:54:27 -0700609 }
610
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -0700611 private void notifyInvalidSelection() {
Dmitri Plotnikov71521242010-11-17 18:47:14 -0800612 if (mListener != null) mListener.onInvalidSelection();
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -0700613 }
614
Dmitri Plotnikove3fbfd92010-04-28 11:20:52 -0700615 @Override
616 protected void finish() {
617 super.finish();
Dmitri Plotnikov71521242010-11-17 18:47:14 -0800618 if (mListener != null) mListener.onFinishAction();
Dmitri Plotnikove3fbfd92010-04-28 11:20:52 -0700619 }
Dmitri Plotnikov8c6955f2010-09-08 20:46:36 -0700620
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800621 private void saveSelectedUri(Uri contactUri) {
Dmitri Plotnikovfa49a662011-01-13 11:04:56 -0800622 if (isSearchMode()) {
Dmitri Plotnikov8c6955f2010-09-08 20:46:36 -0700623 return;
624 }
625
Dmitri Plotnikovfa49a662011-01-13 11:04:56 -0800626 ContactListFilter.storeToPreferences(mPrefs, mFilter);
627
628 Editor editor = mPrefs.edit();
629 if (contactUri == null) {
630 editor.remove(getPersistentSelectionKey());
631 } else {
632 editor.putString(getPersistentSelectionKey(), contactUri.toString());
633 }
634 editor.apply();
635 }
636
637 private void restoreSelectedUri(boolean willReloadData) {
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800638 // The meaning of mSelectionRequired is that we need to show some
639 // selection other than the previous selection saved in shared preferences
640 if (mSelectionRequired) {
Dmitri Plotnikov8c6955f2010-09-08 20:46:36 -0700641 return;
642 }
643
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800644 String selectedUri = mPrefs.getString(getPersistentSelectionKey(), null);
645 if (selectedUri == null) {
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800646 setSelectedContactUri(null, false, false, false, willReloadData);
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800647 } else {
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800648 setSelectedContactUri(Uri.parse(selectedUri), false, false, false, willReloadData);
649 }
650 }
651
652 private void saveFilter() {
Dmitri Plotnikovfa49a662011-01-13 11:04:56 -0800653 ContactListFilter.storeToPreferences(mPrefs, mFilter);
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800654 }
655
656 private void restoreFilter() {
Daisuke Miyakawad4ed72e2011-08-12 18:16:01 -0700657 mFilter = ContactListFilter.restoreDefaultPreferences(mPrefs);
Dmitri Plotnikov8c6955f2010-09-08 20:46:36 -0700658 }
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -0700659
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800660 private String getPersistentSelectionKey() {
661 if (mFilter == null) {
662 return mPersistentSelectionPrefix;
663 } else {
664 return mPersistentSelectionPrefix + "-" + mFilter.getId();
665 }
Dmitri Plotnikov30cc8532010-11-11 20:30:48 -0800666 }
Dmitri Plotnikov732f6522010-04-27 10:30:59 -0700667}