Home | History | Annotate | Download | only in list
      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 
    103         public ContactLookupTask(Uri uri) {
    104             mUri = uri;
    105         }
    106 
    107         @Override
    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 
    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
    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 
    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
    174     public void onAttach(Activity activity) {
    175         super.onAttach(activity);
    176         mPrefs = PreferenceManager.getDefaultSharedPreferences(activity);
    177         restoreFilter();
    178         restoreSelectedUri(false);
    179     }
    180 
    181     @Override
    182     protected void setSearchMode(boolean flag) {
    183         if (isSearchMode() != flag) {
    184             if (!flag) {
    185                 restoreSelectedUri(true);
    186             }
    187             super.setSearchMode(flag);
    188         }
    189     }
    190 
    191     public void setFilter(ContactListFilter filter) {
    192         setFilter(filter, true);
    193     }
    194 
    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 
    216     public ContactListFilter getFilter() {
    217         return mFilter;
    218     }
    219 
    220     @Override
    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
    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 
    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 
    269     protected void onContactUriQueryFinished(Uri uri) {
    270         mRefreshingContactUri = false;
    271         mSelectedContactUri = uri;
    272         parseSelectedContactUri();
    273         checkSelection();
    274     }
    275 
    276     @Override
    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 
    295     public Uri getSelectedContactUri() {
    296         return mSelectedContactUri;
    297     }
    298 
    299     /**
    300      * Sets the new selection for the list.
    301      */
    302     public void setSelectedContactUri(Uri uri) {
    303         setSelectedContactUri(uri, true, false /* no smooth scroll */, true, false);
    304     }
    305 
    306     @Override
    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      */
    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      */
    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 
    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
    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
    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
    431     public void onLoaderReset(Loader<Cursor> loader) {
    432     }
    433 
    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      */
    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 
    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 
    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
    580     public boolean isLoading() {
    581         return mRefreshingContactUri || super.isLoading();
    582     }
    583 
    584     @Override
    585     protected void startLoading() {
    586         mStartedLoading = true;
    587         mSelectionVerified = false;
    588         super.startLoading();
    589     }
    590 
    591     public void reloadDataAndSetSelectedUri(Uri uri) {
    592         setSelectedContactUri(uri, true, true, true, true);
    593         reloadData();
    594     }
    595 
    596     @Override
    597     public void reloadData() {
    598         if (mStartedLoading) {
    599             mSelectionVerified = false;
    600             mLastSelectedPosition = -1;
    601             super.reloadData();
    602         }
    603     }
    604 
    605     public void setOnContactListActionListener(OnContactBrowserActionListener listener) {
    606         mListener = listener;
    607     }
    608 
    609     public void createNewContact() {
    610         if (mListener != null) mListener.onCreateNewContactAction();
    611     }
    612 
    613     public void viewContact(Uri contactUri) {
    614         setSelectedContactUri(contactUri, false, false, true, false);
    615         if (mListener != null) mListener.onViewContactAction(contactUri);
    616     }
    617 
    618     public void editContact(Uri contactUri) {
    619         if (mListener != null) mListener.onEditContactAction(contactUri);
    620     }
    621 
    622     public void deleteContact(Uri contactUri) {
    623         if (mListener != null) mListener.onDeleteContactAction(contactUri);
    624     }
    625 
    626     public void addToFavorites(Uri contactUri) {
    627         if (mListener != null) mListener.onAddToFavoritesAction(contactUri);
    628     }
    629 
    630     public void removeFromFavorites(Uri contactUri) {
    631         if (mListener != null) mListener.onRemoveFromFavoritesAction(contactUri);
    632     }
    633 
    634     public void callContact(Uri contactUri) {
    635         if (mListener != null) mListener.onCallContactAction(contactUri);
    636     }
    637 
    638     public void smsContact(Uri contactUri) {
    639         if (mListener != null) mListener.onSmsContactAction(contactUri);
    640     }
    641 
    642     private void notifyInvalidSelection() {
    643         if (mListener != null) mListener.onInvalidSelection();
    644     }
    645 
    646     @Override
    647     protected void finish() {
    648         super.finish();
    649         if (mListener != null) mListener.onFinishAction();
    650     }
    651 
    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 
    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 
    683     private void saveFilter() {
    684         ContactListFilter.storeToPreferences(mPrefs, mFilter);
    685     }
    686 
    687     private void restoreFilter() {
    688         mFilter = ContactListFilter.restoreDefaultPreferences(mPrefs);
    689     }
    690 
    691     private String getPersistentSelectionKey() {
    692         if (mFilter == null) {
    693             return mPersistentSelectionPrefix;
    694         } else {
    695             return mPersistentSelectionPrefix + "-" + mFilter.getId();
    696         }
    697     }
    698 
    699     public boolean isOptionsMenuChanged() {
    700         // This fragment does not have an option menu of its own
    701         return false;
    702     }
    703 }
    704