DialtactsActivity.java revision 98702de246d42c844fabdb8a1f3407bf1747d379
1/* 2 * Copyright (C) 2013 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.dialer; 18 19import android.animation.Animator; 20import android.animation.Animator.AnimatorListener; 21import android.animation.AnimatorListenerAdapter; 22import android.app.Activity; 23import android.app.backup.BackupManager; 24import android.app.Fragment; 25import android.app.FragmentManager; 26import android.app.FragmentTransaction; 27import android.content.ActivityNotFoundException; 28import android.content.Context; 29import android.content.Intent; 30import android.content.SharedPreferences; 31import android.content.res.Resources; 32import android.net.Uri; 33import android.os.Bundle; 34import android.os.RemoteException; 35import android.os.ServiceManager; 36import android.provider.CallLog.Calls; 37import android.provider.ContactsContract; 38import android.provider.ContactsContract.Contacts; 39import android.provider.ContactsContract.Intents.UI; 40import android.provider.Settings; 41import android.speech.RecognizerIntent; 42import android.support.v4.app.NavUtils; 43import android.telephony.TelephonyManager; 44import android.text.Editable; 45import android.text.TextUtils; 46import android.text.TextWatcher; 47import android.util.Log; 48import android.view.Menu; 49import android.view.MenuItem; 50import android.view.View; 51import android.view.View.OnFocusChangeListener; 52import android.view.ViewConfiguration; 53import android.view.inputmethod.InputMethodManager; 54import android.widget.AbsListView.OnScrollListener; 55import android.widget.EditText; 56import android.widget.ImageView; 57import android.widget.PopupMenu; 58import android.widget.SearchView; 59import android.widget.SearchView.OnCloseListener; 60import android.widget.SearchView.OnQueryTextListener; 61import android.widget.Toast; 62 63import com.android.contacts.common.CallUtil; 64import com.android.contacts.common.activity.TransactionSafeActivity; 65import com.android.contacts.common.dialog.ClearFrequentsDialog; 66import com.android.contacts.common.interactions.ImportExportDialogFragment; 67import com.android.contacts.common.list.ContactListItemView; 68import com.android.contacts.common.list.OnPhoneNumberPickerActionListener; 69import com.android.contacts.common.list.PhoneNumberPickerFragment; 70import com.android.dialer.calllog.CallLogActivity; 71import com.android.dialer.dialpad.DialpadFragment; 72import com.android.dialer.dialpad.SmartDialNameMatcher; 73import com.android.dialer.interactions.PhoneNumberInteraction; 74import com.android.dialer.list.NewPhoneFavoriteFragment; 75import com.android.dialer.list.OnListFragmentScrolledListener; 76import com.android.dialer.list.ShowAllContactsFragment; 77import com.android.dialer.list.SmartDialSearchFragment; 78import com.android.internal.telephony.ITelephony; 79 80import java.util.ArrayList; 81 82/** 83 * The dialer tab's title is 'phone', a more common name (see strings.xml). 84 * 85 * TODO krelease: All classes currently prefixed with New will replace the original classes or 86 * be renamed more appropriately before shipping. 87 */ 88public class DialtactsActivity extends TransactionSafeActivity implements View.OnClickListener, 89 DialpadFragment.OnDialpadQueryChangedListener, PopupMenu.OnMenuItemClickListener, 90 OnListFragmentScrolledListener, 91 NewPhoneFavoriteFragment.OnPhoneFavoriteFragmentStartedListener, 92 DialpadFragment.OnDialpadFragmentStartedListener { 93 private static final String TAG = "DialtactsActivity"; 94 95 public static final boolean DEBUG = false; 96 97 public static final String SHARED_PREFS_NAME = "com.android.dialer_preferences"; 98 99 /** Used to open Call Setting */ 100 private static final String PHONE_PACKAGE = "com.android.phone"; 101 private static final String CALL_SETTINGS_CLASS_NAME = 102 "com.android.phone.CallFeaturesSetting"; 103 /** @see #getCallOrigin() */ 104 private static final String CALL_ORIGIN_DIALTACTS = 105 "com.android.dialer.DialtactsActivity"; 106 107 private static final String TAG_DIALPAD_FRAGMENT = "dialpad"; 108 private static final String TAG_REGULAR_SEARCH_FRAGMENT = "search"; 109 private static final String TAG_SMARTDIAL_SEARCH_FRAGMENT = "smartdial"; 110 private static final String TAG_FAVORITES_FRAGMENT = "favorites"; 111 private static final String TAG_SHOW_ALL_CONTACTS_FRAGMENT = "show_all_contacts"; 112 113 /** 114 * Just for backward compatibility. Should behave as same as {@link Intent#ACTION_DIAL}. 115 */ 116 private static final String ACTION_TOUCH_DIALER = "com.android.phone.action.TOUCH_DIALER"; 117 118 private static final int SUBACTIVITY_ACCOUNT_FILTER = 1; 119 120 private static final int ACTIVITY_REQUEST_CODE_VOICE_SEARCH = 1; 121 122 private String mFilterText; 123 124 /** 125 * The main fragment displaying the user's favorites and frequent contacts 126 */ 127 private NewPhoneFavoriteFragment mPhoneFavoriteFragment; 128 129 /** 130 * Fragment containing the dialpad that slides into view 131 */ 132 private DialpadFragment mDialpadFragment; 133 134 /** 135 * Fragment for searching phone numbers using the alphanumeric keyboard. 136 */ 137 private NewSearchFragment mRegularSearchFragment; 138 139 /** 140 * Fragment for searching phone numbers using the dialpad. 141 */ 142 private SmartDialSearchFragment mSmartDialSearchFragment; 143 144 private ShowAllContactsFragment mShowAllContactsFragment; 145 146 private View mMenuButton; 147 private View mCallHistoryButton; 148 private View mDialpadButton; 149 150 // Padding view used to shift the fragments up when the dialpad is shown. 151 private View mBottomPaddingView; 152 153 /** 154 * True when this Activity is in its search UI (with a {@link SearchView} and 155 * {@link PhoneNumberPickerFragment}). 156 */ 157 private boolean mInSearchUi; 158 private boolean mFirstLaunch; 159 private View mSearchViewContainer; 160 private View mSearchViewCloseButton; 161 private View mVoiceSearchButton; 162 private EditText mSearchView; 163 164 /** 165 * Listener used when one of phone numbers in search UI is selected. This will initiate a 166 * phone call using the phone number. 167 */ 168 private final OnPhoneNumberPickerActionListener mPhoneNumberPickerActionListener = 169 new OnPhoneNumberPickerActionListener() { 170 @Override 171 public void onPickPhoneNumberAction(Uri dataUri) { 172 // Specify call-origin so that users will see the previous tab instead of 173 // CallLog screen (search UI will be automatically exited). 174 PhoneNumberInteraction.startInteractionForPhoneCall( 175 DialtactsActivity.this, dataUri, getCallOrigin()); 176 } 177 178 @Override 179 public void onShortcutIntentCreated(Intent intent) { 180 Log.w(TAG, "Unsupported intent has come (" + intent + "). Ignoring."); 181 } 182 183 @Override 184 public void onHomeInActionBarSelected() { 185 exitSearchUi(); 186 } 187 }; 188 189 /** 190 * Listener used to send search queries to the phone search fragment. 191 */ 192 private final TextWatcher mPhoneSearchQueryTextListener = new TextWatcher() { 193 @Override 194 public void beforeTextChanged(CharSequence s, int start, int count, int after) { 195 } 196 197 @Override 198 public void onTextChanged(CharSequence s, int start, int before, int count) { 199 // TODO krelease: populate the search fragments with the correct 200 // search query at the correct point in time of the fragment lifecycle. 201 // The current behavior is to simply return to the favorites screen 202 // (when docked), or returning to the Dialer after it has been 203 // swapped out of memory. 204 if (mDialpadFragment == null) return; 205 final boolean smartDialSearch = isDialpadShowing(); 206 final String newText = s.toString(); 207 // Show search result with non-empty text. Show a bare list otherwise. 208 if (TextUtils.isEmpty(newText) && mInSearchUi) { 209 exitSearchUi(); 210 mSearchViewCloseButton.setVisibility(View.GONE); 211 return; 212 } else if (!TextUtils.isEmpty(newText) && !mInSearchUi) { 213 enterSearchUi(smartDialSearch); 214 } 215 216 if (smartDialSearch) { 217 mSmartDialSearchFragment.setQueryString(newText, false); 218 } else { 219 mRegularSearchFragment.setQueryString(newText, false); 220 } 221 mSearchViewCloseButton.setVisibility(View.VISIBLE); 222 return; 223 } 224 225 @Override 226 public void afterTextChanged(Editable s) { 227 } 228 }; 229 230 private boolean isDialpadShowing() { 231 return mDialpadFragment != null && mDialpadFragment.isVisible(); 232 } 233 234 @Override 235 protected void onCreate(Bundle savedInstanceState) { 236 super.onCreate(savedInstanceState); 237 238 mFirstLaunch = true; 239 240 final Intent intent = getIntent(); 241 fixIntent(intent); 242 243 setContentView(R.layout.new_dialtacts_activity); 244 245 getActionBar().hide(); 246 247 if (savedInstanceState == null) { 248 mPhoneFavoriteFragment = new NewPhoneFavoriteFragment(); 249 mPhoneFavoriteFragment.setListener(mPhoneFavoriteListener); 250 251 mRegularSearchFragment = new NewSearchFragment(); 252 mSmartDialSearchFragment = new SmartDialSearchFragment(); 253 mDialpadFragment = new DialpadFragment(); 254 mShowAllContactsFragment = new ShowAllContactsFragment(); 255 mShowAllContactsFragment.setOnPhoneNumberPickerActionListener( 256 mPhoneNumberPickerActionListener); 257 258 // TODO krelease: load fragments on demand instead of creating all of them at run time 259 final FragmentTransaction ft = getFragmentManager().beginTransaction(); 260 ft.add(R.id.dialtacts_frame, mPhoneFavoriteFragment, TAG_FAVORITES_FRAGMENT); 261 ft.add(R.id.dialtacts_frame, mRegularSearchFragment, TAG_REGULAR_SEARCH_FRAGMENT); 262 ft.add(R.id.dialtacts_frame, mSmartDialSearchFragment, TAG_SMARTDIAL_SEARCH_FRAGMENT); 263 ft.add(R.id.dialtacts_frame, mShowAllContactsFragment, TAG_SHOW_ALL_CONTACTS_FRAGMENT); 264 ft.add(R.id.dialtacts_container, mDialpadFragment, TAG_DIALPAD_FRAGMENT); 265 ft.commit(); 266 } 267 268 mBottomPaddingView = findViewById(R.id.dialtacts_bottom_padding); 269 prepareSearchView(); 270 271 if (UI.FILTER_CONTACTS_ACTION.equals(intent.getAction()) 272 && savedInstanceState == null) { 273 setupFilterText(intent); 274 } 275 } 276 277 @Override 278 protected void onResume() { 279 super.onResume(); 280 final FragmentManager fm = getFragmentManager(); 281 mPhoneFavoriteFragment = (NewPhoneFavoriteFragment) fm.findFragmentByTag( 282 TAG_FAVORITES_FRAGMENT); 283 mDialpadFragment = (DialpadFragment) fm.findFragmentByTag(TAG_DIALPAD_FRAGMENT); 284 285 mRegularSearchFragment = (NewSearchFragment) fm.findFragmentByTag( 286 TAG_REGULAR_SEARCH_FRAGMENT); 287 mRegularSearchFragment.setOnPhoneNumberPickerActionListener( 288 mPhoneNumberPickerActionListener); 289 290 mSmartDialSearchFragment = (SmartDialSearchFragment) fm.findFragmentByTag( 291 TAG_SMARTDIAL_SEARCH_FRAGMENT); 292 mSmartDialSearchFragment.setOnPhoneNumberPickerActionListener( 293 mPhoneNumberPickerActionListener); 294 295 mShowAllContactsFragment = (ShowAllContactsFragment) fm.findFragmentByTag( 296 TAG_SHOW_ALL_CONTACTS_FRAGMENT); 297 mShowAllContactsFragment.setOnPhoneNumberPickerActionListener( 298 mPhoneNumberPickerActionListener); 299 300 if (mFirstLaunch) { 301 displayFragment(getIntent()); 302 } 303 mFirstLaunch = false; 304 } 305 306 @Override 307 public void onAttachFragment(Fragment fragment) { 308 if (fragment instanceof DialpadFragment || fragment instanceof NewSearchFragment 309 || fragment instanceof SmartDialSearchFragment 310 || fragment instanceof ShowAllContactsFragment) { 311 final FragmentTransaction transaction = getFragmentManager().beginTransaction(); 312 transaction.hide(fragment); 313 transaction.commit(); 314 } 315 // TODO krelease: Save some kind of state here to show the appropriate fragment 316 // based on the state of the dialer when it was last paused 317 } 318 319 @Override 320 public boolean onOptionsItemSelected(MenuItem item) { 321 switch (item.getItemId()) { 322 // Respond to the action bar's Up/Home button 323 case android.R.id.home: 324 hideAllContactsFragment(); 325 } 326 return super.onOptionsItemSelected(item); 327 } 328 329 @Override 330 public boolean onMenuItemClick(MenuItem item) { 331 switch (item.getItemId()) { 332 case R.id.menu_import_export: 333 // We hard-code the "contactsAreAvailable" argument because doing it properly would 334 // involve querying a {@link ProviderStatusLoader}, which we don't want to do right 335 // now in Dialtacts for (potential) performance reasons. Compare with how it is 336 // done in {@link PeopleActivity}. 337 ImportExportDialogFragment.show(getFragmentManager(), true, 338 DialtactsActivity.class); 339 return true; 340 case R.id.menu_clear_frequents: 341 ClearFrequentsDialog.show(getFragmentManager()); 342 return true; 343 case R.id.add_contact: 344 try { 345 startActivity(new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI)); 346 } catch (ActivityNotFoundException e) { 347 Toast toast = Toast.makeText(this, 348 R.string.add_contact_not_available, 349 Toast.LENGTH_SHORT); 350 toast.show(); 351 } 352 return true; 353 case R.id.menu_call_settings: 354 final Intent settingsIntent = DialtactsActivity.getCallSettingsIntent(); 355 startActivity(settingsIntent); 356 } 357 return false; 358 } 359 360 @Override 361 public void onClick(View view) { 362 switch (view.getId()) { 363 case R.id.overflow_menu: { 364 final PopupMenu popupMenu = new PopupMenu(DialtactsActivity.this, view); 365 final Menu menu = popupMenu.getMenu(); 366 popupMenu.inflate(R.menu.dialtacts_options_new); 367 popupMenu.setOnMenuItemClickListener(this); 368 popupMenu.show(); 369 break; 370 } 371 case R.id.dialpad_button: 372 showDialpadFragment(true); 373 break; 374 case R.id.call_history_on_dialpad_button: 375 case R.id.call_history_button: 376 // TODO krelease: This should start an intent with content type 377 // CallLog.Calls.CONTENT_TYPE, once the intent filters for the call log activity 378 // is enabled 379 final Intent intent = new Intent(this, CallLogActivity.class); 380 startActivity(intent); 381 break; 382 case R.id.search_close_button: 383 // Clear the search field 384 if (!TextUtils.isEmpty(mSearchView.getText())) { 385 mSearchView.setText(""); 386 } 387 break; 388 case R.id.voice_search_button: 389 final Intent voiceIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); 390 startActivityForResult(voiceIntent, ACTIVITY_REQUEST_CODE_VOICE_SEARCH); 391 break; 392 default: { 393 Log.wtf(TAG, "Unexpected onClick event from " + view); 394 break; 395 } 396 } 397 } 398 399 @Override 400 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 401 if (requestCode == ACTIVITY_REQUEST_CODE_VOICE_SEARCH) { 402 if (resultCode == RESULT_OK) { 403 final ArrayList<String> matches = data.getStringArrayListExtra( 404 RecognizerIntent.EXTRA_RESULTS); 405 if (matches.size() > 0) { 406 final String match = matches.get(0); 407 mSearchView.setText(match); 408 } else { 409 Log.e(TAG, "Voice search - nothing heard"); 410 } 411 } else { 412 Log.e(TAG, "Voice search failed"); 413 } 414 } 415 super.onActivityResult(requestCode, resultCode, data); 416 } 417 418 private void showDialpadFragment(boolean animate) { 419 final FragmentTransaction ft = getFragmentManager().beginTransaction(); 420 if (animate) { 421 ft.setCustomAnimations(R.anim.slide_in, 0); 422 } 423 ft.show(mDialpadFragment); 424 ft.commit(); 425 } 426 427 private void hideDialpadFragment(boolean animate) { 428 final FragmentTransaction ft = getFragmentManager().beginTransaction(); 429 if (animate) { 430 ft.setCustomAnimations(0, R.anim.slide_out); 431 } 432 ft.hide(mDialpadFragment); 433 ft.commit(); 434 } 435 436 public void showAllContactsFragment() { 437 final FragmentTransaction ft = getFragmentManager().beginTransaction(); 438 ft.hide(mPhoneFavoriteFragment); 439 ft.show(mShowAllContactsFragment); 440 // TODO{klp} Add animation 441 ft.commit(); 442 hideSearchBar(false); 443 } 444 445 private void hideAllContactsFragment() { 446 final FragmentTransaction ft = getFragmentManager().beginTransaction(); 447 ft.hide(mShowAllContactsFragment); 448 ft.show(mPhoneFavoriteFragment); 449 ft.commit(); 450 showSearchBar(); 451 } 452 453 private void prepareSearchView() { 454 mSearchViewContainer = findViewById(R.id.search_view_container); 455 mSearchViewCloseButton = findViewById(R.id.search_close_button); 456 mSearchViewCloseButton.setOnClickListener(this); 457 mVoiceSearchButton = findViewById(R.id.voice_search_button); 458 mVoiceSearchButton.setOnClickListener(this); 459 mSearchView = (EditText) findViewById(R.id.search_view); 460 mSearchView.addTextChangedListener(mPhoneSearchQueryTextListener); 461 mSearchView.setHint(getString(R.string.dialer_hint_find_contact)); 462 mSearchView.setOnFocusChangeListener(new OnFocusChangeListener() { 463 @Override 464 public void onFocusChange(View view, boolean hasFocus) { 465 if (hasFocus) { 466 showInputMethod(view.findFocus()); 467 } 468 } 469 }); 470 } 471 472 private void hideDialpadFragmentIfNecessary() { 473 if (mDialpadFragment.isVisible()) { 474 hideDialpadFragment(true); 475 } 476 } 477 478 final AnimatorListener mHideListener = new AnimatorListenerAdapter() { 479 @Override 480 public void onAnimationEnd(Animator animation) { 481 mSearchViewContainer.setVisibility(View.GONE); 482 } 483 }; 484 485 public void hideSearchBar() { 486 hideSearchBar(true); 487 } 488 489 public void hideSearchBar(boolean shiftView) { 490 if (shiftView) { 491 mSearchViewContainer.animate().cancel(); 492 mSearchViewContainer.setAlpha(1); 493 mSearchViewContainer.setTranslationY(0); 494 mSearchViewContainer.animate().withLayer().alpha(0).translationY(-mSearchView.getHeight()) 495 .setDuration(200).setListener(mHideListener); 496 497 mPhoneFavoriteFragment.getView().animate().withLayer() 498 .translationY(-mSearchViewContainer.getHeight()).setDuration(200).setListener( 499 new AnimatorListenerAdapter() { 500 @Override 501 public void onAnimationEnd(Animator animation) { 502 mBottomPaddingView.setVisibility(View.VISIBLE); 503 mPhoneFavoriteFragment.getView().setTranslationY(0); 504 } 505 }); 506 } else { 507 mSearchViewContainer.setTranslationY(-mSearchView.getHeight()); 508 } 509 } 510 511 public void showSearchBar() { 512 // If the favorites fragment hasn't been fully created before the dialpad fragment 513 // is hidden (i.e. onResume), don't bother animating 514 if (mPhoneFavoriteFragment == null || mPhoneFavoriteFragment.getView() == null) { 515 return; 516 } 517 mSearchViewContainer.animate().cancel(); 518 mSearchViewContainer.setAlpha(0); 519 mSearchViewContainer.setTranslationY(-mSearchViewContainer.getHeight()); 520 mSearchViewContainer.animate().withLayer().alpha(1).translationY(0).setDuration(200) 521 .setListener(new AnimatorListenerAdapter() { 522 @Override 523 public void onAnimationStart(Animator animation) { 524 mSearchViewContainer.setVisibility(View.VISIBLE); 525 } 526 }); 527 528 mPhoneFavoriteFragment.getView().setTranslationY(-mSearchViewContainer.getHeight()); 529 mPhoneFavoriteFragment.getView().animate().withLayer().translationY(0).setDuration(200) 530 .setListener( 531 new AnimatorListenerAdapter() { 532 @Override 533 public void onAnimationStart(Animator animation) { 534 mBottomPaddingView.setVisibility(View.GONE); 535 } 536 }); 537 } 538 539 540 public void setupFakeActionBarItemsForFavoritesFragment() { 541 mMenuButton = findViewById(R.id.overflow_menu); 542 if (mMenuButton != null) { 543 mMenuButton.setOnClickListener(this); 544 } 545 546 mCallHistoryButton = findViewById(R.id.call_history_button); 547 // mCallHistoryButton.setMinimumWidth(fakeMenuItemWidth); 548 mCallHistoryButton.setOnClickListener(this); 549 550 mDialpadButton = findViewById(R.id.dialpad_button); 551 // DialpadButton.setMinimumWidth(fakeMenuItemWidth); 552 mDialpadButton.setOnClickListener(this); 553 } 554 555 public void setupFakeActionBarItemsForDialpadFragment() { 556 final View callhistoryButton = findViewById(R.id.call_history_on_dialpad_button); 557 callhistoryButton.setOnClickListener(this); 558 } 559 560 private void fixIntent(Intent intent) { 561 // This should be cleaned up: the call key used to send an Intent 562 // that just said to go to the recent calls list. It now sends this 563 // abstract action, but this class hasn't been rewritten to deal with it. 564 if (Intent.ACTION_CALL_BUTTON.equals(intent.getAction())) { 565 intent.setDataAndType(Calls.CONTENT_URI, Calls.CONTENT_TYPE); 566 intent.putExtra("call_key", true); 567 setIntent(intent); 568 } 569 } 570 571 /** 572 * Returns true if the intent is due to hitting the green send key (hardware call button: 573 * KEYCODE_CALL) while in a call. 574 * 575 * @param intent the intent that launched this activity 576 * @param recentCallsRequest true if the intent is requesting to view recent calls 577 * @return true if the intent is due to hitting the green send key while in a call 578 */ 579 private boolean isSendKeyWhileInCall(Intent intent, boolean recentCallsRequest) { 580 // If there is a call in progress go to the call screen 581 if (recentCallsRequest) { 582 final boolean callKey = intent.getBooleanExtra("call_key", false); 583 584 try { 585 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone")); 586 if (callKey && phone != null && phone.showCallScreen()) { 587 return true; 588 } 589 } catch (RemoteException e) { 590 Log.e(TAG, "Failed to handle send while in call", e); 591 } 592 } 593 594 return false; 595 } 596 597 /** 598 * Sets the current tab based on the intent's request type 599 * 600 * @param intent Intent that contains information about which tab should be selected 601 */ 602 private void displayFragment(Intent intent) { 603 // TODO krelease: Make navigation via intent work by displaying the correct fragment 604 // as appropriate. 605 606 // If we got here by hitting send and we're in call forward along to the in-call activity 607 boolean recentCallsRequest = Calls.CONTENT_TYPE.equals(intent.resolveType( 608 getContentResolver())); 609 if (isSendKeyWhileInCall(intent, recentCallsRequest)) { 610 finish(); 611 return; 612 } 613 614 if (mDialpadFragment != null && (phoneIsInUse() || isDialIntent(intent))) { 615 mDialpadFragment.setStartedFromNewIntent(true); 616 // TODO krelease: This should use showDialpadFragment(false) to avoid animating 617 // the dialpad in. Need to fix the onPreDrawListener in NewDialpadFragment first. 618 showDialpadFragment(true); 619 } 620 } 621 622 @Override 623 public void onNewIntent(Intent newIntent) { 624 setIntent(newIntent); 625 fixIntent(newIntent); 626 displayFragment(newIntent); 627 final String action = newIntent.getAction(); 628 629 if (mInSearchUi || (mRegularSearchFragment != null && mRegularSearchFragment.isVisible())) { 630 exitSearchUi(); 631 } 632 633 // TODO krelease: Handle onNewIntent for all other fragments 634 /* 635 *if (mViewPager.getCurrentItem() == TAB_INDEX_DIALER) { if (mDialpadFragment != null) { 636 * mDialpadFragment.setStartedFromNewIntent(true); } else { Log.e(TAG, 637 * "DialpadFragment isn't ready yet when the tab is already selected."); } } else if 638 * (mViewPager.getCurrentItem() == TAB_INDEX_CALL_LOG) { if (mCallLogFragment != null) { 639 * mCallLogFragment.configureScreenFromIntent(newIntent); } else { Log.e(TAG, 640 * "CallLogFragment isn't ready yet when the tab is already selected."); } } 641 */ 642 invalidateOptionsMenu(); 643 } 644 645 /** Returns true if the given intent contains a phone number to populate the dialer with */ 646 private boolean isDialIntent(Intent intent) { 647 final String action = intent.getAction(); 648 if (Intent.ACTION_DIAL.equals(action) || ACTION_TOUCH_DIALER.equals(action)) { 649 return true; 650 } 651 if (Intent.ACTION_VIEW.equals(action)) { 652 final Uri data = intent.getData(); 653 if (data != null && CallUtil.SCHEME_TEL.equals(data.getScheme())) { 654 return true; 655 } 656 } 657 return false; 658 } 659 660 /** 661 * Returns an appropriate call origin for this Activity. May return null when no call origin 662 * should be used (e.g. when some 3rd party application launched the screen. Call origin is 663 * for remembering the tab in which the user made a phone call, so the external app's DIAL 664 * request should not be counted.) 665 */ 666 public String getCallOrigin() { 667 return !isDialIntent(getIntent()) ? CALL_ORIGIN_DIALTACTS : null; 668 } 669 670 /** 671 * Retrieves the filter text stored in {@link #setupFilterText(Intent)}. 672 * This text originally came from a FILTER_CONTACTS_ACTION intent received 673 * by this activity. The stored text will then be cleared after after this 674 * method returns. 675 * 676 * @return The stored filter text 677 */ 678 public String getAndClearFilterText() { 679 String filterText = mFilterText; 680 mFilterText = null; 681 return filterText; 682 } 683 684 /** 685 * Stores the filter text associated with a FILTER_CONTACTS_ACTION intent. 686 * This is so child activities can check if they are supposed to display a filter. 687 * 688 * @param intent The intent received in {@link #onNewIntent(Intent)} 689 */ 690 private void setupFilterText(Intent intent) { 691 // If the intent was relaunched from history, don't apply the filter text. 692 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) { 693 return; 694 } 695 String filter = intent.getStringExtra(UI.FILTER_TEXT_EXTRA_KEY); 696 if (filter != null && filter.length() > 0) { 697 mFilterText = filter; 698 } 699 } 700 701 private final NewPhoneFavoriteFragment.Listener mPhoneFavoriteListener = 702 new NewPhoneFavoriteFragment.Listener() { 703 @Override 704 public void onContactSelected(Uri contactUri) { 705 PhoneNumberInteraction.startInteractionForPhoneCall( 706 DialtactsActivity.this, contactUri, getCallOrigin()); 707 } 708 709 @Override 710 public void onCallNumberDirectly(String phoneNumber) { 711 Intent intent = CallUtil.getCallIntent(phoneNumber, getCallOrigin()); 712 startActivity(intent); 713 } 714 }; 715 716 /* TODO krelease: This is only relevant for phones that have a hard button search key (i.e. 717 * Nexus S). Supporting it is a little more tricky because of the dialpad fragment might 718 * be showing when the search key is pressed so there is more state management involved. 719 720 @Override 721 public void startSearch(String initialQuery, boolean selectInitialQuery, 722 Bundle appSearchData, boolean globalSearch) { 723 if (mRegularSearchFragment != null && mRegularSearchFragment.isAdded() && !globalSearch) { 724 if (mInSearchUi) { 725 if (mSearchView.hasFocus()) { 726 showInputMethod(mSearchView.findFocus()); 727 } else { 728 mSearchView.requestFocus(); 729 } 730 } else { 731 enterSearchUi(); 732 } 733 } else { 734 super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch); 735 } 736 }*/ 737 738 private void showInputMethod(View view) { 739 final InputMethodManager imm = (InputMethodManager) getSystemService( 740 Context.INPUT_METHOD_SERVICE); 741 if (imm != null) { 742 imm.showSoftInput(view, 0); 743 } 744 } 745 746 private void hideInputMethod(View view) { 747 final InputMethodManager imm = (InputMethodManager) getSystemService( 748 Context.INPUT_METHOD_SERVICE); 749 if (imm != null && view != null) { 750 imm.hideSoftInputFromWindow(view.getWindowToken(), 0); 751 } 752 } 753 754 /** 755 * Shows the search fragment 756 */ 757 private void enterSearchUi(boolean smartDialSearch) { 758 final FragmentTransaction transaction = getFragmentManager().beginTransaction(); 759 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); 760 transaction.hide(mPhoneFavoriteFragment); 761 if (smartDialSearch) { 762 transaction.show(mSmartDialSearchFragment); 763 } else { 764 transaction.show(mRegularSearchFragment); 765 } 766 transaction.commit(); 767 768 mInSearchUi = true; 769 } 770 771 /** 772 * Hides the search fragment 773 */ 774 private void exitSearchUi() { 775 final FragmentTransaction transaction = getFragmentManager().beginTransaction(); 776 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); 777 transaction.hide(mRegularSearchFragment); 778 transaction.hide(mSmartDialSearchFragment); 779 transaction.show(mPhoneFavoriteFragment); 780 transaction.commit(); 781 mInSearchUi = false; 782 } 783 784 /** Returns an Intent to launch Call Settings screen */ 785 public static Intent getCallSettingsIntent() { 786 final Intent intent = new Intent(Intent.ACTION_MAIN); 787 intent.setClassName(PHONE_PACKAGE, CALL_SETTINGS_CLASS_NAME); 788 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 789 return intent; 790 } 791 792 @Override 793 public void onBackPressed() { 794 if (mDialpadFragment.isVisible()) { 795 hideDialpadFragment(true); 796 } else if (mInSearchUi) { 797 mSearchView.setText(null); 798 } else if (mShowAllContactsFragment.isVisible()) { 799 hideAllContactsFragment(); 800 } else if (isTaskRoot()) { 801 // Instead of stopping, simply push this to the back of the stack. 802 // This is only done when running at the top of the stack; 803 // otherwise, we have been launched by someone else so need to 804 // allow the user to go back to the caller. 805 moveTaskToBack(false); 806 } else { 807 super.onBackPressed(); 808 } 809 } 810 811 @Override 812 public void onDialpadQueryChanged(String query) { 813 final String normalizedQuery = SmartDialNameMatcher.normalizeNumber(query, 814 SmartDialNameMatcher.LATIN_SMART_DIAL_MAP); 815 if (!TextUtils.equals(mSearchView.getText(), normalizedQuery)) { 816 mSearchView.setText(normalizedQuery); 817 } 818 } 819 820 @Override 821 public void onListFragmentScrollStateChange(int scrollState) { 822 if (scrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) { 823 hideDialpadFragmentIfNecessary(); 824 hideInputMethod(getCurrentFocus()); 825 } 826 } 827 828 @Override 829 public void onPhoneFavoriteFragmentStarted() { 830 setupFakeActionBarItemsForFavoritesFragment(); 831 } 832 833 @Override 834 public void onDialpadFragmentStarted() { 835 setupFakeActionBarItemsForDialpadFragment(); 836 } 837 838 private boolean phoneIsInUse() { 839 final TelephonyManager tm = (TelephonyManager) getSystemService( 840 Context.TELEPHONY_SERVICE); 841 return tm.getCallState() != TelephonyManager.CALL_STATE_IDLE; 842 } 843} 844