PeopleActivity.java revision 72a8e70ef935935fdbb26c53f760be59bbdfe824
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 final Intent intent = new Intent(PeopleActivity.this, GroupEditorActivity.class); 840 intent.setData(groupUri); 841 intent.setAction(Intent.ACTION_EDIT); 842 startActivityForResult(intent, SUBACTIVITY_EDIT_GROUP); 843 } 844 } 845 846 public void startActivityAndForwardResult(final Intent intent) { 847 intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); 848 849 // Forward extras to the new activity 850 Bundle extras = getIntent().getExtras(); 851 if (extras != null) { 852 intent.putExtras(extras); 853 } 854 startActivity(intent); 855 finish(); 856 } 857 858 @Override 859 public boolean onCreatePanelMenu(int featureId, Menu menu) { 860 // No menu if contacts are unavailable 861 if (!areContactsAvailable()) { 862 return false; 863 } 864 865 return super.onCreatePanelMenu(featureId, menu); 866 } 867 868 @Override 869 public boolean onPreparePanel(int featureId, View view, Menu menu) { 870 // No menu if contacts are unavailable 871 if (!areContactsAvailable()) { 872 return false; 873 } 874 875 return super.onPreparePanel(featureId, view, menu); 876 } 877 878 @Override 879 public boolean onCreateOptionsMenu(Menu menu) { 880 if (!areContactsAvailable()) { 881 return false; 882 } 883 super.onCreateOptionsMenu(menu); 884 885 MenuInflater inflater = getMenuInflater(); 886 inflater.inflate(R.menu.actions, menu); 887 MenuItem searchMenuItem = menu.findItem(R.id.menu_search); 888 if (searchMenuItem != null && searchMenuItem.getActionView() instanceof SearchView) { 889 SearchView searchView = (SearchView) searchMenuItem.getActionView(); 890 searchView.setQueryHint(getString(R.string.hint_findContacts)); 891 searchView.setIconifiedByDefault(false); 892 893 if (mActionBarAdapter != null) { 894 mActionBarAdapter.setSearchView(searchView); 895 } 896 } 897 898 // On narrow screens we specify a NEW group button in the {@link ActionBar}, so that 899 // it can be in the overflow menu. On wide screens, we use a custom view because we need 900 // its location for anchoring the account-selector popup. 901 final MenuItem addGroup = menu.findItem(R.id.menu_custom_add_group); 902 if (addGroup != null) { 903 mAddGroupImageView = getLayoutInflater().inflate( 904 R.layout.add_group_menu_item, null, false); 905 View item = mAddGroupImageView.findViewById(R.id.menu_item); 906 item.setOnClickListener(new OnClickListener() { 907 @Override 908 public void onClick(View v) { 909 createNewGroupWithAccountDisambiguation(); 910 } 911 }); 912 addGroup.setActionView(mAddGroupImageView); 913 } 914 return true; 915 } 916 917 private void invalidateOptionsMenuIfNeeded() { 918 if (isOptionsMenuChanged()) { 919 invalidateOptionsMenu(); 920 } 921 } 922 923 public boolean isOptionsMenuChanged() { 924 if (mOptionsMenuContactsAvailable != areContactsAvailable()) { 925 return true; 926 } 927 928 if (mListFragment != null && mListFragment.isOptionsMenuChanged()) { 929 return true; 930 } 931 932 if (mContactDetailFragment != null && mContactDetailFragment.isOptionsMenuChanged()) { 933 return true; 934 } 935 936 if (mGroupDetailFragment != null && mGroupDetailFragment.isOptionsMenuChanged()) { 937 return true; 938 } 939 940 return false; 941 } 942 943 @Override 944 public boolean onPrepareOptionsMenu(Menu menu) { 945 mOptionsMenuContactsAvailable = areContactsAvailable(); 946 if (!mOptionsMenuContactsAvailable) { 947 return false; 948 } 949 950 final MenuItem searchMenu = menu.findItem(R.id.menu_search); 951 final MenuItem addContactMenu = menu.findItem(R.id.menu_add_contact); 952 MenuItem addGroupMenu = menu.findItem(R.id.menu_add_group); 953 if (addGroupMenu == null) { 954 addGroupMenu = menu.findItem(R.id.menu_custom_add_group); 955 } 956 957 if (mActionBarAdapter.isSearchMode()) { 958 addContactMenu.setVisible(false); 959 addGroupMenu.setVisible(false); 960 // If search is normally in the overflow menu, when we are in search 961 // mode, hide this option. 962 if (mActionBarAdapter.isSearchInOverflowMenu()) { 963 searchMenu.setVisible(false); 964 } 965 } else { 966 switch (mSelectedTab) { 967 case FAVORITES: 968 // TODO: Fall through until we determine what the menu items should be for 969 // this tab 970 case CONTACTS: 971 addContactMenu.setVisible(true); 972 addGroupMenu.setVisible(false); 973 break; 974 case GROUPS: 975 addContactMenu.setVisible(false); 976 addGroupMenu.setVisible(true); 977 break; 978 } 979 } 980 981 MenuItem settings = menu.findItem(R.id.menu_settings); 982 if (settings != null) { 983 settings.setVisible(!ContactsPreferenceActivity.isEmpty(this)); 984 } 985 986 return true; 987 } 988 989 @Override 990 public boolean onOptionsItemSelected(MenuItem item) { 991 switch (item.getItemId()) { 992 case R.id.menu_settings: { 993 final Intent intent = new Intent(this, ContactsPreferenceActivity.class); 994 startActivity(intent); 995 return true; 996 } 997 case R.id.menu_contacts_filter: { 998 final Intent intent = new Intent(this, CustomContactListFilterActivity.class); 999 startActivityForResult(intent, SUBACTIVITY_CUSTOMIZE_FILTER); 1000 return true; 1001 } 1002 case R.id.menu_search: { 1003 onSearchRequested(); 1004 return true; 1005 } 1006 case R.id.menu_add_contact: { 1007 final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI); 1008 startActivityForResult(intent, SUBACTIVITY_NEW_CONTACT); 1009 return true; 1010 } 1011 case R.id.menu_add_group: { 1012 createNewGroupWithAccountDisambiguation(); 1013 return true; 1014 } 1015 case R.id.menu_import_export: { 1016 ImportExportDialogFragment.show(getFragmentManager()); 1017 return true; 1018 } 1019 case R.id.menu_accounts: { 1020 final Intent intent = new Intent(Settings.ACTION_SYNC_SETTINGS); 1021 intent.putExtra(Settings.EXTRA_AUTHORITIES, new String[] { 1022 ContactsContract.AUTHORITY 1023 }); 1024 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 1025 startActivity(intent); 1026 return true; 1027 } 1028 } 1029 return false; 1030 } 1031 1032 private void createNewGroupWithAccountDisambiguation() { 1033 final ArrayList<Account> accounts = 1034 AccountTypeManager.getInstance(this).getAccounts(true); 1035 if (accounts.size() <= 1 || mAddGroupImageView == null) { 1036 // No account to choose or no control to anchor the popup-menu to 1037 // ==> just go straight to the editor which will disambig if necessary 1038 final Intent intent = new Intent(this, GroupEditorActivity.class); 1039 intent.setAction(Intent.ACTION_INSERT); 1040 startActivityForResult(intent, SUBACTIVITY_NEW_GROUP); 1041 return; 1042 } 1043 1044 final ListPopupWindow popup = new ListPopupWindow(this, null); 1045 popup.setWidth(getResources().getDimensionPixelSize(R.dimen.account_selector_popup_width)); 1046 popup.setAnchorView(mAddGroupImageView); 1047 // Create a list adapter with all writeable accounts (assume that the writeable accounts all 1048 // allow group creation). 1049 final AccountsListAdapter adapter = new AccountsListAdapter(this, true); 1050 popup.setAdapter(adapter); 1051 popup.setOnItemClickListener(new OnItemClickListener() { 1052 @Override 1053 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 1054 popup.dismiss(); 1055 final Intent intent = new Intent(PeopleActivity.this, GroupEditorActivity.class); 1056 intent.setAction(Intent.ACTION_INSERT); 1057 intent.putExtra(Intents.Insert.ACCOUNT, adapter.getItem(position)); 1058 startActivityForResult(intent, SUBACTIVITY_NEW_GROUP); 1059 } 1060 }); 1061 popup.setModal(true); 1062 popup.show(); 1063 } 1064 1065 @Override 1066 public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData, 1067 boolean globalSearch) { 1068 if (mListFragment != null && mListFragment.isAdded() && !globalSearch) { 1069 mListFragment.startSearch(initialQuery); 1070 } else { 1071 super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch); 1072 } 1073 } 1074 1075 @Override 1076 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 1077 switch (requestCode) { 1078 case SUBACTIVITY_CUSTOMIZE_FILTER: { 1079 if (resultCode == Activity.RESULT_OK) { 1080 mContactListFilterController.selectCustomFilter(); 1081 } 1082 break; 1083 } 1084 1085 case SUBACTIVITY_EDIT_CONTACT: 1086 case SUBACTIVITY_NEW_CONTACT: { 1087 if (resultCode == RESULT_OK && mContentPaneDisplayed) { 1088 mRequest.setActionCode(ContactsRequest.ACTION_VIEW_CONTACT); 1089 mListFragment.reloadDataAndSetSelectedUri(data.getData()); 1090 } 1091 break; 1092 } 1093 1094 case SUBACTIVITY_NEW_GROUP: 1095 case SUBACTIVITY_EDIT_GROUP: { 1096 if (resultCode == RESULT_OK && mContentPaneDisplayed) { 1097 mRequest.setActionCode(ContactsRequest.ACTION_GROUP); 1098 mGroupsFragment.setSelectedUri(data.getData()); 1099 } 1100 break; 1101 } 1102 1103 // TODO: Using the new startActivityWithResultFromFragment API this should not be needed 1104 // anymore 1105 case ContactEntryListFragment.ACTIVITY_REQUEST_CODE_PICKER: 1106 if (resultCode == RESULT_OK) { 1107 mListFragment.onPickerResult(data); 1108 } 1109 1110// TODO fix or remove multipicker code 1111// else if (resultCode == RESULT_CANCELED && mMode == MODE_PICK_MULTIPLE_PHONES) { 1112// // Finish the activity if the sub activity was canceled as back key is used 1113// // to confirm user selection in MODE_PICK_MULTIPLE_PHONES. 1114// finish(); 1115// } 1116// break; 1117 } 1118 } 1119 1120 @Override 1121 public boolean onContextItemSelected(MenuItem item) { 1122 ContextMenuAdapter menuAdapter = mListFragment.getContextMenuAdapter(); 1123 if (menuAdapter != null) { 1124 return menuAdapter.onContextItemSelected(item); 1125 } 1126 1127 return super.onContextItemSelected(item); 1128 } 1129 1130 @Override 1131 public boolean onKeyDown(int keyCode, KeyEvent event) { 1132 // TODO move to the fragment 1133 switch (keyCode) { 1134// case KeyEvent.KEYCODE_CALL: { 1135// if (callSelection()) { 1136// return true; 1137// } 1138// break; 1139// } 1140 1141 case KeyEvent.KEYCODE_DEL: { 1142 if (deleteSelection()) { 1143 return true; 1144 } 1145 break; 1146 } 1147 default: { 1148 // Bring up the search UI if the user starts typing 1149 final int unicodeChar = event.getUnicodeChar(); 1150 if (unicodeChar != 0 && !Character.isWhitespace(unicodeChar)) { 1151 String query = new String(new int[]{ unicodeChar }, 0, 1); 1152 if (!mActionBarAdapter.isSearchMode()) { 1153 mActionBarAdapter.setQueryString(query); 1154 mActionBarAdapter.setSearchMode(true); 1155 return true; 1156 } else if (!mRequest.isSearchMode()) { 1157 if (!mSearchInitiated) { 1158 mSearchInitiated = true; 1159 startSearch(query, false, null, false); 1160 return true; 1161 } 1162 } 1163 } 1164 } 1165 } 1166 1167 return super.onKeyDown(keyCode, event); 1168 } 1169 1170 @Override 1171 public void onBackPressed() { 1172 if (mSearchMode && mActionBarAdapter != null) { 1173 mActionBarAdapter.setSearchMode(false); 1174 } else { 1175 super.onBackPressed(); 1176 } 1177 } 1178 1179 private boolean deleteSelection() { 1180 // TODO move to the fragment 1181// if (mActionCode == ContactsRequest.ACTION_DEFAULT) { 1182// final int position = mListView.getSelectedItemPosition(); 1183// if (position != ListView.INVALID_POSITION) { 1184// Uri contactUri = getContactUri(position); 1185// if (contactUri != null) { 1186// doContactDelete(contactUri); 1187// return true; 1188// } 1189// } 1190// } 1191 return false; 1192 } 1193 1194 @Override 1195 protected void onSaveInstanceState(Bundle outState) { 1196 super.onSaveInstanceState(outState); 1197 outState.putBoolean(KEY_SEARCH_MODE, mSearchMode); 1198 if (mActionBarAdapter != null) { 1199 mActionBarAdapter.onSaveInstanceState(outState); 1200 } 1201 } 1202 1203 @Override 1204 protected void onRestoreInstanceState(Bundle inState) { 1205 super.onRestoreInstanceState(inState); 1206 mSearchMode = inState.getBoolean(KEY_SEARCH_MODE); 1207 if (mActionBarAdapter != null) { 1208 mActionBarAdapter.onRestoreInstanceState(inState); 1209 } 1210 } 1211 1212 @Override 1213 public DialogManager getDialogManager() { 1214 return mDialogManager; 1215 } 1216 1217 // Visible for testing 1218 public ContactBrowseListFragment getListFragment() { 1219 return mListFragment; 1220 } 1221 1222 // Visible for testing 1223 public ContactDetailFragment getDetailFragment() { 1224 return mContactDetailFragment; 1225 } 1226} 1227