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