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