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