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