PeopleActivity.java revision 79700889dad553dcde9e22a2fd23df768f68080f
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.group.GroupBrowseListFragment; 25import com.android.contacts.group.GroupBrowseListFragment.OnGroupBrowserActionListener; 26import com.android.contacts.group.GroupDetailFragment; 27import com.android.contacts.interactions.ContactDeletionInteraction; 28import com.android.contacts.interactions.ImportExportDialogFragment; 29import com.android.contacts.interactions.PhoneNumberInteraction; 30import com.android.contacts.list.ContactBrowseListContextMenuAdapter; 31import com.android.contacts.list.ContactBrowseListFragment; 32import com.android.contacts.list.ContactEntryListFragment; 33import com.android.contacts.list.ContactListFilter; 34import com.android.contacts.list.ContactListFilterController; 35import com.android.contacts.list.ContactsIntentResolver; 36import com.android.contacts.list.ContactsRequest; 37import com.android.contacts.list.ContactsUnavailableFragment; 38import com.android.contacts.list.CustomContactListFilterActivity; 39import com.android.contacts.list.DefaultContactBrowseListFragment; 40import com.android.contacts.list.DirectoryListLoader; 41import com.android.contacts.list.OnContactBrowserActionListener; 42import com.android.contacts.list.OnContactsUnavailableActionListener; 43import com.android.contacts.list.ProviderStatusLoader; 44import com.android.contacts.list.ProviderStatusLoader.ProviderStatusListener; 45import com.android.contacts.list.StrequentContactListFragment; 46import com.android.contacts.preference.ContactsPreferenceActivity; 47import com.android.contacts.util.AccountSelectionUtil; 48import com.android.contacts.util.DialogManager; 49import com.android.contacts.widget.ContextMenuAdapter; 50 51import android.accounts.Account; 52import android.app.ActionBar; 53import android.app.ActionBar.Tab; 54import android.app.ActionBar.TabListener; 55import android.app.Activity; 56import android.app.Dialog; 57import android.app.Fragment; 58import android.app.FragmentManager; 59import android.app.FragmentTransaction; 60import android.content.ActivityNotFoundException; 61import android.content.ContentValues; 62import android.content.Intent; 63import android.content.res.TypedArray; 64import android.net.Uri; 65import android.os.Bundle; 66import android.provider.ContactsContract; 67import android.provider.ContactsContract.Contacts; 68import android.provider.ContactsContract.ProviderStatus; 69import android.provider.Settings; 70import android.util.Log; 71import android.view.KeyEvent; 72import android.view.Menu; 73import android.view.MenuInflater; 74import android.view.MenuItem; 75import android.view.View; 76import android.view.Window; 77import android.widget.SearchView; 78import android.widget.Toast; 79 80import java.util.ArrayList; 81 82/** 83 * Displays a list to browse contacts. For xlarge screens, this also displays a detail-pane on 84 * the right. 85 */ 86public class PeopleActivity extends ContactsActivity 87 implements View.OnCreateContextMenuListener, ActionBarAdapter.Listener, 88 DialogManager.DialogShowingViewActivity, 89 ContactListFilterController.ContactListFilterListener, ProviderStatusListener { 90 91 private static final String TAG = "PeopleActivity"; 92 93 private static final int SUBACTIVITY_NEW_CONTACT = 2; 94 private static final int SUBACTIVITY_EDIT_CONTACT = 3; 95 private static final int SUBACTIVITY_CUSTOMIZE_FILTER = 4; 96 97 private static final String KEY_SEARCH_MODE = "searchMode"; 98 99 private DialogManager mDialogManager = new DialogManager(this); 100 101 private ContactsIntentResolver mIntentResolver; 102 private ContactsRequest mRequest; 103 104 private ActionBarAdapter mActionBarAdapter; 105 106 private boolean mSearchMode; 107 108 private ContactBrowseListFragment mListFragment; 109 110 /** 111 * Whether we have a right-side contact or group detail pane for displaying info on that 112 * contact or group while browsing. Generally means "this is a tablet". 113 */ 114 private boolean mContentPaneDisplayed; 115 116 private ContactDetailFragment mContactDetailFragment; 117 private ContactDetailFragmentListener mContactDetailFragmentListener = 118 new ContactDetailFragmentListener(); 119 120 private GroupDetailFragment mGroupDetailFragment; 121 122 private PhoneNumberInteraction mPhoneNumberCallInteraction; 123 private PhoneNumberInteraction mSendTextMessageInteraction; 124 125 private boolean mSearchInitiated; 126 127 private ContactListFilterController mContactListFilterController; 128 129 private ContactsUnavailableFragment mContactsUnavailableFragment; 130 private ProviderStatusLoader mProviderStatusLoader; 131 private int mProviderStatus = -1; 132 133 private boolean mOptionsMenuContactsAvailable; 134 135 private DefaultContactBrowseListFragment mContactsFragment; 136 private StrequentContactListFragment mFavoritesFragment; 137 private GroupBrowseListFragment mGroupsFragment; 138 139 private enum TabState { 140 FAVORITES, CONTACTS, GROUPS 141 } 142 143 private TabState mSelectedTab; 144 145 public PeopleActivity() { 146 mIntentResolver = new ContactsIntentResolver(this); 147 // TODO: Get rid of the ContactListFilterController class because there aren't any 148 // dropdown filters anymore. Just store the selected filter as a member variable. 149 mContactListFilterController = new ContactListFilterController(this); 150 mContactListFilterController.addListener(this); 151 mProviderStatusLoader = new ProviderStatusLoader(this); 152 } 153 154 public boolean areContactsAvailable() { 155 return mProviderStatus == ProviderStatus.STATUS_NORMAL; 156 } 157 158 @Override 159 public void onAttachFragment(Fragment fragment) { 160 if (fragment instanceof ContactBrowseListFragment) { 161 mListFragment = (ContactBrowseListFragment)fragment; 162 mListFragment.setOnContactListActionListener(new ContactBrowserActionListener()); 163 if (!getWindow().hasFeature(Window.FEATURE_ACTION_BAR)) { 164 mListFragment.setContextMenuAdapter( 165 new ContactBrowseListContextMenuAdapter(mListFragment)); 166 } 167 } else if (fragment instanceof GroupBrowseListFragment) { 168 mGroupsFragment = (GroupBrowseListFragment) fragment; 169 mGroupsFragment.setListener(new GroupBrowserActionListener()); 170 } else if (fragment instanceof ContactDetailFragment) { 171 mContactDetailFragment = (ContactDetailFragment) fragment; 172 mContactDetailFragment.setListener(mContactDetailFragmentListener); 173 mContentPaneDisplayed = true; 174 } else if (fragment instanceof ContactsUnavailableFragment) { 175 mContactsUnavailableFragment = (ContactsUnavailableFragment)fragment; 176 mContactsUnavailableFragment.setProviderStatusLoader(mProviderStatusLoader); 177 mContactsUnavailableFragment.setOnContactsUnavailableActionListener( 178 new ContactsUnavailableFragmentListener()); 179 } else if (fragment instanceof GroupDetailFragment) { 180 mGroupDetailFragment = (GroupDetailFragment) fragment; 181 mContentPaneDisplayed = true; 182 } 183 } 184 185 @Override 186 protected void onCreate(Bundle savedState) { 187 super.onCreate(savedState); 188 189 configureContentView(true, savedState); 190 } 191 192 @Override 193 protected void onNewIntent(Intent intent) { 194 setIntent(intent); 195 configureContentView(false, null); 196 } 197 198 private void configureContentView(boolean createContentView, Bundle savedState) { 199 // Extract relevant information from the intent 200 mRequest = mIntentResolver.resolveIntent(getIntent()); 201 if (!mRequest.isValid()) { 202 setResult(RESULT_CANCELED); 203 finish(); 204 return; 205 } 206 207 Intent redirect = mRequest.getRedirectIntent(); 208 if (redirect != null) { 209 // Need to start a different activity 210 startActivity(redirect); 211 finish(); 212 return; 213 } 214 215 if (createContentView) { 216 setContentView(R.layout.people_activity); 217 218 final FragmentManager fragmentManager = getFragmentManager(); 219 mFavoritesFragment = (StrequentContactListFragment) fragmentManager 220 .findFragmentById(R.id.favorites_fragment); 221 mContactsFragment = (DefaultContactBrowseListFragment) fragmentManager 222 .findFragmentById(R.id.contacts_fragment); 223 mGroupsFragment = (GroupBrowseListFragment) fragmentManager 224 .findFragmentById(R.id.groups_fragment); 225 226 // Hide all tabs (the current tab will later be reshown once a tab is selected) 227 final FragmentTransaction transaction = fragmentManager.beginTransaction(); 228 transaction.hide(mFavoritesFragment); 229 transaction.hide(mContactsFragment); 230 transaction.hide(mGroupsFragment); 231 if (mContactDetailFragment != null) { 232 transaction.hide(mContactDetailFragment); 233 } 234 if (mGroupDetailFragment != null) { 235 transaction.hide(mGroupDetailFragment); 236 } 237 transaction.commit(); 238 } 239 240 if (mRequest.getActionCode() == ContactsRequest.ACTION_VIEW_CONTACT 241 && !mContentPaneDisplayed) { 242 redirect = new Intent(this, ContactDetailActivity.class); 243 redirect.setAction(Intent.ACTION_VIEW); 244 redirect.setData(mRequest.getContactUri()); 245 startActivity(redirect); 246 finish(); 247 return; 248 } 249 250 setTitle(mRequest.getActivityTitle()); 251 ActionBar actionBar = getActionBar(); 252 mActionBarAdapter = new ActionBarAdapter(this, this); 253 mActionBarAdapter.onCreate(savedState, mRequest, getActionBar(), !mContentPaneDisplayed); 254 mActionBarAdapter.setContactListFilterController(mContactListFilterController); 255 256 if (createContentView) { 257 actionBar.removeAllTabs(); 258 Tab favoritesTab = actionBar.newTab(); 259 favoritesTab.setText(getString(R.string.strequentList)); 260 favoritesTab.setTabListener(new TabChangeListener(mFavoritesFragment, 261 mContactDetailFragment, TabState.FAVORITES)); 262 actionBar.addTab(favoritesTab); 263 264 Tab peopleTab = actionBar.newTab(); 265 peopleTab.setText(getString(R.string.people)); 266 peopleTab.setTabListener(new TabChangeListener(mContactsFragment, 267 mContactDetailFragment, TabState.CONTACTS)); 268 actionBar.addTab(peopleTab); 269 270 Tab groupsTab = actionBar.newTab(); 271 groupsTab.setText(getString(R.string.contactsGroupsLabel)); 272 groupsTab.setTabListener(new TabChangeListener(mGroupsFragment, 273 mGroupDetailFragment, TabState.GROUPS)); 274 actionBar.addTab(groupsTab); 275 actionBar.setDisplayShowTitleEnabled(true); 276 277 TypedArray a = obtainStyledAttributes(null, R.styleable.ActionBarHomeIcon); 278 boolean showHomeIcon = a.getBoolean(R.styleable.ActionBarHomeIcon_show_home_icon, true); 279 actionBar.setDisplayShowHomeEnabled(showHomeIcon); 280 281 invalidateOptionsMenuIfNeeded(); 282 } 283 284 configureFragments(savedState == null); 285 } 286 287 /** 288 * Tab change listener that is instantiated once for each tab. Handles showing/hiding fragments. 289 * TODO: Use ViewPager so that tabs can be swiped left and right. Figure out how to use the 290 * support library in our app. 291 */ 292 private class TabChangeListener implements TabListener { 293 private final Fragment mBrowseListFragment; 294 295 /** 296 * Right pane fragment that is present on larger screen sizes (can be 297 * null for smaller screen sizes). 298 */ 299 private final Fragment mDetailFragment; 300 private final TabState mTabState; 301 302 public TabChangeListener(Fragment listFragment, Fragment detailFragment, TabState state) { 303 mBrowseListFragment = listFragment; 304 mDetailFragment = detailFragment; 305 mTabState = state; 306 } 307 308 @Override 309 public void onTabUnselected(Tab tab, FragmentTransaction ft) { 310 ft.hide(mBrowseListFragment); 311 if (mDetailFragment != null) { 312 ft.hide(mDetailFragment); 313 } 314 } 315 316 @Override 317 public void onTabSelected(Tab tab, FragmentTransaction ft) { 318 ft.show(mBrowseListFragment); 319 if (mDetailFragment != null) { 320 ft.show(mDetailFragment); 321 } 322 setSelectedTab(mTabState); 323 invalidateOptionsMenu(); 324 } 325 326 @Override 327 public void onTabReselected(Tab tab, FragmentTransaction ft) { 328 } 329 } 330 331 private void setSelectedTab(TabState tab) { 332 mSelectedTab = tab; 333 } 334 335 @Override 336 protected void onPause() { 337 if (mActionBarAdapter != null) { 338 mActionBarAdapter.setListener(null); 339 } 340 341 mOptionsMenuContactsAvailable = false; 342 343 mProviderStatus = -1; 344 mProviderStatusLoader.setProviderStatusListener(null); 345 super.onPause(); 346 } 347 348 @Override 349 protected void onResume() { 350 super.onResume(); 351 if (mActionBarAdapter != null) { 352 mActionBarAdapter.setListener(this); 353 } 354 mProviderStatusLoader.setProviderStatusListener(this); 355 updateFragmentVisibility(); 356 } 357 358 @Override 359 protected void onStart() { 360 mContactListFilterController.onStart(); 361 super.onStart(); 362 } 363 364 private void configureFragments(boolean fromRequest) { 365 if (fromRequest) { 366 ContactListFilter filter = null; 367 int actionCode = mRequest.getActionCode(); 368 switch (actionCode) { 369 case ContactsRequest.ACTION_ALL_CONTACTS: 370 filter = ContactListFilter.createFilterWithType( 371 ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS); 372 break; 373 case ContactsRequest.ACTION_CONTACTS_WITH_PHONES: 374 filter = ContactListFilter.createFilterWithType( 375 ContactListFilter.FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY); 376 break; 377 378 // TODO: handle FREQUENT and STREQUENT according to the spec 379 case ContactsRequest.ACTION_FREQUENT: 380 case ContactsRequest.ACTION_STREQUENT: 381 // For now they are treated the same as STARRED 382 case ContactsRequest.ACTION_STARRED: 383 filter = ContactListFilter.createFilterWithType( 384 ContactListFilter.FILTER_TYPE_STARRED); 385 break; 386 } 387 388 mSearchMode = mRequest.isSearchMode(); 389 if (filter != null) { 390 mContactListFilterController.setContactListFilter(filter, false); 391 mSearchMode = false; 392 } else if (mRequest.getActionCode() == ContactsRequest.ACTION_ALL_CONTACTS) { 393 mContactListFilterController.setContactListFilter( 394 ContactListFilter.createFilterWithType( 395 ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS), false); 396 } 397 398 if (mRequest.getContactUri() != null) { 399 mSearchMode = false; 400 } 401 402 mListFragment.setContactsRequest(mRequest); 403 configureContactListFragmentForRequest(); 404 405 } else { 406 mSearchMode = mActionBarAdapter.isSearchMode(); 407 } 408 409 configureContactListFragment(); 410 configureGroupListFragment(); 411 412 invalidateOptionsMenuIfNeeded(); 413 } 414 415 @Override 416 public void onContactListFiltersLoaded() { 417 if (mListFragment == null || !mListFragment.isAdded()) { 418 return; 419 } 420 421 mListFragment.setFilter(mContactListFilterController.getFilter()); 422 423 invalidateOptionsMenuIfNeeded(); 424 } 425 426 @Override 427 public void onContactListFilterChanged() { 428 if (mListFragment == null || !mListFragment.isAdded()) { 429 return; 430 } 431 432 mListFragment.setFilter(mContactListFilterController.getFilter()); 433 434 invalidateOptionsMenuIfNeeded(); 435 } 436 437 @Override 438 public void onContactListFilterCustomizationRequest() { 439 startActivityForResult(new Intent(this, CustomContactListFilterActivity.class), 440 SUBACTIVITY_CUSTOMIZE_FILTER); 441 } 442 443 private void setupContactDetailFragment(final Uri contactLookupUri) { 444 mContactDetailFragment.loadUri(contactLookupUri); 445 invalidateOptionsMenuIfNeeded(); 446 } 447 448 private void setupGroupDetailFragment(Uri groupUri) { 449 mGroupDetailFragment.loadGroup(groupUri); 450 invalidateOptionsMenuIfNeeded(); 451 } 452 453 /** 454 * Handler for action bar actions. 455 */ 456 @Override 457 public void onAction(Action action) { 458 switch (action) { 459 case START_SEARCH_MODE: 460 // Bring the contact list fragment (and detail fragment if applicable) to the front 461 FragmentTransaction ft = getFragmentManager().beginTransaction(); 462 ft.show(mContactsFragment); 463 if (mContactDetailFragment != null) ft.show(mContactDetailFragment); 464 ft.commit(); 465 clearSearch(); 466 break; 467 case STOP_SEARCH_MODE: 468 // Refresh the fragments because search mode was using them to display search 469 // results. 470 clearSearch(); 471 472 // If the last selected tab was not the "All contacts" tab, then hide these 473 // fragments because we need to show favorites or groups. 474 if (mSelectedTab != null && !mSelectedTab.equals(TabState.CONTACTS)) { 475 FragmentTransaction transaction = getFragmentManager().beginTransaction(); 476 transaction.hide(mContactsFragment); 477 if (mContactDetailFragment != null) transaction.hide(mContactDetailFragment); 478 transaction.commit(); 479 } 480 break; 481 case CHANGE_SEARCH_QUERY: 482 loadSearch(mActionBarAdapter.getQueryString()); 483 break; 484 default: 485 throw new IllegalStateException("Unkonwn ActionBarAdapter action: " + action); 486 } 487 } 488 489 private void clearSearch() { 490 loadSearch(""); 491 } 492 493 private void loadSearch(String query) { 494 configureFragments(false /* from request */); 495 mListFragment.setQueryString(query, true); 496 } 497 498 private void configureContactListFragmentForRequest() { 499 Uri contactUri = mRequest.getContactUri(); 500 if (contactUri != null) { 501 mListFragment.setSelectedContactUri(contactUri); 502 } 503 504 mListFragment.setSearchMode(mRequest.isSearchMode()); 505 mListFragment.setQueryString(mRequest.getQueryString(), false); 506 507 if (mRequest.isDirectorySearchEnabled()) { 508 mListFragment.setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_DEFAULT); 509 } else { 510 mListFragment.setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_NONE); 511 } 512 513 if (mContactListFilterController.isLoaded()) { 514 mListFragment.setFilter(mContactListFilterController.getFilter()); 515 } 516 } 517 518 private void configureContactListFragment() { 519 mListFragment.setSearchMode(mSearchMode); 520 521 mListFragment.setVisibleScrollbarEnabled(!mSearchMode); 522 mListFragment.setVerticalScrollbarPosition( 523 mContentPaneDisplayed 524 ? View.SCROLLBAR_POSITION_LEFT 525 : View.SCROLLBAR_POSITION_RIGHT); 526 mListFragment.setSelectionVisible(mContentPaneDisplayed); 527 mListFragment.setQuickContactEnabled(!mContentPaneDisplayed); 528 } 529 530 private void configureGroupListFragment() { 531 mGroupsFragment.setVerticalScrollbarPosition( 532 mContentPaneDisplayed 533 ? View.SCROLLBAR_POSITION_LEFT 534 : View.SCROLLBAR_POSITION_RIGHT); 535 mGroupsFragment.setSelectionVisible(mContentPaneDisplayed); 536 } 537 538 @Override 539 public void onProviderStatusChange() { 540 updateFragmentVisibility(); 541 } 542 543 private void updateFragmentVisibility() { 544 int providerStatus = mProviderStatusLoader.getProviderStatus(); 545 if (providerStatus == mProviderStatus) { 546 return; 547 } 548 549 mProviderStatus = providerStatus; 550 551 View contactsUnavailableView = findViewById(R.id.contacts_unavailable_view); 552 View mainView = findViewById(R.id.main_view); 553 554 if (mProviderStatus == ProviderStatus.STATUS_NORMAL) { 555 contactsUnavailableView.setVisibility(View.GONE); 556 if (mainView != null) { 557 mainView.setVisibility(View.VISIBLE); 558 } 559 if (mListFragment != null) { 560 mListFragment.setEnabled(true); 561 } 562 } else { 563 if (mListFragment != null) { 564 mListFragment.setEnabled(false); 565 } 566 if (mContactsUnavailableFragment == null) { 567 mContactsUnavailableFragment = new ContactsUnavailableFragment(); 568 mContactsUnavailableFragment.setProviderStatusLoader(mProviderStatusLoader); 569 mContactsUnavailableFragment.setOnContactsUnavailableActionListener( 570 new ContactsUnavailableFragmentListener()); 571 getFragmentManager().beginTransaction() 572 .replace(R.id.contacts_unavailable_container, mContactsUnavailableFragment) 573 .commit(); 574 } else { 575 mContactsUnavailableFragment.update(); 576 } 577 contactsUnavailableView.setVisibility(View.VISIBLE); 578 if (mainView != null) { 579 mainView.setVisibility(View.INVISIBLE); 580 } 581 } 582 583 invalidateOptionsMenuIfNeeded(); 584 } 585 586 private final class ContactBrowserActionListener implements OnContactBrowserActionListener { 587 588 @Override 589 public void onSelectionChange() { 590 if (mContentPaneDisplayed) { 591 setupContactDetailFragment(mListFragment.getSelectedContactUri()); 592 } 593 } 594 595 @Override 596 public void onViewContactAction(Uri contactLookupUri) { 597 if (mContentPaneDisplayed) { 598 setupContactDetailFragment(contactLookupUri); 599 } else { 600 startActivity(new Intent(Intent.ACTION_VIEW, contactLookupUri)); 601 } 602 } 603 604 @Override 605 public void onCreateNewContactAction() { 606 Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI); 607 Bundle extras = getIntent().getExtras(); 608 if (extras != null) { 609 intent.putExtras(extras); 610 } 611 startActivity(intent); 612 } 613 614 @Override 615 public void onEditContactAction(Uri contactLookupUri) { 616 Intent intent = new Intent(Intent.ACTION_EDIT, contactLookupUri); 617 Bundle extras = getIntent().getExtras(); 618 if (extras != null) { 619 intent.putExtras(extras); 620 } 621 startActivityForResult(intent, SUBACTIVITY_EDIT_CONTACT); 622 } 623 624 @Override 625 public void onAddToFavoritesAction(Uri contactUri) { 626 ContentValues values = new ContentValues(1); 627 values.put(Contacts.STARRED, 1); 628 getContentResolver().update(contactUri, values, null, null); 629 } 630 631 @Override 632 public void onRemoveFromFavoritesAction(Uri contactUri) { 633 ContentValues values = new ContentValues(1); 634 values.put(Contacts.STARRED, 0); 635 getContentResolver().update(contactUri, values, null, null); 636 } 637 638 @Override 639 public void onCallContactAction(Uri contactUri) { 640 getPhoneNumberCallInteraction().startInteraction(contactUri); 641 } 642 643 @Override 644 public void onSmsContactAction(Uri contactUri) { 645 getSendTextMessageInteraction().startInteraction(contactUri); 646 } 647 648 @Override 649 public void onDeleteContactAction(Uri contactUri) { 650 ContactDeletionInteraction.start(PeopleActivity.this, contactUri, false); 651 } 652 653 @Override 654 public void onFinishAction() { 655 onBackPressed(); 656 } 657 658 @Override 659 public void onInvalidSelection() { 660 ContactListFilter filter; 661 ContactListFilter currentFilter = mListFragment.getFilter(); 662 if (currentFilter != null 663 && currentFilter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) { 664 filter = ContactListFilter.createFilterWithType( 665 ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS); 666 mListFragment.setFilter(filter); 667 } else { 668 filter = ContactListFilter.createFilterWithType( 669 ContactListFilter.FILTER_TYPE_SINGLE_CONTACT); 670 mListFragment.setFilter(filter, false); 671 } 672 mContactListFilterController.setContactListFilter(filter, true); 673 } 674 } 675 676 private class ContactDetailFragmentListener implements ContactDetailFragment.Listener { 677 @Override 678 public void onContactNotFound() { 679 // Nothing needs to be done here 680 } 681 682 @Override 683 public void onDetailsLoaded(ContactLoader.Result result) { 684 // Nothing needs to be done here 685 } 686 687 @Override 688 public void onEditRequested(Uri contactLookupUri) { 689 startActivityForResult( 690 new Intent(Intent.ACTION_EDIT, contactLookupUri), SUBACTIVITY_EDIT_CONTACT); 691 } 692 693 @Override 694 public void onItemClicked(Intent intent) { 695 try { 696 startActivity(intent); 697 } catch (ActivityNotFoundException e) { 698 Log.e(TAG, "No activity found for intent: " + intent); 699 } 700 } 701 702 @Override 703 public void onDeleteRequested(Uri contactUri) { 704 ContactDeletionInteraction.start(PeopleActivity.this, contactUri, false); 705 } 706 707 @Override 708 public void onCreateRawContactRequested(ArrayList<ContentValues> values, Account account) { 709 Toast.makeText(PeopleActivity.this, R.string.toast_making_personal_copy, 710 Toast.LENGTH_LONG).show(); 711 Intent serviceIntent = ContactSaveService.createNewRawContactIntent( 712 PeopleActivity.this, values, account, 713 PeopleActivity.class, Intent.ACTION_VIEW); 714 startService(serviceIntent); 715 } 716 } 717 718 private class ContactsUnavailableFragmentListener 719 implements OnContactsUnavailableActionListener { 720 721 @Override 722 public void onCreateNewContactAction() { 723 startActivity(new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI)); 724 } 725 726 @Override 727 public void onAddAccountAction() { 728 Intent intent = new Intent(Settings.ACTION_ADD_ACCOUNT); 729 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 730 intent.putExtra(Settings.EXTRA_AUTHORITIES, 731 new String[] { ContactsContract.AUTHORITY }); 732 startActivity(intent); 733 } 734 735 @Override 736 public void onImportContactsFromFileAction() { 737 AccountSelectionUtil.doImportFromSdCard(PeopleActivity.this, null); 738 } 739 740 @Override 741 public void onFreeInternalStorageAction() { 742 startActivity(new Intent(Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS)); 743 } 744 } 745 746 private final class GroupBrowserActionListener implements OnGroupBrowserActionListener { 747 748 @Override 749 public void onViewGroupAction(Uri groupUri) { 750 if (mContentPaneDisplayed) { 751 setupGroupDetailFragment(groupUri); 752 } else { 753 Intent intent = new Intent(PeopleActivity.this, GroupDetailActivity.class); 754 intent.setData(groupUri); 755 startActivity(intent); 756 } 757 } 758 } 759 760 public void startActivityAndForwardResult(final Intent intent) { 761 intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); 762 763 // Forward extras to the new activity 764 Bundle extras = getIntent().getExtras(); 765 if (extras != null) { 766 intent.putExtras(extras); 767 } 768 startActivity(intent); 769 finish(); 770 } 771 772 @Override 773 public boolean onCreatePanelMenu(int featureId, Menu menu) { 774 // No menu if contacts are unavailable 775 if (!areContactsAvailable()) { 776 return false; 777 } 778 779 return super.onCreatePanelMenu(featureId, menu); 780 } 781 782 @Override 783 public boolean onPreparePanel(int featureId, View view, Menu menu) { 784 // No menu if contacts are unavailable 785 if (!areContactsAvailable()) { 786 return false; 787 } 788 789 return super.onPreparePanel(featureId, view, menu); 790 } 791 792 @Override 793 public boolean onCreateOptionsMenu(Menu menu) { 794 if (!areContactsAvailable()) { 795 return false; 796 } 797 super.onCreateOptionsMenu(menu); 798 799 MenuInflater inflater = getMenuInflater(); 800 inflater.inflate(R.menu.actions, menu); 801 MenuItem searchMenuItem = menu.findItem(R.id.menu_search); 802 if (searchMenuItem != null && searchMenuItem.getActionView() instanceof SearchView) { 803 SearchView searchView = (SearchView) searchMenuItem.getActionView(); 804 searchView.setQueryHint(getString(R.string.hint_findContacts)); 805 searchView.setIconifiedByDefault(false); 806 807 if (mActionBarAdapter != null) { 808 mActionBarAdapter.setSearchView(searchView); 809 } 810 } 811 return true; 812 } 813 814 private void invalidateOptionsMenuIfNeeded() { 815 if (isOptionsMenuChanged()) { 816 invalidateOptionsMenu(); 817 } 818 } 819 820 public boolean isOptionsMenuChanged() { 821 if (mOptionsMenuContactsAvailable != areContactsAvailable()) { 822 return true; 823 } 824 825 if (mListFragment != null && mListFragment.isOptionsMenuChanged()) { 826 return true; 827 } 828 829 if (mContactDetailFragment != null && mContactDetailFragment.isOptionsMenuChanged()) { 830 return true; 831 } 832 833 if (mGroupDetailFragment != null && mGroupDetailFragment.isOptionsMenuChanged()) { 834 return true; 835 } 836 837 return false; 838 } 839 840 @Override 841 public boolean onPrepareOptionsMenu(Menu menu) { 842 mOptionsMenuContactsAvailable = areContactsAvailable(); 843 if (!mOptionsMenuContactsAvailable) { 844 return false; 845 } 846 847 final MenuItem addContactMenu = menu.findItem(R.id.menu_add_contact); 848 final MenuItem addGroupMenu = menu.findItem(R.id.menu_add_group); 849 final MenuItem searchMenu = menu.findItem(R.id.menu_search); 850 851 if (mActionBarAdapter.isSearchMode()) { 852 addContactMenu.setVisible(false); 853 addGroupMenu.setVisible(false); 854 // If search is normally in the overflow menu, when we are in search 855 // mode, hide this option. 856 if (mActionBarAdapter.isSearchInOverflowMenu()) { 857 searchMenu.setVisible(false); 858 } 859 } else { 860 switch (mSelectedTab) { 861 case FAVORITES: 862 // TODO: Fall through until we determine what the menu items should be for 863 // this tab 864 case CONTACTS: 865 addContactMenu.setVisible(true); 866 addGroupMenu.setVisible(false); 867 break; 868 case GROUPS: 869 addContactMenu.setVisible(false); 870 addGroupMenu.setVisible(true); 871 break; 872 } 873 } 874 875 MenuItem settings = menu.findItem(R.id.menu_settings); 876 if (settings != null) { 877 settings.setVisible(!ContactsPreferenceActivity.isEmpty(this)); 878 } 879 880 return true; 881 } 882 883 @Override 884 public boolean onOptionsItemSelected(MenuItem item) { 885 switch (item.getItemId()) { 886 case R.id.menu_settings: { 887 final Intent intent = new Intent(this, ContactsPreferenceActivity.class); 888 startActivity(intent); 889 return true; 890 } 891 case R.id.menu_contacts_filter: { 892 final Intent intent = new Intent(this, CustomContactListFilterActivity.class); 893 startActivityForResult(intent, SUBACTIVITY_CUSTOMIZE_FILTER); 894 return true; 895 } 896 case R.id.menu_search: { 897 onSearchRequested(); 898 return true; 899 } 900 case R.id.menu_add_contact: { 901 final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI); 902 startActivityForResult(intent, SUBACTIVITY_NEW_CONTACT); 903 return true; 904 } 905 case R.id.menu_add_group: { 906 // TODO: Hook up "new group" functionality 907 Toast.makeText(this, "NEW GROUP", Toast.LENGTH_SHORT).show(); 908 return true; 909 } 910 case R.id.menu_import_export: { 911 ImportExportDialogFragment.show(getFragmentManager()); 912 return true; 913 } 914 case R.id.menu_accounts: { 915 final Intent intent = new Intent(Settings.ACTION_SYNC_SETTINGS); 916 intent.putExtra(Settings.EXTRA_AUTHORITIES, new String[] { 917 ContactsContract.AUTHORITY 918 }); 919 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 920 startActivity(intent); 921 return true; 922 } 923 } 924 return false; 925 } 926 927 @Override 928 public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData, 929 boolean globalSearch) { 930 if (mListFragment != null && mListFragment.isAdded() && !globalSearch) { 931 mListFragment.startSearch(initialQuery); 932 } else { 933 super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch); 934 } 935 } 936 937 @Override 938 protected Dialog onCreateDialog(int id, Bundle bundle) { 939 if (DialogManager.isManagedId(id)) return mDialogManager.onCreateDialog(id, bundle); 940 941 Dialog dialog = getPhoneNumberCallInteraction().onCreateDialog(id, bundle); 942 if (dialog != null) return dialog; 943 944 dialog = getSendTextMessageInteraction().onCreateDialog(id, bundle); 945 if (dialog != null) return dialog; 946 947 return super.onCreateDialog(id, bundle); 948 } 949 950 @Override 951 protected void onPrepareDialog(int id, Dialog dialog, Bundle bundle) { 952 if (getPhoneNumberCallInteraction().onPrepareDialog(id, dialog, bundle)) { 953 return; 954 } 955 956 if (getSendTextMessageInteraction().onPrepareDialog(id, dialog, bundle)) { 957 return; 958 } 959 960 super.onPrepareDialog(id, dialog, bundle); 961 } 962 963 @Override 964 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 965 switch (requestCode) { 966 case SUBACTIVITY_CUSTOMIZE_FILTER: { 967 if (resultCode == Activity.RESULT_OK) { 968 mContactListFilterController.selectCustomFilter(); 969 } 970 break; 971 } 972 973 case SUBACTIVITY_EDIT_CONTACT: 974 case SUBACTIVITY_NEW_CONTACT: { 975 if (resultCode == RESULT_OK && mContentPaneDisplayed) { 976 mRequest.setActionCode(ContactsRequest.ACTION_VIEW_CONTACT); 977 mListFragment.reloadDataAndSetSelectedUri(data.getData()); 978 } 979 break; 980 } 981 982 // TODO: Using the new startActivityWithResultFromFragment API this should not be needed 983 // anymore 984 case ContactEntryListFragment.ACTIVITY_REQUEST_CODE_PICKER: 985 if (resultCode == RESULT_OK) { 986 mListFragment.onPickerResult(data); 987 } 988 989// TODO fix or remove multipicker code 990// else if (resultCode == RESULT_CANCELED && mMode == MODE_PICK_MULTIPLE_PHONES) { 991// // Finish the activity if the sub activity was canceled as back key is used 992// // to confirm user selection in MODE_PICK_MULTIPLE_PHONES. 993// finish(); 994// } 995// break; 996 } 997 } 998 999 @Override 1000 public boolean onContextItemSelected(MenuItem item) { 1001 ContextMenuAdapter menuAdapter = mListFragment.getContextMenuAdapter(); 1002 if (menuAdapter != null) { 1003 return menuAdapter.onContextItemSelected(item); 1004 } 1005 1006 return super.onContextItemSelected(item); 1007 } 1008 1009 @Override 1010 public boolean onKeyDown(int keyCode, KeyEvent event) { 1011 // TODO move to the fragment 1012 switch (keyCode) { 1013// case KeyEvent.KEYCODE_CALL: { 1014// if (callSelection()) { 1015// return true; 1016// } 1017// break; 1018// } 1019 1020 case KeyEvent.KEYCODE_DEL: { 1021 if (deleteSelection()) { 1022 return true; 1023 } 1024 break; 1025 } 1026 default: { 1027 // Bring up the search UI if the user starts typing 1028 final int unicodeChar = event.getUnicodeChar(); 1029 if (unicodeChar != 0 && !Character.isWhitespace(unicodeChar)) { 1030 String query = new String(new int[]{ unicodeChar }, 0, 1); 1031 if (!mActionBarAdapter.isSearchMode()) { 1032 mActionBarAdapter.setQueryString(query); 1033 mActionBarAdapter.setSearchMode(true); 1034 return true; 1035 } else if (!mRequest.isSearchMode()) { 1036 if (!mSearchInitiated) { 1037 mSearchInitiated = true; 1038 startSearch(query, false, null, false); 1039 return true; 1040 } 1041 } 1042 } 1043 } 1044 } 1045 1046 return super.onKeyDown(keyCode, event); 1047 } 1048 1049 @Override 1050 public void onBackPressed() { 1051 if (mSearchMode && mActionBarAdapter != null) { 1052 mActionBarAdapter.setSearchMode(false); 1053 } else { 1054 super.onBackPressed(); 1055 } 1056 } 1057 1058 private boolean deleteSelection() { 1059 // TODO move to the fragment 1060// if (mActionCode == ContactsRequest.ACTION_DEFAULT) { 1061// final int position = mListView.getSelectedItemPosition(); 1062// if (position != ListView.INVALID_POSITION) { 1063// Uri contactUri = getContactUri(position); 1064// if (contactUri != null) { 1065// doContactDelete(contactUri); 1066// return true; 1067// } 1068// } 1069// } 1070 return false; 1071 } 1072 1073 @Override 1074 protected void onSaveInstanceState(Bundle outState) { 1075 super.onSaveInstanceState(outState); 1076 outState.putBoolean(KEY_SEARCH_MODE, mSearchMode); 1077 if (mActionBarAdapter != null) { 1078 mActionBarAdapter.onSaveInstanceState(outState); 1079 } 1080 } 1081 1082 @Override 1083 protected void onRestoreInstanceState(Bundle inState) { 1084 super.onRestoreInstanceState(inState); 1085 mSearchMode = inState.getBoolean(KEY_SEARCH_MODE); 1086 if (mActionBarAdapter != null) { 1087 mActionBarAdapter.onRestoreInstanceState(inState); 1088 } 1089 } 1090 1091 private PhoneNumberInteraction getPhoneNumberCallInteraction() { 1092 if (mPhoneNumberCallInteraction == null) { 1093 mPhoneNumberCallInteraction = new PhoneNumberInteraction(this, false, null); 1094 } 1095 return mPhoneNumberCallInteraction; 1096 } 1097 1098 private PhoneNumberInteraction getSendTextMessageInteraction() { 1099 if (mSendTextMessageInteraction == null) { 1100 mSendTextMessageInteraction = new PhoneNumberInteraction(this, true, null); 1101 } 1102 return mSendTextMessageInteraction; 1103 } 1104 1105 @Override 1106 public DialogManager getDialogManager() { 1107 return mDialogManager; 1108 } 1109 1110 // Visible for testing 1111 public ContactBrowseListFragment getListFragment() { 1112 return mListFragment; 1113 } 1114 1115 // Visible for testing 1116 public ContactDetailFragment getDetailFragment() { 1117 return mContactDetailFragment; 1118 } 1119} 1120