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.accounts.Account;
     20 import android.app.Fragment;
     21 import android.app.FragmentManager;
     22 import android.app.FragmentTransaction;
     23 import android.content.BroadcastReceiver;
     24 import android.content.ContentResolver;
     25 import android.content.ContentUris;
     26 import android.content.Context;
     27 import android.content.Intent;
     28 import android.content.IntentFilter;
     29 import android.content.SyncStatusObserver;
     30 import android.content.res.Configuration;
     31 import android.graphics.Color;
     32 import android.net.Uri;
     33 import android.os.Bundle;
     34 import android.os.Handler;
     35 import android.provider.ContactsContract;
     36 import android.provider.ContactsContract.Intents;
     37 import android.provider.ContactsContract.ProviderStatus;
     38 import android.support.annotation.LayoutRes;
     39 import android.support.design.widget.CoordinatorLayout;
     40 import android.support.design.widget.Snackbar;
     41 import android.support.v4.content.ContextCompat;
     42 import android.support.v4.content.LocalBroadcastManager;
     43 import android.support.v4.view.GravityCompat;
     44 import android.support.v4.widget.DrawerLayout;
     45 import android.support.v4.widget.SwipeRefreshLayout;
     46 import android.support.v7.app.ActionBarDrawerToggle;
     47 import android.support.v7.app.AppCompatActivity;
     48 import android.support.v7.widget.Toolbar;
     49 import android.util.Log;
     50 import android.view.KeyCharacterMap;
     51 import android.view.KeyEvent;
     52 import android.view.LayoutInflater;
     53 import android.view.View;
     54 import android.view.ViewGroup;
     55 import android.view.accessibility.AccessibilityEvent;
     56 import android.view.accessibility.AccessibilityManager;
     57 import android.widget.ImageButton;
     58 import android.widget.Toast;
     59 
     60 import com.android.contacts.AppCompatContactsActivity;
     61 import com.android.contacts.ContactSaveService;
     62 import com.android.contacts.R;
     63 import com.android.contacts.compat.CompatUtils;
     64 import com.android.contacts.drawer.DrawerFragment;
     65 import com.android.contacts.drawer.DrawerFragment.DrawerFragmentListener;
     66 import com.android.contacts.editor.ContactEditorFragment;
     67 import com.android.contacts.editor.SelectAccountDialogFragment;
     68 import com.android.contacts.group.GroupListItem;
     69 import com.android.contacts.group.GroupMembersFragment;
     70 import com.android.contacts.group.GroupNameEditDialogFragment;
     71 import com.android.contacts.group.GroupUtil;
     72 import com.android.contacts.list.AccountFilterActivity;
     73 import com.android.contacts.list.ContactListFilter;
     74 import com.android.contacts.list.ContactListFilterController;
     75 import com.android.contacts.list.ContactListFilterController.ContactListFilterListener;
     76 import com.android.contacts.list.ContactsIntentResolver;
     77 import com.android.contacts.list.ContactsRequest;
     78 import com.android.contacts.list.ContactsUnavailableFragment;
     79 import com.android.contacts.list.DefaultContactBrowseListFragment;
     80 import com.android.contacts.list.MultiSelectContactsListFragment;
     81 import com.android.contacts.list.ProviderStatusWatcher;
     82 import com.android.contacts.list.ProviderStatusWatcher.ProviderStatusListener;
     83 import com.android.contacts.logging.Logger;
     84 import com.android.contacts.logging.ScreenEvent.ScreenType;
     85 import com.android.contacts.model.AccountTypeManager;
     86 import com.android.contacts.model.account.AccountInfo;
     87 import com.android.contacts.model.account.AccountWithDataSet;
     88 import com.android.contacts.preference.ContactsPreferenceActivity;
     89 import com.android.contacts.util.AccountFilterUtil;
     90 import com.android.contacts.util.Constants;
     91 import com.android.contacts.util.ImplicitIntentsUtil;
     92 import com.android.contacts.util.MaterialColorMapUtils;
     93 import com.android.contacts.util.SharedPreferenceUtil;
     94 import com.android.contacts.util.SyncUtil;
     95 import com.android.contacts.util.ViewUtil;
     96 import com.android.contacts.widget.FloatingActionButtonController;
     97 import com.android.contactsbind.FeatureHighlightHelper;
     98 import com.android.contactsbind.HelpUtils;
     99 import com.android.contactsbind.ObjectFactory;
    100 
    101 import com.google.common.util.concurrent.Futures;
    102 
    103 import java.util.Collections;
    104 import java.util.List;
    105 import java.util.concurrent.atomic.AtomicInteger;
    106 
    107 /**
    108  * Displays a list to browse contacts.
    109  */
    110 public class PeopleActivity extends AppCompatContactsActivity implements
    111         DrawerFragmentListener,
    112         SelectAccountDialogFragment.Listener {
    113 
    114     /** Possible views of Contacts app. */
    115     public enum ContactsView {
    116         NONE,
    117         ALL_CONTACTS,
    118         ASSISTANT,
    119         GROUP_VIEW,
    120         ACCOUNT_VIEW,
    121     }
    122 
    123     private static final String TAG = "PeopleActivity";
    124     private static final String TAG_ALL = "contacts-all";
    125     private static final String TAG_UNAVAILABLE = "contacts-unavailable";
    126     private static final String TAG_GROUP_VIEW = "contacts-groups";
    127     private static final String TAG_SELECT_ACCOUNT_DIALOG = "selectAccountDialog";
    128     private static final String TAG_GROUP_NAME_EDIT_DIALOG = "groupNameEditDialog";
    129 
    130     public static final String TAG_ASSISTANT = "contacts-assistant";
    131     public static final String TAG_SECOND_LEVEL = "second-level";
    132     public static final String TAG_THIRD_LEVEL = "third-level";
    133     public static final String TAG_ASSISTANT_HELPER = "assistant-helper";
    134     public static final String TAG_DUPLICATES = "DuplicatesFragment";
    135     public static final String TAG_DUPLICATES_UTIL = "DuplicatesUtilFragment";
    136 
    137     private static final String KEY_GROUP_URI = "groupUri";
    138     private static final String KEY_CONTACTS_VIEW = "contactsView";
    139     private static final String KEY_NEW_GROUP_ACCOUNT = "newGroupAccount";
    140 
    141     private static final long DRAWER_CLOSE_DELAY = 300L;
    142 
    143     private ContactsIntentResolver mIntentResolver;
    144     private ContactsRequest mRequest;
    145     private AccountTypeManager mAccountTypeManager;
    146 
    147     private FloatingActionButtonController mFloatingActionButtonController;
    148     private View mFloatingActionButtonContainer;
    149     private boolean wasLastFabAnimationScaleIn = false;
    150 
    151     private ProviderStatusWatcher mProviderStatusWatcher;
    152     private Integer mProviderStatus;
    153 
    154     private BroadcastReceiver mSaveServiceListener;
    155 
    156     private boolean mShouldSwitchToGroupView;
    157 
    158     private ContactsView mCurrentView;
    159 
    160     private CoordinatorLayout mLayoutRoot;
    161 
    162     /**
    163      * Showing a list of Contacts. Also used for showing search results in search mode.
    164      */
    165     private DefaultContactBrowseListFragment mContactsListFragment;
    166 
    167     private GroupMembersFragment mMembersFragment;
    168     private Uri mGroupUri;
    169 
    170     /**
    171      * True if this activity instance is a re-created one.  i.e. set true after orientation change.
    172      */
    173     private boolean mIsRecreatedInstance;
    174 
    175     private boolean mShouldSwitchToAllContacts;
    176 
    177     /** Sequential ID assigned to each instance; used for logging */
    178     private final int mInstanceId;
    179     private static final AtomicInteger sNextInstanceId = new AtomicInteger();
    180 
    181     private ContactListFilterController mContactListFilterController;
    182 
    183     /** Navigation drawer related */
    184     private DrawerLayout mDrawerLayout;
    185     private DrawerFragment mDrawerFragment;
    186     private ContactsActionBarDrawerToggle mToggle;
    187     private Toolbar mToolbar;
    188 
    189     // The account the new group will be created under.
    190     private AccountWithDataSet mNewGroupAccount;
    191 
    192     private Object mStatusChangeListenerHandle;
    193 
    194     private final Handler mHandler = new Handler();
    195 
    196     private SyncStatusObserver mSyncStatusObserver = new SyncStatusObserver() {
    197         public void onStatusChanged(int which) {
    198             mHandler.post(new Runnable() {
    199                 public void run() {
    200                     onSyncStateUpdated();
    201                 }
    202             });
    203         }
    204     };
    205 
    206     // Update sync status for accounts in current ContactListFilter
    207     private void onSyncStateUpdated() {
    208         if (isListFragmentInSearchMode() || isListFragmentInSelectionMode()) {
    209             return;
    210         }
    211 
    212         final ContactListFilter filter = mContactListFilterController.getFilter();
    213         if (filter != null) {
    214             final SwipeRefreshLayout swipeRefreshLayout =
    215                     mContactsListFragment.getSwipeRefreshLayout();
    216             if (swipeRefreshLayout == null) {
    217                 if (Log.isLoggable(TAG, Log.DEBUG)) {
    218                     Log.d(TAG, "Can not load swipeRefreshLayout, swipeRefreshLayout is null");
    219                 }
    220                 return;
    221             }
    222 
    223             final List<AccountWithDataSet> accounts;
    224             if (filter.filterType == ContactListFilter.FILTER_TYPE_ACCOUNT &&
    225                     filter.isGoogleAccountType()) {
    226                 accounts = Collections.singletonList(new AccountWithDataSet(filter.accountName,
    227                         filter.accountType, null));
    228             } else if (filter.shouldShowSyncState()) {
    229                 accounts = AccountInfo.extractAccounts(
    230                         mAccountTypeManager.getWritableGoogleAccounts());
    231             } else {
    232                 accounts = Collections.emptyList();
    233             }
    234             if (SyncUtil.isAnySyncing(accounts)) {
    235                 return;
    236             }
    237             swipeRefreshLayout.setRefreshing(false);
    238         }
    239     }
    240 
    241     public void showConnectionErrorMsg() {
    242         Snackbar.make(mLayoutRoot, R.string.connection_error_message, Snackbar.LENGTH_LONG).show();
    243     }
    244 
    245     private final ContactListFilterListener mFilterListener = new ContactListFilterListener() {
    246         @Override
    247         public void onContactListFilterChanged() {
    248             final ContactListFilter filter = mContactListFilterController.getFilter();
    249             handleFilterChangeForFragment(filter);
    250             handleFilterChangeForActivity(filter);
    251         }
    252     };
    253 
    254     private final ProviderStatusListener mProviderStatusListener = new ProviderStatusListener() {
    255         @Override
    256         public void onProviderStatusChange() {
    257             // TODO see if it works with drawer fragment.
    258             updateViewConfiguration(false);
    259         }
    260     };
    261 
    262     private class ContactsActionBarDrawerToggle extends ActionBarDrawerToggle {
    263         private boolean mMenuClickedBefore = SharedPreferenceUtil.getHamburgerMenuClickedBefore(
    264                 PeopleActivity.this);
    265 
    266         public ContactsActionBarDrawerToggle(AppCompatActivity activity, DrawerLayout drawerLayout,
    267                 Toolbar toolbar, int openDrawerContentDescRes, int closeDrawerContentDescRes) {
    268             super(activity, drawerLayout, toolbar, openDrawerContentDescRes,
    269                     closeDrawerContentDescRes);
    270         }
    271 
    272         @Override
    273         public void onDrawerOpened(View drawerView) {
    274             super.onDrawerOpened(drawerView);
    275             if (!mMenuClickedBefore) {
    276                 SharedPreferenceUtil.setHamburgerMenuClickedBefore(PeopleActivity.this);
    277                 mMenuClickedBefore = true;
    278             }
    279             drawerView.requestFocus();
    280             invalidateOptionsMenu();
    281             // Stop search and selection mode like Gmail and Keep. Otherwise, if user switches to
    282             // another fragment in navigation drawer, the current search/selection mode will be
    283             // overlaid by the action bar of the newly-created fragment.
    284             stopSearchAndSelection();
    285             updateStatusBarBackground();
    286         }
    287 
    288         private void stopSearchAndSelection() {
    289             final MultiSelectContactsListFragment listFragment;
    290             if (isAllContactsView() || isAccountView()) {
    291                 listFragment = getListFragment();
    292             } else if (isGroupView()) {
    293                 listFragment = getGroupFragment();
    294             } else {
    295                 listFragment = null;
    296             }
    297             if (listFragment == null) {
    298                 return;
    299             }
    300             final ActionBarAdapter actionBarAdapter = listFragment.getActionBarAdapter();
    301             if (actionBarAdapter == null) {
    302                 return;
    303             }
    304             if (actionBarAdapter.isSearchMode()) {
    305                 actionBarAdapter.setSearchMode(false);
    306             } else if (actionBarAdapter.isSelectionMode()) {
    307                 actionBarAdapter.setSelectionMode(false);
    308             }
    309         }
    310 
    311         @Override
    312         public void onDrawerClosed(View view) {
    313             super.onDrawerClosed(view);
    314             invalidateOptionsMenu();
    315         }
    316 
    317         @Override
    318         public void onDrawerStateChanged(int newState) {
    319             super.onDrawerStateChanged(newState);
    320             // Set transparent status bar when drawer starts to move.
    321             if (newState != DrawerLayout.STATE_IDLE) {
    322                 updateStatusBarBackground();
    323             }
    324         }
    325     }
    326 
    327 
    328     public PeopleActivity() {
    329         mInstanceId = sNextInstanceId.getAndIncrement();
    330         mIntentResolver = new ContactsIntentResolver(this);
    331         mProviderStatusWatcher = ProviderStatusWatcher.getInstance(this);
    332     }
    333 
    334     @Override
    335     public String toString() {
    336         // Shown on logcat
    337         return String.format("%s@%d", getClass().getSimpleName(), mInstanceId);
    338     }
    339 
    340     private boolean areContactsAvailable() {
    341         return (mProviderStatus != null) && mProviderStatus.equals(ProviderStatus.STATUS_NORMAL);
    342     }
    343 
    344     @Override
    345     protected void onCreate(Bundle savedState) {
    346         if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) {
    347             Log.d(Constants.PERFORMANCE_TAG, "PeopleActivity.onCreate start");
    348         }
    349 
    350         // Make sure this is *before* calling super.onCreate
    351         setTheme(R.style.PeopleActivityTheme);
    352         super.onCreate(savedState);
    353 
    354         mAccountTypeManager = AccountTypeManager.getInstance(this);
    355         mContactListFilterController = ContactListFilterController.getInstance(this);
    356 
    357         RequestPermissionsActivity.startPermissionActivityIfNeeded(this);
    358 
    359         if (!processIntent(false)) {
    360             finish();
    361             return;
    362         }
    363 
    364         mContactListFilterController.checkFilterValidity(false);
    365 
    366         super.setContentView(R.layout.contacts_drawer_activity);
    367 
    368         // Set up the action bar.
    369         mToolbar = getView(R.id.toolbar);
    370         setSupportActionBar(mToolbar);
    371 
    372         // Add shadow under toolbar.
    373         ViewUtil.addRectangularOutlineProvider(findViewById(R.id.toolbar_parent), getResources());
    374 
    375         // Set up hamburger button.
    376         mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
    377         mDrawerFragment = (DrawerFragment) getFragmentManager().findFragmentById(R.id.drawer);
    378         mToggle = new ContactsActionBarDrawerToggle(this, mDrawerLayout, mToolbar,
    379                 R.string.navigation_drawer_open, R.string.navigation_drawer_close);
    380         mDrawerLayout.setDrawerListener(mToggle);
    381         // Set fallback handler for when drawer is disabled.
    382         mToggle.setToolbarNavigationClickListener(new View.OnClickListener() {
    383             @Override
    384             public void onClick(View v) {
    385                 onBackPressed();
    386             }
    387         });
    388 
    389         // Set up navigation mode.
    390         if (savedState != null) {
    391             mCurrentView = ContactsView.values()[savedState.getInt(KEY_CONTACTS_VIEW)];
    392         } else {
    393             mCurrentView = ContactsView.ALL_CONTACTS;
    394         }
    395 
    396         if (savedState != null && savedState.containsKey(KEY_NEW_GROUP_ACCOUNT)) {
    397             mNewGroupAccount = AccountWithDataSet.unstringify(
    398                     savedState.getString(KEY_NEW_GROUP_ACCOUNT));
    399         }
    400 
    401         mContactListFilterController.addListener(mFilterListener);
    402         mProviderStatusWatcher.addListener(mProviderStatusListener);
    403 
    404         mIsRecreatedInstance = (savedState != null);
    405 
    406         if (mIsRecreatedInstance) {
    407             mGroupUri = savedState.getParcelable(KEY_GROUP_URI);
    408         }
    409 
    410         createViewsAndFragments();
    411 
    412         if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) {
    413             Log.d(Constants.PERFORMANCE_TAG, "PeopleActivity.onCreate finish");
    414         }
    415         getWindow().setBackgroundDrawable(null);
    416     }
    417 
    418     @Override
    419     protected void onNewIntent(Intent intent) {
    420         final String action = intent.getAction();
    421         if (GroupUtil.ACTION_CREATE_GROUP.equals(action)) {
    422             mGroupUri = intent.getData();
    423             if (mGroupUri == null) {
    424                 toast(R.string.groupSavedErrorToast);
    425                 return;
    426             }
    427             if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "Received group URI " + mGroupUri);
    428             switchView(ContactsView.GROUP_VIEW);
    429             mMembersFragment.toastForSaveAction(action);
    430             return;
    431         }
    432 
    433         if (isGroupSaveAction(action)) {
    434             mGroupUri = intent.getData();
    435             if (mGroupUri == null) {
    436                 popSecondLevel();
    437                 toast(R.string.groupSavedErrorToast);
    438                 return;
    439             }
    440             if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "Received group URI " + mGroupUri);
    441             // ACTION_REMOVE_FROM_GROUP doesn't reload data, so it shouldn't cause b/32223934
    442             // but it's necessary to use the previous fragment since
    443             // GroupMembersFragment#mIsEditMode needs to be persisted between remove actions.
    444             if (GroupUtil.ACTION_REMOVE_FROM_GROUP.equals(action)) {
    445                 switchToOrUpdateGroupView(action);
    446             } else {
    447                 switchView(ContactsView.GROUP_VIEW);
    448             }
    449             mMembersFragment.toastForSaveAction(action);
    450         }
    451 
    452         setIntent(intent);
    453 
    454         if (!processIntent(true)) {
    455             finish();
    456             return;
    457         }
    458 
    459         mContactListFilterController.checkFilterValidity(false);
    460 
    461         if (!isInSecondLevel()) {
    462             // Re-initialize ActionBarAdapter because {@link #onNewIntent(Intent)} doesn't invoke
    463             // {@link Fragment#onActivityCreated(Bundle)} where we initialize ActionBarAdapter
    464             // initially.
    465             mContactsListFragment.setParameters(/* ContactsRequest */ mRequest,
    466                     /* fromOnNewIntent */ true);
    467             mContactsListFragment.initializeActionBarAdapter(null);
    468         }
    469 
    470         initializeFabVisibility();
    471         invalidateOptionsMenuIfNeeded();
    472     }
    473 
    474     private static boolean isGroupSaveAction(String action) {
    475         return GroupUtil.ACTION_UPDATE_GROUP.equals(action)
    476                 || GroupUtil.ACTION_ADD_TO_GROUP.equals(action)
    477                 || GroupUtil.ACTION_REMOVE_FROM_GROUP.equals(action);
    478     }
    479 
    480     private void toast(int resId) {
    481         if (resId >= 0) {
    482             Toast.makeText(this, resId, Toast.LENGTH_SHORT).show();
    483         }
    484     }
    485 
    486     /**
    487      * Resolve the intent and initialize {@link #mRequest}, and launch another activity if redirect
    488      * is needed.
    489      *
    490      * @param forNewIntent set true if it's called from {@link #onNewIntent(Intent)}.
    491      * @return {@code true} if {@link PeopleActivity} should continue running.  {@code false}
    492      *         if it shouldn't, in which case the caller should finish() itself and shouldn't do
    493      *         farther initialization.
    494      */
    495     private boolean processIntent(boolean forNewIntent) {
    496         // Extract relevant information from the intent
    497         mRequest = mIntentResolver.resolveIntent(getIntent());
    498         if (Log.isLoggable(TAG, Log.DEBUG)) {
    499             Log.d(TAG, this + " processIntent: forNewIntent=" + forNewIntent
    500                     + " intent=" + getIntent() + " request=" + mRequest);
    501         }
    502         if (!mRequest.isValid()) {
    503             setResult(RESULT_CANCELED);
    504             return false;
    505         }
    506 
    507         switch (mRequest.getActionCode()) {
    508             case ContactsRequest.ACTION_VIEW_CONTACT: {
    509                 ImplicitIntentsUtil.startQuickContact(
    510                         this, mRequest.getContactUri(), ScreenType.UNKNOWN);
    511                 return false;
    512             }
    513             case ContactsRequest.ACTION_INSERT_GROUP: {
    514                 onCreateGroupMenuItemClicked();
    515                 return true;
    516             }
    517             case ContactsRequest.ACTION_VIEW_GROUP:
    518             case ContactsRequest.ACTION_EDIT_GROUP: {
    519                 mShouldSwitchToGroupView = true;
    520                 return true;
    521             }
    522         }
    523         return true;
    524     }
    525 
    526     private void createViewsAndFragments() {
    527         setContentView(R.layout.people_activity);
    528 
    529         final FragmentManager fragmentManager = getFragmentManager();
    530 
    531         setUpListFragment(fragmentManager);
    532 
    533         mMembersFragment = (GroupMembersFragment) fragmentManager.findFragmentByTag(TAG_GROUP_VIEW);
    534 
    535         // Configure floating action button
    536         mFloatingActionButtonContainer = findViewById(R.id.floating_action_button_container);
    537         final ImageButton floatingActionButton
    538                 = (ImageButton) findViewById(R.id.floating_action_button);
    539         floatingActionButton.setOnClickListener(new View.OnClickListener() {
    540             @Override
    541             public void onClick(View v) {
    542                 AccountFilterUtil.startEditorIntent(PeopleActivity.this, getIntent(),
    543                         mContactListFilterController.getFilter());
    544             }
    545         });
    546         mFloatingActionButtonController = new FloatingActionButtonController(this,
    547                 mFloatingActionButtonContainer, floatingActionButton);
    548 
    549         invalidateOptionsMenuIfNeeded();
    550 
    551         mLayoutRoot = (CoordinatorLayout) findViewById(R.id.root);
    552 
    553         if (mShouldSwitchToGroupView && !mIsRecreatedInstance) {
    554             mGroupUri = mRequest.getContactUri();
    555             switchToOrUpdateGroupView(GroupUtil.ACTION_SWITCH_GROUP);
    556             mShouldSwitchToGroupView = false;
    557         }
    558     }
    559 
    560     @Override
    561     public void setContentView(@LayoutRes int layoutResID) {
    562         final ViewGroup parent = (ViewGroup) findViewById(R.id.content_frame);
    563         if (parent != null) {
    564             parent.removeAllViews();
    565         }
    566         LayoutInflater.from(this).inflate(layoutResID, parent);
    567     }
    568 
    569     private void setUpListFragment(FragmentManager fragmentManager) {
    570         mContactsListFragment = (DefaultContactBrowseListFragment)
    571                 fragmentManager.findFragmentByTag(TAG_ALL);
    572 
    573         if (mContactsListFragment == null) {
    574             mContactsListFragment = new DefaultContactBrowseListFragment();
    575             mContactsListFragment.setAnimateOnLoad(true);
    576             fragmentManager.beginTransaction()
    577                     .add(R.id.contacts_list_container, mContactsListFragment, TAG_ALL)
    578                     .commit();
    579             fragmentManager.executePendingTransactions();
    580         }
    581 
    582         mContactsListFragment.setContactsAvailable(areContactsAvailable());
    583         mContactsListFragment.setListType(mContactListFilterController.getFilterListType());
    584         mContactsListFragment.setParameters(/* ContactsRequest */ mRequest,
    585                 /* fromOnNewIntent */ false);
    586     }
    587 
    588     @Override
    589     protected void onPause() {
    590         mProviderStatusWatcher.stop();
    591 
    592         LocalBroadcastManager.getInstance(this).unregisterReceiver(mSaveServiceListener);
    593 
    594         super.onPause();
    595 
    596         ContentResolver.removeStatusChangeListener(mStatusChangeListenerHandle);
    597         onSyncStateUpdated();
    598     }
    599 
    600     @Override
    601     public void onMultiWindowModeChanged(boolean entering) {
    602         initializeHomeVisibility();
    603     }
    604 
    605     @Override
    606     protected void onResume() {
    607         super.onResume();
    608 
    609         if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
    610             updateStatusBarBackground();
    611         }
    612 
    613         if (mShouldSwitchToAllContacts) {
    614             switchToAllContacts();
    615         }
    616 
    617         mProviderStatusWatcher.start();
    618         updateViewConfiguration(true);
    619 
    620         mStatusChangeListenerHandle = ContentResolver.addStatusChangeListener(
    621                 ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE
    622                         | ContentResolver.SYNC_OBSERVER_TYPE_PENDING
    623                         | ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS,
    624                 mSyncStatusObserver);
    625         onSyncStateUpdated();
    626 
    627         initializeFabVisibility();
    628         initializeHomeVisibility();
    629 
    630         mSaveServiceListener = new SaveServiceListener();
    631         LocalBroadcastManager.getInstance(this).registerReceiver(mSaveServiceListener,
    632                 new IntentFilter(ContactSaveService.BROADCAST_GROUP_DELETED));
    633     }
    634 
    635     public void updateStatusBarBackground() {
    636         updateStatusBarBackground(/* color */ -1);
    637     }
    638 
    639     public void updateStatusBarBackground(int color) {
    640         if (!CompatUtils.isLollipopCompatible()) return;
    641         if (color == -1) {
    642             mDrawerLayout.setStatusBarBackgroundColor(
    643                     MaterialColorMapUtils.getStatusBarColor(this));
    644         } else {
    645             mDrawerLayout.setStatusBarBackgroundColor(color);
    646         }
    647         mDrawerLayout.invalidate();
    648         getWindow().setStatusBarColor(Color.TRANSPARENT);
    649     }
    650 
    651     @Override
    652     protected void onDestroy() {
    653         mProviderStatusWatcher.removeListener(mProviderStatusListener);
    654         mContactListFilterController.removeListener(mFilterListener);
    655         super.onDestroy();
    656     }
    657 
    658     private void initializeFabVisibility() {
    659         mFloatingActionButtonContainer.setVisibility(shouldHideFab() ? View.GONE : View.VISIBLE);
    660         mFloatingActionButtonController.resetIn();
    661         wasLastFabAnimationScaleIn = !shouldHideFab();
    662     }
    663 
    664     private void initializeHomeVisibility() {
    665         // Remove the navigation icon if we return to the fragment in a search or select state
    666         if (getToolbar() != null && (isListFragmentInSelectionMode()
    667                 || isListFragmentInSearchMode() || isGroupsFragmentInSelectionMode()
    668                 || isGroupsFragmentInSearchMode())) {
    669             getToolbar().setNavigationIcon(null);
    670         }
    671     }
    672 
    673     private boolean shouldHideFab() {
    674         if (mContactsListFragment != null && mContactsListFragment.getActionBarAdapter() == null
    675                 || isInSecondLevel()) {
    676             return true;
    677         }
    678         return isListFragmentInSearchMode()
    679                 || isListFragmentInSelectionMode();
    680     }
    681 
    682     public void showFabWithAnimation(boolean showFab) {
    683         if (mFloatingActionButtonContainer == null) {
    684             return;
    685         }
    686         if (showFab) {
    687             if (!wasLastFabAnimationScaleIn) {
    688                 mFloatingActionButtonContainer.setVisibility(View.VISIBLE);
    689                 mFloatingActionButtonController.scaleIn(0);
    690             }
    691             wasLastFabAnimationScaleIn = true;
    692 
    693         } else {
    694             if (wasLastFabAnimationScaleIn) {
    695                 mFloatingActionButtonContainer.setVisibility(View.VISIBLE);
    696                 mFloatingActionButtonController.scaleOut();
    697             }
    698             wasLastFabAnimationScaleIn = false;
    699         }
    700     }
    701 
    702     private void updateViewConfiguration(boolean forceUpdate) {
    703         int providerStatus = mProviderStatusWatcher.getProviderStatus();
    704         if (!forceUpdate && (mProviderStatus != null)
    705                 && (mProviderStatus.equals(providerStatus))) return;
    706         mProviderStatus = providerStatus;
    707 
    708         final FragmentManager fragmentManager= getFragmentManager();
    709         final FragmentTransaction transaction = fragmentManager.beginTransaction();
    710 
    711         // Change in CP2's provider status may not take effect immediately, see b/30566908.
    712         // So we need to handle the case where provider status is STATUS_EMPTY and there is
    713         // actually at least one real account (not "local" account) on device.
    714         if (shouldShowList()) {
    715             if (mContactsListFragment != null) {
    716                 final Fragment unavailableFragment = fragmentManager
    717                         .findFragmentByTag(TAG_UNAVAILABLE);
    718                 if (unavailableFragment != null) {
    719                     transaction.remove(unavailableFragment);
    720                 }
    721                 if (mContactsListFragment.isHidden()) {
    722                     transaction.show(mContactsListFragment);
    723                 }
    724                 mContactsListFragment.setContactsAvailable(areContactsAvailable());
    725                 mContactsListFragment.setEnabled(true);
    726             }
    727         } else {
    728             // Setting up the page so that the user can still use the app
    729             // even without an account.
    730             if (mContactsListFragment != null) {
    731                 mContactsListFragment.setEnabled(false);
    732             }
    733             final ContactsUnavailableFragment fragment = new ContactsUnavailableFragment();
    734             transaction.hide(mContactsListFragment);
    735             transaction.replace(R.id.contacts_unavailable_container, fragment, TAG_UNAVAILABLE);
    736             fragment.updateStatus(mProviderStatus);
    737         }
    738         if (!transaction.isEmpty()) {
    739             transaction.commit();
    740             fragmentManager.executePendingTransactions();
    741         }
    742 
    743         invalidateOptionsMenuIfNeeded();
    744     }
    745 
    746     private boolean shouldShowList() {
    747         return mProviderStatus != null
    748                 && ((mProviderStatus.equals(ProviderStatus.STATUS_EMPTY)
    749                 && mAccountTypeManager.hasNonLocalAccount())
    750                 || mProviderStatus.equals(ProviderStatus.STATUS_NORMAL));
    751     }
    752 
    753     private void invalidateOptionsMenuIfNeeded() {
    754         if (mContactsListFragment != null
    755                 && mContactsListFragment.getOptionsMenuContactsAvailable()
    756                 != areContactsAvailable()) {
    757             invalidateOptionsMenu();
    758         }
    759     }
    760 
    761     @Override
    762     public boolean onKeyDown(int keyCode, KeyEvent event) {
    763         // If the drawer is open, consume KEYCODE_BACK event only.
    764         if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
    765             if (keyCode == KeyEvent.KEYCODE_BACK) {
    766                 // Should eventually go to onBackPressed().
    767                 return super.onKeyDown(keyCode, event);
    768             }
    769             return false;
    770         }
    771         // Bring up the search UI if the user starts typing
    772         final int unicodeChar = event.getUnicodeChar();
    773         if ((unicodeChar != 0)
    774                 // If COMBINING_ACCENT is set, it's not a unicode character.
    775                 && ((unicodeChar & KeyCharacterMap.COMBINING_ACCENT) == 0)
    776                 && !Character.isWhitespace(unicodeChar)) {
    777             if (mContactsListFragment.onKeyDown(unicodeChar)) {
    778                 return true;
    779             }
    780         }
    781 
    782         return super.onKeyDown(keyCode, event);
    783     }
    784 
    785     @Override
    786     public void onBackPressed() {
    787         if (!isSafeToCommitTransactions()) {
    788             return;
    789         }
    790 
    791         // Handle the back event in drawer first.
    792         if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
    793             closeDrawer();
    794             return;
    795         }
    796 
    797         // Handle the back event in "second level".
    798         if (isGroupView()) {
    799             onBackPressedGroupView();
    800             return;
    801         }
    802 
    803         if (isAssistantView()) {
    804             onBackPressedAssistantView();
    805             return;
    806         }
    807 
    808         // If feature highlight is present, let it handle the back event before
    809         // mContactsListFragment.
    810         if (FeatureHighlightHelper.tryRemoveHighlight(this)) {
    811             return;
    812         }
    813 
    814         // Handle the back event in "first level" - mContactsListFragment.
    815         if (maybeHandleInListFragment()) {
    816             return;
    817         }
    818 
    819         super.onBackPressed();
    820     }
    821 
    822     private void onBackPressedGroupView() {
    823         if (mMembersFragment.isEditMode()) {
    824             mMembersFragment.exitEditMode();
    825         } else if (mMembersFragment.getActionBarAdapter().isSelectionMode()) {
    826             mMembersFragment.getActionBarAdapter().setSelectionMode(false);
    827             mMembersFragment.displayCheckBoxes(false);
    828         } else if (mMembersFragment.getActionBarAdapter().isSearchMode()) {
    829             mMembersFragment.getActionBarAdapter().setSearchMode(false);
    830         } else {
    831             switchToAllContacts();
    832         }
    833     }
    834 
    835     private void onBackPressedAssistantView() {
    836         if (!isInThirdLevel()) {
    837             switchToAllContacts();
    838         } else {
    839             setDrawerLockMode(/* enabled */ true);
    840             super.onBackPressed();
    841         }
    842     }
    843 
    844     // Returns true if back event is handled in this method.
    845     private boolean maybeHandleInListFragment() {
    846         if (isListFragmentInSelectionMode()) {
    847             mContactsListFragment.getActionBarAdapter().setSelectionMode(false);
    848             return true;
    849         }
    850 
    851         if (isListFragmentInSearchMode()) {
    852             mContactsListFragment.getActionBarAdapter().setSearchMode(false);
    853             if (mContactsListFragment.wasSearchResultClicked()) {
    854                 mContactsListFragment.resetSearchResultClicked();
    855             } else {
    856                 Logger.logScreenView(this, ScreenType.SEARCH_EXIT);
    857                 Logger.logSearchEvent(mContactsListFragment.createSearchState());
    858             }
    859             return true;
    860         }
    861 
    862         if (!AccountFilterUtil.isAllContactsFilter(mContactListFilterController.getFilter())
    863                 && !mContactsListFragment.isHidden()) {
    864             // If mContactsListFragment is hidden, then mContactsUnavailableFragment is visible so we
    865             // don't need to switch to all contacts.
    866             switchToAllContacts();
    867             return true;
    868         }
    869 
    870         return false;
    871     }
    872 
    873     private boolean isListFragmentInSelectionMode() {
    874         return mContactsListFragment != null && mContactsListFragment.getActionBarAdapter() != null
    875                 && mContactsListFragment.getActionBarAdapter().isSelectionMode();
    876     }
    877 
    878     private boolean isListFragmentInSearchMode() {
    879         return mContactsListFragment != null && mContactsListFragment.getActionBarAdapter() != null
    880                 && mContactsListFragment.getActionBarAdapter().isSearchMode();
    881     }
    882 
    883     private boolean isGroupsFragmentInSelectionMode() {
    884         return mMembersFragment != null && mMembersFragment.getActionBarAdapter() != null
    885                 && mMembersFragment.getActionBarAdapter().isSelectionMode();
    886     }
    887 
    888     private boolean isGroupsFragmentInSearchMode() {
    889         return mMembersFragment != null && mMembersFragment.getActionBarAdapter() != null
    890                 && mMembersFragment.getActionBarAdapter().isSearchMode();
    891     }
    892 
    893     @Override
    894     protected void onSaveInstanceState(Bundle outState) {
    895         super.onSaveInstanceState(outState);
    896         if (mNewGroupAccount != null) {
    897             outState.putString(KEY_NEW_GROUP_ACCOUNT, mNewGroupAccount.stringify());
    898         }
    899         outState.putInt(KEY_CONTACTS_VIEW, mCurrentView.ordinal());
    900         outState.putParcelable(KEY_GROUP_URI, mGroupUri);
    901     }
    902 
    903     @Override
    904     protected void onRestoreInstanceState(Bundle savedInstanceState) {
    905         super.onRestoreInstanceState(savedInstanceState);
    906         mGroupUri = savedInstanceState.getParcelable(KEY_GROUP_URI);
    907     }
    908 
    909     private void onGroupDeleted(final Intent intent) {
    910         if (!ContactSaveService.canUndo(intent)) return;
    911 
    912         final AccessibilityManager am =
    913                 (AccessibilityManager) getSystemService(Context.ACCESSIBILITY_SERVICE);
    914         //TODO set to INDEFINITE and track user interaction to dismiss b/33208886
    915         final int accessibilityLength = 15000;
    916         final int length = am.isEnabled() ? accessibilityLength : Snackbar.LENGTH_LONG;
    917         final String message = getString(R.string.groupDeletedToast);
    918 
    919         final Snackbar snackbar = Snackbar.make(mLayoutRoot, message, length)
    920                 .setAction(R.string.undo, new View.OnClickListener() {
    921                     @Override
    922                     public void onClick(View v) {
    923                         ContactSaveService.startService(PeopleActivity.this,
    924                                 ContactSaveService.createUndoIntent(PeopleActivity.this, intent));
    925                     }
    926                 }).setActionTextColor(ContextCompat.getColor(this, R.color.snackbar_action_text));
    927 
    928         // Announce for a11y talkback
    929         mLayoutRoot.announceForAccessibility(message);
    930         mLayoutRoot.announceForAccessibility(getString(R.string.undo));
    931 
    932         snackbar.show();
    933     }
    934 
    935     private class SaveServiceListener extends BroadcastReceiver {
    936         @Override
    937         public void onReceive(Context context, Intent intent) {
    938             switch (intent.getAction()) {
    939                 case ContactSaveService.BROADCAST_GROUP_DELETED:
    940                     onGroupDeleted(intent);
    941                     break;
    942             }
    943         }
    944     }
    945 
    946     private void onGroupMenuItemClicked(long groupId) {
    947         if (isGroupView() && mMembersFragment != null
    948                 && mMembersFragment.isCurrentGroup(groupId)) {
    949             return;
    950         }
    951         mGroupUri = ContentUris.withAppendedId(ContactsContract.Groups.CONTENT_URI, groupId);
    952         switchToOrUpdateGroupView(GroupUtil.ACTION_SWITCH_GROUP);
    953     }
    954 
    955     private void onFilterMenuItemClicked(Intent intent) {
    956         // We must pop second level first to "restart" mContactsListFragment before changing filter.
    957         if (isInSecondLevel()) {
    958             popSecondLevel();
    959             showFabWithAnimation(/* showFab */ true);
    960             // HACK: swap the current filter to force listeners to update because the group
    961             // member view no longer changes the filter. Fix for b/32223767
    962             final ContactListFilter current = mContactListFilterController.getFilter();
    963             mContactListFilterController.setContactListFilter(
    964                     AccountFilterUtil.createContactsFilter(this), false);
    965             mContactListFilterController.setContactListFilter(current, false);
    966         }
    967         mCurrentView = ContactsView.ACCOUNT_VIEW;
    968         AccountFilterUtil.handleAccountFilterResult(mContactListFilterController,
    969                 AppCompatActivity.RESULT_OK, intent);
    970     }
    971 
    972     private void switchToOrUpdateGroupView(String action) {
    973         // If group fragment is active and visible, we simply update it.
    974         if (mMembersFragment != null && !mMembersFragment.isInactive()) {
    975             mMembersFragment.updateExistingGroupFragment(mGroupUri, action);
    976         } else {
    977             switchView(ContactsView.GROUP_VIEW);
    978         }
    979     }
    980 
    981     protected void launchAssistant() {
    982         switchView(ContactsView.ASSISTANT);
    983     }
    984 
    985     private void switchView(ContactsView contactsView) {
    986         mCurrentView = contactsView;
    987 
    988         final FragmentManager fragmentManager =  getFragmentManager();
    989         final FragmentTransaction transaction = fragmentManager.beginTransaction();
    990         popSecondLevel();
    991         if (isGroupView()) {
    992             mMembersFragment = GroupMembersFragment.newInstance(mGroupUri);
    993             transaction.replace(
    994                     R.id.contacts_list_container, mMembersFragment, TAG_GROUP_VIEW);
    995         } else if (isAssistantView()) {
    996             Fragment uiFragment = fragmentManager.findFragmentByTag(TAG_ASSISTANT);
    997             Fragment unavailableFragment = fragmentManager.findFragmentByTag(TAG_UNAVAILABLE);
    998             if (uiFragment == null) {
    999                 uiFragment = ObjectFactory.getAssistantFragment();
   1000             }
   1001             if (unavailableFragment != null) {
   1002                 transaction.remove(unavailableFragment);
   1003             }
   1004             transaction.replace(R.id.contacts_list_container, uiFragment, TAG_ASSISTANT);
   1005             resetToolBarStatusBarColor();
   1006         }
   1007         transaction.addToBackStack(TAG_SECOND_LEVEL);
   1008         transaction.commit();
   1009         fragmentManager.executePendingTransactions();
   1010 
   1011         showFabWithAnimation(/* showFab */ false);
   1012     }
   1013 
   1014     public void switchToAllContacts() {
   1015         popSecondLevel();
   1016         mShouldSwitchToAllContacts = false;
   1017         mCurrentView = ContactsView.ALL_CONTACTS;
   1018         mDrawerFragment.setNavigationItemChecked(ContactsView.ALL_CONTACTS);
   1019         showFabWithAnimation(/* showFab */ true);
   1020         mContactsListFragment.scrollToTop();
   1021         resetFilter();
   1022         setTitle(getString(R.string.contactsList));
   1023     }
   1024 
   1025     private void resetFilter() {
   1026         final Intent intent = new Intent();
   1027         final ContactListFilter filter = AccountFilterUtil.createContactsFilter(this);
   1028         intent.putExtra(AccountFilterActivity.EXTRA_CONTACT_LIST_FILTER, filter);
   1029         AccountFilterUtil.handleAccountFilterResult(
   1030                 mContactListFilterController, AppCompatActivity.RESULT_OK, intent);
   1031     }
   1032 
   1033     // Reset toolbar and status bar color to Contacts theme color.
   1034     private void resetToolBarStatusBarColor() {
   1035         findViewById(R.id.toolbar_frame).setBackgroundColor(
   1036                 ContextCompat.getColor(this, R.color.primary_color));
   1037         updateStatusBarBackground(ContextCompat.getColor(this, R.color.primary_color_dark));
   1038     }
   1039 
   1040     protected DefaultContactBrowseListFragment getListFragment() {
   1041         return mContactsListFragment;
   1042     }
   1043 
   1044     protected GroupMembersFragment getGroupFragment() {
   1045         return mMembersFragment;
   1046     }
   1047 
   1048     private void handleFilterChangeForFragment(ContactListFilter filter) {
   1049         if (mContactsListFragment.canSetActionBar()) {
   1050             mContactsListFragment.setFilterAndUpdateTitle(filter);
   1051             // Scroll to top after filter is changed.
   1052             mContactsListFragment.scrollToTop();
   1053         }
   1054     }
   1055 
   1056     private void handleFilterChangeForActivity(ContactListFilter filter) {
   1057         // The filter was changed while this activity was in the background. If we're in the
   1058         // assistant view Switch to the main contacts list when we resume to prevent
   1059         // b/31838582 and b/31829161
   1060         // TODO: this is a hack; we need to do some cleanup of the contact list filter stuff
   1061         if (isAssistantView() && filter.isContactsFilterType()) {
   1062             mShouldSwitchToAllContacts = true;
   1063         }
   1064 
   1065         if (CompatUtils.isNCompatible()) {
   1066             getWindow().getDecorView()
   1067                     .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
   1068         }
   1069         invalidateOptionsMenu();
   1070     }
   1071 
   1072     public void updateDrawerGroupMenu(long groupId) {
   1073         if (mDrawerFragment != null) {
   1074             mDrawerFragment.updateGroupMenu(groupId);
   1075         }
   1076     }
   1077 
   1078     public void setDrawerLockMode(boolean enabled) {
   1079         // Prevent drawer from being opened by sliding from the start of screen.
   1080         mDrawerLayout.setDrawerLockMode(enabled ? DrawerLayout.LOCK_MODE_UNLOCKED
   1081                 : DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
   1082 
   1083         // Order of these statements matter.
   1084         // Display back button and disable drawer indicator.
   1085         if (enabled) {
   1086             getSupportActionBar().setDisplayHomeAsUpEnabled(false);
   1087             mToggle.setDrawerIndicatorEnabled(true);
   1088         } else {
   1089             mToggle.setDrawerIndicatorEnabled(false);
   1090             getSupportActionBar().setDisplayHomeAsUpEnabled(true);
   1091         }
   1092     }
   1093 
   1094     public Toolbar getToolbar() {
   1095         return mToolbar;
   1096     }
   1097 
   1098     @Override
   1099     protected void onPostCreate(Bundle savedInstanceState) {
   1100         super.onPostCreate(savedInstanceState);
   1101         mToggle.syncState();
   1102     }
   1103 
   1104     @Override
   1105     public void onConfigurationChanged(Configuration newConfig) {
   1106         super.onConfigurationChanged(newConfig);
   1107         mToggle.onConfigurationChanged(newConfig);
   1108     }
   1109 
   1110     protected void onCreateGroupMenuItemClicked() {
   1111         // Select the account to create the group
   1112         final Bundle extras = getIntent().getExtras();
   1113         final Account account = extras == null ? null :
   1114                 (Account) extras.getParcelable(Intents.Insert.EXTRA_ACCOUNT);
   1115         if (account == null) {
   1116             selectAccountForNewGroup();
   1117         } else {
   1118             final String dataSet = extras == null
   1119                     ? null : extras.getString(Intents.Insert.EXTRA_DATA_SET);
   1120             final AccountWithDataSet accountWithDataSet = new AccountWithDataSet(
   1121                     account.name, account.type, dataSet);
   1122             onAccountChosen(accountWithDataSet, /* extraArgs */ null);
   1123         }
   1124     }
   1125 
   1126     private void selectAccountForNewGroup() {
   1127         // This should never block because the DrawerFragment loads the accounts and the
   1128         // "Create Label" item only exists when that loading finishes
   1129         final List<AccountInfo> accounts = Futures.getUnchecked(AccountTypeManager.getInstance(this)
   1130                 .filterAccountsAsync(AccountTypeManager.AccountFilter.GROUPS_WRITABLE));
   1131         if (accounts.isEmpty()) {
   1132             // We shouldn't present the add group button if there are no writable accounts
   1133             // but check it since it's possible we are started with an Intent.
   1134             Toast.makeText(this, R.string.groupCreateFailedToast, Toast.LENGTH_SHORT).show();
   1135             return;
   1136         }
   1137         // If there is a single writable account, use it w/o showing a dialog.
   1138         if (accounts.size() == 1) {
   1139             onAccountChosen(accounts.get(0).getAccount(), /* extraArgs */ null);
   1140             return;
   1141         }
   1142         SelectAccountDialogFragment.show(getFragmentManager(), R.string.dialog_new_group_account,
   1143                 AccountTypeManager.AccountFilter.GROUPS_WRITABLE, /* extraArgs */ null,
   1144                 TAG_SELECT_ACCOUNT_DIALOG);
   1145     }
   1146 
   1147     // Implementation of SelectAccountDialogFragment.Listener
   1148     @Override
   1149     public void onAccountChosen(AccountWithDataSet account, Bundle extraArgs) {
   1150         mNewGroupAccount = account;
   1151         GroupNameEditDialogFragment.newInstanceForCreation(
   1152                 mNewGroupAccount, GroupUtil.ACTION_CREATE_GROUP)
   1153                 .show(getFragmentManager(), TAG_GROUP_NAME_EDIT_DIALOG);
   1154     }
   1155 
   1156     @Override
   1157     public void onAccountSelectorCancelled() {
   1158     }
   1159 
   1160     // Implementation of DrawerFragmentListener
   1161     @Override
   1162     public void onDrawerItemClicked(){
   1163         closeDrawer();
   1164     }
   1165 
   1166     @Override
   1167     public void onContactsViewSelected(ContactsView mode) {
   1168         if (mode == ContactsView.ALL_CONTACTS) {
   1169             switchToAllContacts();
   1170         } else if (mode == ContactsView.ASSISTANT) {
   1171             launchAssistant();
   1172         } else {
   1173             throw new IllegalStateException("Unknown view " + mode);
   1174         }
   1175     }
   1176 
   1177     @Override
   1178     public void onCreateLabelButtonClicked() {
   1179         onCreateGroupMenuItemClicked();
   1180     }
   1181 
   1182     @Override
   1183     public void onOpenSettings() {
   1184         new Handler().postDelayed(new Runnable() {
   1185             @Override
   1186             public void run() {
   1187                 startActivity(createPreferenceIntent());
   1188             }
   1189         }, DRAWER_CLOSE_DELAY);
   1190     }
   1191 
   1192     @Override
   1193     public void onLaunchHelpFeedback() {
   1194         HelpUtils.launchHelpAndFeedbackForMainScreen(this);
   1195     }
   1196 
   1197     @Override
   1198     public void onGroupViewSelected(GroupListItem groupListItem) {
   1199         onGroupMenuItemClicked(groupListItem.getGroupId());
   1200     }
   1201 
   1202     @Override
   1203     public void onAccountViewSelected(ContactListFilter filter) {
   1204         final Intent intent = new Intent();
   1205         intent.putExtra(AccountFilterActivity.EXTRA_CONTACT_LIST_FILTER, filter);
   1206         onFilterMenuItemClicked(intent);
   1207     }
   1208 
   1209     public boolean isGroupView() {
   1210         return mCurrentView == ContactsView.GROUP_VIEW;
   1211     }
   1212 
   1213     protected boolean isAssistantView() {
   1214         return mCurrentView == ContactsView.ASSISTANT;
   1215     }
   1216 
   1217     protected boolean isAllContactsView() {
   1218         return mCurrentView == ContactsView.ALL_CONTACTS;
   1219     }
   1220 
   1221     protected boolean isAccountView() {
   1222         return mCurrentView == ContactsView.ACCOUNT_VIEW;
   1223     }
   1224 
   1225     public boolean isInSecondLevel() {
   1226         return isGroupView() || isAssistantView();
   1227     }
   1228 
   1229     private boolean isInThirdLevel() {
   1230         return isLastBackStackTag(TAG_THIRD_LEVEL);
   1231     }
   1232 
   1233     private boolean isLastBackStackTag(String tag) {
   1234         final int count = getFragmentManager().getBackStackEntryCount();
   1235         if (count > 0) {
   1236             final FragmentManager.BackStackEntry last =
   1237                     getFragmentManager().getBackStackEntryAt(count - 1);
   1238             if (tag == null) {
   1239                 return last.getName() == null;
   1240             }
   1241             return tag.equals(last.getName());
   1242         }
   1243         return false;
   1244     }
   1245 
   1246     private void popSecondLevel() {
   1247         getFragmentManager().popBackStackImmediate(
   1248                 TAG_ASSISTANT_HELPER, FragmentManager.POP_BACK_STACK_INCLUSIVE);
   1249         getFragmentManager().popBackStackImmediate(
   1250                 TAG_SECOND_LEVEL, FragmentManager.POP_BACK_STACK_INCLUSIVE);
   1251         mMembersFragment = null;
   1252         resetToolBarStatusBarColor();
   1253     }
   1254 
   1255     public void closeDrawer() {
   1256         mDrawerLayout.closeDrawer(GravityCompat.START);
   1257     }
   1258 
   1259     private Intent createPreferenceIntent() {
   1260         final Intent intent = new Intent(this, ContactsPreferenceActivity.class);
   1261         intent.putExtra(ContactsPreferenceActivity.EXTRA_NEW_LOCAL_PROFILE,
   1262                 ContactEditorFragment.INTENT_EXTRA_NEW_LOCAL_PROFILE);
   1263         return intent;
   1264     }
   1265 
   1266 
   1267 }
   1268