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