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.app.ActionBar;
     20 import android.app.Fragment;
     21 import android.app.FragmentTransaction;
     22 import android.content.ActivityNotFoundException;
     23 import android.content.Context;
     24 import android.content.Intent;
     25 import android.content.pm.PackageManager;
     26 import android.content.pm.ResolveInfo;
     27 import android.content.res.Configuration;
     28 import android.content.res.Resources;
     29 import android.net.Uri;
     30 import android.os.Bundle;
     31 import android.provider.ContactsContract.Contacts;
     32 import android.provider.ContactsContract.Intents;
     33 import android.speech.RecognizerIntent;
     34 import android.support.v4.view.ViewPager;
     35 import android.telecom.PhoneAccount;
     36 import android.telecom.TelecomManager;
     37 import android.telephony.TelephonyManager;
     38 import android.text.Editable;
     39 import android.text.TextUtils;
     40 import android.text.TextWatcher;
     41 import android.util.Log;
     42 import android.view.DragEvent;
     43 import android.view.Gravity;
     44 import android.view.KeyEvent;
     45 import android.view.Menu;
     46 import android.view.MenuItem;
     47 import android.view.MotionEvent;
     48 import android.view.View;
     49 import android.view.View.OnDragListener;
     50 import android.view.View.OnTouchListener;
     51 import android.view.ViewTreeObserver;
     52 import android.view.animation.Animation;
     53 import android.view.animation.AnimationUtils;
     54 import android.widget.AbsListView.OnScrollListener;
     55 import android.widget.EditText;
     56 import android.widget.FrameLayout;
     57 import android.widget.ImageButton;
     58 import android.widget.PopupMenu;
     59 import android.widget.Toast;
     60 
     61 import com.android.contacts.common.CallUtil;
     62 import com.android.contacts.common.activity.TransactionSafeActivity;
     63 import com.android.contacts.common.dialog.ClearFrequentsDialog;
     64 import com.android.contacts.common.interactions.ImportExportDialogFragment;
     65 import com.android.contacts.common.interactions.TouchPointManager;
     66 import com.android.contacts.common.list.OnPhoneNumberPickerActionListener;
     67 import com.android.contacts.common.widget.FloatingActionButtonController;
     68 import com.android.contacts.commonbind.analytics.AnalyticsUtil;
     69 import com.android.dialer.calllog.CallLogActivity;
     70 import com.android.dialer.database.DialerDatabaseHelper;
     71 import com.android.dialer.dialpad.DialpadFragment;
     72 import com.android.dialer.dialpad.SmartDialNameMatcher;
     73 import com.android.dialer.dialpad.SmartDialPrefix;
     74 import com.android.dialer.interactions.PhoneNumberInteraction;
     75 import com.android.dialer.list.DragDropController;
     76 import com.android.dialer.list.ListsFragment;
     77 import com.android.dialer.list.OnDragDropListener;
     78 import com.android.dialer.list.OnListFragmentScrolledListener;
     79 import com.android.dialer.list.PhoneFavoriteSquareTileView;
     80 import com.android.dialer.list.RegularSearchFragment;
     81 import com.android.dialer.list.SearchFragment;
     82 import com.android.dialer.list.SmartDialSearchFragment;
     83 import com.android.dialer.list.SpeedDialFragment;
     84 import com.android.dialer.settings.DialerSettingsActivity;
     85 import com.android.dialer.util.DialerUtils;
     86 import com.android.dialer.widget.ActionBarController;
     87 import com.android.dialer.widget.SearchEditTextLayout;
     88 import com.android.dialer.widget.SearchEditTextLayout.OnBackButtonClickedListener;
     89 import com.android.dialerbind.DatabaseHelperManager;
     90 import com.android.phone.common.animation.AnimUtils;
     91 import com.android.phone.common.animation.AnimationListenerAdapter;
     92 
     93 import java.util.ArrayList;
     94 import java.util.List;
     95 
     96 /**
     97  * The dialer tab's title is 'phone', a more common name (see strings.xml).
     98  */
     99 public class DialtactsActivity extends TransactionSafeActivity implements View.OnClickListener,
    100         DialpadFragment.OnDialpadQueryChangedListener,
    101         OnListFragmentScrolledListener,
    102         ListsFragment.HostInterface,
    103         SpeedDialFragment.HostInterface,
    104         SearchFragment.HostInterface,
    105         OnDragDropListener,
    106         OnPhoneNumberPickerActionListener,
    107         PopupMenu.OnMenuItemClickListener,
    108         ViewPager.OnPageChangeListener,
    109         ActionBarController.ActivityUi {
    110     private static final String TAG = "DialtactsActivity";
    111 
    112     public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
    113 
    114     public static final String SHARED_PREFS_NAME = "com.android.dialer_preferences";
    115 
    116     /** @see #getCallOrigin() */
    117     private static final String CALL_ORIGIN_DIALTACTS =
    118             "com.android.dialer.DialtactsActivity";
    119 
    120     private static final String KEY_IN_REGULAR_SEARCH_UI = "in_regular_search_ui";
    121     private static final String KEY_IN_DIALPAD_SEARCH_UI = "in_dialpad_search_ui";
    122     private static final String KEY_SEARCH_QUERY = "search_query";
    123     private static final String KEY_FIRST_LAUNCH = "first_launch";
    124     private static final String KEY_IS_DIALPAD_SHOWN = "is_dialpad_shown";
    125 
    126     private static final String TAG_DIALPAD_FRAGMENT = "dialpad";
    127     private static final String TAG_REGULAR_SEARCH_FRAGMENT = "search";
    128     private static final String TAG_SMARTDIAL_SEARCH_FRAGMENT = "smartdial";
    129     private static final String TAG_FAVORITES_FRAGMENT = "favorites";
    130 
    131     /**
    132      * Just for backward compatibility. Should behave as same as {@link Intent#ACTION_DIAL}.
    133      */
    134     private static final String ACTION_TOUCH_DIALER = "com.android.phone.action.TOUCH_DIALER";
    135 
    136     private static final int ACTIVITY_REQUEST_CODE_VOICE_SEARCH = 1;
    137 
    138     private FrameLayout mParentLayout;
    139 
    140     /**
    141      * Fragment containing the dialpad that slides into view
    142      */
    143     protected DialpadFragment mDialpadFragment;
    144 
    145     /**
    146      * Fragment for searching phone numbers using the alphanumeric keyboard.
    147      */
    148     private RegularSearchFragment mRegularSearchFragment;
    149 
    150     /**
    151      * Fragment for searching phone numbers using the dialpad.
    152      */
    153     private SmartDialSearchFragment mSmartDialSearchFragment;
    154 
    155     /**
    156      * Animation that slides in.
    157      */
    158     private Animation mSlideIn;
    159 
    160     /**
    161      * Animation that slides out.
    162      */
    163     private Animation mSlideOut;
    164 
    165     /**
    166      * Listener for after slide out animation completes on dialer fragment.
    167      */
    168     AnimationListenerAdapter mSlideOutListener = new AnimationListenerAdapter() {
    169         @Override
    170         public void onAnimationEnd(Animation animation) {
    171             commitDialpadFragmentHide();
    172         }
    173     };
    174 
    175     /**
    176      * Fragment containing the speed dial list, recents list, and all contacts list.
    177      */
    178     private ListsFragment mListsFragment;
    179 
    180     /**
    181      * Tracks whether onSaveInstanceState has been called. If true, no fragment transactions can
    182      * be commited.
    183      */
    184     private boolean mStateSaved;
    185     private boolean mIsRestarting;
    186     private boolean mInDialpadSearch;
    187     private boolean mInRegularSearch;
    188     private boolean mClearSearchOnPause;
    189     private boolean mIsDialpadShown;
    190     private boolean mShowDialpadOnResume;
    191 
    192     /**
    193      * Whether or not the device is in landscape orientation.
    194      */
    195     private boolean mIsLandscape;
    196 
    197     /**
    198      * The position of the currently selected tab in the attached {@link ListsFragment}.
    199      */
    200     private int mCurrentTabPosition = 0;
    201 
    202     /**
    203      * True if the dialpad is only temporarily showing due to being in call
    204      */
    205     private boolean mInCallDialpadUp;
    206 
    207     /**
    208      * True when this activity has been launched for the first time.
    209      */
    210     private boolean mFirstLaunch;
    211 
    212     /**
    213      * Search query to be applied to the SearchView in the ActionBar once
    214      * onCreateOptionsMenu has been called.
    215      */
    216     private String mPendingSearchViewQuery;
    217 
    218     private PopupMenu mOverflowMenu;
    219     private EditText mSearchView;
    220     private View mVoiceSearchButton;
    221 
    222     private String mSearchQuery;
    223 
    224     private DialerDatabaseHelper mDialerDatabaseHelper;
    225     private DragDropController mDragDropController;
    226     private ActionBarController mActionBarController;
    227 
    228     private FloatingActionButtonController mFloatingActionButtonController;
    229 
    230     private int mActionBarHeight;
    231 
    232     /**
    233      * The text returned from a voice search query.  Set in {@link #onActivityResult} and used in
    234      * {@link #onResume()} to populate the search box.
    235      */
    236     private String mVoiceSearchQuery;
    237 
    238     private class OptionsPopupMenu extends PopupMenu {
    239         public OptionsPopupMenu(Context context, View anchor) {
    240             super(context, anchor, Gravity.END);
    241         }
    242 
    243         @Override
    244         public void show() {
    245             final Menu menu = getMenu();
    246             final MenuItem clearFrequents = menu.findItem(R.id.menu_clear_frequents);
    247             clearFrequents.setVisible(mListsFragment != null &&
    248                     mListsFragment.getSpeedDialFragment() != null &&
    249                     mListsFragment.getSpeedDialFragment().hasFrequents());
    250             super.show();
    251         }
    252     }
    253 
    254     /**
    255      * Listener that listens to drag events and sends their x and y coordinates to a
    256      * {@link DragDropController}.
    257      */
    258     private class LayoutOnDragListener implements OnDragListener {
    259         @Override
    260         public boolean onDrag(View v, DragEvent event) {
    261             if (event.getAction() == DragEvent.ACTION_DRAG_LOCATION) {
    262                 mDragDropController.handleDragHovered(v, (int) event.getX(), (int) event.getY());
    263             }
    264             return true;
    265         }
    266     }
    267 
    268     /**
    269      * Listener used to send search queries to the phone search fragment.
    270      */
    271     private final TextWatcher mPhoneSearchQueryTextListener = new TextWatcher() {
    272         @Override
    273         public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    274         }
    275 
    276         @Override
    277         public void onTextChanged(CharSequence s, int start, int before, int count) {
    278             final String newText = s.toString();
    279             if (newText.equals(mSearchQuery)) {
    280                 // If the query hasn't changed (perhaps due to activity being destroyed
    281                 // and restored, or user launching the same DIAL intent twice), then there is
    282                 // no need to do anything here.
    283                 return;
    284             }
    285             if (DEBUG) {
    286                 Log.d(TAG, "onTextChange for mSearchView called with new query: " + newText);
    287                 Log.d(TAG, "Previous Query: " + mSearchQuery);
    288             }
    289             mSearchQuery = newText;
    290 
    291             // Show search fragment only when the query string is changed to non-empty text.
    292             if (!TextUtils.isEmpty(newText)) {
    293                 // Call enterSearchUi only if we are switching search modes, or showing a search
    294                 // fragment for the first time.
    295                 final boolean sameSearchMode = (mIsDialpadShown && mInDialpadSearch) ||
    296                         (!mIsDialpadShown && mInRegularSearch);
    297                 if (!sameSearchMode) {
    298                     enterSearchUi(mIsDialpadShown, mSearchQuery);
    299                 }
    300             }
    301 
    302             if (mSmartDialSearchFragment != null && mSmartDialSearchFragment.isVisible()) {
    303                 mSmartDialSearchFragment.setQueryString(mSearchQuery, false /* delaySelection */);
    304             } else if (mRegularSearchFragment != null && mRegularSearchFragment.isVisible()) {
    305                 mRegularSearchFragment.setQueryString(mSearchQuery, false /* delaySelection */);
    306             }
    307         }
    308 
    309         @Override
    310         public void afterTextChanged(Editable s) {
    311         }
    312     };
    313 
    314 
    315     /**
    316      * Open the search UI when the user clicks on the search box.
    317      */
    318     private final View.OnClickListener mSearchViewOnClickListener = new View.OnClickListener() {
    319         @Override
    320         public void onClick(View v) {
    321             if (!isInSearchUi()) {
    322                 mActionBarController.onSearchBoxTapped();
    323                 enterSearchUi(false /* smartDialSearch */, mSearchView.getText().toString());
    324             }
    325         }
    326     };
    327 
    328     /**
    329      * If the search term is empty and the user closes the soft keyboard, close the search UI.
    330      */
    331     private final View.OnKeyListener mSearchEditTextLayoutListener = new View.OnKeyListener() {
    332         @Override
    333         public boolean onKey(View v, int keyCode, KeyEvent event) {
    334             if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN &&
    335                     TextUtils.isEmpty(mSearchView.getText().toString())) {
    336                 maybeExitSearchUi();
    337             }
    338             return false;
    339         }
    340     };
    341 
    342     @Override
    343     public boolean dispatchTouchEvent(MotionEvent ev) {
    344         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
    345             TouchPointManager.getInstance().setPoint((int) ev.getRawX(), (int) ev.getRawY());
    346         }
    347         return super.dispatchTouchEvent(ev);
    348 
    349     }
    350 
    351     @Override
    352     protected void onCreate(Bundle savedInstanceState) {
    353         super.onCreate(savedInstanceState);
    354         mFirstLaunch = true;
    355 
    356         final Resources resources = getResources();
    357         mActionBarHeight = resources.getDimensionPixelSize(R.dimen.action_bar_height_large);
    358 
    359         setContentView(R.layout.dialtacts_activity);
    360         getWindow().setBackgroundDrawable(null);
    361 
    362         final ActionBar actionBar = getActionBar();
    363         actionBar.setCustomView(R.layout.search_edittext);
    364         actionBar.setDisplayShowCustomEnabled(true);
    365         actionBar.setBackgroundDrawable(null);
    366 
    367         mActionBarController = new ActionBarController(this,
    368                 (SearchEditTextLayout) actionBar.getCustomView());
    369 
    370         SearchEditTextLayout searchEditTextLayout =
    371                 (SearchEditTextLayout) actionBar.getCustomView();
    372         searchEditTextLayout.setPreImeKeyListener(mSearchEditTextLayoutListener);
    373 
    374         mSearchView = (EditText) searchEditTextLayout.findViewById(R.id.search_view);
    375         mSearchView.addTextChangedListener(mPhoneSearchQueryTextListener);
    376         mVoiceSearchButton = searchEditTextLayout.findViewById(R.id.voice_search_button);
    377         searchEditTextLayout.findViewById(R.id.search_magnifying_glass)
    378                 .setOnClickListener(mSearchViewOnClickListener);
    379         searchEditTextLayout.findViewById(R.id.search_box_start_search)
    380                 .setOnClickListener(mSearchViewOnClickListener);
    381         searchEditTextLayout.setOnBackButtonClickedListener(new OnBackButtonClickedListener() {
    382             @Override
    383             public void onBackButtonClicked() {
    384                 onBackPressed();
    385             }
    386         });
    387 
    388         mIsLandscape = getResources().getConfiguration().orientation
    389                 == Configuration.ORIENTATION_LANDSCAPE;
    390 
    391         final View floatingActionButtonContainer = findViewById(
    392                 R.id.floating_action_button_container);
    393         ImageButton floatingActionButton = (ImageButton) findViewById(R.id.floating_action_button);
    394         floatingActionButton.setOnClickListener(this);
    395         mFloatingActionButtonController = new FloatingActionButtonController(this,
    396                 floatingActionButtonContainer, floatingActionButton);
    397 
    398         ImageButton optionsMenuButton =
    399                 (ImageButton) searchEditTextLayout.findViewById(R.id.dialtacts_options_menu_button);
    400         optionsMenuButton.setOnClickListener(this);
    401         mOverflowMenu = buildOptionsMenu(searchEditTextLayout);
    402         optionsMenuButton.setOnTouchListener(mOverflowMenu.getDragToOpenListener());
    403 
    404         // Add the favorites fragment, and the dialpad fragment, but only if savedInstanceState
    405         // is null. Otherwise the fragment manager takes care of recreating these fragments.
    406         if (savedInstanceState == null) {
    407             getFragmentManager().beginTransaction()
    408                     .add(R.id.dialtacts_frame, new ListsFragment(), TAG_FAVORITES_FRAGMENT)
    409                     .add(R.id.dialtacts_container, new DialpadFragment(), TAG_DIALPAD_FRAGMENT)
    410                     .commit();
    411         } else {
    412             mSearchQuery = savedInstanceState.getString(KEY_SEARCH_QUERY);
    413             mInRegularSearch = savedInstanceState.getBoolean(KEY_IN_REGULAR_SEARCH_UI);
    414             mInDialpadSearch = savedInstanceState.getBoolean(KEY_IN_DIALPAD_SEARCH_UI);
    415             mFirstLaunch = savedInstanceState.getBoolean(KEY_FIRST_LAUNCH);
    416             mShowDialpadOnResume = savedInstanceState.getBoolean(KEY_IS_DIALPAD_SHOWN);
    417             mActionBarController.restoreInstanceState(savedInstanceState);
    418         }
    419 
    420         final boolean isLayoutRtl = DialerUtils.isRtl();
    421         if (mIsLandscape) {
    422             mSlideIn = AnimationUtils.loadAnimation(this,
    423                     isLayoutRtl ? R.anim.dialpad_slide_in_left : R.anim.dialpad_slide_in_right);
    424             mSlideOut = AnimationUtils.loadAnimation(this,
    425                     isLayoutRtl ? R.anim.dialpad_slide_out_left : R.anim.dialpad_slide_out_right);
    426         } else {
    427             mSlideIn = AnimationUtils.loadAnimation(this, R.anim.dialpad_slide_in_bottom);
    428             mSlideOut = AnimationUtils.loadAnimation(this, R.anim.dialpad_slide_out_bottom);
    429         }
    430 
    431         mSlideIn.setInterpolator(AnimUtils.EASE_IN);
    432         mSlideOut.setInterpolator(AnimUtils.EASE_OUT);
    433 
    434         mSlideOut.setAnimationListener(mSlideOutListener);
    435 
    436         mParentLayout = (FrameLayout) findViewById(R.id.dialtacts_mainlayout);
    437         mParentLayout.setOnDragListener(new LayoutOnDragListener());
    438         floatingActionButtonContainer.getViewTreeObserver().addOnGlobalLayoutListener(
    439                 new ViewTreeObserver.OnGlobalLayoutListener() {
    440                     @Override
    441                     public void onGlobalLayout() {
    442                         final ViewTreeObserver observer =
    443                                 floatingActionButtonContainer.getViewTreeObserver();
    444                         if (!observer.isAlive()) {
    445                             return;
    446                         }
    447                         observer.removeOnGlobalLayoutListener(this);
    448                         int screenWidth = mParentLayout.getWidth();
    449                         mFloatingActionButtonController.setScreenWidth(screenWidth);
    450                         updateFloatingActionButtonControllerAlignment(false /* animate */);
    451                     }
    452                 });
    453 
    454         setupActivityOverlay();
    455 
    456         mDialerDatabaseHelper = DatabaseHelperManager.getDatabaseHelper(this);
    457         SmartDialPrefix.initializeNanpSettings(this);
    458     }
    459 
    460     private void setupActivityOverlay() {
    461         final View activityOverlay = findViewById(R.id.activity_overlay);
    462         activityOverlay.setOnTouchListener(new OnTouchListener() {
    463             @Override
    464             public boolean onTouch(View v, MotionEvent event) {
    465                 if (!mIsDialpadShown) {
    466                     maybeExitSearchUi();
    467                 }
    468                 return false;
    469             }
    470         });
    471     }
    472 
    473     @Override
    474     protected void onResume() {
    475         super.onResume();
    476         mStateSaved = false;
    477         if (mFirstLaunch) {
    478             displayFragment(getIntent());
    479         } else if (!phoneIsInUse() && mInCallDialpadUp) {
    480             hideDialpadFragment(false, true);
    481             mInCallDialpadUp = false;
    482         } else if (mShowDialpadOnResume) {
    483             showDialpadFragment(false);
    484             mShowDialpadOnResume = false;
    485         }
    486 
    487         // If there was a voice query result returned in the {@link #onActivityResult} callback, it
    488         // will have been stashed in mVoiceSearchQuery since the search results fragment cannot be
    489         // shown until onResume has completed.  Active the search UI and set the search term now.
    490         if (!TextUtils.isEmpty(mVoiceSearchQuery)) {
    491             mActionBarController.onSearchBoxTapped();
    492             mSearchView.setText(mVoiceSearchQuery);
    493             mVoiceSearchQuery = null;
    494         }
    495 
    496         mFirstLaunch = false;
    497 
    498         if (mIsRestarting) {
    499             // This is only called when the activity goes from resumed -> paused -> resumed, so it
    500             // will not cause an extra view to be sent out on rotation
    501             if (mIsDialpadShown) {
    502                 AnalyticsUtil.sendScreenView(mDialpadFragment, this);
    503             }
    504             mIsRestarting = false;
    505         }
    506         prepareVoiceSearchButton();
    507         mDialerDatabaseHelper.startSmartDialUpdateThread();
    508         updateFloatingActionButtonControllerAlignment(false /* animate */);
    509     }
    510 
    511     @Override
    512     protected void onRestart() {
    513         super.onRestart();
    514         mIsRestarting = true;
    515     }
    516 
    517     @Override
    518     protected void onPause() {
    519         if (mClearSearchOnPause) {
    520             hideDialpadAndSearchUi();
    521             mClearSearchOnPause = false;
    522         }
    523         if (mSlideOut.hasStarted() && !mSlideOut.hasEnded()) {
    524             commitDialpadFragmentHide();
    525         }
    526         super.onPause();
    527     }
    528 
    529     @Override
    530     protected void onSaveInstanceState(Bundle outState) {
    531         super.onSaveInstanceState(outState);
    532         outState.putString(KEY_SEARCH_QUERY, mSearchQuery);
    533         outState.putBoolean(KEY_IN_REGULAR_SEARCH_UI, mInRegularSearch);
    534         outState.putBoolean(KEY_IN_DIALPAD_SEARCH_UI, mInDialpadSearch);
    535         outState.putBoolean(KEY_FIRST_LAUNCH, mFirstLaunch);
    536         outState.putBoolean(KEY_IS_DIALPAD_SHOWN, mIsDialpadShown);
    537         mActionBarController.saveInstanceState(outState);
    538         mStateSaved = true;
    539     }
    540 
    541     @Override
    542     public void onAttachFragment(Fragment fragment) {
    543         if (fragment instanceof DialpadFragment) {
    544             mDialpadFragment = (DialpadFragment) fragment;
    545             if (!mShowDialpadOnResume) {
    546                 final FragmentTransaction transaction = getFragmentManager().beginTransaction();
    547                 transaction.hide(mDialpadFragment);
    548                 transaction.commit();
    549             }
    550         } else if (fragment instanceof SmartDialSearchFragment) {
    551             mSmartDialSearchFragment = (SmartDialSearchFragment) fragment;
    552             mSmartDialSearchFragment.setOnPhoneNumberPickerActionListener(this);
    553         } else if (fragment instanceof SearchFragment) {
    554             mRegularSearchFragment = (RegularSearchFragment) fragment;
    555             mRegularSearchFragment.setOnPhoneNumberPickerActionListener(this);
    556         } else if (fragment instanceof ListsFragment) {
    557             mListsFragment = (ListsFragment) fragment;
    558             mListsFragment.addOnPageChangeListener(this);
    559         }
    560     }
    561 
    562     protected void handleMenuSettings() {
    563         final Intent intent = new Intent(this, DialerSettingsActivity.class);
    564         startActivity(intent);
    565     }
    566 
    567     @Override
    568     public void onClick(View view) {
    569         switch (view.getId()) {
    570             case R.id.floating_action_button:
    571                 if (!mIsDialpadShown) {
    572                     mInCallDialpadUp = false;
    573                     showDialpadFragment(true);
    574                 }
    575                 break;
    576             case R.id.voice_search_button:
    577                 try {
    578                     startActivityForResult(new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH),
    579                             ACTIVITY_REQUEST_CODE_VOICE_SEARCH);
    580                 } catch (ActivityNotFoundException e) {
    581                     Toast.makeText(DialtactsActivity.this, R.string.voice_search_not_available,
    582                             Toast.LENGTH_SHORT).show();
    583                 }
    584                 break;
    585             case R.id.dialtacts_options_menu_button:
    586                 mOverflowMenu.show();
    587                 break;
    588             default: {
    589                 Log.wtf(TAG, "Unexpected onClick event from " + view);
    590                 break;
    591             }
    592         }
    593     }
    594 
    595     @Override
    596     public boolean onMenuItemClick(MenuItem item) {
    597         switch (item.getItemId()) {
    598             case R.id.menu_history:
    599                 showCallHistory();
    600                 break;
    601             case R.id.menu_add_contact:
    602                 try {
    603                     startActivity(new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI));
    604                 } catch (ActivityNotFoundException e) {
    605                     Toast toast = Toast.makeText(this,
    606                             R.string.add_contact_not_available,
    607                             Toast.LENGTH_SHORT);
    608                     toast.show();
    609                 }
    610                 break;
    611             case R.id.menu_import_export:
    612                 // We hard-code the "contactsAreAvailable" argument because doing it properly would
    613                 // involve querying a {@link ProviderStatusLoader}, which we don't want to do right
    614                 // now in Dialtacts for (potential) performance reasons. Compare with how it is
    615                 // done in {@link PeopleActivity}.
    616                 ImportExportDialogFragment.show(getFragmentManager(), true,
    617                         DialtactsActivity.class);
    618                 return true;
    619             case R.id.menu_clear_frequents:
    620                 ClearFrequentsDialog.show(getFragmentManager());
    621                 return true;
    622             case R.id.menu_call_settings:
    623                 handleMenuSettings();
    624                 return true;
    625         }
    626         return false;
    627     }
    628 
    629     @Override
    630     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    631         if (requestCode == ACTIVITY_REQUEST_CODE_VOICE_SEARCH) {
    632             if (resultCode == RESULT_OK) {
    633                 final ArrayList<String> matches = data.getStringArrayListExtra(
    634                         RecognizerIntent.EXTRA_RESULTS);
    635                 if (matches.size() > 0) {
    636                     final String match = matches.get(0);
    637                     mVoiceSearchQuery = match;
    638                 } else {
    639                     Log.e(TAG, "Voice search - nothing heard");
    640                 }
    641             } else {
    642                 Log.e(TAG, "Voice search failed");
    643             }
    644         }
    645         super.onActivityResult(requestCode, resultCode, data);
    646     }
    647 
    648     /**
    649      * Initiates a fragment transaction to show the dialpad fragment. Animations and other visual
    650      * updates are handled by a callback which is invoked after the dialpad fragment is shown.
    651      * @see #onDialpadShown
    652      */
    653     private void showDialpadFragment(boolean animate) {
    654         if (mIsDialpadShown || mStateSaved) {
    655             return;
    656         }
    657         mIsDialpadShown = true;
    658         mDialpadFragment.setAnimate(animate);
    659         mListsFragment.setUserVisibleHint(false);
    660         AnalyticsUtil.sendScreenView(mDialpadFragment);
    661 
    662         final FragmentTransaction ft = getFragmentManager().beginTransaction();
    663         ft.show(mDialpadFragment);
    664         ft.commit();
    665 
    666         if (animate) {
    667             mFloatingActionButtonController.scaleOut();
    668         } else {
    669             mFloatingActionButtonController.setVisible(false);
    670         }
    671         mActionBarController.onDialpadUp();
    672 
    673         if (!isInSearchUi()) {
    674             enterSearchUi(true /* isSmartDial */, mSearchQuery);
    675         }
    676     }
    677 
    678     /**
    679      * Callback from child DialpadFragment when the dialpad is shown.
    680      */
    681     public void onDialpadShown() {
    682         if (mDialpadFragment.getAnimate()) {
    683             mDialpadFragment.getView().startAnimation(mSlideIn);
    684         } else {
    685             mDialpadFragment.setYFraction(0);
    686         }
    687 
    688         updateSearchFragmentPosition();
    689     }
    690 
    691     /**
    692      * Initiates animations and other visual updates to hide the dialpad. The fragment is hidden in
    693      * a callback after the hide animation ends.
    694      * @see #commitDialpadFragmentHide
    695      */
    696     public void hideDialpadFragment(boolean animate, boolean clearDialpad) {
    697         if (mDialpadFragment == null) {
    698             return;
    699         }
    700         if (clearDialpad) {
    701             mDialpadFragment.clearDialpad();
    702         }
    703         if (!mIsDialpadShown) {
    704             return;
    705         }
    706         mIsDialpadShown = false;
    707         mDialpadFragment.setAnimate(animate);
    708         mListsFragment.setUserVisibleHint(true);
    709         mListsFragment.sendScreenViewForCurrentPosition();
    710 
    711         updateSearchFragmentPosition();
    712 
    713         updateFloatingActionButtonControllerAlignment(animate);
    714         if (animate) {
    715             mDialpadFragment.getView().startAnimation(mSlideOut);
    716         } else {
    717             commitDialpadFragmentHide();
    718         }
    719 
    720         mActionBarController.onDialpadDown();
    721 
    722         if (isInSearchUi()) {
    723             if (TextUtils.isEmpty(mSearchQuery)) {
    724                 exitSearchUi();
    725             }
    726         }
    727     }
    728 
    729     /**
    730      * Finishes hiding the dialpad fragment after any animations are completed.
    731      */
    732     private void commitDialpadFragmentHide() {
    733         if (!mStateSaved && !mDialpadFragment.isHidden()) {
    734             final FragmentTransaction ft = getFragmentManager().beginTransaction();
    735             ft.hide(mDialpadFragment);
    736             ft.commit();
    737         }
    738         mFloatingActionButtonController.scaleIn(AnimUtils.NO_DELAY);
    739     }
    740 
    741     private void updateSearchFragmentPosition() {
    742         SearchFragment fragment = null;
    743         if (mSmartDialSearchFragment != null && mSmartDialSearchFragment.isVisible()) {
    744             fragment = mSmartDialSearchFragment;
    745         } else if (mRegularSearchFragment != null && mRegularSearchFragment.isVisible()) {
    746             fragment = mRegularSearchFragment;
    747         }
    748         if (fragment != null && fragment.isVisible()) {
    749             fragment.updatePosition(true /* animate */);
    750         }
    751     }
    752 
    753     @Override
    754     public boolean isInSearchUi() {
    755         return mInDialpadSearch || mInRegularSearch;
    756     }
    757 
    758     @Override
    759     public boolean hasSearchQuery() {
    760         return !TextUtils.isEmpty(mSearchQuery);
    761     }
    762 
    763     @Override
    764     public boolean shouldShowActionBar() {
    765         return mListsFragment.shouldShowActionBar();
    766     }
    767 
    768     private void setNotInSearchUi() {
    769         mInDialpadSearch = false;
    770         mInRegularSearch = false;
    771     }
    772 
    773     private void hideDialpadAndSearchUi() {
    774         if (mIsDialpadShown) {
    775             hideDialpadFragment(false, true);
    776         } else {
    777             exitSearchUi();
    778         }
    779     }
    780 
    781     private void prepareVoiceSearchButton() {
    782         final Intent voiceIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
    783         if (canIntentBeHandled(voiceIntent)) {
    784             mVoiceSearchButton.setVisibility(View.VISIBLE);
    785             mVoiceSearchButton.setOnClickListener(this);
    786         } else {
    787             mVoiceSearchButton.setVisibility(View.GONE);
    788         }
    789     }
    790 
    791     private OptionsPopupMenu buildOptionsMenu(View invoker) {
    792         final OptionsPopupMenu popupMenu = new OptionsPopupMenu(this, invoker);
    793         popupMenu.inflate(R.menu.dialtacts_options);
    794         final Menu menu = popupMenu.getMenu();
    795         popupMenu.setOnMenuItemClickListener(this);
    796         return popupMenu;
    797     }
    798 
    799     @Override
    800     public boolean onCreateOptionsMenu(Menu menu) {
    801         if (mPendingSearchViewQuery != null) {
    802             mSearchView.setText(mPendingSearchViewQuery);
    803             mPendingSearchViewQuery = null;
    804         }
    805         mActionBarController.restoreActionBarOffset();
    806         return false;
    807     }
    808 
    809     /**
    810      * Returns true if the intent is due to hitting the green send key (hardware call button:
    811      * KEYCODE_CALL) while in a call.
    812      *
    813      * @param intent the intent that launched this activity
    814      * @return true if the intent is due to hitting the green send key while in a call
    815      */
    816     private boolean isSendKeyWhileInCall(Intent intent) {
    817         // If there is a call in progress and the user launched the dialer by hitting the call
    818         // button, go straight to the in-call screen.
    819         final boolean callKey = Intent.ACTION_CALL_BUTTON.equals(intent.getAction());
    820 
    821         if (callKey) {
    822             getTelecomManager().showInCallScreen(false);
    823             return true;
    824         }
    825 
    826         return false;
    827     }
    828 
    829     /**
    830      * Sets the current tab based on the intent's request type
    831      *
    832      * @param intent Intent that contains information about which tab should be selected
    833      */
    834     private void displayFragment(Intent intent) {
    835         // If we got here by hitting send and we're in call forward along to the in-call activity
    836         if (isSendKeyWhileInCall(intent)) {
    837             finish();
    838             return;
    839         }
    840 
    841         if (mDialpadFragment != null) {
    842             final boolean phoneIsInUse = phoneIsInUse();
    843             if (phoneIsInUse || (intent.getData() !=  null && isDialIntent(intent))) {
    844                 mDialpadFragment.setStartedFromNewIntent(true);
    845                 if (phoneIsInUse && !mDialpadFragment.isVisible()) {
    846                     mInCallDialpadUp = true;
    847                 }
    848                 showDialpadFragment(false);
    849             }
    850         }
    851     }
    852 
    853     @Override
    854     public void onNewIntent(Intent newIntent) {
    855         setIntent(newIntent);
    856         mStateSaved = false;
    857         displayFragment(newIntent);
    858 
    859         invalidateOptionsMenu();
    860     }
    861 
    862     /** Returns true if the given intent contains a phone number to populate the dialer with */
    863     private boolean isDialIntent(Intent intent) {
    864         final String action = intent.getAction();
    865         if (Intent.ACTION_DIAL.equals(action) || ACTION_TOUCH_DIALER.equals(action)) {
    866             return true;
    867         }
    868         if (Intent.ACTION_VIEW.equals(action)) {
    869             final Uri data = intent.getData();
    870             if (data != null && PhoneAccount.SCHEME_TEL.equals(data.getScheme())) {
    871                 return true;
    872             }
    873         }
    874         return false;
    875     }
    876 
    877     /**
    878      * Returns an appropriate call origin for this Activity. May return null when no call origin
    879      * should be used (e.g. when some 3rd party application launched the screen. Call origin is
    880      * for remembering the tab in which the user made a phone call, so the external app's DIAL
    881      * request should not be counted.)
    882      */
    883     public String getCallOrigin() {
    884         return !isDialIntent(getIntent()) ? CALL_ORIGIN_DIALTACTS : null;
    885     }
    886 
    887     /**
    888      * Shows the search fragment
    889      */
    890     private void enterSearchUi(boolean smartDialSearch, String query) {
    891         if (mStateSaved || getFragmentManager().isDestroyed()) {
    892             // Weird race condition where fragment is doing work after the activity is destroyed
    893             // due to talkback being on (b/10209937). Just return since we can't do any
    894             // constructive here.
    895             return;
    896         }
    897 
    898         if (DEBUG) {
    899             Log.d(TAG, "Entering search UI - smart dial " + smartDialSearch);
    900         }
    901 
    902         final FragmentTransaction transaction = getFragmentManager().beginTransaction();
    903         if (mInDialpadSearch && mSmartDialSearchFragment != null) {
    904             transaction.remove(mSmartDialSearchFragment);
    905         } else if (mInRegularSearch && mRegularSearchFragment != null) {
    906             transaction.remove(mRegularSearchFragment);
    907         }
    908 
    909         final String tag;
    910         if (smartDialSearch) {
    911             tag = TAG_SMARTDIAL_SEARCH_FRAGMENT;
    912         } else {
    913             tag = TAG_REGULAR_SEARCH_FRAGMENT;
    914         }
    915         mInDialpadSearch = smartDialSearch;
    916         mInRegularSearch = !smartDialSearch;
    917 
    918         SearchFragment fragment = (SearchFragment) getFragmentManager().findFragmentByTag(tag);
    919         transaction.setCustomAnimations(android.R.animator.fade_in, 0);
    920         if (fragment == null) {
    921             if (smartDialSearch) {
    922                 fragment = new SmartDialSearchFragment();
    923             } else {
    924                 fragment = new RegularSearchFragment();
    925             }
    926             transaction.add(R.id.dialtacts_frame, fragment, tag);
    927         } else {
    928             transaction.show(fragment);
    929         }
    930         // DialtactsActivity will provide the options menu
    931         fragment.setHasOptionsMenu(false);
    932         fragment.setShowEmptyListForNullQuery(true);
    933         fragment.setQueryString(query, false /* delaySelection */);
    934         transaction.commit();
    935 
    936         mListsFragment.getView().animate().alpha(0).withLayer();
    937         mListsFragment.setUserVisibleHint(false);
    938     }
    939 
    940     /**
    941      * Hides the search fragment
    942      */
    943     private void exitSearchUi() {
    944         // See related bug in enterSearchUI();
    945         if (getFragmentManager().isDestroyed() || mStateSaved) {
    946             return;
    947         }
    948 
    949         mSearchView.setText(null);
    950         mDialpadFragment.clearDialpad();
    951         setNotInSearchUi();
    952 
    953         final FragmentTransaction transaction = getFragmentManager().beginTransaction();
    954         if (mSmartDialSearchFragment != null) {
    955             transaction.remove(mSmartDialSearchFragment);
    956         }
    957         if (mRegularSearchFragment != null) {
    958             transaction.remove(mRegularSearchFragment);
    959         }
    960         transaction.commit();
    961 
    962         mListsFragment.getView().animate().alpha(1).withLayer();
    963         if (!mDialpadFragment.isVisible()) {
    964             // If the dialpad fragment wasn't previously visible, then send a screen view because
    965             // we are exiting regular search. Otherwise, the screen view will be sent by
    966             // {@link #hideDialpadFragment}.
    967             mListsFragment.sendScreenViewForCurrentPosition();
    968             mListsFragment.setUserVisibleHint(true);
    969         }
    970 
    971         mActionBarController.onSearchUiExited();
    972     }
    973 
    974     @Override
    975     public void onBackPressed() {
    976         if (mStateSaved) {
    977             return;
    978         }
    979         if (mIsDialpadShown) {
    980             if (TextUtils.isEmpty(mSearchQuery) ||
    981                     (mSmartDialSearchFragment != null && mSmartDialSearchFragment.isVisible()
    982                             && mSmartDialSearchFragment.getAdapter().getCount() == 0)) {
    983                 exitSearchUi();
    984             }
    985             hideDialpadFragment(true, false);
    986         } else if (isInSearchUi()) {
    987             exitSearchUi();
    988             DialerUtils.hideInputMethod(mParentLayout);
    989         } else {
    990             super.onBackPressed();
    991         }
    992     }
    993 
    994     /**
    995      * @return True if the search UI was exited, false otherwise
    996      */
    997     private boolean maybeExitSearchUi() {
    998         if (isInSearchUi() && TextUtils.isEmpty(mSearchQuery)) {
    999             exitSearchUi();
   1000             DialerUtils.hideInputMethod(mParentLayout);
   1001             return true;
   1002         }
   1003         return false;
   1004     }
   1005 
   1006     @Override
   1007     public void onDialpadQueryChanged(String query) {
   1008         if (mSmartDialSearchFragment != null) {
   1009             mSmartDialSearchFragment.setAddToContactNumber(query);
   1010         }
   1011         final String normalizedQuery = SmartDialNameMatcher.normalizeNumber(query,
   1012                 SmartDialNameMatcher.LATIN_SMART_DIAL_MAP);
   1013 
   1014         if (!TextUtils.equals(mSearchView.getText(), normalizedQuery)) {
   1015             if (DEBUG) {
   1016                 Log.d(TAG, "onDialpadQueryChanged - new query: " + query);
   1017             }
   1018             if (mDialpadFragment == null || !mDialpadFragment.isVisible()) {
   1019                 // This callback can happen if the dialpad fragment is recreated because of
   1020                 // activity destruction. In that case, don't update the search view because
   1021                 // that would bring the user back to the search fragment regardless of the
   1022                 // previous state of the application. Instead, just return here and let the
   1023                 // fragment manager correctly figure out whatever fragment was last displayed.
   1024                 if (!TextUtils.isEmpty(normalizedQuery)) {
   1025                     mPendingSearchViewQuery = normalizedQuery;
   1026                 }
   1027                 return;
   1028             }
   1029             mSearchView.setText(normalizedQuery);
   1030         }
   1031     }
   1032 
   1033     @Override
   1034     public void onListFragmentScrollStateChange(int scrollState) {
   1035         if (scrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
   1036             hideDialpadFragment(true, false);
   1037             DialerUtils.hideInputMethod(mParentLayout);
   1038         }
   1039     }
   1040 
   1041     @Override
   1042     public void onListFragmentScroll(int firstVisibleItem, int visibleItemCount,
   1043                                      int totalItemCount) {
   1044         // TODO: No-op for now. This should eventually show/hide the actionBar based on
   1045         // interactions with the ListsFragments.
   1046     }
   1047 
   1048     private boolean phoneIsInUse() {
   1049         return getTelecomManager().isInCall();
   1050     }
   1051 
   1052     public static Intent getAddNumberToContactIntent(CharSequence text) {
   1053         return getAddToContactIntent(null /* name */, text /* phoneNumber */,
   1054                 -1 /* phoneNumberType */);
   1055     }
   1056 
   1057     public static Intent getAddToContactIntent(CharSequence name, CharSequence phoneNumber,
   1058             int phoneNumberType) {
   1059         Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
   1060         intent.putExtra(Intents.Insert.PHONE, phoneNumber);
   1061         // Only include the name and phone type extras if they are specified (the method
   1062         // getAddNumberToContactIntent does not use them).
   1063         if (name != null) {
   1064             intent.putExtra(Intents.Insert.NAME, name);
   1065         }
   1066         if (phoneNumberType != -1) {
   1067             intent.putExtra(Intents.Insert.PHONE_TYPE, phoneNumberType);
   1068         }
   1069         intent.setType(Contacts.CONTENT_ITEM_TYPE);
   1070         return intent;
   1071     }
   1072 
   1073     private boolean canIntentBeHandled(Intent intent) {
   1074         final PackageManager packageManager = getPackageManager();
   1075         final List<ResolveInfo> resolveInfo = packageManager.queryIntentActivities(intent,
   1076                 PackageManager.MATCH_DEFAULT_ONLY);
   1077         return resolveInfo != null && resolveInfo.size() > 0;
   1078     }
   1079 
   1080     @Override
   1081     public void showCallHistory() {
   1082         // Use explicit CallLogActivity intent instead of ACTION_VIEW +
   1083         // CONTENT_TYPE, so that we always open our call log from our dialer
   1084         final Intent intent = new Intent(this, CallLogActivity.class);
   1085         startActivity(intent);
   1086     }
   1087 
   1088     /**
   1089      * Called when the user has long-pressed a contact tile to start a drag operation.
   1090      */
   1091     @Override
   1092     public void onDragStarted(int x, int y, PhoneFavoriteSquareTileView view) {
   1093         if (mListsFragment.isPaneOpen()) {
   1094             mActionBarController.setAlpha(ListsFragment.REMOVE_VIEW_SHOWN_ALPHA);
   1095         }
   1096         mListsFragment.showRemoveView(true);
   1097     }
   1098 
   1099     @Override
   1100     public void onDragHovered(int x, int y, PhoneFavoriteSquareTileView view) {
   1101     }
   1102 
   1103     /**
   1104      * Called when the user has released a contact tile after long-pressing it.
   1105      */
   1106     @Override
   1107     public void onDragFinished(int x, int y) {
   1108         if (mListsFragment.isPaneOpen()) {
   1109             mActionBarController.setAlpha(ListsFragment.REMOVE_VIEW_HIDDEN_ALPHA);
   1110         }
   1111         mListsFragment.showRemoveView(false);
   1112     }
   1113 
   1114     @Override
   1115     public void onDroppedOnRemove() {}
   1116 
   1117     /**
   1118      * Allows the SpeedDialFragment to attach the drag controller to mRemoveViewContainer
   1119      * once it has been attached to the activity.
   1120      */
   1121     @Override
   1122     public void setDragDropController(DragDropController dragController) {
   1123         mDragDropController = dragController;
   1124         mListsFragment.getRemoveView().setDragDropController(dragController);
   1125     }
   1126 
   1127     @Override
   1128     public void onPickPhoneNumberAction(Uri dataUri) {
   1129         // Specify call-origin so that users will see the previous tab instead of
   1130         // CallLog screen (search UI will be automatically exited).
   1131         PhoneNumberInteraction.startInteractionForPhoneCall(
   1132                 DialtactsActivity.this, dataUri, getCallOrigin());
   1133         mClearSearchOnPause = true;
   1134     }
   1135 
   1136     @Override
   1137     public void onCallNumberDirectly(String phoneNumber) {
   1138         onCallNumberDirectly(phoneNumber, false /* isVideoCall */);
   1139     }
   1140 
   1141     @Override
   1142     public void onCallNumberDirectly(String phoneNumber, boolean isVideoCall) {
   1143         Intent intent = isVideoCall ?
   1144                 CallUtil.getVideoCallIntent(phoneNumber, getCallOrigin()) :
   1145                 CallUtil.getCallIntent(phoneNumber, getCallOrigin());
   1146         DialerUtils.startActivityWithErrorToast(this, intent);
   1147         mClearSearchOnPause = true;
   1148     }
   1149 
   1150     @Override
   1151     public void onShortcutIntentCreated(Intent intent) {
   1152         Log.w(TAG, "Unsupported intent has come (" + intent + "). Ignoring.");
   1153     }
   1154 
   1155     @Override
   1156     public void onHomeInActionBarSelected() {
   1157         exitSearchUi();
   1158     }
   1159 
   1160     @Override
   1161     public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
   1162         position = mListsFragment.getRtlPosition(position);
   1163         // Only scroll the button when the first tab is selected. The button should scroll from
   1164         // the middle to right position only on the transition from the first tab to the second
   1165         // tab.
   1166         // If the app is in RTL mode, we need to check against the second tab, rather than the
   1167         // first. This is because if we are scrolling between the first and second tabs, the
   1168         // viewpager will report that the starting tab position is 1 rather than 0, due to the
   1169         // reversal of the order of the tabs.
   1170         final boolean isLayoutRtl = DialerUtils.isRtl();
   1171         final boolean shouldScrollButton = position == (isLayoutRtl
   1172                 ? ListsFragment.TAB_INDEX_RECENTS : ListsFragment.TAB_INDEX_SPEED_DIAL);
   1173         if (shouldScrollButton && !mIsLandscape) {
   1174             mFloatingActionButtonController.onPageScrolled(
   1175                     isLayoutRtl ? 1 - positionOffset : positionOffset);
   1176         } else if (position != ListsFragment.TAB_INDEX_SPEED_DIAL) {
   1177             mFloatingActionButtonController.onPageScrolled(1);
   1178         }
   1179     }
   1180 
   1181     @Override
   1182     public void onPageSelected(int position) {
   1183         position = mListsFragment.getRtlPosition(position);
   1184         mCurrentTabPosition = position;
   1185     }
   1186 
   1187     @Override
   1188     public void onPageScrollStateChanged(int state) {
   1189     }
   1190 
   1191     private TelephonyManager getTelephonyManager() {
   1192         return (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
   1193     }
   1194 
   1195     private TelecomManager getTelecomManager() {
   1196         return (TelecomManager) getSystemService(Context.TELECOM_SERVICE);
   1197     }
   1198 
   1199     @Override
   1200     public boolean isActionBarShowing() {
   1201         return mActionBarController.isActionBarShowing();
   1202     }
   1203 
   1204     @Override
   1205     public ActionBarController getActionBarController() {
   1206         return mActionBarController;
   1207     }
   1208 
   1209     public boolean isDialpadShown() {
   1210         return mIsDialpadShown;
   1211     }
   1212 
   1213     @Override
   1214     public int getActionBarHideOffset() {
   1215         return getActionBar().getHideOffset();
   1216     }
   1217 
   1218     @Override
   1219     public void setActionBarHideOffset(int offset) {
   1220         getActionBar().setHideOffset(offset);
   1221     }
   1222 
   1223     @Override
   1224     public int getActionBarHeight() {
   1225         return mActionBarHeight;
   1226     }
   1227 
   1228     /**
   1229      * Updates controller based on currently known information.
   1230      *
   1231      * @param animate Whether or not to animate the transition.
   1232      */
   1233     private void updateFloatingActionButtonControllerAlignment(boolean animate) {
   1234         int align = (!mIsLandscape && mCurrentTabPosition == ListsFragment.TAB_INDEX_SPEED_DIAL) ?
   1235                 FloatingActionButtonController.ALIGN_MIDDLE :
   1236                         FloatingActionButtonController.ALIGN_END;
   1237         mFloatingActionButtonController.align(align, 0 /* offsetX */, 0 /* offsetY */, animate);
   1238     }
   1239 }
   1240