DialtactsActivity.java revision 18f5f79fa3991a864a17596fe14a04045a7540eb
1/* 2 * Copyright (C) 2008 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.R; 20import com.android.contacts.calllog.CallLogFragment; 21import com.android.contacts.dialpad.DialpadFragment; 22import com.android.contacts.interactions.PhoneNumberInteraction; 23import com.android.contacts.list.AccountFilterActivity; 24import com.android.contacts.list.ContactListFilter; 25import com.android.contacts.list.ContactListFilterController; 26import com.android.contacts.list.ContactListFilterController.ContactListFilterListener; 27import com.android.contacts.list.ContactTileAdapter.DisplayType; 28import com.android.contacts.list.ContactTileListFragment; 29import com.android.contacts.list.OnPhoneNumberPickerActionListener; 30import com.android.contacts.list.PhoneNumberPickerFragment; 31import com.android.internal.telephony.ITelephony; 32 33import android.app.ActionBar; 34import android.app.ActionBar.LayoutParams; 35import android.app.ActionBar.Tab; 36import android.app.ActionBar.TabListener; 37import android.app.Activity; 38import android.app.Fragment; 39import android.app.FragmentManager; 40import android.app.FragmentTransaction; 41import android.content.Context; 42import android.content.Intent; 43import android.content.SharedPreferences; 44import android.net.Uri; 45import android.os.Bundle; 46import android.os.RemoteException; 47import android.os.ServiceManager; 48import android.provider.CallLog.Calls; 49import android.provider.ContactsContract.Contacts; 50import android.provider.ContactsContract.Intents.UI; 51import android.support.v13.app.FragmentPagerAdapter; 52import android.support.v4.view.ViewPager; 53import android.support.v4.view.ViewPager.OnPageChangeListener; 54import android.text.TextUtils; 55import android.util.Log; 56import android.view.Menu; 57import android.view.MenuInflater; 58import android.view.MenuItem; 59import android.view.MenuItem.OnMenuItemClickListener; 60import android.view.View; 61import android.view.View.OnClickListener; 62import android.view.ViewConfiguration; 63import android.view.inputmethod.InputMethodManager; 64import android.widget.PopupMenu; 65import android.widget.SearchView; 66import android.widget.SearchView.OnCloseListener; 67import android.widget.SearchView.OnQueryTextListener; 68 69/** 70 * The dialer activity that has one tab with the virtual 12key 71 * dialer, a tab with recent calls in it, a tab with the contacts and 72 * a tab with the favorite. This is the container and the tabs are 73 * embedded using intents. 74 * The dialer tab's title is 'phone', a more common name (see strings.xml). 75 */ 76public class DialtactsActivity extends Activity { 77 private static final String TAG = "DialtactsActivity"; 78 79 /** Used to open Call Setting */ 80 private static final String PHONE_PACKAGE = "com.android.phone"; 81 private static final String CALL_SETTINGS_CLASS_NAME = 82 "com.android.phone.CallFeaturesSetting"; 83 84 /** 85 * Just for backward compatibility. Should behave as same as {@link Intent#ACTION_DIAL}. 86 */ 87 private static final String ACTION_TOUCH_DIALER = "com.android.phone.action.TOUCH_DIALER"; 88 89 /** Used both by {@link ActionBar} and {@link ViewPagerAdapter} */ 90 private static final int TAB_INDEX_DIALER = 0; 91 private static final int TAB_INDEX_CALL_LOG = 1; 92 private static final int TAB_INDEX_FAVORITES = 2; 93 94 private static final int TAB_INDEX_COUNT = 3; 95 96 private static final int SUBACTIVITY_ACCOUNT_FILTER = 0; 97 98 /** Name of the dialtacts shared preferences */ 99 static final String PREFS_DIALTACTS = "dialtacts"; 100 static final boolean PREF_FAVORITES_AS_CONTACTS_DEFAULT = false; 101 102 /** Last manually selected tab index */ 103 private static final String PREF_LAST_MANUALLY_SELECTED_TAB = "last_manually_selected_tab"; 104 private static final int PREF_LAST_MANUALLY_SELECTED_TAB_DEFAULT = TAB_INDEX_DIALER; 105 106 /** 107 * Listener interface for Fragments accommodated in {@link ViewPager} enabling them to know 108 * when it becomes visible or invisible inside the ViewPager. 109 */ 110 public interface ViewPagerVisibilityListener { 111 public void onVisibilityChanged(boolean visible); 112 } 113 114 public class ViewPagerAdapter extends FragmentPagerAdapter { 115 private DialpadFragment mDialpadFragment; 116 private CallLogFragment mCallLogFragment; 117 private ContactTileListFragment mContactTileListFragment; 118 119 public ViewPagerAdapter(FragmentManager fm) { 120 super(fm); 121 } 122 123 @Override 124 public Fragment getItem(int position) { 125 switch (position) { 126 case TAB_INDEX_DIALER: 127 if (mDialpadFragment == null) { 128 mDialpadFragment = new DialpadFragment(); 129 } 130 return mDialpadFragment; 131 case TAB_INDEX_CALL_LOG: 132 if (mCallLogFragment == null) { 133 mCallLogFragment = new CallLogFragment(); 134 } 135 return mCallLogFragment; 136 case TAB_INDEX_FAVORITES: 137 if (mContactTileListFragment == null) { 138 mContactTileListFragment = new ContactTileListFragment(); 139 } 140 return mContactTileListFragment; 141 } 142 throw new IllegalStateException("No fragment at position " + position); 143 } 144 145 @Override 146 public int getCount() { 147 return TAB_INDEX_COUNT; 148 } 149 } 150 151 private class PageChangeListener implements OnPageChangeListener { 152 private int mCurrentPosition = -1; 153 /** 154 * Used during page migration, to remember the next position {@link #onPageSelected(int)} 155 * specified. 156 */ 157 private int mNextPosition = -1; 158 159 @Override 160 public void onPageScrolled( 161 int position, float positionOffset, int positionOffsetPixels) { 162 } 163 164 @Override 165 public void onPageSelected(int position) { 166 final ActionBar actionBar = getActionBar(); 167 if (mCurrentPosition == position) { 168 Log.w(TAG, "Previous position and next position became same (" + position + ")"); 169 } 170 171 actionBar.selectTab(actionBar.getTabAt(position)); 172 mNextPosition = position; 173 } 174 175 public void setCurrentPosition(int position) { 176 mCurrentPosition = position; 177 } 178 179 @Override 180 public void onPageScrollStateChanged(int state) { 181 switch (state) { 182 case ViewPager.SCROLL_STATE_IDLE: { 183 if (mCurrentPosition >= 0) { 184 sendFragmentVisibilityChange(mCurrentPosition, false); 185 } 186 if (mNextPosition >= 0) { 187 sendFragmentVisibilityChange(mNextPosition, true); 188 } 189 invalidateOptionsMenu(); 190 191 mCurrentPosition = mNextPosition; 192 break; 193 } 194 case ViewPager.SCROLL_STATE_DRAGGING: 195 case ViewPager.SCROLL_STATE_SETTLING: 196 default: 197 break; 198 } 199 } 200 } 201 202 private String mFilterText; 203 private Uri mDialUri; 204 205 /** Enables horizontal swipe between Fragments. */ 206 private ViewPager mViewPager; 207 private final PageChangeListener mPageChangeListener = new PageChangeListener(); 208 private DialpadFragment mDialpadFragment; 209 private CallLogFragment mCallLogFragment; 210 private ContactTileListFragment mStrequentFragment; 211 212 private final TabListener mTabListener = new TabListener() { 213 @Override 214 public void onTabUnselected(Tab tab, FragmentTransaction ft) { 215 } 216 217 @Override 218 public void onTabSelected(Tab tab, FragmentTransaction ft) { 219 if (mViewPager.getCurrentItem() != tab.getPosition()) { 220 mViewPager.setCurrentItem(tab.getPosition(), true); 221 } 222 223 // During the call, we don't remember the tab position. 224 if (!DialpadFragment.phoneIsInUse()) { 225 // Remember this tab index. This function is also called, if the tab is set 226 // automatically in which case the setter (setCurrentTab) has to set this to its old 227 // value afterwards 228 mLastManuallySelectedFragment = tab.getPosition(); 229 } 230 } 231 232 @Override 233 public void onTabReselected(Tab tab, FragmentTransaction ft) { 234 } 235 }; 236 237 /** 238 * Fragment for searching phone numbers. Unlike the other Fragments, this doesn't correspond 239 * to tab but is shown by a search action. 240 */ 241 private PhoneNumberPickerFragment mSearchFragment; 242 /** 243 * True when this Activity is in its search UI (with a {@link SearchView} and 244 * {@link PhoneNumberPickerFragment}). 245 */ 246 private boolean mInSearchUi; 247 private SearchView mSearchView; 248 249 private final OnClickListener mFilterOptionClickListener = new OnClickListener() { 250 @Override 251 public void onClick(View view) { 252 final PopupMenu popupMenu = new PopupMenu(DialtactsActivity.this, view); 253 final Menu menu = popupMenu.getMenu(); 254 popupMenu.inflate(R.menu.dialtacts_search_options); 255 final MenuItem filterOptionMenuItem = menu.findItem(R.id.filter_option); 256 filterOptionMenuItem.setOnMenuItemClickListener(mFilterOptionsMenuItemClickListener); 257 final MenuItem addContactOptionMenuItem = menu.findItem(R.id.add_contact); 258 addContactOptionMenuItem.setIntent( 259 new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI)); 260 popupMenu.show(); 261 } 262 }; 263 264 /** 265 * The index of the Fragment (or, the tab) that has last been manually selected. 266 * This value does not keep track of programmatically set Tabs (e.g. Call Log after a Call) 267 */ 268 private int mLastManuallySelectedFragment; 269 270 private ContactListFilterController mContactListFilterController; 271 private OnMenuItemClickListener mFilterOptionsMenuItemClickListener = 272 new OnMenuItemClickListener() { 273 @Override 274 public boolean onMenuItemClick(MenuItem item) { 275 final Intent intent = 276 new Intent(DialtactsActivity.this, AccountFilterActivity.class); 277 ContactListFilter filter = mContactListFilterController.getFilter(); 278 startActivityForResult(intent, SUBACTIVITY_ACCOUNT_FILTER); 279 return true; 280 } 281 }; 282 283 private OnMenuItemClickListener mSearchMenuItemClickListener = 284 new OnMenuItemClickListener() { 285 @Override 286 public boolean onMenuItemClick(MenuItem item) { 287 enterSearchUi(); 288 return true; 289 } 290 }; 291 292 /** 293 * Listener used when one of phone numbers in search UI is selected. This will initiate a 294 * phone call using the phone number. 295 */ 296 private final OnPhoneNumberPickerActionListener mPhoneNumberPickerActionListener = 297 new OnPhoneNumberPickerActionListener() { 298 @Override 299 public void onPickPhoneNumberAction(Uri dataUri) { 300 PhoneNumberInteraction.startInteractionForPhoneCall( 301 DialtactsActivity.this, dataUri); 302 } 303 304 @Override 305 public void onShortcutIntentCreated(Intent intent) { 306 Log.w(TAG, "Unsupported intent has come (" + intent + "). Ignoring."); 307 } 308 309 @Override 310 public void onHomeInActionBarSelected() { 311 exitSearchUi(); 312 } 313 }; 314 315 /** 316 * Listener used to send search queries to the phone search fragment. 317 */ 318 private final OnQueryTextListener mPhoneSearchQueryTextListener = 319 new OnQueryTextListener() { 320 @Override 321 public boolean onQueryTextSubmit(String query) { 322 View view = getCurrentFocus(); 323 if (view != null) { 324 hideInputMethod(view); 325 view.clearFocus(); 326 } 327 return true; 328 } 329 330 @Override 331 public boolean onQueryTextChange(String newText) { 332 // Show search result with non-empty text. Show a bare list otherwise. 333 mSearchFragment.setQueryString(newText, true); 334 mSearchFragment.setSearchMode(!TextUtils.isEmpty(newText)); 335 return true; 336 } 337 }; 338 339 /** 340 * Listener used to handle the "close" button on the right side of {@link SearchView}. 341 * If some text is in the search view, this will clean it up. Otherwise this will exit 342 * the search UI and let users go back to usual Phone UI. 343 * 344 * This does _not_ handle back button. 345 */ 346 private final OnCloseListener mPhoneSearchCloseListener = 347 new OnCloseListener() { 348 @Override 349 public boolean onClose() { 350 if (!TextUtils.isEmpty(mSearchView.getQuery())) { 351 mSearchView.setQuery(null, true); 352 } 353 return true; 354 } 355 }; 356 357 @Override 358 protected void onCreate(Bundle icicle) { 359 super.onCreate(icicle); 360 361 final Intent intent = getIntent(); 362 fixIntent(intent); 363 364 setContentView(R.layout.dialtacts_activity); 365 366 mContactListFilterController = new ContactListFilterController(this); 367 mContactListFilterController.addListener(new ContactListFilterListener() { 368 @Override 369 public void onContactListFilterChanged() { 370 if (mSearchFragment == null || !mSearchFragment.isAdded()) { 371 Log.w(TAG, "Search Fragment isn't available when ContactListFilter is changed"); 372 return; 373 } 374 mSearchFragment.setFilter(mContactListFilterController.getFilter()); 375 376 invalidateOptionsMenu(); 377 } 378 }); 379 380 mViewPager = (ViewPager) findViewById(R.id.pager); 381 mViewPager.setAdapter(new ViewPagerAdapter(getFragmentManager())); 382 mViewPager.setOnPageChangeListener(mPageChangeListener); 383 384 prepareSearchView(); 385 386 // Setup the ActionBar tabs (the order matches the tab-index contants TAB_INDEX_*) 387 setupDialer(); 388 setupCallLog(); 389 setupFavorites(); 390 getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); 391 getActionBar().setDisplayShowTitleEnabled(false); 392 getActionBar().setDisplayShowHomeEnabled(false); 393 394 // Load the last manually loaded tab 395 final SharedPreferences prefs = getSharedPreferences(PREFS_DIALTACTS, MODE_PRIVATE); 396 mLastManuallySelectedFragment = prefs.getInt(PREF_LAST_MANUALLY_SELECTED_TAB, 397 PREF_LAST_MANUALLY_SELECTED_TAB_DEFAULT); 398 if (mLastManuallySelectedFragment >= TAB_INDEX_COUNT) { 399 // Stored value may have exceeded the number of current tabs. Reset it. 400 mLastManuallySelectedFragment = PREF_LAST_MANUALLY_SELECTED_TAB_DEFAULT; 401 } 402 403 setCurrentTab(intent); 404 405 if (UI.FILTER_CONTACTS_ACTION.equals(intent.getAction()) 406 && icicle == null) { 407 setupFilterText(intent); 408 } 409 } 410 411 private void prepareSearchView() { 412 final View searchViewLayout = 413 getLayoutInflater().inflate(R.layout.dialtacts_custom_action_bar, null); 414 mSearchView = (SearchView) searchViewLayout.findViewById(R.id.search_view); 415 mSearchView.setOnQueryTextListener(mPhoneSearchQueryTextListener); 416 mSearchView.setOnCloseListener(mPhoneSearchCloseListener); 417 // Since we're using a custom layout for showing SearchView instead of letting the 418 // search menu icon do that job, we need to manually configure the View so it looks 419 // "shown via search menu". 420 // - it should be iconified by default 421 // - it should not be iconified at this time 422 // See also comments for onActionViewExpanded()/onActionViewCollapsed() 423 mSearchView.setIconifiedByDefault(true); 424 mSearchView.setQueryHint(getString(R.string.hint_findContacts)); 425 mSearchView.setIconified(false); 426 427 if (!ViewConfiguration.get(this).hasPermanentMenuKey()) { 428 // Filter option menu should be shown on the right side of SearchView. 429 final View filterOptionView = searchViewLayout.findViewById(R.id.search_option); 430 filterOptionView.setVisibility(View.VISIBLE); 431 filterOptionView.setOnClickListener(mFilterOptionClickListener); 432 } 433 434 getActionBar().setCustomView(searchViewLayout, 435 new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); 436 } 437 438 @Override 439 public void onAttachFragment(Fragment fragment) { 440 // This method can be called before onCreate(), at which point we cannot rely on ViewPager. 441 // In that case, we will setup the "current position" soon after the ViewPager is ready. 442 final int currentPosition = mViewPager != null ? mViewPager.getCurrentItem() : -1; 443 444 if (fragment instanceof DialpadFragment) { 445 mDialpadFragment = (DialpadFragment) fragment; 446 mDialpadFragment.setListener(mDialpadListener); 447 if (currentPosition == TAB_INDEX_DIALER) { 448 mDialpadFragment.onVisibilityChanged(true); 449 } 450 } else if (fragment instanceof CallLogFragment) { 451 mCallLogFragment = (CallLogFragment) fragment; 452 if (currentPosition == TAB_INDEX_CALL_LOG) { 453 mCallLogFragment.onVisibilityChanged(true); 454 } 455 } else if (fragment instanceof ContactTileListFragment) { 456 mStrequentFragment = (ContactTileListFragment) fragment; 457 mStrequentFragment.enableQuickContact(false); 458 mStrequentFragment.setListener(mStrequentListener); 459 mStrequentFragment.setDisplayType(DisplayType.STREQUENT_PHONE_ONLY); 460 } else if (fragment instanceof PhoneNumberPickerFragment) { 461 mSearchFragment = (PhoneNumberPickerFragment) fragment; 462 mSearchFragment.setOnPhoneNumberPickerActionListener(mPhoneNumberPickerActionListener); 463 mSearchFragment.setQuickContactEnabled(true); 464 final FragmentTransaction transaction = getFragmentManager().beginTransaction(); 465 if (mInSearchUi) { 466 transaction.show(mSearchFragment); 467 } else { 468 transaction.hide(mSearchFragment); 469 } 470 transaction.commit(); 471 } 472 } 473 474 @Override 475 protected void onPause() { 476 super.onPause(); 477 478 final SharedPreferences.Editor editor = 479 getSharedPreferences(PREFS_DIALTACTS, MODE_PRIVATE).edit(); 480 editor.putInt(PREF_LAST_MANUALLY_SELECTED_TAB, mLastManuallySelectedFragment); 481 482 editor.apply(); 483 } 484 485 private void fixIntent(Intent intent) { 486 // This should be cleaned up: the call key used to send an Intent 487 // that just said to go to the recent calls list. It now sends this 488 // abstract action, but this class hasn't been rewritten to deal with it. 489 if (Intent.ACTION_CALL_BUTTON.equals(intent.getAction())) { 490 intent.setDataAndType(Calls.CONTENT_URI, Calls.CONTENT_TYPE); 491 intent.putExtra("call_key", true); 492 setIntent(intent); 493 } 494 } 495 496 private void setupDialer() { 497 final Tab tab = getActionBar().newTab(); 498 // TODO: Temporarily disable tab text labels (in all 4 tabs in this 499 // activity) so that the current tabs will all fit onscreen in 500 // portrait (bug 4520620). (Also note we do setText("") rather 501 // leaving the text null, to work around bug 4521549.) 502 tab.setText(""); // R.string.dialerIconLabel 503 tab.setTabListener(mTabListener); 504 tab.setIcon(R.drawable.ic_tab_dialer); 505 getActionBar().addTab(tab); 506 } 507 508 private void setupCallLog() { 509 final Tab tab = getActionBar().newTab(); 510 tab.setText(""); // R.string.recentCallsIconLabel 511 tab.setIcon(R.drawable.ic_tab_recent); 512 tab.setTabListener(mTabListener); 513 getActionBar().addTab(tab); 514 } 515 516 private void setupFavorites() { 517 final Tab tab = getActionBar().newTab(); 518 tab.setText(""); // R.string.contactsFavoritesLabel 519 tab.setIcon(R.drawable.ic_tab_starred); 520 tab.setTabListener(mTabListener); 521 getActionBar().addTab(tab); 522 } 523 524 /** 525 * Returns true if the intent is due to hitting the green send key while in a call. 526 * 527 * @param intent the intent that launched this activity 528 * @param recentCallsRequest true if the intent is requesting to view recent calls 529 * @return true if the intent is due to hitting the green send key while in a call 530 */ 531 private boolean isSendKeyWhileInCall(final Intent intent, 532 final boolean recentCallsRequest) { 533 // If there is a call in progress go to the call screen 534 if (recentCallsRequest) { 535 final boolean callKey = intent.getBooleanExtra("call_key", false); 536 537 try { 538 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone")); 539 if (callKey && phone != null && phone.showCallScreen()) { 540 return true; 541 } 542 } catch (RemoteException e) { 543 Log.e(TAG, "Failed to handle send while in call", e); 544 } 545 } 546 547 return false; 548 } 549 550 /** 551 * Sets the current tab based on the intent's request type 552 * 553 * @param intent Intent that contains information about which tab should be selected 554 */ 555 private void setCurrentTab(Intent intent) { 556 // If we got here by hitting send and we're in call forward along to the in-call activity 557 final boolean recentCallsRequest = Calls.CONTENT_TYPE.equals(intent.getType()); 558 if (isSendKeyWhileInCall(intent, recentCallsRequest)) { 559 finish(); 560 return; 561 } 562 563 // Remember the old manually selected tab index so that it can be restored if it is 564 // overwritten by one of the programmatic tab selections 565 final int savedTabIndex = mLastManuallySelectedFragment; 566 567 final int tabIndex; 568 if (DialpadFragment.phoneIsInUse() || isDialIntent(intent)) { 569 tabIndex = TAB_INDEX_DIALER; 570 } else if (recentCallsRequest) { 571 tabIndex = TAB_INDEX_CALL_LOG; 572 } else { 573 tabIndex = mLastManuallySelectedFragment; 574 } 575 mViewPager.setCurrentItem(tabIndex, false /* smoothScroll */); 576 if (mViewPager.getCurrentItem() == tabIndex) { 577 mPageChangeListener.setCurrentPosition(tabIndex); 578 sendFragmentVisibilityChange(tabIndex, true); 579 } else { 580 getActionBar().selectTab(getActionBar().getTabAt(tabIndex)); 581 } 582 583 // Restore to the previous manual selection 584 mLastManuallySelectedFragment = savedTabIndex; 585 } 586 587 @Override 588 public void onNewIntent(Intent newIntent) { 589 setIntent(newIntent); 590 fixIntent(newIntent); 591 setCurrentTab(newIntent); 592 final String action = newIntent.getAction(); 593 if (UI.FILTER_CONTACTS_ACTION.equals(action)) { 594 setupFilterText(newIntent); 595 } 596 if (mInSearchUi || mSearchFragment.isVisible()) { 597 exitSearchUi(); 598 } 599 600 if (mViewPager.getCurrentItem() == TAB_INDEX_DIALER) { 601 if (mDialpadFragment != null) { 602 mDialpadFragment.configureScreenFromIntent(newIntent); 603 } else { 604 Log.e(TAG, "DialpadFragment isn't ready yet when the tab is already selected."); 605 } 606 } 607 invalidateOptionsMenu(); 608 } 609 610 /** Returns true if the given intent contains a phone number to populate the dialer with */ 611 private boolean isDialIntent(Intent intent) { 612 final String action = intent.getAction(); 613 if (Intent.ACTION_DIAL.equals(action) || ACTION_TOUCH_DIALER.equals(action)) { 614 return true; 615 } 616 if (Intent.ACTION_VIEW.equals(action)) { 617 final Uri data = intent.getData(); 618 if (data != null && "tel".equals(data.getScheme())) { 619 return true; 620 } 621 } 622 return false; 623 } 624 625 /** 626 * Retrieves the filter text stored in {@link #setupFilterText(Intent)}. 627 * This text originally came from a FILTER_CONTACTS_ACTION intent received 628 * by this activity. The stored text will then be cleared after after this 629 * method returns. 630 * 631 * @return The stored filter text 632 */ 633 public String getAndClearFilterText() { 634 String filterText = mFilterText; 635 mFilterText = null; 636 return filterText; 637 } 638 639 /** 640 * Stores the filter text associated with a FILTER_CONTACTS_ACTION intent. 641 * This is so child activities can check if they are supposed to display a filter. 642 * 643 * @param intent The intent received in {@link #onNewIntent(Intent)} 644 */ 645 private void setupFilterText(Intent intent) { 646 // If the intent was relaunched from history, don't apply the filter text. 647 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) { 648 return; 649 } 650 String filter = intent.getStringExtra(UI.FILTER_TEXT_EXTRA_KEY); 651 if (filter != null && filter.length() > 0) { 652 mFilterText = filter; 653 } 654 } 655 656 @Override 657 public void onBackPressed() { 658 if (mInSearchUi) { 659 // We should let the user go back to usual screens with tabs. 660 exitSearchUi(); 661 } else if (isTaskRoot()) { 662 // Instead of stopping, simply push this to the back of the stack. 663 // This is only done when running at the top of the stack; 664 // otherwise, we have been launched by someone else so need to 665 // allow the user to go back to the caller. 666 moveTaskToBack(false); 667 } else { 668 super.onBackPressed(); 669 } 670 } 671 672 private DialpadFragment.Listener mDialpadListener = new DialpadFragment.Listener() { 673 @Override 674 public void onSearchButtonPressed() { 675 enterSearchUi(); 676 } 677 }; 678 679 private ContactTileListFragment.Listener mStrequentListener = 680 new ContactTileListFragment.Listener() { 681 @Override 682 public void onContactSelected(Uri contactUri) { 683 PhoneNumberInteraction.startInteractionForPhoneCall( 684 DialtactsActivity.this, contactUri); 685 } 686 }; 687 688 @Override 689 public boolean onCreateOptionsMenu(Menu menu) { 690 MenuInflater inflater = getMenuInflater(); 691 inflater.inflate(R.menu.dialtacts_options, menu); 692 return true; 693 } 694 695 @Override 696 public boolean onPrepareOptionsMenu(Menu menu) { 697 final MenuItem searchMenuItem = menu.findItem(R.id.search_on_action_bar); 698 final MenuItem filterOptionMenuItem = menu.findItem(R.id.filter_option); 699 final MenuItem addContactOptionMenuItem = menu.findItem(R.id.add_contact); 700 final MenuItem callSettingsMenuItem = menu.findItem(R.id.menu_call_settings); 701 Tab tab = getActionBar().getSelectedTab(); 702 if (mInSearchUi) { 703 searchMenuItem.setVisible(false); 704 if (ViewConfiguration.get(this).hasPermanentMenuKey()) { 705 filterOptionMenuItem.setVisible(true); 706 filterOptionMenuItem.setOnMenuItemClickListener( 707 mFilterOptionsMenuItemClickListener); 708 addContactOptionMenuItem.setVisible(true); 709 addContactOptionMenuItem.setIntent( 710 new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI)); 711 } else { 712 // Filter option menu should be not be shown as a overflow menu. 713 filterOptionMenuItem.setVisible(false); 714 addContactOptionMenuItem.setVisible(false); 715 } 716 callSettingsMenuItem.setVisible(false); 717 } else { 718 final boolean showCallSettingsMenu; 719 if (tab != null && tab.getPosition() == TAB_INDEX_DIALER) { 720 searchMenuItem.setVisible(false); 721 // When permanent menu key is _not_ available, the call settings menu should be 722 // available via DialpadFragment. 723 showCallSettingsMenu = ViewConfiguration.get(this).hasPermanentMenuKey(); 724 } else { 725 searchMenuItem.setVisible(true); 726 searchMenuItem.setOnMenuItemClickListener(mSearchMenuItemClickListener); 727 showCallSettingsMenu = true; 728 } 729 filterOptionMenuItem.setVisible(false); 730 addContactOptionMenuItem.setVisible(false); 731 732 if (showCallSettingsMenu) { 733 callSettingsMenuItem.setVisible(true); 734 callSettingsMenuItem.setIntent(DialtactsActivity.getCallSettingsIntent()); 735 } else { 736 callSettingsMenuItem.setVisible(false); 737 } 738 } 739 740 return true; 741 } 742 743 @Override 744 public void startSearch(String initialQuery, boolean selectInitialQuery, 745 Bundle appSearchData, boolean globalSearch) { 746 if (mSearchFragment != null && mSearchFragment.isAdded() && !globalSearch) { 747 enterSearchUi(); 748 } else { 749 super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch); 750 } 751 } 752 753 /** 754 * Hides every tab and shows search UI for phone lookup. 755 */ 756 private void enterSearchUi() { 757 final ActionBar actionBar = getActionBar(); 758 759 final Tab tab = actionBar.getSelectedTab(); 760 761 // User can search during the call, but we don't want to remember the status. 762 if (tab != null && !DialpadFragment.phoneIsInUse()) { 763 mLastManuallySelectedFragment = tab.getPosition(); 764 } 765 766 mSearchView.setQuery(null, true); 767 768 actionBar.setDisplayShowCustomEnabled(true); 769 actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); 770 actionBar.setDisplayShowHomeEnabled(true); 771 actionBar.setDisplayHomeAsUpEnabled(true); 772 773 sendFragmentVisibilityChange(mViewPager.getCurrentItem(), false); 774 775 // Show the search fragment and hide everything else. 776 final FragmentTransaction transaction = getFragmentManager().beginTransaction(); 777 transaction.show(mSearchFragment); 778 transaction.commit(); 779 mViewPager.setVisibility(View.GONE); 780 781 // We need to call this and onActionViewCollapsed() manually, since we are using a custom 782 // layout instead of asking the search menu item to take care of SearchView. 783 mSearchView.onActionViewExpanded(); 784 mInSearchUi = true; 785 786 // Clear focus and suppress keyboard show-up. 787 mSearchView.clearFocus(); 788 } 789 790 private void showInputMethod(View view) { 791 InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); 792 if (imm != null) { 793 imm.showSoftInput(view, 0); 794 } 795 } 796 797 private void hideInputMethod(View view) { 798 InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); 799 if (imm != null && view != null) { 800 imm.hideSoftInputFromWindow(view.getWindowToken(), 0); 801 } 802 } 803 804 /** 805 * Goes back to usual Phone UI with tags. Previously selected Tag and associated Fragment 806 * should be automatically focused again. 807 */ 808 private void exitSearchUi() { 809 final ActionBar actionBar = getActionBar(); 810 811 final FragmentTransaction transaction = getFragmentManager().beginTransaction(); 812 transaction.hide(mSearchFragment); 813 transaction.commit(); 814 815 // We want to hide SearchView and show Tabs. Also focus on previously selected one. 816 actionBar.setDisplayShowCustomEnabled(false); 817 actionBar.setDisplayShowHomeEnabled(false); 818 actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); 819 820 sendFragmentVisibilityChange(mViewPager.getCurrentItem(), true); 821 822 mViewPager.setVisibility(View.VISIBLE); 823 824 hideInputMethod(getCurrentFocus()); 825 826 // Request to update option menu. 827 invalidateOptionsMenu(); 828 829 // See comments in onActionViewExpanded() 830 mSearchView.onActionViewCollapsed(); 831 mInSearchUi = false; 832 } 833 834 private Fragment getFragmentAt(int position) { 835 switch (position) { 836 case TAB_INDEX_DIALER: 837 return mDialpadFragment; 838 case TAB_INDEX_CALL_LOG: 839 return mCallLogFragment; 840 case TAB_INDEX_FAVORITES: 841 return mStrequentFragment; 842 default: 843 throw new IllegalStateException("Unknown fragment index: " + position); 844 } 845 } 846 847 private void sendFragmentVisibilityChange(int position, boolean visibility) { 848 final Fragment fragment = getFragmentAt(position); 849 if (fragment instanceof ViewPagerVisibilityListener) { 850 ((ViewPagerVisibilityListener) fragment).onVisibilityChanged(visibility); 851 } 852 } 853 854 /** Returns an Intent to launch Call Settings screen */ 855 public static Intent getCallSettingsIntent() { 856 final Intent intent = new Intent(Intent.ACTION_MAIN); 857 intent.setClassName(PHONE_PACKAGE, CALL_SETTINGS_CLASS_NAME); 858 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 859 return intent; 860 } 861 862 @Override 863 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 864 if (resultCode != Activity.RESULT_OK) { 865 return; 866 } 867 switch (requestCode) { 868 case SUBACTIVITY_ACCOUNT_FILTER: { 869 ContactListFilter filter = (ContactListFilter) data.getParcelableExtra( 870 AccountFilterActivity.KEY_EXTRA_CONTACT_LIST_FILTER); 871 if (filter == null) { 872 return; 873 } 874 if (filter.filterType == ContactListFilter.FILTER_TYPE_CUSTOM) { 875 mContactListFilterController.selectCustomFilter(); 876 } else { 877 mContactListFilterController.setContactListFilter(filter, true); 878 } 879 } 880 break; 881 } 882 } 883} 884