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 @SuppressWarnings("MissingSuperCall") // TODO: Fix me 425 @Override onNewIntent(Intent intent)426 protected void onNewIntent(Intent intent) { 427 final String action = intent.getAction(); 428 if (GroupUtil.ACTION_CREATE_GROUP.equals(action)) { 429 mGroupUri = intent.getData(); 430 if (mGroupUri == null) { 431 toast(R.string.groupSavedErrorToast); 432 return; 433 } 434 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "Received group URI " + mGroupUri); 435 switchView(ContactsView.GROUP_VIEW); 436 mMembersFragment.toastForSaveAction(action); 437 return; 438 } 439 440 if (isGroupSaveAction(action)) { 441 mGroupUri = intent.getData(); 442 if (mGroupUri == null) { 443 popSecondLevel(); 444 toast(R.string.groupSavedErrorToast); 445 return; 446 } 447 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "Received group URI " + mGroupUri); 448 // ACTION_REMOVE_FROM_GROUP doesn't reload data, so it shouldn't cause b/32223934 449 // but it's necessary to use the previous fragment since 450 // GroupMembersFragment#mIsEditMode needs to be persisted between remove actions. 451 if (GroupUtil.ACTION_REMOVE_FROM_GROUP.equals(action)) { 452 switchToOrUpdateGroupView(action); 453 } else { 454 switchView(ContactsView.GROUP_VIEW); 455 } 456 mMembersFragment.toastForSaveAction(action); 457 } 458 459 setIntent(intent); 460 461 if (!processIntent(true)) { 462 finish(); 463 return; 464 } 465 466 mContactListFilterController.checkFilterValidity(false); 467 468 if (!isInSecondLevel()) { 469 // Re-initialize ActionBarAdapter because {@link #onNewIntent(Intent)} doesn't invoke 470 // {@link Fragment#onActivityCreated(Bundle)} where we initialize ActionBarAdapter 471 // initially. 472 mContactsListFragment.setParameters(/* ContactsRequest */ mRequest, 473 /* fromOnNewIntent */ true); 474 mContactsListFragment.initializeActionBarAdapter(null); 475 } 476 477 initializeFabVisibility(); 478 invalidateOptionsMenuIfNeeded(); 479 } 480 isGroupSaveAction(String action)481 private static boolean isGroupSaveAction(String action) { 482 return GroupUtil.ACTION_UPDATE_GROUP.equals(action) 483 || GroupUtil.ACTION_ADD_TO_GROUP.equals(action) 484 || GroupUtil.ACTION_REMOVE_FROM_GROUP.equals(action); 485 } 486 toast(int resId)487 private void toast(int resId) { 488 if (resId >= 0) { 489 Toast.makeText(this, resId, Toast.LENGTH_SHORT).show(); 490 } 491 } 492 493 /** 494 * Resolve the intent and initialize {@link #mRequest}, and launch another activity if redirect 495 * is needed. 496 * 497 * @param forNewIntent set true if it's called from {@link #onNewIntent(Intent)}. 498 * @return {@code true} if {@link PeopleActivity} should continue running. {@code false} 499 * if it shouldn't, in which case the caller should finish() itself and shouldn't do 500 * farther initialization. 501 */ processIntent(boolean forNewIntent)502 private boolean processIntent(boolean forNewIntent) { 503 // Extract relevant information from the intent 504 mRequest = mIntentResolver.resolveIntent(getIntent()); 505 if (Log.isLoggable(TAG, Log.DEBUG)) { 506 Log.d(TAG, this + " processIntent: forNewIntent=" + forNewIntent 507 + " intent=" + getIntent() + " request=" + mRequest); 508 } 509 if (!mRequest.isValid()) { 510 setResult(RESULT_CANCELED); 511 return false; 512 } 513 514 switch (mRequest.getActionCode()) { 515 case ContactsRequest.ACTION_VIEW_CONTACT: { 516 ImplicitIntentsUtil.startQuickContact( 517 this, mRequest.getContactUri(), ScreenType.UNKNOWN); 518 return false; 519 } 520 case ContactsRequest.ACTION_INSERT_GROUP: { 521 onCreateGroupMenuItemClicked(); 522 return true; 523 } 524 case ContactsRequest.ACTION_VIEW_GROUP: 525 case ContactsRequest.ACTION_EDIT_GROUP: { 526 mShouldSwitchToGroupView = true; 527 return true; 528 } 529 } 530 return true; 531 } 532 createViewsAndFragments()533 private void createViewsAndFragments() { 534 setContentView(R.layout.people_activity); 535 536 final FragmentManager fragmentManager = getFragmentManager(); 537 538 setUpListFragment(fragmentManager); 539 540 mMembersFragment = (GroupMembersFragment) fragmentManager.findFragmentByTag(TAG_GROUP_VIEW); 541 542 // Configure floating action button 543 mFloatingActionButtonContainer = findViewById(R.id.floating_action_button_container); 544 final ImageButton floatingActionButton 545 = (ImageButton) findViewById(R.id.floating_action_button); 546 floatingActionButton.setOnClickListener(new View.OnClickListener() { 547 @Override 548 public void onClick(View v) { 549 AccountFilterUtil.startEditorIntent(PeopleActivity.this, getIntent(), 550 mContactListFilterController.getFilter()); 551 } 552 }); 553 mFloatingActionButtonController = new FloatingActionButtonController(this, 554 mFloatingActionButtonContainer, floatingActionButton); 555 556 invalidateOptionsMenuIfNeeded(); 557 558 mLayoutRoot = (CoordinatorLayout) findViewById(R.id.root); 559 560 if (mShouldSwitchToGroupView && !mIsRecreatedInstance) { 561 mGroupUri = mRequest.getContactUri(); 562 switchToOrUpdateGroupView(GroupUtil.ACTION_SWITCH_GROUP); 563 mShouldSwitchToGroupView = false; 564 } 565 } 566 567 @Override setContentView(@ayoutRes int layoutResID)568 public void setContentView(@LayoutRes int layoutResID) { 569 final ViewGroup parent = (ViewGroup) findViewById(R.id.content_frame); 570 if (parent != null) { 571 parent.removeAllViews(); 572 } 573 LayoutInflater.from(this).inflate(layoutResID, parent); 574 } 575 setUpListFragment(FragmentManager fragmentManager)576 private void setUpListFragment(FragmentManager fragmentManager) { 577 mContactsListFragment = (DefaultContactBrowseListFragment) 578 fragmentManager.findFragmentByTag(TAG_ALL); 579 580 if (mContactsListFragment == null) { 581 mContactsListFragment = new DefaultContactBrowseListFragment(); 582 mContactsListFragment.setAnimateOnLoad(true); 583 fragmentManager.beginTransaction() 584 .add(R.id.contacts_list_container, mContactsListFragment, TAG_ALL) 585 .commit(); 586 fragmentManager.executePendingTransactions(); 587 } 588 589 mContactsListFragment.setContactsAvailable(areContactsAvailable()); 590 mContactsListFragment.setListType(mContactListFilterController.getFilterListType()); 591 mContactsListFragment.setParameters(/* ContactsRequest */ mRequest, 592 /* fromOnNewIntent */ false); 593 } 594 595 @Override onPause()596 protected void onPause() { 597 mProviderStatusWatcher.stop(); 598 599 LocalBroadcastManager.getInstance(this).unregisterReceiver(mSaveServiceListener); 600 601 super.onPause(); 602 603 ContentResolver.removeStatusChangeListener(mStatusChangeListenerHandle); 604 onSyncStateUpdated(); 605 } 606 607 @SuppressWarnings("MissingSuperCall") // TODO: Fix me 608 @Override onMultiWindowModeChanged(boolean entering)609 public void onMultiWindowModeChanged(boolean entering) { 610 initializeHomeVisibility(); 611 } 612 613 @Override onResume()614 protected void onResume() { 615 super.onResume(); 616 617 if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) { 618 updateStatusBarBackground(); 619 } 620 621 if (mShouldSwitchToAllContacts) { 622 switchToAllContacts(); 623 } 624 625 mProviderStatusWatcher.start(); 626 updateViewConfiguration(true); 627 628 mStatusChangeListenerHandle = ContentResolver.addStatusChangeListener( 629 ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE 630 | ContentResolver.SYNC_OBSERVER_TYPE_PENDING 631 | ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, 632 mSyncStatusObserver); 633 onSyncStateUpdated(); 634 635 initializeFabVisibility(); 636 initializeHomeVisibility(); 637 638 mSaveServiceListener = new SaveServiceListener(); 639 LocalBroadcastManager.getInstance(this).registerReceiver(mSaveServiceListener, 640 new IntentFilter(ContactSaveService.BROADCAST_GROUP_DELETED)); 641 } 642 updateStatusBarBackground()643 public void updateStatusBarBackground() { 644 updateStatusBarBackground(/* color */ -1); 645 } 646 updateStatusBarBackground(int color)647 public void updateStatusBarBackground(int color) { 648 if (!CompatUtils.isLollipopCompatible()) return; 649 if (color == -1) { 650 mDrawerLayout.setStatusBarBackgroundColor( 651 MaterialColorMapUtils.getStatusBarColor(this)); 652 } else { 653 mDrawerLayout.setStatusBarBackgroundColor(color); 654 } 655 mDrawerLayout.invalidate(); 656 getWindow().setStatusBarColor(Color.TRANSPARENT); 657 } 658 659 @Override onDestroy()660 protected void onDestroy() { 661 mProviderStatusWatcher.removeListener(mProviderStatusListener); 662 mContactListFilterController.removeListener(mFilterListener); 663 super.onDestroy(); 664 } 665 initializeFabVisibility()666 private void initializeFabVisibility() { 667 mFloatingActionButtonContainer.setVisibility(shouldHideFab() ? View.GONE : View.VISIBLE); 668 mFloatingActionButtonController.resetIn(); 669 wasLastFabAnimationScaleIn = !shouldHideFab(); 670 } 671 initializeHomeVisibility()672 private void initializeHomeVisibility() { 673 // Remove the navigation icon if we return to the fragment in a search or select state 674 if (getToolbar() != null && (isListFragmentInSelectionMode() 675 || isListFragmentInSearchMode() || isGroupsFragmentInSelectionMode() 676 || isGroupsFragmentInSearchMode())) { 677 getToolbar().setNavigationIcon(null); 678 } 679 } 680 shouldHideFab()681 private boolean shouldHideFab() { 682 if (mContactsListFragment != null && mContactsListFragment.getActionBarAdapter() == null 683 || isInSecondLevel()) { 684 return true; 685 } 686 return isListFragmentInSearchMode() 687 || isListFragmentInSelectionMode(); 688 } 689 showFabWithAnimation(boolean showFab)690 public void showFabWithAnimation(boolean showFab) { 691 if (mFloatingActionButtonContainer == null) { 692 return; 693 } 694 if (showFab) { 695 if (!wasLastFabAnimationScaleIn) { 696 mFloatingActionButtonContainer.setVisibility(View.VISIBLE); 697 mFloatingActionButtonController.scaleIn(0); 698 } 699 wasLastFabAnimationScaleIn = true; 700 701 } else { 702 if (wasLastFabAnimationScaleIn) { 703 mFloatingActionButtonContainer.setVisibility(View.VISIBLE); 704 mFloatingActionButtonController.scaleOut(); 705 } 706 wasLastFabAnimationScaleIn = false; 707 } 708 } 709 updateViewConfiguration(boolean forceUpdate)710 private void updateViewConfiguration(boolean forceUpdate) { 711 int providerStatus = mProviderStatusWatcher.getProviderStatus(); 712 if (!forceUpdate && (mProviderStatus != null) 713 && (mProviderStatus.equals(providerStatus))) return; 714 mProviderStatus = providerStatus; 715 716 final FragmentManager fragmentManager= getFragmentManager(); 717 final FragmentTransaction transaction = fragmentManager.beginTransaction(); 718 719 // Change in CP2's provider status may not take effect immediately, see b/30566908. 720 // So we need to handle the case where provider status is STATUS_EMPTY and there is 721 // actually at least one real account (not "local" account) on device. 722 if (shouldShowList()) { 723 if (mContactsListFragment != null) { 724 final Fragment unavailableFragment = fragmentManager 725 .findFragmentByTag(TAG_UNAVAILABLE); 726 if (unavailableFragment != null) { 727 transaction.remove(unavailableFragment); 728 } 729 if (mContactsListFragment.isHidden()) { 730 transaction.show(mContactsListFragment); 731 } 732 mContactsListFragment.setContactsAvailable(areContactsAvailable()); 733 mContactsListFragment.setEnabled(true); 734 } 735 } else { 736 // Setting up the page so that the user can still use the app 737 // even without an account. 738 if (mContactsListFragment != null) { 739 mContactsListFragment.setEnabled(false); 740 } 741 final ContactsUnavailableFragment fragment = new ContactsUnavailableFragment(); 742 transaction.hide(mContactsListFragment); 743 transaction.replace(R.id.contacts_unavailable_container, fragment, TAG_UNAVAILABLE); 744 fragment.updateStatus(mProviderStatus); 745 } 746 if (!transaction.isEmpty()) { 747 transaction.commit(); 748 fragmentManager.executePendingTransactions(); 749 } 750 751 invalidateOptionsMenuIfNeeded(); 752 } 753 shouldShowList()754 private boolean shouldShowList() { 755 return mProviderStatus != null 756 && ((mProviderStatus.equals(ProviderStatus.STATUS_EMPTY) 757 && mAccountTypeManager.hasNonLocalAccount()) 758 || mProviderStatus.equals(ProviderStatus.STATUS_NORMAL)); 759 } 760 invalidateOptionsMenuIfNeeded()761 private void invalidateOptionsMenuIfNeeded() { 762 if (mContactsListFragment != null 763 && mContactsListFragment.getOptionsMenuContactsAvailable() 764 != areContactsAvailable()) { 765 invalidateOptionsMenu(); 766 } 767 } 768 769 @Override onKeyDown(int keyCode, KeyEvent event)770 public boolean onKeyDown(int keyCode, KeyEvent event) { 771 // If the drawer is open, consume KEYCODE_BACK event only. 772 if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) { 773 if (keyCode == KeyEvent.KEYCODE_BACK) { 774 // Should eventually go to onBackPressed(). 775 return super.onKeyDown(keyCode, event); 776 } 777 return false; 778 } 779 // Bring up the search UI if the user starts typing 780 final int unicodeChar = event.getUnicodeChar(); 781 if ((unicodeChar != 0) 782 // If COMBINING_ACCENT is set, it's not a unicode character. 783 && ((unicodeChar & KeyCharacterMap.COMBINING_ACCENT) == 0) 784 && !Character.isWhitespace(unicodeChar)) { 785 if (mContactsListFragment.onKeyDown(unicodeChar)) { 786 return true; 787 } 788 } 789 790 return super.onKeyDown(keyCode, event); 791 } 792 793 @Override onBackPressed()794 public void onBackPressed() { 795 if (!isSafeToCommitTransactions()) { 796 return; 797 } 798 799 // Handle the back event in drawer first. 800 if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) { 801 closeDrawer(); 802 return; 803 } 804 805 // Handle the back event in "second level". 806 if (isGroupView()) { 807 onBackPressedGroupView(); 808 return; 809 } 810 811 if (isAssistantView()) { 812 onBackPressedAssistantView(); 813 return; 814 } 815 816 // If feature highlight is present, let it handle the back event before 817 // mContactsListFragment. 818 if (FeatureHighlightHelper.tryRemoveHighlight(this)) { 819 return; 820 } 821 822 // Handle the back event in "first level" - mContactsListFragment. 823 if (maybeHandleInListFragment()) { 824 return; 825 } 826 827 super.onBackPressed(); 828 } 829 onBackPressedGroupView()830 private void onBackPressedGroupView() { 831 if (mMembersFragment.isEditMode()) { 832 mMembersFragment.exitEditMode(); 833 } else if (mMembersFragment.getActionBarAdapter().isSelectionMode()) { 834 mMembersFragment.getActionBarAdapter().setSelectionMode(false); 835 mMembersFragment.displayCheckBoxes(false); 836 } else if (mMembersFragment.getActionBarAdapter().isSearchMode()) { 837 mMembersFragment.getActionBarAdapter().setSearchMode(false); 838 } else { 839 switchToAllContacts(); 840 } 841 } 842 onBackPressedAssistantView()843 private void onBackPressedAssistantView() { 844 if (!isInThirdLevel()) { 845 switchToAllContacts(); 846 } else { 847 setDrawerLockMode(/* enabled */ true); 848 super.onBackPressed(); 849 } 850 } 851 852 // Returns true if back event is handled in this method. maybeHandleInListFragment()853 private boolean maybeHandleInListFragment() { 854 if (isListFragmentInSelectionMode()) { 855 mContactsListFragment.getActionBarAdapter().setSelectionMode(false); 856 return true; 857 } 858 859 if (isListFragmentInSearchMode()) { 860 mContactsListFragment.getActionBarAdapter().setSearchMode(false); 861 if (mContactsListFragment.wasSearchResultClicked()) { 862 mContactsListFragment.resetSearchResultClicked(); 863 } else { 864 Logger.logScreenView(this, ScreenType.SEARCH_EXIT); 865 Logger.logSearchEvent(mContactsListFragment.createSearchState()); 866 } 867 return true; 868 } 869 870 if (!AccountFilterUtil.isAllContactsFilter(mContactListFilterController.getFilter()) 871 && !mContactsListFragment.isHidden()) { 872 // If mContactsListFragment is hidden, then mContactsUnavailableFragment is visible so we 873 // don't need to switch to all contacts. 874 switchToAllContacts(); 875 return true; 876 } 877 878 return false; 879 } 880 isListFragmentInSelectionMode()881 private boolean isListFragmentInSelectionMode() { 882 return mContactsListFragment != null && mContactsListFragment.getActionBarAdapter() != null 883 && mContactsListFragment.getActionBarAdapter().isSelectionMode(); 884 } 885 isListFragmentInSearchMode()886 private boolean isListFragmentInSearchMode() { 887 return mContactsListFragment != null && mContactsListFragment.getActionBarAdapter() != null 888 && mContactsListFragment.getActionBarAdapter().isSearchMode(); 889 } 890 isGroupsFragmentInSelectionMode()891 private boolean isGroupsFragmentInSelectionMode() { 892 return mMembersFragment != null && mMembersFragment.getActionBarAdapter() != null 893 && mMembersFragment.getActionBarAdapter().isSelectionMode(); 894 } 895 isGroupsFragmentInSearchMode()896 private boolean isGroupsFragmentInSearchMode() { 897 return mMembersFragment != null && mMembersFragment.getActionBarAdapter() != null 898 && mMembersFragment.getActionBarAdapter().isSearchMode(); 899 } 900 901 @Override onSaveInstanceState(Bundle outState)902 protected void onSaveInstanceState(Bundle outState) { 903 super.onSaveInstanceState(outState); 904 if (mNewGroupAccount != null) { 905 outState.putString(KEY_NEW_GROUP_ACCOUNT, mNewGroupAccount.stringify()); 906 } 907 outState.putInt(KEY_CONTACTS_VIEW, mCurrentView.ordinal()); 908 outState.putParcelable(KEY_GROUP_URI, mGroupUri); 909 } 910 911 @Override onRestoreInstanceState(Bundle savedInstanceState)912 protected void onRestoreInstanceState(Bundle savedInstanceState) { 913 super.onRestoreInstanceState(savedInstanceState); 914 mGroupUri = savedInstanceState.getParcelable(KEY_GROUP_URI); 915 } 916 onGroupDeleted(final Intent intent)917 private void onGroupDeleted(final Intent intent) { 918 if (!ContactSaveService.canUndo(intent)) return; 919 920 final AccessibilityManager am = 921 (AccessibilityManager) getSystemService(Context.ACCESSIBILITY_SERVICE); 922 //TODO set to INDEFINITE and track user interaction to dismiss b/33208886 923 final int accessibilityLength = 15000; 924 final int length = am.isEnabled() ? accessibilityLength : Snackbar.LENGTH_LONG; 925 final String message = getString(R.string.groupDeletedToast); 926 927 final Snackbar snackbar = Snackbar.make(mLayoutRoot, message, length) 928 .setAction(R.string.undo, new View.OnClickListener() { 929 @Override 930 public void onClick(View v) { 931 ContactSaveService.startService(PeopleActivity.this, 932 ContactSaveService.createUndoIntent(PeopleActivity.this, intent)); 933 } 934 }).setActionTextColor(ContextCompat.getColor(this, R.color.snackbar_action_text)); 935 936 // Announce for a11y talkback 937 mLayoutRoot.announceForAccessibility(message); 938 mLayoutRoot.announceForAccessibility(getString(R.string.undo)); 939 940 snackbar.show(); 941 } 942 943 private class SaveServiceListener extends BroadcastReceiver { 944 @Override onReceive(Context context, Intent intent)945 public void onReceive(Context context, Intent intent) { 946 switch (intent.getAction()) { 947 case ContactSaveService.BROADCAST_GROUP_DELETED: 948 onGroupDeleted(intent); 949 break; 950 } 951 } 952 } 953 onGroupMenuItemClicked(long groupId)954 private void onGroupMenuItemClicked(long groupId) { 955 if (isGroupView() && mMembersFragment != null 956 && mMembersFragment.isCurrentGroup(groupId)) { 957 return; 958 } 959 mGroupUri = ContentUris.withAppendedId(ContactsContract.Groups.CONTENT_URI, groupId); 960 switchToOrUpdateGroupView(GroupUtil.ACTION_SWITCH_GROUP); 961 } 962 onFilterMenuItemClicked(Intent intent)963 private void onFilterMenuItemClicked(Intent intent) { 964 // We must pop second level first to "restart" mContactsListFragment before changing filter. 965 if (isInSecondLevel()) { 966 popSecondLevel(); 967 showFabWithAnimation(/* showFab */ true); 968 // HACK: swap the current filter to force listeners to update because the group 969 // member view no longer changes the filter. Fix for b/32223767 970 final ContactListFilter current = mContactListFilterController.getFilter(); 971 mContactListFilterController.setContactListFilter( 972 AccountFilterUtil.createContactsFilter(this), false); 973 mContactListFilterController.setContactListFilter(current, false); 974 } 975 mCurrentView = ContactsView.ACCOUNT_VIEW; 976 AccountFilterUtil.handleAccountFilterResult(mContactListFilterController, 977 AppCompatActivity.RESULT_OK, intent); 978 } 979 switchToOrUpdateGroupView(String action)980 private void switchToOrUpdateGroupView(String action) { 981 // If group fragment is active and visible, we simply update it. 982 if (mMembersFragment != null && !mMembersFragment.isInactive()) { 983 mMembersFragment.updateExistingGroupFragment(mGroupUri, action); 984 } else { 985 switchView(ContactsView.GROUP_VIEW); 986 } 987 } 988 launchAssistant()989 protected void launchAssistant() { 990 switchView(ContactsView.ASSISTANT); 991 } 992 switchView(ContactsView contactsView)993 private void switchView(ContactsView contactsView) { 994 mCurrentView = contactsView; 995 996 final FragmentManager fragmentManager = getFragmentManager(); 997 final FragmentTransaction transaction = fragmentManager.beginTransaction(); 998 popSecondLevel(); 999 if (isGroupView()) { 1000 mMembersFragment = GroupMembersFragment.newInstance(mGroupUri); 1001 transaction.replace( 1002 R.id.contacts_list_container, mMembersFragment, TAG_GROUP_VIEW); 1003 } else if (isAssistantView()) { 1004 Fragment uiFragment = fragmentManager.findFragmentByTag(TAG_ASSISTANT); 1005 Fragment unavailableFragment = fragmentManager.findFragmentByTag(TAG_UNAVAILABLE); 1006 if (uiFragment == null) { 1007 uiFragment = ObjectFactory.getAssistantFragment(); 1008 } 1009 if (unavailableFragment != null) { 1010 transaction.remove(unavailableFragment); 1011 } 1012 transaction.replace(R.id.contacts_list_container, uiFragment, TAG_ASSISTANT); 1013 resetToolBarStatusBarColor(); 1014 } 1015 transaction.addToBackStack(TAG_SECOND_LEVEL); 1016 transaction.commit(); 1017 fragmentManager.executePendingTransactions(); 1018 1019 showFabWithAnimation(/* showFab */ false); 1020 } 1021 switchToAllContacts()1022 public void switchToAllContacts() { 1023 popSecondLevel(); 1024 mShouldSwitchToAllContacts = false; 1025 mCurrentView = ContactsView.ALL_CONTACTS; 1026 mDrawerFragment.setNavigationItemChecked(ContactsView.ALL_CONTACTS); 1027 showFabWithAnimation(/* showFab */ true); 1028 mContactsListFragment.scrollToTop(); 1029 resetFilter(); 1030 setTitle(getString(R.string.contactsList)); 1031 } 1032 resetFilter()1033 private void resetFilter() { 1034 final Intent intent = new Intent(); 1035 final ContactListFilter filter = AccountFilterUtil.createContactsFilter(this); 1036 intent.putExtra(AccountFilterActivity.EXTRA_CONTACT_LIST_FILTER, filter); 1037 AccountFilterUtil.handleAccountFilterResult( 1038 mContactListFilterController, AppCompatActivity.RESULT_OK, intent); 1039 } 1040 1041 // Reset toolbar and status bar color to Contacts theme color. resetToolBarStatusBarColor()1042 private void resetToolBarStatusBarColor() { 1043 findViewById(R.id.toolbar_frame).setBackgroundColor( 1044 ContextCompat.getColor(this, R.color.primary_color)); 1045 updateStatusBarBackground(ContextCompat.getColor(this, R.color.primary_color_dark)); 1046 } 1047 getListFragment()1048 protected DefaultContactBrowseListFragment getListFragment() { 1049 return mContactsListFragment; 1050 } 1051 getGroupFragment()1052 protected GroupMembersFragment getGroupFragment() { 1053 return mMembersFragment; 1054 } 1055 handleFilterChangeForFragment(ContactListFilter filter)1056 private void handleFilterChangeForFragment(ContactListFilter filter) { 1057 if (mContactsListFragment.canSetActionBar()) { 1058 mContactsListFragment.setFilterAndUpdateTitle(filter); 1059 // Scroll to top after filter is changed. 1060 mContactsListFragment.scrollToTop(); 1061 } 1062 } 1063 handleFilterChangeForActivity(ContactListFilter filter)1064 private void handleFilterChangeForActivity(ContactListFilter filter) { 1065 // The filter was changed while this activity was in the background. If we're in the 1066 // assistant view Switch to the main contacts list when we resume to prevent 1067 // b/31838582 and b/31829161 1068 // TODO: this is a hack; we need to do some cleanup of the contact list filter stuff 1069 if (isAssistantView() && filter.isContactsFilterType()) { 1070 mShouldSwitchToAllContacts = true; 1071 } 1072 1073 if (CompatUtils.isNCompatible()) { 1074 getWindow().getDecorView() 1075 .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); 1076 } 1077 invalidateOptionsMenu(); 1078 } 1079 updateDrawerGroupMenu(long groupId)1080 public void updateDrawerGroupMenu(long groupId) { 1081 if (mDrawerFragment != null) { 1082 mDrawerFragment.updateGroupMenu(groupId); 1083 } 1084 } 1085 setDrawerLockMode(boolean enabled)1086 public void setDrawerLockMode(boolean enabled) { 1087 // Prevent drawer from being opened by sliding from the start of screen. 1088 mDrawerLayout.setDrawerLockMode(enabled ? DrawerLayout.LOCK_MODE_UNLOCKED 1089 : DrawerLayout.LOCK_MODE_LOCKED_CLOSED); 1090 1091 // Order of these statements matter. 1092 // Display back button and disable drawer indicator. 1093 if (enabled) { 1094 getSupportActionBar().setDisplayHomeAsUpEnabled(false); 1095 mToggle.setDrawerIndicatorEnabled(true); 1096 } else { 1097 mToggle.setDrawerIndicatorEnabled(false); 1098 getSupportActionBar().setDisplayHomeAsUpEnabled(true); 1099 } 1100 } 1101 getToolbar()1102 public Toolbar getToolbar() { 1103 return mToolbar; 1104 } 1105 1106 @Override onPostCreate(Bundle savedInstanceState)1107 protected void onPostCreate(Bundle savedInstanceState) { 1108 super.onPostCreate(savedInstanceState); 1109 mToggle.syncState(); 1110 } 1111 1112 @Override onConfigurationChanged(Configuration newConfig)1113 public void onConfigurationChanged(Configuration newConfig) { 1114 super.onConfigurationChanged(newConfig); 1115 mToggle.onConfigurationChanged(newConfig); 1116 } 1117 onCreateGroupMenuItemClicked()1118 protected void onCreateGroupMenuItemClicked() { 1119 // Select the account to create the group 1120 final Bundle extras = getIntent().getExtras(); 1121 final Account account = extras == null ? null : 1122 (Account) extras.getParcelable(Intents.Insert.EXTRA_ACCOUNT); 1123 if (account == null) { 1124 selectAccountForNewGroup(); 1125 } else { 1126 final String dataSet = extras == null 1127 ? null : extras.getString(Intents.Insert.EXTRA_DATA_SET); 1128 final AccountWithDataSet accountWithDataSet = new AccountWithDataSet( 1129 account.name, account.type, dataSet); 1130 onAccountChosen(accountWithDataSet, /* extraArgs */ null); 1131 } 1132 } 1133 selectAccountForNewGroup()1134 private void selectAccountForNewGroup() { 1135 // This should never block because the DrawerFragment loads the accounts and the 1136 // "Create Label" item only exists when that loading finishes 1137 final List<AccountInfo> accounts = Futures.getUnchecked(AccountTypeManager.getInstance(this) 1138 .filterAccountsAsync(AccountTypeManager.AccountFilter.GROUPS_WRITABLE)); 1139 if (accounts.isEmpty()) { 1140 // We shouldn't present the add group button if there are no writable accounts 1141 // but check it since it's possible we are started with an Intent. 1142 Toast.makeText(this, R.string.groupCreateFailedToast, Toast.LENGTH_SHORT).show(); 1143 return; 1144 } 1145 // If there is a single writable account, use it w/o showing a dialog. 1146 if (accounts.size() == 1) { 1147 onAccountChosen(accounts.get(0).getAccount(), /* extraArgs */ null); 1148 return; 1149 } 1150 SelectAccountDialogFragment.show(getFragmentManager(), R.string.dialog_new_group_account, 1151 AccountTypeManager.AccountFilter.GROUPS_WRITABLE, /* extraArgs */ null, 1152 TAG_SELECT_ACCOUNT_DIALOG); 1153 } 1154 1155 // Implementation of SelectAccountDialogFragment.Listener 1156 @Override onAccountChosen(AccountWithDataSet account, Bundle extraArgs)1157 public void onAccountChosen(AccountWithDataSet account, Bundle extraArgs) { 1158 mNewGroupAccount = account; 1159 GroupNameEditDialogFragment.newInstanceForCreation( 1160 mNewGroupAccount, GroupUtil.ACTION_CREATE_GROUP) 1161 .show(getFragmentManager(), TAG_GROUP_NAME_EDIT_DIALOG); 1162 } 1163 1164 @Override onAccountSelectorCancelled()1165 public void onAccountSelectorCancelled() { 1166 } 1167 1168 // Implementation of DrawerFragmentListener 1169 @Override onDrawerItemClicked()1170 public void onDrawerItemClicked(){ 1171 closeDrawer(); 1172 } 1173 1174 @Override onContactsViewSelected(ContactsView mode)1175 public void onContactsViewSelected(ContactsView mode) { 1176 if (mode == ContactsView.ALL_CONTACTS) { 1177 switchToAllContacts(); 1178 } else if (mode == ContactsView.ASSISTANT) { 1179 launchAssistant(); 1180 } else { 1181 throw new IllegalStateException("Unknown view " + mode); 1182 } 1183 } 1184 1185 @Override onCreateLabelButtonClicked()1186 public void onCreateLabelButtonClicked() { 1187 onCreateGroupMenuItemClicked(); 1188 } 1189 1190 @Override onOpenSettings()1191 public void onOpenSettings() { 1192 new Handler().postDelayed(new Runnable() { 1193 @Override 1194 public void run() { 1195 startActivity(createPreferenceIntent()); 1196 } 1197 }, DRAWER_CLOSE_DELAY); 1198 } 1199 1200 @Override onLaunchHelpFeedback()1201 public void onLaunchHelpFeedback() { 1202 HelpUtils.launchHelpAndFeedbackForMainScreen(this); 1203 } 1204 1205 @Override onGroupViewSelected(GroupListItem groupListItem)1206 public void onGroupViewSelected(GroupListItem groupListItem) { 1207 onGroupMenuItemClicked(groupListItem.getGroupId()); 1208 } 1209 1210 @Override onAccountViewSelected(ContactListFilter filter)1211 public void onAccountViewSelected(ContactListFilter filter) { 1212 final Intent intent = new Intent(); 1213 intent.putExtra(AccountFilterActivity.EXTRA_CONTACT_LIST_FILTER, filter); 1214 onFilterMenuItemClicked(intent); 1215 } 1216 isGroupView()1217 public boolean isGroupView() { 1218 return mCurrentView == ContactsView.GROUP_VIEW; 1219 } 1220 isAssistantView()1221 protected boolean isAssistantView() { 1222 return mCurrentView == ContactsView.ASSISTANT; 1223 } 1224 isAllContactsView()1225 protected boolean isAllContactsView() { 1226 return mCurrentView == ContactsView.ALL_CONTACTS; 1227 } 1228 isAccountView()1229 protected boolean isAccountView() { 1230 return mCurrentView == ContactsView.ACCOUNT_VIEW; 1231 } 1232 isInSecondLevel()1233 public boolean isInSecondLevel() { 1234 return isGroupView() || isAssistantView(); 1235 } 1236 isInThirdLevel()1237 private boolean isInThirdLevel() { 1238 return isLastBackStackTag(TAG_THIRD_LEVEL); 1239 } 1240 isLastBackStackTag(String tag)1241 private boolean isLastBackStackTag(String tag) { 1242 final int count = getFragmentManager().getBackStackEntryCount(); 1243 if (count > 0) { 1244 final FragmentManager.BackStackEntry last = 1245 getFragmentManager().getBackStackEntryAt(count - 1); 1246 if (tag == null) { 1247 return last.getName() == null; 1248 } 1249 return tag.equals(last.getName()); 1250 } 1251 return false; 1252 } 1253 popSecondLevel()1254 private void popSecondLevel() { 1255 getFragmentManager().popBackStackImmediate( 1256 TAG_ASSISTANT_HELPER, FragmentManager.POP_BACK_STACK_INCLUSIVE); 1257 getFragmentManager().popBackStackImmediate( 1258 TAG_SECOND_LEVEL, FragmentManager.POP_BACK_STACK_INCLUSIVE); 1259 mMembersFragment = null; 1260 resetToolBarStatusBarColor(); 1261 } 1262 closeDrawer()1263 public void closeDrawer() { 1264 mDrawerLayout.closeDrawer(GravityCompat.START); 1265 } 1266 createPreferenceIntent()1267 private Intent createPreferenceIntent() { 1268 final Intent intent = new Intent(this, ContactsPreferenceActivity.class); 1269 intent.putExtra(ContactsPreferenceActivity.EXTRA_NEW_LOCAL_PROFILE, 1270 ContactEditorFragment.INTENT_EXTRA_NEW_LOCAL_PROFILE); 1271 return intent; 1272 } 1273 1274 1275 } 1276