summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/com/android/dialer/DialerBackupAgent.java2
-rw-r--r--src/com/android/dialer/DialtactsActivity.java20
-rw-r--r--src/com/android/dialer/OldDialtactsActivity.java1292
-rw-r--r--src/com/android/dialer/dialpad/DialpadFragment.java356
-rw-r--r--src/com/android/dialer/dialpad/DialpadImageButton.java137
-rw-r--r--src/com/android/dialer/dialpad/NewDialpadFragment.java1689
-rw-r--r--src/com/android/dialer/dialpad/SmartDialController.java389
-rw-r--r--src/com/android/dialer/dialpad/SmartDialLoaderTask.java5
-rw-r--r--src/com/android/dialer/dialpad/SmartDialMatchPosition.java10
-rw-r--r--src/com/android/dialer/list/PhoneFavoriteFragment.java573
10 files changed, 178 insertions, 4295 deletions
diff --git a/src/com/android/dialer/DialerBackupAgent.java b/src/com/android/dialer/DialerBackupAgent.java
index cbcdf5b9b..928be029c 100644
--- a/src/com/android/dialer/DialerBackupAgent.java
+++ b/src/com/android/dialer/DialerBackupAgent.java
@@ -33,6 +33,6 @@ public class DialerBackupAgent extends BackupAgentHelper
@Override
public void onCreate() {
addHelper(SHARED_KEY, new SharedPreferencesBackupHelper(this,
- OldDialtactsActivity.SHARED_PREFS_NAME));
+ DialtactsActivity.SHARED_PREFS_NAME));
}
}
diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java
index 5e2eee3fd..a0153ea8b 100644
--- a/src/com/android/dialer/DialtactsActivity.java
+++ b/src/com/android/dialer/DialtactsActivity.java
@@ -68,7 +68,7 @@ import com.android.contacts.common.list.ContactListItemView;
import com.android.contacts.common.list.OnPhoneNumberPickerActionListener;
import com.android.contacts.common.list.PhoneNumberPickerFragment;
import com.android.dialer.calllog.CallLogActivity;
-import com.android.dialer.dialpad.NewDialpadFragment;
+import com.android.dialer.dialpad.DialpadFragment;
import com.android.dialer.dialpad.SmartDialNameMatcher;
import com.android.dialer.interactions.PhoneNumberInteraction;
import com.android.dialer.list.NewPhoneFavoriteFragment;
@@ -86,14 +86,16 @@ import java.util.ArrayList;
* be renamed more appropriately before shipping.
*/
public class DialtactsActivity extends TransactionSafeActivity implements View.OnClickListener,
- NewDialpadFragment.OnDialpadQueryChangedListener, PopupMenu.OnMenuItemClickListener,
+ DialpadFragment.OnDialpadQueryChangedListener, PopupMenu.OnMenuItemClickListener,
OnListFragmentScrolledListener,
NewPhoneFavoriteFragment.OnPhoneFavoriteFragmentStartedListener,
- NewDialpadFragment.OnDialpadFragmentStartedListener {
+ DialpadFragment.OnDialpadFragmentStartedListener {
private static final String TAG = "DialtactsActivity";
public static final boolean DEBUG = false;
+ public static final String SHARED_PREFS_NAME = "com.android.dialer_preferences";
+
/** Used to open Call Setting */
private static final String PHONE_PACKAGE = "com.android.phone";
private static final String CALL_SETTINGS_CLASS_NAME =
@@ -127,7 +129,7 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O
/**
* Fragment containing the dialpad that slides into view
*/
- private NewDialpadFragment mDialpadFragment;
+ private DialpadFragment mDialpadFragment;
/**
* Fragment for searching phone numbers using the alphanumeric keyboard.
@@ -245,7 +247,7 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O
mRegularSearchFragment = new NewSearchFragment();
mSmartDialSearchFragment = new SmartDialSearchFragment();
- mDialpadFragment = new NewDialpadFragment();
+ mDialpadFragment = new DialpadFragment();
mShowAllContactsFragment = new ShowAllContactsFragment();
mShowAllContactsFragment.setOnPhoneNumberPickerActionListener(
mPhoneNumberPickerActionListener);
@@ -277,7 +279,7 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O
final FragmentManager fm = getFragmentManager();
mPhoneFavoriteFragment = (NewPhoneFavoriteFragment) fm.findFragmentByTag(
TAG_FAVORITES_FRAGMENT);
- mDialpadFragment = (NewDialpadFragment) fm.findFragmentByTag(TAG_DIALPAD_FRAGMENT);
+ mDialpadFragment = (DialpadFragment) fm.findFragmentByTag(TAG_DIALPAD_FRAGMENT);
mRegularSearchFragment = (NewSearchFragment) fm.findFragmentByTag(
TAG_REGULAR_SEARCH_FRAGMENT);
@@ -297,7 +299,7 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O
@Override
public void onAttachFragment(Fragment fragment) {
- if (fragment instanceof NewDialpadFragment || fragment instanceof NewSearchFragment
+ if (fragment instanceof DialpadFragment || fragment instanceof NewSearchFragment
|| fragment instanceof SmartDialSearchFragment
|| fragment instanceof ShowAllContactsFragment) {
final FragmentTransaction transaction = getFragmentManager().beginTransaction();
@@ -327,7 +329,7 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O
// now in Dialtacts for (potential) performance reasons. Compare with how it is
// done in {@link PeopleActivity}.
ImportExportDialogFragment.show(getFragmentManager(), true,
- OldDialtactsActivity.class);
+ DialtactsActivity.class);
return true;
case R.id.menu_clear_frequents:
ClearFrequentsDialog.show(getFragmentManager());
@@ -343,7 +345,7 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O
}
return true;
case R.id.menu_call_settings:
- final Intent settingsIntent = OldDialtactsActivity.getCallSettingsIntent();
+ final Intent settingsIntent = DialtactsActivity.getCallSettingsIntent();
startActivity(settingsIntent);
}
return false;
diff --git a/src/com/android/dialer/OldDialtactsActivity.java b/src/com/android/dialer/OldDialtactsActivity.java
deleted file mode 100644
index 46b22e15c..000000000
--- a/src/com/android/dialer/OldDialtactsActivity.java
+++ /dev/null
@@ -1,1292 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.dialer;
-
-import android.app.ActionBar;
-import android.app.ActionBar.LayoutParams;
-import android.app.ActionBar.Tab;
-import android.app.ActionBar.TabListener;
-import android.app.Activity;
-import android.app.backup.BackupManager;
-import android.app.Fragment;
-import android.app.FragmentManager;
-import android.app.FragmentTransaction;
-import android.content.ActivityNotFoundException;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.preference.PreferenceManager;
-import android.provider.CallLog.Calls;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Intents.UI;
-import android.support.v13.app.FragmentPagerAdapter;
-import android.support.v4.view.ViewPager;
-import android.support.v4.view.ViewPager.OnPageChangeListener;
-import android.text.TextUtils;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.MenuItem.OnMenuItemClickListener;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.View.OnFocusChangeListener;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.PopupMenu;
-import android.widget.SearchView;
-import android.widget.SearchView.OnCloseListener;
-import android.widget.SearchView.OnQueryTextListener;
-import android.widget.Toast;
-
-import com.android.contacts.common.CallUtil;
-import com.android.contacts.common.activity.TransactionSafeActivity;
-import com.android.contacts.common.list.ContactListFilterController;
-import com.android.contacts.common.list.ContactListFilterController.ContactListFilterListener;
-import com.android.contacts.common.list.ContactListItemView;
-import com.android.contacts.common.list.OnPhoneNumberPickerActionListener;
-import com.android.contacts.common.list.PhoneNumberPickerFragment;
-import com.android.contacts.common.util.AccountFilterUtil;
-import com.android.dialer.calllog.CallLogFragment;
-import com.android.dialer.dialpad.DialpadFragment;
-import com.android.dialer.interactions.PhoneNumberInteraction;
-import com.android.dialer.list.PhoneFavoriteFragment;
-import com.android.dialer.util.OrientationUtil;
-import com.android.internal.telephony.ITelephony;
-
-/**
- * The dialer activity that has one tab with the virtual 12key
- * dialer, a tab with recent calls in it, a tab with the contacts and
- * a tab with the favorite. This is the container and the tabs are
- * embedded using intents.
- * The dialer tab's title is 'phone', a more common name (see strings.xml).
- */
-public class OldDialtactsActivity extends TransactionSafeActivity implements View.OnClickListener {
- private static final String TAG = "DialtactsActivity";
-
- public static final boolean DEBUG = false;
-
- /** Used to open Call Setting */
- private static final String PHONE_PACKAGE = "com.android.phone";
- private static final String CALL_SETTINGS_CLASS_NAME =
- "com.android.phone.CallFeaturesSetting";
-
- /** @see #getCallOrigin() */
- private static final String CALL_ORIGIN_DIALTACTS =
- "com.android.dialer.DialtactsActivity";
-
- /**
- * Just for backward compatibility. Should behave as same as {@link Intent#ACTION_DIAL}.
- */
- private static final String ACTION_TOUCH_DIALER = "com.android.phone.action.TOUCH_DIALER";
-
- /** Used both by {@link ActionBar} and {@link ViewPagerAdapter} */
- private static final int TAB_INDEX_DIALER = 0;
- private static final int TAB_INDEX_CALL_LOG = 1;
- private static final int TAB_INDEX_FAVORITES = 2;
-
- private static final int TAB_INDEX_COUNT = 3;
-
- private SharedPreferences mPrefs;
-
- public static final String SHARED_PREFS_NAME = "com.android.dialer_preferences";
-
- /** Last manually selected tab index */
- private static final String PREF_LAST_MANUALLY_SELECTED_TAB =
- "DialtactsActivity_last_manually_selected_tab";
- private static final int PREF_LAST_MANUALLY_SELECTED_TAB_DEFAULT = TAB_INDEX_DIALER;
-
- private static final int SUBACTIVITY_ACCOUNT_FILTER = 1;
-
- public class ViewPagerAdapter extends FragmentPagerAdapter {
- public ViewPagerAdapter(FragmentManager fm) {
- super(fm);
- }
-
- @Override
- public Fragment getItem(int position) {
- switch (position) {
- case TAB_INDEX_DIALER:
- return new DialpadFragment();
- case TAB_INDEX_CALL_LOG:
- return new CallLogFragment();
- case TAB_INDEX_FAVORITES:
- return new PhoneFavoriteFragment();
- }
- throw new IllegalStateException("No fragment at position " + position);
- }
-
- @Override
- public void setPrimaryItem(ViewGroup container, int position, Object object) {
- // The parent's setPrimaryItem() also calls setMenuVisibility(), so we want to know
- // when it happens.
- if (DEBUG) {
- Log.d(TAG, "FragmentPagerAdapter#setPrimaryItem(), position: " + position);
- }
- super.setPrimaryItem(container, position, object);
- }
-
- @Override
- public int getCount() {
- return TAB_INDEX_COUNT;
- }
- }
-
- /**
- * True when the app detects user's drag event. This variable should not become true when
- * mUserTabClick is true.
- *
- * During user's drag or tab click, we shouldn't show fake buttons but just show real
- * ActionBar at the bottom of the screen, for transition animation.
- */
- boolean mDuringSwipe = false;
- /**
- * True when the app detects user's tab click (at the top of the screen). This variable should
- * not become true when mDuringSwipe is true.
- *
- * During user's drag or tab click, we shouldn't show fake buttons but just show real
- * ActionBar at the bottom of the screen, for transition animation.
- */
- boolean mUserTabClick = false;
-
- private class PageChangeListener implements OnPageChangeListener {
- private int mCurrentPosition = -1;
- /**
- * Used during page migration, to remember the next position {@link #onPageSelected(int)}
- * specified.
- */
- private int mNextPosition = -1;
-
- @Override
- public void onPageScrolled(
- int position, float positionOffset, int positionOffsetPixels) {
- }
-
- @Override
- public void onPageSelected(int position) {
- if (DEBUG) Log.d(TAG, "onPageSelected: position: " + position);
- final ActionBar actionBar = getActionBar();
- if (mDialpadFragment != null) {
- if (mDuringSwipe && position == TAB_INDEX_DIALER) {
- // TODO: Figure out if we want this or not. Right now
- // - with this call, both fake buttons and real action bar overlap
- // - without this call, there's tiny flicker happening to search/menu buttons.
- // If we can reduce the flicker without this call, it would be much better.
- // updateFakeMenuButtonsVisibility(true);
- }
- }
-
- if (mCurrentPosition == position) {
- Log.w(TAG, "Previous position and next position became same (" + position + ")");
- }
-
- actionBar.selectTab(actionBar.getTabAt(position));
- mNextPosition = position;
- }
-
- public void setCurrentPosition(int position) {
- mCurrentPosition = position;
- }
-
- public int getCurrentPosition() {
- return mCurrentPosition;
- }
-
- @Override
- public void onPageScrollStateChanged(int state) {
- switch (state) {
- case ViewPager.SCROLL_STATE_IDLE: {
- if (mNextPosition == -1) {
- // This happens when the user drags the screen just after launching the
- // application, and settle down the same screen without actually swiping it.
- // At that moment mNextPosition is apparently -1 yet, and we expect it
- // being updated by onPageSelected(), which is *not* called if the user
- // settle down the exact same tab after the dragging.
- if (DEBUG) {
- Log.d(TAG, "Next position is not specified correctly. Use current tab ("
- + mViewPager.getCurrentItem() + ")");
- }
- mNextPosition = mViewPager.getCurrentItem();
- }
- if (DEBUG) {
- Log.d(TAG, "onPageScrollStateChanged() with SCROLL_STATE_IDLE. "
- + "mCurrentPosition: " + mCurrentPosition
- + ", mNextPosition: " + mNextPosition);
- }
- // Interpret IDLE as the end of migration (both swipe and tab click)
- mDuringSwipe = false;
- mUserTabClick = false;
-
- updateFakeMenuButtonsVisibility(mNextPosition == TAB_INDEX_DIALER);
- sendFragmentVisibilityChange(mCurrentPosition, false);
- sendFragmentVisibilityChange(mNextPosition, true);
-
- invalidateOptionsMenu();
-
- mCurrentPosition = mNextPosition;
- break;
- }
- case ViewPager.SCROLL_STATE_DRAGGING: {
- if (DEBUG) Log.d(TAG, "onPageScrollStateChanged() with SCROLL_STATE_DRAGGING");
- mDuringSwipe = true;
- mUserTabClick = false;
- break;
- }
- case ViewPager.SCROLL_STATE_SETTLING: {
- if (DEBUG) Log.d(TAG, "onPageScrollStateChanged() with SCROLL_STATE_SETTLING");
- mDuringSwipe = true;
- mUserTabClick = false;
- break;
- }
- default:
- break;
- }
- }
- }
-
- private String mFilterText;
-
- /** Enables horizontal swipe between Fragments. */
- private ViewPager mViewPager;
- private final PageChangeListener mPageChangeListener = new PageChangeListener();
- private DialpadFragment mDialpadFragment;
- private CallLogFragment mCallLogFragment;
- private PhoneFavoriteFragment mPhoneFavoriteFragment;
-
- private View mSearchButton;
- private View mMenuButton;
-
- private final ContactListFilterListener mContactListFilterListener =
- new ContactListFilterListener() {
- @Override
- public void onContactListFilterChanged() {
- boolean doInvalidateOptionsMenu = false;
-
- if (mPhoneFavoriteFragment != null && mPhoneFavoriteFragment.isAdded()) {
- mPhoneFavoriteFragment.setFilter(mContactListFilterController.getFilter());
- doInvalidateOptionsMenu = true;
- }
-
- if (mSearchFragment != null && mSearchFragment.isAdded()) {
- mSearchFragment.setFilter(mContactListFilterController.getFilter());
- doInvalidateOptionsMenu = true;
- } else {
- Log.w(TAG, "Search Fragment isn't available when ContactListFilter is changed");
- }
-
- if (doInvalidateOptionsMenu) {
- invalidateOptionsMenu();
- }
- }
- };
-
- private final TabListener mTabListener = new TabListener() {
- @Override
- public void onTabUnselected(Tab tab, FragmentTransaction ft) {
- if (DEBUG) Log.d(TAG, "onTabUnselected(). tab: " + tab);
- }
-
- @Override
- public void onTabSelected(Tab tab, FragmentTransaction ft) {
- if (DEBUG) {
- Log.d(TAG, "onTabSelected(). tab: " + tab + ", mDuringSwipe: " + mDuringSwipe);
- }
- // When the user swipes the screen horizontally, this method will be called after
- // ViewPager.SCROLL_STATE_DRAGGING and ViewPager.SCROLL_STATE_SETTLING events, while
- // when the user clicks a tab at the ActionBar at the top, this will be called before
- // them. This logic interprets the order difference as a difference of the user action.
- if (!mDuringSwipe) {
- if (DEBUG) {
- Log.d(TAG, "Tab select. from: " + mPageChangeListener.getCurrentPosition()
- + ", to: " + tab.getPosition());
- }
- if (mDialpadFragment != null) {
- updateFakeMenuButtonsVisibility(tab.getPosition() == TAB_INDEX_DIALER);
- }
- mUserTabClick = true;
- }
-
- if (mViewPager.getCurrentItem() != tab.getPosition()) {
- mViewPager.setCurrentItem(tab.getPosition(), true);
- }
-
- // During the call, we don't remember the tab position.
- if (mDialpadFragment == null || !mDialpadFragment.phoneIsInUse()) {
- // Remember this tab index. This function is also called, if the tab is set
- // automatically in which case the setter (setCurrentTab) has to set this to its old
- // value afterwards
- mLastManuallySelectedFragment = tab.getPosition();
- }
- }
-
- @Override
- public void onTabReselected(Tab tab, FragmentTransaction ft) {
- if (DEBUG) Log.d(TAG, "onTabReselected");
- }
- };
-
- /**
- * Fragment for searching phone numbers. Unlike the other Fragments, this doesn't correspond
- * to tab but is shown by a search action.
- */
- private PhoneNumberPickerFragment mSearchFragment;
- /**
- * True when this Activity is in its search UI (with a {@link SearchView} and
- * {@link PhoneNumberPickerFragment}).
- */
- private boolean mInSearchUi;
- private SearchView mSearchView;
-
- private final OnClickListener mFilterOptionClickListener = new OnClickListener() {
- @Override
- public void onClick(View view) {
- final PopupMenu popupMenu = new PopupMenu(OldDialtactsActivity.this, view);
- final Menu menu = popupMenu.getMenu();
- popupMenu.inflate(R.menu.dialtacts_search_options);
- final MenuItem filterOptionMenuItem = menu.findItem(R.id.filter_option);
- filterOptionMenuItem.setOnMenuItemClickListener(mFilterOptionsMenuItemClickListener);
- final MenuItem addContactOptionMenuItem = menu.findItem(R.id.add_contact);
- addContactOptionMenuItem.setIntent(
- new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI));
- popupMenu.show();
- }
- };
-
- /**
- * The index of the Fragment (or, the tab) that has last been manually selected.
- * This value does not keep track of programmatically set Tabs (e.g. Call Log after a Call)
- */
- private int mLastManuallySelectedFragment;
-
- private ContactListFilterController mContactListFilterController;
- private OnMenuItemClickListener mFilterOptionsMenuItemClickListener =
- new OnMenuItemClickListener() {
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- AccountFilterUtil.startAccountFilterActivityForResult(
- OldDialtactsActivity.this, SUBACTIVITY_ACCOUNT_FILTER,
- mContactListFilterController.getFilter());
- return true;
- }
- };
-
- private OnMenuItemClickListener mSearchMenuItemClickListener =
- new OnMenuItemClickListener() {
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- enterSearchUi();
- return true;
- }
- };
-
- /**
- * Listener used when one of phone numbers in search UI is selected. This will initiate a
- * phone call using the phone number.
- */
- private final OnPhoneNumberPickerActionListener mPhoneNumberPickerActionListener =
- new OnPhoneNumberPickerActionListener() {
- @Override
- public void onPickPhoneNumberAction(Uri dataUri) {
- // Specify call-origin so that users will see the previous tab instead of
- // CallLog screen (search UI will be automatically exited).
- PhoneNumberInteraction.startInteractionForPhoneCall(
- OldDialtactsActivity.this, dataUri, getCallOrigin());
- }
-
- @Override
- public void onShortcutIntentCreated(Intent intent) {
- Log.w(TAG, "Unsupported intent has come (" + intent + "). Ignoring.");
- }
-
- @Override
- public void onHomeInActionBarSelected() {
- exitSearchUi();
- }
- };
-
- /**
- * Listener used to send search queries to the phone search fragment.
- */
- private final OnQueryTextListener mPhoneSearchQueryTextListener =
- new OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String query) {
- View view = getCurrentFocus();
- if (view != null) {
- hideInputMethod(view);
- view.clearFocus();
- }
- return true;
- }
-
- @Override
- public boolean onQueryTextChange(String newText) {
- // Show search result with non-empty text. Show a bare list otherwise.
- if (mSearchFragment != null) {
- mSearchFragment.setQueryString(newText, true);
- }
- return true;
- }
- };
-
- /**
- * Listener used to handle the "close" button on the right side of {@link SearchView}.
- * If some text is in the search view, this will clean it up. Otherwise this will exit
- * the search UI and let users go back to usual Phone UI.
- *
- * This does _not_ handle back button.
- */
- private final OnCloseListener mPhoneSearchCloseListener =
- new OnCloseListener() {
- @Override
- public boolean onClose() {
- if (!TextUtils.isEmpty(mSearchView.getQuery())) {
- mSearchView.setQuery(null, true);
- }
- return true;
- }
- };
-
- private final View.OnLayoutChangeListener mFirstLayoutListener
- = new View.OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
- int oldTop, int oldRight, int oldBottom) {
- v.removeOnLayoutChangeListener(this); // Unregister self.
- addSearchFragment();
- }
- };
-
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- final Intent intent = getIntent();
- fixIntent(intent);
-
- setContentView(R.layout.dialtacts_activity);
-
- mContactListFilterController = ContactListFilterController.getInstance(this);
- mContactListFilterController.addListener(mContactListFilterListener);
-
- findViewById(R.id.dialtacts_frame).addOnLayoutChangeListener(mFirstLayoutListener);
-
- mViewPager = (ViewPager) findViewById(R.id.pager);
- mViewPager.setAdapter(new ViewPagerAdapter(getFragmentManager()));
- mViewPager.setOnPageChangeListener(mPageChangeListener);
- mViewPager.setOffscreenPageLimit(2);
-
- // Do same width calculation as ActionBar does
- DisplayMetrics dm = getResources().getDisplayMetrics();
- int minCellSize = getResources().getDimensionPixelSize(R.dimen.fake_menu_button_min_width);
- int cellCount = dm.widthPixels / minCellSize;
- int fakeMenuItemWidth = dm.widthPixels / cellCount;
- if (DEBUG) Log.d(TAG, "The size of fake menu buttons (in pixel): " + fakeMenuItemWidth);
-
- // Soft menu button should appear only when there's no hardware menu button.
- mMenuButton = findViewById(R.id.overflow_menu);
- if (mMenuButton != null) {
- mMenuButton.setMinimumWidth(fakeMenuItemWidth);
- if (ViewConfiguration.get(this).hasPermanentMenuKey()) {
- // This is required for dialpad button's layout, so must not use GONE here.
- mMenuButton.setVisibility(View.INVISIBLE);
- } else {
- mMenuButton.setOnClickListener(this);
- }
- }
- mSearchButton = findViewById(R.id.searchButton);
- if (mSearchButton != null) {
- mSearchButton.setMinimumWidth(fakeMenuItemWidth);
- mSearchButton.setOnClickListener(this);
- }
-
- // Setup the ActionBar tabs (the order matches the tab-index contants TAB_INDEX_*)
- setupDialer();
- setupCallLog();
- setupFavorites();
- getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
- getActionBar().setDisplayShowTitleEnabled(false);
- getActionBar().setDisplayShowHomeEnabled(false);
-
- // Load the last manually loaded tab
- mPrefs = this.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE);
- mLastManuallySelectedFragment = mPrefs.getInt(PREF_LAST_MANUALLY_SELECTED_TAB,
- PREF_LAST_MANUALLY_SELECTED_TAB_DEFAULT);
- if (mLastManuallySelectedFragment >= TAB_INDEX_COUNT) {
- // Stored value may have exceeded the number of current tabs. Reset it.
- mLastManuallySelectedFragment = PREF_LAST_MANUALLY_SELECTED_TAB_DEFAULT;
- }
-
- setCurrentTab(intent);
-
- if (UI.FILTER_CONTACTS_ACTION.equals(intent.getAction())
- && icicle == null) {
- setupFilterText(intent);
- }
- }
-
- @Override
- public void onStart() {
- super.onStart();
- if (mPhoneFavoriteFragment != null) {
- mPhoneFavoriteFragment.setFilter(mContactListFilterController.getFilter());
- }
- if (mSearchFragment != null) {
- mSearchFragment.setFilter(mContactListFilterController.getFilter());
- }
-
- if (mDuringSwipe || mUserTabClick) {
- if (DEBUG) Log.d(TAG, "reset buggy flag state..");
- mDuringSwipe = false;
- mUserTabClick = false;
- }
-
- final int currentPosition = mPageChangeListener.getCurrentPosition();
- if (DEBUG) {
- Log.d(TAG, "onStart(). current position: " + mPageChangeListener.getCurrentPosition()
- + ". Reset all menu visibility state.");
- }
- updateFakeMenuButtonsVisibility(currentPosition == TAB_INDEX_DIALER && !mInSearchUi);
- for (int i = 0; i < TAB_INDEX_COUNT; i++) {
- sendFragmentVisibilityChange(i, i == currentPosition);
- }
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- mContactListFilterController.removeListener(mContactListFilterListener);
- }
-
- @Override
- public void onClick(View view) {
- switch (view.getId()) {
- case R.id.searchButton: {
- enterSearchUi();
- break;
- }
- case R.id.overflow_menu: {
- if (mDialpadFragment != null) {
- PopupMenu popup = mDialpadFragment.constructPopupMenu(view);
- if (popup != null) {
- popup.show();
- }
- } else {
- Log.w(TAG, "DialpadFragment is null during onClick() event for " + view);
- }
- break;
- }
- default: {
- Log.wtf(TAG, "Unexpected onClick event from " + view);
- break;
- }
- }
- }
-
- /**
- * Add search fragment. Note this is called during onLayout, so there's some restrictions,
- * such as executePendingTransaction can't be used in it.
- */
- private void addSearchFragment() {
- // In order to take full advantage of "fragment deferred start", we need to create the
- // search fragment after all other fragments are created.
- // The other fragments are created by the ViewPager on the first onMeasure().
- // We use the first onLayout call, which is after onMeasure().
-
- // Just return if the fragment is already created, which happens after configuration
- // changes.
- if (mSearchFragment != null) return;
-
- final FragmentTransaction ft = getFragmentManager().beginTransaction();
- final Fragment searchFragment = new PhoneNumberPickerFragment();
-
- searchFragment.setUserVisibleHint(false);
- ft.add(R.id.dialtacts_frame, searchFragment);
- ft.hide(searchFragment);
- ft.commitAllowingStateLoss();
- }
-
- private void prepareSearchView() {
- final View searchViewLayout =
- getLayoutInflater().inflate(R.layout.dialtacts_custom_action_bar, null);
- mSearchView = (SearchView) searchViewLayout.findViewById(R.id.search_view);
- mSearchView.setOnQueryTextListener(mPhoneSearchQueryTextListener);
- mSearchView.setOnCloseListener(mPhoneSearchCloseListener);
- // Since we're using a custom layout for showing SearchView instead of letting the
- // search menu icon do that job, we need to manually configure the View so it looks
- // "shown via search menu".
- // - it should be iconified by default
- // - it should not be iconified at this time
- // See also comments for onActionViewExpanded()/onActionViewCollapsed()
- mSearchView.setIconifiedByDefault(true);
- mSearchView.setQueryHint(getString(R.string.hint_findContacts));
- mSearchView.setIconified(false);
- mSearchView.setOnQueryTextFocusChangeListener(new OnFocusChangeListener() {
- @Override
- public void onFocusChange(View view, boolean hasFocus) {
- if (hasFocus) {
- showInputMethod(view.findFocus());
- }
- }
- });
-
- if (!ViewConfiguration.get(this).hasPermanentMenuKey()) {
- // Filter option menu should be shown on the right side of SearchView.
- final View filterOptionView = searchViewLayout.findViewById(R.id.search_option);
- filterOptionView.setVisibility(View.VISIBLE);
- filterOptionView.setOnClickListener(mFilterOptionClickListener);
- }
-
- getActionBar().setCustomView(searchViewLayout,
- new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
- }
-
- @Override
- public void onAttachFragment(Fragment fragment) {
- // This method can be called before onCreate(), at which point we cannot rely on ViewPager.
- // In that case, we will setup the "current position" soon after the ViewPager is ready.
- final int currentPosition = mViewPager != null ? mViewPager.getCurrentItem() : -1;
-
- if (fragment instanceof DialpadFragment) {
- mDialpadFragment = (DialpadFragment) fragment;
- } else if (fragment instanceof CallLogFragment) {
- mCallLogFragment = (CallLogFragment) fragment;
- } else if (fragment instanceof PhoneFavoriteFragment) {
- mPhoneFavoriteFragment = (PhoneFavoriteFragment) fragment;
- mPhoneFavoriteFragment.setListener(mPhoneFavoriteListener);
- if (mContactListFilterController != null
- && mContactListFilterController.getFilter() != null) {
- mPhoneFavoriteFragment.setFilter(mContactListFilterController.getFilter());
- }
- } else if (fragment instanceof PhoneNumberPickerFragment) {
- mSearchFragment = (PhoneNumberPickerFragment) fragment;
- mSearchFragment.setOnPhoneNumberPickerActionListener(mPhoneNumberPickerActionListener);
- mSearchFragment.setQuickContactEnabled(true);
- mSearchFragment.setDarkTheme(true);
- mSearchFragment.setPhotoPosition(ContactListItemView.getDefaultPhotoPosition(
- true /* opposite */));
- mSearchFragment.setUseCallableUri(true);
- if (mContactListFilterController != null
- && mContactListFilterController.getFilter() != null) {
- mSearchFragment.setFilter(mContactListFilterController.getFilter());
- }
- // Here we assume that we're not on the search mode, so let's hide the fragment.
- //
- // We get here either when the fragment is created (normal case), or after configuration
- // changes. In the former case, we're not in search mode because we can only
- // enter search mode if the fragment is created. (see enterSearchUi())
- // In the latter case we're not in search mode either because we don't retain
- // mInSearchUi -- ideally we should but at this point it's not supported.
- mSearchFragment.setUserVisibleHint(false);
- // After configuration changes fragments will forget their "hidden" state, so make
- // sure to hide it.
- if (!mSearchFragment.isHidden()) {
- final FragmentTransaction transaction = getFragmentManager().beginTransaction();
- transaction.hide(mSearchFragment);
- transaction.commitAllowingStateLoss();
- }
- }
- }
-
- @Override
- protected void onPause() {
- super.onPause();
-
- mPrefs.edit().putInt(PREF_LAST_MANUALLY_SELECTED_TAB, mLastManuallySelectedFragment)
- .apply();
- requestBackup();
- }
-
- private void requestBackup() {
- final BackupManager bm = new BackupManager(this);
- bm.dataChanged();
- }
-
- private void fixIntent(Intent intent) {
- // This should be cleaned up: the call key used to send an Intent
- // that just said to go to the recent calls list. It now sends this
- // abstract action, but this class hasn't been rewritten to deal with it.
- if (Intent.ACTION_CALL_BUTTON.equals(intent.getAction())) {
- intent.setDataAndType(Calls.CONTENT_URI, Calls.CONTENT_TYPE);
- intent.putExtra("call_key", true);
- setIntent(intent);
- }
- }
-
- private void setupDialer() {
- final Tab tab = getActionBar().newTab();
- tab.setContentDescription(R.string.dialerIconLabel);
- tab.setTabListener(mTabListener);
- tab.setIcon(R.drawable.ic_tab_dialer);
- getActionBar().addTab(tab);
- }
-
- private void setupCallLog() {
- final Tab tab = getActionBar().newTab();
- tab.setContentDescription(R.string.recentCallsIconLabel);
- tab.setIcon(R.drawable.ic_tab_recent);
- tab.setTabListener(mTabListener);
- getActionBar().addTab(tab);
- }
-
- private void setupFavorites() {
- final Tab tab = getActionBar().newTab();
- tab.setContentDescription(R.string.contactsFavoritesLabel);
- tab.setIcon(R.drawable.ic_tab_all);
- tab.setTabListener(mTabListener);
- getActionBar().addTab(tab);
- }
-
- /**
- * Returns true if the intent is due to hitting the green send key (hardware call button:
- * KEYCODE_CALL) while in a call.
- *
- * @param intent the intent that launched this activity
- * @param recentCallsRequest true if the intent is requesting to view recent calls
- * @return true if the intent is due to hitting the green send key while in a call
- */
- private boolean isSendKeyWhileInCall(final Intent intent,
- final boolean recentCallsRequest) {
- // If there is a call in progress go to the call screen
- if (recentCallsRequest) {
- final boolean callKey = intent.getBooleanExtra("call_key", false);
-
- try {
- ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
- if (callKey && phone != null && phone.showCallScreen()) {
- return true;
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to handle send while in call", e);
- }
- }
-
- return false;
- }
-
- /**
- * Sets the current tab based on the intent's request type
- *
- * @param intent Intent that contains information about which tab should be selected
- */
- private void setCurrentTab(Intent intent) {
- // If we got here by hitting send and we're in call forward along to the in-call activity
- boolean recentCallsRequest = Calls.CONTENT_TYPE.equals(intent.resolveType(
- getContentResolver()));
- if (isSendKeyWhileInCall(intent, recentCallsRequest)) {
- finish();
- return;
- }
-
- // Remember the old manually selected tab index so that it can be restored if it is
- // overwritten by one of the programmatic tab selections
- final int savedTabIndex = mLastManuallySelectedFragment;
-
- final int tabIndex;
- if ((mDialpadFragment != null && mDialpadFragment.phoneIsInUse())
- || isDialIntent(intent)) {
- tabIndex = TAB_INDEX_DIALER;
- } else if (recentCallsRequest) {
- tabIndex = TAB_INDEX_CALL_LOG;
- } else {
- tabIndex = mLastManuallySelectedFragment;
- }
-
- final int previousItemIndex = mViewPager.getCurrentItem();
- mViewPager.setCurrentItem(tabIndex, false /* smoothScroll */);
- if (previousItemIndex != tabIndex) {
- sendFragmentVisibilityChange(previousItemIndex, false /* not visible */ );
- }
- mPageChangeListener.setCurrentPosition(tabIndex);
- sendFragmentVisibilityChange(tabIndex, true /* visible */ );
-
- // Restore to the previous manual selection
- mLastManuallySelectedFragment = savedTabIndex;
- mDuringSwipe = false;
- mUserTabClick = false;
- }
-
- @Override
- public void onNewIntent(Intent newIntent) {
- setIntent(newIntent);
- fixIntent(newIntent);
- setCurrentTab(newIntent);
- final String action = newIntent.getAction();
- if (UI.FILTER_CONTACTS_ACTION.equals(action)) {
- setupFilterText(newIntent);
- }
- if (mInSearchUi || (mSearchFragment != null && mSearchFragment.isVisible())) {
- exitSearchUi();
- }
-
- if (mViewPager.getCurrentItem() == TAB_INDEX_DIALER) {
- if (mDialpadFragment != null) {
- mDialpadFragment.setStartedFromNewIntent(true);
- } else {
- Log.e(TAG, "DialpadFragment isn't ready yet when the tab is already selected.");
- }
- } else if (mViewPager.getCurrentItem() == TAB_INDEX_CALL_LOG) {
- if (mCallLogFragment != null) {
- mCallLogFragment.configureScreenFromIntent(newIntent);
- } else {
- Log.e(TAG, "CallLogFragment isn't ready yet when the tab is already selected.");
- }
- }
- invalidateOptionsMenu();
- }
-
- /** Returns true if the given intent contains a phone number to populate the dialer with */
- private boolean isDialIntent(Intent intent) {
- final String action = intent.getAction();
- if (Intent.ACTION_DIAL.equals(action) || ACTION_TOUCH_DIALER.equals(action)) {
- return true;
- }
- if (Intent.ACTION_VIEW.equals(action)) {
- final Uri data = intent.getData();
- if (data != null && CallUtil.SCHEME_TEL.equals(data.getScheme())) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Returns an appropriate call origin for this Activity. May return null when no call origin
- * should be used (e.g. when some 3rd party application launched the screen. Call origin is
- * for remembering the tab in which the user made a phone call, so the external app's DIAL
- * request should not be counted.)
- */
- public String getCallOrigin() {
- return !isDialIntent(getIntent()) ? CALL_ORIGIN_DIALTACTS : null;
- }
-
- /**
- * Retrieves the filter text stored in {@link #setupFilterText(Intent)}.
- * This text originally came from a FILTER_CONTACTS_ACTION intent received
- * by this activity. The stored text will then be cleared after after this
- * method returns.
- *
- * @return The stored filter text
- */
- public String getAndClearFilterText() {
- String filterText = mFilterText;
- mFilterText = null;
- return filterText;
- }
-
- /**
- * Stores the filter text associated with a FILTER_CONTACTS_ACTION intent.
- * This is so child activities can check if they are supposed to display a filter.
- *
- * @param intent The intent received in {@link #onNewIntent(Intent)}
- */
- private void setupFilterText(Intent intent) {
- // If the intent was relaunched from history, don't apply the filter text.
- if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) {
- return;
- }
- String filter = intent.getStringExtra(UI.FILTER_TEXT_EXTRA_KEY);
- if (filter != null && filter.length() > 0) {
- mFilterText = filter;
- }
- }
-
- @Override
- public void onBackPressed() {
- if (mInSearchUi) {
- // We should let the user go back to usual screens with tabs.
- exitSearchUi();
- } else if (isTaskRoot()) {
- // Instead of stopping, simply push this to the back of the stack.
- // This is only done when running at the top of the stack;
- // otherwise, we have been launched by someone else so need to
- // allow the user to go back to the caller.
- moveTaskToBack(false);
- } else {
- super.onBackPressed();
- }
- }
-
- private final PhoneFavoriteFragment.Listener mPhoneFavoriteListener =
- new PhoneFavoriteFragment.Listener() {
- @Override
- public void onContactSelected(Uri contactUri) {
- PhoneNumberInteraction.startInteractionForPhoneCall(
- OldDialtactsActivity.this, contactUri, getCallOrigin());
- }
-
- @Override
- public void onCallNumberDirectly(String phoneNumber) {
- Intent intent = CallUtil.getCallIntent(phoneNumber, getCallOrigin());
- startActivity(intent);
- }
- };
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.dialtacts_options, menu);
-
- // set up intents and onClick listeners
- final MenuItem callSettingsMenuItem = menu.findItem(R.id.menu_call_settings);
- final MenuItem searchMenuItem = menu.findItem(R.id.search_on_action_bar);
- final MenuItem filterOptionMenuItem = menu.findItem(R.id.filter_option);
-
- callSettingsMenuItem.setIntent(OldDialtactsActivity.getCallSettingsIntent());
- searchMenuItem.setOnMenuItemClickListener(mSearchMenuItemClickListener);
- filterOptionMenuItem.setOnMenuItemClickListener(mFilterOptionsMenuItemClickListener);
-
- return true;
- }
-
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- if (mInSearchUi) {
- prepareOptionsMenuInSearchMode(menu);
- } else {
- // get reference to the currently selected tab
- final Tab tab = getActionBar().getSelectedTab();
- if (tab != null) {
- switch(tab.getPosition()) {
- case TAB_INDEX_DIALER:
- prepareOptionsMenuForDialerTab(menu);
- break;
- case TAB_INDEX_CALL_LOG:
- prepareOptionsMenuForCallLogTab(menu);
- break;
- case TAB_INDEX_FAVORITES:
- prepareOptionsMenuForFavoritesTab(menu);
- break;
- }
- }
- }
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.add_contact:
- try {
- startActivity(new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI));
- } catch (ActivityNotFoundException e) {
- Toast toast = Toast.makeText(this, R.string.add_contact_not_available,
- Toast.LENGTH_SHORT);
- toast.show();
- }
- return true;
- }
- return super.onOptionsItemSelected(item);
- }
-
- private void prepareOptionsMenuInSearchMode(Menu menu) {
- // get references to menu items
- final MenuItem searchMenuItem = menu.findItem(R.id.search_on_action_bar);
- final MenuItem filterOptionMenuItem = menu.findItem(R.id.filter_option);
- final MenuItem addContactOptionMenuItem = menu.findItem(R.id.add_contact);
- final MenuItem callSettingsMenuItem = menu.findItem(R.id.menu_call_settings);
- final MenuItem emptyRightMenuItem = menu.findItem(R.id.empty_right_menu_item);
-
- // prepare the menu items
- searchMenuItem.setVisible(false);
- filterOptionMenuItem.setVisible(ViewConfiguration.get(this).hasPermanentMenuKey());
- addContactOptionMenuItem.setVisible(false);
- callSettingsMenuItem.setVisible(false);
- emptyRightMenuItem.setVisible(false);
- }
-
- private void prepareOptionsMenuForDialerTab(Menu menu) {
- if (DEBUG) {
- Log.d(TAG, "onPrepareOptionsMenu(dialer). swipe: " + mDuringSwipe
- + ", user tab click: " + mUserTabClick);
- }
-
- // get references to menu items
- final MenuItem searchMenuItem = menu.findItem(R.id.search_on_action_bar);
- final MenuItem filterOptionMenuItem = menu.findItem(R.id.filter_option);
- final MenuItem addContactOptionMenuItem = menu.findItem(R.id.add_contact);
- final MenuItem callSettingsMenuItem = menu.findItem(R.id.menu_call_settings);
- final MenuItem emptyRightMenuItem = menu.findItem(R.id.empty_right_menu_item);
-
- // prepare the menu items
- filterOptionMenuItem.setVisible(false);
- addContactOptionMenuItem.setVisible(false);
- if (mDuringSwipe || mUserTabClick) {
- // During horizontal movement, the real ActionBar menu items are shown
- searchMenuItem.setVisible(true);
- callSettingsMenuItem.setVisible(true);
- // When there is a permanent menu key, there is no overflow icon on the right of
- // the action bar which would force the search menu item (if it is visible) to the
- // left. This is the purpose of showing the emptyRightMenuItem.
- emptyRightMenuItem.setVisible(ViewConfiguration.get(this).hasPermanentMenuKey());
- } else {
- // This is when the user is looking at the dialer pad. In this case, the real
- // ActionBar is hidden and fake menu items are shown.
- // Except in landscape, in which case the real search menu item is shown.
- searchMenuItem.setVisible(OrientationUtil.isLandscape(this));
- // If a permanent menu key is available, then we need to show the call settings item
- // so that the call settings item can be invoked by the permanent menu key.
- callSettingsMenuItem.setVisible(ViewConfiguration.get(this).hasPermanentMenuKey());
- emptyRightMenuItem.setVisible(false);
- }
- }
-
- private void prepareOptionsMenuForCallLogTab(Menu menu) {
- // get references to menu items
- final MenuItem searchMenuItem = menu.findItem(R.id.search_on_action_bar);
- final MenuItem filterOptionMenuItem = menu.findItem(R.id.filter_option);
- final MenuItem addContactOptionMenuItem = menu.findItem(R.id.add_contact);
- final MenuItem callSettingsMenuItem = menu.findItem(R.id.menu_call_settings);
- final MenuItem emptyRightMenuItem = menu.findItem(R.id.empty_right_menu_item);
-
- // prepare the menu items
- searchMenuItem.setVisible(true);
- filterOptionMenuItem.setVisible(false);
- addContactOptionMenuItem.setVisible(false);
- callSettingsMenuItem.setVisible(true);
- emptyRightMenuItem.setVisible(ViewConfiguration.get(this).hasPermanentMenuKey());
- }
-
- private void prepareOptionsMenuForFavoritesTab(Menu menu) {
- // get references to menu items
- final MenuItem searchMenuItem = menu.findItem(R.id.search_on_action_bar);
- final MenuItem filterOptionMenuItem = menu.findItem(R.id.filter_option);
- final MenuItem addContactOptionMenuItem = menu.findItem(R.id.add_contact);
- final MenuItem callSettingsMenuItem = menu.findItem(R.id.menu_call_settings);
- final MenuItem emptyRightMenuItem = menu.findItem(R.id.empty_right_menu_item);
-
- // prepare the menu items
- searchMenuItem.setVisible(true);
- filterOptionMenuItem.setVisible(true);
- addContactOptionMenuItem.setVisible(true);
- callSettingsMenuItem.setVisible(true);
- emptyRightMenuItem.setVisible(false);
- }
-
- @Override
- public void startSearch(String initialQuery, boolean selectInitialQuery,
- Bundle appSearchData, boolean globalSearch) {
- if (mSearchFragment != null && mSearchFragment.isAdded() && !globalSearch) {
- if (mInSearchUi) {
- if (mSearchView.hasFocus()) {
- showInputMethod(mSearchView.findFocus());
- } else {
- mSearchView.requestFocus();
- }
- } else {
- enterSearchUi();
- }
- } else {
- super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
- }
- }
-
- /**
- * Hides every tab and shows search UI for phone lookup.
- */
- private void enterSearchUi() {
- if (mSearchFragment == null) {
- // We add the search fragment dynamically in the first onLayoutChange() and
- // mSearchFragment is set sometime later when the fragment transaction is actually
- // executed, which means there's a window when users are able to hit the (physical)
- // search key but mSearchFragment is still null.
- // It's quite hard to handle this case right, so let's just ignore the search key
- // in this case. Users can just hit it again and it will work this time.
- return;
- }
- if (mSearchView == null) {
- prepareSearchView();
- }
-
- final ActionBar actionBar = getActionBar();
-
- final Tab tab = actionBar.getSelectedTab();
-
- // User can search during the call, but we don't want to remember the status.
- if (tab != null && (mDialpadFragment == null ||
- !mDialpadFragment.phoneIsInUse())) {
- mLastManuallySelectedFragment = tab.getPosition();
- }
-
- mSearchView.setQuery(null, true);
-
- actionBar.setDisplayShowCustomEnabled(true);
- actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
- actionBar.setDisplayShowHomeEnabled(true);
- actionBar.setDisplayHomeAsUpEnabled(true);
-
- updateFakeMenuButtonsVisibility(false);
-
- for (int i = 0; i < TAB_INDEX_COUNT; i++) {
- sendFragmentVisibilityChange(i, false /* not visible */ );
- }
-
- // Show the search fragment and hide everything else.
- mSearchFragment.setUserVisibleHint(true);
- final FragmentTransaction transaction = getFragmentManager().beginTransaction();
- transaction.show(mSearchFragment);
- transaction.commitAllowingStateLoss();
- mViewPager.setVisibility(View.GONE);
-
- // We need to call this and onActionViewCollapsed() manually, since we are using a custom
- // layout instead of asking the search menu item to take care of SearchView.
- mSearchView.onActionViewExpanded();
- mInSearchUi = true;
- }
-
- private void showInputMethod(View view) {
- InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
- if (imm != null) {
- if (!imm.showSoftInput(view, 0)) {
- Log.w(TAG, "Failed to show soft input method.");
- }
- }
- }
-
- private void hideInputMethod(View view) {
- InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
- if (imm != null && view != null) {
- imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
- }
- }
-
- /**
- * Goes back to usual Phone UI with tags. Previously selected Tag and associated Fragment
- * should be automatically focused again.
- */
- private void exitSearchUi() {
- final ActionBar actionBar = getActionBar();
-
- // Hide the search fragment, if exists.
- if (mSearchFragment != null) {
- mSearchFragment.setUserVisibleHint(false);
-
- final FragmentTransaction transaction = getFragmentManager().beginTransaction();
- transaction.hide(mSearchFragment);
- transaction.commitAllowingStateLoss();
- }
-
- // We want to hide SearchView and show Tabs. Also focus on previously selected one.
- actionBar.setDisplayShowCustomEnabled(false);
- actionBar.setDisplayShowHomeEnabled(false);
- actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
-
- for (int i = 0; i < TAB_INDEX_COUNT; i++) {
- sendFragmentVisibilityChange(i, i == mViewPager.getCurrentItem());
- }
-
- // Before exiting the search screen, reset swipe state.
- mDuringSwipe = false;
- mUserTabClick = false;
-
- mViewPager.setVisibility(View.VISIBLE);
-
- hideInputMethod(getCurrentFocus());
-
- // Request to update option menu.
- invalidateOptionsMenu();
-
- // See comments in onActionViewExpanded()
- mSearchView.onActionViewCollapsed();
- mInSearchUi = false;
- }
-
- private Fragment getFragmentAt(int position) {
- switch (position) {
- case TAB_INDEX_DIALER:
- return mDialpadFragment;
- case TAB_INDEX_CALL_LOG:
- return mCallLogFragment;
- case TAB_INDEX_FAVORITES:
- return mPhoneFavoriteFragment;
- default:
- throw new IllegalStateException("Unknown fragment index: " + position);
- }
- }
-
- private void sendFragmentVisibilityChange(int position, boolean visibility) {
- if (DEBUG) {
- Log.d(TAG, "sendFragmentVisibiltyChange(). position: " + position
- + ", visibility: " + visibility);
- }
- // Position can be -1 initially. See PageChangeListener.
- if (position >= 0) {
- final Fragment fragment = getFragmentAt(position);
- if (fragment != null) {
- fragment.setMenuVisibility(visibility);
- fragment.setUserVisibleHint(visibility);
- }
- }
- }
-
- /**
- * Update visibility of the search button and menu button at the bottom.
- * They should be invisible when bottom ActionBar's real items are available, and be visible
- * otherwise.
- *
- * @param visible True when visible.
- */
- private void updateFakeMenuButtonsVisibility(boolean visible) {
- // Note: Landscape mode does not have the fake menu and search buttons.
- if (DEBUG) {
- Log.d(TAG, "updateFakeMenuButtonVisibility(" + visible + ")");
- }
-
- if (mSearchButton != null) {
- if (visible) {
- mSearchButton.setVisibility(View.VISIBLE);
- } else {
- mSearchButton.setVisibility(View.INVISIBLE);
- }
- }
- if (mMenuButton != null) {
- if (visible && !ViewConfiguration.get(this).hasPermanentMenuKey()) {
- mMenuButton.setVisibility(View.VISIBLE);
- } else {
- mMenuButton.setVisibility(View.INVISIBLE);
- }
- }
- }
-
- /** Returns an Intent to launch Call Settings screen */
- public static Intent getCallSettingsIntent() {
- final Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.setClassName(PHONE_PACKAGE, CALL_SETTINGS_CLASS_NAME);
- intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- return intent;
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (resultCode != Activity.RESULT_OK) {
- return;
- }
- switch (requestCode) {
- case SUBACTIVITY_ACCOUNT_FILTER: {
- AccountFilterUtil.handleAccountFilterResult(
- mContactListFilterController, resultCode, data);
- }
- break;
- }
- }
-}
diff --git a/src/com/android/dialer/dialpad/DialpadFragment.java b/src/com/android/dialer/dialpad/DialpadFragment.java
index 72f45e452..76cca3362 100644
--- a/src/com/android/dialer/dialpad/DialpadFragment.java
+++ b/src/com/android/dialer/dialpad/DialpadFragment.java
@@ -32,7 +32,6 @@ import android.graphics.BitmapFactory;
import android.media.AudioManager;
import android.media.ToneGenerator;
import android.net.Uri;
-import android.os.AsyncTask;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -50,6 +49,7 @@ import android.text.SpannableString;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.style.RelativeSizeSpan;
+import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
@@ -60,13 +60,16 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnPreDrawListener;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.EditText;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.PopupMenu;
-import android.widget.RelativeLayout;
+import android.widget.TableRow;
import android.widget.TextView;
import com.android.contacts.common.CallUtil;
@@ -75,7 +78,8 @@ import com.android.contacts.common.activity.TransactionSafeActivity;
import com.android.contacts.common.preference.ContactsPreferences;
import com.android.contacts.common.util.PhoneNumberFormatter;
import com.android.contacts.common.util.StopWatch;
-import com.android.dialer.OldDialtactsActivity;
+import com.android.dialer.NeededForReflection;
+import com.android.dialer.DialtactsActivity;
import com.android.dialer.R;
import com.android.dialer.SpecialCharSequenceMgr;
import com.android.dialer.database.DialerDatabaseHelper;
@@ -86,8 +90,6 @@ import com.android.phone.common.CallLogAsync;
import com.android.phone.common.HapticFeedback;
import com.google.common.annotations.VisibleForTesting;
-import java.util.List;
-
/**
* Fragment that displays a twelve-key phone dialpad.
*/
@@ -96,11 +98,52 @@ public class DialpadFragment extends Fragment
View.OnLongClickListener, View.OnKeyListener,
AdapterView.OnItemClickListener, TextWatcher,
PopupMenu.OnMenuItemClickListener,
- DialpadImageButton.OnPressedListener,
- SmartDialLoaderTask.SmartDialLoaderCallback {
+ DialpadKeyButton.OnPressedListener {
private static final String TAG = DialpadFragment.class.getSimpleName();
- private static final boolean DEBUG = OldDialtactsActivity.DEBUG;
+ public interface OnDialpadFragmentStartedListener {
+ public void onDialpadFragmentStarted();
+ }
+
+ /**
+ * LinearLayout with getter and setter methods for the translationY property using floats,
+ * for animation purposes.
+ */
+ public static class DialpadSlidingLinearLayout extends LinearLayout {
+
+ public DialpadSlidingLinearLayout(Context context) {
+ super(context);
+ }
+
+ public DialpadSlidingLinearLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public DialpadSlidingLinearLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @NeededForReflection
+ public float getYFraction() {
+ final int height = getHeight();
+ if (height == 0) return 0;
+ return getTranslationY() / height;
+ }
+
+ @NeededForReflection
+ public void setYFraction(float yFraction) {
+ setTranslationY(yFraction * getHeight());
+ }
+ }
+
+ public interface OnDialpadQueryChangedListener {
+ void onDialpadQueryChanged(String query);
+ }
+
+ private static final boolean DEBUG = DialtactsActivity.DEBUG;
+
+ // This is the amount of screen the dialpad fragment takes up when fully displayed
+ private static final float DIALPAD_SLIDE_FRACTION = 0.67f;
private static final String EMPTY_NUMBER = "";
private static final char PAUSE = ',';
@@ -118,6 +161,8 @@ public class DialpadFragment extends Fragment
private ContactsPreferences mContactsPrefs;
+ private OnDialpadQueryChangedListener mDialpadQueryListener;
+
/**
* View (usually FrameLayout) containing mDigits field. This can be null, in which mDigits
* isn't enclosed by the container.
@@ -145,25 +190,6 @@ public class DialpadFragment extends Fragment
private ListView mDialpadChooser;
private DialpadChooserAdapter mDialpadChooserAdapter;
- /** Will be set only if the view has the smart dialing section. */
- private RelativeLayout mSmartDialContainer;
-
- /**
- * Will be set only if the view has the smart dialing section.
- */
- private SmartDialController mSmartDialAdapter;
-
- /**
- * Use latin character map by default
- */
- private SmartDialMap mSmartDialMap = new LatinSmartDialMap();
-
- /**
- * Master switch controlling whether or not smart dialing is enabled, and whether the
- * smart dialing suggestion strip is visible.
- */
- private boolean mSmartDialEnabled = false;
-
private DialerDatabaseHelper mDialerDatabaseHelper;
/**
@@ -287,8 +313,10 @@ public class DialpadFragment extends Fragment
mDigits.setCursorVisible(false);
}
+ if (mDialpadQueryListener != null) {
+ mDialpadQueryListener.onDialpadQueryChanged(mDigits.getText().toString());
+ }
updateDialAndDeleteButtonEnabledState();
- loadSmartDialEntries();
}
@Override
@@ -308,8 +336,6 @@ public class DialpadFragment extends Fragment
Log.e(TAG, "Vibrate control bool missing.", nfe);
}
- setHasOptionsMenu(true);
-
mProhibitedPhoneNumberRegexp = getResources().getString(
R.string.config_prohibited_phone_number_regexp);
@@ -320,7 +346,30 @@ public class DialpadFragment extends Fragment
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
- View fragmentView = inflater.inflate(R.layout.dialpad_fragment, container, false);
+ final View fragmentView = inflater.inflate(R.layout.new_dialpad_fragment, container,
+ false);
+ fragmentView.buildLayer();
+
+ // TODO krelease: Get rid of this ugly hack which is to prevent the first frame of the
+ // animation from drawing the fragment at translationY = 0
+ final ViewTreeObserver vto = fragmentView.getViewTreeObserver();
+ final OnPreDrawListener preDrawListener = new OnPreDrawListener() {
+
+ @Override
+ public boolean onPreDraw() {
+ if (isHidden()) return true;
+ if (fragmentView.getTranslationY() == 0) {
+ ((DialpadSlidingLinearLayout) fragmentView).setYFraction(
+ DIALPAD_SLIDE_FRACTION);
+ }
+ final ViewTreeObserver vto = fragmentView.getViewTreeObserver();
+ vto.removeOnPreDrawListener(this);
+ return true;
+ }
+
+ };
+
+ vto.addOnPreDrawListener(preDrawListener);
// Load up the resources for the text field.
Resources r = getResources();
@@ -339,18 +388,6 @@ public class DialpadFragment extends Fragment
setupKeypad(fragmentView);
}
- DisplayMetrics dm = getResources().getDisplayMetrics();
- int minCellSize = (int) (56 * dm.density); // 56dip == minimum size of menu buttons
- int cellCount = dm.widthPixels / minCellSize;
- int fakeMenuItemWidth = dm.widthPixels / cellCount;
- mDialButtonContainer = fragmentView.findViewById(R.id.dialButtonContainer);
- // If in portrait, add padding to the dial button since we need space for the
- // search and menu/overflow buttons.
- if (mDialButtonContainer != null && !OrientationUtil.isLandscape(this.getActivity())) {
- mDialButtonContainer.setPadding(
- fakeMenuItemWidth, mDialButtonContainer.getPaddingTop(),
- fakeMenuItemWidth, mDialButtonContainer.getPaddingBottom());
- }
mDialButton = fragmentView.findViewById(R.id.dialButton);
if (r.getBoolean(R.bool.config_show_onscreen_dial_button)) {
mDialButton.setOnClickListener(this);
@@ -379,16 +416,24 @@ public class DialpadFragment extends Fragment
mDialpadChooser = (ListView) fragmentView.findViewById(R.id.dialpadChooser);
mDialpadChooser.setOnItemClickListener(this);
- // Smart dial container. This is null if in landscape mode since it is not present
- // in the landscape dialer layout.
- mSmartDialContainer = (RelativeLayout) fragmentView.findViewById(
- R.id.dialpad_smartdial_container);
+ return fragmentView;
+ }
- if (mSmartDialContainer != null) {
- mSmartDialAdapter = new SmartDialController(getActivity(), mSmartDialContainer,
- new OnSmartDialShortClick(), new OnSmartDialLongClick());
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ final Activity activity = getActivity();
+
+ try {
+ ((OnDialpadFragmentStartedListener) activity).onDialpadFragmentStarted();
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString()
+ + " must implement OnDialpadFragmentStartedListener");
}
- return fragmentView;
+
+ final View overflowButton = getView().findViewById(R.id.overflow_menu_on_dialpad);
+ overflowButton.setOnClickListener(this);
}
private boolean isLayoutReady() {
@@ -472,11 +517,10 @@ public class DialpadFragment extends Fragment
*/
private void configureScreenFromIntent(Activity parent) {
// If we were not invoked with a DIAL intent,
- if (!(parent instanceof OldDialtactsActivity)) {
+ if (!(parent instanceof DialtactsActivity)) {
setStartedFromNewIntent(false);
return;
}
-
// See if we were invoked with a DIAL intent. If we were, fill in the appropriate
// digits in the dialer field.
Intent intent = parent.getIntent();
@@ -542,10 +586,36 @@ public class DialpadFragment extends Fragment
}
private void setupKeypad(View fragmentView) {
- int[] buttonIds = new int[] { R.id.one, R.id.two, R.id.three, R.id.four, R.id.five,
- R.id.six, R.id.seven, R.id.eight, R.id.nine, R.id.zero, R.id.star, R.id.pound};
- for (int id : buttonIds) {
- ((DialpadImageButton) fragmentView.findViewById(id)).setOnPressedListener(this);
+ final int[] buttonIds = new int[] {R.id.zero, R.id.one, R.id.two, R.id.three, R.id.four,
+ R.id.five, R.id.six, R.id.seven, R.id.eight, R.id.nine, R.id.star, R.id.pound};
+
+ final int[] numberIds = new int[] {R.string.dialpad_0_number, R.string.dialpad_1_number,
+ R.string.dialpad_2_number, R.string.dialpad_3_number, R.string.dialpad_4_number,
+ R.string.dialpad_5_number, R.string.dialpad_6_number, R.string.dialpad_7_number,
+ R.string.dialpad_8_number, R.string.dialpad_9_number, R.string.dialpad_star_number,
+ R.string.dialpad_pound_number};
+
+ final int[] letterIds = new int[] {R.string.dialpad_0_letters, R.string.dialpad_1_letters,
+ R.string.dialpad_2_letters, R.string.dialpad_3_letters, R.string.dialpad_4_letters,
+ R.string.dialpad_5_letters, R.string.dialpad_6_letters, R.string.dialpad_7_letters,
+ R.string.dialpad_8_letters, R.string.dialpad_9_letters,
+ R.string.dialpad_star_letters, R.string.dialpad_pound_letters};
+
+ DialpadKeyButton dialpadKey;
+ TextView numberView;
+ TextView lettersView;
+ final Resources resources = getResources();
+ for (int i = 0; i < buttonIds.length; i++) {
+ dialpadKey = (DialpadKeyButton) fragmentView.findViewById(buttonIds[i]);
+ dialpadKey.setLayoutParams(new TableRow.LayoutParams(
+ TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.MATCH_PARENT));
+ dialpadKey.setOnPressedListener(this);
+ numberView = (TextView) dialpadKey.findViewById(R.id.dialpad_key_number);
+ lettersView = (TextView) dialpadKey.findViewById(R.id.dialpad_key_letters);
+ numberView.setText(resources.getString(numberIds[i]));
+ if (lettersView != null) {
+ lettersView.setText(resources.getString(letterIds[i]));
+ }
}
// Long-pressing one button will initiate Voicemail.
@@ -560,6 +630,9 @@ public class DialpadFragment extends Fragment
public void onResume() {
super.onResume();
+ final DialtactsActivity activity = (DialtactsActivity) getActivity();
+ mDialpadQueryListener = activity;
+
final StopWatch stopWatch = StopWatch.start("Dialpad.onResume");
// Query the last dialed number. Do it first because hitting
@@ -568,16 +641,12 @@ public class DialpadFragment extends Fragment
stopWatch.lap("qloc");
- final ContentResolver contentResolver = getActivity().getContentResolver();
+ final ContentResolver contentResolver = activity.getContentResolver();
// retrieve the DTMF tone play back setting.
mDTMFToneEnabled = Settings.System.getInt(contentResolver,
Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1;
- // retrieve dialpad autocomplete setting
- mSmartDialEnabled = Settings.Secure.getInt(contentResolver,
- Settings.Secure.DIALPAD_AUTOCOMPLETE, 0) == 1 && mSmartDialContainer != null;
-
stopWatch.lap("dtwd");
// Retrieve the haptic feedback setting.
@@ -678,6 +747,7 @@ public class DialpadFragment extends Fragment
@Override
public void onStop() {
super.onStop();
+
if (mClearDigitsOnStop) {
mClearDigitsOnStop = false;
mDigits.getText().clear();
@@ -690,47 +760,9 @@ public class DialpadFragment extends Fragment
outState.putBoolean(PREF_DIGITS_FILLED_BY_INTENT, mDigitsFilledByIntent);
}
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- super.onCreateOptionsMenu(menu, inflater);
- // Landscape dialer uses the real actionbar menu, whereas portrait uses a fake one
- // that is created using constructPopupMenu()
- if (OrientationUtil.isLandscape(this.getActivity()) ||
- ViewConfiguration.get(getActivity()).hasPermanentMenuKey() &&
- isLayoutReady() && mDialpadChooser != null) {
- inflater.inflate(R.menu.dialpad_options, menu);
- }
- }
-
- @Override
- public void onPrepareOptionsMenu(Menu menu) {
- // Hardware menu key should be available and Views should already be ready.
- if (OrientationUtil.isLandscape(this.getActivity()) ||
- ViewConfiguration.get(getActivity()).hasPermanentMenuKey() &&
- isLayoutReady() && mDialpadChooser != null) {
- setupMenuItems(menu);
- }
- }
-
private void setupMenuItems(Menu menu) {
- final MenuItem callSettingsMenuItem = menu.findItem(R.id.menu_call_settings_dialpad);
final MenuItem addToContactMenuItem = menu.findItem(R.id.menu_add_contacts);
- // Check if all the menu items are inflated correctly. As a shortcut, we assume all menu
- // items are ready if the first item is non-null.
- if (callSettingsMenuItem == null) {
- return;
- }
-
- final Activity activity = getActivity();
- if (activity != null && ViewConfiguration.get(activity).hasPermanentMenuKey()) {
- // Call settings should be available via its parent Activity.
- callSettingsMenuItem.setVisible(false);
- } else {
- callSettingsMenuItem.setVisible(true);
- callSettingsMenuItem.setIntent(OldDialtactsActivity.getCallSettingsIntent());
- }
-
// We show "add to contacts" menu only when the user is
// seeing usual dialpad and has typed at least one digit.
// We never show a menu if the "choose dialpad" UI is up.
@@ -738,7 +770,6 @@ public class DialpadFragment extends Fragment
addToContactMenuItem.setVisible(false);
} else {
final CharSequence digits = mDigits.getText();
-
// Put the current digits string into an intent
addToContactMenuItem.setIntent(getAddToContactIntent(digits));
addToContactMenuItem.setVisible(true);
@@ -904,6 +935,15 @@ public class DialpadFragment extends Fragment
@Override
public void onClick(View view) {
switch (view.getId()) {
+ case R.id.overflow_menu_on_dialpad: {
+ final PopupMenu popupMenu = new PopupMenu(getActivity(), view);
+ final Menu menu = popupMenu.getMenu();
+ popupMenu.inflate(R.menu.dialpad_options_new);
+ popupMenu.setOnMenuItemClickListener(this);
+ setupMenuItems(menu);
+ popupMenu.show();
+ break;
+ }
case R.id.deleteButton: {
keyPressed(KeyEvent.KEYCODE_DEL);
return;
@@ -926,19 +966,6 @@ public class DialpadFragment extends Fragment
}
}
- public PopupMenu constructPopupMenu(View anchorView) {
- final Context context = getActivity();
- if (context == null) {
- return null;
- }
- final PopupMenu popupMenu = new PopupMenu(context, anchorView);
- final Menu menu = popupMenu.getMenu();
- popupMenu.inflate(R.menu.dialpad_options);
- popupMenu.setOnMenuItemClickListener(this);
- setupMenuItems(menu);
- return popupMenu;
- }
-
@Override
public boolean onLongClick(View view) {
final Editable digits = mDigits.getText();
@@ -1126,8 +1153,8 @@ public class DialpadFragment extends Fragment
mDigits.getText().clear();
} else {
final Intent intent = CallUtil.getCallIntent(number,
- (getActivity() instanceof OldDialtactsActivity ?
- ((OldDialtactsActivity) getActivity()).getCallOrigin() : null));
+ (getActivity() instanceof DialtactsActivity ?
+ ((DialtactsActivity) getActivity()).getCallOrigin() : null));
startActivity(intent);
mClearDigitsOnStop = true;
getActivity().finish();
@@ -1136,8 +1163,8 @@ public class DialpadFragment extends Fragment
}
private String getCallOrigin() {
- return (getActivity() instanceof OldDialtactsActivity) ?
- ((OldDialtactsActivity) getActivity()).getCallOrigin() : null;
+ return (getActivity() instanceof DialtactsActivity) ?
+ ((DialtactsActivity) getActivity()).getCallOrigin() : null;
}
private void handleDialButtonClickWithEmptyDigits() {
@@ -1481,12 +1508,9 @@ public class DialpadFragment extends Fragment
return getTelephonyManager().getCallState() == TelephonyManager.CALL_STATE_OFFHOOK;
}
- /**
- * Returns true whenever any one of the options from the menu is selected.
- * Code changes to support dialpad options
- */
@Override
- public boolean onOptionsItemSelected(MenuItem item) {
+ public boolean onMenuItemClick(MenuItem item) {
+ // R.id.menu_add_contacts already has an add to contact intent populated by setupMenuItems
switch (item.getItemId()) {
case R.id.menu_2s_pause:
updateDialString(PAUSE);
@@ -1499,11 +1523,6 @@ public class DialpadFragment extends Fragment
}
}
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- return onOptionsItemSelected(item);
- }
-
/**
* Updates the dial string (mDigits) after inserting a Pause character (,)
* or Wait character (;).
@@ -1654,79 +1673,22 @@ public class DialpadFragment extends Fragment
return intent;
}
- private String mLastDigitsForSmartDial;
-
- private void loadSmartDialEntries() {
- if (!mSmartDialEnabled || mSmartDialAdapter == null) {
- // No smart dial views. Landscape?
- return;
- }
-
- // Update only when the digits have changed.
- final String digits = SmartDialNameMatcher.normalizeNumber(mDigits.getText().toString(),
- mSmartDialMap);
- if (TextUtils.equals(digits, mLastDigitsForSmartDial)) {
- return;
- }
- mLastDigitsForSmartDial = digits;
-
- if (digits.length() < 1) {
- mSmartDialAdapter.clear();
- } else {
- final SmartDialLoaderTask task = new SmartDialLoaderTask(this, digits, getActivity());
- task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, new String[] {});
- }
- }
-
- @Override
- public void setSmartDialAdapterEntries(List<SmartDialEntry> data, String query) {
- if (data == null || query == null || !query.equals(mLastDigitsForSmartDial)) {
- return;
- }
- mSmartDialAdapter.setEntries(data);
- }
-
private void initializeSmartDialingState() {
// Handle smart dialing related state
- if (mSmartDialEnabled) {
- mSmartDialContainer.setVisibility(View.VISIBLE);
-
- if (DEBUG) {
- Log.w(TAG, "Creating smart dial database");
- }
- mDialerDatabaseHelper.startSmartDialUpdateThread();
- } else {
- if (mSmartDialContainer != null) {
- mSmartDialContainer.setVisibility(View.GONE);
- }
- }
+ // TODO krelease: This should probably be moved to somewhere more appropriate, maybe
+ // into DialtactsActivity
+ mDialerDatabaseHelper.startSmartDialUpdateThread();
}
- private class OnSmartDialLongClick implements View.OnLongClickListener {
- @Override
- public boolean onLongClick(View view) {
- final SmartDialEntry entry = (SmartDialEntry) view.getTag();
- if (entry == null) return false; // just in case.
- mClearDigitsOnStop = true;
- // Show the phone number disambiguation dialog without using the primary
- // phone number so that the user can decide which number to call
- PhoneNumberInteraction.startInteractionForPhoneCall(
- (TransactionSafeActivity) getActivity(), entry.contactUri, false);
- return true;
- }
- }
-
- private class OnSmartDialShortClick implements View.OnClickListener {
- @Override
- public void onClick(View view) {
- final SmartDialEntry entry = (SmartDialEntry) view.getTag();
- if (entry == null) return; // just in case.
- // Dial the displayed phone number immediately
- final Intent intent = CallUtil.getCallIntent(entry.phoneNumber.toString(),
- (getActivity() instanceof OldDialtactsActivity ?
- ((OldDialtactsActivity) getActivity()).getCallOrigin() : null));
- startActivity(intent);
- mClearDigitsOnStop = true;
+ @Override
+ public void onHiddenChanged(boolean hidden) {
+ super.onHiddenChanged(hidden);
+ final DialtactsActivity activity = (DialtactsActivity) getActivity();
+ if (activity == null) return;
+ if (hidden) {
+ activity.showSearchBar();
+ } else {
+ activity.hideSearchBar();
}
}
}
diff --git a/src/com/android/dialer/dialpad/DialpadImageButton.java b/src/com/android/dialer/dialpad/DialpadImageButton.java
deleted file mode 100644
index 8f9ea4d69..000000000
--- a/src/com/android/dialer/dialpad/DialpadImageButton.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.dialer.dialpad;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.ImageButton;
-
-/**
- * Custom {@link ImageButton} for dialpad buttons.
- * <p>
- * This class implements lift-to-type interaction when touch exploration is
- * enabled.
- */
-public class DialpadImageButton extends ImageButton {
- /** Accessibility manager instance used to check touch exploration state. */
- private AccessibilityManager mAccessibilityManager;
-
- /** Bounds used to filter HOVER_EXIT events. */
- private Rect mHoverBounds = new Rect();
-
- public interface OnPressedListener {
- public void onPressed(View view, boolean pressed);
- }
-
- private OnPressedListener mOnPressedListener;
-
- public void setOnPressedListener(OnPressedListener onPressedListener) {
- mOnPressedListener = onPressedListener;
- }
-
- public DialpadImageButton(Context context, AttributeSet attrs) {
- super(context, attrs);
- initForAccessibility(context);
- }
-
- public DialpadImageButton(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- initForAccessibility(context);
- }
-
- private void initForAccessibility(Context context) {
- mAccessibilityManager = (AccessibilityManager) context.getSystemService(
- Context.ACCESSIBILITY_SERVICE);
- }
-
- @Override
- public void setPressed(boolean pressed) {
- super.setPressed(pressed);
- if (mOnPressedListener != null) {
- mOnPressedListener.onPressed(this, pressed);
- }
- }
-
- @Override
- public void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
-
- mHoverBounds.left = getPaddingLeft();
- mHoverBounds.right = w - getPaddingRight();
- mHoverBounds.top = getPaddingTop();
- mHoverBounds.bottom = h - getPaddingBottom();
- }
-
- @Override
- public boolean performAccessibilityAction(int action, Bundle arguments) {
- if (action == AccessibilityNodeInfo.ACTION_CLICK) {
- simulateClickForAccessibility();
- return true;
- }
-
- return super.performAccessibilityAction(action, arguments);
- }
-
- @Override
- public boolean onHoverEvent(MotionEvent event) {
- // When touch exploration is turned on, lifting a finger while inside
- // the button's hover target bounds should perform a click action.
- if (mAccessibilityManager.isEnabled()
- && mAccessibilityManager.isTouchExplorationEnabled()) {
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_HOVER_ENTER:
- // Lift-to-type temporarily disables double-tap activation.
- setClickable(false);
- break;
- case MotionEvent.ACTION_HOVER_EXIT:
- if (mHoverBounds.contains((int) event.getX(), (int) event.getY())) {
- simulateClickForAccessibility();
- }
- setClickable(true);
- break;
- }
- }
-
- return super.onHoverEvent(event);
- }
-
- /**
- * When accessibility is on, simulate press and release to preserve the
- * semantic meaning of performClick(). Required for Braille support.
- */
- private void simulateClickForAccessibility() {
- // Checking the press state prevents double activation.
- if (isPressed()) {
- return;
- }
-
- setPressed(true);
-
- // Stay consistent with performClick() by sending the event after
- // setting the pressed state but before performing the action.
- sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
-
- setPressed(false);
- }
-}
diff --git a/src/com/android/dialer/dialpad/NewDialpadFragment.java b/src/com/android/dialer/dialpad/NewDialpadFragment.java
deleted file mode 100644
index e22c5d12a..000000000
--- a/src/com/android/dialer/dialpad/NewDialpadFragment.java
+++ /dev/null
@@ -1,1689 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.dialer.dialpad;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.app.Fragment;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.media.AudioManager;
-import android.media.ToneGenerator;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemProperties;
-import android.provider.Contacts.Intents.Insert;
-import android.provider.Contacts.People;
-import android.provider.Contacts.Phones;
-import android.provider.Contacts.PhonesColumns;
-import android.provider.Settings;
-import android.telephony.PhoneNumberUtils;
-import android.telephony.PhoneStateListener;
-import android.telephony.TelephonyManager;
-import android.text.Editable;
-import android.text.SpannableString;
-import android.text.TextUtils;
-import android.text.TextWatcher;
-import android.text.style.RelativeSizeSpan;
-import android.util.AttributeSet;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
-import android.view.ViewTreeObserver.OnPreDrawListener;
-import android.widget.AdapterView;
-import android.widget.BaseAdapter;
-import android.widget.EditText;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.ListView;
-import android.widget.PopupMenu;
-import android.widget.TableRow;
-import android.widget.TextView;
-
-import com.android.contacts.common.CallUtil;
-import com.android.contacts.common.GeoUtil;
-import com.android.contacts.common.activity.TransactionSafeActivity;
-import com.android.contacts.common.preference.ContactsPreferences;
-import com.android.contacts.common.util.PhoneNumberFormatter;
-import com.android.contacts.common.util.StopWatch;
-import com.android.dialer.OldDialtactsActivity;
-import com.android.dialer.NeededForReflection;
-import com.android.dialer.DialtactsActivity;
-import com.android.dialer.R;
-import com.android.dialer.SpecialCharSequenceMgr;
-import com.android.dialer.database.DialerDatabaseHelper;
-import com.android.dialer.interactions.PhoneNumberInteraction;
-import com.android.dialer.util.OrientationUtil;
-import com.android.internal.telephony.ITelephony;
-import com.android.phone.common.CallLogAsync;
-import com.android.phone.common.HapticFeedback;
-import com.google.common.annotations.VisibleForTesting;
-
-/**
- * Fragment that displays a twelve-key phone dialpad.
- */
-public class NewDialpadFragment extends Fragment
- implements View.OnClickListener,
- View.OnLongClickListener, View.OnKeyListener,
- AdapterView.OnItemClickListener, TextWatcher,
- PopupMenu.OnMenuItemClickListener,
- DialpadKeyButton.OnPressedListener {
- private static final String TAG = NewDialpadFragment.class.getSimpleName();
-
- public interface OnDialpadFragmentStartedListener {
- public void onDialpadFragmentStarted();
- }
-
- /**
- * LinearLayout with getter and setter methods for the translationY property using floats,
- * for animation purposes.
- */
- public static class DialpadSlidingLinearLayout extends LinearLayout {
-
- public DialpadSlidingLinearLayout(Context context) {
- super(context);
- }
-
- public DialpadSlidingLinearLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public DialpadSlidingLinearLayout(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- @NeededForReflection
- public float getYFraction() {
- return getTranslationY() / getHeight();
- }
-
- @NeededForReflection
- public void setYFraction(float yFraction) {
- setTranslationY(yFraction * getHeight());
- }
- }
-
- public interface OnDialpadQueryChangedListener {
- void onDialpadQueryChanged(String query);
- }
-
- private static final boolean DEBUG = OldDialtactsActivity.DEBUG;
-
- private static final String EMPTY_NUMBER = "";
- private static final char PAUSE = ',';
- private static final char WAIT = ';';
-
- /** The length of DTMF tones in milliseconds */
- private static final int TONE_LENGTH_MS = 150;
- private static final int TONE_LENGTH_INFINITE = -1;
-
- /** The DTMF tone volume relative to other sounds in the stream */
- private static final int TONE_RELATIVE_VOLUME = 80;
-
- /** Stream type used to play the DTMF tones off call, and mapped to the volume control keys */
- private static final int DIAL_TONE_STREAM_TYPE = AudioManager.STREAM_DTMF;
-
- private ContactsPreferences mContactsPrefs;
-
- private OnDialpadQueryChangedListener mDialpadQueryListener;
-
- /**
- * View (usually FrameLayout) containing mDigits field. This can be null, in which mDigits
- * isn't enclosed by the container.
- */
- private View mDigitsContainer;
- private EditText mDigits;
-
- /** Remembers if we need to clear digits field when the screen is completely gone. */
- private boolean mClearDigitsOnStop;
-
- private View mDelete;
- private ToneGenerator mToneGenerator;
- private final Object mToneGeneratorLock = new Object();
- private View mDialpad;
- /**
- * Remembers the number of dialpad buttons which are pressed at this moment.
- * If it becomes 0, meaning no buttons are pressed, we'll call
- * {@link ToneGenerator#stopTone()}; the method shouldn't be called unless the last key is
- * released.
- */
- private int mDialpadPressCount;
-
- private View mDialButtonContainer;
- private View mDialButton;
- private ListView mDialpadChooser;
- private DialpadChooserAdapter mDialpadChooserAdapter;
-
- private DialerDatabaseHelper mDialerDatabaseHelper;
-
- /**
- * Regular expression prohibiting manual phone call. Can be empty, which means "no rule".
- */
- private String mProhibitedPhoneNumberRegexp;
-
-
- // Last number dialed, retrieved asynchronously from the call DB
- // in onCreate. This number is displayed when the user hits the
- // send key and cleared in onPause.
- private final CallLogAsync mCallLog = new CallLogAsync();
- private String mLastNumberDialed = EMPTY_NUMBER;
-
- // determines if we want to playback local DTMF tones.
- private boolean mDTMFToneEnabled;
-
- // Vibration (haptic feedback) for dialer key presses.
- private final HapticFeedback mHaptic = new HapticFeedback();
-
- /** Identifier for the "Add Call" intent extra. */
- private static final String ADD_CALL_MODE_KEY = "add_call_mode";
-
- /**
- * Identifier for intent extra for sending an empty Flash message for
- * CDMA networks. This message is used by the network to simulate a
- * press/depress of the "hookswitch" of a landline phone. Aka "empty flash".
- *
- * TODO: Using an intent extra to tell the phone to send this flash is a
- * temporary measure. To be replaced with an ITelephony call in the future.
- * TODO: Keep in sync with the string defined in OutgoingCallBroadcaster.java
- * in Phone app until this is replaced with the ITelephony API.
- */
- private static final String EXTRA_SEND_EMPTY_FLASH
- = "com.android.phone.extra.SEND_EMPTY_FLASH";
-
- private String mCurrentCountryIso;
-
- private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
- /**
- * Listen for phone state changes so that we can take down the
- * "dialpad chooser" if the phone becomes idle while the
- * chooser UI is visible.
- */
- @Override
- public void onCallStateChanged(int state, String incomingNumber) {
- // Log.i(TAG, "PhoneStateListener.onCallStateChanged: "
- // + state + ", '" + incomingNumber + "'");
- if ((state == TelephonyManager.CALL_STATE_IDLE) && dialpadChooserVisible()) {
- // Log.i(TAG, "Call ended with dialpad chooser visible! Taking it down...");
- // Note there's a race condition in the UI here: the
- // dialpad chooser could conceivably disappear (on its
- // own) at the exact moment the user was trying to select
- // one of the choices, which would be confusing. (But at
- // least that's better than leaving the dialpad chooser
- // onscreen, but useless...)
- showDialpadChooser(false);
- }
- }
- };
-
- private boolean mWasEmptyBeforeTextChange;
-
- /**
- * This field is set to true while processing an incoming DIAL intent, in order to make sure
- * that SpecialCharSequenceMgr actions can be triggered by user input but *not* by a
- * tel: URI passed by some other app. It will be set to false when all digits are cleared.
- */
- private boolean mDigitsFilledByIntent;
-
- private boolean mStartedFromNewIntent = false;
- private boolean mFirstLaunch = false;
-
- private static final String PREF_DIGITS_FILLED_BY_INTENT = "pref_digits_filled_by_intent";
-
- /**
- * Return an Intent for launching voicemail screen.
- */
- private static Intent getVoicemailIntent() {
- final Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
- Uri.fromParts("voicemail", "", null));
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- return intent;
- }
-
- private TelephonyManager getTelephonyManager() {
- return (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE);
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- mWasEmptyBeforeTextChange = TextUtils.isEmpty(s);
- }
-
- @Override
- public void onTextChanged(CharSequence input, int start, int before, int changeCount) {
- if (mWasEmptyBeforeTextChange != TextUtils.isEmpty(input)) {
- final Activity activity = getActivity();
- if (activity != null) {
- activity.invalidateOptionsMenu();
- }
- }
-
- // DTMF Tones do not need to be played here any longer -
- // the DTMF dialer handles that functionality now.
- }
-
- @Override
- public void afterTextChanged(Editable input) {
- // When DTMF dialpad buttons are being pressed, we delay SpecialCharSequencMgr sequence,
- // since some of SpecialCharSequenceMgr's behavior is too abrupt for the "touch-down"
- // behavior.
- if (!mDigitsFilledByIntent &&
- SpecialCharSequenceMgr.handleChars(getActivity(), input.toString(), mDigits)) {
- // A special sequence was entered, clear the digits
- mDigits.getText().clear();
- }
-
- if (isDigitsEmpty()) {
- mDigitsFilledByIntent = false;
- mDigits.setCursorVisible(false);
- }
-
- if (mDialpadQueryListener != null) {
- mDialpadQueryListener.onDialpadQueryChanged(mDigits.getText().toString());
- }
- updateDialAndDeleteButtonEnabledState();
- }
-
- @Override
- public void onCreate(Bundle state) {
- super.onCreate(state);
- mFirstLaunch = true;
- mContactsPrefs = new ContactsPreferences(getActivity());
- mCurrentCountryIso = GeoUtil.getCurrentCountryIso(getActivity());
-
- mDialerDatabaseHelper = DialerDatabaseHelper.getInstance(getActivity());
- SmartDialPrefix.initializeNanpSettings(getActivity());
-
- try {
- mHaptic.init(getActivity(),
- getResources().getBoolean(R.bool.config_enable_dialer_key_vibration));
- } catch (Resources.NotFoundException nfe) {
- Log.e(TAG, "Vibrate control bool missing.", nfe);
- }
-
- mProhibitedPhoneNumberRegexp = getResources().getString(
- R.string.config_prohibited_phone_number_regexp);
-
- if (state != null) {
- mDigitsFilledByIntent = state.getBoolean(PREF_DIGITS_FILLED_BY_INTENT);
- }
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
- final View fragmentView = inflater.inflate(R.layout.new_dialpad_fragment, container,
- false);
- fragmentView.buildLayer();
-
- // TODO krelease: Get rid of this ugly hack which is to prevent the first frame of the
- // animation from drawing the fragment at translationY = 0
- final ViewTreeObserver vto = fragmentView.getViewTreeObserver();
- final OnPreDrawListener preDrawListener = new OnPreDrawListener() {
-
- @Override
- public boolean onPreDraw() {
- if (isHidden()) return true;
- if (fragmentView.getTranslationY() == 0) {
- ((DialpadSlidingLinearLayout) fragmentView).setYFraction(0.67f);
- }
- final ViewTreeObserver vto = fragmentView.getViewTreeObserver();
- vto.removeOnPreDrawListener(this);
- return true;
- }
-
- };
-
- vto.addOnPreDrawListener(preDrawListener);
-
- // Load up the resources for the text field.
- Resources r = getResources();
-
- mDigitsContainer = fragmentView.findViewById(R.id.digits_container);
- mDigits = (EditText) fragmentView.findViewById(R.id.digits);
- mDigits.setKeyListener(UnicodeDialerKeyListener.INSTANCE);
- mDigits.setOnClickListener(this);
- mDigits.setOnKeyListener(this);
- mDigits.setOnLongClickListener(this);
- mDigits.addTextChangedListener(this);
- PhoneNumberFormatter.setPhoneNumberFormattingTextWatcher(getActivity(), mDigits);
- // Check for the presence of the keypad
- View oneButton = fragmentView.findViewById(R.id.one);
- if (oneButton != null) {
- setupKeypad(fragmentView);
- }
-
- mDialButton = fragmentView.findViewById(R.id.dialButton);
- if (r.getBoolean(R.bool.config_show_onscreen_dial_button)) {
- mDialButton.setOnClickListener(this);
- mDialButton.setOnLongClickListener(this);
- } else {
- mDialButton.setVisibility(View.GONE); // It's VISIBLE by default
- mDialButton = null;
- }
-
- mDelete = fragmentView.findViewById(R.id.deleteButton);
- if (mDelete != null) {
- mDelete.setOnClickListener(this);
- mDelete.setOnLongClickListener(this);
- }
-
- mDialpad = fragmentView.findViewById(R.id.dialpad); // This is null in landscape mode.
-
- // In landscape we put the keyboard in phone mode.
- if (null == mDialpad) {
- mDigits.setInputType(android.text.InputType.TYPE_CLASS_PHONE);
- } else {
- mDigits.setCursorVisible(false);
- }
-
- // Set up the "dialpad chooser" UI; see showDialpadChooser().
- mDialpadChooser = (ListView) fragmentView.findViewById(R.id.dialpadChooser);
- mDialpadChooser.setOnItemClickListener(this);
-
- return fragmentView;
- }
-
- @Override
- public void onStart() {
- super.onStart();
-
- final Activity activity = getActivity();
-
- try {
- ((OnDialpadFragmentStartedListener) activity).onDialpadFragmentStarted();
- } catch (ClassCastException e) {
- throw new ClassCastException(activity.toString()
- + " must implement OnDialpadFragmentStartedListener");
- }
-
- final View overflowButton = getView().findViewById(R.id.overflow_menu_on_dialpad);
- overflowButton.setOnClickListener(this);
- }
-
- private boolean isLayoutReady() {
- return mDigits != null;
- }
-
- public EditText getDigitsWidget() {
- return mDigits;
- }
-
- /**
- * @return true when {@link #mDigits} is actually filled by the Intent.
- */
- private boolean fillDigitsIfNecessary(Intent intent) {
- // Only fills digits from an intent if it is a new intent.
- // Otherwise falls back to the previously used number.
- if (!mFirstLaunch && !mStartedFromNewIntent) {
- return false;
- }
-
- final String action = intent.getAction();
- if (Intent.ACTION_DIAL.equals(action) || Intent.ACTION_VIEW.equals(action)) {
- Uri uri = intent.getData();
- if (uri != null) {
- if (CallUtil.SCHEME_TEL.equals(uri.getScheme())) {
- // Put the requested number into the input area
- String data = uri.getSchemeSpecificPart();
- // Remember it is filled via Intent.
- mDigitsFilledByIntent = true;
- final String converted = PhoneNumberUtils.convertKeypadLettersToDigits(
- PhoneNumberUtils.replaceUnicodeDigits(data));
- setFormattedDigits(converted, null);
- return true;
- } else {
- String type = intent.getType();
- if (People.CONTENT_ITEM_TYPE.equals(type)
- || Phones.CONTENT_ITEM_TYPE.equals(type)) {
- // Query the phone number
- Cursor c = getActivity().getContentResolver().query(intent.getData(),
- new String[] {PhonesColumns.NUMBER, PhonesColumns.NUMBER_KEY},
- null, null, null);
- if (c != null) {
- try {
- if (c.moveToFirst()) {
- // Remember it is filled via Intent.
- mDigitsFilledByIntent = true;
- // Put the number into the input area
- setFormattedDigits(c.getString(0), c.getString(1));
- return true;
- }
- } finally {
- c.close();
- }
- }
- }
- }
- }
- }
- return false;
- }
-
- /**
- * Determines whether an add call operation is requested.
- *
- * @param intent The intent.
- * @return {@literal true} if add call operation was requested. {@literal false} otherwise.
- */
- private static boolean isAddCallMode(Intent intent) {
- final String action = intent.getAction();
- if (Intent.ACTION_DIAL.equals(action) || Intent.ACTION_VIEW.equals(action)) {
- // see if we are "adding a call" from the InCallScreen; false by default.
- return intent.getBooleanExtra(ADD_CALL_MODE_KEY, false);
- } else {
- return false;
- }
- }
-
- /**
- * Checks the given Intent and changes dialpad's UI state. For example, if the Intent requires
- * the screen to enter "Add Call" mode, this method will show correct UI for the mode.
- */
- private void configureScreenFromIntent(Activity parent) {
- // If we were not invoked with a DIAL intent,
- if (!(parent instanceof DialtactsActivity)) {
- setStartedFromNewIntent(false);
- return;
- }
- // See if we were invoked with a DIAL intent. If we were, fill in the appropriate
- // digits in the dialer field.
- Intent intent = parent.getIntent();
-
- if (!isLayoutReady()) {
- // This happens typically when parent's Activity#onNewIntent() is called while
- // Fragment#onCreateView() isn't called yet, and thus we cannot configure Views at
- // this point. onViewCreate() should call this method after preparing layouts, so
- // just ignore this call now.
- Log.i(TAG,
- "Screen configuration is requested before onCreateView() is called. Ignored");
- return;
- }
-
- boolean needToShowDialpadChooser = false;
-
- // Be sure *not* to show the dialpad chooser if this is an
- // explicit "Add call" action, though.
- final boolean isAddCallMode = isAddCallMode(intent);
- if (!isAddCallMode) {
-
- // Don't show the chooser when called via onNewIntent() and phone number is present.
- // i.e. User clicks a telephone link from gmail for example.
- // In this case, we want to show the dialpad with the phone number.
- final boolean digitsFilled = fillDigitsIfNecessary(intent);
- if (!(mStartedFromNewIntent && digitsFilled)) {
-
- final String action = intent.getAction();
- if (Intent.ACTION_DIAL.equals(action) || Intent.ACTION_VIEW.equals(action)
- || Intent.ACTION_MAIN.equals(action)) {
- // If there's already an active call, bring up an intermediate UI to
- // make the user confirm what they really want to do.
- if (phoneIsInUse()) {
- needToShowDialpadChooser = true;
- }
- }
-
- }
- }
- showDialpadChooser(needToShowDialpadChooser);
- setStartedFromNewIntent(false);
- }
-
- public void setStartedFromNewIntent(boolean value) {
- mStartedFromNewIntent = value;
- }
-
- /**
- * Sets formatted digits to digits field.
- */
- private void setFormattedDigits(String data, String normalizedNumber) {
- // strip the non-dialable numbers out of the data string.
- String dialString = PhoneNumberUtils.extractNetworkPortion(data);
- dialString =
- PhoneNumberUtils.formatNumber(dialString, normalizedNumber, mCurrentCountryIso);
- if (!TextUtils.isEmpty(dialString)) {
- Editable digits = mDigits.getText();
- digits.replace(0, digits.length(), dialString);
- // for some reason this isn't getting called in the digits.replace call above..
- // but in any case, this will make sure the background drawable looks right
- afterTextChanged(digits);
- }
- }
-
- private void setupKeypad(View fragmentView) {
- final int[] buttonIds = new int[] {R.id.zero, R.id.one, R.id.two, R.id.three, R.id.four,
- R.id.five, R.id.six, R.id.seven, R.id.eight, R.id.nine, R.id.star, R.id.pound};
-
- final int[] numberIds = new int[] {R.string.dialpad_0_number, R.string.dialpad_1_number,
- R.string.dialpad_2_number, R.string.dialpad_3_number, R.string.dialpad_4_number,
- R.string.dialpad_5_number, R.string.dialpad_6_number, R.string.dialpad_7_number,
- R.string.dialpad_8_number, R.string.dialpad_9_number, R.string.dialpad_star_number,
- R.string.dialpad_pound_number};
-
- final int[] letterIds = new int[] {R.string.dialpad_0_letters, R.string.dialpad_1_letters,
- R.string.dialpad_2_letters, R.string.dialpad_3_letters, R.string.dialpad_4_letters,
- R.string.dialpad_5_letters, R.string.dialpad_6_letters, R.string.dialpad_7_letters,
- R.string.dialpad_8_letters, R.string.dialpad_9_letters,
- R.string.dialpad_star_letters, R.string.dialpad_pound_letters};
-
- DialpadKeyButton dialpadKey;
- TextView numberView;
- TextView lettersView;
- final Resources resources = getResources();
- for (int i = 0; i < buttonIds.length; i++) {
- dialpadKey = (DialpadKeyButton) fragmentView.findViewById(buttonIds[i]);
- dialpadKey.setLayoutParams(new TableRow.LayoutParams(
- TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.MATCH_PARENT));
- dialpadKey.setOnPressedListener(this);
- numberView = (TextView) dialpadKey.findViewById(R.id.dialpad_key_number);
- lettersView = (TextView) dialpadKey.findViewById(R.id.dialpad_key_letters);
- numberView.setText(resources.getString(numberIds[i]));
- if (lettersView != null) {
- lettersView.setText(resources.getString(letterIds[i]));
- }
- }
-
- // Long-pressing one button will initiate Voicemail.
- fragmentView.findViewById(R.id.one).setOnLongClickListener(this);
-
- // Long-pressing zero button will enter '+' instead.
- fragmentView.findViewById(R.id.zero).setOnLongClickListener(this);
-
- }
-
- @Override
- public void onResume() {
- super.onResume();
-
- final DialtactsActivity activity = (DialtactsActivity) getActivity();
- mDialpadQueryListener = activity;
-
- final StopWatch stopWatch = StopWatch.start("Dialpad.onResume");
-
- // Query the last dialed number. Do it first because hitting
- // the DB is 'slow'. This call is asynchronous.
- queryLastOutgoingCall();
-
- stopWatch.lap("qloc");
-
- final ContentResolver contentResolver = getActivity().getContentResolver();
-
- // retrieve the DTMF tone play back setting.
- mDTMFToneEnabled = Settings.System.getInt(contentResolver,
- Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1;
-
- stopWatch.lap("dtwd");
-
- // Retrieve the haptic feedback setting.
- mHaptic.checkSystemSetting();
-
- stopWatch.lap("hptc");
-
- // if the mToneGenerator creation fails, just continue without it. It is
- // a local audio signal, and is not as important as the dtmf tone itself.
- synchronized (mToneGeneratorLock) {
- if (mToneGenerator == null) {
- try {
- mToneGenerator = new ToneGenerator(DIAL_TONE_STREAM_TYPE, TONE_RELATIVE_VOLUME);
- } catch (RuntimeException e) {
- Log.w(TAG, "Exception caught while creating local tone generator: " + e);
- mToneGenerator = null;
- }
- }
- }
- stopWatch.lap("tg");
- // Prevent unnecessary confusion. Reset the press count anyway.
- mDialpadPressCount = 0;
-
- // Initialize smart dialing state. This has to be done before anything is filled in before
- // the dialpad edittext to prevent entries from being loaded from a null cache.
- initializeSmartDialingState();
-
- configureScreenFromIntent(getActivity());
-
- stopWatch.lap("fdin");
-
- // While we're in the foreground, listen for phone state changes,
- // purely so that we can take down the "dialpad chooser" if the
- // phone becomes idle while the chooser UI is visible.
- getTelephonyManager().listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
-
- stopWatch.lap("tm");
-
- // Potentially show hint text in the mDigits field when the user
- // hasn't typed any digits yet. (If there's already an active call,
- // this hint text will remind the user that he's about to add a new
- // call.)
- //
- // TODO: consider adding better UI for the case where *both* lines
- // are currently in use. (Right now we let the user try to add
- // another call, but that call is guaranteed to fail. Perhaps the
- // entire dialer UI should be disabled instead.)
- if (phoneIsInUse()) {
- final SpannableString hint = new SpannableString(
- getActivity().getString(R.string.dialerDialpadHintText));
- hint.setSpan(new RelativeSizeSpan(0.8f), 0, hint.length(), 0);
- mDigits.setHint(hint);
- } else {
- // Common case; no hint necessary.
- mDigits.setHint(null);
-
- // Also, a sanity-check: the "dialpad chooser" UI should NEVER
- // be visible if the phone is idle!
- showDialpadChooser(false);
- }
-
- mFirstLaunch = false;
-
- stopWatch.lap("hnt");
-
- updateDialAndDeleteButtonEnabledState();
-
- stopWatch.lap("bes");
-
- stopWatch.stopAndLog(TAG, 50);
- }
-
- @Override
- public void onPause() {
- super.onPause();
-
- // Stop listening for phone state changes.
- getTelephonyManager().listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
-
- // Make sure we don't leave this activity with a tone still playing.
- stopTone();
- // Just in case reset the counter too.
- mDialpadPressCount = 0;
-
- synchronized (mToneGeneratorLock) {
- if (mToneGenerator != null) {
- mToneGenerator.release();
- mToneGenerator = null;
- }
- }
- // TODO: I wonder if we should not check if the AsyncTask that
- // lookup the last dialed number has completed.
- mLastNumberDialed = EMPTY_NUMBER; // Since we are going to query again, free stale number.
-
- SpecialCharSequenceMgr.cleanup();
- }
-
- @Override
- public void onStop() {
- super.onStop();
-
- if (mClearDigitsOnStop) {
- mClearDigitsOnStop = false;
- mDigits.getText().clear();
- }
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putBoolean(PREF_DIGITS_FILLED_BY_INTENT, mDigitsFilledByIntent);
- }
-
- private void setupMenuItems(Menu menu) {
- final MenuItem addToContactMenuItem = menu.findItem(R.id.menu_add_contacts);
-
- // We show "add to contacts" menu only when the user is
- // seeing usual dialpad and has typed at least one digit.
- // We never show a menu if the "choose dialpad" UI is up.
- if (dialpadChooserVisible() || isDigitsEmpty()) {
- addToContactMenuItem.setVisible(false);
- } else {
- final CharSequence digits = mDigits.getText();
- // Put the current digits string into an intent
- addToContactMenuItem.setIntent(getAddToContactIntent(digits));
- addToContactMenuItem.setVisible(true);
- }
- }
-
- private static Intent getAddToContactIntent(CharSequence digits) {
- final Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
- intent.putExtra(Insert.PHONE, digits);
- intent.setType(People.CONTENT_ITEM_TYPE);
- return intent;
- }
-
- private void keyPressed(int keyCode) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_1:
- playTone(ToneGenerator.TONE_DTMF_1, TONE_LENGTH_INFINITE);
- break;
- case KeyEvent.KEYCODE_2:
- playTone(ToneGenerator.TONE_DTMF_2, TONE_LENGTH_INFINITE);
- break;
- case KeyEvent.KEYCODE_3:
- playTone(ToneGenerator.TONE_DTMF_3, TONE_LENGTH_INFINITE);
- break;
- case KeyEvent.KEYCODE_4:
- playTone(ToneGenerator.TONE_DTMF_4, TONE_LENGTH_INFINITE);
- break;
- case KeyEvent.KEYCODE_5:
- playTone(ToneGenerator.TONE_DTMF_5, TONE_LENGTH_INFINITE);
- break;
- case KeyEvent.KEYCODE_6:
- playTone(ToneGenerator.TONE_DTMF_6, TONE_LENGTH_INFINITE);
- break;
- case KeyEvent.KEYCODE_7:
- playTone(ToneGenerator.TONE_DTMF_7, TONE_LENGTH_INFINITE);
- break;
- case KeyEvent.KEYCODE_8:
- playTone(ToneGenerator.TONE_DTMF_8, TONE_LENGTH_INFINITE);
- break;
- case KeyEvent.KEYCODE_9:
- playTone(ToneGenerator.TONE_DTMF_9, TONE_LENGTH_INFINITE);
- break;
- case KeyEvent.KEYCODE_0:
- playTone(ToneGenerator.TONE_DTMF_0, TONE_LENGTH_INFINITE);
- break;
- case KeyEvent.KEYCODE_POUND:
- playTone(ToneGenerator.TONE_DTMF_P, TONE_LENGTH_INFINITE);
- break;
- case KeyEvent.KEYCODE_STAR:
- playTone(ToneGenerator.TONE_DTMF_S, TONE_LENGTH_INFINITE);
- break;
- default:
- break;
- }
-
- mHaptic.vibrate();
- KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
- mDigits.onKeyDown(keyCode, event);
-
- // If the cursor is at the end of the text we hide it.
- final int length = mDigits.length();
- if (length == mDigits.getSelectionStart() && length == mDigits.getSelectionEnd()) {
- mDigits.setCursorVisible(false);
- }
- }
-
- @Override
- public boolean onKey(View view, int keyCode, KeyEvent event) {
- switch (view.getId()) {
- case R.id.digits:
- if (keyCode == KeyEvent.KEYCODE_ENTER) {
- dialButtonPressed();
- return true;
- }
- break;
- }
- return false;
- }
-
- /**
- * When a key is pressed, we start playing DTMF tone, do vibration, and enter the digit
- * immediately. When a key is released, we stop the tone. Note that the "key press" event will
- * be delivered by the system with certain amount of delay, it won't be synced with user's
- * actual "touch-down" behavior.
- */
- @Override
- public void onPressed(View view, boolean pressed) {
- if (DEBUG) Log.d(TAG, "onPressed(). view: " + view + ", pressed: " + pressed);
- if (pressed) {
- switch (view.getId()) {
- case R.id.one: {
- keyPressed(KeyEvent.KEYCODE_1);
- break;
- }
- case R.id.two: {
- keyPressed(KeyEvent.KEYCODE_2);
- break;
- }
- case R.id.three: {
- keyPressed(KeyEvent.KEYCODE_3);
- break;
- }
- case R.id.four: {
- keyPressed(KeyEvent.KEYCODE_4);
- break;
- }
- case R.id.five: {
- keyPressed(KeyEvent.KEYCODE_5);
- break;
- }
- case R.id.six: {
- keyPressed(KeyEvent.KEYCODE_6);
- break;
- }
- case R.id.seven: {
- keyPressed(KeyEvent.KEYCODE_7);
- break;
- }
- case R.id.eight: {
- keyPressed(KeyEvent.KEYCODE_8);
- break;
- }
- case R.id.nine: {
- keyPressed(KeyEvent.KEYCODE_9);
- break;
- }
- case R.id.zero: {
- keyPressed(KeyEvent.KEYCODE_0);
- break;
- }
- case R.id.pound: {
- keyPressed(KeyEvent.KEYCODE_POUND);
- break;
- }
- case R.id.star: {
- keyPressed(KeyEvent.KEYCODE_STAR);
- break;
- }
- default: {
- Log.wtf(TAG, "Unexpected onTouch(ACTION_DOWN) event from: " + view);
- break;
- }
- }
- mDialpadPressCount++;
- } else {
- view.jumpDrawablesToCurrentState();
- mDialpadPressCount--;
- if (mDialpadPressCount < 0) {
- // e.g.
- // - when the user action is detected as horizontal swipe, at which only
- // "up" event is thrown.
- // - when the user long-press '0' button, at which dialpad will decrease this count
- // while it still gets press-up event here.
- if (DEBUG) Log.d(TAG, "mKeyPressCount become negative.");
- stopTone();
- mDialpadPressCount = 0;
- } else if (mDialpadPressCount == 0) {
- stopTone();
- }
- }
- }
-
- @Override
- public void onClick(View view) {
- switch (view.getId()) {
- case R.id.overflow_menu_on_dialpad: {
- final PopupMenu popupMenu = new PopupMenu(getActivity(), view);
- final Menu menu = popupMenu.getMenu();
- popupMenu.inflate(R.menu.dialpad_options_new);
- popupMenu.setOnMenuItemClickListener(this);
- setupMenuItems(menu);
- popupMenu.show();
- break;
- }
- case R.id.deleteButton: {
- keyPressed(KeyEvent.KEYCODE_DEL);
- return;
- }
- case R.id.dialButton: {
- mHaptic.vibrate(); // Vibrate here too, just like we do for the regular keys
- dialButtonPressed();
- return;
- }
- case R.id.digits: {
- if (!isDigitsEmpty()) {
- mDigits.setCursorVisible(true);
- }
- return;
- }
- default: {
- Log.wtf(TAG, "Unexpected onClick() event from: " + view);
- return;
- }
- }
- }
-
- @Override
- public boolean onLongClick(View view) {
- final Editable digits = mDigits.getText();
- final int id = view.getId();
- switch (id) {
- case R.id.deleteButton: {
- digits.clear();
- // TODO: The framework forgets to clear the pressed
- // status of disabled button. Until this is fixed,
- // clear manually the pressed status. b/2133127
- mDelete.setPressed(false);
- return true;
- }
- case R.id.one: {
- // '1' may be already entered since we rely on onTouch() event for numeric buttons.
- // Just for safety we also check if the digits field is empty or not.
- if (isDigitsEmpty() || TextUtils.equals(mDigits.getText(), "1")) {
- // We'll try to initiate voicemail and thus we want to remove irrelevant string.
- removePreviousDigitIfPossible();
-
- if (isVoicemailAvailable()) {
- callVoicemail();
- } else if (getActivity() != null) {
- // Voicemail is unavailable maybe because Airplane mode is turned on.
- // Check the current status and show the most appropriate error message.
- final boolean isAirplaneModeOn =
- Settings.System.getInt(getActivity().getContentResolver(),
- Settings.System.AIRPLANE_MODE_ON, 0) != 0;
- if (isAirplaneModeOn) {
- DialogFragment dialogFragment = ErrorDialogFragment.newInstance(
- R.string.dialog_voicemail_airplane_mode_message);
- dialogFragment.show(getFragmentManager(),
- "voicemail_request_during_airplane_mode");
- } else {
- DialogFragment dialogFragment = ErrorDialogFragment.newInstance(
- R.string.dialog_voicemail_not_ready_message);
- dialogFragment.show(getFragmentManager(), "voicemail_not_ready");
- }
- }
- return true;
- }
- return false;
- }
- case R.id.zero: {
- // Remove tentative input ('0') done by onTouch().
- removePreviousDigitIfPossible();
- keyPressed(KeyEvent.KEYCODE_PLUS);
-
- // Stop tone immediately and decrease the press count, so that possible subsequent
- // dial button presses won't honor the 0 click any more.
- // Note: this *will* make mDialpadPressCount negative when the 0 key is released,
- // which should be handled appropriately.
- stopTone();
- if (mDialpadPressCount > 0) mDialpadPressCount--;
-
- return true;
- }
- case R.id.digits: {
- // Right now EditText does not show the "paste" option when cursor is not visible.
- // To show that, make the cursor visible, and return false, letting the EditText
- // show the option by itself.
- mDigits.setCursorVisible(true);
- return false;
- }
- case R.id.dialButton: {
- if (isDigitsEmpty()) {
- handleDialButtonClickWithEmptyDigits();
- // This event should be consumed so that onClick() won't do the exactly same
- // thing.
- return true;
- } else {
- return false;
- }
- }
- }
- return false;
- }
-
- /**
- * Remove the digit just before the current position. This can be used if we want to replace
- * the previous digit or cancel previously entered character.
- */
- private void removePreviousDigitIfPossible() {
- final Editable editable = mDigits.getText();
- final int currentPosition = mDigits.getSelectionStart();
- if (currentPosition > 0) {
- mDigits.setSelection(currentPosition);
- mDigits.getText().delete(currentPosition - 1, currentPosition);
- }
- }
-
- public void callVoicemail() {
- startActivity(getVoicemailIntent());
- mClearDigitsOnStop = true;
- getActivity().finish();
- }
-
- public static class ErrorDialogFragment extends DialogFragment {
- private int mTitleResId;
- private int mMessageResId;
-
- private static final String ARG_TITLE_RES_ID = "argTitleResId";
- private static final String ARG_MESSAGE_RES_ID = "argMessageResId";
-
- public static ErrorDialogFragment newInstance(int messageResId) {
- return newInstance(0, messageResId);
- }
-
- public static ErrorDialogFragment newInstance(int titleResId, int messageResId) {
- final ErrorDialogFragment fragment = new ErrorDialogFragment();
- final Bundle args = new Bundle();
- args.putInt(ARG_TITLE_RES_ID, titleResId);
- args.putInt(ARG_MESSAGE_RES_ID, messageResId);
- fragment.setArguments(args);
- return fragment;
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mTitleResId = getArguments().getInt(ARG_TITLE_RES_ID);
- mMessageResId = getArguments().getInt(ARG_MESSAGE_RES_ID);
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
- if (mTitleResId != 0) {
- builder.setTitle(mTitleResId);
- }
- if (mMessageResId != 0) {
- builder.setMessage(mMessageResId);
- }
- builder.setPositiveButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dismiss();
- }
- });
- return builder.create();
- }
- }
-
- /**
- * In most cases, when the dial button is pressed, there is a
- * number in digits area. Pack it in the intent, start the
- * outgoing call broadcast as a separate task and finish this
- * activity.
- *
- * When there is no digit and the phone is CDMA and off hook,
- * we're sending a blank flash for CDMA. CDMA networks use Flash
- * messages when special processing needs to be done, mainly for
- * 3-way or call waiting scenarios. Presumably, here we're in a
- * special 3-way scenario where the network needs a blank flash
- * before being able to add the new participant. (This is not the
- * case with all 3-way calls, just certain CDMA infrastructures.)
- *
- * Otherwise, there is no digit, display the last dialed
- * number. Don't finish since the user may want to edit it. The
- * user needs to press the dial button again, to dial it (general
- * case described above).
- */
- public void dialButtonPressed() {
- if (isDigitsEmpty()) { // No number entered.
- handleDialButtonClickWithEmptyDigits();
- } else {
- final String number = mDigits.getText().toString();
-
- // "persist.radio.otaspdial" is a temporary hack needed for one carrier's automated
- // test equipment.
- // TODO: clean it up.
- if (number != null
- && !TextUtils.isEmpty(mProhibitedPhoneNumberRegexp)
- && number.matches(mProhibitedPhoneNumberRegexp)
- && (SystemProperties.getInt("persist.radio.otaspdial", 0) != 1)) {
- Log.i(TAG, "The phone number is prohibited explicitly by a rule.");
- if (getActivity() != null) {
- DialogFragment dialogFragment = ErrorDialogFragment.newInstance(
- R.string.dialog_phone_call_prohibited_message);
- dialogFragment.show(getFragmentManager(), "phone_prohibited_dialog");
- }
-
- // Clear the digits just in case.
- mDigits.getText().clear();
- } else {
- final Intent intent = CallUtil.getCallIntent(number,
- (getActivity() instanceof OldDialtactsActivity ?
- ((OldDialtactsActivity) getActivity()).getCallOrigin() : null));
- startActivity(intent);
- mClearDigitsOnStop = true;
- getActivity().finish();
- }
- }
- }
-
- private String getCallOrigin() {
- return (getActivity() instanceof OldDialtactsActivity) ?
- ((OldDialtactsActivity) getActivity()).getCallOrigin() : null;
- }
-
- private void handleDialButtonClickWithEmptyDigits() {
- if (phoneIsCdma() && phoneIsOffhook()) {
- // This is really CDMA specific. On GSM is it possible
- // to be off hook and wanted to add a 3rd party using
- // the redial feature.
- startActivity(newFlashIntent());
- } else {
- if (!TextUtils.isEmpty(mLastNumberDialed)) {
- // Recall the last number dialed.
- mDigits.setText(mLastNumberDialed);
-
- // ...and move the cursor to the end of the digits string,
- // so you'll be able to delete digits using the Delete
- // button (just as if you had typed the number manually.)
- //
- // Note we use mDigits.getText().length() here, not
- // mLastNumberDialed.length(), since the EditText widget now
- // contains a *formatted* version of mLastNumberDialed (due to
- // mTextWatcher) and its length may have changed.
- mDigits.setSelection(mDigits.getText().length());
- } else {
- // There's no "last number dialed" or the
- // background query is still running. There's
- // nothing useful for the Dial button to do in
- // this case. Note: with a soft dial button, this
- // can never happens since the dial button is
- // disabled under these conditons.
- playTone(ToneGenerator.TONE_PROP_NACK);
- }
- }
- }
-
- /**
- * Plays the specified tone for TONE_LENGTH_MS milliseconds.
- */
- private void playTone(int tone) {
- playTone(tone, TONE_LENGTH_MS);
- }
-
- /**
- * Play the specified tone for the specified milliseconds
- *
- * The tone is played locally, using the audio stream for phone calls.
- * Tones are played only if the "Audible touch tones" user preference
- * is checked, and are NOT played if the device is in silent mode.
- *
- * The tone length can be -1, meaning "keep playing the tone." If the caller does so, it should
- * call stopTone() afterward.
- *
- * @param tone a tone code from {@link ToneGenerator}
- * @param durationMs tone length.
- */
- private void playTone(int tone, int durationMs) {
- // if local tone playback is disabled, just return.
- if (!mDTMFToneEnabled) {
- return;
- }
-
- // Also do nothing if the phone is in silent mode.
- // We need to re-check the ringer mode for *every* playTone()
- // call, rather than keeping a local flag that's updated in
- // onResume(), since it's possible to toggle silent mode without
- // leaving the current activity (via the ENDCALL-longpress menu.)
- AudioManager audioManager =
- (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
- int ringerMode = audioManager.getRingerMode();
- if ((ringerMode == AudioManager.RINGER_MODE_SILENT)
- || (ringerMode == AudioManager.RINGER_MODE_VIBRATE)) {
- return;
- }
-
- synchronized (mToneGeneratorLock) {
- if (mToneGenerator == null) {
- Log.w(TAG, "playTone: mToneGenerator == null, tone: " + tone);
- return;
- }
-
- // Start the new tone (will stop any playing tone)
- mToneGenerator.startTone(tone, durationMs);
- }
- }
-
- /**
- * Stop the tone if it is played.
- */
- private void stopTone() {
- // if local tone playback is disabled, just return.
- if (!mDTMFToneEnabled) {
- return;
- }
- synchronized (mToneGeneratorLock) {
- if (mToneGenerator == null) {
- Log.w(TAG, "stopTone: mToneGenerator == null");
- return;
- }
- mToneGenerator.stopTone();
- }
- }
-
- /**
- * Brings up the "dialpad chooser" UI in place of the usual Dialer
- * elements (the textfield/button and the dialpad underneath).
- *
- * We show this UI if the user brings up the Dialer while a call is
- * already in progress, since there's a good chance we got here
- * accidentally (and the user really wanted the in-call dialpad instead).
- * So in this situation we display an intermediate UI that lets the user
- * explicitly choose between the in-call dialpad ("Use touch tone
- * keypad") and the regular Dialer ("Add call"). (Or, the option "Return
- * to call in progress" just goes back to the in-call UI with no dialpad
- * at all.)
- *
- * @param enabled If true, show the "dialpad chooser" instead
- * of the regular Dialer UI
- */
- private void showDialpadChooser(boolean enabled) {
- // Check if onCreateView() is already called by checking one of View objects.
- if (!isLayoutReady()) {
- return;
- }
-
- if (enabled) {
- // Log.i(TAG, "Showing dialpad chooser!");
- if (mDigitsContainer != null) {
- mDigitsContainer.setVisibility(View.GONE);
- } else {
- // mDigits is not enclosed by the container. Make the digits field itself gone.
- mDigits.setVisibility(View.GONE);
- }
- if (mDialpad != null) mDialpad.setVisibility(View.GONE);
- if (mDialButtonContainer != null) mDialButtonContainer.setVisibility(View.GONE);
-
- mDialpadChooser.setVisibility(View.VISIBLE);
-
- // Instantiate the DialpadChooserAdapter and hook it up to the
- // ListView. We do this only once.
- if (mDialpadChooserAdapter == null) {
- mDialpadChooserAdapter = new DialpadChooserAdapter(getActivity());
- }
- mDialpadChooser.setAdapter(mDialpadChooserAdapter);
- } else {
- // Log.i(TAG, "Displaying normal Dialer UI.");
- if (mDigitsContainer != null) {
- mDigitsContainer.setVisibility(View.VISIBLE);
- } else {
- mDigits.setVisibility(View.VISIBLE);
- }
- if (mDialpad != null) mDialpad.setVisibility(View.VISIBLE);
- if (mDialButtonContainer != null) mDialButtonContainer.setVisibility(View.VISIBLE);
- mDialpadChooser.setVisibility(View.GONE);
- }
- }
-
- /**
- * @return true if we're currently showing the "dialpad chooser" UI.
- */
- private boolean dialpadChooserVisible() {
- return mDialpadChooser.getVisibility() == View.VISIBLE;
- }
-
- /**
- * Simple list adapter, binding to an icon + text label
- * for each item in the "dialpad chooser" list.
- */
- private static class DialpadChooserAdapter extends BaseAdapter {
- private LayoutInflater mInflater;
-
- // Simple struct for a single "choice" item.
- static class ChoiceItem {
- String text;
- Bitmap icon;
- int id;
-
- public ChoiceItem(String s, Bitmap b, int i) {
- text = s;
- icon = b;
- id = i;
- }
- }
-
- // IDs for the possible "choices":
- static final int DIALPAD_CHOICE_USE_DTMF_DIALPAD = 101;
- static final int DIALPAD_CHOICE_RETURN_TO_CALL = 102;
- static final int DIALPAD_CHOICE_ADD_NEW_CALL = 103;
-
- private static final int NUM_ITEMS = 3;
- private ChoiceItem mChoiceItems[] = new ChoiceItem[NUM_ITEMS];
-
- public DialpadChooserAdapter(Context context) {
- // Cache the LayoutInflate to avoid asking for a new one each time.
- mInflater = LayoutInflater.from(context);
-
- // Initialize the possible choices.
- // TODO: could this be specified entirely in XML?
-
- // - "Use touch tone keypad"
- mChoiceItems[0] = new ChoiceItem(
- context.getString(R.string.dialer_useDtmfDialpad),
- BitmapFactory.decodeResource(context.getResources(),
- R.drawable.ic_dialer_fork_tt_keypad),
- DIALPAD_CHOICE_USE_DTMF_DIALPAD);
-
- // - "Return to call in progress"
- mChoiceItems[1] = new ChoiceItem(
- context.getString(R.string.dialer_returnToInCallScreen),
- BitmapFactory.decodeResource(context.getResources(),
- R.drawable.ic_dialer_fork_current_call),
- DIALPAD_CHOICE_RETURN_TO_CALL);
-
- // - "Add call"
- mChoiceItems[2] = new ChoiceItem(
- context.getString(R.string.dialer_addAnotherCall),
- BitmapFactory.decodeResource(context.getResources(),
- R.drawable.ic_dialer_fork_add_call),
- DIALPAD_CHOICE_ADD_NEW_CALL);
- }
-
- @Override
- public int getCount() {
- return NUM_ITEMS;
- }
-
- /**
- * Return the ChoiceItem for a given position.
- */
- @Override
- public Object getItem(int position) {
- return mChoiceItems[position];
- }
-
- /**
- * Return a unique ID for each possible choice.
- */
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- /**
- * Make a view for each row.
- */
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- // When convertView is non-null, we can reuse it (there's no need
- // to reinflate it.)
- if (convertView == null) {
- convertView = mInflater.inflate(R.layout.dialpad_chooser_list_item, null);
- }
-
- TextView text = (TextView) convertView.findViewById(R.id.text);
- text.setText(mChoiceItems[position].text);
-
- ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
- icon.setImageBitmap(mChoiceItems[position].icon);
-
- return convertView;
- }
- }
-
- /**
- * Handle clicks from the dialpad chooser.
- */
- @Override
- public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
- DialpadChooserAdapter.ChoiceItem item =
- (DialpadChooserAdapter.ChoiceItem) parent.getItemAtPosition(position);
- int itemId = item.id;
- switch (itemId) {
- case DialpadChooserAdapter.DIALPAD_CHOICE_USE_DTMF_DIALPAD:
- // Log.i(TAG, "DIALPAD_CHOICE_USE_DTMF_DIALPAD");
- // Fire off an intent to go back to the in-call UI
- // with the dialpad visible.
- returnToInCallScreen(true);
- break;
-
- case DialpadChooserAdapter.DIALPAD_CHOICE_RETURN_TO_CALL:
- // Log.i(TAG, "DIALPAD_CHOICE_RETURN_TO_CALL");
- // Fire off an intent to go back to the in-call UI
- // (with the dialpad hidden).
- returnToInCallScreen(false);
- break;
-
- case DialpadChooserAdapter.DIALPAD_CHOICE_ADD_NEW_CALL:
- // Log.i(TAG, "DIALPAD_CHOICE_ADD_NEW_CALL");
- // Ok, guess the user really did want to be here (in the
- // regular Dialer) after all. Bring back the normal Dialer UI.
- showDialpadChooser(false);
- break;
-
- default:
- Log.w(TAG, "onItemClick: unexpected itemId: " + itemId);
- break;
- }
- }
-
- /**
- * Returns to the in-call UI (where there's presumably a call in
- * progress) in response to the user selecting "use touch tone keypad"
- * or "return to call" from the dialpad chooser.
- */
- private void returnToInCallScreen(boolean showDialpad) {
- try {
- ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
- if (phone != null) phone.showCallScreenWithDialpad(showDialpad);
- } catch (RemoteException e) {
- Log.w(TAG, "phone.showCallScreenWithDialpad() failed", e);
- }
-
- // Finally, finish() ourselves so that we don't stay on the
- // activity stack.
- // Note that we do this whether or not the showCallScreenWithDialpad()
- // call above had any effect or not! (That call is a no-op if the
- // phone is idle, which can happen if the current call ends while
- // the dialpad chooser is up. In this case we can't show the
- // InCallScreen, and there's no point staying here in the Dialer,
- // so we just take the user back where he came from...)
- getActivity().finish();
- }
-
- /**
- * @return true if the phone is "in use", meaning that at least one line
- * is active (ie. off hook or ringing or dialing).
- */
- public boolean phoneIsInUse() {
- return getTelephonyManager().getCallState() != TelephonyManager.CALL_STATE_IDLE;
- }
-
- /**
- * @return true if the phone is a CDMA phone type
- */
- private boolean phoneIsCdma() {
- return getTelephonyManager().getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA;
- }
-
- /**
- * @return true if the phone state is OFFHOOK
- */
- private boolean phoneIsOffhook() {
- return getTelephonyManager().getCallState() == TelephonyManager.CALL_STATE_OFFHOOK;
- }
-
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- // R.id.menu_add_contacts already has an add to contact intent populated by setupMenuItems
- switch (item.getItemId()) {
- case R.id.menu_2s_pause:
- updateDialString(PAUSE);
- return true;
- case R.id.menu_add_wait:
- updateDialString(WAIT);
- return true;
- default:
- return false;
- }
- }
-
- /**
- * Updates the dial string (mDigits) after inserting a Pause character (,)
- * or Wait character (;).
- */
- private void updateDialString(char newDigit) {
- if(newDigit != WAIT && newDigit != PAUSE) {
- Log.wtf(TAG, "Not expected for anything other than PAUSE & WAIT");
- return;
- }
-
- int selectionStart;
- int selectionEnd;
-
- // SpannableStringBuilder editable_text = new SpannableStringBuilder(mDigits.getText());
- int anchor = mDigits.getSelectionStart();
- int point = mDigits.getSelectionEnd();
-
- selectionStart = Math.min(anchor, point);
- selectionEnd = Math.max(anchor, point);
-
- if (selectionStart == -1) {
- selectionStart = selectionEnd = mDigits.length();
- }
-
- Editable digits = mDigits.getText();
-
- if (canAddDigit(digits, selectionStart, selectionEnd, newDigit)) {
- digits.replace(selectionStart, selectionEnd, Character.toString(newDigit));
-
- if (selectionStart != selectionEnd) {
- // Unselect: back to a regular cursor, just pass the character inserted.
- mDigits.setSelection(selectionStart + 1);
- }
- }
- }
-
- /**
- * Update the enabledness of the "Dial" and "Backspace" buttons if applicable.
- */
- private void updateDialAndDeleteButtonEnabledState() {
- final boolean digitsNotEmpty = !isDigitsEmpty();
-
- if (mDialButton != null) {
- // On CDMA phones, if we're already on a call, we *always*
- // enable the Dial button (since you can press it without
- // entering any digits to send an empty flash.)
- if (phoneIsCdma() && phoneIsOffhook()) {
- mDialButton.setEnabled(true);
- } else {
- // Common case: GSM, or CDMA but not on a call.
- // Enable the Dial button if some digits have
- // been entered, or if there is a last dialed number
- // that could be redialed.
- mDialButton.setEnabled(digitsNotEmpty ||
- !TextUtils.isEmpty(mLastNumberDialed));
- }
- }
- mDelete.setEnabled(digitsNotEmpty);
- }
-
- /**
- * Check if voicemail is enabled/accessible.
- *
- * @return true if voicemail is enabled and accessibly. Note that this can be false
- * "temporarily" after the app boot.
- * @see TelephonyManager#getVoiceMailNumber()
- */
- private boolean isVoicemailAvailable() {
- try {
- return getTelephonyManager().getVoiceMailNumber() != null;
- } catch (SecurityException se) {
- // Possibly no READ_PHONE_STATE privilege.
- Log.w(TAG, "SecurityException is thrown. Maybe privilege isn't sufficient.");
- }
- return false;
- }
-
- /**
- * Returns true of the newDigit parameter can be added at the current selection
- * point, otherwise returns false.
- * Only prevents input of WAIT and PAUSE digits at an unsupported position.
- * Fails early if start == -1 or start is larger than end.
- */
- @VisibleForTesting
- /* package */ static boolean canAddDigit(CharSequence digits, int start, int end,
- char newDigit) {
- if(newDigit != WAIT && newDigit != PAUSE) {
- Log.wtf(TAG, "Should not be called for anything other than PAUSE & WAIT");
- return false;
- }
-
- // False if no selection, or selection is reversed (end < start)
- if (start == -1 || end < start) {
- return false;
- }
-
- // unsupported selection-out-of-bounds state
- if (start > digits.length() || end > digits.length()) return false;
-
- // Special digit cannot be the first digit
- if (start == 0) return false;
-
- if (newDigit == WAIT) {
- // preceding char is ';' (WAIT)
- if (digits.charAt(start - 1) == WAIT) return false;
-
- // next char is ';' (WAIT)
- if ((digits.length() > end) && (digits.charAt(end) == WAIT)) return false;
- }
-
- return true;
- }
-
- /**
- * @return true if the widget with the phone number digits is empty.
- */
- private boolean isDigitsEmpty() {
- return mDigits.length() == 0;
- }
-
- /**
- * Starts the asyn query to get the last dialed/outgoing
- * number. When the background query finishes, mLastNumberDialed
- * is set to the last dialed number or an empty string if none
- * exists yet.
- */
- private void queryLastOutgoingCall() {
- mLastNumberDialed = EMPTY_NUMBER;
- CallLogAsync.GetLastOutgoingCallArgs lastCallArgs =
- new CallLogAsync.GetLastOutgoingCallArgs(
- getActivity(),
- new CallLogAsync.OnLastOutgoingCallComplete() {
- @Override
- public void lastOutgoingCall(String number) {
- // TODO: Filter out emergency numbers if
- // the carrier does not want redial for
- // these.
- mLastNumberDialed = number;
- updateDialAndDeleteButtonEnabledState();
- }
- });
- mCallLog.getLastOutgoingCall(lastCallArgs);
- }
-
- private Intent newFlashIntent() {
- final Intent intent = CallUtil.getCallIntent(EMPTY_NUMBER);
- intent.putExtra(EXTRA_SEND_EMPTY_FLASH, true);
- return intent;
- }
-
- private void initializeSmartDialingState() {
- // Handle smart dialing related state
- // TODO krelease: This should probably be moved to somewhere more appropriate, maybe
- // into DialtactsActivity
- mDialerDatabaseHelper.startSmartDialUpdateThread();
- }
-
- @Override
- public void onHiddenChanged(boolean hidden) {
- super.onHiddenChanged(hidden);
- final DialtactsActivity activity = (DialtactsActivity) getActivity();
- if (activity == null) return;
- if (hidden) {
- activity.showSearchBar();
- } else {
- activity.hideSearchBar();
- }
- }
-}
diff --git a/src/com/android/dialer/dialpad/SmartDialController.java b/src/com/android/dialer/dialpad/SmartDialController.java
deleted file mode 100644
index 5ce993be1..000000000
--- a/src/com/android/dialer/dialpad/SmartDialController.java
+++ /dev/null
@@ -1,389 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.dialer.dialpad;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.text.Spannable;
-import android.text.SpannableString;
-import android.text.TextUtils;
-import android.text.style.ForegroundColorSpan;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.view.View.OnLongClickListener;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
-import android.view.animation.OvershootInterpolator;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.dialer.R;
-
-import com.google.common.collect.Lists;
-
-import java.util.List;
-
-/**
-* This class controls the display and animation logic behind the smart dialing suggestion strip.
-*
-* It allows a list of SmartDialEntries to be assigned to the suggestion strip via
-* {@link #setEntries}, and also animates the removal of old suggestions.
-*
-* To avoid creating new views every time new entries are assigned, references to 2 *
-* {@link #NUM_SUGGESTIONS} views are kept in {@link #mViews} and {@link #mViewOverlays}.
-*
-* {@code mViews} contains the active views that are currently being displayed to the user,
-* while {@code mViewOverlays} contains the views that are used as view overlays. The view
-* overlays are used to provide the illusion of the former suggestions fading out. These two
-* lists of views are rotated each time a new set of entries is assigned to achieve the appropriate
-* cross fade animations using the new {@link View#getOverlay()} API.
-*/
-public class SmartDialController {
- public static final String LOG_TAG = "SmartDial";
-
- /**
- * Handtuned interpolator used to achieve the bounce effect when suggestions slide up. It
- * uses a combination of a decelerate interpolator and overshoot interpolator to first
- * decelerate, and then overshoot its top bounds and bounce back to its final position.
- */
- private class DecelerateAndOvershootInterpolator implements Interpolator {
- private DecelerateInterpolator a;
- private OvershootInterpolator b;
-
- public DecelerateAndOvershootInterpolator() {
- a = new DecelerateInterpolator(1.5f);
- b = new OvershootInterpolator(1.3f);
- }
-
- @Override
- public float getInterpolation(float input) {
- if (input > 0.6) {
- return b.getInterpolation(input);
- } else {
- return a.getInterpolation(input);
- }
- }
-
- }
-
- private DecelerateAndOvershootInterpolator mDecelerateAndOvershootInterpolator =
- new DecelerateAndOvershootInterpolator();
- private AccelerateDecelerateInterpolator mAccelerateDecelerateInterpolator =
- new AccelerateDecelerateInterpolator();
-
- private List<SmartDialEntry> mEntries;
- private List<SmartDialEntry> mOldEntries;
-
- private final int mNameHighlightedTextColor;
- private final int mNumberHighlightedTextColor;
-
- private final LinearLayout mList;
- private final View mBackground;
-
- private final List<LinearLayout> mViewOverlays = Lists.newArrayList();
- private final List<LinearLayout> mViews = Lists.newArrayList();
-
- private static final int NUM_SUGGESTIONS = 3;
-
- private static final long ANIM_DURATION = 200;
-
- private static final float BACKGROUND_FADE_AMOUNT = 0.25f;
-
- Resources mResources;
-
- public SmartDialController(Context context, ViewGroup parent,
- OnClickListener shortClickListener, OnLongClickListener longClickListener) {
- final Resources res = context.getResources();
- mResources = res;
-
- mNameHighlightedTextColor = res.getColor(R.color.smartdial_name_highlighted_text_color);
- mNumberHighlightedTextColor = res.getColor(
- R.color.smartdial_number_highlighted_text_color);
-
- mList = (LinearLayout) parent.findViewById(R.id.dialpad_smartdial_list);
- mBackground = parent.findViewById(R.id.dialpad_smartdial_list_background);
-
- mEntries = Lists.newArrayList();
- for (int i = 0; i < NUM_SUGGESTIONS; i++) {
- mEntries.add(SmartDialEntry.NULL);
- }
-
- mOldEntries = mEntries;
-
- final LayoutInflater inflater = (LayoutInflater) context.getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
-
- for (int i = 0; i < NUM_SUGGESTIONS * 2; i++) {
- final LinearLayout view = (LinearLayout) inflater.inflate(
- R.layout.dialpad_smartdial_item, mList, false);
- view.setOnClickListener(shortClickListener);
- view.setOnLongClickListener(longClickListener);
- if (i < NUM_SUGGESTIONS) {
- mViews.add(view);
- } else {
- mViewOverlays.add(view);
- }
- // Add all the views to mList so that they can get measured properly for animation
- // purposes. Once setEntries is called they will be removed and added as appropriate.
- view.setEnabled(false);
- mList.addView(view);
- }
- }
-
- /** Remove all entries. */
- public void clear() {
- mOldEntries = mEntries;
- mEntries = Lists.newArrayList();
- for (int i = 0; i < NUM_SUGGESTIONS; i++) {
- mEntries.add(SmartDialEntry.NULL);
- }
- updateViews();
- }
-
- /** Set entries. At the end of this method {@link #mEntries} should contain exactly
- * {@link #NUM_SUGGESTIONS} entries.*/
- public void setEntries(List<SmartDialEntry> entries) {
- if (entries == null) throw new IllegalArgumentException();
- mOldEntries = mEntries;
- mEntries = entries;
-
- final int size = mEntries.size();
- if (size <= 1) {
- if (size == 0) {
- mEntries.add(SmartDialEntry.NULL);
- }
- // add a null entry to push the single entry into the middle
- mEntries.add(0, SmartDialEntry.NULL);
- } else if (size >= 2) {
- // swap the 1st and 2nd entries so that the highest confidence match goes into the
- // middle
- swap(0, 1);
- }
-
- while (mEntries.size() < NUM_SUGGESTIONS) {
- mEntries.add(SmartDialEntry.NULL);
- }
-
- updateViews();
- }
-
- /**
- * This method is called every time a new set of SmartDialEntries is to be assigned to the
- * suggestions view. The current set of active views are to be used as view overlays and
- * faded out, while the former view overlays are assigned the current entries, added to
- * {@link #mList} and faded into view.
- */
- private void updateViews() {
- // Remove all views from the root in preparation to swap the two sets of views
- mList.removeAllViews();
- try {
- mList.getOverlay().clear();
- } catch (NullPointerException e) {
- // Catch possible NPE b/8895794
- }
-
- // Used to track whether or not to animate the overlay. In the case where the suggestion
- // at position i will slide from the left or right, or if the suggestion at position i
- // has not changed, the overlay at i should be hidden immediately. Overlay animations are
- // set in a separate loop from the active views to avoid unnecessarily reanimating the same
- // overlay multiple times.
- boolean[] dontAnimateOverlay = new boolean[NUM_SUGGESTIONS];
- boolean noSuggestions = true;
-
- // At this point in time {@link #mViews} contains the former active views with old
- // suggestions that will be swapped out to serve as view overlays, while
- // {@link #mViewOverlays} contains the former overlays that will now serve as active
- // views.
- for (int i = 0; i < NUM_SUGGESTIONS; i++) {
- // Retrieve the former overlay to be used as the new active view
- final LinearLayout active = mViewOverlays.get(i);
- final SmartDialEntry item = mEntries.get(i);
-
- noSuggestions &= (item == SmartDialEntry.NULL);
-
- assignEntryToView(active, mEntries.get(i));
- final SmartDialEntry oldItem = mOldEntries.get(i);
- // The former active view will now be used as an overlay for the cross-fade effect
- final LinearLayout overlay = mViews.get(i);
- show(active);
- if (!containsSameContact(oldItem, item)) {
- // Determine what kind of animation to use for the new view
- if (i == 1) { // Middle suggestion
- if (containsSameContact(item, mOldEntries.get(0))) {
- // Suggestion went from the left to the middle, slide it left to right
- animateSlideFromLeft(active);
- dontAnimateOverlay[0] = true;
- } else if (containsSameContact(item, mOldEntries.get(2))) {
- // Suggestion sent from the right to the middle, slide it right to left
- animateSlideFromRight(active);
- dontAnimateOverlay[2] = true;
- } else {
- animateFadeInAndSlideUp(active);
- }
- } else { // Left/Right suggestion
- if (i == 2 && containsSameContact(item, mOldEntries.get(1))) {
- // Suggestion went from middle to the right, slide it left to right
- animateSlideFromLeft(active);
- dontAnimateOverlay[1] = true;
- } else if (i == 0 && containsSameContact(item, mOldEntries.get(1))) {
- // Suggestion went from middle to the left, slide it right to left
- animateSlideFromRight(active);
- dontAnimateOverlay[1] = true;
- } else {
- animateFadeInAndSlideUp(active);
- }
- }
- } else {
- // Since the same item is in the same spot, don't do any animations and just
- // show the new view.
- dontAnimateOverlay[i] = true;
- }
- mList.getOverlay().add(overlay);
- mList.addView(active);
- // Keep track of active views and view overlays
- mViews.set(i, active);
- mViewOverlays.set(i, overlay);
- }
-
- // Separate loop for overlay animations. At this point in time {@link #mViewOverlays}
- // contains the actual overlays.
- for (int i = 0; i < NUM_SUGGESTIONS; i++) {
- final LinearLayout overlay = mViewOverlays.get(i);
- if (!dontAnimateOverlay[i]) {
- animateFadeOutAndSlideDown(overlay);
- } else {
- hide(overlay);
- }
- }
-
- // Fade out the background to 25% opacity if there are suggestions. If there are no
- // suggestions, display the background as usual.
- mBackground.animate().withLayer().alpha(noSuggestions ? 1.0f : BACKGROUND_FADE_AMOUNT);
- }
-
- private void show(View view) {
- view.animate().cancel();
- view.setAlpha(1);
- view.setTranslationX(0);
- view.setTranslationY(0);
- }
-
- private void hide(View view) {
- view.animate().cancel();
- view.setAlpha(0);
- }
-
- private void animateFadeInAndSlideUp(View view) {
- view.animate().cancel();
- view.setAlpha(0.2f);
- view.setTranslationY(view.getHeight());
- view.animate().withLayer().alpha(1).translationY(0).setDuration(ANIM_DURATION).
- setInterpolator(mDecelerateAndOvershootInterpolator);
- }
-
- private void animateFadeOutAndSlideDown(View view) {
- view.animate().cancel();
- view.setAlpha(1);
- view.setTranslationY(0);
- view.animate().withLayer().alpha(0).translationY(view.getHeight()).setDuration(
- ANIM_DURATION).setInterpolator(mAccelerateDecelerateInterpolator);
- }
-
- private void animateSlideFromLeft(View view) {
- view.animate().cancel();
- view.setAlpha(1);
- view.setTranslationX(-1 * view.getWidth());
- view.animate().withLayer().translationX(0).setDuration(ANIM_DURATION).setInterpolator(
- mAccelerateDecelerateInterpolator);
- }
-
- private void animateSlideFromRight(View view) {
- view.animate().cancel();
- view.setAlpha(1);
- view.setTranslationX(view.getWidth());
- view.animate().withLayer().translationX(0).setDuration(ANIM_DURATION).setInterpolator(
- mAccelerateDecelerateInterpolator);
- }
-
- // Swaps the items in pos1 and pos2 of mEntries
- private void swap(int pos1, int pos2) {
- if (pos1 == pos2) {
- return;
- }
- final SmartDialEntry temp = mEntries.get(pos1);
- mEntries.set(pos1, mEntries.get(pos2));
- mEntries.set(pos2, temp);
- }
-
- // Returns whether two SmartDialEntries contain the same contact
- private boolean containsSameContact(SmartDialEntry x, SmartDialEntry y) {
- return x.contactUri.equals(y.contactUri);
- }
-
- // Sets the information within a SmartDialEntry to the provided view
- private void assignEntryToView(LinearLayout view, SmartDialEntry item) {
- final TextView nameView = (TextView) view.findViewById(R.id.contact_name);
-
- final TextView numberView = (TextView) view.findViewById(
- R.id.contact_number);
-
- if (item == SmartDialEntry.NULL) {
- // Clear the text in case the view was reused.
- nameView.setText("");
- numberView.setText("");
- view.setEnabled(false);
- return;
- }
-
- // Highlight the display name with the provided match positions
- if (!TextUtils.isEmpty(item.displayName)) {
- final SpannableString displayName = new SpannableString(item.displayName);
- for (final SmartDialMatchPosition p : item.matchPositions) {
- if (p.start < p.end) {
- if (p.end > displayName.length()) {
- p.end = displayName.length();
- }
- // Create a new ForegroundColorSpan for each section of the name to highlight,
- // otherwise multiple highlights won't work.
- displayName.setSpan(new ForegroundColorSpan(mNameHighlightedTextColor), p.start,
- p.end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- }
- nameView.setText(displayName);
- }
-
- // Highlight the phone number with the provided match positions
- if (!TextUtils.isEmpty(item.phoneNumber)) {
- final SmartDialMatchPosition p = item.phoneNumberMatchPosition;
- final SpannableString phoneNumber = new SpannableString(item.phoneNumber);
- if (p != null && p.start < p.end) {
- if (p.end > phoneNumber.length()) {
- p.end = phoneNumber.length();
- }
- phoneNumber.setSpan(new ForegroundColorSpan(mNumberHighlightedTextColor), p.start,
- p.end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- numberView.setText(phoneNumber);
- }
- view.setEnabled(true);
- view.setTag(item);
- }
-}
diff --git a/src/com/android/dialer/dialpad/SmartDialLoaderTask.java b/src/com/android/dialer/dialpad/SmartDialLoaderTask.java
index 71cbfa27d..38a04ad8f 100644
--- a/src/com/android/dialer/dialpad/SmartDialLoaderTask.java
+++ b/src/com/android/dialer/dialpad/SmartDialLoaderTask.java
@@ -16,8 +16,6 @@
package com.android.dialer.dialpad;
-import static com.android.dialer.dialpad.SmartDialController.LOG_TAG;
-
import android.content.Context;
import android.os.AsyncTask;
import android.provider.ContactsContract;
@@ -40,6 +38,7 @@ import java.util.List;
* callback function.
*/
public class SmartDialLoaderTask extends AsyncTask<String, Integer, List<SmartDialEntry>> {
+ private final String TAG = SmartDialLoaderTask.class.getSimpleName();
public interface SmartDialLoaderCallback {
void setSmartDialAdapterEntries(List<SmartDialEntry> list, String query);
@@ -101,7 +100,7 @@ public class SmartDialLoaderTask extends AsyncTask<String, Integer, List<SmartDi
));
}
if (DEBUG) {
- stopWatch.stopAndLog(LOG_TAG + " Match Complete", 0);
+ stopWatch.stopAndLog(TAG + " Match Complete", 0);
}
return candidates;
}
diff --git a/src/com/android/dialer/dialpad/SmartDialMatchPosition.java b/src/com/android/dialer/dialpad/SmartDialMatchPosition.java
index 452ac9110..bab2c50d8 100644
--- a/src/com/android/dialer/dialpad/SmartDialMatchPosition.java
+++ b/src/com/android/dialer/dialpad/SmartDialMatchPosition.java
@@ -16,8 +16,6 @@
package com.android.dialer.dialpad;
-import static com.android.dialer.dialpad.SmartDialController.LOG_TAG;
-
import android.util.Log;
import java.util.ArrayList;
@@ -25,10 +23,12 @@ import java.util.ArrayList;
/**
* Stores information about a range of characters matched in a display name The integers
* start and end indicate that the range start to end (exclusive) correspond to some characters
- * in the query. Used by {@link SmartDialController} to highlight certain parts of the contact's
- * display name to indicate that those ranges matched the user's query.
+ * in the query. Used to highlight certain parts of the contact's display name to indicate that
+ * those ranges matched the user's query.
*/
public class SmartDialMatchPosition {
+ private static final String TAG = SmartDialMatchPosition.class.getSimpleName();
+
public int start;
public int end;
@@ -64,7 +64,7 @@ public class SmartDialMatchPosition {
public static void print(ArrayList<SmartDialMatchPosition> list) {
for (int i = 0; i < list.size(); i ++) {
SmartDialMatchPosition m = list.get(i);
- Log.d(LOG_TAG, "[" + m.start + "," + m.end + "]");
+ Log.d(TAG, "[" + m.start + "," + m.end + "]");
}
}
}
diff --git a/src/com/android/dialer/list/PhoneFavoriteFragment.java b/src/com/android/dialer/list/PhoneFavoriteFragment.java
deleted file mode 100644
index 3b2f4d769..000000000
--- a/src/com/android/dialer/list/PhoneFavoriteFragment.java
+++ /dev/null
@@ -1,573 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.dialer.list;
-
-import android.app.Activity;
-import android.app.Fragment;
-import android.app.LoaderManager;
-import android.content.CursorLoader;
-import android.content.Intent;
-import android.content.Loader;
-import android.database.Cursor;
-import android.graphics.Rect;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.Directory;
-import android.provider.Settings;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.AbsListView;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.FrameLayout;
-import android.widget.ListView;
-import android.widget.TextView;
-
-import com.android.contacts.common.ContactPhotoManager;
-import com.android.contacts.common.ContactTileLoaderFactory;
-import com.android.contacts.common.dialog.ClearFrequentsDialog;
-import com.android.contacts.common.list.ContactListFilter;
-import com.android.contacts.common.list.ContactListFilterController;
-import com.android.contacts.common.list.ContactListItemView;
-import com.android.contacts.common.list.ContactTileAdapter;
-import com.android.contacts.common.list.ContactTileView;
-import com.android.contacts.common.list.PhoneNumberListAdapter;
-import com.android.contacts.common.preference.ContactsPreferences;
-import com.android.contacts.common.util.AccountFilterUtil;
-import com.android.contacts.common.interactions.ImportExportDialogFragment;
-import com.android.dialer.OldDialtactsActivity;
-import com.android.dialer.R;
-
-/**
- * Fragment for Phone UI's favorite screen.
- *
- * This fragment contains three kinds of contacts in one screen: "starred", "frequent", and "all"
- * contacts. To show them at once, this merges results from {@link com.android.contacts.common.list.ContactTileAdapter} and
- * {@link com.android.contacts.common.list.PhoneNumberListAdapter} into one unified list using {@link PhoneFavoriteMergedAdapter}.
- * A contact filter header is also inserted between those adapters' results.
- */
-public class PhoneFavoriteFragment extends Fragment implements OnItemClickListener {
- private static final String TAG = PhoneFavoriteFragment.class.getSimpleName();
- private static final boolean DEBUG = false;
-
- /**
- * Used with LoaderManager.
- */
- private static int LOADER_ID_CONTACT_TILE = 1;
- private static int LOADER_ID_ALL_CONTACTS = 2;
-
- private static final String KEY_FILTER = "filter";
-
- private static final int REQUEST_CODE_ACCOUNT_FILTER = 1;
-
- public interface Listener {
- public void onContactSelected(Uri contactUri);
- public void onCallNumberDirectly(String phoneNumber);
- }
-
- private class ContactTileLoaderListener implements LoaderManager.LoaderCallbacks<Cursor> {
- @Override
- public CursorLoader onCreateLoader(int id, Bundle args) {
- if (DEBUG) Log.d(TAG, "ContactTileLoaderListener#onCreateLoader.");
- return ContactTileLoaderFactory.createStrequentPhoneOnlyLoader(getActivity());
- }
-
- @Override
- public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
- if (DEBUG) Log.d(TAG, "ContactTileLoaderListener#onLoadFinished");
- mContactTileAdapter.setContactCursor(data);
-
- if (mAllContactsForceReload) {
- mAllContactsAdapter.onDataReload();
- // Use restartLoader() to make LoaderManager to load the section again.
- getLoaderManager().restartLoader(
- LOADER_ID_ALL_CONTACTS, null, mAllContactsLoaderListener);
- } else if (!mAllContactsLoaderStarted) {
- // Load "all" contacts if not loaded yet.
- getLoaderManager().initLoader(
- LOADER_ID_ALL_CONTACTS, null, mAllContactsLoaderListener);
- }
- mAllContactsForceReload = false;
- mAllContactsLoaderStarted = true;
-
- // Show the filter header with "loading" state.
- updateFilterHeaderView();
- mAccountFilterHeader.setVisibility(View.VISIBLE);
-
- // invalidate the options menu if needed
- invalidateOptionsMenuIfNeeded();
- }
-
- @Override
- public void onLoaderReset(Loader<Cursor> loader) {
- if (DEBUG) Log.d(TAG, "ContactTileLoaderListener#onLoaderReset. ");
- }
- }
-
- private class AllContactsLoaderListener implements LoaderManager.LoaderCallbacks<Cursor> {
- @Override
- public Loader<Cursor> onCreateLoader(int id, Bundle args) {
- if (DEBUG) Log.d(TAG, "AllContactsLoaderListener#onCreateLoader");
- CursorLoader loader = new CursorLoader(getActivity(), null, null, null, null, null);
- mAllContactsAdapter.configureLoader(loader, Directory.DEFAULT);
- return loader;
- }
-
- @Override
- public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
- if (DEBUG) Log.d(TAG, "AllContactsLoaderListener#onLoadFinished");
- mAllContactsAdapter.changeCursor(0, data);
- updateFilterHeaderView();
- mHandler.removeMessages(MESSAGE_SHOW_LOADING_EFFECT);
- mLoadingView.setVisibility(View.VISIBLE);
- }
-
- @Override
- public void onLoaderReset(Loader<Cursor> loader) {
- if (DEBUG) Log.d(TAG, "AllContactsLoaderListener#onLoaderReset. ");
- }
- }
-
- private class ContactTileAdapterListener implements ContactTileView.Listener {
- @Override
- public void onContactSelected(Uri contactUri, Rect targetRect) {
- if (mListener != null) {
- mListener.onContactSelected(contactUri);
- }
- }
-
- @Override
- public void onCallNumberDirectly(String phoneNumber) {
- if (mListener != null) {
- mListener.onCallNumberDirectly(phoneNumber);
- }
- }
-
- @Override
- public int getApproximateTileWidth() {
- return getView().getWidth() / mContactTileAdapter.getColumnCount();
- }
- }
-
- private class FilterHeaderClickListener implements OnClickListener {
- @Override
- public void onClick(View view) {
- AccountFilterUtil.startAccountFilterActivityForResult(
- PhoneFavoriteFragment.this,
- REQUEST_CODE_ACCOUNT_FILTER,
- mFilter);
- }
- }
-
- private class ContactsPreferenceChangeListener
- implements ContactsPreferences.ChangeListener {
- @Override
- public void onChange() {
- if (loadContactsPreferences()) {
- requestReloadAllContacts();
- }
- }
- }
-
- private class ScrollListener implements ListView.OnScrollListener {
- private boolean mShouldShowFastScroller;
- @Override
- public void onScroll(AbsListView view,
- int firstVisibleItem, int visibleItemCount, int totalItemCount) {
- // FastScroller should be visible only when the user is seeing "all" contacts section.
- final boolean shouldShow = mAdapter.shouldShowFirstScroller(firstVisibleItem);
- if (shouldShow != mShouldShowFastScroller) {
- mListView.setVerticalScrollBarEnabled(shouldShow);
- mListView.setFastScrollEnabled(shouldShow);
- mListView.setFastScrollAlwaysVisible(shouldShow);
- mShouldShowFastScroller = shouldShow;
- }
- }
-
- @Override
- public void onScrollStateChanged(AbsListView view, int scrollState) {
- }
- }
-
- private static final int MESSAGE_SHOW_LOADING_EFFECT = 1;
- private static final int LOADING_EFFECT_DELAY = 500; // ms
- private final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MESSAGE_SHOW_LOADING_EFFECT:
- mLoadingView.setVisibility(View.VISIBLE);
- break;
- }
- }
- };
-
- private Listener mListener;
- private PhoneFavoriteMergedAdapter mAdapter;
- private ContactTileAdapter mContactTileAdapter;
- private PhoneNumberListAdapter mAllContactsAdapter;
-
- /**
- * true when the loader for {@link PhoneNumberListAdapter} has started already.
- */
- private boolean mAllContactsLoaderStarted;
- /**
- * true when the loader for {@link PhoneNumberListAdapter} must reload "all" contacts again.
- * It typically happens when {@link ContactsPreferences} has changed its settings
- * (display order and sort order)
- */
- private boolean mAllContactsForceReload;
-
- private ContactsPreferences mContactsPrefs;
- private ContactListFilter mFilter;
-
- private TextView mEmptyView;
- private ListView mListView;
- /**
- * Layout containing {@link #mAccountFilterHeader}. Used to limit area being "pressed".
- */
- private FrameLayout mAccountFilterHeaderContainer;
- private View mAccountFilterHeader;
-
- /**
- * Layout used when contacts load is slower than expected and thus "loading" view should be
- * shown.
- */
- private View mLoadingView;
-
- private final ContactTileView.Listener mContactTileAdapterListener =
- new ContactTileAdapterListener();
- private final LoaderManager.LoaderCallbacks<Cursor> mContactTileLoaderListener =
- new ContactTileLoaderListener();
- private final LoaderManager.LoaderCallbacks<Cursor> mAllContactsLoaderListener =
- new AllContactsLoaderListener();
- private final OnClickListener mFilterHeaderClickListener = new FilterHeaderClickListener();
- private final ContactsPreferenceChangeListener mContactsPreferenceChangeListener =
- new ContactsPreferenceChangeListener();
- private final ScrollListener mScrollListener = new ScrollListener();
-
- private boolean mOptionsMenuHasFrequents;
-
- @Override
- public void onAttach(Activity activity) {
- if (DEBUG) Log.d(TAG, "onAttach()");
- super.onAttach(activity);
-
- mContactsPrefs = new ContactsPreferences(activity);
-
- // Construct two base adapters which will become part of PhoneFavoriteMergedAdapter.
- // We don't construct the resultant adapter at this moment since it requires LayoutInflater
- // that will be available on onCreateView().
-
- mContactTileAdapter = new ContactTileAdapter(activity, mContactTileAdapterListener,
- getResources().getInteger(R.integer.contact_tile_column_count_in_favorites),
- ContactTileAdapter.DisplayType.STREQUENT_PHONE_ONLY);
- mContactTileAdapter.setPhotoLoader(ContactPhotoManager.getInstance(activity));
-
- // Setup the "all" adapter manually. See also the setup logic in ContactEntryListFragment.
- mAllContactsAdapter = new PhoneNumberListAdapter(activity);
- mAllContactsAdapter.setDisplayPhotos(true);
- mAllContactsAdapter.setQuickContactEnabled(true);
- mAllContactsAdapter.setSearchMode(false);
- mAllContactsAdapter.setIncludeProfile(false);
- mAllContactsAdapter.setSelectionVisible(false);
- mAllContactsAdapter.setDarkTheme(true);
- mAllContactsAdapter.setPhotoLoader(ContactPhotoManager.getInstance(activity));
- // Disable directory header.
- mAllContactsAdapter.setHasHeader(0, false);
- // Show A-Z section index.
- mAllContactsAdapter.setSectionHeaderDisplayEnabled(true);
- // Disable pinned header. It doesn't work with this fragment.
- mAllContactsAdapter.setPinnedPartitionHeadersEnabled(false);
- // Put photos on START (LEFT in LTR layout direction and RIGHT in RTL layout direction)
- // for consistency with "frequent" contacts section.
- mAllContactsAdapter.setPhotoPosition(ContactListItemView.getDefaultPhotoPosition(
- true /* opposite */ ));
-
- // Use Callable.CONTENT_URI which will include not only phone numbers but also SIP
- // addresses.
- mAllContactsAdapter.setUseCallableUri(true);
-
- mAllContactsAdapter.setContactNameDisplayOrder(mContactsPrefs.getDisplayOrder());
- mAllContactsAdapter.setSortOrder(mContactsPrefs.getSortOrder());
- }
-
- @Override
- public void onCreate(Bundle savedState) {
- if (DEBUG) Log.d(TAG, "onCreate()");
- super.onCreate(savedState);
- if (savedState != null) {
- mFilter = savedState.getParcelable(KEY_FILTER);
-
- if (mFilter != null) {
- mAllContactsAdapter.setFilter(mFilter);
- }
- }
- setHasOptionsMenu(true);
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putParcelable(KEY_FILTER, mFilter);
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- final View listLayout = inflater.inflate(
- R.layout.phone_contact_tile_list, container, false);
-
- mListView = (ListView) listLayout.findViewById(R.id.contact_tile_list);
- mListView.setItemsCanFocus(true);
- mListView.setOnItemClickListener(this);
- mListView.setVerticalScrollBarEnabled(false);
- mListView.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_RIGHT);
- mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
-
- // Create the account filter header but keep it hidden until "all" contacts are loaded.
- mAccountFilterHeaderContainer = new FrameLayout(getActivity(), null);
- mAccountFilterHeader = inflater.inflate(R.layout.account_filter_header_for_phone_favorite,
- mListView, false);
- mAccountFilterHeader.setOnClickListener(mFilterHeaderClickListener);
- mAccountFilterHeaderContainer.addView(mAccountFilterHeader);
-
- mLoadingView = inflater.inflate(R.layout.phone_loading_contacts, mListView, false);
-
- mAdapter = new PhoneFavoriteMergedAdapter(getActivity(),
- mContactTileAdapter, mAccountFilterHeaderContainer, mAllContactsAdapter,
- mLoadingView);
-
- mListView.setAdapter(mAdapter);
-
- mListView.setOnScrollListener(mScrollListener);
- mListView.setFastScrollEnabled(false);
- mListView.setFastScrollAlwaysVisible(false);
-
- mEmptyView = (TextView) listLayout.findViewById(R.id.contact_tile_list_empty);
- mEmptyView.setText(getString(R.string.listTotalAllContactsZero));
- mListView.setEmptyView(mEmptyView);
-
- updateFilterHeaderView();
-
- return listLayout;
- }
-
- private boolean isOptionsMenuChanged() {
- return mOptionsMenuHasFrequents != hasFrequents();
- }
-
- private void invalidateOptionsMenuIfNeeded() {
- if (isOptionsMenuChanged()) {
- getActivity().invalidateOptionsMenu();
- }
- }
-
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- super.onCreateOptionsMenu(menu, inflater);
- inflater.inflate(R.menu.phone_favorite_options, menu);
- }
-
- @Override
- public void onPrepareOptionsMenu(Menu menu) {
- final MenuItem clearFrequents = menu.findItem(R.id.menu_clear_frequents);
- mOptionsMenuHasFrequents = hasFrequents();
- clearFrequents.setVisible(mOptionsMenuHasFrequents);
- }
-
- private boolean hasFrequents() {
- return mContactTileAdapter.getNumFrequents() > 0;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.menu_import_export:
- // We hard-code the "contactsAreAvailable" argument because doing it properly would
- // involve querying a {@link ProviderStatusLoader}, which we don't want to do right
- // now in Dialtacts for (potential) performance reasons. Compare with how it is
- // done in {@link PeopleActivity}.
- ImportExportDialogFragment.show(getFragmentManager(), true,
- OldDialtactsActivity.class);
- return true;
- case R.id.menu_accounts:
- final Intent intent = new Intent(Settings.ACTION_SYNC_SETTINGS);
- intent.putExtra(Settings.EXTRA_AUTHORITIES, new String[] {
- ContactsContract.AUTHORITY
- });
- intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
- startActivity(intent);
- return true;
- case R.id.menu_clear_frequents:
- ClearFrequentsDialog.show(getFragmentManager());
- return true;
- }
- return false;
- }
-
- @Override
- public void onStart() {
- super.onStart();
-
- mContactsPrefs.registerChangeListener(mContactsPreferenceChangeListener);
-
- // If ContactsPreferences has changed, we need to reload "all" contacts with the new
- // settings. If mAllContactsFoarceReload is already true, it should be kept.
- if (loadContactsPreferences()) {
- mAllContactsForceReload = true;
- }
-
- // Use initLoader() instead of restartLoader() to refraining unnecessary reload.
- // This method call implicitly assures ContactTileLoaderListener's onLoadFinished() will
- // be called, on which we'll check if "all" contacts should be reloaded again or not.
- getLoaderManager().initLoader(LOADER_ID_CONTACT_TILE, null, mContactTileLoaderListener);
-
- // Delay showing "loading" view until certain amount of time so that users won't see
- // instant flash of the view when the contacts load is fast enough.
- // This will be kept shown until both tile and all sections are loaded.
- mLoadingView.setVisibility(View.INVISIBLE);
- mHandler.sendEmptyMessageDelayed(MESSAGE_SHOW_LOADING_EFFECT, LOADING_EFFECT_DELAY);
- }
-
- @Override
- public void onStop() {
- super.onStop();
- mContactsPrefs.unregisterChangeListener();
- }
-
- /**
- * {@inheritDoc}
- *
- * This is only effective for elements provided by {@link #mContactTileAdapter}.
- * {@link #mContactTileAdapter} has its own logic for click events.
- */
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- final int contactTileAdapterCount = mContactTileAdapter.getCount();
- if (position <= contactTileAdapterCount) {
- Log.e(TAG, "onItemClick() event for unexpected position. "
- + "The position " + position + " is before \"all\" section. Ignored.");
- } else {
- final int localPosition = position - mContactTileAdapter.getCount() - 1;
- if (mListener != null) {
- mListener.onContactSelected(mAllContactsAdapter.getDataUri(localPosition));
- }
- }
- }
-
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (requestCode == REQUEST_CODE_ACCOUNT_FILTER) {
- if (getActivity() != null) {
- AccountFilterUtil.handleAccountFilterResult(
- ContactListFilterController.getInstance(getActivity()), resultCode, data);
- } else {
- Log.e(TAG, "getActivity() returns null during Fragment#onActivityResult()");
- }
- }
- }
-
- private boolean loadContactsPreferences() {
- if (mContactsPrefs == null || mAllContactsAdapter == null) {
- return false;
- }
-
- boolean changed = false;
- final int currentDisplayOrder = mContactsPrefs.getDisplayOrder();
- if (mAllContactsAdapter.getContactNameDisplayOrder() != currentDisplayOrder) {
- mAllContactsAdapter.setContactNameDisplayOrder(currentDisplayOrder);
- changed = true;
- }
-
- final int currentSortOrder = mContactsPrefs.getSortOrder();
- if (mAllContactsAdapter.getSortOrder() != currentSortOrder) {
- mAllContactsAdapter.setSortOrder(currentSortOrder);
- changed = true;
- }
-
- return changed;
- }
-
- /**
- * Requests to reload "all" contacts. If the section is already loaded, this method will
- * force reloading it now. If the section isn't loaded yet, the actual load may be done later
- * (on {@link #onStart()}.
- */
- private void requestReloadAllContacts() {
- if (DEBUG) {
- Log.d(TAG, "requestReloadAllContacts()"
- + " mAllContactsAdapter: " + mAllContactsAdapter
- + ", mAllContactsLoaderStarted: " + mAllContactsLoaderStarted);
- }
-
- if (mAllContactsAdapter == null || !mAllContactsLoaderStarted) {
- // Remember this request until next load on onStart().
- mAllContactsForceReload = true;
- return;
- }
-
- if (DEBUG) Log.d(TAG, "Reload \"all\" contacts now.");
-
- mAllContactsAdapter.onDataReload();
- // Use restartLoader() to make LoaderManager to load the section again.
- getLoaderManager().restartLoader(LOADER_ID_ALL_CONTACTS, null, mAllContactsLoaderListener);
- }
-
- private void updateFilterHeaderView() {
- final ContactListFilter filter = getFilter();
- if (mAccountFilterHeader == null || mAllContactsAdapter == null || filter == null) {
- return;
- }
- AccountFilterUtil.updateAccountFilterTitleForPhone(mAccountFilterHeader, filter, true);
- }
-
- public ContactListFilter getFilter() {
- return mFilter;
- }
-
- public void setFilter(ContactListFilter filter) {
- if ((mFilter == null && filter == null) || (mFilter != null && mFilter.equals(filter))) {
- return;
- }
-
- if (DEBUG) {
- Log.d(TAG, "setFilter(). old filter (" + mFilter
- + ") will be replaced with new filter (" + filter + ")");
- }
-
- mFilter = filter;
-
- if (mAllContactsAdapter != null) {
- mAllContactsAdapter.setFilter(mFilter);
- requestReloadAllContacts();
- updateFilterHeaderView();
- }
- }
-
- public void setListener(Listener listener) {
- mListener = listener;
- }
-}