blob: bf4f891c09bc87777c726c335d92c84f4db754c5 [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;
Chiao Cheng50b7bd32012-11-30 11:11:59 -080038import com.android.contacts.common.list.AutoScrollListView;
Chiao Chenga0233a02012-11-01 16:41:08 -070039import com.android.contacts.common.list.ContactListAdapter;
40import com.android.contacts.common.list.ContactListFilter;
41import com.android.contacts.common.list.DirectoryPartition;
Wenyi Wang2b943992016-05-20 17:21:35 -070042import com.android.contacts.common.logging.ListEvent.ListType;
Yorke Leecd321f62013-10-28 15:20:15 -070043import com.android.contacts.common.util.ContactLoaderUtils;
Chiao Chenge0b2f1e2012-06-12 13:07:56 -070044
Dmitri Plotnikov892fb512010-12-15 16:25:16 -080045import java.util.List;
46
Dmitri Plotnikov732f6522010-04-27 10:30:59 -070047/**
Dmitri Plotnikov416b5692010-04-27 18:14:58 -070048 * Fragment containing a contact list used for browsing (as compared to
49 * picking a contact with one of the PICK intents).
Dmitri Plotnikov732f6522010-04-27 10:30:59 -070050 */
Dmitri Plotnikov238d9842010-05-04 14:14:57 -070051public abstract class ContactBrowseListFragment extends
Walter Jange9ea4f02016-05-10 09:39:46 -070052 MultiSelectContactsListFragment<ContactListAdapter> {
Dmitri Plotnikov732f6522010-04-27 10:30:59 -070053
Dmitri Plotnikov264023c2010-12-15 12:04:13 -080054 private static final String TAG = "ContactList";
55
Dmitri Plotnikovcfc64892010-07-09 17:55:09 -070056 private static final String KEY_SELECTED_URI = "selectedUri";
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -070057 private static final String KEY_SELECTION_VERIFIED = "selectionVerified";
Dmitri Plotnikov264023c2010-12-15 12:04:13 -080058 private static final String KEY_FILTER = "filter";
Dmitri Plotnikov682e1522011-01-16 17:04:32 -080059 private static final String KEY_LAST_SELECTED_POSITION = "lastSelected";
Dmitri Plotnikovd9591042010-11-23 09:25:03 -080060
Dmitri Plotnikovd9591042010-11-23 09:25:03 -080061 private static final String PERSISTENT_SELECTION_PREFIX = "defaultContactBrowserSelection";
62
63 /**
64 * The id for a delayed message that triggers automatic selection of the first
65 * found contact in search mode.
66 */
67 private static final int MESSAGE_AUTOSELECT_FIRST_FOUND_CONTACT = 1;
68
69 /**
70 * The delay that is used for automatically selecting the first found contact.
71 */
72 private static final int DELAY_AUTOSELECT_FIRST_FOUND_CONTACT_MILLIS = 500;
73
74 /**
75 * The minimum number of characters in the search query that is required
76 * before we automatically select the first found contact.
77 */
78 private static final int AUTOSELECT_FIRST_FOUND_CONTACT_MIN_QUERY_LENGTH = 2;
79
Dmitri Plotnikovd9591042010-11-23 09:25:03 -080080 private SharedPreferences mPrefs;
81 private Handler mHandler;
Dmitri Plotnikov3c69e212010-09-27 11:43:39 -070082
Dmitri Plotnikovd9591042010-11-23 09:25:03 -080083 private boolean mStartedLoading;
84 private boolean mSelectionRequired;
85 private boolean mSelectionToScreenRequested;
86 private boolean mSmoothScrollRequested;
87 private boolean mSelectionPersistenceRequested;
Dmitri Plotnikovcfc64892010-07-09 17:55:09 -070088 private Uri mSelectedContactUri;
Dmitri Plotnikov7ccdc1f2010-08-02 18:18:01 -070089 private long mSelectedContactDirectoryId;
90 private String mSelectedContactLookupKey;
Dmitri Plotnikov7d402242011-01-09 16:52:08 -080091 private long mSelectedContactId;
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -070092 private boolean mSelectionVerified;
Dmitri Plotnikov682e1522011-01-16 17:04:32 -080093 private int mLastSelectedPosition = -1;
Dmitri Plotnikov892fb512010-12-15 16:25:16 -080094 private boolean mRefreshingContactUri;
yaolu9d998592016-10-14 23:21:00 -070095 private ContactListFilter mFilter;
Dmitri Plotnikovd9591042010-11-23 09:25:03 -080096 private String mPersistentSelectionPrefix = PERSISTENT_SELECTION_PREFIX;
Dmitri Plotnikovcfc64892010-07-09 17:55:09 -070097
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -070098 protected OnContactBrowserActionListener mListener;
Katherine Kuan6cd5b0a2011-09-16 11:46:01 -070099 private ContactLookupTask mContactLookupTask;
Dmitri Plotnikov732f6522010-04-27 10:30:59 -0700100
Katherine Kuan6cd5b0a2011-09-16 11:46:01 -0700101 private final class ContactLookupTask extends AsyncTask<Void, Void, Uri> {
Dmitri Plotnikov5ffd9292010-07-12 17:04:20 -0700102
Katherine Kuan6cd5b0a2011-09-16 11:46:01 -0700103 private final Uri mUri;
104 private boolean mIsCancelled;
Dmitri Plotnikov892fb512010-12-15 16:25:16 -0800105
Katherine Kuan6cd5b0a2011-09-16 11:46:01 -0700106 public ContactLookupTask(Uri uri) {
107 mUri = uri;
Dmitri Plotnikov5ffd9292010-07-12 17:04:20 -0700108 }
109
110 @Override
Katherine Kuan6cd5b0a2011-09-16 11:46:01 -0700111 protected Uri doInBackground(Void... args) {
112 Cursor cursor = null;
113 try {
114 final ContentResolver resolver = getContext().getContentResolver();
115 final Uri uriCurrentFormat = ContactLoaderUtils.ensureIsContactUri(resolver, mUri);
116 cursor = resolver.query(uriCurrentFormat,
117 new String[] { Contacts._ID, Contacts.LOOKUP_KEY }, null, null, null);
Dmitri Plotnikov892fb512010-12-15 16:25:16 -0800118
Katherine Kuan6cd5b0a2011-09-16 11:46:01 -0700119 if (cursor != null && cursor.moveToFirst()) {
120 final long contactId = cursor.getLong(0);
121 final String lookupKey = cursor.getString(1);
122 if (contactId != 0 && !TextUtils.isEmpty(lookupKey)) {
123 return Contacts.getLookupUri(contactId, lookupKey);
124 }
125 }
126
127 Log.e(TAG, "Error: No contact ID or lookup key for contact " + mUri);
128 return null;
Jay Shrauner02ecc3f2014-12-12 11:29:16 -0800129 } catch (Exception e) {
130 Log.e(TAG, "Error loading the contact: " + mUri, e);
131 return null;
Katherine Kuan6cd5b0a2011-09-16 11:46:01 -0700132 } finally {
133 if (cursor != null) {
134 cursor.close();
135 }
136 }
137 }
138
139 public void cancel() {
140 super.cancel(true);
141 // Use a flag to keep track of whether the {@link AsyncTask} was cancelled or not in
142 // order to ensure onPostExecute() is not executed after the cancel request. The flag is
143 // necessary because {@link AsyncTask} still calls onPostExecute() if the cancel request
144 // came after the worker thread was finished.
145 mIsCancelled = true;
146 }
147
148 @Override
149 protected void onPostExecute(Uri uri) {
150 // Make sure the {@link Fragment} is at least still attached to the {@link Activity}
Katherine Kuandb90ba12011-11-10 20:57:26 -0800151 // before continuing. Null URIs should still be allowed so that the list can be
152 // refreshed and a default contact can be selected (i.e. the case of deleted
153 // contacts).
154 if (mIsCancelled || !isAdded()) {
Dmitri Plotnikov892fb512010-12-15 16:25:16 -0800155 return;
156 }
Katherine Kuandb90ba12011-11-10 20:57:26 -0800157 onContactUriQueryFinished(uri);
Dmitri Plotnikov5ffd9292010-07-12 17:04:20 -0700158 }
Dmitri Plotnikov892fb512010-12-15 16:25:16 -0800159 }
160
Dmitri Plotnikov52518842011-01-19 10:39:58 -0800161 private boolean mDelaySelection;
162
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800163 private Handler getHandler() {
164 if (mHandler == null) {
165 mHandler = new Handler() {
166 @Override
167 public void handleMessage(Message msg) {
168 switch (msg.what) {
169 case MESSAGE_AUTOSELECT_FIRST_FOUND_CONTACT:
170 selectDefaultContact();
171 break;
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800172 }
173 }
174 };
175 }
176 return mHandler;
177 }
178
179 @Override
180 public void onAttach(Activity activity) {
181 super.onAttach(activity);
182 mPrefs = PreferenceManager.getDefaultSharedPreferences(activity);
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800183 restoreFilter();
184 restoreSelectedUri(false);
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800185 }
186
Dmitri Plotnikovfa49a662011-01-13 11:04:56 -0800187 @Override
Makoto Onuki1db00f62011-10-19 12:40:12 -0700188 protected void setSearchMode(boolean flag) {
Dmitri Plotnikovfa49a662011-01-13 11:04:56 -0800189 if (isSearchMode() != flag) {
190 if (!flag) {
191 restoreSelectedUri(true);
192 }
193 super.setSearchMode(flag);
194 }
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800195 }
196
Wenyi Wangdf3e8492016-09-09 11:10:07 -0700197 public void updateListFilter(ContactListFilter filter, boolean restoreSelectedUri) {
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800198 if (mFilter == null && filter == null) {
199 return;
200 }
201
202 if (mFilter != null && mFilter.equals(filter)) {
Wenyi Wang2b943992016-05-20 17:21:35 -0700203 setLogListEvents(false);
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800204 return;
205 }
206
Dmitri Plotnikov8daa1792010-12-20 17:50:57 -0800207 Log.v(TAG, "New filter: " + filter);
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800208
Wenyi Wangdf3e8492016-09-09 11:10:07 -0700209 setListType(filter.toListType());
Wenyi Wang2b943992016-05-20 17:21:35 -0700210 setLogListEvents(true);
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800211 mFilter = filter;
Dmitri Plotnikov682e1522011-01-16 17:04:32 -0800212 mLastSelectedPosition = -1;
Wenyi Wang40e79102016-06-01 20:43:56 -0700213
Dmitri Plotnikova5a27442010-12-27 21:06:38 -0800214 if (restoreSelectedUri) {
215 mSelectedContactUri = null;
216 restoreSelectedUri(true);
217 }
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800218 reloadData();
219 }
220
221 public ContactListFilter getFilter() {
222 return mFilter;
223 }
224
Dmitri Plotnikov68e32a32010-05-06 16:15:14 -0700225 @Override
Dmitri Plotnikovcfc64892010-07-09 17:55:09 -0700226 public void restoreSavedState(Bundle savedState) {
227 super.restoreSavedState(savedState);
228
229 if (savedState == null) {
230 return;
231 }
232
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800233 mFilter = savedState.getParcelable(KEY_FILTER);
Dmitri Plotnikovcfc64892010-07-09 17:55:09 -0700234 mSelectedContactUri = savedState.getParcelable(KEY_SELECTED_URI);
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -0700235 mSelectionVerified = savedState.getBoolean(KEY_SELECTION_VERIFIED);
Dmitri Plotnikov682e1522011-01-16 17:04:32 -0800236 mLastSelectedPosition = savedState.getInt(KEY_LAST_SELECTED_POSITION);
Dmitri Plotnikov580286b2010-08-14 18:06:18 -0700237 parseSelectedContactUri();
Dmitri Plotnikovcfc64892010-07-09 17:55:09 -0700238 }
239
240 @Override
241 public void onSaveInstanceState(Bundle outState) {
242 super.onSaveInstanceState(outState);
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800243 outState.putParcelable(KEY_FILTER, mFilter);
Dmitri Plotnikovcfc64892010-07-09 17:55:09 -0700244 outState.putParcelable(KEY_SELECTED_URI, mSelectedContactUri);
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -0700245 outState.putBoolean(KEY_SELECTION_VERIFIED, mSelectionVerified);
Dmitri Plotnikov682e1522011-01-16 17:04:32 -0800246 outState.putInt(KEY_LAST_SELECTED_POSITION, mLastSelectedPosition);
Dmitri Plotnikovcfc64892010-07-09 17:55:09 -0700247 }
248
Dmitri Plotnikov892fb512010-12-15 16:25:16 -0800249 protected void refreshSelectedContactUri() {
Katherine Kuan6cd5b0a2011-09-16 11:46:01 -0700250 if (mContactLookupTask != null) {
251 mContactLookupTask.cancel();
Dmitri Plotnikov892fb512010-12-15 16:25:16 -0800252 }
Dmitri Plotnikovcfc64892010-07-09 17:55:09 -0700253
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800254 if (!isSelectionVisible()) {
255 return;
Dmitri Plotnikov7ccdc1f2010-08-02 18:18:01 -0700256 }
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800257
Dmitri Plotnikov892fb512010-12-15 16:25:16 -0800258 mRefreshingContactUri = true;
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800259
260 if (mSelectedContactUri == null) {
Dmitri Plotnikov892fb512010-12-15 16:25:16 -0800261 onContactUriQueryFinished(null);
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800262 return;
263 }
264
265 if (mSelectedContactDirectoryId != Directory.DEFAULT
266 && mSelectedContactDirectoryId != Directory.LOCAL_INVISIBLE) {
Dmitri Plotnikov892fb512010-12-15 16:25:16 -0800267 onContactUriQueryFinished(mSelectedContactUri);
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800268 } else {
Katherine Kuan6cd5b0a2011-09-16 11:46:01 -0700269 mContactLookupTask = new ContactLookupTask(mSelectedContactUri);
270 mContactLookupTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null);
Dmitri Plotnikov892fb512010-12-15 16:25:16 -0800271 }
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800272 }
273
Dmitri Plotnikov892fb512010-12-15 16:25:16 -0800274 protected void onContactUriQueryFinished(Uri uri) {
275 mRefreshingContactUri = false;
276 mSelectedContactUri = uri;
277 parseSelectedContactUri();
278 checkSelection();
Dmitri Plotnikov7ccdc1f2010-08-02 18:18:01 -0700279 }
280
Dmitri Plotnikovcfc64892010-07-09 17:55:09 -0700281 public Uri getSelectedContactUri() {
282 return mSelectedContactUri;
283 }
284
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800285 /**
286 * Sets the new selection for the list.
287 */
Dmitri Plotnikovcfc64892010-07-09 17:55:09 -0700288 public void setSelectedContactUri(Uri uri) {
Katherine Kuan0b3af922011-12-28 14:35:21 -0800289 setSelectedContactUri(uri, true, false /* no smooth scroll */, true, false);
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800290 }
291
Dmitri Plotnikov52518842011-01-19 10:39:58 -0800292 @Override
293 public void setQueryString(String queryString, boolean delaySelection) {
294 mDelaySelection = delaySelection;
295 super.setQueryString(queryString, delaySelection);
296 }
297
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800298 /**
Katherine Kuane1cabcd2011-11-11 20:40:20 -0800299 * Sets whether or not a contact selection must be made.
300 * @param required if true, we need to check if the selection is present in
301 * the list and if not notify the listener so that it can load a
302 * different list.
303 * TODO: Figure out how to reconcile this with {@link #setSelectedContactUri},
304 * without causing unnecessary loading of the list if the selected contact URI is
305 * the same as before.
306 */
307 public void setSelectionRequired(boolean required) {
308 mSelectionRequired = required;
309 }
310
311 /**
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800312 * Sets the new contact selection.
313 *
314 * @param uri the new selection
315 * @param required if true, we need to check if the selection is present in
316 * the list and if not notify the listener so that it can load a
317 * different list
318 * @param smoothScroll if true, the UI will roll smoothly to the new
319 * selection
320 * @param persistent if true, the selection will be stored in shared
321 * preferences.
322 * @param willReloadData if true, the selection will be remembered but not
323 * actually shown, because we are expecting that the data will be
324 * reloaded momentarily
325 */
326 private void setSelectedContactUri(Uri uri, boolean required, boolean smoothScroll,
327 boolean persistent, boolean willReloadData) {
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800328 mSmoothScrollRequested = smoothScroll;
329 mSelectionToScreenRequested = true;
330
Dmitri Plotnikov174fdc22010-10-29 13:55:01 -0700331 if ((mSelectedContactUri == null && uri != null)
Dmitri Plotnikovcfc64892010-07-09 17:55:09 -0700332 || (mSelectedContactUri != null && !mSelectedContactUri.equals(uri))) {
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800333 mSelectionVerified = false;
334 mSelectionRequired = required;
335 mSelectionPersistenceRequested = persistent;
Dmitri Plotnikov7ccdc1f2010-08-02 18:18:01 -0700336 mSelectedContactUri = uri;
Dmitri Plotnikov580286b2010-08-14 18:06:18 -0700337 parseSelectedContactUri();
Dmitri Plotnikov7ccdc1f2010-08-02 18:18:01 -0700338
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800339 if (!willReloadData) {
340 // Configure the adapter to show the selection based on the
341 // lookup key extracted from the URI
342 ContactListAdapter adapter = getAdapter();
343 if (adapter != null) {
Dmitri Plotnikov7d402242011-01-09 16:52:08 -0800344 adapter.setSelectedContact(mSelectedContactDirectoryId,
345 mSelectedContactLookupKey, mSelectedContactId);
Dmitri Plotnikov2f28eda2010-12-15 16:49:01 -0800346 getListView().invalidateViews();
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800347 }
348 }
349
Dmitri Plotnikov892fb512010-12-15 16:25:16 -0800350 // Also, launch a loader to pick up a new lookup URI in case it has changed
351 refreshSelectedContactUri();
Dmitri Plotnikovcfc64892010-07-09 17:55:09 -0700352 }
353 }
354
Dmitri Plotnikov580286b2010-08-14 18:06:18 -0700355 private void parseSelectedContactUri() {
356 if (mSelectedContactUri != null) {
Dmitri Plotnikov90715b32010-11-23 10:57:23 -0800357 String directoryParam =
Dmitri Plotnikov892fb512010-12-15 16:25:16 -0800358 mSelectedContactUri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY);
Dmitri Plotnikov90715b32010-11-23 10:57:23 -0800359 mSelectedContactDirectoryId = TextUtils.isEmpty(directoryParam) ? Directory.DEFAULT
360 : Long.parseLong(directoryParam);
361 if (mSelectedContactUri.toString().startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) {
Dmitri Plotnikov892fb512010-12-15 16:25:16 -0800362 List<String> pathSegments = mSelectedContactUri.getPathSegments();
363 mSelectedContactLookupKey = Uri.encode(pathSegments.get(2));
Dmitri Plotnikovb906cbe2011-01-18 11:14:05 -0800364 if (pathSegments.size() == 4) {
Dmitri Plotnikov0c708092011-01-16 16:40:34 -0800365 mSelectedContactId = ContentUris.parseId(mSelectedContactUri);
366 }
Dmitri Plotnikovfa49a662011-01-13 11:04:56 -0800367 } else if (mSelectedContactUri.toString().startsWith(Contacts.CONTENT_URI.toString()) &&
368 mSelectedContactUri.getPathSegments().size() >= 2) {
Dmitri Plotnikov90715b32010-11-23 10:57:23 -0800369 mSelectedContactLookupKey = null;
Dmitri Plotnikov7d402242011-01-09 16:52:08 -0800370 mSelectedContactId = ContentUris.parseId(mSelectedContactUri);
371 } else {
372 Log.e(TAG, "Unsupported contact URI: " + mSelectedContactUri);
373 mSelectedContactLookupKey = null;
374 mSelectedContactId = 0;
Dmitri Plotnikov580286b2010-08-14 18:06:18 -0700375 }
376
Dmitri Plotnikov580286b2010-08-14 18:06:18 -0700377 } else {
378 mSelectedContactDirectoryId = Directory.DEFAULT;
379 mSelectedContactLookupKey = null;
Dmitri Plotnikov7d402242011-01-09 16:52:08 -0800380 mSelectedContactId = 0;
Dmitri Plotnikov580286b2010-08-14 18:06:18 -0700381 }
382 }
383
Dmitri Plotnikov7ccdc1f2010-08-02 18:18:01 -0700384 @Override
Walter Jange9ea4f02016-05-10 09:39:46 -0700385 public ContactListAdapter getAdapter() {
386 return (ContactListAdapter) super.getAdapter();
387 }
388
389 @Override
Dmitri Plotnikov7ccdc1f2010-08-02 18:18:01 -0700390 protected void configureAdapter() {
391 super.configureAdapter();
Dmitri Plotnikov7ccdc1f2010-08-02 18:18:01 -0700392
Dmitri Plotnikov7ccdc1f2010-08-02 18:18:01 -0700393 ContactListAdapter adapter = getAdapter();
394 if (adapter == null) {
395 return;
396 }
397
Isaac Katzenelson8fdf8902011-08-10 21:00:54 -0700398 boolean searchMode = isSearchMode();
399 if (!searchMode && mFilter != null) {
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800400 adapter.setFilter(mFilter);
Dmitri Plotnikov3c8c3e82010-12-22 17:09:21 -0800401 if (mSelectionRequired
402 || mFilter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) {
Dmitri Plotnikov7d402242011-01-09 16:52:08 -0800403 adapter.setSelectedContact(
404 mSelectedContactDirectoryId, mSelectedContactLookupKey, mSelectedContactId);
Dmitri Plotnikov93a05db2010-12-22 15:23:54 -0800405 }
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800406 }
Dave Santoro0a4d2252011-05-20 11:40:34 -0700407
Wenyi Wang65751292016-09-01 15:23:25 -0700408 adapter.setIncludeFavorites(!searchMode && mFilter.isContactsFilterType());
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -0700409 }
410
411 @Override
Dmitri Plotnikov174fdc22010-10-29 13:55:01 -0700412 public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
413 super.onLoadFinished(loader, data);
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800414 mSelectionVerified = false;
Dmitri Plotnikov892fb512010-12-15 16:25:16 -0800415
416 // Refresh the currently selected lookup in case it changed while we were sleeping
417 refreshSelectedContactUri();
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -0700418 }
419
Dmitri Plotnikov0cd01302010-12-17 18:41:57 -0800420 @Override
Dianne Hackborn4ef95cc2010-12-16 00:44:33 -0800421 public void onLoaderReset(Loader<Cursor> loader) {
422 }
423
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -0700424 private void checkSelection() {
Dmitri Plotnikov174fdc22010-10-29 13:55:01 -0700425 if (mSelectionVerified) {
426 return;
427 }
428
Dmitri Plotnikov93a05db2010-12-22 15:23:54 -0800429 if (mRefreshingContactUri) {
430 return;
431 }
432
433 if (isLoadingDirectoryList()) {
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -0700434 return;
435 }
436
437 ContactListAdapter adapter = getAdapter();
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800438 if (adapter == null) {
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800439 return;
440 }
441
Dmitri Plotnikov93a05db2010-12-22 15:23:54 -0800442 boolean directoryLoading = true;
443 int count = adapter.getPartitionCount();
444 for (int i = 0; i < count; i++) {
445 Partition partition = adapter.getPartition(i);
446 if (partition instanceof DirectoryPartition) {
447 DirectoryPartition directory = (DirectoryPartition) partition;
448 if (directory.getDirectoryId() == mSelectedContactDirectoryId) {
449 directoryLoading = directory.isLoading();
450 break;
451 }
452 }
453 }
454
455 if (directoryLoading) {
456 return;
457 }
458
Dmitri Plotnikov7d402242011-01-09 16:52:08 -0800459 adapter.setSelectedContact(
460 mSelectedContactDirectoryId, mSelectedContactLookupKey, mSelectedContactId);
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800461
Katherine Kuane1cabcd2011-11-11 20:40:20 -0800462 final int selectedPosition = adapter.getSelectedContactPosition();
Dmitri Plotnikov682e1522011-01-16 17:04:32 -0800463 if (selectedPosition != -1) {
464 mLastSelectedPosition = selectedPosition;
465 } else {
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800466 if (isSearchMode()) {
Dmitri Plotnikov52518842011-01-19 10:39:58 -0800467 if (mDelaySelection) {
468 selectFirstFoundContactAfterDelay();
469 if (mListener != null) {
470 mListener.onSelectionChange();
471 }
472 return;
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800473 }
Dmitri Plotnikov52518842011-01-19 10:39:58 -0800474 } else if (mSelectionRequired) {
Dmitri Plotnikov8fe78212011-01-16 17:58:42 -0800475 // A specific contact was requested, but it's not in the loaded list.
476
477 // Try reconfiguring and reloading the list that will hopefully contain
478 // the requested contact. Only take one attempt to avoid an infinite loop
479 // in case the contact cannot be found at all.
480 mSelectionRequired = false;
481
482 // If we were looking at a different specific contact, just reload
Chiao Cheng3af2d482012-11-28 15:11:36 -0800483 // FILTER_TYPE_ALL_ACCOUNTS is needed for the case where a new contact is added
484 // on a tablet and the loader is returning a stale list. In this case, the contact
485 // will not be found until the next load. b/7621855 This will only fix the most
486 // common case where all accounts are shown. It will not fix the one account case.
487 // TODO: we may want to add more FILTER_TYPEs or relax this check to fix all other
488 // FILTER_TYPE cases.
Dmitri Plotnikov8fe78212011-01-16 17:58:42 -0800489 if (mFilter != null
Chiao Cheng3af2d482012-11-28 15:11:36 -0800490 && (mFilter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT
491 || mFilter.filterType == ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS)) {
Dmitri Plotnikov8fe78212011-01-16 17:58:42 -0800492 reloadData();
493 } else {
494 // Otherwise, call the listener, which will adjust the filter.
495 notifyInvalidSelection();
496 }
497 return;
Dmitri Plotnikov52518842011-01-19 10:39:58 -0800498 } else if (mFilter != null
Dmitri Plotnikov8fe78212011-01-16 17:58:42 -0800499 && mFilter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) {
Dmitri Plotnikov52518842011-01-19 10:39:58 -0800500 // If we were trying to load a specific contact, but that contact no longer
501 // exists, call the listener, which will adjust the filter.
Dmitri Plotnikov8fe78212011-01-16 17:58:42 -0800502 notifyInvalidSelection();
503 return;
504 }
505
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800506 saveSelectedUri(null);
507 selectDefaultContact();
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800508 }
509
510 mSelectionRequired = false;
511 mSelectionVerified = true;
512
513 if (mSelectionPersistenceRequested) {
514 saveSelectedUri(mSelectedContactUri);
515 mSelectionPersistenceRequested = false;
516 }
517
518 if (mSelectionToScreenRequested) {
Katherine Kuane1cabcd2011-11-11 20:40:20 -0800519 requestSelectionToScreen(selectedPosition);
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800520 }
521
Dmitri Plotnikov2f28eda2010-12-15 16:49:01 -0800522 getListView().invalidateViews();
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800523
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800524 if (mListener != null) {
525 mListener.onSelectionChange();
526 }
527 }
528
529 /**
530 * Automatically selects the first found contact in search mode. The selection
531 * is updated after a delay to allow the user to type without to much UI churn
532 * and to save bandwidth on directory queries.
533 */
534 public void selectFirstFoundContactAfterDelay() {
535 Handler handler = getHandler();
536 handler.removeMessages(MESSAGE_AUTOSELECT_FIRST_FOUND_CONTACT);
537
538 String queryString = getQueryString();
539 if (queryString != null
540 && queryString.length() >= AUTOSELECT_FIRST_FOUND_CONTACT_MIN_QUERY_LENGTH) {
541 handler.sendEmptyMessageDelayed(MESSAGE_AUTOSELECT_FIRST_FOUND_CONTACT,
542 DELAY_AUTOSELECT_FIRST_FOUND_CONTACT_MILLIS);
543 } else {
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800544 setSelectedContactUri(null, false, false, false, false);
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800545 }
546 }
547
548 protected void selectDefaultContact() {
Dmitri Plotnikov682e1522011-01-16 17:04:32 -0800549 Uri contactUri = null;
Dmitri Plotnikov07bf52d2011-02-22 18:46:40 -0800550 ContactListAdapter adapter = getAdapter();
Dmitri Plotnikov682e1522011-01-16 17:04:32 -0800551 if (mLastSelectedPosition != -1) {
Dmitri Plotnikov07bf52d2011-02-22 18:46:40 -0800552 int count = adapter.getCount();
553 int pos = mLastSelectedPosition;
554 if (pos >= count && count > 0) {
555 pos = count - 1;
556 }
557 contactUri = adapter.getContactUri(pos);
Dmitri Plotnikov682e1522011-01-16 17:04:32 -0800558 }
559
560 if (contactUri == null) {
Dmitri Plotnikov07bf52d2011-02-22 18:46:40 -0800561 contactUri = adapter.getFirstContactUri();
Dmitri Plotnikov682e1522011-01-16 17:04:32 -0800562 }
563
564 setSelectedContactUri(contactUri, false, mSmoothScrollRequested, false, false);
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800565 }
566
Katherine Kuane1cabcd2011-11-11 20:40:20 -0800567 protected void requestSelectionToScreen(int selectedPosition) {
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800568 if (selectedPosition != -1) {
Dmitri Plotnikov753f8f62010-12-15 11:10:27 -0800569 AutoScrollListView listView = (AutoScrollListView)getListView();
570 listView.requestPositionToScreen(
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800571 selectedPosition + listView.getHeaderViewsCount(), mSmoothScrollRequested);
572 mSelectionToScreenRequested = false;
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -0700573 }
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -0700574 }
575
Dmitri Plotnikov174fdc22010-10-29 13:55:01 -0700576 @Override
577 public boolean isLoading() {
Dmitri Plotnikov892fb512010-12-15 16:25:16 -0800578 return mRefreshingContactUri || super.isLoading();
Dmitri Plotnikov174fdc22010-10-29 13:55:01 -0700579 }
580
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -0700581 @Override
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800582 protected void startLoading() {
583 mStartedLoading = true;
584 mSelectionVerified = false;
585 super.startLoading();
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -0700586 }
587
Dmitri Plotnikova5a27442010-12-27 21:06:38 -0800588 public void reloadDataAndSetSelectedUri(Uri uri) {
589 setSelectedContactUri(uri, true, true, true, true);
590 reloadData();
591 }
592
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -0700593 @Override
594 public void reloadData() {
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800595 if (mStartedLoading) {
596 mSelectionVerified = false;
Dmitri Plotnikov04bad512011-01-17 12:43:01 -0800597 mLastSelectedPosition = -1;
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800598 super.reloadData();
599 }
Dmitri Plotnikov7ccdc1f2010-08-02 18:18:01 -0700600 }
601
Dmitri Plotnikov416b5692010-04-27 18:14:58 -0700602 public void setOnContactListActionListener(OnContactBrowserActionListener listener) {
Dmitri Plotnikov732f6522010-04-27 10:30:59 -0700603 mListener = listener;
604 }
605
Wenyi Wang2b943992016-05-20 17:21:35 -0700606 public void viewContact(int position, Uri contactUri, boolean isEnterpriseContact) {
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800607 setSelectedContactUri(contactUri, false, false, true, false);
Wenyi Wang2b943992016-05-20 17:21:35 -0700608 if (mListener != null) mListener.onViewContactAction(position, contactUri,
609 isEnterpriseContact);
Dmitri Plotnikov1323d692010-04-27 14:54:27 -0700610 }
Dmitri Plotnikov732f6522010-04-27 10:30:59 -0700611
Dmitri Plotnikov1323d692010-04-27 14:54:27 -0700612 public void deleteContact(Uri contactUri) {
Dmitri Plotnikov71521242010-11-17 18:47:14 -0800613 if (mListener != null) mListener.onDeleteContactAction(contactUri);
Dmitri Plotnikov1323d692010-04-27 14:54:27 -0700614 }
615
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -0700616 private void notifyInvalidSelection() {
Dmitri Plotnikov71521242010-11-17 18:47:14 -0800617 if (mListener != null) mListener.onInvalidSelection();
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -0700618 }
619
Dmitri Plotnikove3fbfd92010-04-28 11:20:52 -0700620 @Override
621 protected void finish() {
622 super.finish();
Dmitri Plotnikov71521242010-11-17 18:47:14 -0800623 if (mListener != null) mListener.onFinishAction();
Dmitri Plotnikove3fbfd92010-04-28 11:20:52 -0700624 }
Dmitri Plotnikov8c6955f2010-09-08 20:46:36 -0700625
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800626 private void saveSelectedUri(Uri contactUri) {
Dmitri Plotnikovfa49a662011-01-13 11:04:56 -0800627 if (isSearchMode()) {
Dmitri Plotnikov8c6955f2010-09-08 20:46:36 -0700628 return;
629 }
630
Dmitri Plotnikovfa49a662011-01-13 11:04:56 -0800631 ContactListFilter.storeToPreferences(mPrefs, mFilter);
632
633 Editor editor = mPrefs.edit();
634 if (contactUri == null) {
635 editor.remove(getPersistentSelectionKey());
636 } else {
637 editor.putString(getPersistentSelectionKey(), contactUri.toString());
638 }
639 editor.apply();
640 }
641
642 private void restoreSelectedUri(boolean willReloadData) {
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800643 // The meaning of mSelectionRequired is that we need to show some
644 // selection other than the previous selection saved in shared preferences
645 if (mSelectionRequired) {
Dmitri Plotnikov8c6955f2010-09-08 20:46:36 -0700646 return;
647 }
648
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800649 String selectedUri = mPrefs.getString(getPersistentSelectionKey(), null);
650 if (selectedUri == null) {
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800651 setSelectedContactUri(null, false, false, false, willReloadData);
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800652 } else {
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800653 setSelectedContactUri(Uri.parse(selectedUri), false, false, false, willReloadData);
654 }
655 }
656
657 private void saveFilter() {
Dmitri Plotnikovfa49a662011-01-13 11:04:56 -0800658 ContactListFilter.storeToPreferences(mPrefs, mFilter);
Dmitri Plotnikov264023c2010-12-15 12:04:13 -0800659 }
660
661 private void restoreFilter() {
Daisuke Miyakawad4ed72e2011-08-12 18:16:01 -0700662 mFilter = ContactListFilter.restoreDefaultPreferences(mPrefs);
Dmitri Plotnikov8c6955f2010-09-08 20:46:36 -0700663 }
Dmitri Plotnikov6e850a02010-10-28 10:46:21 -0700664
Dmitri Plotnikovd9591042010-11-23 09:25:03 -0800665 private String getPersistentSelectionKey() {
666 if (mFilter == null) {
667 return mPersistentSelectionPrefix;
668 } else {
669 return mPersistentSelectionPrefix + "-" + mFilter.getId();
670 }
Dmitri Plotnikov30cc8532010-11-11 20:30:48 -0800671 }
Dmitri Plotnikov732f6522010-04-27 10:30:59 -0700672}