• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.contacts.activities;
18 
19 import com.android.contacts.ContactsUtils;
20 import com.android.contacts.R;
21 import com.android.contacts.calllog.CallLogFragment;
22 import com.android.contacts.dialpad.DialpadFragment;
23 import com.android.contacts.interactions.PhoneNumberInteraction;
24 import com.android.contacts.list.ContactListFilterController;
25 import com.android.contacts.list.ContactListFilterController.ContactListFilterListener;
26 import com.android.contacts.list.ContactListItemView;
27 import com.android.contacts.list.OnPhoneNumberPickerActionListener;
28 import com.android.contacts.list.PhoneFavoriteFragment;
29 import com.android.contacts.list.PhoneNumberPickerFragment;
30 import com.android.contacts.util.AccountFilterUtil;
31 import com.android.contacts.util.Constants;
32 import com.android.internal.telephony.ITelephony;
33 
34 import android.app.ActionBar;
35 import android.app.ActionBar.LayoutParams;
36 import android.app.ActionBar.Tab;
37 import android.app.ActionBar.TabListener;
38 import android.app.Activity;
39 import android.app.Fragment;
40 import android.app.FragmentManager;
41 import android.app.FragmentTransaction;
42 import android.content.Context;
43 import android.content.Intent;
44 import android.content.SharedPreferences;
45 import android.net.Uri;
46 import android.os.Bundle;
47 import android.os.RemoteException;
48 import android.os.ServiceManager;
49 import android.preference.PreferenceManager;
50 import android.provider.CallLog.Calls;
51 import android.provider.ContactsContract.Contacts;
52 import android.provider.ContactsContract.Intents.UI;
53 import android.support.v13.app.FragmentPagerAdapter;
54 import android.support.v4.view.ViewPager;
55 import android.support.v4.view.ViewPager.OnPageChangeListener;
56 import android.text.TextUtils;
57 import android.util.DisplayMetrics;
58 import android.util.Log;
59 import android.view.Menu;
60 import android.view.MenuInflater;
61 import android.view.MenuItem;
62 import android.view.MenuItem.OnMenuItemClickListener;
63 import android.view.View;
64 import android.view.View.OnClickListener;
65 import android.view.View.OnFocusChangeListener;
66 import android.view.ViewConfiguration;
67 import android.view.ViewGroup;
68 import android.view.inputmethod.InputMethodManager;
69 import android.widget.PopupMenu;
70 import android.widget.SearchView;
71 import android.widget.SearchView.OnCloseListener;
72 import android.widget.SearchView.OnQueryTextListener;
73 
74 /**
75  * The dialer activity that has one tab with the virtual 12key
76  * dialer, a tab with recent calls in it, a tab with the contacts and
77  * a tab with the favorite. This is the container and the tabs are
78  * embedded using intents.
79  * The dialer tab's title is 'phone', a more common name (see strings.xml).
80  */
81 public class DialtactsActivity extends TransactionSafeActivity
82         implements View.OnClickListener {
83     private static final String TAG = "DialtactsActivity";
84 
85     public static final boolean DEBUG = false;
86 
87     /** Used to open Call Setting */
88     private static final String PHONE_PACKAGE = "com.android.phone";
89     private static final String CALL_SETTINGS_CLASS_NAME =
90             "com.android.phone.CallFeaturesSetting";
91 
92     /**
93      * Copied from PhoneApp. See comments in Phone app for more detail.
94      */
95     public static final String EXTRA_CALL_ORIGIN = "com.android.phone.CALL_ORIGIN";
96     /** @see #getCallOrigin() */
97     private static final String CALL_ORIGIN_DIALTACTS =
98             "com.android.contacts.activities.DialtactsActivity";
99 
100     /**
101      * Just for backward compatibility. Should behave as same as {@link Intent#ACTION_DIAL}.
102      */
103     private static final String ACTION_TOUCH_DIALER = "com.android.phone.action.TOUCH_DIALER";
104 
105     /** Used both by {@link ActionBar} and {@link ViewPagerAdapter} */
106     private static final int TAB_INDEX_DIALER = 0;
107     private static final int TAB_INDEX_CALL_LOG = 1;
108     private static final int TAB_INDEX_FAVORITES = 2;
109 
110     private static final int TAB_INDEX_COUNT = 3;
111 
112     private SharedPreferences mPrefs;
113 
114     /** Last manually selected tab index */
115     private static final String PREF_LAST_MANUALLY_SELECTED_TAB =
116             "DialtactsActivity_last_manually_selected_tab";
117     private static final int PREF_LAST_MANUALLY_SELECTED_TAB_DEFAULT = TAB_INDEX_DIALER;
118 
119     private static final int SUBACTIVITY_ACCOUNT_FILTER = 1;
120 
121     public class ViewPagerAdapter extends FragmentPagerAdapter {
ViewPagerAdapter(FragmentManager fm)122         public ViewPagerAdapter(FragmentManager fm) {
123             super(fm);
124         }
125 
126         @Override
getItem(int position)127         public Fragment getItem(int position) {
128             switch (position) {
129                 case TAB_INDEX_DIALER:
130                     return new DialpadFragment();
131                 case TAB_INDEX_CALL_LOG:
132                     return new CallLogFragment();
133                 case TAB_INDEX_FAVORITES:
134                     return new PhoneFavoriteFragment();
135             }
136             throw new IllegalStateException("No fragment at position " + position);
137         }
138 
139         @Override
setPrimaryItem(ViewGroup container, int position, Object object)140         public void setPrimaryItem(ViewGroup container, int position, Object object) {
141             // The parent's setPrimaryItem() also calls setMenuVisibility(), so we want to know
142             // when it happens.
143             if (DEBUG) {
144                 Log.d(TAG, "FragmentPagerAdapter#setPrimaryItem(), position: " + position);
145             }
146             super.setPrimaryItem(container, position, object);
147         }
148 
149         @Override
getCount()150         public int getCount() {
151             return TAB_INDEX_COUNT;
152         }
153     }
154 
155     /**
156      * True when the app detects user's drag event. This variable should not become true when
157      * mUserTabClick is true.
158      *
159      * During user's drag or tab click, we shouldn't show fake buttons but just show real
160      * ActionBar at the bottom of the screen, for transition animation.
161      */
162     boolean mDuringSwipe = false;
163     /**
164      * True when the app detects user's tab click (at the top of the screen). This variable should
165      * not become true when mDuringSwipe is true.
166      *
167      * During user's drag or tab click, we shouldn't show fake buttons but just show real
168      * ActionBar at the bottom of the screen, for transition animation.
169      */
170     boolean mUserTabClick = false;
171 
172     private class PageChangeListener implements OnPageChangeListener {
173         private int mCurrentPosition = -1;
174         /**
175          * Used during page migration, to remember the next position {@link #onPageSelected(int)}
176          * specified.
177          */
178         private int mNextPosition = -1;
179 
180         @Override
onPageScrolled( int position, float positionOffset, int positionOffsetPixels)181         public void onPageScrolled(
182                 int position, float positionOffset, int positionOffsetPixels) {
183         }
184 
185         @Override
onPageSelected(int position)186         public void onPageSelected(int position) {
187             if (DEBUG) Log.d(TAG, "onPageSelected: position: " + position);
188             final ActionBar actionBar = getActionBar();
189             if (mDialpadFragment != null) {
190                 if (mDuringSwipe && position == TAB_INDEX_DIALER) {
191                     // TODO: Figure out if we want this or not. Right now
192                     // - with this call, both fake buttons and real action bar overlap
193                     // - without this call, there's tiny flicker happening to search/menu buttons.
194                     // If we can reduce the flicker without this call, it would be much better.
195                     // updateFakeMenuButtonsVisibility(true);
196                 }
197             }
198 
199             if (mCurrentPosition == position) {
200                 Log.w(TAG, "Previous position and next position became same (" + position + ")");
201             }
202 
203             actionBar.selectTab(actionBar.getTabAt(position));
204             mNextPosition = position;
205         }
206 
setCurrentPosition(int position)207         public void setCurrentPosition(int position) {
208             mCurrentPosition = position;
209         }
210 
getCurrentPosition()211         public int getCurrentPosition() {
212             return mCurrentPosition;
213         }
214 
215         @Override
onPageScrollStateChanged(int state)216         public void onPageScrollStateChanged(int state) {
217             switch (state) {
218                 case ViewPager.SCROLL_STATE_IDLE: {
219                     if (mNextPosition == -1) {
220                         // This happens when the user drags the screen just after launching the
221                         // application, and settle down the same screen without actually swiping it.
222                         // At that moment mNextPosition is apparently -1 yet, and we expect it
223                         // being updated by onPageSelected(), which is *not* called if the user
224                         // settle down the exact same tab after the dragging.
225                         if (DEBUG) {
226                             Log.d(TAG, "Next position is not specified correctly. Use current tab ("
227                                     + mViewPager.getCurrentItem() + ")");
228                         }
229                         mNextPosition = mViewPager.getCurrentItem();
230                     }
231                     if (DEBUG) {
232                         Log.d(TAG, "onPageScrollStateChanged() with SCROLL_STATE_IDLE. "
233                                 + "mCurrentPosition: " + mCurrentPosition
234                                 + ", mNextPosition: " + mNextPosition);
235                     }
236                     // Interpret IDLE as the end of migration (both swipe and tab click)
237                     mDuringSwipe = false;
238                     mUserTabClick = false;
239 
240                     updateFakeMenuButtonsVisibility(mNextPosition == TAB_INDEX_DIALER);
241                     sendFragmentVisibilityChange(mCurrentPosition, false);
242                     sendFragmentVisibilityChange(mNextPosition, true);
243 
244                     invalidateOptionsMenu();
245 
246                     mCurrentPosition = mNextPosition;
247                     break;
248                 }
249                 case ViewPager.SCROLL_STATE_DRAGGING: {
250                     if (DEBUG) Log.d(TAG, "onPageScrollStateChanged() with SCROLL_STATE_DRAGGING");
251                     mDuringSwipe = true;
252                     mUserTabClick = false;
253                     break;
254                 }
255                 case ViewPager.SCROLL_STATE_SETTLING: {
256                     if (DEBUG) Log.d(TAG, "onPageScrollStateChanged() with SCROLL_STATE_SETTLING");
257                     mDuringSwipe = true;
258                     mUserTabClick = false;
259                     break;
260                 }
261                 default:
262                     break;
263             }
264         }
265     }
266 
267     private String mFilterText;
268 
269     /** Enables horizontal swipe between Fragments. */
270     private ViewPager mViewPager;
271     private final PageChangeListener mPageChangeListener = new PageChangeListener();
272     private DialpadFragment mDialpadFragment;
273     private CallLogFragment mCallLogFragment;
274     private PhoneFavoriteFragment mPhoneFavoriteFragment;
275 
276     private View mSearchButton;
277     private View mMenuButton;
278 
279     private final ContactListFilterListener mContactListFilterListener =
280             new ContactListFilterListener() {
281         @Override
282         public void onContactListFilterChanged() {
283             boolean doInvalidateOptionsMenu = false;
284 
285             if (mPhoneFavoriteFragment != null && mPhoneFavoriteFragment.isAdded()) {
286                 mPhoneFavoriteFragment.setFilter(mContactListFilterController.getFilter());
287                 doInvalidateOptionsMenu = true;
288             }
289 
290             if (mSearchFragment != null && mSearchFragment.isAdded()) {
291                 mSearchFragment.setFilter(mContactListFilterController.getFilter());
292                 doInvalidateOptionsMenu = true;
293             } else {
294                 Log.w(TAG, "Search Fragment isn't available when ContactListFilter is changed");
295             }
296 
297             if (doInvalidateOptionsMenu) {
298                 invalidateOptionsMenu();
299             }
300         }
301     };
302 
303     private final TabListener mTabListener = new TabListener() {
304         @Override
305         public void onTabUnselected(Tab tab, FragmentTransaction ft) {
306             if (DEBUG) Log.d(TAG, "onTabUnselected(). tab: " + tab);
307         }
308 
309         @Override
310         public void onTabSelected(Tab tab, FragmentTransaction ft) {
311             if (DEBUG) {
312                 Log.d(TAG, "onTabSelected(). tab: " + tab + ", mDuringSwipe: " + mDuringSwipe);
313             }
314             // When the user swipes the screen horizontally, this method will be called after
315             // ViewPager.SCROLL_STATE_DRAGGING and ViewPager.SCROLL_STATE_SETTLING events, while
316             // when the user clicks a tab at the ActionBar at the top, this will be called before
317             // them. This logic interprets the order difference as a difference of the user action.
318             if (!mDuringSwipe) {
319                 if (DEBUG) {
320                     Log.d(TAG, "Tab select. from: " + mPageChangeListener.getCurrentPosition()
321                             + ", to: " + tab.getPosition());
322                 }
323                 if (mDialpadFragment != null) {
324                     updateFakeMenuButtonsVisibility(tab.getPosition() == TAB_INDEX_DIALER);
325                 }
326                 mUserTabClick = true;
327             }
328 
329             if (mViewPager.getCurrentItem() != tab.getPosition()) {
330                 mViewPager.setCurrentItem(tab.getPosition(), true);
331             }
332 
333             // During the call, we don't remember the tab position.
334             if (!DialpadFragment.phoneIsInUse()) {
335                 // Remember this tab index. This function is also called, if the tab is set
336                 // automatically in which case the setter (setCurrentTab) has to set this to its old
337                 // value afterwards
338                 mLastManuallySelectedFragment = tab.getPosition();
339             }
340         }
341 
342         @Override
343         public void onTabReselected(Tab tab, FragmentTransaction ft) {
344             if (DEBUG) Log.d(TAG, "onTabReselected");
345         }
346     };
347 
348     /**
349      * Fragment for searching phone numbers. Unlike the other Fragments, this doesn't correspond
350      * to tab but is shown by a search action.
351      */
352     private PhoneNumberPickerFragment mSearchFragment;
353     /**
354      * True when this Activity is in its search UI (with a {@link SearchView} and
355      * {@link PhoneNumberPickerFragment}).
356      */
357     private boolean mInSearchUi;
358     private SearchView mSearchView;
359 
360     private final OnClickListener mFilterOptionClickListener = new OnClickListener() {
361         @Override
362         public void onClick(View view) {
363             final PopupMenu popupMenu = new PopupMenu(DialtactsActivity.this, view);
364             final Menu menu = popupMenu.getMenu();
365             popupMenu.inflate(R.menu.dialtacts_search_options);
366             final MenuItem filterOptionMenuItem = menu.findItem(R.id.filter_option);
367             filterOptionMenuItem.setOnMenuItemClickListener(mFilterOptionsMenuItemClickListener);
368             final MenuItem addContactOptionMenuItem = menu.findItem(R.id.add_contact);
369             addContactOptionMenuItem.setIntent(
370                     new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI));
371             popupMenu.show();
372         }
373     };
374 
375     /**
376      * The index of the Fragment (or, the tab) that has last been manually selected.
377      * This value does not keep track of programmatically set Tabs (e.g. Call Log after a Call)
378      */
379     private int mLastManuallySelectedFragment;
380 
381     private ContactListFilterController mContactListFilterController;
382     private OnMenuItemClickListener mFilterOptionsMenuItemClickListener =
383             new OnMenuItemClickListener() {
384         @Override
385         public boolean onMenuItemClick(MenuItem item) {
386             AccountFilterUtil.startAccountFilterActivityForResult(
387                     DialtactsActivity.this, SUBACTIVITY_ACCOUNT_FILTER,
388                     mContactListFilterController.getFilter());
389             return true;
390         }
391     };
392 
393     private OnMenuItemClickListener mSearchMenuItemClickListener =
394             new OnMenuItemClickListener() {
395         @Override
396         public boolean onMenuItemClick(MenuItem item) {
397             enterSearchUi();
398             return true;
399         }
400     };
401 
402     /**
403      * Listener used when one of phone numbers in search UI is selected. This will initiate a
404      * phone call using the phone number.
405      */
406     private final OnPhoneNumberPickerActionListener mPhoneNumberPickerActionListener =
407             new OnPhoneNumberPickerActionListener() {
408                 @Override
409                 public void onPickPhoneNumberAction(Uri dataUri) {
410                     // Specify call-origin so that users will see the previous tab instead of
411                     // CallLog screen (search UI will be automatically exited).
412                     PhoneNumberInteraction.startInteractionForPhoneCall(
413                             DialtactsActivity.this, dataUri, getCallOrigin());
414                 }
415 
416                 @Override
417                 public void onShortcutIntentCreated(Intent intent) {
418                     Log.w(TAG, "Unsupported intent has come (" + intent + "). Ignoring.");
419                 }
420 
421                 @Override
422                 public void onHomeInActionBarSelected() {
423                     exitSearchUi();
424                 }
425     };
426 
427     /**
428      * Listener used to send search queries to the phone search fragment.
429      */
430     private final OnQueryTextListener mPhoneSearchQueryTextListener =
431             new OnQueryTextListener() {
432                 @Override
433                 public boolean onQueryTextSubmit(String query) {
434                     View view = getCurrentFocus();
435                     if (view != null) {
436                         hideInputMethod(view);
437                         view.clearFocus();
438                     }
439                     return true;
440                 }
441 
442                 @Override
443                 public boolean onQueryTextChange(String newText) {
444                     // Show search result with non-empty text. Show a bare list otherwise.
445                     if (mSearchFragment != null) {
446                         mSearchFragment.setQueryString(newText, true);
447                     }
448                     return true;
449                 }
450     };
451 
452     /**
453      * Listener used to handle the "close" button on the right side of {@link SearchView}.
454      * If some text is in the search view, this will clean it up. Otherwise this will exit
455      * the search UI and let users go back to usual Phone UI.
456      *
457      * This does _not_ handle back button.
458      */
459     private final OnCloseListener mPhoneSearchCloseListener =
460             new OnCloseListener() {
461                 @Override
462                 public boolean onClose() {
463                     if (!TextUtils.isEmpty(mSearchView.getQuery())) {
464                         mSearchView.setQuery(null, true);
465                     }
466                     return true;
467                 }
468     };
469 
470     private final View.OnLayoutChangeListener mFirstLayoutListener
471             = new View.OnLayoutChangeListener() {
472         @Override
473         public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
474                 int oldTop, int oldRight, int oldBottom) {
475             v.removeOnLayoutChangeListener(this); // Unregister self.
476             addSearchFragment();
477         }
478     };
479 
480     @Override
onCreate(Bundle icicle)481     protected void onCreate(Bundle icicle) {
482         super.onCreate(icicle);
483 
484         final Intent intent = getIntent();
485         fixIntent(intent);
486 
487         setContentView(R.layout.dialtacts_activity);
488 
489         mContactListFilterController = ContactListFilterController.getInstance(this);
490         mContactListFilterController.addListener(mContactListFilterListener);
491 
492         findViewById(R.id.dialtacts_frame).addOnLayoutChangeListener(mFirstLayoutListener);
493 
494         mViewPager = (ViewPager) findViewById(R.id.pager);
495         mViewPager.setAdapter(new ViewPagerAdapter(getFragmentManager()));
496         mViewPager.setOnPageChangeListener(mPageChangeListener);
497         mViewPager.setOffscreenPageLimit(2);
498 
499         // Do same width calculation as ActionBar does
500         DisplayMetrics dm = getResources().getDisplayMetrics();
501         int minCellSize = getResources().getDimensionPixelSize(R.dimen.fake_menu_button_min_width);
502         int cellCount = dm.widthPixels / minCellSize;
503         int fakeMenuItemWidth = dm.widthPixels / cellCount;
504         if (DEBUG) Log.d(TAG, "The size of fake menu buttons (in pixel): " + fakeMenuItemWidth);
505 
506         // Soft menu button should appear only when there's no hardware menu button.
507         mMenuButton = findViewById(R.id.overflow_menu);
508         if (mMenuButton != null) {
509             mMenuButton.setMinimumWidth(fakeMenuItemWidth);
510             if (ViewConfiguration.get(this).hasPermanentMenuKey()) {
511                 // This is required for dialpad button's layout, so must not use GONE here.
512                 mMenuButton.setVisibility(View.INVISIBLE);
513             } else {
514                 mMenuButton.setOnClickListener(this);
515             }
516         }
517         mSearchButton = findViewById(R.id.searchButton);
518         if (mSearchButton != null) {
519             mSearchButton.setMinimumWidth(fakeMenuItemWidth);
520             mSearchButton.setOnClickListener(this);
521         }
522 
523         // Setup the ActionBar tabs (the order matches the tab-index contants TAB_INDEX_*)
524         setupDialer();
525         setupCallLog();
526         setupFavorites();
527         getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
528         getActionBar().setDisplayShowTitleEnabled(false);
529         getActionBar().setDisplayShowHomeEnabled(false);
530 
531         // Load the last manually loaded tab
532         mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
533         mLastManuallySelectedFragment = mPrefs.getInt(PREF_LAST_MANUALLY_SELECTED_TAB,
534                 PREF_LAST_MANUALLY_SELECTED_TAB_DEFAULT);
535         if (mLastManuallySelectedFragment >= TAB_INDEX_COUNT) {
536             // Stored value may have exceeded the number of current tabs. Reset it.
537             mLastManuallySelectedFragment = PREF_LAST_MANUALLY_SELECTED_TAB_DEFAULT;
538         }
539 
540         setCurrentTab(intent);
541 
542         if (UI.FILTER_CONTACTS_ACTION.equals(intent.getAction())
543                 && icicle == null) {
544             setupFilterText(intent);
545         }
546     }
547 
548     @Override
onStart()549     public void onStart() {
550         super.onStart();
551         if (mPhoneFavoriteFragment != null) {
552             mPhoneFavoriteFragment.setFilter(mContactListFilterController.getFilter());
553         }
554         if (mSearchFragment != null) {
555             mSearchFragment.setFilter(mContactListFilterController.getFilter());
556         }
557 
558         if (mDuringSwipe || mUserTabClick) {
559             if (DEBUG) Log.d(TAG, "reset buggy flag state..");
560             mDuringSwipe = false;
561             mUserTabClick = false;
562         }
563 
564         final int currentPosition = mPageChangeListener.getCurrentPosition();
565         if (DEBUG) {
566             Log.d(TAG, "onStart(). current position: " + mPageChangeListener.getCurrentPosition()
567                     + ". Reset all menu visibility state.");
568         }
569         updateFakeMenuButtonsVisibility(currentPosition == TAB_INDEX_DIALER && !mInSearchUi);
570         for (int i = 0; i < TAB_INDEX_COUNT; i++) {
571             sendFragmentVisibilityChange(i, i == currentPosition);
572         }
573     }
574 
575     @Override
onDestroy()576     public void onDestroy() {
577         super.onDestroy();
578         mContactListFilterController.removeListener(mContactListFilterListener);
579     }
580 
581     @Override
onClick(View view)582     public void onClick(View view) {
583         switch (view.getId()) {
584             case R.id.searchButton: {
585                 enterSearchUi();
586                 break;
587             }
588             case R.id.overflow_menu: {
589                 if (mDialpadFragment != null) {
590                     PopupMenu popup = mDialpadFragment.constructPopupMenu(view);
591                     if (popup != null) {
592                         popup.show();
593                     }
594                 } else {
595                     Log.w(TAG, "DialpadFragment is null during onClick() event for " + view);
596                 }
597                 break;
598             }
599             default: {
600                 Log.wtf(TAG, "Unexpected onClick event from " + view);
601                 break;
602             }
603         }
604     }
605 
606     /**
607      * Add search fragment.  Note this is called during onLayout, so there's some restrictions,
608      * such as executePendingTransaction can't be used in it.
609      */
addSearchFragment()610     private void addSearchFragment() {
611         // In order to take full advantage of "fragment deferred start", we need to create the
612         // search fragment after all other fragments are created.
613         // The other fragments are created by the ViewPager on the first onMeasure().
614         // We use the first onLayout call, which is after onMeasure().
615 
616         // Just return if the fragment is already created, which happens after configuration
617         // changes.
618         if (mSearchFragment != null) return;
619 
620         final FragmentTransaction ft = getFragmentManager().beginTransaction();
621         final Fragment searchFragment = new PhoneNumberPickerFragment();
622 
623         searchFragment.setUserVisibleHint(false);
624         ft.add(R.id.dialtacts_frame, searchFragment);
625         ft.hide(searchFragment);
626         ft.commitAllowingStateLoss();
627     }
628 
prepareSearchView()629     private void prepareSearchView() {
630         final View searchViewLayout =
631                 getLayoutInflater().inflate(R.layout.dialtacts_custom_action_bar, null);
632         mSearchView = (SearchView) searchViewLayout.findViewById(R.id.search_view);
633         mSearchView.setOnQueryTextListener(mPhoneSearchQueryTextListener);
634         mSearchView.setOnCloseListener(mPhoneSearchCloseListener);
635         // Since we're using a custom layout for showing SearchView instead of letting the
636         // search menu icon do that job, we need to manually configure the View so it looks
637         // "shown via search menu".
638         // - it should be iconified by default
639         // - it should not be iconified at this time
640         // See also comments for onActionViewExpanded()/onActionViewCollapsed()
641         mSearchView.setIconifiedByDefault(true);
642         mSearchView.setQueryHint(getString(R.string.hint_findContacts));
643         mSearchView.setIconified(false);
644         mSearchView.setOnQueryTextFocusChangeListener(new OnFocusChangeListener() {
645             @Override
646             public void onFocusChange(View view, boolean hasFocus) {
647                 if (hasFocus) {
648                     showInputMethod(view.findFocus());
649                 }
650             }
651         });
652 
653         if (!ViewConfiguration.get(this).hasPermanentMenuKey()) {
654             // Filter option menu should be shown on the right side of SearchView.
655             final View filterOptionView = searchViewLayout.findViewById(R.id.search_option);
656             filterOptionView.setVisibility(View.VISIBLE);
657             filterOptionView.setOnClickListener(mFilterOptionClickListener);
658         }
659 
660         getActionBar().setCustomView(searchViewLayout,
661                 new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
662     }
663 
664     @Override
onAttachFragment(Fragment fragment)665     public void onAttachFragment(Fragment fragment) {
666         // This method can be called before onCreate(), at which point we cannot rely on ViewPager.
667         // In that case, we will setup the "current position" soon after the ViewPager is ready.
668         final int currentPosition = mViewPager != null ? mViewPager.getCurrentItem() : -1;
669 
670         if (fragment instanceof DialpadFragment) {
671             mDialpadFragment = (DialpadFragment) fragment;
672         } else if (fragment instanceof CallLogFragment) {
673             mCallLogFragment = (CallLogFragment) fragment;
674         } else if (fragment instanceof PhoneFavoriteFragment) {
675             mPhoneFavoriteFragment = (PhoneFavoriteFragment) fragment;
676             mPhoneFavoriteFragment.setListener(mPhoneFavoriteListener);
677             if (mContactListFilterController != null
678                     && mContactListFilterController.getFilter() != null) {
679                 mPhoneFavoriteFragment.setFilter(mContactListFilterController.getFilter());
680             }
681         } else if (fragment instanceof PhoneNumberPickerFragment) {
682             mSearchFragment = (PhoneNumberPickerFragment) fragment;
683             mSearchFragment.setOnPhoneNumberPickerActionListener(mPhoneNumberPickerActionListener);
684             mSearchFragment.setQuickContactEnabled(true);
685             mSearchFragment.setDarkTheme(true);
686             mSearchFragment.setPhotoPosition(ContactListItemView.PhotoPosition.LEFT);
687             mSearchFragment.setUseCallableUri(true);
688             if (mContactListFilterController != null
689                     && mContactListFilterController.getFilter() != null) {
690                 mSearchFragment.setFilter(mContactListFilterController.getFilter());
691             }
692             // Here we assume that we're not on the search mode, so let's hide the fragment.
693             //
694             // We get here either when the fragment is created (normal case), or after configuration
695             // changes.  In the former case, we're not in search mode because we can only
696             // enter search mode if the fragment is created.  (see enterSearchUi())
697             // In the latter case we're not in search mode either because we don't retain
698             // mInSearchUi -- ideally we should but at this point it's not supported.
699             mSearchFragment.setUserVisibleHint(false);
700             // After configuration changes fragments will forget their "hidden" state, so make
701             // sure to hide it.
702             if (!mSearchFragment.isHidden()) {
703                 final FragmentTransaction transaction = getFragmentManager().beginTransaction();
704                 transaction.hide(mSearchFragment);
705                 transaction.commitAllowingStateLoss();
706             }
707         }
708     }
709 
710     @Override
onPause()711     protected void onPause() {
712         super.onPause();
713 
714         mPrefs.edit().putInt(PREF_LAST_MANUALLY_SELECTED_TAB, mLastManuallySelectedFragment)
715                 .apply();
716     }
717 
fixIntent(Intent intent)718     private void fixIntent(Intent intent) {
719         // This should be cleaned up: the call key used to send an Intent
720         // that just said to go to the recent calls list.  It now sends this
721         // abstract action, but this class hasn't been rewritten to deal with it.
722         if (Intent.ACTION_CALL_BUTTON.equals(intent.getAction())) {
723             intent.setDataAndType(Calls.CONTENT_URI, Calls.CONTENT_TYPE);
724             intent.putExtra("call_key", true);
725             setIntent(intent);
726         }
727     }
728 
setupDialer()729     private void setupDialer() {
730         final Tab tab = getActionBar().newTab();
731         tab.setContentDescription(R.string.dialerIconLabel);
732         tab.setTabListener(mTabListener);
733         tab.setIcon(R.drawable.ic_tab_dialer);
734         getActionBar().addTab(tab);
735     }
736 
setupCallLog()737     private void setupCallLog() {
738         final Tab tab = getActionBar().newTab();
739         tab.setContentDescription(R.string.recentCallsIconLabel);
740         tab.setIcon(R.drawable.ic_tab_recent);
741         tab.setTabListener(mTabListener);
742         getActionBar().addTab(tab);
743     }
744 
setupFavorites()745     private void setupFavorites() {
746         final Tab tab = getActionBar().newTab();
747         tab.setContentDescription(R.string.contactsFavoritesLabel);
748         tab.setIcon(R.drawable.ic_tab_all);
749         tab.setTabListener(mTabListener);
750         getActionBar().addTab(tab);
751     }
752 
753     /**
754      * Returns true if the intent is due to hitting the green send key while in a call.
755      *
756      * @param intent the intent that launched this activity
757      * @param recentCallsRequest true if the intent is requesting to view recent calls
758      * @return true if the intent is due to hitting the green send key while in a call
759      */
isSendKeyWhileInCall(final Intent intent, final boolean recentCallsRequest)760     private boolean isSendKeyWhileInCall(final Intent intent,
761             final boolean recentCallsRequest) {
762         // If there is a call in progress go to the call screen
763         if (recentCallsRequest) {
764             final boolean callKey = intent.getBooleanExtra("call_key", false);
765 
766             try {
767                 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
768                 if (callKey && phone != null && phone.showCallScreen()) {
769                     return true;
770                 }
771             } catch (RemoteException e) {
772                 Log.e(TAG, "Failed to handle send while in call", e);
773             }
774         }
775 
776         return false;
777     }
778 
779     /**
780      * Sets the current tab based on the intent's request type
781      *
782      * @param intent Intent that contains information about which tab should be selected
783      */
setCurrentTab(Intent intent)784     private void setCurrentTab(Intent intent) {
785         // If we got here by hitting send and we're in call forward along to the in-call activity
786         final boolean recentCallsRequest = Calls.CONTENT_TYPE.equals(intent.getType());
787         if (isSendKeyWhileInCall(intent, recentCallsRequest)) {
788             finish();
789             return;
790         }
791 
792         // Remember the old manually selected tab index so that it can be restored if it is
793         // overwritten by one of the programmatic tab selections
794         final int savedTabIndex = mLastManuallySelectedFragment;
795 
796         final int tabIndex;
797         if (DialpadFragment.phoneIsInUse() || isDialIntent(intent)) {
798             tabIndex = TAB_INDEX_DIALER;
799         } else if (recentCallsRequest) {
800             tabIndex = TAB_INDEX_CALL_LOG;
801         } else {
802             tabIndex = mLastManuallySelectedFragment;
803         }
804 
805         final int previousItemIndex = mViewPager.getCurrentItem();
806         mViewPager.setCurrentItem(tabIndex, false /* smoothScroll */);
807         if (previousItemIndex != tabIndex) {
808             sendFragmentVisibilityChange(previousItemIndex, false /* not visible */ );
809         }
810         mPageChangeListener.setCurrentPosition(tabIndex);
811         sendFragmentVisibilityChange(tabIndex, true /* visible */ );
812 
813         // Restore to the previous manual selection
814         mLastManuallySelectedFragment = savedTabIndex;
815         mDuringSwipe = false;
816         mUserTabClick = false;
817     }
818 
819     @Override
onNewIntent(Intent newIntent)820     public void onNewIntent(Intent newIntent) {
821         setIntent(newIntent);
822         fixIntent(newIntent);
823         setCurrentTab(newIntent);
824         final String action = newIntent.getAction();
825         if (UI.FILTER_CONTACTS_ACTION.equals(action)) {
826             setupFilterText(newIntent);
827         }
828         if (mInSearchUi || (mSearchFragment != null && mSearchFragment.isVisible())) {
829             exitSearchUi();
830         }
831 
832         if (mViewPager.getCurrentItem() == TAB_INDEX_DIALER) {
833             if (mDialpadFragment != null) {
834                 mDialpadFragment.configureScreenFromIntent(newIntent);
835             } else {
836                 Log.e(TAG, "DialpadFragment isn't ready yet when the tab is already selected.");
837             }
838         } else if (mViewPager.getCurrentItem() == TAB_INDEX_CALL_LOG) {
839             if (mCallLogFragment != null) {
840                 mCallLogFragment.configureScreenFromIntent(newIntent);
841             } else {
842                 Log.e(TAG, "CallLogFragment isn't ready yet when the tab is already selected.");
843             }
844         }
845         invalidateOptionsMenu();
846     }
847 
848     /** Returns true if the given intent contains a phone number to populate the dialer with */
isDialIntent(Intent intent)849     private boolean isDialIntent(Intent intent) {
850         final String action = intent.getAction();
851         if (Intent.ACTION_DIAL.equals(action) || ACTION_TOUCH_DIALER.equals(action)) {
852             return true;
853         }
854         if (Intent.ACTION_VIEW.equals(action)) {
855             final Uri data = intent.getData();
856             if (data != null && Constants.SCHEME_TEL.equals(data.getScheme())) {
857                 return true;
858             }
859         }
860         return false;
861     }
862 
863     /**
864      * Returns an appropriate call origin for this Activity. May return null when no call origin
865      * should be used (e.g. when some 3rd party application launched the screen. Call origin is
866      * for remembering the tab in which the user made a phone call, so the external app's DIAL
867      * request should not be counted.)
868      */
getCallOrigin()869     public String getCallOrigin() {
870         return !isDialIntent(getIntent()) ? CALL_ORIGIN_DIALTACTS : null;
871     }
872 
873     /**
874      * Retrieves the filter text stored in {@link #setupFilterText(Intent)}.
875      * This text originally came from a FILTER_CONTACTS_ACTION intent received
876      * by this activity. The stored text will then be cleared after after this
877      * method returns.
878      *
879      * @return The stored filter text
880      */
getAndClearFilterText()881     public String getAndClearFilterText() {
882         String filterText = mFilterText;
883         mFilterText = null;
884         return filterText;
885     }
886 
887     /**
888      * Stores the filter text associated with a FILTER_CONTACTS_ACTION intent.
889      * This is so child activities can check if they are supposed to display a filter.
890      *
891      * @param intent The intent received in {@link #onNewIntent(Intent)}
892      */
setupFilterText(Intent intent)893     private void setupFilterText(Intent intent) {
894         // If the intent was relaunched from history, don't apply the filter text.
895         if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) {
896             return;
897         }
898         String filter = intent.getStringExtra(UI.FILTER_TEXT_EXTRA_KEY);
899         if (filter != null && filter.length() > 0) {
900             mFilterText = filter;
901         }
902     }
903 
904     @Override
onBackPressed()905     public void onBackPressed() {
906         if (mInSearchUi) {
907             // We should let the user go back to usual screens with tabs.
908             exitSearchUi();
909         } else if (isTaskRoot()) {
910             // Instead of stopping, simply push this to the back of the stack.
911             // This is only done when running at the top of the stack;
912             // otherwise, we have been launched by someone else so need to
913             // allow the user to go back to the caller.
914             moveTaskToBack(false);
915         } else {
916             super.onBackPressed();
917         }
918     }
919 
920     private final PhoneFavoriteFragment.Listener mPhoneFavoriteListener =
921             new PhoneFavoriteFragment.Listener() {
922         @Override
923         public void onContactSelected(Uri contactUri) {
924             PhoneNumberInteraction.startInteractionForPhoneCall(
925                     DialtactsActivity.this, contactUri, getCallOrigin());
926         }
927 
928         @Override
929         public void onCallNumberDirectly(String phoneNumber) {
930             Intent intent = ContactsUtils.getCallIntent(phoneNumber, getCallOrigin());
931             startActivity(intent);
932         }
933     };
934 
935     @Override
onCreateOptionsMenu(Menu menu)936     public boolean onCreateOptionsMenu(Menu menu) {
937         MenuInflater inflater = getMenuInflater();
938         inflater.inflate(R.menu.dialtacts_options, menu);
939 
940         // set up intents and onClick listeners
941         final MenuItem callSettingsMenuItem = menu.findItem(R.id.menu_call_settings);
942         final MenuItem searchMenuItem = menu.findItem(R.id.search_on_action_bar);
943         final MenuItem filterOptionMenuItem = menu.findItem(R.id.filter_option);
944         final MenuItem addContactOptionMenuItem = menu.findItem(R.id.add_contact);
945 
946         callSettingsMenuItem.setIntent(DialtactsActivity.getCallSettingsIntent());
947         searchMenuItem.setOnMenuItemClickListener(mSearchMenuItemClickListener);
948         filterOptionMenuItem.setOnMenuItemClickListener(mFilterOptionsMenuItemClickListener);
949         addContactOptionMenuItem.setIntent(
950                 new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI));
951 
952         return true;
953     }
954 
955     @Override
onPrepareOptionsMenu(Menu menu)956     public boolean onPrepareOptionsMenu(Menu menu) {
957         if (mInSearchUi) {
958             prepareOptionsMenuInSearchMode(menu);
959         } else {
960             // get reference to the currently selected tab
961             final Tab tab = getActionBar().getSelectedTab();
962             if (tab != null) {
963                 switch(tab.getPosition()) {
964                     case TAB_INDEX_DIALER:
965                         prepareOptionsMenuForDialerTab(menu);
966                         break;
967                     case TAB_INDEX_CALL_LOG:
968                         prepareOptionsMenuForCallLogTab(menu);
969                         break;
970                     case TAB_INDEX_FAVORITES:
971                         prepareOptionsMenuForFavoritesTab(menu);
972                         break;
973                 }
974             }
975         }
976         return true;
977     }
978 
prepareOptionsMenuInSearchMode(Menu menu)979     private void prepareOptionsMenuInSearchMode(Menu menu) {
980         // get references to menu items
981         final MenuItem searchMenuItem = menu.findItem(R.id.search_on_action_bar);
982         final MenuItem filterOptionMenuItem = menu.findItem(R.id.filter_option);
983         final MenuItem addContactOptionMenuItem = menu.findItem(R.id.add_contact);
984         final MenuItem callSettingsMenuItem = menu.findItem(R.id.menu_call_settings);
985         final MenuItem emptyRightMenuItem = menu.findItem(R.id.empty_right_menu_item);
986 
987         // prepare the menu items
988         searchMenuItem.setVisible(false);
989         filterOptionMenuItem.setVisible(ViewConfiguration.get(this).hasPermanentMenuKey());
990         addContactOptionMenuItem.setVisible(false);
991         callSettingsMenuItem.setVisible(false);
992         emptyRightMenuItem.setVisible(false);
993     }
994 
prepareOptionsMenuForDialerTab(Menu menu)995     private void prepareOptionsMenuForDialerTab(Menu menu) {
996         if (DEBUG) {
997             Log.d(TAG, "onPrepareOptionsMenu(dialer). swipe: " + mDuringSwipe
998                     + ", user tab click: " + mUserTabClick);
999         }
1000 
1001         // get references to menu items
1002         final MenuItem searchMenuItem = menu.findItem(R.id.search_on_action_bar);
1003         final MenuItem filterOptionMenuItem = menu.findItem(R.id.filter_option);
1004         final MenuItem addContactOptionMenuItem = menu.findItem(R.id.add_contact);
1005         final MenuItem callSettingsMenuItem = menu.findItem(R.id.menu_call_settings);
1006         final MenuItem emptyRightMenuItem = menu.findItem(R.id.empty_right_menu_item);
1007 
1008         // prepare the menu items
1009         filterOptionMenuItem.setVisible(false);
1010         addContactOptionMenuItem.setVisible(false);
1011         if (mDuringSwipe || mUserTabClick) {
1012             // During horizontal movement, the real ActionBar menu items are shown
1013             searchMenuItem.setVisible(true);
1014             callSettingsMenuItem.setVisible(true);
1015             // When there is a permanent menu key, there is no overflow icon on the right of
1016             // the action bar which would force the search menu item (if it is visible) to the
1017             // left.  This is the purpose of showing the emptyRightMenuItem.
1018             emptyRightMenuItem.setVisible(ViewConfiguration.get(this).hasPermanentMenuKey());
1019         } else {
1020             // This is when the user is looking at the dialer pad.  In this case, the real
1021             // ActionBar is hidden and fake menu items are shown.
1022             searchMenuItem.setVisible(false);
1023             // If a permanent menu key is available, then we need to show the call settings item
1024             // so that the call settings item can be invoked by the permanent menu key.
1025             callSettingsMenuItem.setVisible(ViewConfiguration.get(this).hasPermanentMenuKey());
1026             emptyRightMenuItem.setVisible(false);
1027         }
1028     }
1029 
prepareOptionsMenuForCallLogTab(Menu menu)1030     private void prepareOptionsMenuForCallLogTab(Menu menu) {
1031         // get references to menu items
1032         final MenuItem searchMenuItem = menu.findItem(R.id.search_on_action_bar);
1033         final MenuItem filterOptionMenuItem = menu.findItem(R.id.filter_option);
1034         final MenuItem addContactOptionMenuItem = menu.findItem(R.id.add_contact);
1035         final MenuItem callSettingsMenuItem = menu.findItem(R.id.menu_call_settings);
1036         final MenuItem emptyRightMenuItem = menu.findItem(R.id.empty_right_menu_item);
1037 
1038         // prepare the menu items
1039         searchMenuItem.setVisible(true);
1040         filterOptionMenuItem.setVisible(false);
1041         addContactOptionMenuItem.setVisible(false);
1042         callSettingsMenuItem.setVisible(true);
1043         emptyRightMenuItem.setVisible(ViewConfiguration.get(this).hasPermanentMenuKey());
1044     }
1045 
prepareOptionsMenuForFavoritesTab(Menu menu)1046     private void prepareOptionsMenuForFavoritesTab(Menu menu) {
1047         // get references to menu items
1048         final MenuItem searchMenuItem = menu.findItem(R.id.search_on_action_bar);
1049         final MenuItem filterOptionMenuItem = menu.findItem(R.id.filter_option);
1050         final MenuItem addContactOptionMenuItem = menu.findItem(R.id.add_contact);
1051         final MenuItem callSettingsMenuItem = menu.findItem(R.id.menu_call_settings);
1052         final MenuItem emptyRightMenuItem = menu.findItem(R.id.empty_right_menu_item);
1053 
1054         // prepare the menu items
1055         searchMenuItem.setVisible(true);
1056         filterOptionMenuItem.setVisible(true);
1057         addContactOptionMenuItem.setVisible(true);
1058         callSettingsMenuItem.setVisible(true);
1059         emptyRightMenuItem.setVisible(false);
1060     }
1061 
1062     @Override
startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData, boolean globalSearch)1063     public void startSearch(String initialQuery, boolean selectInitialQuery,
1064             Bundle appSearchData, boolean globalSearch) {
1065         if (mSearchFragment != null && mSearchFragment.isAdded() && !globalSearch) {
1066             if (mInSearchUi) {
1067                 if (mSearchView.hasFocus()) {
1068                     showInputMethod(mSearchView.findFocus());
1069                 } else {
1070                     mSearchView.requestFocus();
1071                 }
1072             } else {
1073                 enterSearchUi();
1074             }
1075         } else {
1076             super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
1077         }
1078     }
1079 
1080     /**
1081      * Hides every tab and shows search UI for phone lookup.
1082      */
enterSearchUi()1083     private void enterSearchUi() {
1084         if (mSearchFragment == null) {
1085             // We add the search fragment dynamically in the first onLayoutChange() and
1086             // mSearchFragment is set sometime later when the fragment transaction is actually
1087             // executed, which means there's a window when users are able to hit the (physical)
1088             // search key but mSearchFragment is still null.
1089             // It's quite hard to handle this case right, so let's just ignore the search key
1090             // in this case.  Users can just hit it again and it will work this time.
1091             return;
1092         }
1093         if (mSearchView == null) {
1094             prepareSearchView();
1095         }
1096 
1097         final ActionBar actionBar = getActionBar();
1098 
1099         final Tab tab = actionBar.getSelectedTab();
1100 
1101         // User can search during the call, but we don't want to remember the status.
1102         if (tab != null && !DialpadFragment.phoneIsInUse()) {
1103             mLastManuallySelectedFragment = tab.getPosition();
1104         }
1105 
1106         mSearchView.setQuery(null, true);
1107 
1108         actionBar.setDisplayShowCustomEnabled(true);
1109         actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
1110         actionBar.setDisplayShowHomeEnabled(true);
1111         actionBar.setDisplayHomeAsUpEnabled(true);
1112 
1113         updateFakeMenuButtonsVisibility(false);
1114 
1115         for (int i = 0; i < TAB_INDEX_COUNT; i++) {
1116             sendFragmentVisibilityChange(i, false /* not visible */ );
1117         }
1118 
1119         // Show the search fragment and hide everything else.
1120         mSearchFragment.setUserVisibleHint(true);
1121         final FragmentTransaction transaction = getFragmentManager().beginTransaction();
1122         transaction.show(mSearchFragment);
1123         transaction.commitAllowingStateLoss();
1124         mViewPager.setVisibility(View.GONE);
1125 
1126         // We need to call this and onActionViewCollapsed() manually, since we are using a custom
1127         // layout instead of asking the search menu item to take care of SearchView.
1128         mSearchView.onActionViewExpanded();
1129         mInSearchUi = true;
1130     }
1131 
showInputMethod(View view)1132     private void showInputMethod(View view) {
1133         InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
1134         if (imm != null) {
1135             if (!imm.showSoftInput(view, 0)) {
1136                 Log.w(TAG, "Failed to show soft input method.");
1137             }
1138         }
1139     }
1140 
hideInputMethod(View view)1141     private void hideInputMethod(View view) {
1142         InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
1143         if (imm != null && view != null) {
1144             imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
1145         }
1146     }
1147 
1148     /**
1149      * Goes back to usual Phone UI with tags. Previously selected Tag and associated Fragment
1150      * should be automatically focused again.
1151      */
exitSearchUi()1152     private void exitSearchUi() {
1153         final ActionBar actionBar = getActionBar();
1154 
1155         // Hide the search fragment, if exists.
1156         if (mSearchFragment != null) {
1157             mSearchFragment.setUserVisibleHint(false);
1158 
1159             final FragmentTransaction transaction = getFragmentManager().beginTransaction();
1160             transaction.hide(mSearchFragment);
1161             transaction.commitAllowingStateLoss();
1162         }
1163 
1164         // We want to hide SearchView and show Tabs. Also focus on previously selected one.
1165         actionBar.setDisplayShowCustomEnabled(false);
1166         actionBar.setDisplayShowHomeEnabled(false);
1167         actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
1168 
1169         for (int i = 0; i < TAB_INDEX_COUNT; i++) {
1170             sendFragmentVisibilityChange(i, i == mViewPager.getCurrentItem());
1171         }
1172 
1173         // Before exiting the search screen, reset swipe state.
1174         mDuringSwipe = false;
1175         mUserTabClick = false;
1176 
1177         mViewPager.setVisibility(View.VISIBLE);
1178 
1179         hideInputMethod(getCurrentFocus());
1180 
1181         // Request to update option menu.
1182         invalidateOptionsMenu();
1183 
1184         // See comments in onActionViewExpanded()
1185         mSearchView.onActionViewCollapsed();
1186         mInSearchUi = false;
1187     }
1188 
getFragmentAt(int position)1189     private Fragment getFragmentAt(int position) {
1190         switch (position) {
1191             case TAB_INDEX_DIALER:
1192                 return mDialpadFragment;
1193             case TAB_INDEX_CALL_LOG:
1194                 return mCallLogFragment;
1195             case TAB_INDEX_FAVORITES:
1196                 return mPhoneFavoriteFragment;
1197             default:
1198                 throw new IllegalStateException("Unknown fragment index: " + position);
1199         }
1200     }
1201 
sendFragmentVisibilityChange(int position, boolean visibility)1202     private void sendFragmentVisibilityChange(int position, boolean visibility) {
1203         if (DEBUG) {
1204             Log.d(TAG, "sendFragmentVisibiltyChange(). position: " + position
1205                     + ", visibility: " + visibility);
1206         }
1207         // Position can be -1 initially. See PageChangeListener.
1208         if (position >= 0) {
1209             final Fragment fragment = getFragmentAt(position);
1210             if (fragment != null) {
1211                 fragment.setMenuVisibility(visibility);
1212                 fragment.setUserVisibleHint(visibility);
1213             }
1214         }
1215     }
1216 
1217     /**
1218      * Update visibility of the search button and menu button at the bottom.
1219      * They should be invisible when bottom ActionBar's real items are available, and be visible
1220      * otherwise.
1221      *
1222      * @param visible True when visible.
1223      */
updateFakeMenuButtonsVisibility(boolean visible)1224     private void updateFakeMenuButtonsVisibility(boolean visible) {
1225         if (DEBUG) {
1226             Log.d(TAG, "updateFakeMenuButtonVisibility(" + visible + ")");
1227         }
1228 
1229         if (mSearchButton != null) {
1230             if (visible) {
1231                 mSearchButton.setVisibility(View.VISIBLE);
1232             } else {
1233                 mSearchButton.setVisibility(View.INVISIBLE);
1234             }
1235         }
1236         if (mMenuButton != null) {
1237             if (visible && !ViewConfiguration.get(this).hasPermanentMenuKey()) {
1238                 mMenuButton.setVisibility(View.VISIBLE);
1239             } else {
1240                 mMenuButton.setVisibility(View.INVISIBLE);
1241             }
1242         }
1243     }
1244 
1245     /** Returns an Intent to launch Call Settings screen */
getCallSettingsIntent()1246     public static Intent getCallSettingsIntent() {
1247         final Intent intent = new Intent(Intent.ACTION_MAIN);
1248         intent.setClassName(PHONE_PACKAGE, CALL_SETTINGS_CLASS_NAME);
1249         intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
1250         return intent;
1251     }
1252 
1253     @Override
onActivityResult(int requestCode, int resultCode, Intent data)1254     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
1255         if (resultCode != Activity.RESULT_OK) {
1256             return;
1257         }
1258         switch (requestCode) {
1259             case SUBACTIVITY_ACCOUNT_FILTER: {
1260                 AccountFilterUtil.handleAccountFilterResult(
1261                         mContactListFilterController, resultCode, data);
1262             }
1263             break;
1264         }
1265     }
1266 }
1267