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