PeopleActivity.java revision 4959ca3d0093a798859660f2914003d0da777645
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 17package com.android.contacts.activities; 18 19import com.android.contacts.ContactLoader; 20import com.android.contacts.ContactSaveService; 21import com.android.contacts.ContactsActivity; 22import com.android.contacts.R; 23import com.android.contacts.detail.ContactDetailFragment; 24import com.android.contacts.detail.ContactDetailLayoutController; 25import com.android.contacts.detail.ContactDetailTabCarousel; 26import com.android.contacts.detail.ContactDetailUpdatesFragment; 27import com.android.contacts.detail.ContactLoaderFragment; 28import com.android.contacts.detail.ContactLoaderFragment.ContactLoaderFragmentListener; 29import com.android.contacts.group.GroupBrowseListFragment; 30import com.android.contacts.group.GroupBrowseListFragment.OnGroupBrowserActionListener; 31import com.android.contacts.group.GroupDetailFragment; 32import com.android.contacts.interactions.ContactDeletionInteraction; 33import com.android.contacts.interactions.ImportExportDialogFragment; 34import com.android.contacts.interactions.PhoneNumberInteraction; 35import com.android.contacts.list.AccountFilterActivity; 36import com.android.contacts.list.ContactBrowseListContextMenuAdapter; 37import com.android.contacts.list.ContactBrowseListFragment; 38import com.android.contacts.list.ContactEntryListFragment; 39import com.android.contacts.list.ContactListFilter; 40import com.android.contacts.list.ContactListFilterController; 41import com.android.contacts.list.ContactTileAdapter.DisplayType; 42import com.android.contacts.list.ContactsIntentResolver; 43import com.android.contacts.list.ContactsRequest; 44import com.android.contacts.list.ContactsUnavailableFragment; 45import com.android.contacts.list.CustomContactListFilterActivity; 46import com.android.contacts.list.DefaultContactBrowseListFragment; 47import com.android.contacts.list.DirectoryListLoader; 48import com.android.contacts.list.OnContactBrowserActionListener; 49import com.android.contacts.list.OnContactsUnavailableActionListener; 50import com.android.contacts.list.ProviderStatusLoader; 51import com.android.contacts.list.ProviderStatusLoader.ProviderStatusListener; 52import com.android.contacts.list.StrequentContactListFragment; 53import com.android.contacts.model.AccountTypeManager; 54import com.android.contacts.preference.ContactsPreferenceActivity; 55import com.android.contacts.util.AccountSelectionUtil; 56import com.android.contacts.util.AccountsListAdapter; 57import com.android.contacts.util.DialogManager; 58import com.android.contacts.util.PhoneCapabilityTester; 59import com.android.contacts.widget.ContextMenuAdapter; 60 61import android.accounts.Account; 62import android.app.ActionBar; 63import android.app.ActionBar.Tab; 64import android.app.ActionBar.TabListener; 65import android.app.Activity; 66import android.app.Fragment; 67import android.app.FragmentManager; 68import android.app.FragmentTransaction; 69import android.content.ActivityNotFoundException; 70import android.content.ContentValues; 71import android.content.Intent; 72import android.content.res.TypedArray; 73import android.net.Uri; 74import android.os.Bundle; 75import android.os.Handler; 76import android.provider.ContactsContract; 77import android.provider.ContactsContract.Contacts; 78import android.provider.ContactsContract.Intents; 79import android.provider.ContactsContract.ProviderStatus; 80import android.provider.Settings; 81import android.support.v4.view.ViewPager; 82import android.util.Log; 83import android.view.KeyEvent; 84import android.view.Menu; 85import android.view.MenuInflater; 86import android.view.MenuItem; 87import android.view.View; 88import android.view.View.OnClickListener; 89import android.view.Window; 90import android.widget.AdapterView; 91import android.widget.AdapterView.OnItemClickListener; 92import android.widget.ListPopupWindow; 93import android.widget.SearchView; 94import android.widget.Toast; 95 96import java.util.ArrayList; 97 98/** 99 * Displays a list to browse contacts. For xlarge screens, this also displays a detail-pane on 100 * the right. 101 */ 102public class PeopleActivity extends ContactsActivity 103 implements View.OnCreateContextMenuListener, ActionBarAdapter.Listener, 104 DialogManager.DialogShowingViewActivity, 105 ContactListFilterController.ContactListFilterListener, ProviderStatusListener { 106 107 private static final String TAG = "PeopleActivity"; 108 109 private static final int SUBACTIVITY_NEW_CONTACT = 2; 110 private static final int SUBACTIVITY_EDIT_CONTACT = 3; 111 private static final int SUBACTIVITY_NEW_GROUP = 4; 112 private static final int SUBACTIVITY_EDIT_GROUP = 5; 113 private static final int SUBACTIVITY_ACCOUNT_FILTER = 6; 114 private static final int SUBACTIVITY_CUSTOMIZE_FILTER = 7; 115 116 private static final String KEY_SEARCH_MODE = "searchMode"; 117 118 private DialogManager mDialogManager = new DialogManager(this); 119 120 private ContactsIntentResolver mIntentResolver; 121 private ContactsRequest mRequest; 122 123 private ActionBarAdapter mActionBarAdapter; 124 125 private boolean mSearchMode; 126 127 private ContactDetailFragment mContactDetailFragment; 128 private ContactDetailUpdatesFragment mContactDetailUpdatesFragment; 129 private final ContactDetailFragmentListener mContactDetailFragmentListener = 130 new ContactDetailFragmentListener(); 131 132 private ContactLoaderFragment mContactDetailLoaderFragment; 133 private final ContactDetailLoaderFragmentListener mContactDetailLoaderFragmentListener = 134 new ContactDetailLoaderFragmentListener(); 135 136 private GroupDetailFragment mGroupDetailFragment; 137 private final GroupDetailFragmentListener mGroupDetailFragmentListener = 138 new GroupDetailFragmentListener(); 139 140 private StrequentContactListFragment.Listener mFavoritesFragmentListener = 141 new StrequentContactListFragmentListener(); 142 143 private boolean mSearchInitiated; 144 145 private ContactListFilterController mContactListFilterController; 146 147 private ContactsUnavailableFragment mContactsUnavailableFragment; 148 private ProviderStatusLoader mProviderStatusLoader; 149 private int mProviderStatus = -1; 150 151 private boolean mOptionsMenuContactsAvailable; 152 153 /** 154 * Showing a list of Contacts. Also used for showing search results in search mode. 155 */ 156 private DefaultContactBrowseListFragment mAllFragment; 157 private StrequentContactListFragment mFavoritesFragment; 158 private StrequentContactListFragment mFrequentFragment; 159 private GroupBrowseListFragment mGroupsFragment; 160 161 private View mFavoritesView; 162 private View mBrowserView; 163 private View mDetailsView; 164 165 private View mAddGroupImageView; 166 167 private ContactDetailLayoutController mContactDetailLayoutController; 168 169 private final Handler mHandler = new Handler(); 170 171 /** 172 * TODO: Use ViewPager so that tabs can be swiped left and right. Figure out how to use the 173 * support library in our app. 174 */ 175 private final TabListener mTabListener = new TabListener() { 176 @Override 177 public void onTabUnselected(Tab tab, FragmentTransaction ft) { 178 hideFragmentOnTabUnselect((TabState) tab.getTag(), ft); 179 } 180 181 @Override 182 public void onTabSelected(Tab tab, FragmentTransaction ft) { 183 final TabState tabState = (TabState) tab.getTag(); 184 setSelectedTab(tabState); 185 showFragmentOnTabSelect(tabState, ft); 186 invalidateOptionsMenu(); 187 } 188 189 @Override 190 public void onTabReselected(Tab tab, FragmentTransaction ft) { 191 } 192 }; 193 194 private enum TabState { 195 FAVORITES, ALL, GROUPS 196 } 197 198 private TabState mSelectedTab; 199 200 public PeopleActivity() { 201 mIntentResolver = new ContactsIntentResolver(this); 202 mContactListFilterController = new ContactListFilterController(this); 203 mContactListFilterController.addListener(this); 204 mProviderStatusLoader = new ProviderStatusLoader(this); 205 } 206 207 public boolean areContactsAvailable() { 208 return mProviderStatus == ProviderStatus.STATUS_NORMAL; 209 } 210 211 @Override 212 public void onAttachFragment(Fragment fragment) { 213 if (fragment instanceof DefaultContactBrowseListFragment) { 214 mAllFragment = (DefaultContactBrowseListFragment)fragment; 215 mAllFragment.setOnContactListActionListener(new ContactBrowserActionListener()); 216 if (!getWindow().hasFeature(Window.FEATURE_ACTION_BAR)) { 217 mAllFragment.setContextMenuAdapter( 218 new ContactBrowseListContextMenuAdapter(mAllFragment)); 219 } 220 } else if (fragment instanceof GroupBrowseListFragment) { 221 mGroupsFragment = (GroupBrowseListFragment) fragment; 222 mGroupsFragment.setListener(new GroupBrowserActionListener()); 223 } else if (fragment instanceof ContactDetailFragment) { 224 mContactDetailFragment = (ContactDetailFragment) fragment; 225 mContactDetailFragment.setListener(mContactDetailFragmentListener); 226 } else if (fragment instanceof ContactDetailUpdatesFragment) { 227 mContactDetailUpdatesFragment = (ContactDetailUpdatesFragment) fragment; 228 } else if (fragment instanceof ContactsUnavailableFragment) { 229 mContactsUnavailableFragment = (ContactsUnavailableFragment)fragment; 230 mContactsUnavailableFragment.setProviderStatusLoader(mProviderStatusLoader); 231 mContactsUnavailableFragment.setOnContactsUnavailableActionListener( 232 new ContactsUnavailableFragmentListener()); 233 } else if (fragment instanceof ContactLoaderFragment) { 234 mContactDetailLoaderFragment = (ContactLoaderFragment) fragment; 235 mContactDetailLoaderFragment.setListener(mContactDetailLoaderFragmentListener); 236 } else if (fragment instanceof GroupDetailFragment) { 237 mGroupDetailFragment = (GroupDetailFragment) fragment; 238 mGroupDetailFragment.setListener(mGroupDetailFragmentListener); 239 mGroupDetailFragment.setQuickContact(PhoneCapabilityTester.isUsingTwoPanes(this)); 240 } else if (fragment instanceof StrequentContactListFragment) { 241 mFavoritesFragment = (StrequentContactListFragment) fragment; 242 mFavoritesFragment.setListener(mFavoritesFragmentListener); 243 mFavoritesFragment.setDisplayType(DisplayType.STARRED_ONLY); 244 mFavoritesFragment.setQuickContact(PhoneCapabilityTester.isUsingTwoPanes(this)); 245 } 246 } 247 248 @Override 249 protected void onCreate(Bundle savedState) { 250 super.onCreate(savedState); 251 252 configureContentView(true, savedState); 253 } 254 255 @Override 256 protected void onNewIntent(Intent intent) { 257 setIntent(intent); 258 configureContentView(false, null); 259 } 260 261 private void configureContentView(boolean createContentView, Bundle savedState) { 262 // Extract relevant information from the intent 263 mRequest = mIntentResolver.resolveIntent(getIntent()); 264 if (!mRequest.isValid()) { 265 setResult(RESULT_CANCELED); 266 finish(); 267 return; 268 } 269 270 Intent redirect = mRequest.getRedirectIntent(); 271 if (redirect != null) { 272 // Need to start a different activity 273 startActivity(redirect); 274 finish(); 275 return; 276 } 277 278 if (createContentView) { 279 setContentView(R.layout.people_activity); 280 281 mFavoritesView = findViewById(R.id.favorites_view); 282 mDetailsView = findViewById(R.id.details_view); 283 mBrowserView = findViewById(R.id.browse_view); 284 285 final FragmentManager fragmentManager = getFragmentManager(); 286 mFavoritesFragment = (StrequentContactListFragment) fragmentManager 287 .findFragmentById(R.id.favorites_fragment); 288 mFrequentFragment = (StrequentContactListFragment) fragmentManager 289 .findFragmentById(R.id.frequent_fragment); 290 mAllFragment = (DefaultContactBrowseListFragment) fragmentManager 291 .findFragmentById(R.id.all_fragment); 292 mGroupsFragment = (GroupBrowseListFragment) fragmentManager 293 .findFragmentById(R.id.groups_fragment); 294 // Hide all tabs (the current tab will later be reshown once a tab is selected) 295 final FragmentTransaction transaction = fragmentManager.beginTransaction(); 296 transaction.hide(mAllFragment); 297 transaction.hide(mGroupsFragment); 298 299 if (mFrequentFragment != null) { 300 mFrequentFragment.setDisplayType(DisplayType.FREQUENT_ONLY); 301 } 302 if (mContactDetailFragment != null) { 303 transaction.hide(mContactDetailFragment); 304 } 305 if (mGroupDetailFragment != null) { 306 transaction.hide(mGroupDetailFragment); 307 } 308 transaction.commit(); 309 } 310 311 if (mRequest.getActionCode() == ContactsRequest.ACTION_VIEW_CONTACT 312 && !PhoneCapabilityTester.isUsingTwoPanes(this)) { 313 redirect = new Intent(this, ContactDetailActivity.class); 314 redirect.setAction(Intent.ACTION_VIEW); 315 redirect.setData(mRequest.getContactUri()); 316 startActivity(redirect); 317 finish(); 318 return; 319 } 320 321 setTitle(mRequest.getActivityTitle()); 322 ActionBar actionBar = getActionBar(); 323 mActionBarAdapter = new ActionBarAdapter(this, this); 324 mActionBarAdapter.onCreate(savedState, mRequest, getActionBar()); 325 326 ViewPager viewPager = (ViewPager) findViewById(R.id.pager); 327 ContactDetailTabCarousel tabCarousel = (ContactDetailTabCarousel) 328 findViewById(R.id.tab_carousel); 329 mContactDetailLayoutController = new ContactDetailLayoutController( 330 getFragmentManager(), viewPager, tabCarousel, 331 mContactDetailFragmentListener); 332 333 if (createContentView) { 334 actionBar.removeAllTabs(); 335 Tab favoritesTab = actionBar.newTab(); 336 favoritesTab.setTag(TabState.FAVORITES); 337 favoritesTab.setText(getString(R.string.contactsFavoritesLabel)); 338 favoritesTab.setTabListener(mTabListener); 339 actionBar.addTab(favoritesTab); 340 341 Tab allTab = actionBar.newTab(); 342 allTab.setTag(TabState.ALL); 343 allTab.setText(getString(R.string.contactsAllLabel)); 344 allTab.setTabListener(mTabListener); 345 actionBar.addTab(allTab); 346 347 Tab groupsTab = actionBar.newTab(); 348 groupsTab.setTag(TabState.GROUPS); 349 groupsTab.setText(getString(R.string.contactsGroupsLabel)); 350 groupsTab.setTabListener(mTabListener); 351 actionBar.addTab(groupsTab); 352 actionBar.setDisplayShowTitleEnabled(true); 353 354 TypedArray a = obtainStyledAttributes(null, R.styleable.ActionBarHomeIcon); 355 boolean showHomeIcon = a.getBoolean(R.styleable.ActionBarHomeIcon_show_home_icon, true); 356 actionBar.setDisplayShowHomeEnabled(showHomeIcon); 357 358 invalidateOptionsMenuIfNeeded(); 359 } 360 361 configureFragments(savedState == null); 362 } 363 364 private void hideFragmentOnTabUnselect(TabState newTabState, FragmentTransaction ft) { 365 switch (newTabState) { 366 case FAVORITES: { 367 ft.hide(mFavoritesFragment); 368 if (mFrequentFragment != null) { 369 ft.hide(mFrequentFragment); 370 } 371 break; 372 } 373 case ALL: { 374 ft.hide(mAllFragment); 375 if (mContactDetailFragment != null) { 376 ft.hide(mContactDetailFragment); 377 } 378 break; 379 } 380 case GROUPS: { 381 ft.hide(mGroupsFragment); 382 if (mGroupDetailFragment != null) { 383 ft.hide(mGroupDetailFragment); 384 } 385 break; 386 } 387 default: { 388 throw new IllegalStateException("Unexpected tab state: " + newTabState); 389 } 390 } 391 } 392 393 private void showFragmentOnTabSelect(TabState newTabState, FragmentTransaction ft) { 394 switch (newTabState) { 395 case FAVORITES: { 396 ft.show(mFavoritesFragment); 397 if (mFrequentFragment != null) { 398 ft.show(mFrequentFragment); 399 } 400 break; 401 } 402 case ALL: { 403 ft.show(mAllFragment); 404 if (mContactDetailFragment != null) { 405 ft.show(mContactDetailFragment); 406 } 407 break; 408 } 409 case GROUPS: { 410 ft.show(mGroupsFragment); 411 if (mGroupDetailFragment != null) { 412 ft.show(mGroupDetailFragment); 413 } 414 break; 415 } 416 default: { 417 throw new IllegalStateException("Unexpected tab state: " + newTabState); 418 } 419 } 420 } 421 422 private void setSelectedTab(TabState tab) { 423 mSelectedTab = tab; 424 425 if (PhoneCapabilityTester.isUsingTwoPanes(this)) { 426 switch (mSelectedTab) { 427 case FAVORITES: 428 mFavoritesView.setVisibility(View.VISIBLE); 429 mBrowserView.setVisibility(View.GONE); 430 mDetailsView.setVisibility(View.GONE); 431 break; 432 case GROUPS: 433 case ALL: 434 mFavoritesView.setVisibility(View.GONE); 435 mBrowserView.setVisibility(View.VISIBLE); 436 mDetailsView.setVisibility(View.VISIBLE); 437 break; 438 } 439 } 440 } 441 442 @Override 443 protected void onPause() { 444 if (mActionBarAdapter != null) { 445 mActionBarAdapter.setListener(null); 446 } 447 448 mOptionsMenuContactsAvailable = false; 449 450 mProviderStatus = -1; 451 mProviderStatusLoader.setProviderStatusListener(null); 452 super.onPause(); 453 } 454 455 @Override 456 protected void onResume() { 457 super.onResume(); 458 if (mActionBarAdapter != null) { 459 mActionBarAdapter.setListener(this); 460 } 461 mProviderStatusLoader.setProviderStatusListener(this); 462 updateFragmentVisibility(); 463 } 464 465 @Override 466 protected void onStart() { 467 mContactListFilterController.onStart(); 468 super.onStart(); 469 } 470 471 private void configureFragments(boolean fromRequest) { 472 if (fromRequest) { 473 ContactListFilter filter = null; 474 int actionCode = mRequest.getActionCode(); 475 switch (actionCode) { 476 case ContactsRequest.ACTION_ALL_CONTACTS: 477 filter = ContactListFilter.createFilterWithType( 478 ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS); 479 break; 480 case ContactsRequest.ACTION_CONTACTS_WITH_PHONES: 481 filter = ContactListFilter.createFilterWithType( 482 ContactListFilter.FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY); 483 break; 484 485 // TODO: handle FREQUENT and STREQUENT according to the spec 486 case ContactsRequest.ACTION_FREQUENT: 487 case ContactsRequest.ACTION_STREQUENT: 488 // For now they are treated the same as STARRED 489 case ContactsRequest.ACTION_STARRED: 490 filter = ContactListFilter.createFilterWithType( 491 ContactListFilter.FILTER_TYPE_STARRED); 492 break; 493 case ContactsRequest.ACTION_VIEW_CONTACT: 494 if (PhoneCapabilityTester.isUsingTwoPanes(this)) { 495 getActionBar().setSelectedNavigationItem(TabState.ALL.ordinal()); 496 } 497 } 498 499 mSearchMode = mRequest.isSearchMode(); 500 if (filter != null) { 501 mContactListFilterController.setContactListFilter(filter, false); 502 mSearchMode = false; 503 } else if (mRequest.getActionCode() == ContactsRequest.ACTION_ALL_CONTACTS) { 504 mContactListFilterController.setContactListFilter( 505 ContactListFilter.createFilterWithType( 506 ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS), false); 507 } 508 509 if (mRequest.getContactUri() != null) { 510 mSearchMode = false; 511 } 512 513 mAllFragment.setContactsRequest(mRequest); 514 configureContactListFragmentForRequest(); 515 516 } else { 517 mSearchMode = mActionBarAdapter.isSearchMode(); 518 } 519 520 configureContactListFragment(); 521 configureGroupListFragment(); 522 523 invalidateOptionsMenuIfNeeded(); 524 } 525 526 @Override 527 public void onContactListFilterChanged() { 528 if (mAllFragment == null || !mAllFragment.isAdded()) { 529 return; 530 } 531 532 mAllFragment.setFilter(mContactListFilterController.getFilter()); 533 534 invalidateOptionsMenuIfNeeded(); 535 } 536 537 private void setupContactDetailFragment(final Uri contactLookupUri) { 538 mContactDetailLoaderFragment.loadUri(contactLookupUri); 539 invalidateOptionsMenuIfNeeded(); 540 } 541 542 private void setupGroupDetailFragment(Uri groupUri) { 543 mGroupDetailFragment.loadGroup(groupUri); 544 invalidateOptionsMenuIfNeeded(); 545 } 546 547 /** 548 * Handler for action bar actions. 549 */ 550 @Override 551 public void onAction(Action action) { 552 switch (action) { 553 case START_SEARCH_MODE: 554 // Checking if multi fragments are being displayed 555 if (PhoneCapabilityTester.isUsingTwoPanes(this)) { 556 mFavoritesView.setVisibility(View.GONE); 557 mBrowserView.setVisibility(View.VISIBLE); 558 mDetailsView.setVisibility(View.VISIBLE); 559 } 560 // Bring the contact list fragment (and detail fragment if applicable) to the front 561 FragmentTransaction ft = getFragmentManager().beginTransaction(); 562 ft.show(mAllFragment); 563 if (mContactDetailFragment != null) ft.show(mContactDetailFragment); 564 ft.commit(); 565 clearSearch(); 566 break; 567 case STOP_SEARCH_MODE: 568 // Refresh the fragments because search mode was using them to display search 569 // results. 570 clearSearch(); 571 572 // If the last selected tab was not the "All contacts" tab, then hide these 573 // fragments because we need to show favorites or groups. 574 if (mSelectedTab != null && !mSelectedTab.equals(TabState.ALL)) { 575 FragmentTransaction transaction = getFragmentManager().beginTransaction(); 576 transaction.hide(mAllFragment); 577 if (mContactDetailFragment != null) transaction.hide(mContactDetailFragment); 578 transaction.commit(); 579 } 580 if (mSelectedTab != null) setSelectedTab(mSelectedTab); 581 break; 582 case CHANGE_SEARCH_QUERY: 583 loadSearch(mActionBarAdapter.getQueryString()); 584 break; 585 default: 586 throw new IllegalStateException("Unkonwn ActionBarAdapter action: " + action); 587 } 588 } 589 590 private void clearSearch() { 591 loadSearch(""); 592 } 593 594 private void loadSearch(String query) { 595 configureFragments(false /* from request */); 596 mAllFragment.setQueryString(query, true); 597 } 598 599 private void configureContactListFragmentForRequest() { 600 Uri contactUri = mRequest.getContactUri(); 601 if (contactUri != null) { 602 mAllFragment.setSelectedContactUri(contactUri); 603 } 604 605 mAllFragment.setSearchMode(mRequest.isSearchMode()); 606 mAllFragment.setQueryString(mRequest.getQueryString(), false); 607 608 if (mRequest.isDirectorySearchEnabled()) { 609 mAllFragment.setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_DEFAULT); 610 } else { 611 mAllFragment.setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_NONE); 612 } 613 614 if (mContactListFilterController.isInitialized()) { 615 mAllFragment.setFilter(mContactListFilterController.getFilter()); 616 } 617 } 618 619 private void configureContactListFragment() { 620 mAllFragment.setSearchMode(mSearchMode); 621 622 final boolean useTwoPane = PhoneCapabilityTester.isUsingTwoPanes(this); 623 mAllFragment.setVisibleScrollbarEnabled(!mSearchMode); 624 mAllFragment.setVerticalScrollbarPosition( 625 useTwoPane 626 ? View.SCROLLBAR_POSITION_LEFT 627 : View.SCROLLBAR_POSITION_RIGHT); 628 mAllFragment.setSelectionVisible(useTwoPane); 629 mAllFragment.setQuickContactEnabled(!useTwoPane); 630 } 631 632 private void configureGroupListFragment() { 633 final boolean useTwoPane = PhoneCapabilityTester.isUsingTwoPanes(this); 634 mGroupsFragment.setVerticalScrollbarPosition( 635 useTwoPane 636 ? View.SCROLLBAR_POSITION_LEFT 637 : View.SCROLLBAR_POSITION_RIGHT); 638 mGroupsFragment.setSelectionVisible(useTwoPane); 639 } 640 641 @Override 642 public void onProviderStatusChange() { 643 updateFragmentVisibility(); 644 } 645 646 private void updateFragmentVisibility() { 647 int providerStatus = mProviderStatusLoader.getProviderStatus(); 648 if (providerStatus == mProviderStatus) { 649 return; 650 } 651 652 mProviderStatus = providerStatus; 653 654 View contactsUnavailableView = findViewById(R.id.contacts_unavailable_view); 655 View mainView = findViewById(R.id.main_view); 656 657 if (mProviderStatus == ProviderStatus.STATUS_NORMAL) { 658 contactsUnavailableView.setVisibility(View.GONE); 659 if (mainView != null) { 660 mainView.setVisibility(View.VISIBLE); 661 } 662 if (mAllFragment != null) { 663 mAllFragment.setEnabled(true); 664 } 665 } else { 666 if (mAllFragment != null) { 667 mAllFragment.setEnabled(false); 668 } 669 if (mContactsUnavailableFragment == null) { 670 mContactsUnavailableFragment = new ContactsUnavailableFragment(); 671 mContactsUnavailableFragment.setProviderStatusLoader(mProviderStatusLoader); 672 mContactsUnavailableFragment.setOnContactsUnavailableActionListener( 673 new ContactsUnavailableFragmentListener()); 674 getFragmentManager().beginTransaction() 675 .replace(R.id.contacts_unavailable_container, mContactsUnavailableFragment) 676 .commit(); 677 } else { 678 mContactsUnavailableFragment.update(); 679 } 680 contactsUnavailableView.setVisibility(View.VISIBLE); 681 if (mainView != null) { 682 mainView.setVisibility(View.INVISIBLE); 683 } 684 } 685 686 invalidateOptionsMenuIfNeeded(); 687 } 688 689 private final class ContactBrowserActionListener implements OnContactBrowserActionListener { 690 691 @Override 692 public void onSelectionChange() { 693 if (PhoneCapabilityTester.isUsingTwoPanes(PeopleActivity.this)) { 694 setupContactDetailFragment(mAllFragment.getSelectedContactUri()); 695 } 696 } 697 698 @Override 699 public void onViewContactAction(Uri contactLookupUri) { 700 if (PhoneCapabilityTester.isUsingTwoPanes(PeopleActivity.this)) { 701 setupContactDetailFragment(contactLookupUri); 702 } else { 703 startActivity(new Intent(Intent.ACTION_VIEW, contactLookupUri)); 704 } 705 } 706 707 @Override 708 public void onCreateNewContactAction() { 709 Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI); 710 Bundle extras = getIntent().getExtras(); 711 if (extras != null) { 712 intent.putExtras(extras); 713 } 714 startActivity(intent); 715 } 716 717 @Override 718 public void onEditContactAction(Uri contactLookupUri) { 719 Intent intent = new Intent(Intent.ACTION_EDIT, contactLookupUri); 720 Bundle extras = getIntent().getExtras(); 721 if (extras != null) { 722 intent.putExtras(extras); 723 } 724 startActivityForResult(intent, SUBACTIVITY_EDIT_CONTACT); 725 } 726 727 @Override 728 public void onAddToFavoritesAction(Uri contactUri) { 729 ContentValues values = new ContentValues(1); 730 values.put(Contacts.STARRED, 1); 731 getContentResolver().update(contactUri, values, null, null); 732 } 733 734 @Override 735 public void onRemoveFromFavoritesAction(Uri contactUri) { 736 ContentValues values = new ContentValues(1); 737 values.put(Contacts.STARRED, 0); 738 getContentResolver().update(contactUri, values, null, null); 739 } 740 741 @Override 742 public void onCallContactAction(Uri contactUri) { 743 PhoneNumberInteraction.startInteractionForPhoneCall(PeopleActivity.this, contactUri); 744 } 745 746 @Override 747 public void onSmsContactAction(Uri contactUri) { 748 PhoneNumberInteraction.startInteractionForTextMessage(PeopleActivity.this, contactUri); 749 } 750 751 @Override 752 public void onDeleteContactAction(Uri contactUri) { 753 ContactDeletionInteraction.start(PeopleActivity.this, contactUri, false); 754 } 755 756 @Override 757 public void onFinishAction() { 758 onBackPressed(); 759 } 760 761 @Override 762 public void onInvalidSelection() { 763 ContactListFilter filter; 764 ContactListFilter currentFilter = mAllFragment.getFilter(); 765 if (currentFilter != null 766 && currentFilter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) { 767 filter = ContactListFilter.createFilterWithType( 768 ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS); 769 mAllFragment.setFilter(filter); 770 } else { 771 filter = ContactListFilter.createFilterWithType( 772 ContactListFilter.FILTER_TYPE_SINGLE_CONTACT); 773 mAllFragment.setFilter(filter, false); 774 } 775 mContactListFilterController.setContactListFilter(filter, true); 776 } 777 } 778 779 private class ContactDetailLoaderFragmentListener implements ContactLoaderFragmentListener { 780 @Override 781 public void onContactNotFound() { 782 // Nothing needs to be done here 783 } 784 785 @Override 786 public void onDetailsLoaded(final ContactLoader.Result result) { 787 if (result == null) { 788 return; 789 } 790 // Since {@link FragmentTransaction}s cannot be done in the onLoadFinished() of the 791 // {@link LoaderCallbacks}, then post this {@link Runnable} to the {@link Handler} 792 // on the main thread to execute later. 793 mHandler.post(new Runnable() { 794 @Override 795 public void run() { 796 if (!mContactDetailLayoutController.isInitialized()) { 797 mContactDetailLayoutController.setContactDetailFragment( 798 mContactDetailFragment); 799 mContactDetailLayoutController.setContactDetailUpdatesFragment( 800 mContactDetailUpdatesFragment); 801 mContactDetailLayoutController.initialize(); 802 } 803 mContactDetailLayoutController.setContactData(result); 804 } 805 }); 806 } 807 808 @Override 809 public void onEditRequested(Uri contactLookupUri) { 810 startActivityForResult( 811 new Intent(Intent.ACTION_EDIT, contactLookupUri), SUBACTIVITY_EDIT_CONTACT); 812 } 813 814 @Override 815 public void onDeleteRequested(Uri contactUri) { 816 ContactDeletionInteraction.start(PeopleActivity.this, contactUri, false); 817 } 818 } 819 820 public class ContactDetailFragmentListener implements ContactDetailFragment.Listener { 821 @Override 822 public void onItemClicked(Intent intent) { 823 try { 824 startActivity(intent); 825 } catch (ActivityNotFoundException e) { 826 Log.e(TAG, "No activity found for intent: " + intent); 827 } 828 } 829 830 @Override 831 public void onCreateRawContactRequested(ArrayList<ContentValues> values, Account account) { 832 Toast.makeText(PeopleActivity.this, R.string.toast_making_personal_copy, 833 Toast.LENGTH_LONG).show(); 834 Intent serviceIntent = ContactSaveService.createNewRawContactIntent( 835 PeopleActivity.this, values, account, 836 PeopleActivity.class, Intent.ACTION_VIEW); 837 startService(serviceIntent); 838 } 839 } 840 841 private class ContactsUnavailableFragmentListener 842 implements OnContactsUnavailableActionListener { 843 844 @Override 845 public void onCreateNewContactAction() { 846 startActivity(new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI)); 847 } 848 849 @Override 850 public void onAddAccountAction() { 851 Intent intent = new Intent(Settings.ACTION_ADD_ACCOUNT); 852 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 853 intent.putExtra(Settings.EXTRA_AUTHORITIES, 854 new String[] { ContactsContract.AUTHORITY }); 855 startActivity(intent); 856 } 857 858 @Override 859 public void onImportContactsFromFileAction() { 860 AccountSelectionUtil.doImportFromSdCard(PeopleActivity.this, null); 861 } 862 863 @Override 864 public void onFreeInternalStorageAction() { 865 startActivity(new Intent(Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS)); 866 } 867 } 868 869 private final class StrequentContactListFragmentListener 870 implements StrequentContactListFragment.Listener { 871 @Override 872 public void onContactSelected(Uri contactUri) { 873 if (PhoneCapabilityTester.isUsingTwoPanes(PeopleActivity.this)) { 874 setupContactDetailFragment(contactUri); 875 } else { 876 startActivity(new Intent(Intent.ACTION_VIEW, contactUri)); 877 } 878 } 879 } 880 881 private final class GroupBrowserActionListener implements OnGroupBrowserActionListener { 882 883 @Override 884 public void onViewGroupAction(Uri groupUri) { 885 if (PhoneCapabilityTester.isUsingTwoPanes(PeopleActivity.this)) { 886 setupGroupDetailFragment(groupUri); 887 } else { 888 Intent intent = new Intent(PeopleActivity.this, GroupDetailActivity.class); 889 intent.setData(groupUri); 890 startActivity(intent); 891 } 892 } 893 } 894 895 private class GroupDetailFragmentListener implements GroupDetailFragment.Listener { 896 @Override 897 public void onGroupSizeUpdated(String size) { 898 // Nothing needs to be done here because the size will be displayed in the detail 899 // fragment 900 } 901 902 @Override 903 public void onGroupTitleUpdated(String title) { 904 // Nothing needs to be done here because the title will be displayed in the detail 905 // fragment 906 } 907 908 @Override 909 public void onEditRequested(Uri groupUri) { 910 final Intent intent = new Intent(PeopleActivity.this, GroupEditorActivity.class); 911 intent.setData(groupUri); 912 intent.setAction(Intent.ACTION_EDIT); 913 startActivityForResult(intent, SUBACTIVITY_EDIT_GROUP); 914 } 915 } 916 917 public void startActivityAndForwardResult(final Intent intent) { 918 intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); 919 920 // Forward extras to the new activity 921 Bundle extras = getIntent().getExtras(); 922 if (extras != null) { 923 intent.putExtras(extras); 924 } 925 startActivity(intent); 926 finish(); 927 } 928 929 @Override 930 public boolean onCreatePanelMenu(int featureId, Menu menu) { 931 // No menu if contacts are unavailable 932 if (!areContactsAvailable()) { 933 return false; 934 } 935 936 return super.onCreatePanelMenu(featureId, menu); 937 } 938 939 @Override 940 public boolean onPreparePanel(int featureId, View view, Menu menu) { 941 // No menu if contacts are unavailable 942 if (!areContactsAvailable()) { 943 return false; 944 } 945 946 return super.onPreparePanel(featureId, view, menu); 947 } 948 949 @Override 950 public boolean onCreateOptionsMenu(Menu menu) { 951 if (!areContactsAvailable()) { 952 return false; 953 } 954 super.onCreateOptionsMenu(menu); 955 956 MenuInflater inflater = getMenuInflater(); 957 inflater.inflate(R.menu.actions, menu); 958 959 // On narrow screens we specify a NEW group button in the {@link ActionBar}, so that 960 // it can be in the overflow menu. On wide screens, we use a custom view because we need 961 // its location for anchoring the account-selector popup. 962 final MenuItem addGroup = menu.findItem(R.id.menu_custom_add_group); 963 if (addGroup != null) { 964 mAddGroupImageView = getLayoutInflater().inflate( 965 R.layout.add_group_menu_item, null, false); 966 View item = mAddGroupImageView.findViewById(R.id.menu_item); 967 item.setOnClickListener(new OnClickListener() { 968 @Override 969 public void onClick(View v) { 970 createNewGroupWithAccountDisambiguation(); 971 } 972 }); 973 addGroup.setActionView(mAddGroupImageView); 974 } 975 return true; 976 } 977 978 private void invalidateOptionsMenuIfNeeded() { 979 if (isOptionsMenuChanged()) { 980 invalidateOptionsMenu(); 981 } 982 } 983 984 public boolean isOptionsMenuChanged() { 985 if (mOptionsMenuContactsAvailable != areContactsAvailable()) { 986 return true; 987 } 988 989 if (mAllFragment != null && mAllFragment.isOptionsMenuChanged()) { 990 return true; 991 } 992 993 if (mContactDetailLoaderFragment != null && 994 mContactDetailLoaderFragment.isOptionsMenuChanged()) { 995 return true; 996 } 997 998 if (mGroupDetailFragment != null && mGroupDetailFragment.isOptionsMenuChanged()) { 999 return true; 1000 } 1001 1002 return false; 1003 } 1004 1005 @Override 1006 public boolean onPrepareOptionsMenu(Menu menu) { 1007 mOptionsMenuContactsAvailable = areContactsAvailable(); 1008 if (!mOptionsMenuContactsAvailable) { 1009 return false; 1010 } 1011 1012 final MenuItem searchMenu = menu.findItem(R.id.menu_search); 1013 final MenuItem addContactMenu = menu.findItem(R.id.menu_add_contact); 1014 MenuItem addGroupMenu = menu.findItem(R.id.menu_add_group); 1015 if (addGroupMenu == null) { 1016 addGroupMenu = menu.findItem(R.id.menu_custom_add_group); 1017 } 1018 1019 if (mActionBarAdapter.isSearchMode()) { 1020 addContactMenu.setVisible(false); 1021 addGroupMenu.setVisible(false); 1022 if (searchMenu != null) { 1023 searchMenu.setVisible(false); // Don't show the search menu in search mode. 1024 } 1025 } else { 1026 switch (mSelectedTab) { 1027 case FAVORITES: 1028 // TODO: Fall through until we determine what the menu items should be for 1029 // this tab 1030 case ALL: 1031 addContactMenu.setVisible(true); 1032 addGroupMenu.setVisible(false); 1033 break; 1034 case GROUPS: 1035 addContactMenu.setVisible(false); 1036 addGroupMenu.setVisible(true); 1037 break; 1038 } 1039 } 1040 1041 MenuItem settings = menu.findItem(R.id.menu_settings); 1042 if (settings != null) { 1043 settings.setVisible(!ContactsPreferenceActivity.isEmpty(this)); 1044 } 1045 1046 return true; 1047 } 1048 1049 @Override 1050 public boolean onOptionsItemSelected(MenuItem item) { 1051 switch (item.getItemId()) { 1052 case R.id.menu_settings: { 1053 final Intent intent = new Intent(this, ContactsPreferenceActivity.class); 1054 startActivity(intent); 1055 return true; 1056 } 1057 case R.id.menu_contacts_filter: { 1058 final Intent intent = new Intent(this, AccountFilterActivity.class); 1059 startActivityForResult(intent, SUBACTIVITY_ACCOUNT_FILTER); 1060 return true; 1061 } 1062 case R.id.menu_search: { 1063 onSearchRequested(); 1064 return true; 1065 } 1066 case R.id.menu_add_contact: { 1067 final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI); 1068 startActivityForResult(intent, SUBACTIVITY_NEW_CONTACT); 1069 return true; 1070 } 1071 case R.id.menu_add_group: { 1072 createNewGroupWithAccountDisambiguation(); 1073 return true; 1074 } 1075 case R.id.menu_import_export: { 1076 ImportExportDialogFragment.show(getFragmentManager()); 1077 return true; 1078 } 1079 case R.id.menu_accounts: { 1080 final Intent intent = new Intent(Settings.ACTION_SYNC_SETTINGS); 1081 intent.putExtra(Settings.EXTRA_AUTHORITIES, new String[] { 1082 ContactsContract.AUTHORITY 1083 }); 1084 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 1085 startActivity(intent); 1086 return true; 1087 } 1088 } 1089 return false; 1090 } 1091 1092 private void createNewGroupWithAccountDisambiguation() { 1093 final ArrayList<Account> accounts = 1094 AccountTypeManager.getInstance(this).getAccounts(true); 1095 if (accounts.size() <= 1 || mAddGroupImageView == null) { 1096 // No account to choose or no control to anchor the popup-menu to 1097 // ==> just go straight to the editor which will disambig if necessary 1098 final Intent intent = new Intent(this, GroupEditorActivity.class); 1099 intent.setAction(Intent.ACTION_INSERT); 1100 startActivityForResult(intent, SUBACTIVITY_NEW_GROUP); 1101 return; 1102 } 1103 1104 final ListPopupWindow popup = new ListPopupWindow(this, null); 1105 popup.setWidth(getResources().getDimensionPixelSize(R.dimen.account_selector_popup_width)); 1106 popup.setAnchorView(mAddGroupImageView); 1107 // Create a list adapter with all writeable accounts (assume that the writeable accounts all 1108 // allow group creation). 1109 final AccountsListAdapter adapter = new AccountsListAdapter(this, true); 1110 popup.setAdapter(adapter); 1111 popup.setOnItemClickListener(new OnItemClickListener() { 1112 @Override 1113 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 1114 popup.dismiss(); 1115 final Intent intent = new Intent(PeopleActivity.this, GroupEditorActivity.class); 1116 intent.setAction(Intent.ACTION_INSERT); 1117 intent.putExtra(Intents.Insert.ACCOUNT, adapter.getItem(position)); 1118 startActivityForResult(intent, SUBACTIVITY_NEW_GROUP); 1119 } 1120 }); 1121 popup.setModal(true); 1122 popup.show(); 1123 } 1124 1125 @Override 1126 public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData, 1127 boolean globalSearch) { 1128 if (mAllFragment != null && mAllFragment.isAdded() && !globalSearch) { 1129 mAllFragment.startSearch(initialQuery); 1130 } else { 1131 super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch); 1132 } 1133 } 1134 1135 @Override 1136 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 1137 switch (requestCode) { 1138 case SUBACTIVITY_ACCOUNT_FILTER: { 1139 if (resultCode == Activity.RESULT_OK) { 1140 ContactListFilter filter = (ContactListFilter) data.getParcelableExtra( 1141 AccountFilterActivity.KEY_EXTRA_CONTACT_LIST_FILTER); 1142 if (filter == null) { 1143 return; 1144 } 1145 // If this is a custom filter, launch the activity to customize the display list 1146 if (filter.filterType == ContactListFilter.FILTER_TYPE_CUSTOM) { 1147 final Intent intent = new Intent(this, 1148 CustomContactListFilterActivity.class); 1149 startActivityForResult(intent, SUBACTIVITY_CUSTOMIZE_FILTER); 1150 } else { 1151 mContactListFilterController.setContactListFilter(filter, true); 1152 } 1153 } 1154 break; 1155 } 1156 case SUBACTIVITY_CUSTOMIZE_FILTER: { 1157 if (resultCode == Activity.RESULT_OK) { 1158 mContactListFilterController.selectCustomFilter(); 1159 } 1160 break; 1161 } 1162 case SUBACTIVITY_EDIT_CONTACT: 1163 case SUBACTIVITY_NEW_CONTACT: { 1164 if (resultCode == RESULT_OK && PhoneCapabilityTester.isUsingTwoPanes(this)) { 1165 mRequest.setActionCode(ContactsRequest.ACTION_VIEW_CONTACT); 1166 mAllFragment.reloadDataAndSetSelectedUri(data.getData()); 1167 } 1168 break; 1169 } 1170 1171 case SUBACTIVITY_NEW_GROUP: 1172 case SUBACTIVITY_EDIT_GROUP: { 1173 if (resultCode == RESULT_OK && PhoneCapabilityTester.isUsingTwoPanes(this)) { 1174 mRequest.setActionCode(ContactsRequest.ACTION_GROUP); 1175 mGroupsFragment.setSelectedUri(data.getData()); 1176 } 1177 break; 1178 } 1179 1180 // TODO: Using the new startActivityWithResultFromFragment API this should not be needed 1181 // anymore 1182 case ContactEntryListFragment.ACTIVITY_REQUEST_CODE_PICKER: 1183 if (resultCode == RESULT_OK) { 1184 mAllFragment.onPickerResult(data); 1185 } 1186 1187// TODO fix or remove multipicker code 1188// else if (resultCode == RESULT_CANCELED && mMode == MODE_PICK_MULTIPLE_PHONES) { 1189// // Finish the activity if the sub activity was canceled as back key is used 1190// // to confirm user selection in MODE_PICK_MULTIPLE_PHONES. 1191// finish(); 1192// } 1193// break; 1194 } 1195 } 1196 1197 @Override 1198 public boolean onContextItemSelected(MenuItem item) { 1199 ContextMenuAdapter menuAdapter = mAllFragment.getContextMenuAdapter(); 1200 if (menuAdapter != null) { 1201 return menuAdapter.onContextItemSelected(item); 1202 } 1203 1204 return super.onContextItemSelected(item); 1205 } 1206 1207 @Override 1208 public boolean onKeyDown(int keyCode, KeyEvent event) { 1209 // TODO move to the fragment 1210 switch (keyCode) { 1211// case KeyEvent.KEYCODE_CALL: { 1212// if (callSelection()) { 1213// return true; 1214// } 1215// break; 1216// } 1217 1218 case KeyEvent.KEYCODE_DEL: { 1219 if (deleteSelection()) { 1220 return true; 1221 } 1222 break; 1223 } 1224 default: { 1225 // Bring up the search UI if the user starts typing 1226 final int unicodeChar = event.getUnicodeChar(); 1227 if (unicodeChar != 0 && !Character.isWhitespace(unicodeChar)) { 1228 String query = new String(new int[]{ unicodeChar }, 0, 1); 1229 if (!mActionBarAdapter.isSearchMode()) { 1230 mActionBarAdapter.setQueryString(query); 1231 mActionBarAdapter.setSearchMode(true); 1232 return true; 1233 } else if (!mRequest.isSearchMode()) { 1234 if (!mSearchInitiated) { 1235 mSearchInitiated = true; 1236 startSearch(query, false, null, false); 1237 return true; 1238 } 1239 } 1240 } 1241 } 1242 } 1243 1244 return super.onKeyDown(keyCode, event); 1245 } 1246 1247 @Override 1248 public void onBackPressed() { 1249 if (mSearchMode && mActionBarAdapter != null) { 1250 mActionBarAdapter.setSearchMode(false); 1251 } else { 1252 super.onBackPressed(); 1253 } 1254 } 1255 1256 private boolean deleteSelection() { 1257 // TODO move to the fragment 1258// if (mActionCode == ContactsRequest.ACTION_DEFAULT) { 1259// final int position = mListView.getSelectedItemPosition(); 1260// if (position != ListView.INVALID_POSITION) { 1261// Uri contactUri = getContactUri(position); 1262// if (contactUri != null) { 1263// doContactDelete(contactUri); 1264// return true; 1265// } 1266// } 1267// } 1268 return false; 1269 } 1270 1271 @Override 1272 protected void onSaveInstanceState(Bundle outState) { 1273 super.onSaveInstanceState(outState); 1274 outState.putBoolean(KEY_SEARCH_MODE, mSearchMode); 1275 if (mActionBarAdapter != null) { 1276 mActionBarAdapter.onSaveInstanceState(outState); 1277 } 1278 if (mContactDetailLayoutController != null) { 1279 mContactDetailLayoutController.onSaveInstanceState(outState); 1280 } 1281 } 1282 1283 @Override 1284 protected void onRestoreInstanceState(Bundle inState) { 1285 super.onRestoreInstanceState(inState); 1286 mSearchMode = inState.getBoolean(KEY_SEARCH_MODE); 1287 if (mActionBarAdapter != null) { 1288 mActionBarAdapter.onRestoreInstanceState(inState); 1289 } 1290 if (mContactDetailLayoutController != null) { 1291 mContactDetailLayoutController.onRestoreInstanceState(inState); 1292 } 1293 } 1294 1295 @Override 1296 public DialogManager getDialogManager() { 1297 return mDialogManager; 1298 } 1299 1300 // Visible for testing 1301 public ContactBrowseListFragment getListFragment() { 1302 return mAllFragment; 1303 } 1304 1305 // Visible for testing 1306 public ContactDetailFragment getDetailFragment() { 1307 return mContactDetailFragment; 1308 } 1309} 1310