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