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