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 17 package com.android.contacts.activities; 18 19 import android.app.Fragment; 20 import android.app.FragmentManager; 21 import android.app.FragmentTransaction; 22 import android.content.ActivityNotFoundException; 23 import android.content.Intent; 24 import android.graphics.Rect; 25 import android.net.Uri; 26 import android.os.Bundle; 27 import android.os.Parcelable; 28 import android.os.UserManager; 29 import android.preference.PreferenceActivity; 30 import android.provider.ContactsContract; 31 import android.provider.ContactsContract.Contacts; 32 import android.provider.ContactsContract.ProviderStatus; 33 import android.provider.ContactsContract.QuickContact; 34 import android.provider.Settings; 35 import android.support.v13.app.FragmentPagerAdapter; 36 import android.support.v4.view.PagerAdapter; 37 import android.support.v4.view.ViewPager; 38 import android.text.TextUtils; 39 import android.util.Log; 40 import android.view.KeyCharacterMap; 41 import android.view.KeyEvent; 42 import android.view.Menu; 43 import android.view.MenuInflater; 44 import android.view.MenuItem; 45 import android.view.View; 46 import android.view.ViewGroup; 47 import android.view.Window; 48 import android.widget.ImageButton; 49 import android.widget.Toast; 50 import android.widget.Toolbar; 51 52 import com.android.contacts.ContactsActivity; 53 import com.android.contacts.R; 54 import com.android.contacts.activities.ActionBarAdapter.TabState; 55 import com.android.contacts.common.ContactsUtils; 56 import com.android.contacts.common.dialog.ClearFrequentsDialog; 57 import com.android.contacts.interactions.ContactDeletionInteraction; 58 import com.android.contacts.common.interactions.ImportExportDialogFragment; 59 import com.android.contacts.common.list.ContactEntryListFragment; 60 import com.android.contacts.common.list.ContactListFilter; 61 import com.android.contacts.common.list.ContactListFilterController; 62 import com.android.contacts.common.list.ContactTileAdapter.DisplayType; 63 import com.android.contacts.list.ContactTileListFragment; 64 import com.android.contacts.list.ContactsIntentResolver; 65 import com.android.contacts.list.ContactsRequest; 66 import com.android.contacts.list.ContactsUnavailableFragment; 67 import com.android.contacts.list.DefaultContactBrowseListFragment; 68 import com.android.contacts.common.list.DirectoryListLoader; 69 import com.android.contacts.common.preference.DisplayOptionsPreferenceFragment; 70 import com.android.contacts.list.OnContactBrowserActionListener; 71 import com.android.contacts.list.OnContactsUnavailableActionListener; 72 import com.android.contacts.list.ProviderStatusWatcher; 73 import com.android.contacts.list.ProviderStatusWatcher.ProviderStatusListener; 74 import com.android.contacts.common.list.ViewPagerTabs; 75 import com.android.contacts.preference.ContactsPreferenceActivity; 76 import com.android.contacts.common.util.AccountFilterUtil; 77 import com.android.contacts.common.util.ViewUtil; 78 import com.android.contacts.quickcontact.QuickContactActivity; 79 import com.android.contacts.util.AccountPromptUtils; 80 import com.android.contacts.common.util.Constants; 81 import com.android.contacts.util.DialogManager; 82 import com.android.contacts.util.HelpUtils; 83 84 import java.util.Locale; 85 import java.util.concurrent.atomic.AtomicInteger; 86 87 /** 88 * Displays a list to browse contacts. 89 */ 90 public class PeopleActivity extends ContactsActivity implements 91 View.OnCreateContextMenuListener, 92 View.OnClickListener, 93 ActionBarAdapter.Listener, 94 DialogManager.DialogShowingViewActivity, 95 ContactListFilterController.ContactListFilterListener, 96 ProviderStatusListener { 97 98 private static final String TAG = "PeopleActivity"; 99 100 private static final String ENABLE_DEBUG_OPTIONS_HIDDEN_CODE = "debug debug!"; 101 102 // These values needs to start at 2. See {@link ContactEntryListFragment}. 103 private static final int SUBACTIVITY_ACCOUNT_FILTER = 2; 104 105 private final DialogManager mDialogManager = new DialogManager(this); 106 107 private ContactsIntentResolver mIntentResolver; 108 private ContactsRequest mRequest; 109 110 private ActionBarAdapter mActionBarAdapter; 111 112 private ContactTileListFragment.Listener mFavoritesFragmentListener = 113 new StrequentContactListFragmentListener(); 114 115 private ContactListFilterController mContactListFilterController; 116 117 private ContactsUnavailableFragment mContactsUnavailableFragment; 118 private ProviderStatusWatcher mProviderStatusWatcher; 119 private ProviderStatusWatcher.Status mProviderStatus; 120 121 private boolean mOptionsMenuContactsAvailable; 122 123 /** 124 * Showing a list of Contacts. Also used for showing search results in search mode. 125 */ 126 private DefaultContactBrowseListFragment mAllFragment; 127 private ContactTileListFragment mFavoritesFragment; 128 129 /** ViewPager for swipe */ 130 private ViewPager mTabPager; 131 private ViewPagerTabs mViewPagerTabs; 132 private TabPagerAdapter mTabPagerAdapter; 133 private String[] mTabTitles; 134 private final TabPagerListener mTabPagerListener = new TabPagerListener(); 135 136 private boolean mEnableDebugMenuOptions; 137 138 /** 139 * True if this activity instance is a re-created one. i.e. set true after orientation change. 140 * This is set in {@link #onCreate} for later use in {@link #onStart}. 141 */ 142 private boolean mIsRecreatedInstance; 143 144 /** 145 * If {@link #configureFragments(boolean)} is already called. Used to avoid calling it twice 146 * in {@link #onStart}. 147 * (This initialization only needs to be done once in onStart() when the Activity was just 148 * created from scratch -- i.e. onCreate() was just called) 149 */ 150 private boolean mFragmentInitialized; 151 152 /** 153 * This is to disable {@link #onOptionsItemSelected} when we trying to stop the activity. 154 */ 155 private boolean mDisableOptionItemSelected; 156 157 /** Sequential ID assigned to each instance; used for logging */ 158 private final int mInstanceId; 159 private static final AtomicInteger sNextInstanceId = new AtomicInteger(); 160 PeopleActivity()161 public PeopleActivity() { 162 mInstanceId = sNextInstanceId.getAndIncrement(); 163 mIntentResolver = new ContactsIntentResolver(this); 164 mProviderStatusWatcher = ProviderStatusWatcher.getInstance(this); 165 } 166 167 @Override toString()168 public String toString() { 169 // Shown on logcat 170 return String.format("%s@%d", getClass().getSimpleName(), mInstanceId); 171 } 172 areContactsAvailable()173 public boolean areContactsAvailable() { 174 return (mProviderStatus != null) 175 && mProviderStatus.status == ProviderStatus.STATUS_NORMAL; 176 } 177 areContactWritableAccountsAvailable()178 private boolean areContactWritableAccountsAvailable() { 179 return ContactsUtils.areContactWritableAccountsAvailable(this); 180 } 181 areGroupWritableAccountsAvailable()182 private boolean areGroupWritableAccountsAvailable() { 183 return ContactsUtils.areGroupWritableAccountsAvailable(this); 184 } 185 186 /** 187 * Initialize fragments that are (or may not be) in the layout. 188 * 189 * For the fragments that are in the layout, we initialize them in 190 * {@link #createViewsAndFragments(Bundle)} after inflating the layout. 191 * 192 * However, the {@link ContactsUnavailableFragment} is a special fragment which may not 193 * be in the layout, so we have to do the initialization here. 194 * 195 * The ContactsUnavailableFragment is always created at runtime. 196 */ 197 @Override onAttachFragment(Fragment fragment)198 public void onAttachFragment(Fragment fragment) { 199 if (fragment instanceof ContactsUnavailableFragment) { 200 mContactsUnavailableFragment = (ContactsUnavailableFragment)fragment; 201 mContactsUnavailableFragment.setOnContactsUnavailableActionListener( 202 new ContactsUnavailableFragmentListener()); 203 } 204 } 205 206 @Override onCreate(Bundle savedState)207 protected void onCreate(Bundle savedState) { 208 if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) { 209 Log.d(Constants.PERFORMANCE_TAG, "PeopleActivity.onCreate start"); 210 } 211 super.onCreate(savedState); 212 213 if (!processIntent(false)) { 214 finish(); 215 return; 216 } 217 mContactListFilterController = ContactListFilterController.getInstance(this); 218 mContactListFilterController.checkFilterValidity(false); 219 mContactListFilterController.addListener(this); 220 221 mProviderStatusWatcher.addListener(this); 222 223 mIsRecreatedInstance = (savedState != null); 224 createViewsAndFragments(savedState); 225 226 if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) { 227 Log.d(Constants.PERFORMANCE_TAG, "PeopleActivity.onCreate finish"); 228 } 229 getWindow().setBackgroundDrawable(null); 230 } 231 232 @Override onNewIntent(Intent intent)233 protected void onNewIntent(Intent intent) { 234 setIntent(intent); 235 if (!processIntent(true)) { 236 finish(); 237 return; 238 } 239 mActionBarAdapter.initialize(null, mRequest); 240 241 mContactListFilterController.checkFilterValidity(false); 242 243 // Re-configure fragments. 244 configureFragments(true /* from request */); 245 invalidateOptionsMenuIfNeeded(); 246 } 247 248 /** 249 * Resolve the intent and initialize {@link #mRequest}, and launch another activity if redirect 250 * is needed. 251 * 252 * @param forNewIntent set true if it's called from {@link #onNewIntent(Intent)}. 253 * @return {@code true} if {@link PeopleActivity} should continue running. {@code false} 254 * if it shouldn't, in which case the caller should finish() itself and shouldn't do 255 * farther initialization. 256 */ processIntent(boolean forNewIntent)257 private boolean processIntent(boolean forNewIntent) { 258 // Extract relevant information from the intent 259 mRequest = mIntentResolver.resolveIntent(getIntent()); 260 if (Log.isLoggable(TAG, Log.DEBUG)) { 261 Log.d(TAG, this + " processIntent: forNewIntent=" + forNewIntent 262 + " intent=" + getIntent() + " request=" + mRequest); 263 } 264 if (!mRequest.isValid()) { 265 setResult(RESULT_CANCELED); 266 return false; 267 } 268 269 Intent redirect = mRequest.getRedirectIntent(); 270 if (redirect != null) { 271 // Need to start a different activity 272 startActivity(redirect); 273 return false; 274 } 275 276 if (mRequest.getActionCode() == ContactsRequest.ACTION_VIEW_CONTACT) { 277 redirect = new Intent(this, QuickContactActivity.class); 278 redirect.setAction(Intent.ACTION_VIEW); 279 redirect.setData(mRequest.getContactUri()); 280 startActivity(redirect); 281 return false; 282 } 283 return true; 284 } 285 createViewsAndFragments(Bundle savedState)286 private void createViewsAndFragments(Bundle savedState) { 287 // Disable the ActionBar so that we can use a Toolbar. This needs to be called before 288 // setContentView(). 289 getWindow().requestFeature(Window.FEATURE_NO_TITLE); 290 291 setContentView(R.layout.people_activity); 292 293 final FragmentManager fragmentManager = getFragmentManager(); 294 295 // Hide all tabs (the current tab will later be reshown once a tab is selected) 296 final FragmentTransaction transaction = fragmentManager.beginTransaction(); 297 298 mTabTitles = new String[TabState.COUNT]; 299 mTabTitles[TabState.FAVORITES] = getString(R.string.favorites_tab_label); 300 mTabTitles[TabState.ALL] = getString(R.string.all_contacts_tab_label); 301 mTabPager = getView(R.id.tab_pager); 302 mTabPagerAdapter = new TabPagerAdapter(); 303 mTabPager.setAdapter(mTabPagerAdapter); 304 mTabPager.setOnPageChangeListener(mTabPagerListener); 305 306 // Configure toolbar and toolbar tabs. If in landscape mode, we configure tabs differntly. 307 final Toolbar toolbar = getView(R.id.toolbar); 308 setActionBar(toolbar); 309 final ViewPagerTabs portraitViewPagerTabs 310 = (ViewPagerTabs) findViewById(R.id.lists_pager_header); 311 ViewPagerTabs landscapeViewPagerTabs = null; 312 if (portraitViewPagerTabs == null) { 313 landscapeViewPagerTabs = (ViewPagerTabs) getLayoutInflater().inflate( 314 R.layout.people_activity_tabs_lands, toolbar, /* attachToRoot = */ false); 315 mViewPagerTabs = landscapeViewPagerTabs; 316 } else { 317 mViewPagerTabs = portraitViewPagerTabs; 318 } 319 mViewPagerTabs.setViewPager(mTabPager); 320 321 final String FAVORITE_TAG = "tab-pager-favorite"; 322 final String ALL_TAG = "tab-pager-all"; 323 324 // Create the fragments and add as children of the view pager. 325 // The pager adapter will only change the visibility; it'll never create/destroy 326 // fragments. 327 // However, if it's after screen rotation, the fragments have been re-created by 328 // the fragment manager, so first see if there're already the target fragments 329 // existing. 330 mFavoritesFragment = (ContactTileListFragment) 331 fragmentManager.findFragmentByTag(FAVORITE_TAG); 332 mAllFragment = (DefaultContactBrowseListFragment) 333 fragmentManager.findFragmentByTag(ALL_TAG); 334 335 if (mFavoritesFragment == null) { 336 mFavoritesFragment = new ContactTileListFragment(); 337 mAllFragment = new DefaultContactBrowseListFragment(); 338 339 transaction.add(R.id.tab_pager, mFavoritesFragment, FAVORITE_TAG); 340 transaction.add(R.id.tab_pager, mAllFragment, ALL_TAG); 341 } 342 343 mFavoritesFragment.setListener(mFavoritesFragmentListener); 344 345 mAllFragment.setOnContactListActionListener(new ContactBrowserActionListener()); 346 347 // Hide all fragments for now. We adjust visibility when we get onSelectedTabChanged() 348 // from ActionBarAdapter. 349 transaction.hide(mFavoritesFragment); 350 transaction.hide(mAllFragment); 351 352 transaction.commitAllowingStateLoss(); 353 fragmentManager.executePendingTransactions(); 354 355 // Setting Properties after fragment is created 356 mFavoritesFragment.setDisplayType(DisplayType.STREQUENT); 357 358 mActionBarAdapter = new ActionBarAdapter(this, this, getActionBar(), 359 portraitViewPagerTabs, landscapeViewPagerTabs, toolbar); 360 mActionBarAdapter.initialize(savedState, mRequest); 361 362 // Add shadow under toolbar 363 ViewUtil.addRectangularOutlineProvider(findViewById(R.id.toolbar_parent), getResources()); 364 365 // Configure action button 366 final View floatingActionButtonContainer = findViewById( 367 R.id.floating_action_button_container); 368 ViewUtil.setupFloatingActionButton(floatingActionButtonContainer, getResources()); 369 final ImageButton floatingActionButton = (ImageButton) findViewById(R.id.floating_action_button); 370 floatingActionButton.setOnClickListener(this); 371 372 invalidateOptionsMenuIfNeeded(); 373 } 374 375 @Override onStart()376 protected void onStart() { 377 if (!mFragmentInitialized) { 378 mFragmentInitialized = true; 379 /* Configure fragments if we haven't. 380 * 381 * Note it's a one-shot initialization, so we want to do this in {@link #onCreate}. 382 * 383 * However, because this method may indirectly touch views in fragments but fragments 384 * created in {@link #configureContentView} using a {@link FragmentTransaction} will NOT 385 * have views until {@link Activity#onCreate} finishes (they would if they were inflated 386 * from a layout), we need to do it here in {@link #onStart()}. 387 * 388 * (When {@link Fragment#onCreateView} is called is different in the former case and 389 * in the latter case, unfortunately.) 390 * 391 * Also, we skip most of the work in it if the activity is a re-created one. 392 * (so the argument.) 393 */ 394 configureFragments(!mIsRecreatedInstance); 395 } 396 super.onStart(); 397 } 398 399 @Override onPause()400 protected void onPause() { 401 mOptionsMenuContactsAvailable = false; 402 mProviderStatusWatcher.stop(); 403 super.onPause(); 404 } 405 406 @Override onResume()407 protected void onResume() { 408 super.onResume(); 409 410 mProviderStatusWatcher.start(); 411 updateViewConfiguration(true); 412 413 // Re-register the listener, which may have been cleared when onSaveInstanceState was 414 // called. See also: onSaveInstanceState 415 mActionBarAdapter.setListener(this); 416 mDisableOptionItemSelected = false; 417 if (mTabPager != null) { 418 mTabPager.setOnPageChangeListener(mTabPagerListener); 419 } 420 // Current tab may have changed since the last onSaveInstanceState(). Make sure 421 // the actual contents match the tab. 422 updateFragmentsVisibility(); 423 } 424 425 @Override onStop()426 protected void onStop() { 427 super.onStop(); 428 } 429 430 @Override onDestroy()431 protected void onDestroy() { 432 mProviderStatusWatcher.removeListener(this); 433 434 // Some of variables will be null if this Activity redirects Intent. 435 // See also onCreate() or other methods called during the Activity's initialization. 436 if (mActionBarAdapter != null) { 437 mActionBarAdapter.setListener(null); 438 } 439 if (mContactListFilterController != null) { 440 mContactListFilterController.removeListener(this); 441 } 442 443 super.onDestroy(); 444 } 445 configureFragments(boolean fromRequest)446 private void configureFragments(boolean fromRequest) { 447 if (fromRequest) { 448 ContactListFilter filter = null; 449 int actionCode = mRequest.getActionCode(); 450 boolean searchMode = mRequest.isSearchMode(); 451 final int tabToOpen; 452 switch (actionCode) { 453 case ContactsRequest.ACTION_ALL_CONTACTS: 454 filter = ContactListFilter.createFilterWithType( 455 ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS); 456 tabToOpen = TabState.ALL; 457 break; 458 case ContactsRequest.ACTION_CONTACTS_WITH_PHONES: 459 filter = ContactListFilter.createFilterWithType( 460 ContactListFilter.FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY); 461 tabToOpen = TabState.ALL; 462 break; 463 464 case ContactsRequest.ACTION_FREQUENT: 465 case ContactsRequest.ACTION_STREQUENT: 466 case ContactsRequest.ACTION_STARRED: 467 tabToOpen = TabState.FAVORITES; 468 break; 469 case ContactsRequest.ACTION_VIEW_CONTACT: 470 tabToOpen = TabState.ALL; 471 break; 472 default: 473 tabToOpen = -1; 474 break; 475 } 476 if (tabToOpen != -1) { 477 mActionBarAdapter.setCurrentTab(tabToOpen); 478 } 479 480 if (filter != null) { 481 mContactListFilterController.setContactListFilter(filter, false); 482 searchMode = false; 483 } 484 485 if (mRequest.getContactUri() != null) { 486 searchMode = false; 487 } 488 489 mActionBarAdapter.setSearchMode(searchMode); 490 configureContactListFragmentForRequest(); 491 } 492 493 configureContactListFragment(); 494 495 invalidateOptionsMenuIfNeeded(); 496 } 497 498 @Override onContactListFilterChanged()499 public void onContactListFilterChanged() { 500 if (mAllFragment == null || !mAllFragment.isAdded()) { 501 return; 502 } 503 504 mAllFragment.setFilter(mContactListFilterController.getFilter()); 505 506 invalidateOptionsMenuIfNeeded(); 507 } 508 509 /** 510 * Handler for action bar actions. 511 */ 512 @Override onAction(int action)513 public void onAction(int action) { 514 switch (action) { 515 case ActionBarAdapter.Listener.Action.START_SEARCH_MODE: 516 // Tell the fragments that we're in the search mode 517 configureFragments(false /* from request */); 518 updateFragmentsVisibility(); 519 invalidateOptionsMenu(); 520 break; 521 case ActionBarAdapter.Listener.Action.STOP_SEARCH_MODE: 522 setQueryTextToFragment(""); 523 updateFragmentsVisibility(); 524 invalidateOptionsMenu(); 525 break; 526 case ActionBarAdapter.Listener.Action.CHANGE_SEARCH_QUERY: 527 final String queryString = mActionBarAdapter.getQueryString(); 528 setQueryTextToFragment(queryString); 529 updateDebugOptionsVisibility( 530 ENABLE_DEBUG_OPTIONS_HIDDEN_CODE.equals(queryString)); 531 break; 532 default: 533 throw new IllegalStateException("Unkonwn ActionBarAdapter action: " + action); 534 } 535 } 536 537 @Override onSelectedTabChanged()538 public void onSelectedTabChanged() { 539 updateFragmentsVisibility(); 540 } 541 542 @Override onUpButtonPressed()543 public void onUpButtonPressed() { 544 onBackPressed(); 545 } 546 updateDebugOptionsVisibility(boolean visible)547 private void updateDebugOptionsVisibility(boolean visible) { 548 if (mEnableDebugMenuOptions != visible) { 549 mEnableDebugMenuOptions = visible; 550 invalidateOptionsMenu(); 551 } 552 } 553 554 /** 555 * Updates the fragment/view visibility according to the current mode, such as 556 * {@link ActionBarAdapter#isSearchMode()} and {@link ActionBarAdapter#getCurrentTab()}. 557 */ updateFragmentsVisibility()558 private void updateFragmentsVisibility() { 559 int tab = mActionBarAdapter.getCurrentTab(); 560 561 if (mActionBarAdapter.isSearchMode()) { 562 mTabPagerAdapter.setSearchMode(true); 563 } else { 564 // No smooth scrolling if quitting from the search mode. 565 final boolean wasSearchMode = mTabPagerAdapter.isSearchMode(); 566 mTabPagerAdapter.setSearchMode(false); 567 if (mTabPager.getCurrentItem() != tab) { 568 mTabPager.setCurrentItem(tab, !wasSearchMode); 569 } 570 } 571 invalidateOptionsMenu(); 572 showEmptyStateForTab(tab); 573 } 574 showEmptyStateForTab(int tab)575 private void showEmptyStateForTab(int tab) { 576 if (mContactsUnavailableFragment != null) { 577 switch (getTabPositionForTextDirection(tab)) { 578 case TabState.FAVORITES: 579 mContactsUnavailableFragment.setMessageText( 580 R.string.listTotalAllContactsZeroStarred, -1); 581 break; 582 case TabState.ALL: 583 mContactsUnavailableFragment.setMessageText(R.string.noContacts, -1); 584 break; 585 } 586 // When using the mContactsUnavailableFragment the ViewPager doesn't contain two views. 587 // Therefore, we have to trick the ViewPagerTabs into thinking we have changed tabs 588 // when the mContactsUnavailableFragment changes. Otherwise the tab strip won't move. 589 mViewPagerTabs.onPageScrolled(tab, 0, 0); 590 } 591 } 592 593 private class TabPagerListener implements ViewPager.OnPageChangeListener { 594 595 // This package-protected constructor is here because of a possible compiler bug. 596 // PeopleActivity$1.class should be generated due to the private outer/inner class access 597 // needed here. But for some reason, PeopleActivity$1.class is missing. 598 // Since $1 class is needed as a jvm work around to get access to the inner class, 599 // changing the constructor to package-protected or public will solve the problem. 600 // To verify whether $1 class is needed, javap PeopleActivity$TabPagerListener and look for 601 // references to PeopleActivity$1. 602 // 603 // When the constructor is private and PeopleActivity$1.class is missing, proguard will 604 // correctly catch this and throw warnings and error out the build on user/userdebug builds. 605 // 606 // All private inner classes below also need this fix. TabPagerListener()607 TabPagerListener() {} 608 609 @Override onPageScrollStateChanged(int state)610 public void onPageScrollStateChanged(int state) { 611 if (!mTabPagerAdapter.isSearchMode()) { 612 mViewPagerTabs.onPageScrollStateChanged(state); 613 } 614 } 615 616 @Override onPageScrolled(int position, float positionOffset, int positionOffsetPixels)617 public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 618 if (!mTabPagerAdapter.isSearchMode()) { 619 mViewPagerTabs.onPageScrolled(position, positionOffset, positionOffsetPixels); 620 } 621 } 622 623 @Override onPageSelected(int position)624 public void onPageSelected(int position) { 625 // Make sure not in the search mode, in which case position != TabState.ordinal(). 626 if (!mTabPagerAdapter.isSearchMode()) { 627 mActionBarAdapter.setCurrentTab(position, false); 628 mViewPagerTabs.onPageSelected(position); 629 showEmptyStateForTab(position); 630 invalidateOptionsMenu(); 631 } 632 } 633 } 634 635 /** 636 * Adapter for the {@link ViewPager}. Unlike {@link FragmentPagerAdapter}, 637 * {@link #instantiateItem} returns existing fragments, and {@link #instantiateItem}/ 638 * {@link #destroyItem} show/hide fragments instead of attaching/detaching. 639 * 640 * In search mode, we always show the "all" fragment, and disable the swipe. We change the 641 * number of items to 1 to disable the swipe. 642 * 643 * TODO figure out a more straight way to disable swipe. 644 */ 645 private class TabPagerAdapter extends PagerAdapter { 646 private final FragmentManager mFragmentManager; 647 private FragmentTransaction mCurTransaction = null; 648 649 private boolean mTabPagerAdapterSearchMode; 650 651 private Fragment mCurrentPrimaryItem; 652 TabPagerAdapter()653 public TabPagerAdapter() { 654 mFragmentManager = getFragmentManager(); 655 } 656 isSearchMode()657 public boolean isSearchMode() { 658 return mTabPagerAdapterSearchMode; 659 } 660 setSearchMode(boolean searchMode)661 public void setSearchMode(boolean searchMode) { 662 if (searchMode == mTabPagerAdapterSearchMode) { 663 return; 664 } 665 mTabPagerAdapterSearchMode = searchMode; 666 notifyDataSetChanged(); 667 } 668 669 @Override getCount()670 public int getCount() { 671 return mTabPagerAdapterSearchMode ? 1 : TabState.COUNT; 672 } 673 674 /** Gets called when the number of items changes. */ 675 @Override getItemPosition(Object object)676 public int getItemPosition(Object object) { 677 if (mTabPagerAdapterSearchMode) { 678 if (object == mAllFragment) { 679 return 0; // Only 1 page in search mode 680 } 681 } else { 682 if (object == mFavoritesFragment) { 683 return getTabPositionForTextDirection(TabState.FAVORITES); 684 } 685 if (object == mAllFragment) { 686 return getTabPositionForTextDirection(TabState.ALL); 687 } 688 } 689 return POSITION_NONE; 690 } 691 692 @Override startUpdate(ViewGroup container)693 public void startUpdate(ViewGroup container) { 694 } 695 getFragment(int position)696 private Fragment getFragment(int position) { 697 position = getTabPositionForTextDirection(position); 698 if (mTabPagerAdapterSearchMode) { 699 if (position != 0) { 700 // This has only been observed in monkey tests. 701 // Let's log this issue, but not crash 702 Log.w(TAG, "Request fragment at position=" + position + ", eventhough we " + 703 "are in search mode"); 704 } 705 return mAllFragment; 706 } else { 707 if (position == TabState.FAVORITES) { 708 return mFavoritesFragment; 709 } else if (position == TabState.ALL) { 710 return mAllFragment; 711 } 712 } 713 throw new IllegalArgumentException("position: " + position); 714 } 715 716 @Override instantiateItem(ViewGroup container, int position)717 public Object instantiateItem(ViewGroup container, int position) { 718 if (mCurTransaction == null) { 719 mCurTransaction = mFragmentManager.beginTransaction(); 720 } 721 Fragment f = getFragment(position); 722 mCurTransaction.show(f); 723 724 // Non primary pages are not visible. 725 f.setUserVisibleHint(f == mCurrentPrimaryItem); 726 return f; 727 } 728 729 @Override destroyItem(ViewGroup container, int position, Object object)730 public void destroyItem(ViewGroup container, int position, Object object) { 731 if (mCurTransaction == null) { 732 mCurTransaction = mFragmentManager.beginTransaction(); 733 } 734 mCurTransaction.hide((Fragment) object); 735 } 736 737 @Override finishUpdate(ViewGroup container)738 public void finishUpdate(ViewGroup container) { 739 if (mCurTransaction != null) { 740 mCurTransaction.commitAllowingStateLoss(); 741 mCurTransaction = null; 742 mFragmentManager.executePendingTransactions(); 743 } 744 } 745 746 @Override isViewFromObject(View view, Object object)747 public boolean isViewFromObject(View view, Object object) { 748 return ((Fragment) object).getView() == view; 749 } 750 751 @Override setPrimaryItem(ViewGroup container, int position, Object object)752 public void setPrimaryItem(ViewGroup container, int position, Object object) { 753 Fragment fragment = (Fragment) object; 754 if (mCurrentPrimaryItem != fragment) { 755 if (mCurrentPrimaryItem != null) { 756 mCurrentPrimaryItem.setUserVisibleHint(false); 757 } 758 if (fragment != null) { 759 fragment.setUserVisibleHint(true); 760 } 761 mCurrentPrimaryItem = fragment; 762 } 763 } 764 765 @Override saveState()766 public Parcelable saveState() { 767 return null; 768 } 769 770 @Override restoreState(Parcelable state, ClassLoader loader)771 public void restoreState(Parcelable state, ClassLoader loader) { 772 } 773 774 @Override getPageTitle(int position)775 public CharSequence getPageTitle(int position) { 776 return mTabTitles[position]; 777 } 778 } 779 setQueryTextToFragment(String query)780 private void setQueryTextToFragment(String query) { 781 mAllFragment.setQueryString(query, true); 782 mAllFragment.setVisibleScrollbarEnabled(!mAllFragment.isSearchMode()); 783 } 784 configureContactListFragmentForRequest()785 private void configureContactListFragmentForRequest() { 786 Uri contactUri = mRequest.getContactUri(); 787 if (contactUri != null) { 788 mAllFragment.setSelectedContactUri(contactUri); 789 } 790 791 mAllFragment.setFilter(mContactListFilterController.getFilter()); 792 setQueryTextToFragment(mActionBarAdapter.getQueryString()); 793 794 if (mRequest.isDirectorySearchEnabled()) { 795 mAllFragment.setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_DEFAULT); 796 } else { 797 mAllFragment.setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_NONE); 798 } 799 } 800 configureContactListFragment()801 private void configureContactListFragment() { 802 // Filter may be changed when this Activity is in background. 803 mAllFragment.setFilter(mContactListFilterController.getFilter()); 804 805 mAllFragment.setVerticalScrollbarPosition(getScrollBarPosition()); 806 mAllFragment.setSelectionVisible(false); 807 } 808 getScrollBarPosition()809 private int getScrollBarPosition() { 810 return isRTL() ? View.SCROLLBAR_POSITION_LEFT : View.SCROLLBAR_POSITION_RIGHT; 811 } 812 isRTL()813 private boolean isRTL() { 814 final Locale locale = Locale.getDefault(); 815 return TextUtils.getLayoutDirectionFromLocale(locale) == View.LAYOUT_DIRECTION_RTL; 816 } 817 818 @Override onProviderStatusChange()819 public void onProviderStatusChange() { 820 updateViewConfiguration(false); 821 } 822 updateViewConfiguration(boolean forceUpdate)823 private void updateViewConfiguration(boolean forceUpdate) { 824 ProviderStatusWatcher.Status providerStatus = mProviderStatusWatcher.getProviderStatus(); 825 if (!forceUpdate && (mProviderStatus != null) 826 && (providerStatus.status == mProviderStatus.status)) return; 827 mProviderStatus = providerStatus; 828 829 View contactsUnavailableView = findViewById(R.id.contacts_unavailable_view); 830 831 if (mProviderStatus.status == ProviderStatus.STATUS_NORMAL) { 832 // Ensure that the mTabPager is visible; we may have made it invisible below. 833 contactsUnavailableView.setVisibility(View.GONE); 834 if (mTabPager != null) { 835 mTabPager.setVisibility(View.VISIBLE); 836 } 837 838 if (mAllFragment != null) { 839 mAllFragment.setEnabled(true); 840 } 841 } else { 842 // If there are no accounts on the device and we should show the "no account" prompt 843 // (based on {@link SharedPreferences}), then launch the account setup activity so the 844 // user can sign-in or create an account. 845 // 846 // Also check for ability to modify accounts. In limited user mode, you can't modify 847 // accounts so there is no point sending users to account setup activity. 848 final UserManager userManager = UserManager.get(this); 849 final boolean disallowModifyAccounts = userManager.getUserRestrictions().getBoolean( 850 UserManager.DISALLOW_MODIFY_ACCOUNTS); 851 if (!disallowModifyAccounts && !areContactWritableAccountsAvailable() && 852 AccountPromptUtils.shouldShowAccountPrompt(this)) { 853 AccountPromptUtils.neverShowAccountPromptAgain(this); 854 AccountPromptUtils.launchAccountPrompt(this); 855 return; 856 } 857 858 // Otherwise, continue setting up the page so that the user can still use the app 859 // without an account. 860 if (mAllFragment != null) { 861 mAllFragment.setEnabled(false); 862 } 863 if (mContactsUnavailableFragment == null) { 864 mContactsUnavailableFragment = new ContactsUnavailableFragment(); 865 mContactsUnavailableFragment.setOnContactsUnavailableActionListener( 866 new ContactsUnavailableFragmentListener()); 867 getFragmentManager().beginTransaction() 868 .replace(R.id.contacts_unavailable_container, mContactsUnavailableFragment) 869 .commitAllowingStateLoss(); 870 } 871 mContactsUnavailableFragment.updateStatus(mProviderStatus); 872 873 // Show the contactsUnavailableView, and hide the mTabPager so that we don't 874 // see it sliding in underneath the contactsUnavailableView at the edges. 875 contactsUnavailableView.setVisibility(View.VISIBLE); 876 if (mTabPager != null) { 877 mTabPager.setVisibility(View.GONE); 878 } 879 880 showEmptyStateForTab(mActionBarAdapter.getCurrentTab()); 881 } 882 883 invalidateOptionsMenuIfNeeded(); 884 } 885 886 private final class ContactBrowserActionListener implements OnContactBrowserActionListener { ContactBrowserActionListener()887 ContactBrowserActionListener() {} 888 889 @Override onSelectionChange()890 public void onSelectionChange() { 891 892 } 893 894 @Override onViewContactAction(Uri contactLookupUri)895 public void onViewContactAction(Uri contactLookupUri) { 896 Intent intent = QuickContact.composeQuickContactsIntent(PeopleActivity.this, 897 (Rect) null, contactLookupUri, QuickContactActivity.MODE_FULLY_EXPANDED, null); 898 startActivity(intent); 899 } 900 901 @Override onDeleteContactAction(Uri contactUri)902 public void onDeleteContactAction(Uri contactUri) { 903 ContactDeletionInteraction.start(PeopleActivity.this, contactUri, false); 904 } 905 906 @Override onFinishAction()907 public void onFinishAction() { 908 onBackPressed(); 909 } 910 911 @Override onInvalidSelection()912 public void onInvalidSelection() { 913 ContactListFilter filter; 914 ContactListFilter currentFilter = mAllFragment.getFilter(); 915 if (currentFilter != null 916 && currentFilter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) { 917 filter = ContactListFilter.createFilterWithType( 918 ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS); 919 mAllFragment.setFilter(filter); 920 } else { 921 filter = ContactListFilter.createFilterWithType( 922 ContactListFilter.FILTER_TYPE_SINGLE_CONTACT); 923 mAllFragment.setFilter(filter, false); 924 } 925 mContactListFilterController.setContactListFilter(filter, true); 926 } 927 } 928 929 private class ContactsUnavailableFragmentListener 930 implements OnContactsUnavailableActionListener { ContactsUnavailableFragmentListener()931 ContactsUnavailableFragmentListener() {} 932 933 @Override onCreateNewContactAction()934 public void onCreateNewContactAction() { 935 startActivity(new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI)); 936 } 937 938 @Override onAddAccountAction()939 public void onAddAccountAction() { 940 Intent intent = new Intent(Settings.ACTION_ADD_ACCOUNT); 941 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 942 intent.putExtra(Settings.EXTRA_AUTHORITIES, 943 new String[] { ContactsContract.AUTHORITY }); 944 startActivity(intent); 945 } 946 947 @Override onImportContactsFromFileAction()948 public void onImportContactsFromFileAction() { 949 ImportExportDialogFragment.show(getFragmentManager(), areContactsAvailable(), 950 PeopleActivity.class); 951 } 952 953 @Override onFreeInternalStorageAction()954 public void onFreeInternalStorageAction() { 955 startActivity(new Intent(Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS)); 956 } 957 } 958 959 private final class StrequentContactListFragmentListener 960 implements ContactTileListFragment.Listener { StrequentContactListFragmentListener()961 StrequentContactListFragmentListener() {} 962 963 @Override onContactSelected(Uri contactUri, Rect targetRect)964 public void onContactSelected(Uri contactUri, Rect targetRect) { 965 Intent intent = QuickContact.composeQuickContactsIntent(PeopleActivity.this, 966 targetRect, contactUri, QuickContactActivity.MODE_FULLY_EXPANDED, null); 967 startActivity(intent); 968 } 969 970 @Override onCallNumberDirectly(String phoneNumber)971 public void onCallNumberDirectly(String phoneNumber) { 972 // No need to call phone number directly from People app. 973 Log.w(TAG, "unexpected invocation of onCallNumberDirectly()"); 974 } 975 } 976 977 @Override onCreateOptionsMenu(Menu menu)978 public boolean onCreateOptionsMenu(Menu menu) { 979 if (!areContactsAvailable()) { 980 // If contacts aren't available, hide all menu items. 981 return false; 982 } 983 super.onCreateOptionsMenu(menu); 984 985 MenuInflater inflater = getMenuInflater(); 986 inflater.inflate(R.menu.people_options, menu); 987 988 return true; 989 } 990 invalidateOptionsMenuIfNeeded()991 private void invalidateOptionsMenuIfNeeded() { 992 if (isOptionsMenuChanged()) { 993 invalidateOptionsMenu(); 994 } 995 } 996 isOptionsMenuChanged()997 public boolean isOptionsMenuChanged() { 998 if (mOptionsMenuContactsAvailable != areContactsAvailable()) { 999 return true; 1000 } 1001 1002 if (mAllFragment != null && mAllFragment.isOptionsMenuChanged()) { 1003 return true; 1004 } 1005 1006 return false; 1007 } 1008 1009 @Override onPrepareOptionsMenu(Menu menu)1010 public boolean onPrepareOptionsMenu(Menu menu) { 1011 mOptionsMenuContactsAvailable = areContactsAvailable(); 1012 if (!mOptionsMenuContactsAvailable) { 1013 return false; 1014 } 1015 1016 // Get references to individual menu items in the menu 1017 final MenuItem contactsFilterMenu = menu.findItem(R.id.menu_contacts_filter); 1018 final MenuItem clearFrequentsMenu = menu.findItem(R.id.menu_clear_frequents); 1019 final MenuItem helpMenu = menu.findItem(R.id.menu_help); 1020 1021 final boolean isSearchMode = mActionBarAdapter.isSearchMode(); 1022 if (isSearchMode) { 1023 contactsFilterMenu.setVisible(false); 1024 clearFrequentsMenu.setVisible(false); 1025 helpMenu.setVisible(false); 1026 } else { 1027 switch (getTabPositionForTextDirection(mActionBarAdapter.getCurrentTab())) { 1028 case TabState.FAVORITES: 1029 contactsFilterMenu.setVisible(false); 1030 clearFrequentsMenu.setVisible(hasFrequents()); 1031 break; 1032 case TabState.ALL: 1033 contactsFilterMenu.setVisible(true); 1034 clearFrequentsMenu.setVisible(false); 1035 break; 1036 } 1037 HelpUtils.prepareHelpMenuItem(this, helpMenu, R.string.help_url_people_main); 1038 } 1039 final boolean showMiscOptions = !isSearchMode; 1040 makeMenuItemVisible(menu, R.id.menu_search, showMiscOptions); 1041 makeMenuItemVisible(menu, R.id.menu_import_export, showMiscOptions); 1042 makeMenuItemVisible(menu, R.id.menu_accounts, showMiscOptions); 1043 makeMenuItemVisible(menu, R.id.menu_settings, 1044 showMiscOptions && !ContactsPreferenceActivity.isEmpty(this)); 1045 1046 // Debug options need to be visible even in search mode. 1047 makeMenuItemVisible(menu, R.id.export_database, mEnableDebugMenuOptions); 1048 1049 return true; 1050 } 1051 1052 /** 1053 * Returns whether there are any frequently contacted people being displayed 1054 * @return 1055 */ hasFrequents()1056 private boolean hasFrequents() { 1057 return mFavoritesFragment.hasFrequents(); 1058 } 1059 makeMenuItemVisible(Menu menu, int itemId, boolean visible)1060 private void makeMenuItemVisible(Menu menu, int itemId, boolean visible) { 1061 MenuItem item =menu.findItem(itemId); 1062 if (item != null) { 1063 item.setVisible(visible); 1064 } 1065 } 1066 1067 @Override onOptionsItemSelected(MenuItem item)1068 public boolean onOptionsItemSelected(MenuItem item) { 1069 if (mDisableOptionItemSelected) { 1070 return false; 1071 } 1072 1073 switch (item.getItemId()) { 1074 case android.R.id.home: { 1075 // The home icon on the action bar is pressed 1076 if (mActionBarAdapter.isUpShowing()) { 1077 // "UP" icon press -- should be treated as "back". 1078 onBackPressed(); 1079 } 1080 return true; 1081 } 1082 case R.id.menu_settings: { 1083 final Intent intent = new Intent(this, ContactsPreferenceActivity.class); 1084 // Since there is only one section right now, make sure it is selected on 1085 // small screens. 1086 intent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT, 1087 DisplayOptionsPreferenceFragment.class.getName()); 1088 // By default, the title of the activity should be equivalent to the fragment 1089 // title. We set this argument to avoid this. Because of a bug, the following 1090 // line isn't necessary. But, once the bug is fixed this may become necessary. 1091 // b/5045558 refers to this issue, as well as another. 1092 intent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT_TITLE, 1093 R.string.activity_title_settings); 1094 startActivity(intent); 1095 return true; 1096 } 1097 case R.id.menu_contacts_filter: { 1098 AccountFilterUtil.startAccountFilterActivityForResult( 1099 this, SUBACTIVITY_ACCOUNT_FILTER, 1100 mContactListFilterController.getFilter()); 1101 return true; 1102 } 1103 case R.id.menu_search: { 1104 onSearchRequested(); 1105 return true; 1106 } 1107 case R.id.menu_import_export: { 1108 ImportExportDialogFragment.show(getFragmentManager(), areContactsAvailable(), 1109 PeopleActivity.class); 1110 return true; 1111 } 1112 case R.id.menu_clear_frequents: { 1113 ClearFrequentsDialog.show(getFragmentManager()); 1114 return true; 1115 } 1116 case R.id.menu_accounts: { 1117 final Intent intent = new Intent(Settings.ACTION_SYNC_SETTINGS); 1118 intent.putExtra(Settings.EXTRA_AUTHORITIES, new String[] { 1119 ContactsContract.AUTHORITY 1120 }); 1121 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 1122 startActivity(intent); 1123 return true; 1124 } 1125 case R.id.export_database: { 1126 final Intent intent = new Intent("com.android.providers.contacts.DUMP_DATABASE"); 1127 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 1128 startActivity(intent); 1129 return true; 1130 } 1131 } 1132 return false; 1133 } 1134 1135 @Override onSearchRequested()1136 public boolean onSearchRequested() { // Search key pressed. 1137 mActionBarAdapter.setSearchMode(true); 1138 return true; 1139 } 1140 1141 @Override onActivityResult(int requestCode, int resultCode, Intent data)1142 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 1143 switch (requestCode) { 1144 case SUBACTIVITY_ACCOUNT_FILTER: { 1145 AccountFilterUtil.handleAccountFilterResult( 1146 mContactListFilterController, resultCode, data); 1147 break; 1148 } 1149 1150 // TODO: Using the new startActivityWithResultFromFragment API this should not be needed 1151 // anymore 1152 case ContactEntryListFragment.ACTIVITY_REQUEST_CODE_PICKER: 1153 if (resultCode == RESULT_OK) { 1154 mAllFragment.onPickerResult(data); 1155 } 1156 1157 // TODO fix or remove multipicker code 1158 // else if (resultCode == RESULT_CANCELED && mMode == MODE_PICK_MULTIPLE_PHONES) { 1159 // // Finish the activity if the sub activity was canceled as back key is used 1160 // // to confirm user selection in MODE_PICK_MULTIPLE_PHONES. 1161 // finish(); 1162 // } 1163 // break; 1164 } 1165 } 1166 1167 @Override onKeyDown(int keyCode, KeyEvent event)1168 public boolean onKeyDown(int keyCode, KeyEvent event) { 1169 // TODO move to the fragment 1170 switch (keyCode) { 1171 // case KeyEvent.KEYCODE_CALL: { 1172 // if (callSelection()) { 1173 // return true; 1174 // } 1175 // break; 1176 // } 1177 1178 case KeyEvent.KEYCODE_DEL: { 1179 if (deleteSelection()) { 1180 return true; 1181 } 1182 break; 1183 } 1184 default: { 1185 // Bring up the search UI if the user starts typing 1186 final int unicodeChar = event.getUnicodeChar(); 1187 if ((unicodeChar != 0) 1188 // If COMBINING_ACCENT is set, it's not a unicode character. 1189 && ((unicodeChar & KeyCharacterMap.COMBINING_ACCENT) == 0) 1190 && !Character.isWhitespace(unicodeChar)) { 1191 String query = new String(new int[]{ unicodeChar }, 0, 1); 1192 if (!mActionBarAdapter.isSearchMode()) { 1193 mActionBarAdapter.setSearchMode(true); 1194 mActionBarAdapter.setQueryString(query); 1195 return true; 1196 } 1197 } 1198 } 1199 } 1200 1201 return super.onKeyDown(keyCode, event); 1202 } 1203 1204 @Override onBackPressed()1205 public void onBackPressed() { 1206 if (mActionBarAdapter.isSearchMode()) { 1207 mActionBarAdapter.setSearchMode(false); 1208 } else { 1209 super.onBackPressed(); 1210 } 1211 } 1212 deleteSelection()1213 private boolean deleteSelection() { 1214 // TODO move to the fragment 1215 // if (mActionCode == ContactsRequest.ACTION_DEFAULT) { 1216 // final int position = mListView.getSelectedItemPosition(); 1217 // if (position != ListView.INVALID_POSITION) { 1218 // Uri contactUri = getContactUri(position); 1219 // if (contactUri != null) { 1220 // doContactDelete(contactUri); 1221 // return true; 1222 // } 1223 // } 1224 // } 1225 return false; 1226 } 1227 1228 @Override onSaveInstanceState(Bundle outState)1229 protected void onSaveInstanceState(Bundle outState) { 1230 super.onSaveInstanceState(outState); 1231 mActionBarAdapter.onSaveInstanceState(outState); 1232 1233 // Clear the listener to make sure we don't get callbacks after onSaveInstanceState, 1234 // in order to avoid doing fragment transactions after it. 1235 // TODO Figure out a better way to deal with the issue. 1236 mDisableOptionItemSelected = true; 1237 mActionBarAdapter.setListener(null); 1238 if (mTabPager != null) { 1239 mTabPager.setOnPageChangeListener(null); 1240 } 1241 } 1242 1243 @Override onRestoreInstanceState(Bundle savedInstanceState)1244 protected void onRestoreInstanceState(Bundle savedInstanceState) { 1245 super.onRestoreInstanceState(savedInstanceState); 1246 // In our own lifecycle, the focus is saved and restore but later taken away by the 1247 // ViewPager. As a hack, we force focus on the SearchView if we know that we are searching. 1248 // This fixes the keyboard going away on screen rotation 1249 if (mActionBarAdapter.isSearchMode()) { 1250 mActionBarAdapter.setFocusOnSearchView(); 1251 } 1252 } 1253 1254 @Override getDialogManager()1255 public DialogManager getDialogManager() { 1256 return mDialogManager; 1257 } 1258 1259 @Override onClick(View view)1260 public void onClick(View view) { 1261 switch (view.getId()) { 1262 case R.id.floating_action_button: 1263 Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI); 1264 Bundle extras = getIntent().getExtras(); 1265 if (extras != null) { 1266 intent.putExtras(extras); 1267 } 1268 try { 1269 startActivity(intent); 1270 } catch (ActivityNotFoundException ex) { 1271 Toast.makeText(PeopleActivity.this, R.string.missing_app, 1272 Toast.LENGTH_SHORT).show(); 1273 } 1274 break; 1275 default: 1276 Log.wtf(TAG, "Unexpected onClick event from " + view); 1277 } 1278 } 1279 1280 /** 1281 * Returns the tab position adjusted for the text direction. 1282 */ getTabPositionForTextDirection(int position)1283 private int getTabPositionForTextDirection(int position) { 1284 if (isRTL()) { 1285 return TabState.COUNT - 1 - position; 1286 } 1287 return position; 1288 } 1289 } 1290