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