• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.common.widget.CompositeCursorAdapter.Partition;
19 import com.android.contacts.R;
20 import com.android.contacts.util.ContactLoaderUtils;
21 import com.android.contacts.widget.AutoScrollListView;
22 
23 import android.app.Activity;
24 import android.content.ContentResolver;
25 import android.content.ContentUris;
26 import android.content.Loader;
27 import android.content.SharedPreferences;
28 import android.content.SharedPreferences.Editor;
29 import android.database.Cursor;
30 import android.net.Uri;
31 import android.os.AsyncTask;
32 import android.os.Bundle;
33 import android.os.Handler;
34 import android.os.Message;
35 import android.preference.PreferenceManager;
36 import android.provider.ContactsContract;
37 import android.provider.ContactsContract.Contacts;
38 import android.provider.ContactsContract.Directory;
39 import android.text.TextUtils;
40 import android.util.Log;
41 
42 import java.util.List;
43 
44 /**
45  * Fragment containing a contact list used for browsing (as compared to
46  * picking a contact with one of the PICK intents).
47  */
48 public abstract class ContactBrowseListFragment extends
49         ContactEntryListFragment<ContactListAdapter> {
50 
51     private static final String TAG = "ContactList";
52 
53     private static final String KEY_SELECTED_URI = "selectedUri";
54     private static final String KEY_SELECTION_VERIFIED = "selectionVerified";
55     private static final String KEY_FILTER = "filter";
56     private static final String KEY_LAST_SELECTED_POSITION = "lastSelected";
57 
58     private static final String PERSISTENT_SELECTION_PREFIX = "defaultContactBrowserSelection";
59 
60     /**
61      * The id for a delayed message that triggers automatic selection of the first
62      * found contact in search mode.
63      */
64     private static final int MESSAGE_AUTOSELECT_FIRST_FOUND_CONTACT = 1;
65 
66     /**
67      * The delay that is used for automatically selecting the first found contact.
68      */
69     private static final int DELAY_AUTOSELECT_FIRST_FOUND_CONTACT_MILLIS = 500;
70 
71     /**
72      * The minimum number of characters in the search query that is required
73      * before we automatically select the first found contact.
74      */
75     private static final int AUTOSELECT_FIRST_FOUND_CONTACT_MIN_QUERY_LENGTH = 2;
76 
77     private SharedPreferences mPrefs;
78     private Handler mHandler;
79 
80     private boolean mStartedLoading;
81     private boolean mSelectionRequired;
82     private boolean mSelectionToScreenRequested;
83     private boolean mSmoothScrollRequested;
84     private boolean mSelectionPersistenceRequested;
85     private Uri mSelectedContactUri;
86     private long mSelectedContactDirectoryId;
87     private String mSelectedContactLookupKey;
88     private long mSelectedContactId;
89     private boolean mSelectionVerified;
90     private int mLastSelectedPosition = -1;
91     private boolean mRefreshingContactUri;
92     private ContactListFilter mFilter;
93     private String mPersistentSelectionPrefix = PERSISTENT_SELECTION_PREFIX;
94 
95     protected OnContactBrowserActionListener mListener;
96     private ContactLookupTask mContactLookupTask;
97 
98     private final class ContactLookupTask extends AsyncTask<Void, Void, Uri> {
99 
100         private final Uri mUri;
101         private boolean mIsCancelled;
102 
ContactLookupTask(Uri uri)103         public ContactLookupTask(Uri uri) {
104             mUri = uri;
105         }
106 
107         @Override
doInBackground(Void... args)108         protected Uri doInBackground(Void... args) {
109             Cursor cursor = null;
110             try {
111                 final ContentResolver resolver = getContext().getContentResolver();
112                 final Uri uriCurrentFormat = ContactLoaderUtils.ensureIsContactUri(resolver, mUri);
113                 cursor = resolver.query(uriCurrentFormat,
114                         new String[] { Contacts._ID, Contacts.LOOKUP_KEY }, null, null, null);
115 
116                 if (cursor != null && cursor.moveToFirst()) {
117                     final long contactId = cursor.getLong(0);
118                     final String lookupKey = cursor.getString(1);
119                     if (contactId != 0 && !TextUtils.isEmpty(lookupKey)) {
120                         return Contacts.getLookupUri(contactId, lookupKey);
121                     }
122                 }
123 
124                 Log.e(TAG, "Error: No contact ID or lookup key for contact " + mUri);
125                 return null;
126             } finally {
127                 if (cursor != null) {
128                     cursor.close();
129                 }
130             }
131         }
132 
cancel()133         public void cancel() {
134             super.cancel(true);
135             // Use a flag to keep track of whether the {@link AsyncTask} was cancelled or not in
136             // order to ensure onPostExecute() is not executed after the cancel request. The flag is
137             // necessary because {@link AsyncTask} still calls onPostExecute() if the cancel request
138             // came after the worker thread was finished.
139             mIsCancelled = true;
140         }
141 
142         @Override
onPostExecute(Uri uri)143         protected void onPostExecute(Uri uri) {
144             // Make sure the {@link Fragment} is at least still attached to the {@link Activity}
145             // before continuing. Null URIs should still be allowed so that the list can be
146             // refreshed and a default contact can be selected (i.e. the case of deleted
147             // contacts).
148             if (mIsCancelled || !isAdded()) {
149                 return;
150             }
151             onContactUriQueryFinished(uri);
152         }
153     }
154 
155     private boolean mDelaySelection;
156 
getHandler()157     private Handler getHandler() {
158         if (mHandler == null) {
159             mHandler = new Handler() {
160                 @Override
161                 public void handleMessage(Message msg) {
162                     switch (msg.what) {
163                         case MESSAGE_AUTOSELECT_FIRST_FOUND_CONTACT:
164                             selectDefaultContact();
165                             break;
166                     }
167                 }
168             };
169         }
170         return mHandler;
171     }
172 
173     @Override
onAttach(Activity activity)174     public void onAttach(Activity activity) {
175         super.onAttach(activity);
176         mPrefs = PreferenceManager.getDefaultSharedPreferences(activity);
177         restoreFilter();
178         restoreSelectedUri(false);
179     }
180 
181     @Override
setSearchMode(boolean flag)182     protected void setSearchMode(boolean flag) {
183         if (isSearchMode() != flag) {
184             if (!flag) {
185                 restoreSelectedUri(true);
186             }
187             super.setSearchMode(flag);
188         }
189     }
190 
setFilter(ContactListFilter filter)191     public void setFilter(ContactListFilter filter) {
192         setFilter(filter, true);
193     }
194 
setFilter(ContactListFilter filter, boolean restoreSelectedUri)195     public void setFilter(ContactListFilter filter, boolean restoreSelectedUri) {
196         if (mFilter == null && filter == null) {
197             return;
198         }
199 
200         if (mFilter != null && mFilter.equals(filter)) {
201             return;
202         }
203 
204         Log.v(TAG, "New filter: " + filter);
205 
206         mFilter = filter;
207         mLastSelectedPosition = -1;
208         saveFilter();
209         if (restoreSelectedUri) {
210             mSelectedContactUri = null;
211             restoreSelectedUri(true);
212         }
213         reloadData();
214     }
215 
getFilter()216     public ContactListFilter getFilter() {
217         return mFilter;
218     }
219 
220     @Override
restoreSavedState(Bundle savedState)221     public void restoreSavedState(Bundle savedState) {
222         super.restoreSavedState(savedState);
223 
224         if (savedState == null) {
225             return;
226         }
227 
228         mFilter = savedState.getParcelable(KEY_FILTER);
229         mSelectedContactUri = savedState.getParcelable(KEY_SELECTED_URI);
230         mSelectionVerified = savedState.getBoolean(KEY_SELECTION_VERIFIED);
231         mLastSelectedPosition = savedState.getInt(KEY_LAST_SELECTED_POSITION);
232         parseSelectedContactUri();
233     }
234 
235     @Override
onSaveInstanceState(Bundle outState)236     public void onSaveInstanceState(Bundle outState) {
237         super.onSaveInstanceState(outState);
238         outState.putParcelable(KEY_FILTER, mFilter);
239         outState.putParcelable(KEY_SELECTED_URI, mSelectedContactUri);
240         outState.putBoolean(KEY_SELECTION_VERIFIED, mSelectionVerified);
241         outState.putInt(KEY_LAST_SELECTED_POSITION, mLastSelectedPosition);
242     }
243 
refreshSelectedContactUri()244     protected void refreshSelectedContactUri() {
245         if (mContactLookupTask != null) {
246             mContactLookupTask.cancel();
247         }
248 
249         if (!isSelectionVisible()) {
250             return;
251         }
252 
253         mRefreshingContactUri = true;
254 
255         if (mSelectedContactUri == null) {
256             onContactUriQueryFinished(null);
257             return;
258         }
259 
260         if (mSelectedContactDirectoryId != Directory.DEFAULT
261                 && mSelectedContactDirectoryId != Directory.LOCAL_INVISIBLE) {
262             onContactUriQueryFinished(mSelectedContactUri);
263         } else {
264             mContactLookupTask = new ContactLookupTask(mSelectedContactUri);
265             mContactLookupTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null);
266         }
267     }
268 
onContactUriQueryFinished(Uri uri)269     protected void onContactUriQueryFinished(Uri uri) {
270         mRefreshingContactUri = false;
271         mSelectedContactUri = uri;
272         parseSelectedContactUri();
273         checkSelection();
274     }
275 
276     @Override
prepareEmptyView()277     protected void prepareEmptyView() {
278         if (isSearchMode()) {
279             return;
280         } else if (isSyncActive()) {
281             if (hasIccCard()) {
282                 setEmptyText(R.string.noContactsHelpTextWithSync);
283             } else {
284                 setEmptyText(R.string.noContactsNoSimHelpTextWithSync);
285             }
286         } else {
287             if (hasIccCard()) {
288                 setEmptyText(R.string.noContactsHelpText);
289             } else {
290                 setEmptyText(R.string.noContactsNoSimHelpText);
291             }
292         }
293     }
294 
getSelectedContactUri()295     public Uri getSelectedContactUri() {
296         return mSelectedContactUri;
297     }
298 
299     /**
300      * Sets the new selection for the list.
301      */
setSelectedContactUri(Uri uri)302     public void setSelectedContactUri(Uri uri) {
303         setSelectedContactUri(uri, true, false /* no smooth scroll */, true, false);
304     }
305 
306     @Override
setQueryString(String queryString, boolean delaySelection)307     public void setQueryString(String queryString, boolean delaySelection) {
308         mDelaySelection = delaySelection;
309         super.setQueryString(queryString, delaySelection);
310     }
311 
312     /**
313      * Sets whether or not a contact selection must be made.
314      * @param required if true, we need to check if the selection is present in
315      *            the list and if not notify the listener so that it can load a
316      *            different list.
317      * TODO: Figure out how to reconcile this with {@link #setSelectedContactUri},
318      * without causing unnecessary loading of the list if the selected contact URI is
319      * the same as before.
320      */
setSelectionRequired(boolean required)321     public void setSelectionRequired(boolean required) {
322         mSelectionRequired = required;
323     }
324 
325     /**
326      * Sets the new contact selection.
327      *
328      * @param uri the new selection
329      * @param required if true, we need to check if the selection is present in
330      *            the list and if not notify the listener so that it can load a
331      *            different list
332      * @param smoothScroll if true, the UI will roll smoothly to the new
333      *            selection
334      * @param persistent if true, the selection will be stored in shared
335      *            preferences.
336      * @param willReloadData if true, the selection will be remembered but not
337      *            actually shown, because we are expecting that the data will be
338      *            reloaded momentarily
339      */
setSelectedContactUri(Uri uri, boolean required, boolean smoothScroll, boolean persistent, boolean willReloadData)340     private void setSelectedContactUri(Uri uri, boolean required, boolean smoothScroll,
341             boolean persistent, boolean willReloadData) {
342         mSmoothScrollRequested = smoothScroll;
343         mSelectionToScreenRequested = true;
344 
345         if ((mSelectedContactUri == null && uri != null)
346                 || (mSelectedContactUri != null && !mSelectedContactUri.equals(uri))) {
347             mSelectionVerified = false;
348             mSelectionRequired = required;
349             mSelectionPersistenceRequested = persistent;
350             mSelectedContactUri = uri;
351             parseSelectedContactUri();
352 
353             if (!willReloadData) {
354                 // Configure the adapter to show the selection based on the
355                 // lookup key extracted from the URI
356                 ContactListAdapter adapter = getAdapter();
357                 if (adapter != null) {
358                     adapter.setSelectedContact(mSelectedContactDirectoryId,
359                             mSelectedContactLookupKey, mSelectedContactId);
360                     getListView().invalidateViews();
361                 }
362             }
363 
364             // Also, launch a loader to pick up a new lookup URI in case it has changed
365             refreshSelectedContactUri();
366         }
367     }
368 
parseSelectedContactUri()369     private void parseSelectedContactUri() {
370         if (mSelectedContactUri != null) {
371             String directoryParam =
372                     mSelectedContactUri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY);
373             mSelectedContactDirectoryId = TextUtils.isEmpty(directoryParam) ? Directory.DEFAULT
374                     : Long.parseLong(directoryParam);
375             if (mSelectedContactUri.toString().startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) {
376                 List<String> pathSegments = mSelectedContactUri.getPathSegments();
377                 mSelectedContactLookupKey = Uri.encode(pathSegments.get(2));
378                 if (pathSegments.size() == 4) {
379                     mSelectedContactId = ContentUris.parseId(mSelectedContactUri);
380                 }
381             } else if (mSelectedContactUri.toString().startsWith(Contacts.CONTENT_URI.toString()) &&
382                     mSelectedContactUri.getPathSegments().size() >= 2) {
383                 mSelectedContactLookupKey = null;
384                 mSelectedContactId = ContentUris.parseId(mSelectedContactUri);
385             } else {
386                 Log.e(TAG, "Unsupported contact URI: " + mSelectedContactUri);
387                 mSelectedContactLookupKey = null;
388                 mSelectedContactId = 0;
389             }
390 
391         } else {
392             mSelectedContactDirectoryId = Directory.DEFAULT;
393             mSelectedContactLookupKey = null;
394             mSelectedContactId = 0;
395         }
396     }
397 
398     @Override
configureAdapter()399     protected void configureAdapter() {
400         super.configureAdapter();
401 
402         ContactListAdapter adapter = getAdapter();
403         if (adapter == null) {
404             return;
405         }
406 
407         boolean searchMode = isSearchMode();
408         if (!searchMode && mFilter != null) {
409             adapter.setFilter(mFilter);
410             if (mSelectionRequired
411                     || mFilter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) {
412                 adapter.setSelectedContact(
413                         mSelectedContactDirectoryId, mSelectedContactLookupKey, mSelectedContactId);
414             }
415         }
416 
417         // Display the user's profile if not in search mode
418         adapter.setIncludeProfile(!searchMode);
419     }
420 
421     @Override
onLoadFinished(Loader<Cursor> loader, Cursor data)422     public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
423         super.onLoadFinished(loader, data);
424         mSelectionVerified = false;
425 
426         // Refresh the currently selected lookup in case it changed while we were sleeping
427         refreshSelectedContactUri();
428     }
429 
430     @Override
onLoaderReset(Loader<Cursor> loader)431     public void onLoaderReset(Loader<Cursor> loader) {
432     }
433 
checkSelection()434     private void checkSelection() {
435         if (mSelectionVerified) {
436             return;
437         }
438 
439         if (mRefreshingContactUri) {
440             return;
441         }
442 
443         if (isLoadingDirectoryList()) {
444             return;
445         }
446 
447         ContactListAdapter adapter = getAdapter();
448         if (adapter == null) {
449             return;
450         }
451 
452         boolean directoryLoading = true;
453         int count = adapter.getPartitionCount();
454         for (int i = 0; i < count; i++) {
455             Partition partition = adapter.getPartition(i);
456             if (partition instanceof DirectoryPartition) {
457                 DirectoryPartition directory = (DirectoryPartition) partition;
458                 if (directory.getDirectoryId() == mSelectedContactDirectoryId) {
459                     directoryLoading = directory.isLoading();
460                     break;
461                 }
462             }
463         }
464 
465         if (directoryLoading) {
466             return;
467         }
468 
469         adapter.setSelectedContact(
470                 mSelectedContactDirectoryId, mSelectedContactLookupKey, mSelectedContactId);
471 
472         final int selectedPosition = adapter.getSelectedContactPosition();
473         if (selectedPosition != -1) {
474             mLastSelectedPosition = selectedPosition;
475         } else {
476             if (isSearchMode()) {
477                 if (mDelaySelection) {
478                     selectFirstFoundContactAfterDelay();
479                     if (mListener != null) {
480                         mListener.onSelectionChange();
481                     }
482                     return;
483                 }
484             } else if (mSelectionRequired) {
485                 // A specific contact was requested, but it's not in the loaded list.
486 
487                 // Try reconfiguring and reloading the list that will hopefully contain
488                 // the requested contact. Only take one attempt to avoid an infinite loop
489                 // in case the contact cannot be found at all.
490                 mSelectionRequired = false;
491 
492                 // If we were looking at a different specific contact, just reload
493                 if (mFilter != null
494                         && mFilter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) {
495                     reloadData();
496                 } else {
497                     // Otherwise, call the listener, which will adjust the filter.
498                     notifyInvalidSelection();
499                 }
500                 return;
501             } else if (mFilter != null
502                     && mFilter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) {
503                 // If we were trying to load a specific contact, but that contact no longer
504                 // exists, call the listener, which will adjust the filter.
505                 notifyInvalidSelection();
506                 return;
507             }
508 
509             saveSelectedUri(null);
510             selectDefaultContact();
511         }
512 
513         mSelectionRequired = false;
514         mSelectionVerified = true;
515 
516         if (mSelectionPersistenceRequested) {
517             saveSelectedUri(mSelectedContactUri);
518             mSelectionPersistenceRequested = false;
519         }
520 
521         if (mSelectionToScreenRequested) {
522             requestSelectionToScreen(selectedPosition);
523         }
524 
525         getListView().invalidateViews();
526 
527         if (mListener != null) {
528             mListener.onSelectionChange();
529         }
530     }
531 
532     /**
533      * Automatically selects the first found contact in search mode.  The selection
534      * is updated after a delay to allow the user to type without to much UI churn
535      * and to save bandwidth on directory queries.
536      */
selectFirstFoundContactAfterDelay()537     public void selectFirstFoundContactAfterDelay() {
538         Handler handler = getHandler();
539         handler.removeMessages(MESSAGE_AUTOSELECT_FIRST_FOUND_CONTACT);
540 
541         String queryString = getQueryString();
542         if (queryString != null
543                 && queryString.length() >= AUTOSELECT_FIRST_FOUND_CONTACT_MIN_QUERY_LENGTH) {
544             handler.sendEmptyMessageDelayed(MESSAGE_AUTOSELECT_FIRST_FOUND_CONTACT,
545                     DELAY_AUTOSELECT_FIRST_FOUND_CONTACT_MILLIS);
546         } else {
547             setSelectedContactUri(null, false, false, false, false);
548         }
549     }
550 
selectDefaultContact()551     protected void selectDefaultContact() {
552         Uri contactUri = null;
553         ContactListAdapter adapter = getAdapter();
554         if (mLastSelectedPosition != -1) {
555             int count = adapter.getCount();
556             int pos = mLastSelectedPosition;
557             if (pos >= count && count > 0) {
558                 pos = count - 1;
559             }
560             contactUri = adapter.getContactUri(pos);
561         }
562 
563         if (contactUri == null) {
564             contactUri = adapter.getFirstContactUri();
565         }
566 
567         setSelectedContactUri(contactUri, false, mSmoothScrollRequested, false, false);
568     }
569 
requestSelectionToScreen(int selectedPosition)570     protected void requestSelectionToScreen(int selectedPosition) {
571         if (selectedPosition != -1) {
572             AutoScrollListView listView = (AutoScrollListView)getListView();
573             listView.requestPositionToScreen(
574                     selectedPosition + listView.getHeaderViewsCount(), mSmoothScrollRequested);
575             mSelectionToScreenRequested = false;
576         }
577     }
578 
579     @Override
isLoading()580     public boolean isLoading() {
581         return mRefreshingContactUri || super.isLoading();
582     }
583 
584     @Override
startLoading()585     protected void startLoading() {
586         mStartedLoading = true;
587         mSelectionVerified = false;
588         super.startLoading();
589     }
590 
reloadDataAndSetSelectedUri(Uri uri)591     public void reloadDataAndSetSelectedUri(Uri uri) {
592         setSelectedContactUri(uri, true, true, true, true);
593         reloadData();
594     }
595 
596     @Override
reloadData()597     public void reloadData() {
598         if (mStartedLoading) {
599             mSelectionVerified = false;
600             mLastSelectedPosition = -1;
601             super.reloadData();
602         }
603     }
604 
setOnContactListActionListener(OnContactBrowserActionListener listener)605     public void setOnContactListActionListener(OnContactBrowserActionListener listener) {
606         mListener = listener;
607     }
608 
createNewContact()609     public void createNewContact() {
610         if (mListener != null) mListener.onCreateNewContactAction();
611     }
612 
viewContact(Uri contactUri)613     public void viewContact(Uri contactUri) {
614         setSelectedContactUri(contactUri, false, false, true, false);
615         if (mListener != null) mListener.onViewContactAction(contactUri);
616     }
617 
editContact(Uri contactUri)618     public void editContact(Uri contactUri) {
619         if (mListener != null) mListener.onEditContactAction(contactUri);
620     }
621 
deleteContact(Uri contactUri)622     public void deleteContact(Uri contactUri) {
623         if (mListener != null) mListener.onDeleteContactAction(contactUri);
624     }
625 
addToFavorites(Uri contactUri)626     public void addToFavorites(Uri contactUri) {
627         if (mListener != null) mListener.onAddToFavoritesAction(contactUri);
628     }
629 
removeFromFavorites(Uri contactUri)630     public void removeFromFavorites(Uri contactUri) {
631         if (mListener != null) mListener.onRemoveFromFavoritesAction(contactUri);
632     }
633 
callContact(Uri contactUri)634     public void callContact(Uri contactUri) {
635         if (mListener != null) mListener.onCallContactAction(contactUri);
636     }
637 
smsContact(Uri contactUri)638     public void smsContact(Uri contactUri) {
639         if (mListener != null) mListener.onSmsContactAction(contactUri);
640     }
641 
notifyInvalidSelection()642     private void notifyInvalidSelection() {
643         if (mListener != null) mListener.onInvalidSelection();
644     }
645 
646     @Override
finish()647     protected void finish() {
648         super.finish();
649         if (mListener != null) mListener.onFinishAction();
650     }
651 
saveSelectedUri(Uri contactUri)652     private void saveSelectedUri(Uri contactUri) {
653         if (isSearchMode()) {
654             return;
655         }
656 
657         ContactListFilter.storeToPreferences(mPrefs, mFilter);
658 
659         Editor editor = mPrefs.edit();
660         if (contactUri == null) {
661             editor.remove(getPersistentSelectionKey());
662         } else {
663             editor.putString(getPersistentSelectionKey(), contactUri.toString());
664         }
665         editor.apply();
666     }
667 
restoreSelectedUri(boolean willReloadData)668     private void restoreSelectedUri(boolean willReloadData) {
669         // The meaning of mSelectionRequired is that we need to show some
670         // selection other than the previous selection saved in shared preferences
671         if (mSelectionRequired) {
672             return;
673         }
674 
675         String selectedUri = mPrefs.getString(getPersistentSelectionKey(), null);
676         if (selectedUri == null) {
677             setSelectedContactUri(null, false, false, false, willReloadData);
678         } else {
679             setSelectedContactUri(Uri.parse(selectedUri), false, false, false, willReloadData);
680         }
681     }
682 
saveFilter()683     private void saveFilter() {
684         ContactListFilter.storeToPreferences(mPrefs, mFilter);
685     }
686 
restoreFilter()687     private void restoreFilter() {
688         mFilter = ContactListFilter.restoreDefaultPreferences(mPrefs);
689     }
690 
getPersistentSelectionKey()691     private String getPersistentSelectionKey() {
692         if (mFilter == null) {
693             return mPersistentSelectionPrefix;
694         } else {
695             return mPersistentSelectionPrefix + "-" + mFilter.getId();
696         }
697     }
698 
isOptionsMenuChanged()699     public boolean isOptionsMenuChanged() {
700         // This fragment does not have an option menu of its own
701         return false;
702     }
703 }
704