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