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