Home | History | Annotate | Download | only in dialer
      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 
     17 package com.android.dialer;
     18 
     19 import android.animation.Animator;
     20 import android.animation.Animator.AnimatorListener;
     21 import android.animation.AnimatorListenerAdapter;
     22 import android.app.Activity;
     23 import android.app.Fragment;
     24 import android.app.FragmentManager;
     25 import android.app.FragmentManager.BackStackEntry;
     26 import android.app.FragmentTransaction;
     27 import android.content.ActivityNotFoundException;
     28 import android.content.Context;
     29 import android.content.Intent;
     30 import android.graphics.drawable.Drawable;
     31 import android.net.Uri;
     32 import android.os.Bundle;
     33 import android.os.RemoteException;
     34 import android.os.ServiceManager;
     35 import android.provider.CallLog.Calls;
     36 import android.provider.ContactsContract.Contacts;
     37 import android.provider.ContactsContract.Intents;
     38 import android.provider.ContactsContract.Intents.UI;
     39 import android.speech.RecognizerIntent;
     40 import android.telephony.TelephonyManager;
     41 import android.text.Editable;
     42 import android.text.Spannable;
     43 import android.text.SpannableStringBuilder;
     44 import android.text.TextUtils;
     45 import android.text.TextWatcher;
     46 import android.text.style.ImageSpan;
     47 import android.util.Log;
     48 import android.view.Menu;
     49 import android.view.MenuItem;
     50 import android.view.View;
     51 import android.view.View.OnFocusChangeListener;
     52 import android.view.inputmethod.InputMethodManager;
     53 import android.widget.AbsListView.OnScrollListener;
     54 import android.widget.EditText;
     55 import android.widget.PopupMenu;
     56 import android.widget.Toast;
     57 
     58 import com.android.contacts.common.CallUtil;
     59 import com.android.contacts.common.activity.TransactionSafeActivity;
     60 import com.android.contacts.common.dialog.ClearFrequentsDialog;
     61 import com.android.contacts.common.interactions.ImportExportDialogFragment;
     62 import com.android.contacts.common.list.OnPhoneNumberPickerActionListener;
     63 import com.android.dialer.calllog.CallLogActivity;
     64 import com.android.dialer.database.DialerDatabaseHelper;
     65 import com.android.dialer.dialpad.DialpadFragment;
     66 import com.android.dialer.dialpad.SmartDialNameMatcher;
     67 import com.android.dialer.dialpad.SmartDialPrefix;
     68 import com.android.dialer.interactions.PhoneNumberInteraction;
     69 import com.android.dialer.list.AllContactsActivity;
     70 import com.android.dialer.list.OnListFragmentScrolledListener;
     71 import com.android.dialer.list.PhoneFavoriteFragment;
     72 import com.android.dialer.list.RegularSearchFragment;
     73 import com.android.dialer.list.SearchFragment;
     74 import com.android.dialer.list.SmartDialSearchFragment;
     75 import com.android.dialerbind.DatabaseHelperManager;
     76 import com.android.internal.telephony.ITelephony;
     77 
     78 import java.util.ArrayList;
     79 
     80 /**
     81  * The dialer tab's title is 'phone', a more common name (see strings.xml).
     82  */
     83 public class DialtactsActivity extends TransactionSafeActivity implements View.OnClickListener,
     84         DialpadFragment.OnDialpadQueryChangedListener, PopupMenu.OnMenuItemClickListener,
     85         OnListFragmentScrolledListener,
     86         DialpadFragment.OnDialpadFragmentStartedListener,
     87         PhoneFavoriteFragment.OnShowAllContactsListener {
     88     private static final String TAG = "DialtactsActivity";
     89 
     90     public static final boolean DEBUG = false;
     91 
     92     public static final String SHARED_PREFS_NAME = "com.android.dialer_preferences";
     93 
     94     /** Used to open Call Setting */
     95     private static final String PHONE_PACKAGE = "com.android.phone";
     96     private static final String CALL_SETTINGS_CLASS_NAME =
     97             "com.android.phone.CallFeaturesSetting";
     98     /** @see #getCallOrigin() */
     99     private static final String CALL_ORIGIN_DIALTACTS =
    100             "com.android.dialer.DialtactsActivity";
    101 
    102     private static final String KEY_IN_REGULAR_SEARCH_UI = "in_regular_search_ui";
    103     private static final String KEY_IN_DIALPAD_SEARCH_UI = "in_dialpad_search_ui";
    104     private static final String KEY_SEARCH_QUERY = "search_query";
    105     private static final String KEY_FIRST_LAUNCH = "first_launch";
    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 
    112     /**
    113      * Just for backward compatibility. Should behave as same as {@link Intent#ACTION_DIAL}.
    114      */
    115     private static final String ACTION_TOUCH_DIALER = "com.android.phone.action.TOUCH_DIALER";
    116 
    117     private static final int SUBACTIVITY_ACCOUNT_FILTER = 1;
    118 
    119     private static final int ACTIVITY_REQUEST_CODE_VOICE_SEARCH = 1;
    120 
    121     private String mFilterText;
    122 
    123     /**
    124      * The main fragment displaying the user's favorites and frequent contacts
    125      */
    126     private PhoneFavoriteFragment mPhoneFavoriteFragment;
    127 
    128     /**
    129      * Fragment containing the dialpad that slides into view
    130      */
    131     private DialpadFragment mDialpadFragment;
    132 
    133     /**
    134      * Fragment for searching phone numbers using the alphanumeric keyboard.
    135      */
    136     private RegularSearchFragment mRegularSearchFragment;
    137 
    138     /**
    139      * Fragment for searching phone numbers using the dialpad.
    140      */
    141     private SmartDialSearchFragment mSmartDialSearchFragment;
    142 
    143     private View mMenuButton;
    144     private View mCallHistoryButton;
    145     private View mDialpadButton;
    146     private PopupMenu mOverflowMenu;
    147 
    148     // Padding view used to shift the fragments up when the dialpad is shown.
    149     private View mBottomPaddingView;
    150     private View mFragmentsFrame;
    151     private View mActionBar;
    152 
    153     private boolean mInDialpadSearch;
    154     private boolean mInRegularSearch;
    155     private boolean mClearSearchOnPause;
    156 
    157     /**
    158      * True if the dialpad is only temporarily showing due to being in call
    159      */
    160     private boolean mInCallDialpadUp;
    161 
    162     /**
    163      * True when this activity has been launched for the first time.
    164      */
    165     private boolean mFirstLaunch;
    166     private View mSearchViewContainer;
    167     private View mSearchViewCloseButton;
    168     private View mVoiceSearchButton;
    169     private EditText mSearchView;
    170 
    171     private String mSearchQuery;
    172 
    173     private DialerDatabaseHelper mDialerDatabaseHelper;
    174 
    175     private class OverflowPopupMenu extends PopupMenu {
    176         public OverflowPopupMenu(Context context, View anchor) {
    177             super(context, anchor);
    178         }
    179 
    180         @Override
    181         public void show() {
    182             final Menu menu = getMenu();
    183             final MenuItem clearFrequents = menu.findItem(R.id.menu_clear_frequents);
    184             clearFrequents.setVisible(mPhoneFavoriteFragment.hasFrequents());
    185             super.show();
    186         }
    187     }
    188 
    189     /**
    190      * Listener used when one of phone numbers in search UI is selected. This will initiate a
    191      * phone call using the phone number.
    192      */
    193     private final OnPhoneNumberPickerActionListener mPhoneNumberPickerActionListener =
    194             new OnPhoneNumberPickerActionListener() {
    195                 @Override
    196                 public void onPickPhoneNumberAction(Uri dataUri) {
    197                     // Specify call-origin so that users will see the previous tab instead of
    198                     // CallLog screen (search UI will be automatically exited).
    199                     PhoneNumberInteraction.startInteractionForPhoneCall(
    200                         DialtactsActivity.this, dataUri, getCallOrigin());
    201                     mClearSearchOnPause = true;
    202                 }
    203 
    204                 @Override
    205                 public void onCallNumberDirectly(String phoneNumber) {
    206                     Intent intent = CallUtil.getCallIntent(phoneNumber, getCallOrigin());
    207                     startActivity(intent);
    208                     mClearSearchOnPause = true;
    209                 }
    210 
    211                 @Override
    212                 public void onShortcutIntentCreated(Intent intent) {
    213                     Log.w(TAG, "Unsupported intent has come (" + intent + "). Ignoring.");
    214                 }
    215 
    216                 @Override
    217                 public void onHomeInActionBarSelected() {
    218                     exitSearchUi();
    219                 }
    220     };
    221 
    222     /**
    223      * Listener used to send search queries to the phone search fragment.
    224      */
    225     private final TextWatcher mPhoneSearchQueryTextListener = new TextWatcher() {
    226             @Override
    227             public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    228             }
    229 
    230             @Override
    231             public void onTextChanged(CharSequence s, int start, int before, int count) {
    232                 final String newText = s.toString();
    233                 if (newText.equals(mSearchQuery)) {
    234                     // If the query hasn't changed (perhaps due to activity being destroyed
    235                     // and restored, or user launching the same DIAL intent twice), then there is
    236                     // no need to do anything here.
    237                     return;
    238                 }
    239                 mSearchQuery = newText;
    240                 if (DEBUG) {
    241                     Log.d(TAG, "onTextChange for mSearchView called with new query: " + s);
    242                 }
    243                 final boolean dialpadSearch = isDialpadShowing();
    244 
    245                 // Show search result with non-empty text. Show a bare list otherwise.
    246                 if (TextUtils.isEmpty(newText) && getInSearchUi()) {
    247                     exitSearchUi();
    248                     mSearchViewCloseButton.setVisibility(View.GONE);
    249                     mVoiceSearchButton.setVisibility(View.VISIBLE);
    250                     return;
    251                 } else if (!TextUtils.isEmpty(newText)) {
    252                     final boolean sameSearchMode = (dialpadSearch && mInDialpadSearch) ||
    253                             (!dialpadSearch && mInRegularSearch);
    254                     if (!sameSearchMode) {
    255                         // call enterSearchUi only if we are switching search modes, or entering
    256                         // search ui for the first time
    257                         enterSearchUi(dialpadSearch, newText);
    258                     }
    259 
    260                     if (dialpadSearch && mSmartDialSearchFragment != null) {
    261                             mSmartDialSearchFragment.setQueryString(newText, false);
    262                     } else if (mRegularSearchFragment != null) {
    263                         mRegularSearchFragment.setQueryString(newText, false);
    264                     }
    265                     mSearchViewCloseButton.setVisibility(View.VISIBLE);
    266                     mVoiceSearchButton.setVisibility(View.GONE);
    267                     return;
    268                 }
    269             }
    270 
    271             @Override
    272             public void afterTextChanged(Editable s) {
    273             }
    274     };
    275 
    276     private boolean isDialpadShowing() {
    277         return mDialpadFragment != null && mDialpadFragment.isVisible();
    278     }
    279 
    280     @Override
    281     protected void onCreate(Bundle savedInstanceState) {
    282         super.onCreate(savedInstanceState);
    283         mFirstLaunch = true;
    284 
    285         final Intent intent = getIntent();
    286         fixIntent(intent);
    287 
    288         setContentView(R.layout.dialtacts_activity);
    289 
    290         getActionBar().hide();
    291 
    292         // Add the favorites fragment, and the dialpad fragment, but only if savedInstanceState
    293         // is null. Otherwise the fragment manager takes care of recreating these fragments.
    294         if (savedInstanceState == null) {
    295             final PhoneFavoriteFragment phoneFavoriteFragment = new PhoneFavoriteFragment();
    296 
    297             final FragmentTransaction ft = getFragmentManager().beginTransaction();
    298             ft.add(R.id.dialtacts_frame, phoneFavoriteFragment, TAG_FAVORITES_FRAGMENT);
    299             ft.add(R.id.dialtacts_container, new DialpadFragment(), TAG_DIALPAD_FRAGMENT);
    300             ft.commit();
    301         } else {
    302             mSearchQuery = savedInstanceState.getString(KEY_SEARCH_QUERY);
    303             mInRegularSearch = savedInstanceState.getBoolean(KEY_IN_REGULAR_SEARCH_UI);
    304             mInDialpadSearch = savedInstanceState.getBoolean(KEY_IN_DIALPAD_SEARCH_UI);
    305             mFirstLaunch = savedInstanceState.getBoolean(KEY_FIRST_LAUNCH);
    306         }
    307 
    308         mBottomPaddingView = findViewById(R.id.dialtacts_bottom_padding);
    309         mFragmentsFrame = findViewById(R.id.dialtacts_frame);
    310         mActionBar = findViewById(R.id.fake_action_bar);
    311         prepareSearchView();
    312 
    313         if (UI.FILTER_CONTACTS_ACTION.equals(intent.getAction())
    314                 && savedInstanceState == null) {
    315             setupFilterText(intent);
    316         }
    317 
    318         setupFakeActionBarItems();
    319 
    320         mDialerDatabaseHelper = DatabaseHelperManager.getDatabaseHelper(this);
    321         SmartDialPrefix.initializeNanpSettings(this);
    322     }
    323 
    324     @Override
    325     protected void onResume() {
    326         super.onResume();
    327         if (mFirstLaunch) {
    328             displayFragment(getIntent());
    329         } else if (!phoneIsInUse() && mInCallDialpadUp) {
    330             hideDialpadFragment(false, true);
    331             mInCallDialpadUp = false;
    332         }
    333 
    334         mFirstLaunch = false;
    335         mDialerDatabaseHelper.startSmartDialUpdateThread();
    336     }
    337 
    338     @Override
    339     protected void onPause() {
    340         if (mClearSearchOnPause) {
    341             hideDialpadAndSearchUi();
    342             mClearSearchOnPause = false;
    343         }
    344         super.onPause();
    345     }
    346 
    347     @Override
    348     protected void onSaveInstanceState(Bundle outState) {
    349         super.onSaveInstanceState(outState);
    350         outState.putString(KEY_SEARCH_QUERY, mSearchQuery);
    351         outState.putBoolean(KEY_IN_REGULAR_SEARCH_UI, mInRegularSearch);
    352         outState.putBoolean(KEY_IN_DIALPAD_SEARCH_UI, mInDialpadSearch);
    353         outState.putBoolean(KEY_FIRST_LAUNCH, mFirstLaunch);
    354     }
    355 
    356     @Override
    357     public void onAttachFragment(Fragment fragment) {
    358         if (fragment instanceof DialpadFragment) {
    359             mDialpadFragment = (DialpadFragment) fragment;
    360             final FragmentTransaction transaction = getFragmentManager().beginTransaction();
    361             transaction.hide(mDialpadFragment);
    362             transaction.commit();
    363         } else if (fragment instanceof SmartDialSearchFragment) {
    364             mSmartDialSearchFragment = (SmartDialSearchFragment) fragment;
    365             mSmartDialSearchFragment.setOnPhoneNumberPickerActionListener(
    366                     mPhoneNumberPickerActionListener);
    367         } else if (fragment instanceof SearchFragment) {
    368             mRegularSearchFragment = (RegularSearchFragment) fragment;
    369             mRegularSearchFragment.setOnPhoneNumberPickerActionListener(
    370                     mPhoneNumberPickerActionListener);
    371         } else if (fragment instanceof PhoneFavoriteFragment) {
    372             mPhoneFavoriteFragment = (PhoneFavoriteFragment) fragment;
    373             mPhoneFavoriteFragment.setListener(mPhoneFavoriteListener);
    374         }
    375     }
    376 
    377     @Override
    378     public boolean onMenuItemClick(MenuItem item) {
    379         switch (item.getItemId()) {
    380             case R.id.menu_import_export:
    381                 // We hard-code the "contactsAreAvailable" argument because doing it properly would
    382                 // involve querying a {@link ProviderStatusLoader}, which we don't want to do right
    383                 // now in Dialtacts for (potential) performance reasons. Compare with how it is
    384                 // done in {@link PeopleActivity}.
    385                 ImportExportDialogFragment.show(getFragmentManager(), true,
    386                         DialtactsActivity.class);
    387                 return true;
    388             case R.id.menu_clear_frequents:
    389                 ClearFrequentsDialog.show(getFragmentManager());
    390                 return true;
    391             case R.id.menu_add_contact:
    392                 try {
    393                     startActivity(new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI));
    394                 } catch (ActivityNotFoundException e) {
    395                     Toast toast = Toast.makeText(this,
    396                             R.string.add_contact_not_available,
    397                             Toast.LENGTH_SHORT);
    398                     toast.show();
    399                 }
    400                 return true;
    401             case R.id.menu_call_settings:
    402                 handleMenuSettings();
    403                 return true;
    404             case R.id.menu_all_contacts:
    405                 onShowAllContacts();
    406                 return true;
    407         }
    408         return false;
    409     }
    410 
    411     protected void handleMenuSettings() {
    412         openTelephonySetting(this);
    413     }
    414 
    415     public static void openTelephonySetting(Activity activity) {
    416         final Intent settingsIntent = getCallSettingsIntent();
    417         activity.startActivity(settingsIntent);
    418     }
    419 
    420     @Override
    421     public void onClick(View view) {
    422         switch (view.getId()) {
    423             case R.id.overflow_menu: {
    424                 mOverflowMenu.show();
    425                 break;
    426             }
    427             case R.id.dialpad_button:
    428                 // Reset the boolean flag that tracks whether the dialpad was up because
    429                 // we were in call. Regardless of whether it was true before, we want to
    430                 // show the dialpad because the user has explicitly clicked the dialpad
    431                 // button.
    432                 mInCallDialpadUp = false;
    433                 showDialpadFragment(true);
    434                 break;
    435             case R.id.call_history_on_dialpad_button:
    436             case R.id.call_history_button:
    437                 // Use explicit CallLogActivity intent instead of ACTION_VIEW +
    438                 // CONTENT_TYPE, so that we always open our call log from our dialer
    439                 final Intent intent = new Intent(this, CallLogActivity.class);
    440                 startActivity(intent);
    441                 break;
    442             case R.id.search_close_button:
    443                 // Clear the search field
    444                 if (!TextUtils.isEmpty(mSearchView.getText())) {
    445                     mDialpadFragment.clearDialpad();
    446                     mSearchView.setText("");
    447                 }
    448                 break;
    449             case R.id.voice_search_button:
    450                 final Intent voiceIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
    451                 startActivityForResult(voiceIntent, ACTIVITY_REQUEST_CODE_VOICE_SEARCH);
    452                 break;
    453             default: {
    454                 Log.wtf(TAG, "Unexpected onClick event from " + view);
    455                 break;
    456             }
    457         }
    458     }
    459 
    460     @Override
    461     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    462         if (requestCode == ACTIVITY_REQUEST_CODE_VOICE_SEARCH) {
    463             if (resultCode == RESULT_OK) {
    464                 final ArrayList<String> matches = data.getStringArrayListExtra(
    465                         RecognizerIntent.EXTRA_RESULTS);
    466                 if (matches.size() > 0) {
    467                     final String match = matches.get(0);
    468                     mSearchView.setText(match);
    469                 } else {
    470                     Log.e(TAG, "Voice search - nothing heard");
    471                 }
    472             } else {
    473                 Log.e(TAG, "Voice search failed");
    474             }
    475         }
    476         super.onActivityResult(requestCode, resultCode, data);
    477     }
    478 
    479     private void showDialpadFragment(boolean animate) {
    480         mDialpadFragment.setAdjustTranslationForAnimation(animate);
    481         final FragmentTransaction ft = getFragmentManager().beginTransaction();
    482         if (animate) {
    483             ft.setCustomAnimations(R.anim.slide_in, 0);
    484         } else {
    485             mDialpadFragment.setYFraction(0);
    486         }
    487         ft.show(mDialpadFragment);
    488         ft.commit();
    489     }
    490 
    491     public void hideDialpadFragment(boolean animate, boolean clearDialpad) {
    492         if (mDialpadFragment == null) return;
    493         if (clearDialpad) {
    494             mDialpadFragment.clearDialpad();
    495         }
    496         if (!mDialpadFragment.isVisible()) return;
    497         mDialpadFragment.setAdjustTranslationForAnimation(animate);
    498         final FragmentTransaction ft = getFragmentManager().beginTransaction();
    499         if (animate) {
    500             ft.setCustomAnimations(0, R.anim.slide_out);
    501         }
    502         ft.hide(mDialpadFragment);
    503         ft.commit();
    504     }
    505 
    506     private void prepareSearchView() {
    507         mSearchViewContainer = findViewById(R.id.search_view_container);
    508         mSearchViewCloseButton = findViewById(R.id.search_close_button);
    509         mSearchViewCloseButton.setOnClickListener(this);
    510         mVoiceSearchButton = findViewById(R.id.voice_search_button);
    511         mVoiceSearchButton.setOnClickListener(this);
    512         mSearchView = (EditText) findViewById(R.id.search_view);
    513         mSearchView.addTextChangedListener(mPhoneSearchQueryTextListener);
    514 
    515         final String hintText = getString(R.string.dialer_hint_find_contact);
    516 
    517         // The following code is used to insert an icon into a CharSequence (copied from
    518         // SearchView)
    519         final SpannableStringBuilder ssb = new SpannableStringBuilder("   "); // for the icon
    520         ssb.append(hintText);
    521         final Drawable searchIcon = getResources().getDrawable(R.drawable.ic_ab_search);
    522         final int textSize = (int) (mSearchView.getTextSize() * 1.20);
    523         searchIcon.setBounds(0, 0, textSize, textSize);
    524         ssb.setSpan(new ImageSpan(searchIcon), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    525 
    526         mSearchView.setHint(ssb);
    527     }
    528 
    529     final AnimatorListener mHideListener = new AnimatorListenerAdapter() {
    530         @Override
    531         public void onAnimationEnd(Animator animation) {
    532             mSearchViewContainer.setVisibility(View.GONE);
    533         }
    534     };
    535 
    536     private boolean getInSearchUi() {
    537         return mInDialpadSearch || mInRegularSearch;
    538     }
    539 
    540     private void setNotInSearchUi() {
    541         mInDialpadSearch = false;
    542         mInRegularSearch = false;
    543     }
    544 
    545     private void hideDialpadAndSearchUi() {
    546         mSearchView.setText(null);
    547         hideDialpadFragment(false, true);
    548     }
    549 
    550     public void hideSearchBar() {
    551        hideSearchBar(true);
    552     }
    553 
    554     public void hideSearchBar(boolean shiftView) {
    555         if (shiftView) {
    556             mSearchViewContainer.animate().cancel();
    557             mSearchViewContainer.setAlpha(1);
    558             mSearchViewContainer.setTranslationY(0);
    559             mSearchViewContainer.animate().withLayer().alpha(0).translationY(-mSearchView.getHeight())
    560                     .setDuration(200).setListener(mHideListener);
    561 
    562             mFragmentsFrame.animate().withLayer()
    563                     .translationY(-mSearchViewContainer.getHeight()).setDuration(200).setListener(
    564                     new AnimatorListenerAdapter() {
    565                         @Override
    566                         public void onAnimationEnd(Animator animation) {
    567                             mBottomPaddingView.setVisibility(View.VISIBLE);
    568                             mFragmentsFrame.setTranslationY(0);
    569                             mActionBar.setVisibility(View.INVISIBLE);
    570                         }
    571                     });
    572         } else {
    573             mSearchViewContainer.setTranslationY(-mSearchView.getHeight());
    574             mActionBar.setVisibility(View.INVISIBLE);
    575         }
    576     }
    577 
    578     public void showSearchBar() {
    579         mSearchViewContainer.animate().cancel();
    580         mSearchViewContainer.setAlpha(0);
    581         mSearchViewContainer.setTranslationY(-mSearchViewContainer.getHeight());
    582         mSearchViewContainer.animate().withLayer().alpha(1).translationY(0).setDuration(200)
    583                 .setListener(new AnimatorListenerAdapter() {
    584                     @Override
    585                     public void onAnimationStart(Animator animation) {
    586                         mSearchViewContainer.setVisibility(View.VISIBLE);
    587                         mActionBar.setVisibility(View.VISIBLE);
    588                     }
    589                 });
    590 
    591         mFragmentsFrame.setTranslationY(-mSearchViewContainer.getHeight());
    592         mFragmentsFrame.animate().withLayer().translationY(0).setDuration(200)
    593                 .setListener(
    594                         new AnimatorListenerAdapter() {
    595                             @Override
    596                             public void onAnimationStart(Animator animation) {
    597                                 mBottomPaddingView.setVisibility(View.GONE);
    598                             }
    599                         });
    600     }
    601 
    602 
    603     public void setupFakeActionBarItems() {
    604         mMenuButton = findViewById(R.id.overflow_menu);
    605         if (mMenuButton != null) {
    606             mMenuButton.setOnClickListener(this);
    607 
    608             mOverflowMenu = new OverflowPopupMenu(DialtactsActivity.this, mMenuButton);
    609             final Menu menu = mOverflowMenu.getMenu();
    610             mOverflowMenu.inflate(R.menu.dialtacts_options);
    611             mOverflowMenu.setOnMenuItemClickListener(this);
    612             mMenuButton.setOnTouchListener(mOverflowMenu.getDragToOpenListener());
    613         }
    614 
    615         mCallHistoryButton = findViewById(R.id.call_history_button);
    616         // mCallHistoryButton.setMinimumWidth(fakeMenuItemWidth);
    617         mCallHistoryButton.setOnClickListener(this);
    618 
    619         mDialpadButton = findViewById(R.id.dialpad_button);
    620         // DialpadButton.setMinimumWidth(fakeMenuItemWidth);
    621         mDialpadButton.setOnClickListener(this);
    622     }
    623 
    624     public void setupFakeActionBarItemsForDialpadFragment() {
    625         final View callhistoryButton = findViewById(R.id.call_history_on_dialpad_button);
    626         callhistoryButton.setOnClickListener(this);
    627     }
    628 
    629     private void fixIntent(Intent intent) {
    630         // This should be cleaned up: the call key used to send an Intent
    631         // that just said to go to the recent calls list.  It now sends this
    632         // abstract action, but this class hasn't been rewritten to deal with it.
    633         if (Intent.ACTION_CALL_BUTTON.equals(intent.getAction())) {
    634             intent.setDataAndType(Calls.CONTENT_URI, Calls.CONTENT_TYPE);
    635             intent.putExtra("call_key", true);
    636             setIntent(intent);
    637         }
    638     }
    639 
    640     /**
    641      * Returns true if the intent is due to hitting the green send key (hardware call button:
    642      * KEYCODE_CALL) while in a call.
    643      *
    644      * @param intent the intent that launched this activity
    645      * @param recentCallsRequest true if the intent is requesting to view recent calls
    646      * @return true if the intent is due to hitting the green send key while in a call
    647      */
    648     private boolean isSendKeyWhileInCall(Intent intent, boolean recentCallsRequest) {
    649         // If there is a call in progress go to the call screen
    650         if (recentCallsRequest) {
    651             final boolean callKey = intent.getBooleanExtra("call_key", false);
    652 
    653             try {
    654                 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
    655                 if (callKey && phone != null && phone.showCallScreen()) {
    656                     return true;
    657                 }
    658             } catch (RemoteException e) {
    659                 Log.e(TAG, "Failed to handle send while in call", e);
    660             }
    661         }
    662 
    663         return false;
    664     }
    665 
    666     /**
    667      * Sets the current tab based on the intent's request type
    668      *
    669      * @param intent Intent that contains information about which tab should be selected
    670      */
    671     private void displayFragment(Intent intent) {
    672         // If we got here by hitting send and we're in call forward along to the in-call activity
    673         boolean recentCallsRequest = Calls.CONTENT_TYPE.equals(intent.resolveType(
    674             getContentResolver()));
    675         if (isSendKeyWhileInCall(intent, recentCallsRequest)) {
    676             finish();
    677             return;
    678         }
    679 
    680         if (mDialpadFragment != null) {
    681             final boolean phoneIsInUse = phoneIsInUse();
    682             if (phoneIsInUse || isDialIntent(intent)) {
    683                 mDialpadFragment.setStartedFromNewIntent(true);
    684                 if (phoneIsInUse && !mDialpadFragment.isVisible()) {
    685                     mInCallDialpadUp = true;
    686                 }
    687                 showDialpadFragment(false);
    688             }
    689         }
    690     }
    691 
    692     @Override
    693     public void onNewIntent(Intent newIntent) {
    694         setIntent(newIntent);
    695         fixIntent(newIntent);
    696         displayFragment(newIntent);
    697         final String action = newIntent.getAction();
    698 
    699         invalidateOptionsMenu();
    700     }
    701 
    702     /** Returns true if the given intent contains a phone number to populate the dialer with */
    703     private boolean isDialIntent(Intent intent) {
    704         final String action = intent.getAction();
    705         if (Intent.ACTION_DIAL.equals(action) || ACTION_TOUCH_DIALER.equals(action)) {
    706             return true;
    707         }
    708         if (Intent.ACTION_VIEW.equals(action)) {
    709             final Uri data = intent.getData();
    710             if (data != null && CallUtil.SCHEME_TEL.equals(data.getScheme())) {
    711                 return true;
    712             }
    713         }
    714         return false;
    715     }
    716 
    717     /**
    718      * Returns an appropriate call origin for this Activity. May return null when no call origin
    719      * should be used (e.g. when some 3rd party application launched the screen. Call origin is
    720      * for remembering the tab in which the user made a phone call, so the external app's DIAL
    721      * request should not be counted.)
    722      */
    723     public String getCallOrigin() {
    724         return !isDialIntent(getIntent()) ? CALL_ORIGIN_DIALTACTS : null;
    725     }
    726 
    727     /**
    728      * Retrieves the filter text stored in {@link #setupFilterText(Intent)}.
    729      * This text originally came from a FILTER_CONTACTS_ACTION intent received
    730      * by this activity. The stored text will then be cleared after after this
    731      * method returns.
    732      *
    733      * @return The stored filter text
    734      */
    735     public String getAndClearFilterText() {
    736         String filterText = mFilterText;
    737         mFilterText = null;
    738         return filterText;
    739     }
    740 
    741     /**
    742      * Stores the filter text associated with a FILTER_CONTACTS_ACTION intent.
    743      * This is so child activities can check if they are supposed to display a filter.
    744      *
    745      * @param intent The intent received in {@link #onNewIntent(Intent)}
    746      */
    747     private void setupFilterText(Intent intent) {
    748         // If the intent was relaunched from history, don't apply the filter text.
    749         if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) {
    750             return;
    751         }
    752         String filter = intent.getStringExtra(UI.FILTER_TEXT_EXTRA_KEY);
    753         if (filter != null && filter.length() > 0) {
    754             mFilterText = filter;
    755         }
    756     }
    757 
    758     private final PhoneFavoriteFragment.Listener mPhoneFavoriteListener =
    759             new PhoneFavoriteFragment.Listener() {
    760         @Override
    761         public void onContactSelected(Uri contactUri) {
    762             PhoneNumberInteraction.startInteractionForPhoneCall(
    763                         DialtactsActivity.this, contactUri, getCallOrigin());
    764         }
    765 
    766         @Override
    767         public void onCallNumberDirectly(String phoneNumber) {
    768             Intent intent = CallUtil.getCallIntent(phoneNumber, getCallOrigin());
    769             startActivity(intent);
    770         }
    771     };
    772 
    773     /* TODO krelease: This is only relevant for phones that have a hard button search key (i.e.
    774      * Nexus S). Supporting it is a little more tricky because of the dialpad fragment might
    775      * be showing when the search key is pressed so there is more state management involved.
    776 
    777     @Override
    778     public void startSearch(String initialQuery, boolean selectInitialQuery,
    779             Bundle appSearchData, boolean globalSearch) {
    780         if (mRegularSearchFragment != null && mRegularSearchFragment.isAdded() && !globalSearch) {
    781             if (mInSearchUi) {
    782                 if (mSearchView.hasFocus()) {
    783                     showInputMethod(mSearchView.findFocus());
    784                 } else {
    785                     mSearchView.requestFocus();
    786                 }
    787             } else {
    788                 enterSearchUi();
    789             }
    790         } else {
    791             super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
    792         }
    793     }*/
    794 
    795     private void showInputMethod(View view) {
    796         final InputMethodManager imm = (InputMethodManager) getSystemService(
    797                 Context.INPUT_METHOD_SERVICE);
    798         if (imm != null) {
    799             imm.showSoftInput(view, 0);
    800         }
    801     }
    802 
    803     private void hideInputMethod(View view) {
    804         final InputMethodManager imm = (InputMethodManager) getSystemService(
    805                 Context.INPUT_METHOD_SERVICE);
    806         if (imm != null && view != null) {
    807             imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
    808         }
    809     }
    810 
    811     /**
    812      * Shows the search fragment
    813      */
    814     private void enterSearchUi(boolean smartDialSearch, String query) {
    815         if (getFragmentManager().isDestroyed()) {
    816             // Weird race condition where fragment is doing work after the activity is destroyed
    817             // due to talkback being on (b/10209937). Just return since we can't do any
    818             // constructive here.
    819             return;
    820         }
    821 
    822         if (DEBUG) {
    823             Log.d(TAG, "Entering search UI - smart dial " + smartDialSearch);
    824         }
    825 
    826         final FragmentTransaction transaction = getFragmentManager().beginTransaction();
    827         transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
    828 
    829         SearchFragment fragment;
    830         if (mInDialpadSearch) {
    831             transaction.remove(mSmartDialSearchFragment);
    832         } else if (mInRegularSearch) {
    833             transaction.remove(mRegularSearchFragment);
    834         } else {
    835             transaction.remove(mPhoneFavoriteFragment);
    836         }
    837 
    838         final String tag;
    839         if (smartDialSearch) {
    840             tag = TAG_SMARTDIAL_SEARCH_FRAGMENT;
    841         } else {
    842             tag = TAG_REGULAR_SEARCH_FRAGMENT;
    843         }
    844         mInDialpadSearch = smartDialSearch;
    845         mInRegularSearch = !smartDialSearch;
    846 
    847         fragment = (SearchFragment) getFragmentManager().findFragmentByTag(tag);
    848         if (fragment == null) {
    849             if (smartDialSearch) {
    850                 fragment = new SmartDialSearchFragment();
    851             } else {
    852                 fragment = new RegularSearchFragment();
    853             }
    854         }
    855         transaction.replace(R.id.dialtacts_frame, fragment, tag);
    856         transaction.addToBackStack(null);
    857         fragment.setQueryString(query, false);
    858         transaction.commit();
    859     }
    860 
    861     /**
    862      * Hides the search fragment
    863      */
    864     private void exitSearchUi() {
    865         // See related bug in enterSearchUI();
    866         if (getFragmentManager().isDestroyed()) {
    867             return;
    868         }
    869         // Go all the way back to the favorites fragment, regardless of how many times we
    870         // transitioned between search fragments
    871         getFragmentManager().popBackStack(0, FragmentManager.POP_BACK_STACK_INCLUSIVE);
    872         setNotInSearchUi();
    873     }
    874 
    875     /** Returns an Intent to launch Call Settings screen */
    876     public static Intent getCallSettingsIntent() {
    877         final Intent intent = new Intent(Intent.ACTION_MAIN);
    878         intent.setClassName(PHONE_PACKAGE, CALL_SETTINGS_CLASS_NAME);
    879         intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    880         return intent;
    881     }
    882 
    883     @Override
    884     public void onBackPressed() {
    885         if (mDialpadFragment != null && mDialpadFragment.isVisible()) {
    886             hideDialpadFragment(true, false);
    887         } else if (getInSearchUi()) {
    888             mSearchView.setText(null);
    889             mDialpadFragment.clearDialpad();
    890         } else if (isTaskRoot()) {
    891             // Instead of stopping, simply push this to the back of the stack.
    892             // This is only done when running at the top of the stack;
    893             // otherwise, we have been launched by someone else so need to
    894             // allow the user to go back to the caller.
    895             moveTaskToBack(false);
    896         } else {
    897             super.onBackPressed();
    898         }
    899     }
    900 
    901     @Override
    902     public void onDialpadQueryChanged(String query) {
    903         final String normalizedQuery = SmartDialNameMatcher.normalizeNumber(query,
    904                 SmartDialNameMatcher.LATIN_SMART_DIAL_MAP);
    905         if (!TextUtils.equals(mSearchView.getText(), normalizedQuery)) {
    906             if (DEBUG) {
    907                 Log.d(TAG, "onDialpadQueryChanged - new query: " + query);
    908             }
    909             if (mDialpadFragment == null || !mDialpadFragment.isVisible()) {
    910                 // This callback can happen if the dialpad fragment is recreated because of
    911                 // activity destruction. In that case, don't update the search view because
    912                 // that would bring the user back to the search fragment regardless of the
    913                 // previous state of the application. Instead, just return here and let the
    914                 // fragment manager correctly figure out whatever fragment was last displayed.
    915                 return;
    916             }
    917             mSearchView.setText(normalizedQuery);
    918         }
    919     }
    920 
    921     @Override
    922     public void onListFragmentScrollStateChange(int scrollState) {
    923         if (scrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
    924             hideDialpadFragment(true, false);
    925             hideInputMethod(getCurrentFocus());
    926         }
    927     }
    928 
    929     @Override
    930     public void onDialpadFragmentStarted() {
    931         setupFakeActionBarItemsForDialpadFragment();
    932     }
    933 
    934     private boolean phoneIsInUse() {
    935         final TelephonyManager tm = (TelephonyManager) getSystemService(
    936                 Context.TELEPHONY_SERVICE);
    937         return tm.getCallState() != TelephonyManager.CALL_STATE_IDLE;
    938     }
    939 
    940     @Override
    941     public void onShowAllContacts() {
    942         final Intent intent = new Intent(this, AllContactsActivity.class);
    943         startActivity(intent);
    944     }
    945 
    946     public static Intent getAddNumberToContactIntent(CharSequence text) {
    947         final Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
    948         intent.putExtra(Intents.Insert.PHONE, text);
    949         intent.setType(Contacts.CONTENT_ITEM_TYPE);
    950         return intent;
    951     }
    952 
    953     public static Intent getInsertContactWithNameIntent(CharSequence text) {
    954         final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
    955         intent.putExtra(Intents.Insert.NAME, text);
    956         return intent;
    957     }
    958 }
    959