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