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