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