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 androidx.annotation.LayoutRes; 39 import androidx.coordinatorlayout.widget.CoordinatorLayout; 40 import com.google.android.material.snackbar.Snackbar; 41 import androidx.core.content.ContextCompat; 42 import androidx.localbroadcastmanager.content.LocalBroadcastManager; 43 import androidx.core.view.GravityCompat; 44 import androidx.drawerlayout.widget.DrawerLayout; 45 import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; 46 import androidx.appcompat.app.ActionBarDrawerToggle; 47 import androidx.appcompat.app.AppCompatActivity; 48 import androidx.appcompat.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 onSyncStateUpdated()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 showConnectionErrorMsg()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 ContactsActionBarDrawerToggle(AppCompatActivity activity, DrawerLayout drawerLayout, Toolbar toolbar, int openDrawerContentDescRes, int closeDrawerContentDescRes)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 onDrawerOpened(View drawerView)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 stopSearchAndSelection()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 onDrawerClosed(View view)312 public void onDrawerClosed(View view) { 313 super.onDrawerClosed(view); 314 invalidateOptionsMenu(); 315 } 316 317 @Override onDrawerStateChanged(int newState)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 PeopleActivity()328 public PeopleActivity() { 329 mInstanceId = sNextInstanceId.getAndIncrement(); 330 mIntentResolver = new ContactsIntentResolver(this); 331 mProviderStatusWatcher = ProviderStatusWatcher.getInstance(this); 332 } 333 334 @Override toString()335 public String toString() { 336 // Shown on logcat 337 return String.format("%s@%d", getClass().getSimpleName(), mInstanceId); 338 } 339 areContactsAvailable()340 private boolean areContactsAvailable() { 341 return (mProviderStatus != null) && mProviderStatus.equals(ProviderStatus.STATUS_NORMAL); 342 } 343 344 @Override onCreate(Bundle savedState)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 onNewIntent(Intent intent)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 isGroupSaveAction(String action)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 toast(int resId)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 */ processIntent(boolean forNewIntent)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 createViewsAndFragments()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 setContentView(@ayoutRes int layoutResID)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 setUpListFragment(FragmentManager fragmentManager)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 onPause()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 onMultiWindowModeChanged(boolean entering)601 public void onMultiWindowModeChanged(boolean entering) { 602 initializeHomeVisibility(); 603 } 604 605 @Override onResume()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 updateStatusBarBackground()635 public void updateStatusBarBackground() { 636 updateStatusBarBackground(/* color */ -1); 637 } 638 updateStatusBarBackground(int color)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 onDestroy()652 protected void onDestroy() { 653 mProviderStatusWatcher.removeListener(mProviderStatusListener); 654 mContactListFilterController.removeListener(mFilterListener); 655 super.onDestroy(); 656 } 657 initializeFabVisibility()658 private void initializeFabVisibility() { 659 mFloatingActionButtonContainer.setVisibility(shouldHideFab() ? View.GONE : View.VISIBLE); 660 mFloatingActionButtonController.resetIn(); 661 wasLastFabAnimationScaleIn = !shouldHideFab(); 662 } 663 initializeHomeVisibility()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 shouldHideFab()673 private boolean shouldHideFab() { 674 if (mContactsListFragment != null && mContactsListFragment.getActionBarAdapter() == null 675 || isInSecondLevel()) { 676 return true; 677 } 678 return isListFragmentInSearchMode() 679 || isListFragmentInSelectionMode(); 680 } 681 showFabWithAnimation(boolean showFab)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 updateViewConfiguration(boolean forceUpdate)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 shouldShowList()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 invalidateOptionsMenuIfNeeded()753 private void invalidateOptionsMenuIfNeeded() { 754 if (mContactsListFragment != null 755 && mContactsListFragment.getOptionsMenuContactsAvailable() 756 != areContactsAvailable()) { 757 invalidateOptionsMenu(); 758 } 759 } 760 761 @Override onKeyDown(int keyCode, KeyEvent event)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 onBackPressed()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 onBackPressedGroupView()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 onBackPressedAssistantView()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. maybeHandleInListFragment()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 isListFragmentInSelectionMode()873 private boolean isListFragmentInSelectionMode() { 874 return mContactsListFragment != null && mContactsListFragment.getActionBarAdapter() != null 875 && mContactsListFragment.getActionBarAdapter().isSelectionMode(); 876 } 877 isListFragmentInSearchMode()878 private boolean isListFragmentInSearchMode() { 879 return mContactsListFragment != null && mContactsListFragment.getActionBarAdapter() != null 880 && mContactsListFragment.getActionBarAdapter().isSearchMode(); 881 } 882 isGroupsFragmentInSelectionMode()883 private boolean isGroupsFragmentInSelectionMode() { 884 return mMembersFragment != null && mMembersFragment.getActionBarAdapter() != null 885 && mMembersFragment.getActionBarAdapter().isSelectionMode(); 886 } 887 isGroupsFragmentInSearchMode()888 private boolean isGroupsFragmentInSearchMode() { 889 return mMembersFragment != null && mMembersFragment.getActionBarAdapter() != null 890 && mMembersFragment.getActionBarAdapter().isSearchMode(); 891 } 892 893 @Override onSaveInstanceState(Bundle outState)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 onRestoreInstanceState(Bundle savedInstanceState)904 protected void onRestoreInstanceState(Bundle savedInstanceState) { 905 super.onRestoreInstanceState(savedInstanceState); 906 mGroupUri = savedInstanceState.getParcelable(KEY_GROUP_URI); 907 } 908 onGroupDeleted(final Intent intent)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 onReceive(Context context, Intent intent)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 onGroupMenuItemClicked(long groupId)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 onFilterMenuItemClicked(Intent intent)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 switchToOrUpdateGroupView(String action)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 launchAssistant()981 protected void launchAssistant() { 982 switchView(ContactsView.ASSISTANT); 983 } 984 switchView(ContactsView contactsView)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 switchToAllContacts()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 resetFilter()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. resetToolBarStatusBarColor()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 getListFragment()1040 protected DefaultContactBrowseListFragment getListFragment() { 1041 return mContactsListFragment; 1042 } 1043 getGroupFragment()1044 protected GroupMembersFragment getGroupFragment() { 1045 return mMembersFragment; 1046 } 1047 handleFilterChangeForFragment(ContactListFilter filter)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 handleFilterChangeForActivity(ContactListFilter filter)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 updateDrawerGroupMenu(long groupId)1072 public void updateDrawerGroupMenu(long groupId) { 1073 if (mDrawerFragment != null) { 1074 mDrawerFragment.updateGroupMenu(groupId); 1075 } 1076 } 1077 setDrawerLockMode(boolean enabled)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 getToolbar()1094 public Toolbar getToolbar() { 1095 return mToolbar; 1096 } 1097 1098 @Override onPostCreate(Bundle savedInstanceState)1099 protected void onPostCreate(Bundle savedInstanceState) { 1100 super.onPostCreate(savedInstanceState); 1101 mToggle.syncState(); 1102 } 1103 1104 @Override onConfigurationChanged(Configuration newConfig)1105 public void onConfigurationChanged(Configuration newConfig) { 1106 super.onConfigurationChanged(newConfig); 1107 mToggle.onConfigurationChanged(newConfig); 1108 } 1109 onCreateGroupMenuItemClicked()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 selectAccountForNewGroup()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 onAccountChosen(AccountWithDataSet account, Bundle extraArgs)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 onAccountSelectorCancelled()1157 public void onAccountSelectorCancelled() { 1158 } 1159 1160 // Implementation of DrawerFragmentListener 1161 @Override onDrawerItemClicked()1162 public void onDrawerItemClicked(){ 1163 closeDrawer(); 1164 } 1165 1166 @Override onContactsViewSelected(ContactsView mode)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 onCreateLabelButtonClicked()1178 public void onCreateLabelButtonClicked() { 1179 onCreateGroupMenuItemClicked(); 1180 } 1181 1182 @Override onOpenSettings()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 onLaunchHelpFeedback()1193 public void onLaunchHelpFeedback() { 1194 HelpUtils.launchHelpAndFeedbackForMainScreen(this); 1195 } 1196 1197 @Override onGroupViewSelected(GroupListItem groupListItem)1198 public void onGroupViewSelected(GroupListItem groupListItem) { 1199 onGroupMenuItemClicked(groupListItem.getGroupId()); 1200 } 1201 1202 @Override onAccountViewSelected(ContactListFilter filter)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 isGroupView()1209 public boolean isGroupView() { 1210 return mCurrentView == ContactsView.GROUP_VIEW; 1211 } 1212 isAssistantView()1213 protected boolean isAssistantView() { 1214 return mCurrentView == ContactsView.ASSISTANT; 1215 } 1216 isAllContactsView()1217 protected boolean isAllContactsView() { 1218 return mCurrentView == ContactsView.ALL_CONTACTS; 1219 } 1220 isAccountView()1221 protected boolean isAccountView() { 1222 return mCurrentView == ContactsView.ACCOUNT_VIEW; 1223 } 1224 isInSecondLevel()1225 public boolean isInSecondLevel() { 1226 return isGroupView() || isAssistantView(); 1227 } 1228 isInThirdLevel()1229 private boolean isInThirdLevel() { 1230 return isLastBackStackTag(TAG_THIRD_LEVEL); 1231 } 1232 isLastBackStackTag(String tag)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 popSecondLevel()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 closeDrawer()1255 public void closeDrawer() { 1256 mDrawerLayout.closeDrawer(GravityCompat.START); 1257 } 1258 createPreferenceIntent()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