Home | History | Annotate | Download | only in activities
      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 
     17 package com.android.contacts.activities;
     18 
     19 import android.app.Fragment;
     20 import android.app.FragmentManager;
     21 import android.app.FragmentTransaction;
     22 import android.content.ActivityNotFoundException;
     23 import android.content.ContentValues;
     24 import android.content.Intent;
     25 import android.graphics.Rect;
     26 import android.net.Uri;
     27 import android.os.Bundle;
     28 import android.os.Handler;
     29 import android.os.Parcelable;
     30 import android.os.UserManager;
     31 import android.preference.PreferenceActivity;
     32 import android.provider.ContactsContract;
     33 import android.provider.ContactsContract.Contacts;
     34 import android.provider.ContactsContract.ProviderStatus;
     35 import android.provider.ContactsContract.QuickContact;
     36 import android.provider.Settings;
     37 import android.support.v13.app.FragmentPagerAdapter;
     38 import android.support.v4.view.PagerAdapter;
     39 import android.support.v4.view.ViewPager;
     40 import android.text.TextUtils;
     41 import android.util.Log;
     42 import android.view.KeyCharacterMap;
     43 import android.view.KeyEvent;
     44 import android.view.Menu;
     45 import android.view.MenuInflater;
     46 import android.view.MenuItem;
     47 import android.view.MenuItem.OnMenuItemClickListener;
     48 import android.view.View;
     49 import android.view.ViewGroup;
     50 import android.widget.Toast;
     51 
     52 import com.android.contacts.ContactSaveService;
     53 import com.android.contacts.ContactsActivity;
     54 import com.android.contacts.ContactsUtils;
     55 import com.android.contacts.R;
     56 import com.android.contacts.activities.ActionBarAdapter.TabState;
     57 import com.android.contacts.detail.ContactDetailFragment;
     58 import com.android.contacts.detail.ContactDetailLayoutController;
     59 import com.android.contacts.detail.ContactDetailUpdatesFragment;
     60 import com.android.contacts.detail.ContactLoaderFragment;
     61 import com.android.contacts.detail.ContactLoaderFragment.ContactLoaderFragmentListener;
     62 import com.android.contacts.common.dialog.ClearFrequentsDialog;
     63 import com.android.contacts.group.GroupBrowseListFragment;
     64 import com.android.contacts.group.GroupBrowseListFragment.OnGroupBrowserActionListener;
     65 import com.android.contacts.group.GroupDetailFragment;
     66 import com.android.contacts.interactions.ContactDeletionInteraction;
     67 import com.android.contacts.common.interactions.ImportExportDialogFragment;
     68 import com.android.contacts.list.ContactBrowseListFragment;
     69 import com.android.contacts.common.list.ContactEntryListFragment;
     70 import com.android.contacts.common.list.ContactListFilter;
     71 import com.android.contacts.common.list.ContactListFilterController;
     72 import com.android.contacts.common.list.ContactTileAdapter.DisplayType;
     73 import com.android.contacts.list.ContactTileFrequentFragment;
     74 import com.android.contacts.list.ContactTileListFragment;
     75 import com.android.contacts.list.ContactsIntentResolver;
     76 import com.android.contacts.list.ContactsRequest;
     77 import com.android.contacts.list.ContactsUnavailableFragment;
     78 import com.android.contacts.list.DefaultContactBrowseListFragment;
     79 import com.android.contacts.common.list.DirectoryListLoader;
     80 import com.android.contacts.list.OnContactBrowserActionListener;
     81 import com.android.contacts.list.OnContactsUnavailableActionListener;
     82 import com.android.contacts.list.ProviderStatusWatcher;
     83 import com.android.contacts.list.ProviderStatusWatcher.ProviderStatusListener;
     84 import com.android.contacts.model.Contact;
     85 import com.android.contacts.common.model.account.AccountWithDataSet;
     86 import com.android.contacts.preference.ContactsPreferenceActivity;
     87 import com.android.contacts.preference.DisplayOptionsPreferenceFragment;
     88 import com.android.contacts.common.util.AccountFilterUtil;
     89 import com.android.contacts.util.AccountPromptUtils;
     90 import com.android.contacts.common.util.Constants;
     91 import com.android.contacts.util.DialogManager;
     92 import com.android.contacts.util.HelpUtils;
     93 import com.android.contacts.util.PhoneCapabilityTester;
     94 import com.android.contacts.common.util.UriUtils;
     95 import com.android.contacts.widget.TransitionAnimationView;
     96 
     97 import java.util.ArrayList;
     98 import java.util.Locale;
     99 import java.util.concurrent.atomic.AtomicInteger;
    100 
    101 /**
    102  * Displays a list to browse contacts. For xlarge screens, this also displays a detail-pane on
    103  * the right.
    104  */
    105 public class PeopleActivity extends ContactsActivity
    106         implements View.OnCreateContextMenuListener, ActionBarAdapter.Listener,
    107         DialogManager.DialogShowingViewActivity,
    108         ContactListFilterController.ContactListFilterListener, ProviderStatusListener {
    109 
    110     private static final String TAG = "PeopleActivity";
    111 
    112     private static final int TAB_FADE_IN_DURATION = 500;
    113 
    114     private static final String ENABLE_DEBUG_OPTIONS_HIDDEN_CODE = "debug debug!";
    115 
    116     // These values needs to start at 2. See {@link ContactEntryListFragment}.
    117     private static final int SUBACTIVITY_NEW_CONTACT = 2;
    118     private static final int SUBACTIVITY_EDIT_CONTACT = 3;
    119     private static final int SUBACTIVITY_NEW_GROUP = 4;
    120     private static final int SUBACTIVITY_EDIT_GROUP = 5;
    121     private static final int SUBACTIVITY_ACCOUNT_FILTER = 6;
    122 
    123     private final DialogManager mDialogManager = new DialogManager(this);
    124 
    125     private ContactsIntentResolver mIntentResolver;
    126     private ContactsRequest mRequest;
    127 
    128     private ActionBarAdapter mActionBarAdapter;
    129 
    130     private ContactDetailFragment mContactDetailFragment;
    131 
    132     private ContactLoaderFragment mContactDetailLoaderFragment;
    133     private final ContactDetailLoaderFragmentListener mContactDetailLoaderFragmentListener =
    134             new ContactDetailLoaderFragmentListener();
    135 
    136     private GroupDetailFragment mGroupDetailFragment;
    137     private final GroupDetailFragmentListener mGroupDetailFragmentListener =
    138             new GroupDetailFragmentListener();
    139 
    140     private ContactTileListFragment.Listener mFavoritesFragmentListener =
    141             new StrequentContactListFragmentListener();
    142 
    143     private ContactListFilterController mContactListFilterController;
    144 
    145     private ContactsUnavailableFragment mContactsUnavailableFragment;
    146     private ProviderStatusWatcher mProviderStatusWatcher;
    147     private ProviderStatusWatcher.Status mProviderStatus;
    148 
    149     private boolean mOptionsMenuContactsAvailable;
    150 
    151     /**
    152      * Showing a list of Contacts. Also used for showing search results in search mode.
    153      */
    154     private DefaultContactBrowseListFragment mAllFragment;
    155     private ContactTileListFragment mFavoritesFragment;
    156     private ContactTileFrequentFragment mFrequentFragment;
    157     private GroupBrowseListFragment mGroupsFragment;
    158 
    159     private View mFavoritesView;
    160     private View mBrowserView;
    161     private TransitionAnimationView mPeopleActivityView;
    162     private TransitionAnimationView mContactDetailsView;
    163     private TransitionAnimationView mGroupDetailsView;
    164 
    165     /** ViewPager for swipe, used only on the phone (i.e. one-pane mode) */
    166     private ViewPager mTabPager;
    167     private TabPagerAdapter mTabPagerAdapter;
    168     private final TabPagerListener mTabPagerListener = new TabPagerListener();
    169 
    170     private ContactDetailLayoutController mContactDetailLayoutController;
    171 
    172     private boolean mEnableDebugMenuOptions;
    173 
    174     private final Handler mHandler = new Handler();
    175 
    176     /**
    177      * True if this activity instance is a re-created one.  i.e. set true after orientation change.
    178      * This is set in {@link #onCreate} for later use in {@link #onStart}.
    179      */
    180     private boolean mIsRecreatedInstance;
    181 
    182     /**
    183      * If {@link #configureFragments(boolean)} is already called.  Used to avoid calling it twice
    184      * in {@link #onStart}.
    185      * (This initialization only needs to be done once in onStart() when the Activity was just
    186      * created from scratch -- i.e. onCreate() was just called)
    187      */
    188     private boolean mFragmentInitialized;
    189 
    190     /**
    191      * Whether or not the current contact filter is valid or not. We need to do a check on
    192      * start of the app to verify that the user is not in single contact mode. If so, we should
    193      * dynamically change the filter, unless the incoming intent specifically requested a contact
    194      * that should be displayed in that mode.
    195      */
    196     private boolean mCurrentFilterIsValid;
    197 
    198     /**
    199      * This is to disable {@link #onOptionsItemSelected} when we trying to stop the activity.
    200      */
    201     private boolean mDisableOptionItemSelected;
    202 
    203     /** Sequential ID assigned to each instance; used for logging */
    204     private final int mInstanceId;
    205     private static final AtomicInteger sNextInstanceId = new AtomicInteger();
    206 
    207     public PeopleActivity() {
    208         mInstanceId = sNextInstanceId.getAndIncrement();
    209         mIntentResolver = new ContactsIntentResolver(this);
    210         mProviderStatusWatcher = ProviderStatusWatcher.getInstance(this);
    211     }
    212 
    213     @Override
    214     public String toString() {
    215         // Shown on logcat
    216         return String.format("%s@%d", getClass().getSimpleName(), mInstanceId);
    217     }
    218 
    219     public boolean areContactsAvailable() {
    220         return (mProviderStatus != null)
    221                 && mProviderStatus.status == ProviderStatus.STATUS_NORMAL;
    222     }
    223 
    224     private boolean areContactWritableAccountsAvailable() {
    225         return ContactsUtils.areContactWritableAccountsAvailable(this);
    226     }
    227 
    228     private boolean areGroupWritableAccountsAvailable() {
    229         return ContactsUtils.areGroupWritableAccountsAvailable(this);
    230     }
    231 
    232     /**
    233      * Initialize fragments that are (or may not be) in the layout.
    234      *
    235      * For the fragments that are in the layout, we initialize them in
    236      * {@link #createViewsAndFragments(Bundle)} after inflating the layout.
    237      *
    238      * However, there are special fragments which may not be in the layout, so we have to do the
    239      * initialization here.
    240      * The target fragments are:
    241      * - {@link ContactDetailFragment} and {@link ContactDetailUpdatesFragment}:  They may not be
    242      *   in the layout depending on the configuration.  (i.e. portrait)
    243      * - {@link ContactsUnavailableFragment}: We always create it at runtime.
    244      */
    245     @Override
    246     public void onAttachFragment(Fragment fragment) {
    247         if (fragment instanceof ContactDetailFragment) {
    248             mContactDetailFragment = (ContactDetailFragment) fragment;
    249         } else if (fragment instanceof ContactsUnavailableFragment) {
    250             mContactsUnavailableFragment = (ContactsUnavailableFragment)fragment;
    251             mContactsUnavailableFragment.setOnContactsUnavailableActionListener(
    252                     new ContactsUnavailableFragmentListener());
    253         }
    254     }
    255 
    256     @Override
    257     protected void onCreate(Bundle savedState) {
    258         if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) {
    259             Log.d(Constants.PERFORMANCE_TAG, "PeopleActivity.onCreate start");
    260         }
    261         super.onCreate(savedState);
    262 
    263         if (!processIntent(false)) {
    264             finish();
    265             return;
    266         }
    267         mContactListFilterController = ContactListFilterController.getInstance(this);
    268         mContactListFilterController.checkFilterValidity(false);
    269         mContactListFilterController.addListener(this);
    270 
    271         mProviderStatusWatcher.addListener(this);
    272 
    273         mIsRecreatedInstance = (savedState != null);
    274         createViewsAndFragments(savedState);
    275         getWindow().setBackgroundDrawableResource(R.color.background_primary);
    276         if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) {
    277             Log.d(Constants.PERFORMANCE_TAG, "PeopleActivity.onCreate finish");
    278         }
    279     }
    280 
    281     @Override
    282     protected void onNewIntent(Intent intent) {
    283         setIntent(intent);
    284         if (!processIntent(true)) {
    285             finish();
    286             return;
    287         }
    288         mActionBarAdapter.initialize(null, mRequest);
    289 
    290         mContactListFilterController.checkFilterValidity(false);
    291         mCurrentFilterIsValid = true;
    292 
    293         // Re-configure fragments.
    294         configureFragments(true /* from request */);
    295         invalidateOptionsMenuIfNeeded();
    296     }
    297 
    298     /**
    299      * Resolve the intent and initialize {@link #mRequest}, and launch another activity if redirect
    300      * is needed.
    301      *
    302      * @param forNewIntent set true if it's called from {@link #onNewIntent(Intent)}.
    303      * @return {@code true} if {@link PeopleActivity} should continue running.  {@code false}
    304      *         if it shouldn't, in which case the caller should finish() itself and shouldn't do
    305      *         farther initialization.
    306      */
    307     private boolean processIntent(boolean forNewIntent) {
    308         // Extract relevant information from the intent
    309         mRequest = mIntentResolver.resolveIntent(getIntent());
    310         if (Log.isLoggable(TAG, Log.DEBUG)) {
    311             Log.d(TAG, this + " processIntent: forNewIntent=" + forNewIntent
    312                     + " intent=" + getIntent() + " request=" + mRequest);
    313         }
    314         if (!mRequest.isValid()) {
    315             setResult(RESULT_CANCELED);
    316             return false;
    317         }
    318 
    319         Intent redirect = mRequest.getRedirectIntent();
    320         if (redirect != null) {
    321             // Need to start a different activity
    322             startActivity(redirect);
    323             return false;
    324         }
    325 
    326         if (mRequest.getActionCode() == ContactsRequest.ACTION_VIEW_CONTACT
    327                 && !PhoneCapabilityTester.isUsingTwoPanes(this)) {
    328             redirect = new Intent(this, ContactDetailActivity.class);
    329             redirect.setAction(Intent.ACTION_VIEW);
    330             redirect.setData(mRequest.getContactUri());
    331             startActivity(redirect);
    332             return false;
    333         }
    334         return true;
    335     }
    336 
    337     private void createViewsAndFragments(Bundle savedState) {
    338         setContentView(R.layout.people_activity);
    339 
    340         final FragmentManager fragmentManager = getFragmentManager();
    341 
    342         // Hide all tabs (the current tab will later be reshown once a tab is selected)
    343         final FragmentTransaction transaction = fragmentManager.beginTransaction();
    344 
    345         // Prepare the fragments which are used both on 1-pane and on 2-pane.
    346         final boolean isUsingTwoPanes = PhoneCapabilityTester.isUsingTwoPanes(this);
    347         if (isUsingTwoPanes) {
    348             mFavoritesFragment = getFragment(R.id.favorites_fragment);
    349             mAllFragment = getFragment(R.id.all_fragment);
    350             mGroupsFragment = getFragment(R.id.groups_fragment);
    351         } else {
    352             mTabPager = getView(R.id.tab_pager);
    353             mTabPagerAdapter = new TabPagerAdapter();
    354             mTabPager.setAdapter(mTabPagerAdapter);
    355             mTabPager.setOnPageChangeListener(mTabPagerListener);
    356 
    357             final String FAVORITE_TAG = "tab-pager-favorite";
    358             final String ALL_TAG = "tab-pager-all";
    359             final String GROUPS_TAG = "tab-pager-groups";
    360 
    361             // Create the fragments and add as children of the view pager.
    362             // The pager adapter will only change the visibility; it'll never create/destroy
    363             // fragments.
    364             // However, if it's after screen rotation, the fragments have been re-created by
    365             // the fragment manager, so first see if there're already the target fragments
    366             // existing.
    367             mFavoritesFragment = (ContactTileListFragment)
    368                     fragmentManager.findFragmentByTag(FAVORITE_TAG);
    369             mAllFragment = (DefaultContactBrowseListFragment)
    370                     fragmentManager.findFragmentByTag(ALL_TAG);
    371             mGroupsFragment = (GroupBrowseListFragment)
    372                     fragmentManager.findFragmentByTag(GROUPS_TAG);
    373 
    374             if (mFavoritesFragment == null) {
    375                 mFavoritesFragment = new ContactTileListFragment();
    376                 mAllFragment = new DefaultContactBrowseListFragment();
    377                 mGroupsFragment = new GroupBrowseListFragment();
    378 
    379                 transaction.add(R.id.tab_pager, mFavoritesFragment, FAVORITE_TAG);
    380                 transaction.add(R.id.tab_pager, mAllFragment, ALL_TAG);
    381                 transaction.add(R.id.tab_pager, mGroupsFragment, GROUPS_TAG);
    382             }
    383         }
    384 
    385         mFavoritesFragment.setListener(mFavoritesFragmentListener);
    386 
    387         mAllFragment.setOnContactListActionListener(new ContactBrowserActionListener());
    388 
    389         mGroupsFragment.setListener(new GroupBrowserActionListener());
    390 
    391         // Hide all fragments for now.  We adjust visibility when we get onSelectedTabChanged()
    392         // from ActionBarAdapter.
    393         transaction.hide(mFavoritesFragment);
    394         transaction.hide(mAllFragment);
    395         transaction.hide(mGroupsFragment);
    396 
    397         if (isUsingTwoPanes) {
    398             // Prepare 2-pane only fragments/views...
    399 
    400             // Container views for fragments
    401             mPeopleActivityView = getView(R.id.people_view);
    402             mFavoritesView = getView(R.id.favorites_view);
    403             mContactDetailsView = getView(R.id.contact_details_view);
    404             mGroupDetailsView = getView(R.id.group_details_view);
    405             mBrowserView = getView(R.id.browse_view);
    406 
    407             // Only favorites tab with two panes has a separate frequent fragment
    408             if (PhoneCapabilityTester.isUsingTwoPanesInFavorites(this)) {
    409                 mFrequentFragment = getFragment(R.id.frequent_fragment);
    410                 mFrequentFragment.setListener(mFavoritesFragmentListener);
    411                 mFrequentFragment.setDisplayType(DisplayType.FREQUENT_ONLY);
    412                 mFrequentFragment.enableQuickContact(true);
    413             }
    414 
    415             mContactDetailLoaderFragment = getFragment(R.id.contact_detail_loader_fragment);
    416             mContactDetailLoaderFragment.setListener(mContactDetailLoaderFragmentListener);
    417 
    418             mGroupDetailFragment = getFragment(R.id.group_detail_fragment);
    419             mGroupDetailFragment.setListener(mGroupDetailFragmentListener);
    420             mGroupDetailFragment.setQuickContact(true);
    421 
    422             if (mContactDetailFragment != null) {
    423                 transaction.hide(mContactDetailFragment);
    424             }
    425             transaction.hide(mGroupDetailFragment);
    426 
    427             // Configure contact details
    428             mContactDetailLayoutController = new ContactDetailLayoutController(this, savedState,
    429                     getFragmentManager(), mContactDetailsView,
    430                     findViewById(R.id.contact_detail_container),
    431                     new ContactDetailFragmentListener());
    432         }
    433         transaction.commitAllowingStateLoss();
    434         fragmentManager.executePendingTransactions();
    435 
    436         // Setting Properties after fragment is created
    437         if (PhoneCapabilityTester.isUsingTwoPanesInFavorites(this)) {
    438             mFavoritesFragment.enableQuickContact(true);
    439             mFavoritesFragment.setDisplayType(DisplayType.STARRED_ONLY);
    440         } else {
    441             // For 2-pane in All and Groups but not in Favorites fragment, show the chevron
    442             // for quick contact popup
    443             mFavoritesFragment.enableQuickContact(isUsingTwoPanes);
    444             mFavoritesFragment.setDisplayType(DisplayType.STREQUENT);
    445         }
    446 
    447         // Configure action bar
    448         mActionBarAdapter = new ActionBarAdapter(this, this, getActionBar(), isUsingTwoPanes);
    449         mActionBarAdapter.initialize(savedState, mRequest);
    450 
    451         invalidateOptionsMenuIfNeeded();
    452     }
    453 
    454     @Override
    455     protected void onStart() {
    456         if (!mFragmentInitialized) {
    457             mFragmentInitialized = true;
    458             /* Configure fragments if we haven't.
    459              *
    460              * Note it's a one-shot initialization, so we want to do this in {@link #onCreate}.
    461              *
    462              * However, because this method may indirectly touch views in fragments but fragments
    463              * created in {@link #configureContentView} using a {@link FragmentTransaction} will NOT
    464              * have views until {@link Activity#onCreate} finishes (they would if they were inflated
    465              * from a layout), we need to do it here in {@link #onStart()}.
    466              *
    467              * (When {@link Fragment#onCreateView} is called is different in the former case and
    468              * in the latter case, unfortunately.)
    469              *
    470              * Also, we skip most of the work in it if the activity is a re-created one.
    471              * (so the argument.)
    472              */
    473             configureFragments(!mIsRecreatedInstance);
    474         } else if (PhoneCapabilityTester.isUsingTwoPanes(this) && !mCurrentFilterIsValid) {
    475             // We only want to do the filter check in onStart for wide screen devices where it
    476             // is often possible to get into single contact mode. Only do this check if
    477             // the filter hasn't already been set properly (i.e. onCreate or onActivityResult).
    478 
    479             // Since there is only one {@link ContactListFilterController} across multiple
    480             // activity instances, make sure the filter controller is in sync withthe current
    481             // contact list fragment filter.
    482             // TODO: Clean this up. Perhaps change {@link ContactListFilterController} to not be a
    483             // singleton?
    484             mContactListFilterController.setContactListFilter(mAllFragment.getFilter(), true);
    485             mContactListFilterController.checkFilterValidity(true);
    486             mCurrentFilterIsValid = true;
    487         }
    488         super.onStart();
    489     }
    490 
    491     @Override
    492     protected void onPause() {
    493         mOptionsMenuContactsAvailable = false;
    494         mProviderStatusWatcher.stop();
    495         super.onPause();
    496     }
    497 
    498     @Override
    499     protected void onResume() {
    500         super.onResume();
    501 
    502         mProviderStatusWatcher.start();
    503         updateViewConfiguration(true);
    504 
    505         // Re-register the listener, which may have been cleared when onSaveInstanceState was
    506         // called.  See also: onSaveInstanceState
    507         mActionBarAdapter.setListener(this);
    508         mDisableOptionItemSelected = false;
    509         if (mTabPager != null) {
    510             mTabPager.setOnPageChangeListener(mTabPagerListener);
    511         }
    512         // Current tab may have changed since the last onSaveInstanceState().  Make sure
    513         // the actual contents match the tab.
    514         updateFragmentsVisibility();
    515     }
    516 
    517     @Override
    518     protected void onStop() {
    519         super.onStop();
    520         mCurrentFilterIsValid = false;
    521     }
    522 
    523     @Override
    524     protected void onDestroy() {
    525         mProviderStatusWatcher.removeListener(this);
    526 
    527         // Some of variables will be null if this Activity redirects Intent.
    528         // See also onCreate() or other methods called during the Activity's initialization.
    529         if (mActionBarAdapter != null) {
    530             mActionBarAdapter.setListener(null);
    531         }
    532         if (mContactListFilterController != null) {
    533             mContactListFilterController.removeListener(this);
    534         }
    535 
    536         super.onDestroy();
    537     }
    538 
    539     private void configureFragments(boolean fromRequest) {
    540         if (fromRequest) {
    541             ContactListFilter filter = null;
    542             int actionCode = mRequest.getActionCode();
    543             boolean searchMode = mRequest.isSearchMode();
    544             final int tabToOpen;
    545             switch (actionCode) {
    546                 case ContactsRequest.ACTION_ALL_CONTACTS:
    547                     filter = ContactListFilter.createFilterWithType(
    548                             ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
    549                     tabToOpen = TabState.ALL;
    550                     break;
    551                 case ContactsRequest.ACTION_CONTACTS_WITH_PHONES:
    552                     filter = ContactListFilter.createFilterWithType(
    553                             ContactListFilter.FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY);
    554                     tabToOpen = TabState.ALL;
    555                     break;
    556 
    557                 case ContactsRequest.ACTION_FREQUENT:
    558                 case ContactsRequest.ACTION_STREQUENT:
    559                 case ContactsRequest.ACTION_STARRED:
    560                     tabToOpen = TabState.FAVORITES;
    561                     break;
    562                 case ContactsRequest.ACTION_VIEW_CONTACT:
    563                     // We redirect this intent to the detail activity on 1-pane, so we don't get
    564                     // here.  It's only for 2-pane.
    565                     Uri currentlyLoadedContactUri = mContactDetailFragment.getUri();
    566                     if (currentlyLoadedContactUri != null
    567                             && !mRequest.getContactUri().equals(currentlyLoadedContactUri)) {
    568                         mContactDetailsView.setMaskVisibility(true);
    569                     }
    570                     tabToOpen = TabState.ALL;
    571                     break;
    572                 case ContactsRequest.ACTION_GROUP:
    573                     tabToOpen = TabState.GROUPS;
    574                     break;
    575                 default:
    576                     tabToOpen = -1;
    577                     break;
    578             }
    579             if (tabToOpen != -1) {
    580                 mActionBarAdapter.setCurrentTab(tabToOpen);
    581             }
    582 
    583             if (filter != null) {
    584                 mContactListFilterController.setContactListFilter(filter, false);
    585                 searchMode = false;
    586             }
    587 
    588             if (mRequest.getContactUri() != null) {
    589                 searchMode = false;
    590             }
    591 
    592             mActionBarAdapter.setSearchMode(searchMode);
    593             configureContactListFragmentForRequest();
    594         }
    595 
    596         configureContactListFragment();
    597         configureGroupListFragment();
    598 
    599         invalidateOptionsMenuIfNeeded();
    600     }
    601 
    602     @Override
    603     public void onContactListFilterChanged() {
    604         if (mAllFragment == null || !mAllFragment.isAdded()) {
    605             return;
    606         }
    607 
    608         mAllFragment.setFilter(mContactListFilterController.getFilter());
    609 
    610         invalidateOptionsMenuIfNeeded();
    611     }
    612 
    613     private void setupContactDetailFragment(final Uri contactLookupUri) {
    614         mContactDetailLoaderFragment.loadUri(contactLookupUri);
    615         invalidateOptionsMenuIfNeeded();
    616     }
    617 
    618     private void setupGroupDetailFragment(Uri groupUri) {
    619         // If we are switching from one group to another, do a cross-fade
    620         if (mGroupDetailFragment != null && mGroupDetailFragment.getGroupUri() != null &&
    621                 !UriUtils.areEqual(mGroupDetailFragment.getGroupUri(), groupUri)) {
    622             mGroupDetailsView.startMaskTransition(false, -1);
    623         }
    624         mGroupDetailFragment.loadGroup(groupUri);
    625         invalidateOptionsMenuIfNeeded();
    626     }
    627 
    628     /**
    629      * Handler for action bar actions.
    630      */
    631     @Override
    632     public void onAction(int action) {
    633         switch (action) {
    634             case ActionBarAdapter.Listener.Action.START_SEARCH_MODE:
    635                 // Tell the fragments that we're in the search mode
    636                 configureFragments(false /* from request */);
    637                 updateFragmentsVisibility();
    638                 invalidateOptionsMenu();
    639                 break;
    640             case ActionBarAdapter.Listener.Action.STOP_SEARCH_MODE:
    641                 setQueryTextToFragment("");
    642                 updateFragmentsVisibility();
    643                 invalidateOptionsMenu();
    644                 break;
    645             case ActionBarAdapter.Listener.Action.CHANGE_SEARCH_QUERY:
    646                 final String queryString = mActionBarAdapter.getQueryString();
    647                 setQueryTextToFragment(queryString);
    648                 updateDebugOptionsVisibility(
    649                         ENABLE_DEBUG_OPTIONS_HIDDEN_CODE.equals(queryString));
    650                 break;
    651             default:
    652                 throw new IllegalStateException("Unkonwn ActionBarAdapter action: " + action);
    653         }
    654     }
    655 
    656     @Override
    657     public void onSelectedTabChanged() {
    658         updateFragmentsVisibility();
    659     }
    660 
    661     private void updateDebugOptionsVisibility(boolean visible) {
    662         if (mEnableDebugMenuOptions != visible) {
    663             mEnableDebugMenuOptions = visible;
    664             invalidateOptionsMenu();
    665         }
    666     }
    667 
    668     /**
    669      * Updates the fragment/view visibility according to the current mode, such as
    670      * {@link ActionBarAdapter#isSearchMode()} and {@link ActionBarAdapter#getCurrentTab()}.
    671      */
    672     private void updateFragmentsVisibility() {
    673         int tab = mActionBarAdapter.getCurrentTab();
    674 
    675         // We use ViewPager on 1-pane.
    676         if (!PhoneCapabilityTester.isUsingTwoPanes(this)) {
    677             if (mActionBarAdapter.isSearchMode()) {
    678                 mTabPagerAdapter.setSearchMode(true);
    679             } else {
    680                 // No smooth scrolling if quitting from the search mode.
    681                 final boolean wasSearchMode = mTabPagerAdapter.isSearchMode();
    682                 mTabPagerAdapter.setSearchMode(false);
    683                 if (mTabPager.getCurrentItem() != tab) {
    684                     mTabPager.setCurrentItem(tab, !wasSearchMode);
    685                 }
    686             }
    687             invalidateOptionsMenu();
    688             showEmptyStateForTab(tab);
    689             if (tab == TabState.GROUPS) {
    690                 mGroupsFragment.setAddAccountsVisibility(!areGroupWritableAccountsAvailable());
    691             }
    692             return;
    693         }
    694 
    695         // for the tablet...
    696 
    697         // If in search mode, we use the all list + contact details to show the result.
    698         if (mActionBarAdapter.isSearchMode()) {
    699             tab = TabState.ALL;
    700         }
    701 
    702         switch (tab) {
    703             case TabState.FAVORITES:
    704                 mFavoritesView.setVisibility(View.VISIBLE);
    705                 mBrowserView.setVisibility(View.GONE);
    706                 mGroupDetailsView.setVisibility(View.GONE);
    707                 mContactDetailsView.setVisibility(View.GONE);
    708                 break;
    709             case TabState.GROUPS:
    710                 mFavoritesView.setVisibility(View.GONE);
    711                 mBrowserView.setVisibility(View.VISIBLE);
    712                 mGroupDetailsView.setVisibility(View.VISIBLE);
    713                 mContactDetailsView.setVisibility(View.GONE);
    714                 mGroupsFragment.setAddAccountsVisibility(!areGroupWritableAccountsAvailable());
    715                 break;
    716             case TabState.ALL:
    717                 mFavoritesView.setVisibility(View.GONE);
    718                 mBrowserView.setVisibility(View.VISIBLE);
    719                 mContactDetailsView.setVisibility(View.VISIBLE);
    720                 mGroupDetailsView.setVisibility(View.GONE);
    721                 break;
    722         }
    723         mPeopleActivityView.startMaskTransition(false, TAB_FADE_IN_DURATION);
    724         FragmentManager fragmentManager = getFragmentManager();
    725         FragmentTransaction ft = fragmentManager.beginTransaction();
    726 
    727         // Note mContactDetailLoaderFragment is an invisible fragment, but we still have to show/
    728         // hide it so its options menu will be shown/hidden.
    729         switch (tab) {
    730             case TabState.FAVORITES:
    731                 showFragment(ft, mFavoritesFragment);
    732                 showFragment(ft, mFrequentFragment);
    733                 hideFragment(ft, mAllFragment);
    734                 hideFragment(ft, mContactDetailLoaderFragment);
    735                 hideFragment(ft, mContactDetailFragment);
    736                 hideFragment(ft, mGroupsFragment);
    737                 hideFragment(ft, mGroupDetailFragment);
    738                 break;
    739             case TabState.ALL:
    740                 hideFragment(ft, mFavoritesFragment);
    741                 hideFragment(ft, mFrequentFragment);
    742                 showFragment(ft, mAllFragment);
    743                 showFragment(ft, mContactDetailLoaderFragment);
    744                 showFragment(ft, mContactDetailFragment);
    745                 hideFragment(ft, mGroupsFragment);
    746                 hideFragment(ft, mGroupDetailFragment);
    747                 break;
    748             case TabState.GROUPS:
    749                 hideFragment(ft, mFavoritesFragment);
    750                 hideFragment(ft, mFrequentFragment);
    751                 hideFragment(ft, mAllFragment);
    752                 hideFragment(ft, mContactDetailLoaderFragment);
    753                 hideFragment(ft, mContactDetailFragment);
    754                 showFragment(ft, mGroupsFragment);
    755                 showFragment(ft, mGroupDetailFragment);
    756                 break;
    757         }
    758         if (!ft.isEmpty()) {
    759             ft.commitAllowingStateLoss();
    760             fragmentManager.executePendingTransactions();
    761             // When switching tabs, we need to invalidate options menu, but executing a
    762             // fragment transaction does it implicitly.  We don't have to call invalidateOptionsMenu
    763             // manually.
    764         }
    765         showEmptyStateForTab(tab);
    766     }
    767 
    768     private void showEmptyStateForTab(int tab) {
    769         if (mContactsUnavailableFragment != null) {
    770             switch (tab) {
    771                 case TabState.FAVORITES:
    772                     mContactsUnavailableFragment.setMessageText(
    773                             R.string.listTotalAllContactsZeroStarred, -1);
    774                     break;
    775                 case TabState.GROUPS:
    776                     mContactsUnavailableFragment.setMessageText(R.string.noGroups,
    777                             areGroupWritableAccountsAvailable() ? -1 : R.string.noAccounts);
    778                     break;
    779                 case TabState.ALL:
    780                     mContactsUnavailableFragment.setMessageText(R.string.noContacts, -1);
    781                     break;
    782             }
    783         }
    784     }
    785 
    786     private class TabPagerListener implements ViewPager.OnPageChangeListener {
    787 
    788         // This package-protected constructor is here because of a possible compiler bug.
    789         // PeopleActivity$1.class should be generated due to the private outer/inner class access
    790         // needed here.  But for some reason, PeopleActivity$1.class is missing.
    791         // Since $1 class is needed as a jvm work around to get access to the inner class,
    792         // changing the constructor to package-protected or public will solve the problem.
    793         // To verify whether $1 class is needed, javap PeopleActivity$TabPagerListener and look for
    794         // references to PeopleActivity$1.
    795         //
    796         // When the constructor is private and PeopleActivity$1.class is missing, proguard will
    797         // correctly catch this and throw warnings and error out the build on user/userdebug builds.
    798         //
    799         // All private inner classes below also need this fix.
    800         TabPagerListener() {}
    801 
    802         @Override
    803         public void onPageScrollStateChanged(int state) {
    804         }
    805 
    806         @Override
    807         public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    808         }
    809 
    810         @Override
    811         public void onPageSelected(int position) {
    812             // Make sure not in the search mode, in which case position != TabState.ordinal().
    813             if (!mTabPagerAdapter.isSearchMode()) {
    814                 mActionBarAdapter.setCurrentTab(position, false);
    815                 showEmptyStateForTab(position);
    816                 if (position == TabState.GROUPS) {
    817                     mGroupsFragment.setAddAccountsVisibility(!areGroupWritableAccountsAvailable());
    818                 }
    819                 invalidateOptionsMenu();
    820             }
    821         }
    822     }
    823 
    824     /**
    825      * Adapter for the {@link ViewPager}.  Unlike {@link FragmentPagerAdapter},
    826      * {@link #instantiateItem} returns existing fragments, and {@link #instantiateItem}/
    827      * {@link #destroyItem} show/hide fragments instead of attaching/detaching.
    828      *
    829      * In search mode, we always show the "all" fragment, and disable the swipe.  We change the
    830      * number of items to 1 to disable the swipe.
    831      *
    832      * TODO figure out a more straight way to disable swipe.
    833      */
    834     private class TabPagerAdapter extends PagerAdapter {
    835         private final FragmentManager mFragmentManager;
    836         private FragmentTransaction mCurTransaction = null;
    837 
    838         private boolean mTabPagerAdapterSearchMode;
    839 
    840         private Fragment mCurrentPrimaryItem;
    841 
    842         public TabPagerAdapter() {
    843             mFragmentManager = getFragmentManager();
    844         }
    845 
    846         public boolean isSearchMode() {
    847             return mTabPagerAdapterSearchMode;
    848         }
    849 
    850         public void setSearchMode(boolean searchMode) {
    851             if (searchMode == mTabPagerAdapterSearchMode) {
    852                 return;
    853             }
    854             mTabPagerAdapterSearchMode = searchMode;
    855             notifyDataSetChanged();
    856         }
    857 
    858         @Override
    859         public int getCount() {
    860             return mTabPagerAdapterSearchMode ? 1 : TabState.COUNT;
    861         }
    862 
    863         /** Gets called when the number of items changes. */
    864         @Override
    865         public int getItemPosition(Object object) {
    866             if (mTabPagerAdapterSearchMode) {
    867                 if (object == mAllFragment) {
    868                     return 0; // Only 1 page in search mode
    869                 }
    870             } else {
    871                 if (object == mFavoritesFragment) {
    872                     return TabState.FAVORITES;
    873                 }
    874                 if (object == mAllFragment) {
    875                     return TabState.ALL;
    876                 }
    877                 if (object == mGroupsFragment) {
    878                     return TabState.GROUPS;
    879                 }
    880             }
    881             return POSITION_NONE;
    882         }
    883 
    884         @Override
    885         public void startUpdate(ViewGroup container) {
    886         }
    887 
    888         private Fragment getFragment(int position) {
    889             if (mTabPagerAdapterSearchMode) {
    890                 if (position != 0) {
    891                     // This has only been observed in monkey tests.
    892                     // Let's log this issue, but not crash
    893                     Log.w(TAG, "Request fragment at position=" + position + ", eventhough we " +
    894                             "are in search mode");
    895                 }
    896                 return mAllFragment;
    897             } else {
    898                 if (position == TabState.FAVORITES) {
    899                     return mFavoritesFragment;
    900                 } else if (position == TabState.ALL) {
    901                     return mAllFragment;
    902                 } else if (position == TabState.GROUPS) {
    903                     return mGroupsFragment;
    904                 }
    905             }
    906             throw new IllegalArgumentException("position: " + position);
    907         }
    908 
    909         @Override
    910         public Object instantiateItem(ViewGroup container, int position) {
    911             if (mCurTransaction == null) {
    912                 mCurTransaction = mFragmentManager.beginTransaction();
    913             }
    914             Fragment f = getFragment(position);
    915             mCurTransaction.show(f);
    916 
    917             // Non primary pages are not visible.
    918             f.setUserVisibleHint(f == mCurrentPrimaryItem);
    919             return f;
    920         }
    921 
    922         @Override
    923         public void destroyItem(ViewGroup container, int position, Object object) {
    924             if (mCurTransaction == null) {
    925                 mCurTransaction = mFragmentManager.beginTransaction();
    926             }
    927             mCurTransaction.hide((Fragment) object);
    928         }
    929 
    930         @Override
    931         public void finishUpdate(ViewGroup container) {
    932             if (mCurTransaction != null) {
    933                 mCurTransaction.commitAllowingStateLoss();
    934                 mCurTransaction = null;
    935                 mFragmentManager.executePendingTransactions();
    936             }
    937         }
    938 
    939         @Override
    940         public boolean isViewFromObject(View view, Object object) {
    941             return ((Fragment) object).getView() == view;
    942         }
    943 
    944         @Override
    945         public void setPrimaryItem(ViewGroup container, int position, Object object) {
    946             Fragment fragment = (Fragment) object;
    947             if (mCurrentPrimaryItem != fragment) {
    948                 if (mCurrentPrimaryItem != null) {
    949                     mCurrentPrimaryItem.setUserVisibleHint(false);
    950                 }
    951                 if (fragment != null) {
    952                     fragment.setUserVisibleHint(true);
    953                 }
    954                 mCurrentPrimaryItem = fragment;
    955             }
    956         }
    957 
    958         @Override
    959         public Parcelable saveState() {
    960             return null;
    961         }
    962 
    963         @Override
    964         public void restoreState(Parcelable state, ClassLoader loader) {
    965         }
    966     }
    967 
    968     private void setQueryTextToFragment(String query) {
    969         mAllFragment.setQueryString(query, true);
    970         mAllFragment.setVisibleScrollbarEnabled(!mAllFragment.isSearchMode());
    971     }
    972 
    973     private void configureContactListFragmentForRequest() {
    974         Uri contactUri = mRequest.getContactUri();
    975         if (contactUri != null) {
    976             // For an incoming request, explicitly require a selection if we are on 2-pane UI,
    977             // (i.e. even if we view the same selected contact, the contact may no longer be
    978             // in the list, so we must refresh the list).
    979             if (PhoneCapabilityTester.isUsingTwoPanes(this)) {
    980                 mAllFragment.setSelectionRequired(true);
    981             }
    982             mAllFragment.setSelectedContactUri(contactUri);
    983         }
    984 
    985         mAllFragment.setFilter(mContactListFilterController.getFilter());
    986         setQueryTextToFragment(mActionBarAdapter.getQueryString());
    987 
    988         if (mRequest.isDirectorySearchEnabled()) {
    989             mAllFragment.setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_DEFAULT);
    990         } else {
    991             mAllFragment.setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_NONE);
    992         }
    993     }
    994 
    995     private void configureContactListFragment() {
    996         // Filter may be changed when this Activity is in background.
    997         mAllFragment.setFilter(mContactListFilterController.getFilter());
    998 
    999         final boolean useTwoPane = PhoneCapabilityTester.isUsingTwoPanes(this);
   1000 
   1001         mAllFragment.setVerticalScrollbarPosition(getScrollBarPosition(useTwoPane));
   1002         mAllFragment.setSelectionVisible(useTwoPane);
   1003         mAllFragment.setQuickContactEnabled(!useTwoPane);
   1004     }
   1005 
   1006     private int getScrollBarPosition(boolean useTwoPane) {
   1007         final boolean isLayoutRtl = isRTL();
   1008         final int position;
   1009         if (useTwoPane) {
   1010             position = isLayoutRtl ? View.SCROLLBAR_POSITION_RIGHT : View.SCROLLBAR_POSITION_LEFT;
   1011         } else {
   1012             position = isLayoutRtl ? View.SCROLLBAR_POSITION_LEFT : View.SCROLLBAR_POSITION_RIGHT;
   1013         }
   1014         return position;
   1015     }
   1016 
   1017     private boolean isRTL() {
   1018         final Locale locale = Locale.getDefault();
   1019         return TextUtils.getLayoutDirectionFromLocale(locale) == View.LAYOUT_DIRECTION_RTL;
   1020     }
   1021 
   1022     private void configureGroupListFragment() {
   1023         final boolean useTwoPane = PhoneCapabilityTester.isUsingTwoPanes(this);
   1024         mGroupsFragment.setVerticalScrollbarPosition(getScrollBarPosition(useTwoPane));
   1025         mGroupsFragment.setSelectionVisible(useTwoPane);
   1026     }
   1027 
   1028     @Override
   1029     public void onProviderStatusChange() {
   1030         updateViewConfiguration(false);
   1031     }
   1032 
   1033     private void updateViewConfiguration(boolean forceUpdate) {
   1034         ProviderStatusWatcher.Status providerStatus = mProviderStatusWatcher.getProviderStatus();
   1035         if (!forceUpdate && (mProviderStatus != null)
   1036                 && (providerStatus.status == mProviderStatus.status)) return;
   1037         mProviderStatus = providerStatus;
   1038 
   1039         View contactsUnavailableView = findViewById(R.id.contacts_unavailable_view);
   1040         View mainView = findViewById(R.id.main_view);
   1041 
   1042         if (mProviderStatus.status == ProviderStatus.STATUS_NORMAL) {
   1043             // Ensure that the mTabPager is visible; we may have made it invisible below.
   1044             contactsUnavailableView.setVisibility(View.GONE);
   1045             if (mTabPager != null) {
   1046                 mTabPager.setVisibility(View.VISIBLE);
   1047             }
   1048 
   1049             if (mainView != null) {
   1050                 mainView.setVisibility(View.VISIBLE);
   1051             }
   1052             if (mAllFragment != null) {
   1053                 mAllFragment.setEnabled(true);
   1054             }
   1055         } else {
   1056             // If there are no accounts on the device and we should show the "no account" prompt
   1057             // (based on {@link SharedPreferences}), then launch the account setup activity so the
   1058             // user can sign-in or create an account.
   1059             //
   1060             // Also check for ability to modify accounts.  In limited user mode, you can't modify
   1061             // accounts so there is no point sending users to account setup activity.
   1062             final UserManager userManager = UserManager.get(this);
   1063             final boolean disallowModifyAccounts = userManager.getUserRestrictions().getBoolean(
   1064                     UserManager.DISALLOW_MODIFY_ACCOUNTS);
   1065             if (!disallowModifyAccounts && !areContactWritableAccountsAvailable() &&
   1066                     AccountPromptUtils.shouldShowAccountPrompt(this)) {
   1067                 AccountPromptUtils.launchAccountPrompt(this);
   1068                 return;
   1069             }
   1070 
   1071             // Otherwise, continue setting up the page so that the user can still use the app
   1072             // without an account.
   1073             if (mAllFragment != null) {
   1074                 mAllFragment.setEnabled(false);
   1075             }
   1076             if (mContactsUnavailableFragment == null) {
   1077                 mContactsUnavailableFragment = new ContactsUnavailableFragment();
   1078                 mContactsUnavailableFragment.setOnContactsUnavailableActionListener(
   1079                         new ContactsUnavailableFragmentListener());
   1080                 getFragmentManager().beginTransaction()
   1081                         .replace(R.id.contacts_unavailable_container, mContactsUnavailableFragment)
   1082                         .commitAllowingStateLoss();
   1083             }
   1084             mContactsUnavailableFragment.updateStatus(mProviderStatus);
   1085 
   1086             // Show the contactsUnavailableView, and hide the mTabPager so that we don't
   1087             // see it sliding in underneath the contactsUnavailableView at the edges.
   1088             contactsUnavailableView.setVisibility(View.VISIBLE);
   1089             if (mTabPager != null) {
   1090                 mTabPager.setVisibility(View.GONE);
   1091             }
   1092 
   1093             if (mainView != null) {
   1094                 mainView.setVisibility(View.INVISIBLE);
   1095             }
   1096 
   1097             showEmptyStateForTab(mActionBarAdapter.getCurrentTab());
   1098         }
   1099 
   1100         invalidateOptionsMenuIfNeeded();
   1101     }
   1102 
   1103     private final class ContactBrowserActionListener implements OnContactBrowserActionListener {
   1104         ContactBrowserActionListener() {}
   1105 
   1106         @Override
   1107         public void onSelectionChange() {
   1108             if (PhoneCapabilityTester.isUsingTwoPanes(PeopleActivity.this)) {
   1109                 setupContactDetailFragment(mAllFragment.getSelectedContactUri());
   1110             }
   1111         }
   1112 
   1113         @Override
   1114         public void onViewContactAction(Uri contactLookupUri) {
   1115             if (PhoneCapabilityTester.isUsingTwoPanes(PeopleActivity.this)) {
   1116                 setupContactDetailFragment(contactLookupUri);
   1117             } else {
   1118                 Intent intent = new Intent(Intent.ACTION_VIEW, contactLookupUri);
   1119                 startActivity(intent);
   1120             }
   1121         }
   1122 
   1123         @Override
   1124         public void onCreateNewContactAction() {
   1125             Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
   1126             Bundle extras = getIntent().getExtras();
   1127             if (extras != null) {
   1128                 intent.putExtras(extras);
   1129             }
   1130             startActivity(intent);
   1131         }
   1132 
   1133         @Override
   1134         public void onEditContactAction(Uri contactLookupUri) {
   1135             Intent intent = new Intent(Intent.ACTION_EDIT, contactLookupUri);
   1136             Bundle extras = getIntent().getExtras();
   1137             if (extras != null) {
   1138                 intent.putExtras(extras);
   1139             }
   1140             intent.putExtra(
   1141                     ContactEditorActivity.INTENT_KEY_FINISH_ACTIVITY_ON_SAVE_COMPLETED, true);
   1142             startActivityForResult(intent, SUBACTIVITY_EDIT_CONTACT);
   1143         }
   1144 
   1145         @Override
   1146         public void onAddToFavoritesAction(Uri contactUri) {
   1147             ContentValues values = new ContentValues(1);
   1148             values.put(Contacts.STARRED, 1);
   1149             getContentResolver().update(contactUri, values, null, null);
   1150         }
   1151 
   1152         @Override
   1153         public void onRemoveFromFavoritesAction(Uri contactUri) {
   1154             ContentValues values = new ContentValues(1);
   1155             values.put(Contacts.STARRED, 0);
   1156             getContentResolver().update(contactUri, values, null, null);
   1157         }
   1158 
   1159         @Override
   1160         public void onDeleteContactAction(Uri contactUri) {
   1161             ContactDeletionInteraction.start(PeopleActivity.this, contactUri, false);
   1162         }
   1163 
   1164         @Override
   1165         public void onFinishAction() {
   1166             onBackPressed();
   1167         }
   1168 
   1169         @Override
   1170         public void onInvalidSelection() {
   1171             ContactListFilter filter;
   1172             ContactListFilter currentFilter = mAllFragment.getFilter();
   1173             if (currentFilter != null
   1174                     && currentFilter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) {
   1175                 filter = ContactListFilter.createFilterWithType(
   1176                         ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
   1177                 mAllFragment.setFilter(filter);
   1178             } else {
   1179                 filter = ContactListFilter.createFilterWithType(
   1180                         ContactListFilter.FILTER_TYPE_SINGLE_CONTACT);
   1181                 mAllFragment.setFilter(filter, false);
   1182             }
   1183             mContactListFilterController.setContactListFilter(filter, true);
   1184         }
   1185     }
   1186 
   1187     private class ContactDetailLoaderFragmentListener implements ContactLoaderFragmentListener {
   1188         ContactDetailLoaderFragmentListener() {}
   1189 
   1190         @Override
   1191         public void onContactNotFound() {
   1192             // Nothing needs to be done here
   1193         }
   1194 
   1195         @Override
   1196         public void onDetailsLoaded(final Contact result) {
   1197             if (result == null) {
   1198                 // Nothing is loaded. Show empty state.
   1199                 mContactDetailLayoutController.showEmptyState();
   1200                 return;
   1201             }
   1202             // Since {@link FragmentTransaction}s cannot be done in the onLoadFinished() of the
   1203             // {@link LoaderCallbacks}, then post this {@link Runnable} to the {@link Handler}
   1204             // on the main thread to execute later.
   1205             mHandler.post(new Runnable() {
   1206                 @Override
   1207                 public void run() {
   1208                     // If the activity is destroyed (or will be destroyed soon), don't update the UI
   1209                     if (isFinishing()) {
   1210                         return;
   1211                     }
   1212                     mContactDetailLayoutController.setContactData(result);
   1213                 }
   1214             });
   1215         }
   1216 
   1217         @Override
   1218         public void onEditRequested(Uri contactLookupUri) {
   1219             Intent intent = new Intent(Intent.ACTION_EDIT, contactLookupUri);
   1220             intent.putExtra(
   1221                     ContactEditorActivity.INTENT_KEY_FINISH_ACTIVITY_ON_SAVE_COMPLETED, true);
   1222             startActivityForResult(intent, SUBACTIVITY_EDIT_CONTACT);
   1223         }
   1224 
   1225         @Override
   1226         public void onDeleteRequested(Uri contactUri) {
   1227             ContactDeletionInteraction.start(PeopleActivity.this, contactUri, false);
   1228         }
   1229     }
   1230 
   1231     public class ContactDetailFragmentListener implements ContactDetailFragment.Listener {
   1232         @Override
   1233         public void onItemClicked(Intent intent) {
   1234             if (intent == null) {
   1235                 return;
   1236             }
   1237             try {
   1238                 startActivity(intent);
   1239             } catch (ActivityNotFoundException e) {
   1240                 Log.e(TAG, "No activity found for intent: " + intent);
   1241             }
   1242         }
   1243 
   1244         @Override
   1245         public void onCreateRawContactRequested(ArrayList<ContentValues> values,
   1246                 AccountWithDataSet account) {
   1247             Toast.makeText(PeopleActivity.this, R.string.toast_making_personal_copy,
   1248                     Toast.LENGTH_LONG).show();
   1249             Intent serviceIntent = ContactSaveService.createNewRawContactIntent(
   1250                     PeopleActivity.this, values, account,
   1251                     PeopleActivity.class, Intent.ACTION_VIEW);
   1252             startService(serviceIntent);
   1253         }
   1254     }
   1255 
   1256     private class ContactsUnavailableFragmentListener
   1257             implements OnContactsUnavailableActionListener {
   1258         ContactsUnavailableFragmentListener() {}
   1259 
   1260         @Override
   1261         public void onCreateNewContactAction() {
   1262             startActivity(new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI));
   1263         }
   1264 
   1265         @Override
   1266         public void onAddAccountAction() {
   1267             Intent intent = new Intent(Settings.ACTION_ADD_ACCOUNT);
   1268             intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
   1269             intent.putExtra(Settings.EXTRA_AUTHORITIES,
   1270                     new String[] { ContactsContract.AUTHORITY });
   1271             startActivity(intent);
   1272         }
   1273 
   1274         @Override
   1275         public void onImportContactsFromFileAction() {
   1276             ImportExportDialogFragment.show(getFragmentManager(), areContactsAvailable(),
   1277                     PeopleActivity.class);
   1278         }
   1279 
   1280         @Override
   1281         public void onFreeInternalStorageAction() {
   1282             startActivity(new Intent(Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS));
   1283         }
   1284     }
   1285 
   1286     private final class StrequentContactListFragmentListener
   1287             implements ContactTileListFragment.Listener {
   1288         StrequentContactListFragmentListener() {}
   1289 
   1290         @Override
   1291         public void onContactSelected(Uri contactUri, Rect targetRect) {
   1292             if (PhoneCapabilityTester.isUsingTwoPanes(PeopleActivity.this)) {
   1293                 QuickContact.showQuickContact(PeopleActivity.this, targetRect, contactUri, 0, null);
   1294             } else {
   1295                 startActivity(new Intent(Intent.ACTION_VIEW, contactUri));
   1296             }
   1297         }
   1298 
   1299         @Override
   1300         public void onCallNumberDirectly(String phoneNumber) {
   1301             // No need to call phone number directly from People app.
   1302             Log.w(TAG, "unexpected invocation of onCallNumberDirectly()");
   1303         }
   1304     }
   1305 
   1306     private final class GroupBrowserActionListener implements OnGroupBrowserActionListener {
   1307 
   1308         GroupBrowserActionListener() {}
   1309 
   1310         @Override
   1311         public void onViewGroupAction(Uri groupUri) {
   1312             if (PhoneCapabilityTester.isUsingTwoPanes(PeopleActivity.this)) {
   1313                 setupGroupDetailFragment(groupUri);
   1314             } else {
   1315                 Intent intent = new Intent(PeopleActivity.this, GroupDetailActivity.class);
   1316                 intent.setData(groupUri);
   1317                 startActivity(intent);
   1318             }
   1319         }
   1320     }
   1321 
   1322     private class GroupDetailFragmentListener implements GroupDetailFragment.Listener {
   1323 
   1324         GroupDetailFragmentListener() {}
   1325 
   1326         @Override
   1327         public void onGroupSizeUpdated(String size) {
   1328             // Nothing needs to be done here because the size will be displayed in the detail
   1329             // fragment
   1330         }
   1331 
   1332         @Override
   1333         public void onGroupTitleUpdated(String title) {
   1334             // Nothing needs to be done here because the title will be displayed in the detail
   1335             // fragment
   1336         }
   1337 
   1338         @Override
   1339         public void onAccountTypeUpdated(String accountTypeString, String dataSet) {
   1340             // Nothing needs to be done here because the group source will be displayed in the
   1341             // detail fragment
   1342         }
   1343 
   1344         @Override
   1345         public void onEditRequested(Uri groupUri) {
   1346             final Intent intent = new Intent(PeopleActivity.this, GroupEditorActivity.class);
   1347             intent.setData(groupUri);
   1348             intent.setAction(Intent.ACTION_EDIT);
   1349             startActivityForResult(intent, SUBACTIVITY_EDIT_GROUP);
   1350         }
   1351 
   1352         @Override
   1353         public void onContactSelected(Uri contactUri) {
   1354             // Nothing needs to be done here because either quickcontact will be displayed
   1355             // or activity will take care of selection
   1356         }
   1357     }
   1358 
   1359     public void startActivityAndForwardResult(final Intent intent) {
   1360         intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
   1361 
   1362         // Forward extras to the new activity
   1363         Bundle extras = getIntent().getExtras();
   1364         if (extras != null) {
   1365             intent.putExtras(extras);
   1366         }
   1367         startActivity(intent);
   1368         finish();
   1369     }
   1370 
   1371     @Override
   1372     public boolean onCreateOptionsMenu(Menu menu) {
   1373         if (!areContactsAvailable()) {
   1374             // If contacts aren't available, hide all menu items.
   1375             return false;
   1376         }
   1377         super.onCreateOptionsMenu(menu);
   1378 
   1379         MenuInflater inflater = getMenuInflater();
   1380         inflater.inflate(R.menu.people_options, menu);
   1381 
   1382         return true;
   1383     }
   1384 
   1385     private void invalidateOptionsMenuIfNeeded() {
   1386         if (isOptionsMenuChanged()) {
   1387             invalidateOptionsMenu();
   1388         }
   1389     }
   1390 
   1391     public boolean isOptionsMenuChanged() {
   1392         if (mOptionsMenuContactsAvailable != areContactsAvailable()) {
   1393             return true;
   1394         }
   1395 
   1396         if (mAllFragment != null && mAllFragment.isOptionsMenuChanged()) {
   1397             return true;
   1398         }
   1399 
   1400         if (mContactDetailLoaderFragment != null &&
   1401                 mContactDetailLoaderFragment.isOptionsMenuChanged()) {
   1402             return true;
   1403         }
   1404 
   1405         if (mGroupDetailFragment != null && mGroupDetailFragment.isOptionsMenuChanged()) {
   1406             return true;
   1407         }
   1408 
   1409         return false;
   1410     }
   1411 
   1412     @Override
   1413     public boolean onPrepareOptionsMenu(Menu menu) {
   1414         mOptionsMenuContactsAvailable = areContactsAvailable();
   1415         if (!mOptionsMenuContactsAvailable) {
   1416             return false;
   1417         }
   1418 
   1419         // Get references to individual menu items in the menu
   1420         final MenuItem addContactMenu = menu.findItem(R.id.menu_add_contact);
   1421         final MenuItem contactsFilterMenu = menu.findItem(R.id.menu_contacts_filter);
   1422 
   1423         MenuItem addGroupMenu = menu.findItem(R.id.menu_add_group);
   1424 
   1425         final MenuItem clearFrequentsMenu = menu.findItem(R.id.menu_clear_frequents);
   1426         final MenuItem helpMenu = menu.findItem(R.id.menu_help);
   1427 
   1428         final boolean isSearchMode = mActionBarAdapter.isSearchMode();
   1429         if (isSearchMode) {
   1430             addContactMenu.setVisible(false);
   1431             addGroupMenu.setVisible(false);
   1432             contactsFilterMenu.setVisible(false);
   1433             clearFrequentsMenu.setVisible(false);
   1434             helpMenu.setVisible(false);
   1435         } else {
   1436             switch (mActionBarAdapter.getCurrentTab()) {
   1437                 case TabState.FAVORITES:
   1438                     addContactMenu.setVisible(true);
   1439                     addGroupMenu.setVisible(false);
   1440                     contactsFilterMenu.setVisible(false);
   1441                     clearFrequentsMenu.setVisible(hasFrequents());
   1442                     break;
   1443                 case TabState.ALL:
   1444                     addContactMenu.setVisible(true);
   1445                     addGroupMenu.setVisible(false);
   1446                     contactsFilterMenu.setVisible(true);
   1447                     clearFrequentsMenu.setVisible(false);
   1448                     break;
   1449                 case TabState.GROUPS:
   1450                     // Do not display the "new group" button if no accounts are available
   1451                     if (areGroupWritableAccountsAvailable()) {
   1452                         addGroupMenu.setVisible(true);
   1453                     } else {
   1454                         addGroupMenu.setVisible(false);
   1455                     }
   1456                     addContactMenu.setVisible(false);
   1457                     contactsFilterMenu.setVisible(false);
   1458                     clearFrequentsMenu.setVisible(false);
   1459                     break;
   1460             }
   1461             HelpUtils.prepareHelpMenuItem(this, helpMenu, R.string.help_url_people_main);
   1462         }
   1463         final boolean showMiscOptions = !isSearchMode;
   1464         makeMenuItemVisible(menu, R.id.menu_search, showMiscOptions);
   1465         makeMenuItemVisible(menu, R.id.menu_import_export, showMiscOptions);
   1466         makeMenuItemVisible(menu, R.id.menu_accounts, showMiscOptions);
   1467         makeMenuItemVisible(menu, R.id.menu_settings,
   1468                 showMiscOptions && !ContactsPreferenceActivity.isEmpty(this));
   1469 
   1470         // Debug options need to be visible even in search mode.
   1471         makeMenuItemVisible(menu, R.id.export_database, mEnableDebugMenuOptions);
   1472 
   1473         return true;
   1474     }
   1475 
   1476     /**
   1477      * Returns whether there are any frequently contacted people being displayed
   1478      * @return
   1479      */
   1480     private boolean hasFrequents() {
   1481         if (PhoneCapabilityTester.isUsingTwoPanesInFavorites(this)) {
   1482             return mFrequentFragment.hasFrequents();
   1483         } else {
   1484             return mFavoritesFragment.hasFrequents();
   1485         }
   1486     }
   1487 
   1488     private void makeMenuItemVisible(Menu menu, int itemId, boolean visible) {
   1489         MenuItem item =menu.findItem(itemId);
   1490         if (item != null) {
   1491             item.setVisible(visible);
   1492         }
   1493     }
   1494 
   1495     @Override
   1496     public boolean onOptionsItemSelected(MenuItem item) {
   1497         if (mDisableOptionItemSelected) {
   1498             return false;
   1499         }
   1500 
   1501         switch (item.getItemId()) {
   1502             case android.R.id.home: {
   1503                 // The home icon on the action bar is pressed
   1504                 if (mActionBarAdapter.isUpShowing()) {
   1505                     // "UP" icon press -- should be treated as "back".
   1506                     onBackPressed();
   1507                 }
   1508                 return true;
   1509             }
   1510             case R.id.menu_settings: {
   1511                 final Intent intent = new Intent(this, ContactsPreferenceActivity.class);
   1512                 // as there is only one section right now, make sure it is selected
   1513                 // on small screens, this also hides the section selector
   1514                 // Due to b/5045558, this code unfortunately only works properly on phones
   1515                 boolean settingsAreMultiPane = getResources().getBoolean(
   1516                         com.android.internal.R.bool.preferences_prefer_dual_pane);
   1517                 if (!settingsAreMultiPane) {
   1518                     intent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT,
   1519                             DisplayOptionsPreferenceFragment.class.getName());
   1520                     intent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT_TITLE,
   1521                             R.string.activity_title_settings);
   1522                 }
   1523                 startActivity(intent);
   1524                 return true;
   1525             }
   1526             case R.id.menu_contacts_filter: {
   1527                 AccountFilterUtil.startAccountFilterActivityForResult(
   1528                         this, SUBACTIVITY_ACCOUNT_FILTER,
   1529                         mContactListFilterController.getFilter());
   1530                 return true;
   1531             }
   1532             case R.id.menu_search: {
   1533                 onSearchRequested();
   1534                 return true;
   1535             }
   1536             case R.id.menu_add_contact: {
   1537                 final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
   1538                 // On 2-pane UI, we can let the editor activity finish itself and return
   1539                 // to this activity to display the new contact.
   1540                 if (PhoneCapabilityTester.isUsingTwoPanes(this)) {
   1541                     intent.putExtra(
   1542                             ContactEditorActivity.INTENT_KEY_FINISH_ACTIVITY_ON_SAVE_COMPLETED,
   1543                             true);
   1544                     startActivityForResult(intent, SUBACTIVITY_NEW_CONTACT);
   1545                 } else {
   1546                     // Otherwise, on 1-pane UI, we need the editor to launch the view contact
   1547                     // intent itself.
   1548                     startActivity(intent);
   1549                 }
   1550                 return true;
   1551             }
   1552             case R.id.menu_add_group: {
   1553                 createNewGroup();
   1554                 return true;
   1555             }
   1556             case R.id.menu_import_export: {
   1557                 ImportExportDialogFragment.show(getFragmentManager(), areContactsAvailable(),
   1558                         PeopleActivity.class);
   1559                 return true;
   1560             }
   1561             case R.id.menu_clear_frequents: {
   1562                 ClearFrequentsDialog.show(getFragmentManager());
   1563                 return true;
   1564             }
   1565             case R.id.menu_accounts: {
   1566                 final Intent intent = new Intent(Settings.ACTION_SYNC_SETTINGS);
   1567                 intent.putExtra(Settings.EXTRA_AUTHORITIES, new String[] {
   1568                     ContactsContract.AUTHORITY
   1569                 });
   1570                 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
   1571                 startActivity(intent);
   1572                 return true;
   1573             }
   1574             case R.id.export_database: {
   1575                 final Intent intent = new Intent("com.android.providers.contacts.DUMP_DATABASE");
   1576                 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
   1577                 startActivity(intent);
   1578                 return true;
   1579             }
   1580         }
   1581         return false;
   1582     }
   1583 
   1584     private void createNewGroup() {
   1585         final Intent intent = new Intent(this, GroupEditorActivity.class);
   1586         intent.setAction(Intent.ACTION_INSERT);
   1587         startActivityForResult(intent, SUBACTIVITY_NEW_GROUP);
   1588     }
   1589 
   1590     @Override
   1591     public boolean onSearchRequested() { // Search key pressed.
   1592         mActionBarAdapter.setSearchMode(true);
   1593         return true;
   1594     }
   1595 
   1596     @Override
   1597     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
   1598         switch (requestCode) {
   1599             case SUBACTIVITY_ACCOUNT_FILTER: {
   1600                 AccountFilterUtil.handleAccountFilterResult(
   1601                         mContactListFilterController, resultCode, data);
   1602                 break;
   1603             }
   1604 
   1605             case SUBACTIVITY_NEW_CONTACT:
   1606             case SUBACTIVITY_EDIT_CONTACT: {
   1607                 if (resultCode == RESULT_OK && PhoneCapabilityTester.isUsingTwoPanes(this)) {
   1608                     mRequest.setActionCode(ContactsRequest.ACTION_VIEW_CONTACT);
   1609                     mAllFragment.setSelectionRequired(true);
   1610                     mAllFragment.setSelectedContactUri(data.getData());
   1611                     // Suppress IME if in search mode
   1612                     if (mActionBarAdapter != null) {
   1613                         mActionBarAdapter.clearFocusOnSearchView();
   1614                     }
   1615                     // No need to change the contact filter
   1616                     mCurrentFilterIsValid = true;
   1617                 }
   1618                 break;
   1619             }
   1620 
   1621             case SUBACTIVITY_NEW_GROUP:
   1622             case SUBACTIVITY_EDIT_GROUP: {
   1623                 if (resultCode == RESULT_OK && PhoneCapabilityTester.isUsingTwoPanes(this)) {
   1624                     mRequest.setActionCode(ContactsRequest.ACTION_GROUP);
   1625                     mGroupsFragment.setSelectedUri(data.getData());
   1626                 }
   1627                 break;
   1628             }
   1629 
   1630             // TODO: Using the new startActivityWithResultFromFragment API this should not be needed
   1631             // anymore
   1632             case ContactEntryListFragment.ACTIVITY_REQUEST_CODE_PICKER:
   1633                 if (resultCode == RESULT_OK) {
   1634                     mAllFragment.onPickerResult(data);
   1635                 }
   1636 
   1637 // TODO fix or remove multipicker code
   1638 //                else if (resultCode == RESULT_CANCELED && mMode == MODE_PICK_MULTIPLE_PHONES) {
   1639 //                    // Finish the activity if the sub activity was canceled as back key is used
   1640 //                    // to confirm user selection in MODE_PICK_MULTIPLE_PHONES.
   1641 //                    finish();
   1642 //                }
   1643 //                break;
   1644         }
   1645     }
   1646 
   1647     @Override
   1648     public boolean onKeyDown(int keyCode, KeyEvent event) {
   1649         // TODO move to the fragment
   1650         switch (keyCode) {
   1651 //            case KeyEvent.KEYCODE_CALL: {
   1652 //                if (callSelection()) {
   1653 //                    return true;
   1654 //                }
   1655 //                break;
   1656 //            }
   1657 
   1658             case KeyEvent.KEYCODE_DEL: {
   1659                 if (deleteSelection()) {
   1660                     return true;
   1661                 }
   1662                 break;
   1663             }
   1664             default: {
   1665                 // Bring up the search UI if the user starts typing
   1666                 final int unicodeChar = event.getUnicodeChar();
   1667                 if ((unicodeChar != 0)
   1668                         // If COMBINING_ACCENT is set, it's not a unicode character.
   1669                         && ((unicodeChar & KeyCharacterMap.COMBINING_ACCENT) == 0)
   1670                         && !Character.isWhitespace(unicodeChar)) {
   1671                     String query = new String(new int[]{ unicodeChar }, 0, 1);
   1672                     if (!mActionBarAdapter.isSearchMode()) {
   1673                         mActionBarAdapter.setQueryString(query);
   1674                         mActionBarAdapter.setSearchMode(true);
   1675                         return true;
   1676                     }
   1677                 }
   1678             }
   1679         }
   1680 
   1681         return super.onKeyDown(keyCode, event);
   1682     }
   1683 
   1684     @Override
   1685     public void onBackPressed() {
   1686         if (mActionBarAdapter.isSearchMode()) {
   1687             mActionBarAdapter.setSearchMode(false);
   1688         } else {
   1689             super.onBackPressed();
   1690         }
   1691     }
   1692 
   1693     private boolean deleteSelection() {
   1694         // TODO move to the fragment
   1695 //        if (mActionCode == ContactsRequest.ACTION_DEFAULT) {
   1696 //            final int position = mListView.getSelectedItemPosition();
   1697 //            if (position != ListView.INVALID_POSITION) {
   1698 //                Uri contactUri = getContactUri(position);
   1699 //                if (contactUri != null) {
   1700 //                    doContactDelete(contactUri);
   1701 //                    return true;
   1702 //                }
   1703 //            }
   1704 //        }
   1705         return false;
   1706     }
   1707 
   1708     @Override
   1709     protected void onSaveInstanceState(Bundle outState) {
   1710         super.onSaveInstanceState(outState);
   1711         mActionBarAdapter.onSaveInstanceState(outState);
   1712         if (mContactDetailLayoutController != null) {
   1713             mContactDetailLayoutController.onSaveInstanceState(outState);
   1714         }
   1715 
   1716         // Clear the listener to make sure we don't get callbacks after onSaveInstanceState,
   1717         // in order to avoid doing fragment transactions after it.
   1718         // TODO Figure out a better way to deal with the issue.
   1719         mDisableOptionItemSelected = true;
   1720         mActionBarAdapter.setListener(null);
   1721         if (mTabPager != null) {
   1722             mTabPager.setOnPageChangeListener(null);
   1723         }
   1724     }
   1725 
   1726     @Override
   1727     protected void onRestoreInstanceState(Bundle savedInstanceState) {
   1728         super.onRestoreInstanceState(savedInstanceState);
   1729         // In our own lifecycle, the focus is saved and restore but later taken away by the
   1730         // ViewPager. As a hack, we force focus on the SearchView if we know that we are searching.
   1731         // This fixes the keyboard going away on screen rotation
   1732         if (mActionBarAdapter.isSearchMode()) {
   1733             mActionBarAdapter.setFocusOnSearchView();
   1734         }
   1735     }
   1736 
   1737     @Override
   1738     public DialogManager getDialogManager() {
   1739         return mDialogManager;
   1740     }
   1741 
   1742     // Visible for testing
   1743     public ContactBrowseListFragment getListFragment() {
   1744         return mAllFragment;
   1745     }
   1746 
   1747     // Visible for testing
   1748     public ContactDetailFragment getDetailFragment() {
   1749         return mContactDetailFragment;
   1750     }
   1751 }
   1752