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