PeopleActivity.java revision 2cc3b3e8e08b484fa6b228dcdf8025aa8ea99c08
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.setListener(null); 394 super.onDestroy(); 395 } 396 397 private void configureFragments(boolean fromRequest) { 398 if (fromRequest) { 399 ContactListFilter filter = null; 400 int actionCode = mRequest.getActionCode(); 401 switch (actionCode) { 402 case ContactsRequest.ACTION_ALL_CONTACTS: 403 filter = ContactListFilter.createFilterWithType( 404 ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS); 405 break; 406 case ContactsRequest.ACTION_CONTACTS_WITH_PHONES: 407 filter = ContactListFilter.createFilterWithType( 408 ContactListFilter.FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY); 409 break; 410 411 // TODO: handle FREQUENT and STREQUENT according to the spec 412 case ContactsRequest.ACTION_FREQUENT: 413 case ContactsRequest.ACTION_STREQUENT: 414 // For now they are treated the same as STARRED 415 case ContactsRequest.ACTION_STARRED: 416 filter = ContactListFilter.createFilterWithType( 417 ContactListFilter.FILTER_TYPE_STARRED); 418 break; 419 case ContactsRequest.ACTION_VIEW_CONTACT: 420 if (PhoneCapabilityTester.isUsingTwoPanes(this)) { 421 mActionBarAdapter.setCurrentTab(TabState.ALL); 422 } 423 } 424 425 mSearchMode = mRequest.isSearchMode(); 426 if (filter != null) { 427 mContactListFilterController.setContactListFilter(filter, false); 428 mSearchMode = false; 429 } else if (mRequest.getActionCode() == ContactsRequest.ACTION_ALL_CONTACTS) { 430 mContactListFilterController.setContactListFilter( 431 ContactListFilter.createFilterWithType( 432 ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS), false); 433 } 434 435 if (mRequest.getContactUri() != null) { 436 mSearchMode = false; 437 } 438 439 mAllFragment.setContactsRequest(mRequest); 440 configureContactListFragmentForRequest(); 441 442 } else { 443 mSearchMode = mActionBarAdapter.isSearchMode(); 444 } 445 446 configureContactListFragment(); 447 configureGroupListFragment(); 448 449 invalidateOptionsMenuIfNeeded(); 450 } 451 452 @Override 453 public void onContactListFilterChanged() { 454 if (mAllFragment == null || !mAllFragment.isAdded()) { 455 return; 456 } 457 458 mAllFragment.setFilter(mContactListFilterController.getFilter()); 459 460 invalidateOptionsMenuIfNeeded(); 461 } 462 463 private void setupContactDetailFragment(final Uri contactLookupUri) { 464 mContactDetailLoaderFragment.loadUri(contactLookupUri); 465 invalidateOptionsMenuIfNeeded(); 466 } 467 468 private void setupGroupDetailFragment(Uri groupUri) { 469 mGroupDetailFragment.loadGroup(groupUri); 470 invalidateOptionsMenuIfNeeded(); 471 } 472 473 /** 474 * Handler for action bar actions. 475 */ 476 @Override 477 public void onAction(Action action) { 478 switch (action) { 479 case START_SEARCH_MODE: 480 clearSearch(); 481 updateFragmentsVisibility(); 482 break; 483 case STOP_SEARCH_MODE: 484 clearSearch(); 485 updateFragmentsVisibility(); 486 break; 487 case CHANGE_SEARCH_QUERY: 488 loadSearch(mActionBarAdapter.getQueryString()); 489 break; 490 default: 491 throw new IllegalStateException("Unkonwn ActionBarAdapter action: " + action); 492 } 493 } 494 495 @Override 496 public void onSelectedTabChanged() { 497 updateFragmentsVisibility(); 498 } 499 500 /** 501 * Updates the fragment/view visibility according to the current mode, such as 502 * {@link ActionBarAdapter#isSearchMode()} and {@link ActionBarAdapter#getCurrentTab()}. 503 */ 504 private void updateFragmentsVisibility() { 505 TabState tab = mActionBarAdapter.getCurrentTab(); 506 507 // We use ViewPager on 1-pane. 508 if (!PhoneCapabilityTester.isUsingTwoPanes(this)) { 509 if (mActionBarAdapter.isSearchMode()) { 510 mTabPagerAdapter.setSearchMode(true); 511 } else { 512 mTabPagerAdapter.setSearchMode(false); 513 int tabIndex = tab.ordinal(); 514 if (mTabPager.getCurrentItem() != tabIndex) { 515 mTabPager.setCurrentItem(tab.ordinal(), false /* no smooth scroll */); 516 } 517 } 518 return; 519 } 520 521 // for the tablet... 522 523 // If in search mode, we use the all list + contact details to show the result. 524 if (mActionBarAdapter.isSearchMode()) { 525 tab = TabState.ALL; 526 } 527 switch (tab) { 528 case FAVORITES: 529 mFavoritesView.setVisibility(View.VISIBLE); 530 mBrowserView.setVisibility(View.GONE); 531 mDetailsView.setVisibility(View.GONE); 532 break; 533 case GROUPS: 534 case ALL: 535 mFavoritesView.setVisibility(View.GONE); 536 mBrowserView.setVisibility(View.VISIBLE); 537 mDetailsView.setVisibility(View.VISIBLE); 538 break; 539 } 540 FragmentManager fragmentManager = getFragmentManager(); 541 FragmentTransaction ft = fragmentManager.beginTransaction(); 542 543 switch (tab) { 544 case FAVORITES: 545 showFragment(ft, mFavoritesFragment); 546 showFragment(ft, mFrequentFragment); 547 hideFragment(ft, mAllFragment); 548 hideFragment(ft, mContactDetailFragment); 549 hideFragment(ft, mGroupsFragment); 550 hideFragment(ft, mGroupDetailFragment); 551 break; 552 case ALL: 553 hideFragment(ft, mFavoritesFragment); 554 hideFragment(ft, mFrequentFragment); 555 showFragment(ft, mAllFragment); 556 showFragment(ft, mContactDetailFragment); 557 hideFragment(ft, mGroupsFragment); 558 hideFragment(ft, mGroupDetailFragment); 559 break; 560 case GROUPS: 561 hideFragment(ft, mFavoritesFragment); 562 hideFragment(ft, mFrequentFragment); 563 hideFragment(ft, mAllFragment); 564 hideFragment(ft, mContactDetailFragment); 565 showFragment(ft, mGroupsFragment); 566 showFragment(ft, mGroupDetailFragment); 567 break; 568 } 569 if (!ft.isEmpty()) { 570 ft.commit(); 571 fragmentManager.executePendingTransactions(); 572 } 573 } 574 575 private class TabPagerListener implements ViewPager.OnPageChangeListener { 576 @Override 577 public void onPageScrollStateChanged(int state) { 578 } 579 580 @Override 581 public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 582 } 583 584 @Override 585 public void onPageSelected(int position) { 586 // Make sure not in the search mode, in which case position != TabState.ordinal(). 587 if (!mTabPagerAdapter.isSearchMode()) { 588 mActionBarAdapter.setCurrentTab(TabState.fromInt(position), false); 589 } 590 } 591 } 592 593 /** 594 * Adapter for the {@link ViewPager}. Unlike {@link FragmentPagerAdapter}, 595 * {@link #instantiateItem} returns existing fragments, and {@link #instantiateItem}/ 596 * {@link #destroyItem} show/hide fragments instead of attaching/detaching. 597 * 598 * In search mode, we always show the "all" fragment, and disable the swipe. We change the 599 * number of items to 1 to disable the swipe. 600 * 601 * TODO figure out a more straight way to disable swipe. 602 */ 603 private class TabPagerAdapter extends PagerAdapter { 604 private final FragmentManager mFragmentManager; 605 private FragmentTransaction mCurTransaction = null; 606 607 private boolean mTabPagerAdapterSearchMode; 608 609 public TabPagerAdapter() { 610 mFragmentManager = getFragmentManager(); 611 } 612 613 public boolean isSearchMode() { 614 return mTabPagerAdapterSearchMode; 615 } 616 617 public void setSearchMode(boolean searchMode) { 618 if (searchMode == mTabPagerAdapterSearchMode) { 619 return; 620 } 621 mTabPagerAdapterSearchMode = searchMode; 622 notifyDataSetChanged(); 623 } 624 625 @Override 626 public int getCount() { 627 return mTabPagerAdapterSearchMode ? 1 : TabState.values().length; 628 } 629 630 /** Gets called when the number of items changes. */ 631 @Override 632 public int getItemPosition(Object object) { 633 if (mTabPagerAdapterSearchMode) { 634 if (object == mAllFragment) { 635 return 0; // Only 1 page in search mode 636 } 637 } else { 638 if (object == mFavoritesFragment) { 639 return TabState.FAVORITES.ordinal(); 640 } 641 if (object == mAllFragment) { 642 return TabState.ALL.ordinal(); 643 } 644 if (object == mGroupsFragment) { 645 return TabState.GROUPS.ordinal(); 646 } 647 } 648 return POSITION_NONE; 649 } 650 651 @Override 652 public void startUpdate(View container) { 653 } 654 655 private Fragment getFragment(int position) { 656 if (mTabPagerAdapterSearchMode) { 657 if (position == 0) { 658 return mAllFragment; 659 } 660 } else { 661 if (position == TabState.FAVORITES.ordinal()) { 662 return mFavoritesFragment; 663 } else if (position == TabState.ALL.ordinal()) { 664 return mAllFragment; 665 } else if (position == TabState.GROUPS.ordinal()) { 666 return mGroupsFragment; 667 } 668 } 669 throw new IllegalArgumentException("position: " + position); 670 } 671 672 @Override 673 public Object instantiateItem(View container, int position) { 674 if (mCurTransaction == null) { 675 mCurTransaction = mFragmentManager.beginTransaction(); 676 } 677 Fragment f = getFragment(position); 678 mCurTransaction.show(f); 679 return f; 680 } 681 682 @Override 683 public void destroyItem(View container, int position, Object object) { 684 if (mCurTransaction == null) { 685 mCurTransaction = mFragmentManager.beginTransaction(); 686 } 687 mCurTransaction.hide((Fragment) object); 688 } 689 690 @Override 691 public void finishUpdate(View container) { 692 if (mCurTransaction != null) { 693 mCurTransaction.commit(); 694 mCurTransaction = null; 695 mFragmentManager.executePendingTransactions(); 696 } 697 } 698 699 @Override 700 public boolean isViewFromObject(View view, Object object) { 701 return ((Fragment) object).getView() == view; 702 } 703 704 @Override 705 public Parcelable saveState() { 706 return null; 707 } 708 709 @Override 710 public void restoreState(Parcelable state, ClassLoader loader) { 711 } 712 } 713 714 private void clearSearch() { 715 loadSearch(""); 716 } 717 718 private void loadSearch(String query) { 719 configureFragments(false /* from request */); 720 mAllFragment.setQueryString(query, true); 721 } 722 723 private void configureContactListFragmentForRequest() { 724 Uri contactUri = mRequest.getContactUri(); 725 if (contactUri != null) { 726 mAllFragment.setSelectedContactUri(contactUri); 727 } 728 729 mAllFragment.setSearchMode(mRequest.isSearchMode()); 730 mAllFragment.setQueryString(mRequest.getQueryString(), false); 731 732 if (mRequest.isDirectorySearchEnabled()) { 733 mAllFragment.setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_DEFAULT); 734 } else { 735 mAllFragment.setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_NONE); 736 } 737 738 if (mContactListFilterController.isInitialized()) { 739 mAllFragment.setFilter(mContactListFilterController.getFilter()); 740 } 741 } 742 743 private void configureContactListFragment() { 744 mAllFragment.setSearchMode(mSearchMode); 745 746 final boolean useTwoPane = PhoneCapabilityTester.isUsingTwoPanes(this); 747 mAllFragment.setVisibleScrollbarEnabled(!mSearchMode); 748 mAllFragment.setVerticalScrollbarPosition( 749 useTwoPane 750 ? View.SCROLLBAR_POSITION_LEFT 751 : View.SCROLLBAR_POSITION_RIGHT); 752 mAllFragment.setSelectionVisible(useTwoPane); 753 mAllFragment.setQuickContactEnabled(!useTwoPane); 754 } 755 756 private void configureGroupListFragment() { 757 final boolean useTwoPane = PhoneCapabilityTester.isUsingTwoPanes(this); 758 mGroupsFragment.setVerticalScrollbarPosition( 759 useTwoPane 760 ? View.SCROLLBAR_POSITION_LEFT 761 : View.SCROLLBAR_POSITION_RIGHT); 762 mGroupsFragment.setSelectionVisible(useTwoPane); 763 } 764 765 @Override 766 public void onProviderStatusChange() { 767 showContactsUnavailableFragmentIfNecessary(); 768 } 769 770 private void showContactsUnavailableFragmentIfNecessary() { 771 int providerStatus = mProviderStatusLoader.getProviderStatus(); 772 if (providerStatus == mProviderStatus) { 773 return; 774 } 775 776 mProviderStatus = providerStatus; 777 778 View contactsUnavailableView = findViewById(R.id.contacts_unavailable_view); 779 View mainView = findViewById(R.id.main_view); 780 781 if (mProviderStatus == ProviderStatus.STATUS_NORMAL) { 782 contactsUnavailableView.setVisibility(View.GONE); 783 if (mainView != null) { 784 mainView.setVisibility(View.VISIBLE); 785 } 786 if (mAllFragment != null) { 787 mAllFragment.setEnabled(true); 788 } 789 } else { 790 if (mAllFragment != null) { 791 mAllFragment.setEnabled(false); 792 } 793 if (mContactsUnavailableFragment == null) { 794 mContactsUnavailableFragment = new ContactsUnavailableFragment(); 795 mContactsUnavailableFragment.setProviderStatusLoader(mProviderStatusLoader); 796 mContactsUnavailableFragment.setOnContactsUnavailableActionListener( 797 new ContactsUnavailableFragmentListener()); 798 getFragmentManager().beginTransaction() 799 .replace(R.id.contacts_unavailable_container, mContactsUnavailableFragment) 800 .commit(); 801 } else { 802 mContactsUnavailableFragment.update(); 803 } 804 contactsUnavailableView.setVisibility(View.VISIBLE); 805 if (mainView != null) { 806 mainView.setVisibility(View.INVISIBLE); 807 } 808 } 809 810 invalidateOptionsMenuIfNeeded(); 811 } 812 813 private final class ContactBrowserActionListener implements OnContactBrowserActionListener { 814 815 @Override 816 public void onSelectionChange() { 817 if (PhoneCapabilityTester.isUsingTwoPanes(PeopleActivity.this)) { 818 setupContactDetailFragment(mAllFragment.getSelectedContactUri()); 819 } 820 } 821 822 @Override 823 public void onViewContactAction(Uri contactLookupUri) { 824 if (PhoneCapabilityTester.isUsingTwoPanes(PeopleActivity.this)) { 825 setupContactDetailFragment(contactLookupUri); 826 } else { 827 startActivity(new Intent(Intent.ACTION_VIEW, contactLookupUri)); 828 } 829 } 830 831 @Override 832 public void onCreateNewContactAction() { 833 Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI); 834 Bundle extras = getIntent().getExtras(); 835 if (extras != null) { 836 intent.putExtras(extras); 837 } 838 startActivity(intent); 839 } 840 841 @Override 842 public void onEditContactAction(Uri contactLookupUri) { 843 Intent intent = new Intent(Intent.ACTION_EDIT, contactLookupUri); 844 Bundle extras = getIntent().getExtras(); 845 if (extras != null) { 846 intent.putExtras(extras); 847 } 848 startActivityForResult(intent, SUBACTIVITY_EDIT_CONTACT); 849 } 850 851 @Override 852 public void onAddToFavoritesAction(Uri contactUri) { 853 ContentValues values = new ContentValues(1); 854 values.put(Contacts.STARRED, 1); 855 getContentResolver().update(contactUri, values, null, null); 856 } 857 858 @Override 859 public void onRemoveFromFavoritesAction(Uri contactUri) { 860 ContentValues values = new ContentValues(1); 861 values.put(Contacts.STARRED, 0); 862 getContentResolver().update(contactUri, values, null, null); 863 } 864 865 @Override 866 public void onCallContactAction(Uri contactUri) { 867 PhoneNumberInteraction.startInteractionForPhoneCall(PeopleActivity.this, contactUri); 868 } 869 870 @Override 871 public void onSmsContactAction(Uri contactUri) { 872 PhoneNumberInteraction.startInteractionForTextMessage(PeopleActivity.this, contactUri); 873 } 874 875 @Override 876 public void onDeleteContactAction(Uri contactUri) { 877 ContactDeletionInteraction.start(PeopleActivity.this, contactUri, false); 878 } 879 880 @Override 881 public void onFinishAction() { 882 onBackPressed(); 883 } 884 885 @Override 886 public void onInvalidSelection() { 887 ContactListFilter filter; 888 ContactListFilter currentFilter = mAllFragment.getFilter(); 889 if (currentFilter != null 890 && currentFilter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) { 891 filter = ContactListFilter.createFilterWithType( 892 ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS); 893 mAllFragment.setFilter(filter); 894 } else { 895 filter = ContactListFilter.createFilterWithType( 896 ContactListFilter.FILTER_TYPE_SINGLE_CONTACT); 897 mAllFragment.setFilter(filter, false); 898 } 899 mContactListFilterController.setContactListFilter(filter, true); 900 } 901 } 902 903 private class ContactDetailLoaderFragmentListener implements ContactLoaderFragmentListener { 904 @Override 905 public void onContactNotFound() { 906 // Nothing needs to be done here 907 } 908 909 @Override 910 public void onDetailsLoaded(final ContactLoader.Result result) { 911 if (result == null) { 912 return; 913 } 914 // Since {@link FragmentTransaction}s cannot be done in the onLoadFinished() of the 915 // {@link LoaderCallbacks}, then post this {@link Runnable} to the {@link Handler} 916 // on the main thread to execute later. 917 mHandler.post(new Runnable() { 918 @Override 919 public void run() { 920 if (!mContactDetailLayoutController.isInitialized()) { 921 mContactDetailLayoutController.setContactDetailFragment( 922 mContactDetailFragment); 923 mContactDetailLayoutController.setContactDetailUpdatesFragment( 924 mContactDetailUpdatesFragment); 925 mContactDetailLayoutController.initialize(); 926 } 927 mContactDetailLayoutController.setContactData(result); 928 } 929 }); 930 } 931 932 @Override 933 public void onEditRequested(Uri contactLookupUri) { 934 startActivityForResult( 935 new Intent(Intent.ACTION_EDIT, contactLookupUri), SUBACTIVITY_EDIT_CONTACT); 936 } 937 938 @Override 939 public void onDeleteRequested(Uri contactUri) { 940 ContactDeletionInteraction.start(PeopleActivity.this, contactUri, false); 941 } 942 } 943 944 public class ContactDetailFragmentListener implements ContactDetailFragment.Listener { 945 @Override 946 public void onItemClicked(Intent intent) { 947 try { 948 startActivity(intent); 949 } catch (ActivityNotFoundException e) { 950 Log.e(TAG, "No activity found for intent: " + intent); 951 } 952 } 953 954 @Override 955 public void onCreateRawContactRequested(ArrayList<ContentValues> values, Account account) { 956 Toast.makeText(PeopleActivity.this, R.string.toast_making_personal_copy, 957 Toast.LENGTH_LONG).show(); 958 Intent serviceIntent = ContactSaveService.createNewRawContactIntent( 959 PeopleActivity.this, values, account, 960 PeopleActivity.class, Intent.ACTION_VIEW); 961 startService(serviceIntent); 962 } 963 } 964 965 private class ContactsUnavailableFragmentListener 966 implements OnContactsUnavailableActionListener { 967 968 @Override 969 public void onCreateNewContactAction() { 970 startActivity(new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI)); 971 } 972 973 @Override 974 public void onAddAccountAction() { 975 Intent intent = new Intent(Settings.ACTION_ADD_ACCOUNT); 976 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 977 intent.putExtra(Settings.EXTRA_AUTHORITIES, 978 new String[] { ContactsContract.AUTHORITY }); 979 startActivity(intent); 980 } 981 982 @Override 983 public void onImportContactsFromFileAction() { 984 AccountSelectionUtil.doImportFromSdCard(PeopleActivity.this, null); 985 } 986 987 @Override 988 public void onFreeInternalStorageAction() { 989 startActivity(new Intent(Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS)); 990 } 991 } 992 993 private final class StrequentContactListFragmentListener 994 implements StrequentContactListFragment.Listener { 995 @Override 996 public void onContactSelected(Uri contactUri) { 997 if (PhoneCapabilityTester.isUsingTwoPanes(PeopleActivity.this)) { 998 setupContactDetailFragment(contactUri); 999 } else { 1000 startActivity(new Intent(Intent.ACTION_VIEW, contactUri)); 1001 } 1002 } 1003 } 1004 1005 private final class GroupBrowserActionListener implements OnGroupBrowserActionListener { 1006 1007 @Override 1008 public void onViewGroupAction(Uri groupUri) { 1009 if (PhoneCapabilityTester.isUsingTwoPanes(PeopleActivity.this)) { 1010 setupGroupDetailFragment(groupUri); 1011 } else { 1012 Intent intent = new Intent(PeopleActivity.this, GroupDetailActivity.class); 1013 intent.setData(groupUri); 1014 startActivity(intent); 1015 } 1016 } 1017 } 1018 1019 private class GroupDetailFragmentListener implements GroupDetailFragment.Listener { 1020 @Override 1021 public void onGroupSizeUpdated(String size) { 1022 // Nothing needs to be done here because the size will be displayed in the detail 1023 // fragment 1024 } 1025 1026 @Override 1027 public void onGroupTitleUpdated(String title) { 1028 // Nothing needs to be done here because the title will be displayed in the detail 1029 // fragment 1030 } 1031 1032 @Override 1033 public void onEditRequested(Uri groupUri) { 1034 final Intent intent = new Intent(PeopleActivity.this, GroupEditorActivity.class); 1035 intent.setData(groupUri); 1036 intent.setAction(Intent.ACTION_EDIT); 1037 startActivityForResult(intent, SUBACTIVITY_EDIT_GROUP); 1038 } 1039 1040 @Override 1041 public void onContactSelected(Uri contactUri) { 1042 // Nothing needs to be done here because either quickcontact will be displayed 1043 // or activity will take care of selection 1044 } 1045 } 1046 1047 public void startActivityAndForwardResult(final Intent intent) { 1048 intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); 1049 1050 // Forward extras to the new activity 1051 Bundle extras = getIntent().getExtras(); 1052 if (extras != null) { 1053 intent.putExtras(extras); 1054 } 1055 startActivity(intent); 1056 finish(); 1057 } 1058 1059 @Override 1060 public boolean onCreatePanelMenu(int featureId, Menu menu) { 1061 // No menu if contacts are unavailable 1062 if (!areContactsAvailable()) { 1063 return false; 1064 } 1065 1066 return super.onCreatePanelMenu(featureId, menu); 1067 } 1068 1069 @Override 1070 public boolean onPreparePanel(int featureId, View view, Menu menu) { 1071 // No menu if contacts are unavailable 1072 if (!areContactsAvailable()) { 1073 return false; 1074 } 1075 1076 return super.onPreparePanel(featureId, view, menu); 1077 } 1078 1079 @Override 1080 public boolean onCreateOptionsMenu(Menu menu) { 1081 if (!areContactsAvailable()) { 1082 return false; 1083 } 1084 super.onCreateOptionsMenu(menu); 1085 1086 MenuInflater inflater = getMenuInflater(); 1087 inflater.inflate(R.menu.actions, menu); 1088 1089 // On narrow screens we specify a NEW group button in the {@link ActionBar}, so that 1090 // it can be in the overflow menu. On wide screens, we use a custom view because we need 1091 // its location for anchoring the account-selector popup. 1092 final MenuItem addGroup = menu.findItem(R.id.menu_custom_add_group); 1093 if (addGroup != null) { 1094 mAddGroupImageView = getLayoutInflater().inflate( 1095 R.layout.add_group_menu_item, null, false); 1096 View item = mAddGroupImageView.findViewById(R.id.menu_item); 1097 item.setOnClickListener(new OnClickListener() { 1098 @Override 1099 public void onClick(View v) { 1100 createNewGroupWithAccountDisambiguation(); 1101 } 1102 }); 1103 addGroup.setActionView(mAddGroupImageView); 1104 } 1105 return true; 1106 } 1107 1108 private void invalidateOptionsMenuIfNeeded() { 1109 if (isOptionsMenuChanged()) { 1110 invalidateOptionsMenu(); 1111 } 1112 } 1113 1114 public boolean isOptionsMenuChanged() { 1115 if (mOptionsMenuContactsAvailable != areContactsAvailable()) { 1116 return true; 1117 } 1118 1119 if (mAllFragment != null && mAllFragment.isOptionsMenuChanged()) { 1120 return true; 1121 } 1122 1123 if (mContactDetailLoaderFragment != null && 1124 mContactDetailLoaderFragment.isOptionsMenuChanged()) { 1125 return true; 1126 } 1127 1128 if (mGroupDetailFragment != null && mGroupDetailFragment.isOptionsMenuChanged()) { 1129 return true; 1130 } 1131 1132 return false; 1133 } 1134 1135 @Override 1136 public boolean onPrepareOptionsMenu(Menu menu) { 1137 mOptionsMenuContactsAvailable = areContactsAvailable(); 1138 if (!mOptionsMenuContactsAvailable) { 1139 return false; 1140 } 1141 1142 final MenuItem searchMenu = menu.findItem(R.id.menu_search); 1143 final MenuItem addContactMenu = menu.findItem(R.id.menu_add_contact); 1144 MenuItem addGroupMenu = menu.findItem(R.id.menu_add_group); 1145 if (addGroupMenu == null) { 1146 addGroupMenu = menu.findItem(R.id.menu_custom_add_group); 1147 } 1148 1149 if (mActionBarAdapter.isSearchMode()) { 1150 addContactMenu.setVisible(false); 1151 addGroupMenu.setVisible(false); 1152 if (searchMenu != null) { 1153 searchMenu.setVisible(false); // Don't show the search menu in search mode. 1154 } 1155 } else { 1156 switch (mActionBarAdapter.getCurrentTab()) { 1157 case FAVORITES: 1158 // TODO: Fall through until we determine what the menu items should be for 1159 // this tab 1160 case ALL: 1161 addContactMenu.setVisible(true); 1162 addGroupMenu.setVisible(false); 1163 break; 1164 case GROUPS: 1165 addContactMenu.setVisible(false); 1166 addGroupMenu.setVisible(true); 1167 break; 1168 } 1169 } 1170 1171 MenuItem settings = menu.findItem(R.id.menu_settings); 1172 if (settings != null) { 1173 settings.setVisible(!ContactsPreferenceActivity.isEmpty(this)); 1174 } 1175 1176 return true; 1177 } 1178 1179 @Override 1180 public boolean onOptionsItemSelected(MenuItem item) { 1181 switch (item.getItemId()) { 1182 case R.id.menu_settings: { 1183 final Intent intent = new Intent(this, ContactsPreferenceActivity.class); 1184 startActivity(intent); 1185 return true; 1186 } 1187 case R.id.menu_contacts_filter: { 1188 final Intent intent = new Intent(this, AccountFilterActivity.class); 1189 startActivityForResult(intent, SUBACTIVITY_ACCOUNT_FILTER); 1190 return true; 1191 } 1192 case R.id.menu_search: { 1193 onSearchRequested(); 1194 return true; 1195 } 1196 case R.id.menu_add_contact: { 1197 final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI); 1198 startActivityForResult(intent, SUBACTIVITY_NEW_CONTACT); 1199 return true; 1200 } 1201 case R.id.menu_add_group: { 1202 createNewGroupWithAccountDisambiguation(); 1203 return true; 1204 } 1205 case R.id.menu_import_export: { 1206 ImportExportDialogFragment.show(getFragmentManager()); 1207 return true; 1208 } 1209 case R.id.menu_accounts: { 1210 final Intent intent = new Intent(Settings.ACTION_SYNC_SETTINGS); 1211 intent.putExtra(Settings.EXTRA_AUTHORITIES, new String[] { 1212 ContactsContract.AUTHORITY 1213 }); 1214 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 1215 startActivity(intent); 1216 return true; 1217 } 1218 } 1219 return false; 1220 } 1221 1222 private void createNewGroupWithAccountDisambiguation() { 1223 final ArrayList<Account> accounts = 1224 AccountTypeManager.getInstance(this).getAccounts(true); 1225 if (accounts.size() <= 1 || mAddGroupImageView == null) { 1226 // No account to choose or no control to anchor the popup-menu to 1227 // ==> just go straight to the editor which will disambig if necessary 1228 final Intent intent = new Intent(this, GroupEditorActivity.class); 1229 intent.setAction(Intent.ACTION_INSERT); 1230 startActivityForResult(intent, SUBACTIVITY_NEW_GROUP); 1231 return; 1232 } 1233 1234 final ListPopupWindow popup = new ListPopupWindow(this, null); 1235 popup.setWidth(getResources().getDimensionPixelSize(R.dimen.account_selector_popup_width)); 1236 popup.setAnchorView(mAddGroupImageView); 1237 // Create a list adapter with all writeable accounts (assume that the writeable accounts all 1238 // allow group creation). 1239 final AccountsListAdapter adapter = new AccountsListAdapter(this, true); 1240 popup.setAdapter(adapter); 1241 popup.setOnItemClickListener(new OnItemClickListener() { 1242 @Override 1243 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 1244 popup.dismiss(); 1245 final Intent intent = new Intent(PeopleActivity.this, GroupEditorActivity.class); 1246 intent.setAction(Intent.ACTION_INSERT); 1247 intent.putExtra(Intents.Insert.ACCOUNT, adapter.getItem(position)); 1248 startActivityForResult(intent, SUBACTIVITY_NEW_GROUP); 1249 } 1250 }); 1251 popup.setModal(true); 1252 popup.show(); 1253 } 1254 1255 @Override 1256 public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData, 1257 boolean globalSearch) { 1258 if (mAllFragment != null && mAllFragment.isAdded() && !globalSearch) { 1259 mAllFragment.startSearch(initialQuery); 1260 } else { 1261 super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch); 1262 } 1263 } 1264 1265 @Override 1266 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 1267 switch (requestCode) { 1268 case SUBACTIVITY_ACCOUNT_FILTER: { 1269 if (resultCode == Activity.RESULT_OK) { 1270 ContactListFilter filter = (ContactListFilter) data.getParcelableExtra( 1271 AccountFilterActivity.KEY_EXTRA_CONTACT_LIST_FILTER); 1272 if (filter == null) { 1273 return; 1274 } 1275 // If this is a custom filter, launch the activity to customize the display list 1276 if (filter.filterType == ContactListFilter.FILTER_TYPE_CUSTOM) { 1277 final Intent intent = new Intent(this, 1278 CustomContactListFilterActivity.class); 1279 startActivityForResult(intent, SUBACTIVITY_CUSTOMIZE_FILTER); 1280 } else { 1281 mContactListFilterController.setContactListFilter(filter, true); 1282 } 1283 } 1284 break; 1285 } 1286 case SUBACTIVITY_CUSTOMIZE_FILTER: { 1287 if (resultCode == Activity.RESULT_OK) { 1288 mContactListFilterController.selectCustomFilter(); 1289 } 1290 break; 1291 } 1292 case SUBACTIVITY_EDIT_CONTACT: 1293 case SUBACTIVITY_NEW_CONTACT: { 1294 if (resultCode == RESULT_OK && PhoneCapabilityTester.isUsingTwoPanes(this)) { 1295 mRequest.setActionCode(ContactsRequest.ACTION_VIEW_CONTACT); 1296 mAllFragment.reloadDataAndSetSelectedUri(data.getData()); 1297 } 1298 break; 1299 } 1300 1301 case SUBACTIVITY_NEW_GROUP: 1302 case SUBACTIVITY_EDIT_GROUP: { 1303 if (resultCode == RESULT_OK && PhoneCapabilityTester.isUsingTwoPanes(this)) { 1304 mRequest.setActionCode(ContactsRequest.ACTION_GROUP); 1305 mGroupsFragment.setSelectedUri(data.getData()); 1306 } 1307 break; 1308 } 1309 1310 // TODO: Using the new startActivityWithResultFromFragment API this should not be needed 1311 // anymore 1312 case ContactEntryListFragment.ACTIVITY_REQUEST_CODE_PICKER: 1313 if (resultCode == RESULT_OK) { 1314 mAllFragment.onPickerResult(data); 1315 } 1316 1317// TODO fix or remove multipicker code 1318// else if (resultCode == RESULT_CANCELED && mMode == MODE_PICK_MULTIPLE_PHONES) { 1319// // Finish the activity if the sub activity was canceled as back key is used 1320// // to confirm user selection in MODE_PICK_MULTIPLE_PHONES. 1321// finish(); 1322// } 1323// break; 1324 } 1325 } 1326 1327 @Override 1328 public boolean onKeyDown(int keyCode, KeyEvent event) { 1329 // TODO move to the fragment 1330 switch (keyCode) { 1331// case KeyEvent.KEYCODE_CALL: { 1332// if (callSelection()) { 1333// return true; 1334// } 1335// break; 1336// } 1337 1338 case KeyEvent.KEYCODE_DEL: { 1339 if (deleteSelection()) { 1340 return true; 1341 } 1342 break; 1343 } 1344 default: { 1345 // Bring up the search UI if the user starts typing 1346 final int unicodeChar = event.getUnicodeChar(); 1347 if (unicodeChar != 0 && !Character.isWhitespace(unicodeChar)) { 1348 String query = new String(new int[]{ unicodeChar }, 0, 1); 1349 if (!mActionBarAdapter.isSearchMode()) { 1350 mActionBarAdapter.setQueryString(query); 1351 mActionBarAdapter.setSearchMode(true); 1352 return true; 1353 } else if (!mRequest.isSearchMode()) { 1354 if (!mSearchInitiated) { 1355 mSearchInitiated = true; 1356 startSearch(query, false, null, false); 1357 return true; 1358 } 1359 } 1360 } 1361 } 1362 } 1363 1364 return super.onKeyDown(keyCode, event); 1365 } 1366 1367 @Override 1368 public void onBackPressed() { 1369 if (mSearchMode && mActionBarAdapter != null) { 1370 mActionBarAdapter.setSearchMode(false); 1371 } else { 1372 super.onBackPressed(); 1373 } 1374 } 1375 1376 private boolean deleteSelection() { 1377 // TODO move to the fragment 1378// if (mActionCode == ContactsRequest.ACTION_DEFAULT) { 1379// final int position = mListView.getSelectedItemPosition(); 1380// if (position != ListView.INVALID_POSITION) { 1381// Uri contactUri = getContactUri(position); 1382// if (contactUri != null) { 1383// doContactDelete(contactUri); 1384// return true; 1385// } 1386// } 1387// } 1388 return false; 1389 } 1390 1391 @Override 1392 protected void onSaveInstanceState(Bundle outState) { 1393 super.onSaveInstanceState(outState); 1394 outState.putBoolean(KEY_SEARCH_MODE, mSearchMode); 1395 mActionBarAdapter.onSaveInstanceState(outState); 1396 if (mContactDetailLayoutController != null) { 1397 mContactDetailLayoutController.onSaveInstanceState(outState); 1398 } 1399 1400 // Clear the listener to make sure we don't get callbacks after onSaveInstanceState, 1401 // in order to avoid doing fragment transactions after it. 1402 // TODO Figure out a better way to deal with the issue. 1403 mActionBarAdapter.setListener(null); 1404 } 1405 1406 @Override 1407 protected void onRestoreInstanceState(Bundle inState) { 1408 super.onRestoreInstanceState(inState); 1409 mSearchMode = inState.getBoolean(KEY_SEARCH_MODE); 1410 if (mContactDetailLayoutController != null) { 1411 mContactDetailLayoutController.onRestoreInstanceState(inState); 1412 } 1413 } 1414 1415 @Override 1416 public DialogManager getDialogManager() { 1417 return mDialogManager; 1418 } 1419 1420 // Visible for testing 1421 public ContactBrowseListFragment getListFragment() { 1422 return mAllFragment; 1423 } 1424 1425 // Visible for testing 1426 public ContactDetailFragment getDetailFragment() { 1427 return mContactDetailFragment; 1428 } 1429} 1430