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