• 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.R;
20 import com.android.contacts.calllog.CallLogFragment;
21 import com.android.contacts.dialpad.DialpadFragment;
22 import com.android.contacts.interactions.PhoneNumberInteraction;
23 import com.android.contacts.list.AccountFilterActivity;
24 import com.android.contacts.list.ContactListFilter;
25 import com.android.contacts.list.ContactListFilterController;
26 import com.android.contacts.list.ContactListFilterController.ContactListFilterListener;
27 import com.android.contacts.list.ContactListItemView;
28 import com.android.contacts.list.OnPhoneNumberPickerActionListener;
29 import com.android.contacts.list.PhoneFavoriteFragment;
30 import com.android.contacts.list.PhoneNumberPickerFragment;
31 import com.android.internal.telephony.ITelephony;
32 
33 import android.app.ActionBar;
34 import android.app.ActionBar.LayoutParams;
35 import android.app.ActionBar.Tab;
36 import android.app.ActionBar.TabListener;
37 import android.app.Activity;
38 import android.app.Fragment;
39 import android.app.FragmentManager;
40 import android.app.FragmentTransaction;
41 import android.content.Context;
42 import android.content.Intent;
43 import android.content.SharedPreferences;
44 import android.net.Uri;
45 import android.os.Bundle;
46 import android.os.RemoteException;
47 import android.os.ServiceManager;
48 import android.preference.PreferenceManager;
49 import android.provider.CallLog.Calls;
50 import android.provider.ContactsContract.Contacts;
51 import android.provider.ContactsContract.Intents.UI;
52 import android.support.v13.app.FragmentPagerAdapter;
53 import android.support.v4.view.ViewPager;
54 import android.support.v4.view.ViewPager.OnPageChangeListener;
55 import android.text.TextUtils;
56 import android.util.Log;
57 import android.view.Menu;
58 import android.view.MenuInflater;
59 import android.view.MenuItem;
60 import android.view.MenuItem.OnMenuItemClickListener;
61 import android.view.View;
62 import android.view.View.OnClickListener;
63 import android.view.View.OnFocusChangeListener;
64 import android.view.ViewConfiguration;
65 import android.view.inputmethod.InputMethodManager;
66 import android.widget.PopupMenu;
67 import android.widget.SearchView;
68 import android.widget.SearchView.OnCloseListener;
69 import 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  */
78 public 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 {
onVisibilityChanged(boolean visible)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 
ViewPagerAdapter(FragmentManager fm)125         public ViewPagerAdapter(FragmentManager fm) {
126             super(fm);
127         }
128 
129         @Override
getItem(int position)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
getCount()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
onPageScrolled( int position, float positionOffset, int positionOffsetPixels)166         public void onPageScrolled(
167                 int position, float positionOffset, int positionOffsetPixels) {
168         }
169 
170         @Override
onPageSelected(int position)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 
setCurrentPosition(int position)181         public void setCurrentPosition(int position) {
182             mCurrentPosition = position;
183         }
184 
185         @Override
onPageScrollStateChanged(int state)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
onCreate(Bundle icicle)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
onStart()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 
prepareSearchView()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
onAttachFragment(Fragment fragment)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
onPause()522     protected void onPause() {
523         super.onPause();
524 
525         mPrefs.edit().putInt(PREF_LAST_MANUALLY_SELECTED_TAB, mLastManuallySelectedFragment)
526                 .apply();
527     }
528 
fixIntent(Intent intent)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 
setupDialer()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 
setupCallLog()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 
setupFavorites()556     private void setupFavorites() {
557         final Tab tab = getActionBar().newTab();
558         tab.setContentDescription(R.string.contactsFavoritesLabel);
559         tab.setIcon(R.drawable.ic_tab_all);
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      */
isSendKeyWhileInCall(final Intent intent, final boolean recentCallsRequest)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      */
setCurrentTab(Intent intent)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
onNewIntent(Intent newIntent)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 */
isDialIntent(Intent intent)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      */
getAndClearFilterText()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      */
setupFilterText(Intent intent)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
onBackPressed()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
onCreateOptionsMenu(Menu menu)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
onPrepareOptionsMenu(Menu menu)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
startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData, boolean globalSearch)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      */
enterSearchUi()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 
showInputMethod(View view)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 
hideInputMethod(View view)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      */
exitSearchUi()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 
getFragmentAt(int position)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 
sendFragmentVisibilityChange(int position, boolean visibility)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 */
getCallSettingsIntent()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
onActivityResult(int requestCode, int resultCode, Intent data)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