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