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