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.Intent;
     24 import android.graphics.Rect;
     25 import android.net.Uri;
     26 import android.os.Bundle;
     27 import android.os.Parcelable;
     28 import android.os.UserManager;
     29 import android.preference.PreferenceActivity;
     30 import android.provider.ContactsContract;
     31 import android.provider.ContactsContract.Contacts;
     32 import android.provider.ContactsContract.ProviderStatus;
     33 import android.provider.ContactsContract.QuickContact;
     34 import android.provider.Settings;
     35 import android.support.v13.app.FragmentPagerAdapter;
     36 import android.support.v4.view.PagerAdapter;
     37 import android.support.v4.view.ViewPager;
     38 import android.text.TextUtils;
     39 import android.util.Log;
     40 import android.view.KeyCharacterMap;
     41 import android.view.KeyEvent;
     42 import android.view.Menu;
     43 import android.view.MenuInflater;
     44 import android.view.MenuItem;
     45 import android.view.View;
     46 import android.view.ViewGroup;
     47 import android.view.Window;
     48 import android.widget.ImageButton;
     49 import android.widget.Toast;
     50 import android.widget.Toolbar;
     51 
     52 import com.android.contacts.ContactsActivity;
     53 import com.android.contacts.R;
     54 import com.android.contacts.activities.ActionBarAdapter.TabState;
     55 import com.android.contacts.common.ContactsUtils;
     56 import com.android.contacts.common.dialog.ClearFrequentsDialog;
     57 import com.android.contacts.interactions.ContactDeletionInteraction;
     58 import com.android.contacts.common.interactions.ImportExportDialogFragment;
     59 import com.android.contacts.common.list.ContactEntryListFragment;
     60 import com.android.contacts.common.list.ContactListFilter;
     61 import com.android.contacts.common.list.ContactListFilterController;
     62 import com.android.contacts.common.list.ContactTileAdapter.DisplayType;
     63 import com.android.contacts.list.ContactTileListFragment;
     64 import com.android.contacts.list.ContactsIntentResolver;
     65 import com.android.contacts.list.ContactsRequest;
     66 import com.android.contacts.list.ContactsUnavailableFragment;
     67 import com.android.contacts.list.DefaultContactBrowseListFragment;
     68 import com.android.contacts.common.list.DirectoryListLoader;
     69 import com.android.contacts.common.preference.DisplayOptionsPreferenceFragment;
     70 import com.android.contacts.list.OnContactBrowserActionListener;
     71 import com.android.contacts.list.OnContactsUnavailableActionListener;
     72 import com.android.contacts.list.ProviderStatusWatcher;
     73 import com.android.contacts.list.ProviderStatusWatcher.ProviderStatusListener;
     74 import com.android.contacts.common.list.ViewPagerTabs;
     75 import com.android.contacts.preference.ContactsPreferenceActivity;
     76 import com.android.contacts.common.util.AccountFilterUtil;
     77 import com.android.contacts.common.util.ViewUtil;
     78 import com.android.contacts.quickcontact.QuickContactActivity;
     79 import com.android.contacts.util.AccountPromptUtils;
     80 import com.android.contacts.common.util.Constants;
     81 import com.android.contacts.util.DialogManager;
     82 import com.android.contacts.util.HelpUtils;
     83 
     84 import java.util.Locale;
     85 import java.util.concurrent.atomic.AtomicInteger;
     86 
     87 /**
     88  * Displays a list to browse contacts.
     89  */
     90 public class PeopleActivity extends ContactsActivity implements
     91         View.OnCreateContextMenuListener,
     92         View.OnClickListener,
     93         ActionBarAdapter.Listener,
     94         DialogManager.DialogShowingViewActivity,
     95         ContactListFilterController.ContactListFilterListener,
     96         ProviderStatusListener {
     97 
     98     private static final String TAG = "PeopleActivity";
     99 
    100     private static final String ENABLE_DEBUG_OPTIONS_HIDDEN_CODE = "debug debug!";
    101 
    102     // These values needs to start at 2. See {@link ContactEntryListFragment}.
    103     private static final int SUBACTIVITY_ACCOUNT_FILTER = 2;
    104 
    105     private final DialogManager mDialogManager = new DialogManager(this);
    106 
    107     private ContactsIntentResolver mIntentResolver;
    108     private ContactsRequest mRequest;
    109 
    110     private ActionBarAdapter mActionBarAdapter;
    111 
    112     private ContactTileListFragment.Listener mFavoritesFragmentListener =
    113             new StrequentContactListFragmentListener();
    114 
    115     private ContactListFilterController mContactListFilterController;
    116 
    117     private ContactsUnavailableFragment mContactsUnavailableFragment;
    118     private ProviderStatusWatcher mProviderStatusWatcher;
    119     private ProviderStatusWatcher.Status mProviderStatus;
    120 
    121     private boolean mOptionsMenuContactsAvailable;
    122 
    123     /**
    124      * Showing a list of Contacts. Also used for showing search results in search mode.
    125      */
    126     private DefaultContactBrowseListFragment mAllFragment;
    127     private ContactTileListFragment mFavoritesFragment;
    128 
    129     /** ViewPager for swipe */
    130     private ViewPager mTabPager;
    131     private ViewPagerTabs mViewPagerTabs;
    132     private TabPagerAdapter mTabPagerAdapter;
    133     private String[] mTabTitles;
    134     private final TabPagerListener mTabPagerListener = new TabPagerListener();
    135 
    136     private boolean mEnableDebugMenuOptions;
    137 
    138     /**
    139      * True if this activity instance is a re-created one.  i.e. set true after orientation change.
    140      * This is set in {@link #onCreate} for later use in {@link #onStart}.
    141      */
    142     private boolean mIsRecreatedInstance;
    143 
    144     /**
    145      * If {@link #configureFragments(boolean)} is already called.  Used to avoid calling it twice
    146      * in {@link #onStart}.
    147      * (This initialization only needs to be done once in onStart() when the Activity was just
    148      * created from scratch -- i.e. onCreate() was just called)
    149      */
    150     private boolean mFragmentInitialized;
    151 
    152     /**
    153      * This is to disable {@link #onOptionsItemSelected} when we trying to stop the activity.
    154      */
    155     private boolean mDisableOptionItemSelected;
    156 
    157     /** Sequential ID assigned to each instance; used for logging */
    158     private final int mInstanceId;
    159     private static final AtomicInteger sNextInstanceId = new AtomicInteger();
    160 
    161     public PeopleActivity() {
    162         mInstanceId = sNextInstanceId.getAndIncrement();
    163         mIntentResolver = new ContactsIntentResolver(this);
    164         mProviderStatusWatcher = ProviderStatusWatcher.getInstance(this);
    165     }
    166 
    167     @Override
    168     public String toString() {
    169         // Shown on logcat
    170         return String.format("%s@%d", getClass().getSimpleName(), mInstanceId);
    171     }
    172 
    173     public boolean areContactsAvailable() {
    174         return (mProviderStatus != null)
    175                 && mProviderStatus.status == ProviderStatus.STATUS_NORMAL;
    176     }
    177 
    178     private boolean areContactWritableAccountsAvailable() {
    179         return ContactsUtils.areContactWritableAccountsAvailable(this);
    180     }
    181 
    182     private boolean areGroupWritableAccountsAvailable() {
    183         return ContactsUtils.areGroupWritableAccountsAvailable(this);
    184     }
    185 
    186     /**
    187      * Initialize fragments that are (or may not be) in the layout.
    188      *
    189      * For the fragments that are in the layout, we initialize them in
    190      * {@link #createViewsAndFragments(Bundle)} after inflating the layout.
    191      *
    192      * However, the {@link ContactsUnavailableFragment} is a special fragment which may not
    193      * be in the layout, so we have to do the initialization here.
    194      *
    195      * The ContactsUnavailableFragment is always created at runtime.
    196      */
    197     @Override
    198     public void onAttachFragment(Fragment fragment) {
    199         if (fragment instanceof ContactsUnavailableFragment) {
    200             mContactsUnavailableFragment = (ContactsUnavailableFragment)fragment;
    201             mContactsUnavailableFragment.setOnContactsUnavailableActionListener(
    202                     new ContactsUnavailableFragmentListener());
    203         }
    204     }
    205 
    206     @Override
    207     protected void onCreate(Bundle savedState) {
    208         if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) {
    209             Log.d(Constants.PERFORMANCE_TAG, "PeopleActivity.onCreate start");
    210         }
    211         super.onCreate(savedState);
    212 
    213         if (!processIntent(false)) {
    214             finish();
    215             return;
    216         }
    217         mContactListFilterController = ContactListFilterController.getInstance(this);
    218         mContactListFilterController.checkFilterValidity(false);
    219         mContactListFilterController.addListener(this);
    220 
    221         mProviderStatusWatcher.addListener(this);
    222 
    223         mIsRecreatedInstance = (savedState != null);
    224         createViewsAndFragments(savedState);
    225 
    226         if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) {
    227             Log.d(Constants.PERFORMANCE_TAG, "PeopleActivity.onCreate finish");
    228         }
    229         getWindow().setBackgroundDrawable(null);
    230     }
    231 
    232     @Override
    233     protected void onNewIntent(Intent intent) {
    234         setIntent(intent);
    235         if (!processIntent(true)) {
    236             finish();
    237             return;
    238         }
    239         mActionBarAdapter.initialize(null, mRequest);
    240 
    241         mContactListFilterController.checkFilterValidity(false);
    242 
    243         // Re-configure fragments.
    244         configureFragments(true /* from request */);
    245         invalidateOptionsMenuIfNeeded();
    246     }
    247 
    248     /**
    249      * Resolve the intent and initialize {@link #mRequest}, and launch another activity if redirect
    250      * is needed.
    251      *
    252      * @param forNewIntent set true if it's called from {@link #onNewIntent(Intent)}.
    253      * @return {@code true} if {@link PeopleActivity} should continue running.  {@code false}
    254      *         if it shouldn't, in which case the caller should finish() itself and shouldn't do
    255      *         farther initialization.
    256      */
    257     private boolean processIntent(boolean forNewIntent) {
    258         // Extract relevant information from the intent
    259         mRequest = mIntentResolver.resolveIntent(getIntent());
    260         if (Log.isLoggable(TAG, Log.DEBUG)) {
    261             Log.d(TAG, this + " processIntent: forNewIntent=" + forNewIntent
    262                     + " intent=" + getIntent() + " request=" + mRequest);
    263         }
    264         if (!mRequest.isValid()) {
    265             setResult(RESULT_CANCELED);
    266             return false;
    267         }
    268 
    269         Intent redirect = mRequest.getRedirectIntent();
    270         if (redirect != null) {
    271             // Need to start a different activity
    272             startActivity(redirect);
    273             return false;
    274         }
    275 
    276         if (mRequest.getActionCode() == ContactsRequest.ACTION_VIEW_CONTACT) {
    277             redirect = new Intent(this, QuickContactActivity.class);
    278             redirect.setAction(Intent.ACTION_VIEW);
    279             redirect.setData(mRequest.getContactUri());
    280             startActivity(redirect);
    281             return false;
    282         }
    283         return true;
    284     }
    285 
    286     private void createViewsAndFragments(Bundle savedState) {
    287         // Disable the ActionBar so that we can use a Toolbar. This needs to be called before
    288         // setContentView().
    289         getWindow().requestFeature(Window.FEATURE_NO_TITLE);
    290 
    291         setContentView(R.layout.people_activity);
    292 
    293         final FragmentManager fragmentManager = getFragmentManager();
    294 
    295         // Hide all tabs (the current tab will later be reshown once a tab is selected)
    296         final FragmentTransaction transaction = fragmentManager.beginTransaction();
    297 
    298         mTabTitles = new String[TabState.COUNT];
    299         mTabTitles[TabState.FAVORITES] = getString(R.string.favorites_tab_label);
    300         mTabTitles[TabState.ALL] = getString(R.string.all_contacts_tab_label);
    301         mTabPager = getView(R.id.tab_pager);
    302         mTabPagerAdapter = new TabPagerAdapter();
    303         mTabPager.setAdapter(mTabPagerAdapter);
    304         mTabPager.setOnPageChangeListener(mTabPagerListener);
    305 
    306         // Configure toolbar and toolbar tabs. If in landscape mode, we  configure tabs differntly.
    307         final Toolbar toolbar = getView(R.id.toolbar);
    308         setActionBar(toolbar);
    309         final ViewPagerTabs portraitViewPagerTabs
    310                 = (ViewPagerTabs) findViewById(R.id.lists_pager_header);
    311         ViewPagerTabs landscapeViewPagerTabs = null;
    312         if (portraitViewPagerTabs ==  null) {
    313             landscapeViewPagerTabs = (ViewPagerTabs) getLayoutInflater().inflate(
    314                     R.layout.people_activity_tabs_lands, toolbar, /* attachToRoot = */ false);
    315             mViewPagerTabs = landscapeViewPagerTabs;
    316         } else {
    317             mViewPagerTabs = portraitViewPagerTabs;
    318         }
    319         mViewPagerTabs.setViewPager(mTabPager);
    320 
    321         final String FAVORITE_TAG = "tab-pager-favorite";
    322         final String ALL_TAG = "tab-pager-all";
    323 
    324         // Create the fragments and add as children of the view pager.
    325         // The pager adapter will only change the visibility; it'll never create/destroy
    326         // fragments.
    327         // However, if it's after screen rotation, the fragments have been re-created by
    328         // the fragment manager, so first see if there're already the target fragments
    329         // existing.
    330         mFavoritesFragment = (ContactTileListFragment)
    331                 fragmentManager.findFragmentByTag(FAVORITE_TAG);
    332         mAllFragment = (DefaultContactBrowseListFragment)
    333                 fragmentManager.findFragmentByTag(ALL_TAG);
    334 
    335         if (mFavoritesFragment == null) {
    336             mFavoritesFragment = new ContactTileListFragment();
    337             mAllFragment = new DefaultContactBrowseListFragment();
    338 
    339             transaction.add(R.id.tab_pager, mFavoritesFragment, FAVORITE_TAG);
    340             transaction.add(R.id.tab_pager, mAllFragment, ALL_TAG);
    341         }
    342 
    343         mFavoritesFragment.setListener(mFavoritesFragmentListener);
    344 
    345         mAllFragment.setOnContactListActionListener(new ContactBrowserActionListener());
    346 
    347         // Hide all fragments for now.  We adjust visibility when we get onSelectedTabChanged()
    348         // from ActionBarAdapter.
    349         transaction.hide(mFavoritesFragment);
    350         transaction.hide(mAllFragment);
    351 
    352         transaction.commitAllowingStateLoss();
    353         fragmentManager.executePendingTransactions();
    354 
    355         // Setting Properties after fragment is created
    356         mFavoritesFragment.setDisplayType(DisplayType.STREQUENT);
    357 
    358         mActionBarAdapter = new ActionBarAdapter(this, this, getActionBar(),
    359                 portraitViewPagerTabs, landscapeViewPagerTabs, toolbar);
    360         mActionBarAdapter.initialize(savedState, mRequest);
    361 
    362         // Add shadow under toolbar
    363         ViewUtil.addRectangularOutlineProvider(findViewById(R.id.toolbar_parent), getResources());
    364 
    365         // Configure action button
    366         final View floatingActionButtonContainer = findViewById(
    367                 R.id.floating_action_button_container);
    368         ViewUtil.setupFloatingActionButton(floatingActionButtonContainer, getResources());
    369         final ImageButton floatingActionButton = (ImageButton) findViewById(R.id.floating_action_button);
    370         floatingActionButton.setOnClickListener(this);
    371 
    372         invalidateOptionsMenuIfNeeded();
    373     }
    374 
    375     @Override
    376     protected void onStart() {
    377         if (!mFragmentInitialized) {
    378             mFragmentInitialized = true;
    379             /* Configure fragments if we haven't.
    380              *
    381              * Note it's a one-shot initialization, so we want to do this in {@link #onCreate}.
    382              *
    383              * However, because this method may indirectly touch views in fragments but fragments
    384              * created in {@link #configureContentView} using a {@link FragmentTransaction} will NOT
    385              * have views until {@link Activity#onCreate} finishes (they would if they were inflated
    386              * from a layout), we need to do it here in {@link #onStart()}.
    387              *
    388              * (When {@link Fragment#onCreateView} is called is different in the former case and
    389              * in the latter case, unfortunately.)
    390              *
    391              * Also, we skip most of the work in it if the activity is a re-created one.
    392              * (so the argument.)
    393              */
    394             configureFragments(!mIsRecreatedInstance);
    395         }
    396         super.onStart();
    397     }
    398 
    399     @Override
    400     protected void onPause() {
    401         mOptionsMenuContactsAvailable = false;
    402         mProviderStatusWatcher.stop();
    403         super.onPause();
    404     }
    405 
    406     @Override
    407     protected void onResume() {
    408         super.onResume();
    409 
    410         mProviderStatusWatcher.start();
    411         updateViewConfiguration(true);
    412 
    413         // Re-register the listener, which may have been cleared when onSaveInstanceState was
    414         // called.  See also: onSaveInstanceState
    415         mActionBarAdapter.setListener(this);
    416         mDisableOptionItemSelected = false;
    417         if (mTabPager != null) {
    418             mTabPager.setOnPageChangeListener(mTabPagerListener);
    419         }
    420         // Current tab may have changed since the last onSaveInstanceState().  Make sure
    421         // the actual contents match the tab.
    422         updateFragmentsVisibility();
    423     }
    424 
    425     @Override
    426     protected void onStop() {
    427         super.onStop();
    428     }
    429 
    430     @Override
    431     protected void onDestroy() {
    432         mProviderStatusWatcher.removeListener(this);
    433 
    434         // Some of variables will be null if this Activity redirects Intent.
    435         // See also onCreate() or other methods called during the Activity's initialization.
    436         if (mActionBarAdapter != null) {
    437             mActionBarAdapter.setListener(null);
    438         }
    439         if (mContactListFilterController != null) {
    440             mContactListFilterController.removeListener(this);
    441         }
    442 
    443         super.onDestroy();
    444     }
    445 
    446     private void configureFragments(boolean fromRequest) {
    447         if (fromRequest) {
    448             ContactListFilter filter = null;
    449             int actionCode = mRequest.getActionCode();
    450             boolean searchMode = mRequest.isSearchMode();
    451             final int tabToOpen;
    452             switch (actionCode) {
    453                 case ContactsRequest.ACTION_ALL_CONTACTS:
    454                     filter = ContactListFilter.createFilterWithType(
    455                             ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
    456                     tabToOpen = TabState.ALL;
    457                     break;
    458                 case ContactsRequest.ACTION_CONTACTS_WITH_PHONES:
    459                     filter = ContactListFilter.createFilterWithType(
    460                             ContactListFilter.FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY);
    461                     tabToOpen = TabState.ALL;
    462                     break;
    463 
    464                 case ContactsRequest.ACTION_FREQUENT:
    465                 case ContactsRequest.ACTION_STREQUENT:
    466                 case ContactsRequest.ACTION_STARRED:
    467                     tabToOpen = TabState.FAVORITES;
    468                     break;
    469                 case ContactsRequest.ACTION_VIEW_CONTACT:
    470                     tabToOpen = TabState.ALL;
    471                     break;
    472                 default:
    473                     tabToOpen = -1;
    474                     break;
    475             }
    476             if (tabToOpen != -1) {
    477                 mActionBarAdapter.setCurrentTab(tabToOpen);
    478             }
    479 
    480             if (filter != null) {
    481                 mContactListFilterController.setContactListFilter(filter, false);
    482                 searchMode = false;
    483             }
    484 
    485             if (mRequest.getContactUri() != null) {
    486                 searchMode = false;
    487             }
    488 
    489             mActionBarAdapter.setSearchMode(searchMode);
    490             configureContactListFragmentForRequest();
    491         }
    492 
    493         configureContactListFragment();
    494 
    495         invalidateOptionsMenuIfNeeded();
    496     }
    497 
    498     @Override
    499     public void onContactListFilterChanged() {
    500         if (mAllFragment == null || !mAllFragment.isAdded()) {
    501             return;
    502         }
    503 
    504         mAllFragment.setFilter(mContactListFilterController.getFilter());
    505 
    506         invalidateOptionsMenuIfNeeded();
    507     }
    508 
    509     /**
    510      * Handler for action bar actions.
    511      */
    512     @Override
    513     public void onAction(int action) {
    514         switch (action) {
    515             case ActionBarAdapter.Listener.Action.START_SEARCH_MODE:
    516                 // Tell the fragments that we're in the search mode
    517                 configureFragments(false /* from request */);
    518                 updateFragmentsVisibility();
    519                 invalidateOptionsMenu();
    520                 break;
    521             case ActionBarAdapter.Listener.Action.STOP_SEARCH_MODE:
    522                 setQueryTextToFragment("");
    523                 updateFragmentsVisibility();
    524                 invalidateOptionsMenu();
    525                 break;
    526             case ActionBarAdapter.Listener.Action.CHANGE_SEARCH_QUERY:
    527                 final String queryString = mActionBarAdapter.getQueryString();
    528                 setQueryTextToFragment(queryString);
    529                 updateDebugOptionsVisibility(
    530                         ENABLE_DEBUG_OPTIONS_HIDDEN_CODE.equals(queryString));
    531                 break;
    532             default:
    533                 throw new IllegalStateException("Unkonwn ActionBarAdapter action: " + action);
    534         }
    535     }
    536 
    537     @Override
    538     public void onSelectedTabChanged() {
    539         updateFragmentsVisibility();
    540     }
    541 
    542     @Override
    543     public void onUpButtonPressed() {
    544         onBackPressed();
    545     }
    546 
    547     private void updateDebugOptionsVisibility(boolean visible) {
    548         if (mEnableDebugMenuOptions != visible) {
    549             mEnableDebugMenuOptions = visible;
    550             invalidateOptionsMenu();
    551         }
    552     }
    553 
    554     /**
    555      * Updates the fragment/view visibility according to the current mode, such as
    556      * {@link ActionBarAdapter#isSearchMode()} and {@link ActionBarAdapter#getCurrentTab()}.
    557      */
    558     private void updateFragmentsVisibility() {
    559         int tab = mActionBarAdapter.getCurrentTab();
    560 
    561         if (mActionBarAdapter.isSearchMode()) {
    562             mTabPagerAdapter.setSearchMode(true);
    563         } else {
    564             // No smooth scrolling if quitting from the search mode.
    565             final boolean wasSearchMode = mTabPagerAdapter.isSearchMode();
    566             mTabPagerAdapter.setSearchMode(false);
    567             if (mTabPager.getCurrentItem() != tab) {
    568                 mTabPager.setCurrentItem(tab, !wasSearchMode);
    569             }
    570         }
    571         invalidateOptionsMenu();
    572         showEmptyStateForTab(tab);
    573     }
    574 
    575     private void showEmptyStateForTab(int tab) {
    576         if (mContactsUnavailableFragment != null) {
    577             switch (getTabPositionForTextDirection(tab)) {
    578                 case TabState.FAVORITES:
    579                     mContactsUnavailableFragment.setMessageText(
    580                             R.string.listTotalAllContactsZeroStarred, -1);
    581                     break;
    582                 case TabState.ALL:
    583                     mContactsUnavailableFragment.setMessageText(R.string.noContacts, -1);
    584                     break;
    585             }
    586             // When using the mContactsUnavailableFragment the ViewPager doesn't contain two views.
    587             // Therefore, we have to trick the ViewPagerTabs into thinking we have changed tabs
    588             // when the mContactsUnavailableFragment changes. Otherwise the tab strip won't move.
    589             mViewPagerTabs.onPageScrolled(tab, 0, 0);
    590         }
    591     }
    592 
    593     private class TabPagerListener implements ViewPager.OnPageChangeListener {
    594 
    595         // This package-protected constructor is here because of a possible compiler bug.
    596         // PeopleActivity$1.class should be generated due to the private outer/inner class access
    597         // needed here.  But for some reason, PeopleActivity$1.class is missing.
    598         // Since $1 class is needed as a jvm work around to get access to the inner class,
    599         // changing the constructor to package-protected or public will solve the problem.
    600         // To verify whether $1 class is needed, javap PeopleActivity$TabPagerListener and look for
    601         // references to PeopleActivity$1.
    602         //
    603         // When the constructor is private and PeopleActivity$1.class is missing, proguard will
    604         // correctly catch this and throw warnings and error out the build on user/userdebug builds.
    605         //
    606         // All private inner classes below also need this fix.
    607         TabPagerListener() {}
    608 
    609         @Override
    610         public void onPageScrollStateChanged(int state) {
    611             if (!mTabPagerAdapter.isSearchMode()) {
    612                 mViewPagerTabs.onPageScrollStateChanged(state);
    613             }
    614         }
    615 
    616         @Override
    617         public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    618             if (!mTabPagerAdapter.isSearchMode()) {
    619                 mViewPagerTabs.onPageScrolled(position, positionOffset, positionOffsetPixels);
    620             }
    621         }
    622 
    623         @Override
    624         public void onPageSelected(int position) {
    625             // Make sure not in the search mode, in which case position != TabState.ordinal().
    626             if (!mTabPagerAdapter.isSearchMode()) {
    627                 mActionBarAdapter.setCurrentTab(position, false);
    628                 mViewPagerTabs.onPageSelected(position);
    629                 showEmptyStateForTab(position);
    630                 invalidateOptionsMenu();
    631             }
    632         }
    633     }
    634 
    635     /**
    636      * Adapter for the {@link ViewPager}.  Unlike {@link FragmentPagerAdapter},
    637      * {@link #instantiateItem} returns existing fragments, and {@link #instantiateItem}/
    638      * {@link #destroyItem} show/hide fragments instead of attaching/detaching.
    639      *
    640      * In search mode, we always show the "all" fragment, and disable the swipe.  We change the
    641      * number of items to 1 to disable the swipe.
    642      *
    643      * TODO figure out a more straight way to disable swipe.
    644      */
    645     private class TabPagerAdapter extends PagerAdapter {
    646         private final FragmentManager mFragmentManager;
    647         private FragmentTransaction mCurTransaction = null;
    648 
    649         private boolean mTabPagerAdapterSearchMode;
    650 
    651         private Fragment mCurrentPrimaryItem;
    652 
    653         public TabPagerAdapter() {
    654             mFragmentManager = getFragmentManager();
    655         }
    656 
    657         public boolean isSearchMode() {
    658             return mTabPagerAdapterSearchMode;
    659         }
    660 
    661         public void setSearchMode(boolean searchMode) {
    662             if (searchMode == mTabPagerAdapterSearchMode) {
    663                 return;
    664             }
    665             mTabPagerAdapterSearchMode = searchMode;
    666             notifyDataSetChanged();
    667         }
    668 
    669         @Override
    670         public int getCount() {
    671             return mTabPagerAdapterSearchMode ? 1 : TabState.COUNT;
    672         }
    673 
    674         /** Gets called when the number of items changes. */
    675         @Override
    676         public int getItemPosition(Object object) {
    677             if (mTabPagerAdapterSearchMode) {
    678                 if (object == mAllFragment) {
    679                     return 0; // Only 1 page in search mode
    680                 }
    681             } else {
    682                 if (object == mFavoritesFragment) {
    683                     return getTabPositionForTextDirection(TabState.FAVORITES);
    684                 }
    685                 if (object == mAllFragment) {
    686                     return getTabPositionForTextDirection(TabState.ALL);
    687                 }
    688             }
    689             return POSITION_NONE;
    690         }
    691 
    692         @Override
    693         public void startUpdate(ViewGroup container) {
    694         }
    695 
    696         private Fragment getFragment(int position) {
    697             position = getTabPositionForTextDirection(position);
    698             if (mTabPagerAdapterSearchMode) {
    699                 if (position != 0) {
    700                     // This has only been observed in monkey tests.
    701                     // Let's log this issue, but not crash
    702                     Log.w(TAG, "Request fragment at position=" + position + ", eventhough we " +
    703                             "are in search mode");
    704                 }
    705                 return mAllFragment;
    706             } else {
    707                 if (position == TabState.FAVORITES) {
    708                     return mFavoritesFragment;
    709                 } else if (position == TabState.ALL) {
    710                     return mAllFragment;
    711                 }
    712             }
    713             throw new IllegalArgumentException("position: " + position);
    714         }
    715 
    716         @Override
    717         public Object instantiateItem(ViewGroup container, int position) {
    718             if (mCurTransaction == null) {
    719                 mCurTransaction = mFragmentManager.beginTransaction();
    720             }
    721             Fragment f = getFragment(position);
    722             mCurTransaction.show(f);
    723 
    724             // Non primary pages are not visible.
    725             f.setUserVisibleHint(f == mCurrentPrimaryItem);
    726             return f;
    727         }
    728 
    729         @Override
    730         public void destroyItem(ViewGroup container, int position, Object object) {
    731             if (mCurTransaction == null) {
    732                 mCurTransaction = mFragmentManager.beginTransaction();
    733             }
    734             mCurTransaction.hide((Fragment) object);
    735         }
    736 
    737         @Override
    738         public void finishUpdate(ViewGroup container) {
    739             if (mCurTransaction != null) {
    740                 mCurTransaction.commitAllowingStateLoss();
    741                 mCurTransaction = null;
    742                 mFragmentManager.executePendingTransactions();
    743             }
    744         }
    745 
    746         @Override
    747         public boolean isViewFromObject(View view, Object object) {
    748             return ((Fragment) object).getView() == view;
    749         }
    750 
    751         @Override
    752         public void setPrimaryItem(ViewGroup container, int position, Object object) {
    753             Fragment fragment = (Fragment) object;
    754             if (mCurrentPrimaryItem != fragment) {
    755                 if (mCurrentPrimaryItem != null) {
    756                     mCurrentPrimaryItem.setUserVisibleHint(false);
    757                 }
    758                 if (fragment != null) {
    759                     fragment.setUserVisibleHint(true);
    760                 }
    761                 mCurrentPrimaryItem = fragment;
    762             }
    763         }
    764 
    765         @Override
    766         public Parcelable saveState() {
    767             return null;
    768         }
    769 
    770         @Override
    771         public void restoreState(Parcelable state, ClassLoader loader) {
    772         }
    773 
    774         @Override
    775         public CharSequence getPageTitle(int position) {
    776             return mTabTitles[position];
    777         }
    778     }
    779 
    780     private void setQueryTextToFragment(String query) {
    781         mAllFragment.setQueryString(query, true);
    782         mAllFragment.setVisibleScrollbarEnabled(!mAllFragment.isSearchMode());
    783     }
    784 
    785     private void configureContactListFragmentForRequest() {
    786         Uri contactUri = mRequest.getContactUri();
    787         if (contactUri != null) {
    788             mAllFragment.setSelectedContactUri(contactUri);
    789         }
    790 
    791         mAllFragment.setFilter(mContactListFilterController.getFilter());
    792         setQueryTextToFragment(mActionBarAdapter.getQueryString());
    793 
    794         if (mRequest.isDirectorySearchEnabled()) {
    795             mAllFragment.setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_DEFAULT);
    796         } else {
    797             mAllFragment.setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_NONE);
    798         }
    799     }
    800 
    801     private void configureContactListFragment() {
    802         // Filter may be changed when this Activity is in background.
    803         mAllFragment.setFilter(mContactListFilterController.getFilter());
    804 
    805         mAllFragment.setVerticalScrollbarPosition(getScrollBarPosition());
    806         mAllFragment.setSelectionVisible(false);
    807     }
    808 
    809     private int getScrollBarPosition() {
    810         return isRTL() ? View.SCROLLBAR_POSITION_LEFT : View.SCROLLBAR_POSITION_RIGHT;
    811     }
    812 
    813     private boolean isRTL() {
    814         final Locale locale = Locale.getDefault();
    815         return TextUtils.getLayoutDirectionFromLocale(locale) == View.LAYOUT_DIRECTION_RTL;
    816     }
    817 
    818     @Override
    819     public void onProviderStatusChange() {
    820         updateViewConfiguration(false);
    821     }
    822 
    823     private void updateViewConfiguration(boolean forceUpdate) {
    824         ProviderStatusWatcher.Status providerStatus = mProviderStatusWatcher.getProviderStatus();
    825         if (!forceUpdate && (mProviderStatus != null)
    826                 && (providerStatus.status == mProviderStatus.status)) return;
    827         mProviderStatus = providerStatus;
    828 
    829         View contactsUnavailableView = findViewById(R.id.contacts_unavailable_view);
    830 
    831         if (mProviderStatus.status == ProviderStatus.STATUS_NORMAL) {
    832             // Ensure that the mTabPager is visible; we may have made it invisible below.
    833             contactsUnavailableView.setVisibility(View.GONE);
    834             if (mTabPager != null) {
    835                 mTabPager.setVisibility(View.VISIBLE);
    836             }
    837 
    838             if (mAllFragment != null) {
    839                 mAllFragment.setEnabled(true);
    840             }
    841         } else {
    842             // If there are no accounts on the device and we should show the "no account" prompt
    843             // (based on {@link SharedPreferences}), then launch the account setup activity so the
    844             // user can sign-in or create an account.
    845             //
    846             // Also check for ability to modify accounts.  In limited user mode, you can't modify
    847             // accounts so there is no point sending users to account setup activity.
    848             final UserManager userManager = UserManager.get(this);
    849             final boolean disallowModifyAccounts = userManager.getUserRestrictions().getBoolean(
    850                     UserManager.DISALLOW_MODIFY_ACCOUNTS);
    851             if (!disallowModifyAccounts && !areContactWritableAccountsAvailable() &&
    852                     AccountPromptUtils.shouldShowAccountPrompt(this)) {
    853                 AccountPromptUtils.neverShowAccountPromptAgain(this);
    854                 AccountPromptUtils.launchAccountPrompt(this);
    855                 return;
    856             }
    857 
    858             // Otherwise, continue setting up the page so that the user can still use the app
    859             // without an account.
    860             if (mAllFragment != null) {
    861                 mAllFragment.setEnabled(false);
    862             }
    863             if (mContactsUnavailableFragment == null) {
    864                 mContactsUnavailableFragment = new ContactsUnavailableFragment();
    865                 mContactsUnavailableFragment.setOnContactsUnavailableActionListener(
    866                         new ContactsUnavailableFragmentListener());
    867                 getFragmentManager().beginTransaction()
    868                         .replace(R.id.contacts_unavailable_container, mContactsUnavailableFragment)
    869                         .commitAllowingStateLoss();
    870             }
    871             mContactsUnavailableFragment.updateStatus(mProviderStatus);
    872 
    873             // Show the contactsUnavailableView, and hide the mTabPager so that we don't
    874             // see it sliding in underneath the contactsUnavailableView at the edges.
    875             contactsUnavailableView.setVisibility(View.VISIBLE);
    876             if (mTabPager != null) {
    877                 mTabPager.setVisibility(View.GONE);
    878             }
    879 
    880             showEmptyStateForTab(mActionBarAdapter.getCurrentTab());
    881         }
    882 
    883         invalidateOptionsMenuIfNeeded();
    884     }
    885 
    886     private final class ContactBrowserActionListener implements OnContactBrowserActionListener {
    887         ContactBrowserActionListener() {}
    888 
    889         @Override
    890         public void onSelectionChange() {
    891 
    892         }
    893 
    894         @Override
    895         public void onViewContactAction(Uri contactLookupUri) {
    896             Intent intent = QuickContact.composeQuickContactsIntent(PeopleActivity.this,
    897                     (Rect) null, contactLookupUri, QuickContactActivity.MODE_FULLY_EXPANDED, null);
    898             startActivity(intent);
    899         }
    900 
    901         @Override
    902         public void onDeleteContactAction(Uri contactUri) {
    903             ContactDeletionInteraction.start(PeopleActivity.this, contactUri, false);
    904         }
    905 
    906         @Override
    907         public void onFinishAction() {
    908             onBackPressed();
    909         }
    910 
    911         @Override
    912         public void onInvalidSelection() {
    913             ContactListFilter filter;
    914             ContactListFilter currentFilter = mAllFragment.getFilter();
    915             if (currentFilter != null
    916                     && currentFilter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) {
    917                 filter = ContactListFilter.createFilterWithType(
    918                         ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
    919                 mAllFragment.setFilter(filter);
    920             } else {
    921                 filter = ContactListFilter.createFilterWithType(
    922                         ContactListFilter.FILTER_TYPE_SINGLE_CONTACT);
    923                 mAllFragment.setFilter(filter, false);
    924             }
    925             mContactListFilterController.setContactListFilter(filter, true);
    926         }
    927     }
    928 
    929     private class ContactsUnavailableFragmentListener
    930             implements OnContactsUnavailableActionListener {
    931         ContactsUnavailableFragmentListener() {}
    932 
    933         @Override
    934         public void onCreateNewContactAction() {
    935             startActivity(new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI));
    936         }
    937 
    938         @Override
    939         public void onAddAccountAction() {
    940             Intent intent = new Intent(Settings.ACTION_ADD_ACCOUNT);
    941             intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
    942             intent.putExtra(Settings.EXTRA_AUTHORITIES,
    943                     new String[] { ContactsContract.AUTHORITY });
    944             startActivity(intent);
    945         }
    946 
    947         @Override
    948         public void onImportContactsFromFileAction() {
    949             ImportExportDialogFragment.show(getFragmentManager(), areContactsAvailable(),
    950                     PeopleActivity.class);
    951         }
    952 
    953         @Override
    954         public void onFreeInternalStorageAction() {
    955             startActivity(new Intent(Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS));
    956         }
    957     }
    958 
    959     private final class StrequentContactListFragmentListener
    960             implements ContactTileListFragment.Listener {
    961         StrequentContactListFragmentListener() {}
    962 
    963         @Override
    964         public void onContactSelected(Uri contactUri, Rect targetRect) {
    965             Intent intent = QuickContact.composeQuickContactsIntent(PeopleActivity.this,
    966                     targetRect, contactUri, QuickContactActivity.MODE_FULLY_EXPANDED, null);
    967             startActivity(intent);
    968         }
    969 
    970         @Override
    971         public void onCallNumberDirectly(String phoneNumber) {
    972             // No need to call phone number directly from People app.
    973             Log.w(TAG, "unexpected invocation of onCallNumberDirectly()");
    974         }
    975     }
    976 
    977     @Override
    978     public boolean onCreateOptionsMenu(Menu menu) {
    979         if (!areContactsAvailable()) {
    980             // If contacts aren't available, hide all menu items.
    981             return false;
    982         }
    983         super.onCreateOptionsMenu(menu);
    984 
    985         MenuInflater inflater = getMenuInflater();
    986         inflater.inflate(R.menu.people_options, menu);
    987 
    988         return true;
    989     }
    990 
    991     private void invalidateOptionsMenuIfNeeded() {
    992         if (isOptionsMenuChanged()) {
    993             invalidateOptionsMenu();
    994         }
    995     }
    996 
    997     public boolean isOptionsMenuChanged() {
    998         if (mOptionsMenuContactsAvailable != areContactsAvailable()) {
    999             return true;
   1000         }
   1001 
   1002         if (mAllFragment != null && mAllFragment.isOptionsMenuChanged()) {
   1003             return true;
   1004         }
   1005 
   1006         return false;
   1007     }
   1008 
   1009     @Override
   1010     public boolean onPrepareOptionsMenu(Menu menu) {
   1011         mOptionsMenuContactsAvailable = areContactsAvailable();
   1012         if (!mOptionsMenuContactsAvailable) {
   1013             return false;
   1014         }
   1015 
   1016         // Get references to individual menu items in the menu
   1017         final MenuItem contactsFilterMenu = menu.findItem(R.id.menu_contacts_filter);
   1018         final MenuItem clearFrequentsMenu = menu.findItem(R.id.menu_clear_frequents);
   1019         final MenuItem helpMenu = menu.findItem(R.id.menu_help);
   1020 
   1021         final boolean isSearchMode = mActionBarAdapter.isSearchMode();
   1022         if (isSearchMode) {
   1023             contactsFilterMenu.setVisible(false);
   1024             clearFrequentsMenu.setVisible(false);
   1025             helpMenu.setVisible(false);
   1026         } else {
   1027             switch (getTabPositionForTextDirection(mActionBarAdapter.getCurrentTab())) {
   1028                 case TabState.FAVORITES:
   1029                     contactsFilterMenu.setVisible(false);
   1030                     clearFrequentsMenu.setVisible(hasFrequents());
   1031                     break;
   1032                 case TabState.ALL:
   1033                     contactsFilterMenu.setVisible(true);
   1034                     clearFrequentsMenu.setVisible(false);
   1035                     break;
   1036             }
   1037             HelpUtils.prepareHelpMenuItem(this, helpMenu, R.string.help_url_people_main);
   1038         }
   1039         final boolean showMiscOptions = !isSearchMode;
   1040         makeMenuItemVisible(menu, R.id.menu_search, showMiscOptions);
   1041         makeMenuItemVisible(menu, R.id.menu_import_export, showMiscOptions);
   1042         makeMenuItemVisible(menu, R.id.menu_accounts, showMiscOptions);
   1043         makeMenuItemVisible(menu, R.id.menu_settings,
   1044                 showMiscOptions && !ContactsPreferenceActivity.isEmpty(this));
   1045 
   1046         // Debug options need to be visible even in search mode.
   1047         makeMenuItemVisible(menu, R.id.export_database, mEnableDebugMenuOptions);
   1048 
   1049         return true;
   1050     }
   1051 
   1052     /**
   1053      * Returns whether there are any frequently contacted people being displayed
   1054      * @return
   1055      */
   1056     private boolean hasFrequents() {
   1057         return mFavoritesFragment.hasFrequents();
   1058     }
   1059 
   1060     private void makeMenuItemVisible(Menu menu, int itemId, boolean visible) {
   1061         MenuItem item =menu.findItem(itemId);
   1062         if (item != null) {
   1063             item.setVisible(visible);
   1064         }
   1065     }
   1066 
   1067     @Override
   1068     public boolean onOptionsItemSelected(MenuItem item) {
   1069         if (mDisableOptionItemSelected) {
   1070             return false;
   1071         }
   1072 
   1073         switch (item.getItemId()) {
   1074             case android.R.id.home: {
   1075                 // The home icon on the action bar is pressed
   1076                 if (mActionBarAdapter.isUpShowing()) {
   1077                     // "UP" icon press -- should be treated as "back".
   1078                     onBackPressed();
   1079                 }
   1080                 return true;
   1081             }
   1082             case R.id.menu_settings: {
   1083                 final Intent intent = new Intent(this, ContactsPreferenceActivity.class);
   1084                 // Since there is only one section right now, make sure it is selected on
   1085                 // small screens.
   1086                 intent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT,
   1087                         DisplayOptionsPreferenceFragment.class.getName());
   1088                 // By default, the title of the activity should be equivalent to the fragment
   1089                 // title. We set this argument to avoid this. Because of a bug, the following
   1090                 // line isn't necessary. But, once the bug is fixed this may become necessary.
   1091                 // b/5045558 refers to this issue, as well as another.
   1092                 intent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT_TITLE,
   1093                         R.string.activity_title_settings);
   1094                 startActivity(intent);
   1095                 return true;
   1096             }
   1097             case R.id.menu_contacts_filter: {
   1098                 AccountFilterUtil.startAccountFilterActivityForResult(
   1099                         this, SUBACTIVITY_ACCOUNT_FILTER,
   1100                         mContactListFilterController.getFilter());
   1101                 return true;
   1102             }
   1103             case R.id.menu_search: {
   1104                 onSearchRequested();
   1105                 return true;
   1106             }
   1107             case R.id.menu_import_export: {
   1108                 ImportExportDialogFragment.show(getFragmentManager(), areContactsAvailable(),
   1109                         PeopleActivity.class);
   1110                 return true;
   1111             }
   1112             case R.id.menu_clear_frequents: {
   1113                 ClearFrequentsDialog.show(getFragmentManager());
   1114                 return true;
   1115             }
   1116             case R.id.menu_accounts: {
   1117                 final Intent intent = new Intent(Settings.ACTION_SYNC_SETTINGS);
   1118                 intent.putExtra(Settings.EXTRA_AUTHORITIES, new String[] {
   1119                     ContactsContract.AUTHORITY
   1120                 });
   1121                 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
   1122                 startActivity(intent);
   1123                 return true;
   1124             }
   1125             case R.id.export_database: {
   1126                 final Intent intent = new Intent("com.android.providers.contacts.DUMP_DATABASE");
   1127                 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
   1128                 startActivity(intent);
   1129                 return true;
   1130             }
   1131         }
   1132         return false;
   1133     }
   1134 
   1135     @Override
   1136     public boolean onSearchRequested() { // Search key pressed.
   1137         mActionBarAdapter.setSearchMode(true);
   1138         return true;
   1139     }
   1140 
   1141     @Override
   1142     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
   1143         switch (requestCode) {
   1144             case SUBACTIVITY_ACCOUNT_FILTER: {
   1145                 AccountFilterUtil.handleAccountFilterResult(
   1146                         mContactListFilterController, resultCode, data);
   1147                 break;
   1148             }
   1149 
   1150             // TODO: Using the new startActivityWithResultFromFragment API this should not be needed
   1151             // anymore
   1152             case ContactEntryListFragment.ACTIVITY_REQUEST_CODE_PICKER:
   1153                 if (resultCode == RESULT_OK) {
   1154                     mAllFragment.onPickerResult(data);
   1155                 }
   1156 
   1157 // TODO fix or remove multipicker code
   1158 //                else if (resultCode == RESULT_CANCELED && mMode == MODE_PICK_MULTIPLE_PHONES) {
   1159 //                    // Finish the activity if the sub activity was canceled as back key is used
   1160 //                    // to confirm user selection in MODE_PICK_MULTIPLE_PHONES.
   1161 //                    finish();
   1162 //                }
   1163 //                break;
   1164         }
   1165     }
   1166 
   1167     @Override
   1168     public boolean onKeyDown(int keyCode, KeyEvent event) {
   1169         // TODO move to the fragment
   1170         switch (keyCode) {
   1171 //            case KeyEvent.KEYCODE_CALL: {
   1172 //                if (callSelection()) {
   1173 //                    return true;
   1174 //                }
   1175 //                break;
   1176 //            }
   1177 
   1178             case KeyEvent.KEYCODE_DEL: {
   1179                 if (deleteSelection()) {
   1180                     return true;
   1181                 }
   1182                 break;
   1183             }
   1184             default: {
   1185                 // Bring up the search UI if the user starts typing
   1186                 final int unicodeChar = event.getUnicodeChar();
   1187                 if ((unicodeChar != 0)
   1188                         // If COMBINING_ACCENT is set, it's not a unicode character.
   1189                         && ((unicodeChar & KeyCharacterMap.COMBINING_ACCENT) == 0)
   1190                         && !Character.isWhitespace(unicodeChar)) {
   1191                     String query = new String(new int[]{ unicodeChar }, 0, 1);
   1192                     if (!mActionBarAdapter.isSearchMode()) {
   1193                         mActionBarAdapter.setSearchMode(true);
   1194                         mActionBarAdapter.setQueryString(query);
   1195                         return true;
   1196                     }
   1197                 }
   1198             }
   1199         }
   1200 
   1201         return super.onKeyDown(keyCode, event);
   1202     }
   1203 
   1204     @Override
   1205     public void onBackPressed() {
   1206         if (mActionBarAdapter.isSearchMode()) {
   1207             mActionBarAdapter.setSearchMode(false);
   1208         } else {
   1209             super.onBackPressed();
   1210         }
   1211     }
   1212 
   1213     private boolean deleteSelection() {
   1214         // TODO move to the fragment
   1215 //        if (mActionCode == ContactsRequest.ACTION_DEFAULT) {
   1216 //            final int position = mListView.getSelectedItemPosition();
   1217 //            if (position != ListView.INVALID_POSITION) {
   1218 //                Uri contactUri = getContactUri(position);
   1219 //                if (contactUri != null) {
   1220 //                    doContactDelete(contactUri);
   1221 //                    return true;
   1222 //                }
   1223 //            }
   1224 //        }
   1225         return false;
   1226     }
   1227 
   1228     @Override
   1229     protected void onSaveInstanceState(Bundle outState) {
   1230         super.onSaveInstanceState(outState);
   1231         mActionBarAdapter.onSaveInstanceState(outState);
   1232 
   1233         // Clear the listener to make sure we don't get callbacks after onSaveInstanceState,
   1234         // in order to avoid doing fragment transactions after it.
   1235         // TODO Figure out a better way to deal with the issue.
   1236         mDisableOptionItemSelected = true;
   1237         mActionBarAdapter.setListener(null);
   1238         if (mTabPager != null) {
   1239             mTabPager.setOnPageChangeListener(null);
   1240         }
   1241     }
   1242 
   1243     @Override
   1244     protected void onRestoreInstanceState(Bundle savedInstanceState) {
   1245         super.onRestoreInstanceState(savedInstanceState);
   1246         // In our own lifecycle, the focus is saved and restore but later taken away by the
   1247         // ViewPager. As a hack, we force focus on the SearchView if we know that we are searching.
   1248         // This fixes the keyboard going away on screen rotation
   1249         if (mActionBarAdapter.isSearchMode()) {
   1250             mActionBarAdapter.setFocusOnSearchView();
   1251         }
   1252     }
   1253 
   1254     @Override
   1255     public DialogManager getDialogManager() {
   1256         return mDialogManager;
   1257     }
   1258 
   1259     @Override
   1260     public void onClick(View view) {
   1261         switch (view.getId()) {
   1262             case R.id.floating_action_button:
   1263                 Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
   1264                 Bundle extras = getIntent().getExtras();
   1265                 if (extras != null) {
   1266                     intent.putExtras(extras);
   1267                 }
   1268                 try {
   1269                     startActivity(intent);
   1270                 } catch (ActivityNotFoundException ex) {
   1271                     Toast.makeText(PeopleActivity.this, R.string.missing_app,
   1272                             Toast.LENGTH_SHORT).show();
   1273                 }
   1274                 break;
   1275         default:
   1276             Log.wtf(TAG, "Unexpected onClick event from " + view);
   1277         }
   1278     }
   1279 
   1280     /**
   1281      * Returns the tab position adjusted for the text direction.
   1282      */
   1283     private int getTabPositionForTextDirection(int position) {
   1284         if (isRTL()) {
   1285             return TabState.COUNT - 1 - position;
   1286         }
   1287         return position;
   1288     }
   1289 }
   1290