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