summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/com/android/dialer/NeededForReflection.java30
-rw-r--r--src/com/android/dialer/NewDialtactsActivity.java1168
-rw-r--r--src/com/android/dialer/NewSearchFragment.java75
-rw-r--r--src/com/android/dialer/calllog/CallLogAdapter.java2
-rw-r--r--src/com/android/dialer/calllog/CallLogQueryHandler.java13
-rw-r--r--src/com/android/dialer/calllog/IntentProvider.java3
-rw-r--r--src/com/android/dialer/calllog/NewCallLogActivity.java189
-rw-r--r--src/com/android/dialer/calllog/NewCallLogAdapter.java23
-rw-r--r--src/com/android/dialer/calllog/NewCallLogFragment.java179
-rw-r--r--src/com/android/dialer/calllog/NewCallLogListItemHelper.java9
-rw-r--r--src/com/android/dialer/database/DialerDatabaseHelper.java135
-rw-r--r--src/com/android/dialer/dialpad/NewDialpadFragment.java238
-rw-r--r--src/com/android/dialer/dialpad/SmartDialCursorLoader.java182
-rw-r--r--src/com/android/dialer/dialpad/SmartDialMatchPosition.java2
-rw-r--r--src/com/android/dialer/dialpad/SmartDialNameMatcher.java71
-rw-r--r--src/com/android/dialer/list/NewPhoneFavoriteFragment.java204
-rw-r--r--src/com/android/dialer/list/NewPhoneFavoriteMergedAdapter.java133
-rw-r--r--src/com/android/dialer/list/OnListFragmentScrolledListener.java24
-rw-r--r--src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java2
-rw-r--r--src/com/android/dialer/list/PhoneFavoriteRegularRowView.java82
-rw-r--r--src/com/android/dialer/list/PhoneFavoriteTileView.java65
-rw-r--r--src/com/android/dialer/list/PhoneFavoritesTileAdapter.java515
-rw-r--r--src/com/android/dialer/list/SmartDialNumberListAdapter.java114
-rw-r--r--src/com/android/dialer/list/SmartDialNumberPickerFragment.java76
-rw-r--r--src/com/android/dialer/list/SmartDialSearchFragment.java120
25 files changed, 2273 insertions, 1381 deletions
diff --git a/src/com/android/dialer/NeededForReflection.java b/src/com/android/dialer/NeededForReflection.java
new file mode 100644
index 000000000..e836908b1
--- /dev/null
+++ b/src/com/android/dialer/NeededForReflection.java
@@ -0,0 +1,30 @@
+/*
+ * 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;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that the class, constructor, method or field is used for reflection and therefore cannot
+ * be removed by tools like ProGuard.
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target({ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.FIELD})
+public @interface NeededForReflection{}
diff --git a/src/com/android/dialer/NewDialtactsActivity.java b/src/com/android/dialer/NewDialtactsActivity.java
index 584c951b3..66cbe8530 100644
--- a/src/com/android/dialer/NewDialtactsActivity.java
+++ b/src/com/android/dialer/NewDialtactsActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2013 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.
@@ -16,10 +16,9 @@
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.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.AnimatorListenerAdapter;
import android.app.Activity;
import android.app.backup.BackupManager;
import android.app.Fragment;
@@ -33,26 +32,20 @@ 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;
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.provider.Settings;
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.AbsListView.OnScrollListener;
import android.widget.PopupMenu;
import android.widget.SearchView;
import android.widget.SearchView.OnCloseListener;
@@ -61,27 +54,30 @@ 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.dialog.ClearFrequentsDialog;
+import com.android.contacts.common.interactions.ImportExportDialogFragment;
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.calllog.NewCallLogActivity;
+import com.android.dialer.dialpad.NewDialpadFragment;
+import com.android.dialer.dialpad.SmartDialNameMatcher;
import com.android.dialer.interactions.PhoneNumberInteraction;
-import com.android.dialer.list.PhoneFavoriteFragment;
-import com.android.dialer.util.OrientationUtil;
+import com.android.dialer.list.NewPhoneFavoriteFragment;
+import com.android.dialer.list.OnListFragmentScrolledListener;
+import com.android.dialer.list.SmartDialSearchFragment;
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).
+ *
+ * TODO krelease: All classes currently prefixed with New will replace the original classes or
+ * be renamed more appropriately before shipping.
*/
-public class NewDialtactsActivity extends TransactionSafeActivity implements View.OnClickListener {
+public class NewDialtactsActivity extends TransactionSafeActivity implements View.OnClickListener,
+ NewDialpadFragment.OnDialpadQueryChangedListener, PopupMenu.OnMenuItemClickListener,
+ OnListFragmentScrolledListener,
+ NewPhoneFavoriteFragment.OnPhoneFavoriteFragmentStartedListener {
private static final String TAG = "DialtactsActivity";
public static final boolean DEBUG = false;
@@ -95,18 +91,16 @@ public class NewDialtactsActivity extends TransactionSafeActivity implements Vie
private static final String CALL_ORIGIN_DIALTACTS =
"com.android.dialer.DialtactsActivity";
+ private static final String TAG_DIALPAD_FRAGMENT = "dialpad";
+ private static final String TAG_REGULAR_SEARCH_FRAGMENT = "search";
+ private static final String TAG_SMARTDIAL_SEARCH_FRAGMENT = "smartdial";
+ private static final String TAG_FAVORITES_FRAGMENT = "favorites";
+
/**
* 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";
@@ -114,291 +108,51 @@ public class NewDialtactsActivity extends TransactionSafeActivity implements Vie
/** 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;
- }
- }
+ private String mFilterText;
/**
- * 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.
+ * The main fragment displaying the user's favorites and frequent contacts
*/
- boolean mDuringSwipe = false;
+ private NewPhoneFavoriteFragment mPhoneFavoriteFragment;
+
/**
- * 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.
+ * Fragment containing the dialpad that slides into view
*/
- 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();
+ private NewDialpadFragment mDialpadFragment;
- 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;
+ /**
+ * Fragment for searching phone numbers using the alphanumeric keyboard.
+ */
+ private NewSearchFragment mRegularSearchFragment;
- /** Enables horizontal swipe between Fragments. */
- private ViewPager mViewPager;
- private final PageChangeListener mPageChangeListener = new PageChangeListener();
- private DialpadFragment mDialpadFragment;
- private CallLogFragment mCallLogFragment;
- private PhoneFavoriteFragment mPhoneFavoriteFragment;
+ /**
+ * Fragment for searching phone numbers using the dialpad.
+ */
+ private SmartDialSearchFragment mSmartDialSearchFragment;
- private View mSearchButton;
private View mMenuButton;
+ private View mCallHistoryButton;
+ private View mDialpadButton;
- 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");
- }
- };
+ // Padding view used to shift the fragments up when the dialpad is shown.
+ private View mBottomPaddingView;
/**
- * 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(NewDialtactsActivity.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(
- NewDialtactsActivity.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.
@@ -410,7 +164,7 @@ public class NewDialtactsActivity extends TransactionSafeActivity implements Vie
// Specify call-origin so that users will see the previous tab instead of
// CallLog screen (search UI will be automatically exited).
PhoneNumberInteraction.startInteractionForPhoneCall(
- NewDialtactsActivity.this, dataUri, getCallOrigin());
+ NewDialtactsActivity.this, dataUri, getCallOrigin());
}
@Override
@@ -441,14 +195,29 @@ public class NewDialtactsActivity extends TransactionSafeActivity implements Vie
@Override
public boolean onQueryTextChange(String newText) {
+ final boolean smartDialSearch = isDialpadShowing();
+
// Show search result with non-empty text. Show a bare list otherwise.
- if (mSearchFragment != null) {
- mSearchFragment.setQueryString(newText, true);
+ if (TextUtils.isEmpty(newText) && mInSearchUi) {
+ exitSearchUi();
+ return true;
+ } else if (!TextUtils.isEmpty(newText) && !mInSearchUi) {
+ enterSearchUi(smartDialSearch);
+ }
+
+ if (isDialpadShowing()) {
+ mSmartDialSearchFragment.setQueryString(newText, false);
+ } else {
+ mRegularSearchFragment.setQueryString(newText, false);
}
return true;
}
};
+ private boolean isDialpadShowing() {
+ return mDialpadFragment.isVisible();
+ }
+
/**
* 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
@@ -467,135 +236,132 @@ public class NewDialtactsActivity extends TransactionSafeActivity implements Vie
}
};
- 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);
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
final Intent intent = getIntent();
fixIntent(intent);
- setContentView(R.layout.dialtacts_activity);
-
- mContactListFilterController = ContactListFilterController.getInstance(this);
- mContactListFilterController.addListener(mContactListFilterListener);
+ setContentView(R.layout.new_dialtacts_activity);
- findViewById(R.id.dialtacts_frame).addOnLayoutChangeListener(mFirstLayoutListener);
+ getActionBar().hide();
- mViewPager = (ViewPager) findViewById(R.id.pager);
- mViewPager.setAdapter(new ViewPagerAdapter(getFragmentManager()));
- mViewPager.setOnPageChangeListener(mPageChangeListener);
- mViewPager.setOffscreenPageLimit(2);
+ mPhoneFavoriteFragment = new NewPhoneFavoriteFragment();
+ mPhoneFavoriteFragment.setListener(mPhoneFavoriteListener);
- // 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);
+ mRegularSearchFragment = new NewSearchFragment();
+ mSmartDialSearchFragment = new SmartDialSearchFragment();
+ mDialpadFragment = new NewDialpadFragment();
- // 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);
+ // TODO krelease: load fragments on demand instead of creating all of them at run time
+ final FragmentTransaction ft = getFragmentManager().beginTransaction();
+ ft.add(R.id.dialtacts_frame, mPhoneFavoriteFragment, TAG_FAVORITES_FRAGMENT);
+ ft.add(R.id.dialtacts_frame, mRegularSearchFragment, TAG_REGULAR_SEARCH_FRAGMENT);
+ ft.add(R.id.dialtacts_frame, mSmartDialSearchFragment, TAG_SMARTDIAL_SEARCH_FRAGMENT);
+ ft.add(R.id.dialtacts_container, mDialpadFragment, TAG_DIALPAD_FRAGMENT);
+ ft.hide(mRegularSearchFragment);
+ ft.hide(mDialpadFragment);
+ ft.hide(mSmartDialSearchFragment);
+ ft.commit();
+
+ mBottomPaddingView = findViewById(R.id.dialtacts_bottom_padding);
+ prepareSearchView();
// 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);
+ /*
+ * TODO krelease : Remember which fragment was last displayed, and then redisplay it as
+ * necessary. 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; }
+ */
if (UI.FILTER_CONTACTS_ACTION.equals(intent.getAction())
- && icicle == null) {
+ && savedInstanceState == 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;
+ protected void onResume() {
+ super.onResume();
+ final FragmentManager fm = getFragmentManager();
+ mPhoneFavoriteFragment = (NewPhoneFavoriteFragment) fm.findFragmentByTag(
+ TAG_FAVORITES_FRAGMENT);
+ mDialpadFragment = (NewDialpadFragment) fm.findFragmentByTag(TAG_DIALPAD_FRAGMENT);
+
+ mRegularSearchFragment = (NewSearchFragment) fm.findFragmentByTag(
+ TAG_REGULAR_SEARCH_FRAGMENT);
+ mRegularSearchFragment.setOnPhoneNumberPickerActionListener(
+ mPhoneNumberPickerActionListener);
+ if (!mRegularSearchFragment.isHidden()) {
+ final FragmentTransaction transaction = getFragmentManager().beginTransaction();
+ transaction.hide(mRegularSearchFragment);
+ transaction.commit();
}
- 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);
+ mSmartDialSearchFragment = (SmartDialSearchFragment) fm.findFragmentByTag(
+ TAG_SMARTDIAL_SEARCH_FRAGMENT);
+ mSmartDialSearchFragment.setOnPhoneNumberPickerActionListener(
+ mPhoneNumberPickerActionListener);
+ if (!mSmartDialSearchFragment.isHidden()) {
+ final FragmentTransaction transaction = getFragmentManager().beginTransaction();
+ transaction.hide(mSmartDialSearchFragment);
+ transaction.commit();
}
}
@Override
- public void onDestroy() {
- super.onDestroy();
- mContactListFilterController.removeListener(mContactListFilterListener);
+ public boolean onMenuItemClick(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,
+ DialtactsActivity.class);
+ return true;
+ case R.id.menu_clear_frequents:
+ ClearFrequentsDialog.show(getFragmentManager());
+ return true;
+ 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;
+ case R.id.menu_call_settings:
+ final Intent settingsIntent = DialtactsActivity.getCallSettingsIntent();
+ startActivity(settingsIntent);
+ }
+ return false;
}
@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);
- }
+ final PopupMenu popupMenu = new PopupMenu(NewDialtactsActivity.this, view);
+ final Menu menu = popupMenu.getMenu();
+ popupMenu.inflate(R.menu.dialtacts_options_new);
+ popupMenu.setOnMenuItemClickListener(this);
+ popupMenu.show();
break;
}
+ case R.id.dialpad_button:
+ showDialpadFragment();
+ break;
+ case R.id.call_history_button:
+ final Intent intent = new Intent(this, NewCallLogActivity.class);
+ startActivity(intent);
+ break;
default: {
Log.wtf(TAG, "Unexpected onClick event from " + view);
break;
@@ -603,33 +369,22 @@ public class NewDialtactsActivity extends TransactionSafeActivity implements Vie
}
}
- /**
- * 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;
-
+ private void showDialpadFragment() {
final FragmentTransaction ft = getFragmentManager().beginTransaction();
- final Fragment searchFragment = new PhoneNumberPickerFragment();
+ ft.setCustomAnimations(R.anim.slide_in, 0);
+ ft.show(mDialpadFragment);
+ ft.commit();
+ }
- searchFragment.setUserVisibleHint(false);
- ft.add(R.id.dialtacts_frame, searchFragment);
- ft.hide(searchFragment);
- ft.commitAllowingStateLoss();
+ private void hideDialpadFragment() {
+ final FragmentTransaction ft = getFragmentManager().beginTransaction();
+ ft.setCustomAnimations(0, R.anim.slide_out);
+ ft.hide(mDialpadFragment);
+ ft.commit();
}
private void prepareSearchView() {
- final View searchViewLayout =
- getLayoutInflater().inflate(R.layout.dialtacts_custom_action_bar, null);
- mSearchView = (SearchView) searchViewLayout.findViewById(R.id.search_view);
+ mSearchView = (SearchView) 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
@@ -639,8 +394,9 @@ public class NewDialtactsActivity extends TransactionSafeActivity implements Vie
// - 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.setQueryHint(getString(R.string.dialer_hint_find_contact));
mSearchView.setIconified(false);
+ mSearchView.clearFocus();
mSearchView.setOnQueryTextFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(View view, boolean hasFocus) {
@@ -649,69 +405,87 @@ public class NewDialtactsActivity extends TransactionSafeActivity implements Vie
}
}
});
+ }
+
+ private void hideDialpadFragmentIfNecessary() {
+ if (mDialpadFragment.isVisible()) {
+ hideDialpadFragment();
+ }
+ }
- 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);
+ final AnimatorListener mHideListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mSearchView.setVisibility(View.GONE);
}
+ };
+
+ public void hideSearchBar() {
+ mSearchView.animate().cancel();
+ mSearchView.setAlpha(1);
+ mSearchView.setTranslationY(0);
+ mSearchView.animate().withLayer().alpha(0).translationY(-mSearchView.getHeight()).
+ setDuration(200).setListener(mHideListener);
+
+ mPhoneFavoriteFragment.getView().animate().withLayer()
+ .translationY(-mSearchView.getHeight()).setDuration(200).setListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mBottomPaddingView.setVisibility(View.VISIBLE);
+ mPhoneFavoriteFragment.getView().setTranslationY(0);
+ }
+ });
+ }
+
+ public void showSearchBar() {
+ mSearchView.animate().cancel();
+ mSearchView.setAlpha(0);
+ mSearchView.setTranslationY(-mSearchView.getHeight());
+ mSearchView.animate().withLayer().alpha(1).translationY(0).setDuration(200)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mSearchView.setVisibility(View.VISIBLE);
+ }
+ });
- getActionBar().setCustomView(searchViewLayout,
- new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+ mPhoneFavoriteFragment.getView().setTranslationY(-mSearchView.getHeight());
+ mPhoneFavoriteFragment.getView().animate().withLayer().translationY(0).setDuration(200)
+ .setListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mBottomPaddingView.setVisibility(View.GONE);
+ }
+ });
}
- @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();
+
+ public void setupFakeActionBarItems() {
+ 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);
}
}
+
+ mCallHistoryButton = findViewById(R.id.call_history_button);
+ // mCallHistoryButton.setMinimumWidth(fakeMenuItemWidth);
+ mCallHistoryButton.setOnClickListener(this);
+
+ mDialpadButton = findViewById(R.id.dialpad_button);
+ // DialpadButton.setMinimumWidth(fakeMenuItemWidth);
+ mDialpadButton.setOnClickListener(this);
}
@Override
protected void onPause() {
super.onPause();
-
mPrefs.edit().putInt(PREF_LAST_MANUALLY_SELECTED_TAB, mLastManuallySelectedFragment)
.apply();
requestBackup();
@@ -733,30 +507,6 @@ public class NewDialtactsActivity extends TransactionSafeActivity implements Vie
}
}
- 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.
@@ -765,8 +515,7 @@ public class NewDialtactsActivity extends TransactionSafeActivity implements Vie
* @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) {
+ private boolean isSendKeyWhileInCall(Intent intent, boolean recentCallsRequest) {
// If there is a call in progress go to the call screen
if (recentCallsRequest) {
final boolean callKey = intent.getBooleanExtra("call_key", false);
@@ -789,7 +538,10 @@ public class NewDialtactsActivity extends TransactionSafeActivity implements Vie
*
* @param intent Intent that contains information about which tab should be selected
*/
- private void setCurrentTab(Intent intent) {
+ private void displayFragment(Intent intent) {
+ // TODO krelease: Make navigation via intent work by displaying the correct fragment
+ // as appropriate.
+
// 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()));
@@ -797,61 +549,30 @@ public class NewDialtactsActivity extends TransactionSafeActivity implements Vie
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);
+ displayFragment(newIntent);
final String action = newIntent.getAction();
if (UI.FILTER_CONTACTS_ACTION.equals(action)) {
setupFilterText(newIntent);
}
- if (mInSearchUi || (mSearchFragment != null && mSearchFragment.isVisible())) {
+ if (mInSearchUi || (mRegularSearchFragment != null && mRegularSearchFragment.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.");
- }
- }
+ // TODO krelease: Handle onNewIntent for all other fragments
+ /*
+ *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();
}
@@ -911,28 +632,12 @@ public class NewDialtactsActivity extends TransactionSafeActivity implements Vie
}
}
- @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() {
+ private final NewPhoneFavoriteFragment.Listener mPhoneFavoriteListener =
+ new NewPhoneFavoriteFragment.Listener() {
@Override
public void onContactSelected(Uri contactUri) {
PhoneNumberInteraction.startInteractionForPhoneCall(
- NewDialtactsActivity.this, contactUri, getCallOrigin());
+ NewDialtactsActivity.this, contactUri, getCallOrigin());
}
@Override
@@ -942,151 +647,14 @@ public class NewDialtactsActivity extends TransactionSafeActivity implements Vie
}
};
- @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(DialtactsActivity.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);
- }
+ /* TODO krelease: This is only relevant for phones that have a hard button search key (i.e.
+ * Nexus S). Supporting it is a little more tricky because of the dialpad fragment might
+ * be showing when the search key is pressed so there is more state management involved.
@Override
public void startSearch(String initialQuery, boolean selectInitialQuery,
Bundle appSearchData, boolean globalSearch) {
- if (mSearchFragment != null && mSearchFragment.isAdded() && !globalSearch) {
+ if (mRegularSearchFragment != null && mRegularSearchFragment.isAdded() && !globalSearch) {
if (mInSearchUi) {
if (mSearchView.hasFocus()) {
showInputMethod(mSearchView.findFocus());
@@ -1099,173 +667,52 @@ public class NewDialtactsActivity extends TransactionSafeActivity implements Vie
} 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);
+ final 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.");
- }
+ imm.showSoftInput(view, 0);
}
}
private void hideInputMethod(View view) {
- InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
+ final 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.
+ * Shows the search fragment
*/
- 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 enterSearchUi(boolean smartDialSearch) {
+ final FragmentTransaction transaction = getFragmentManager().beginTransaction();
+ transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
+ transaction.hide(mPhoneFavoriteFragment);
+ if (smartDialSearch) {
+ transaction.show(mSmartDialSearchFragment);
+ } else {
+ transaction.show(mRegularSearchFragment);
}
- }
+ transaction.commit();
- 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);
- }
- }
+ mInSearchUi = true;
}
/**
- * 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.
+ * Hides the search fragment
*/
- 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);
- }
- }
+ private void exitSearchUi() {
+ final FragmentTransaction transaction = getFragmentManager().beginTransaction();
+ transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
+ transaction.hide(mRegularSearchFragment);
+ transaction.hide(mSmartDialSearchFragment);
+ transaction.show(mPhoneFavoriteFragment);
+ transaction.commit();
+ mInSearchUi = false;
}
/** Returns an Intent to launch Call Settings screen */
@@ -1277,16 +724,41 @@ public class NewDialtactsActivity extends TransactionSafeActivity implements Vie
}
@Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (resultCode != Activity.RESULT_OK) {
- return;
+ public void onBackPressed() {
+ if (mDialpadFragment.isVisible()) {
+ hideDialpadFragment();
+ } else if (mInSearchUi) {
+ mSearchView.setQuery(null, false);
+ } 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();
}
- switch (requestCode) {
- case SUBACTIVITY_ACCOUNT_FILTER: {
- AccountFilterUtil.handleAccountFilterResult(
- mContactListFilterController, resultCode, data);
- }
- break;
+ }
+
+ @Override
+ public void onDialpadQueryChanged(String query) {
+ final String normalizedQuery = SmartDialNameMatcher.normalizeNumber(query,
+ SmartDialNameMatcher.LATIN_SMART_DIAL_MAP);
+ if (!TextUtils.equals(mSearchView.getQuery(), normalizedQuery)) {
+ mSearchView.setQuery(normalizedQuery, false);
+ }
+ }
+
+ @Override
+ public void onListFragmentScrollStateChange(int scrollState) {
+ if (scrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
+ hideDialpadFragmentIfNecessary();
+ hideInputMethod(getCurrentFocus());
}
}
+
+ @Override
+ public void onPhoneFavoriteFragmentStarted() {
+ setupFakeActionBarItems();
+ }
}
diff --git a/src/com/android/dialer/NewSearchFragment.java b/src/com/android/dialer/NewSearchFragment.java
new file mode 100644
index 000000000..c428db239
--- /dev/null
+++ b/src/com/android/dialer/NewSearchFragment.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2013 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.Activity;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.AbsListView.OnScrollListener;
+import android.widget.ListView;
+
+import com.android.contacts.common.list.ContactListItemView;
+import com.android.contacts.common.list.PhoneNumberListAdapter;
+import com.android.contacts.common.list.PhoneNumberPickerFragment;
+import com.android.dialer.list.OnListFragmentScrolledListener;
+
+public class NewSearchFragment extends PhoneNumberPickerFragment {
+
+ private OnListFragmentScrolledListener mActivityScrollListener;
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+
+ setQuickContactEnabled(true);
+ setDarkTheme(false);
+ setPhotoPosition(ContactListItemView.getDefaultPhotoPosition(true /* opposite */));
+ setUseCallableUri(true);
+
+ try {
+ mActivityScrollListener = (OnListFragmentScrolledListener) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString()
+ + " must implement OnListFragmentScrolledListener");
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ getListView().setOnScrollListener(new OnScrollListener() {
+ @Override
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ mActivityScrollListener.onListFragmentScrollStateChange(scrollState);
+ }
+
+ @Override
+ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
+ int totalItemCount) {
+ }
+ });
+ }
+
+ @Override
+ protected void setSearchMode(boolean flag) {
+ super.setSearchMode(flag);
+ // This hides the "All contacts with phone numbers" header in the search fragment
+ getAdapter().setHasHeader(0, false);
+ }
+}
diff --git a/src/com/android/dialer/calllog/CallLogAdapter.java b/src/com/android/dialer/calllog/CallLogAdapter.java
index 46f010a15..46bb4fd28 100644
--- a/src/com/android/dialer/calllog/CallLogAdapter.java
+++ b/src/com/android/dialer/calllog/CallLogAdapter.java
@@ -517,7 +517,7 @@ import java.util.LinkedList;
views.primaryActionView.setTag(
IntentProvider.getCallDetailIntentProvider(
- this, c.getPosition(), c.getLong(CallLogQuery.ID), count));
+ getCursor(), c.getPosition(), c.getLong(CallLogQuery.ID), count));
// Store away the voicemail information so we can play it directly.
if (callType == Calls.VOICEMAIL_TYPE) {
String voicemailUri = c.getString(CallLogQuery.VOICEMAIL_URI);
diff --git a/src/com/android/dialer/calllog/CallLogQueryHandler.java b/src/com/android/dialer/calllog/CallLogQueryHandler.java
index fed381427..91a2e5d2b 100644
--- a/src/com/android/dialer/calllog/CallLogQueryHandler.java
+++ b/src/com/android/dialer/calllog/CallLogQueryHandler.java
@@ -45,7 +45,7 @@ import java.util.List;
import javax.annotation.concurrent.GuardedBy;
/** Handles asynchronous queries to the call log. */
-/*package*/ class CallLogQueryHandler extends NoNullCursorAsyncQueryHandler {
+public class CallLogQueryHandler extends NoNullCursorAsyncQueryHandler {
private static final String[] EMPTY_STRING_ARRAY = new String[0];
private static final String TAG = "CallLogQueryHandler";
@@ -62,6 +62,8 @@ import javax.annotation.concurrent.GuardedBy;
/** The token for the query to fetch voicemail status messages. */
private static final int QUERY_VOICEMAIL_STATUS_TOKEN = 58;
+ private final int mLogLimit;
+
/**
* Call type similar to Calls.INCOMING_TYPE used to specify all types instead of one particular
* type.
@@ -121,10 +123,16 @@ import javax.annotation.concurrent.GuardedBy;
}
public CallLogQueryHandler(ContentResolver contentResolver, Listener listener) {
+ this(contentResolver, listener, -1);
+ }
+
+ public CallLogQueryHandler(ContentResolver contentResolver, Listener listener, int limit) {
super(contentResolver);
mListener = new WeakReference<Listener>(listener);
+ mLogLimit = limit;
}
+
/**
* Fetches the list of calls from the call log for a given type.
* <p>
@@ -154,8 +162,9 @@ import javax.annotation.concurrent.GuardedBy;
selection = String.format("(%s = ?)", Calls.TYPE);
selectionArgs.add(Integer.toString(callType));
}
+ final int limit = (mLogLimit == -1) ? NUM_LOGS_TO_DISPLAY : mLogLimit;
Uri uri = Calls.CONTENT_URI_WITH_VOICEMAIL.buildUpon()
- .appendQueryParameter(Calls.LIMIT_PARAM_KEY, Integer.toString(NUM_LOGS_TO_DISPLAY))
+ .appendQueryParameter(Calls.LIMIT_PARAM_KEY, Integer.toString(limit))
.build();
startQuery(token, requestId, uri,
CallLogQuery._PROJECTION, selection, selectionArgs.toArray(EMPTY_STRING_ARRAY),
diff --git a/src/com/android/dialer/calllog/IntentProvider.java b/src/com/android/dialer/calllog/IntentProvider.java
index 1b79d6ebc..01ebf2f3e 100644
--- a/src/com/android/dialer/calllog/IntentProvider.java
+++ b/src/com/android/dialer/calllog/IntentProvider.java
@@ -62,11 +62,10 @@ public abstract class IntentProvider {
}
public static IntentProvider getCallDetailIntentProvider(
- final CallLogAdapter adapter, final int position, final long id, final int groupSize) {
+ final Cursor cursor, final int position, final long id, final int groupSize) {
return new IntentProvider() {
@Override
public Intent getIntent(Context context) {
- Cursor cursor = adapter.getCursor();
cursor.moveToPosition(position);
Intent intent = new Intent(context, CallDetailActivity.class);
diff --git a/src/com/android/dialer/calllog/NewCallLogActivity.java b/src/com/android/dialer/calllog/NewCallLogActivity.java
new file mode 100644
index 000000000..a3704cc98
--- /dev/null
+++ b/src/com/android/dialer/calllog/NewCallLogActivity.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2013 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.calllog;
+
+import android.app.ActionBar;
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.ActionBar.Tab;
+import android.app.ActionBar.TabListener;
+import android.app.FragmentTransaction;
+import android.content.Intent;
+import android.os.Bundle;
+import android.provider.CallLog.Calls;
+import android.support.v13.app.FragmentPagerAdapter;
+import android.support.v4.view.PagerTabStrip;
+import android.support.v4.view.ViewPager;
+import android.support.v4.view.ViewPager.OnPageChangeListener;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+
+import com.android.dialer.NewDialtactsActivity;
+import com.android.dialer.R;
+import com.android.dialer.calllog.NewCallLogFragment;
+
+public class NewCallLogActivity extends Activity {
+
+ private ViewPager mViewPager;
+ private ViewPagerAdapter mViewPagerAdapter;
+ private NewCallLogFragment mAllCallsFragment;
+ private NewCallLogFragment mMissedCallsFragment;
+ private NewCallLogFragment mVoicemailsFragment;
+
+ private static final int TAB_INDEX_ALL = 0;
+ private static final int TAB_INDEX_MISSED = 1;
+ private static final int TAB_INDEX_VOICEMAIL = 2;
+
+ private static final int TAB_INDEX_COUNT = 3;
+
+ public class ViewPagerAdapter extends FragmentPagerAdapter {
+ public ViewPagerAdapter(FragmentManager fm) {
+ super(fm);
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ switch (position) {
+ case TAB_INDEX_ALL:
+ mAllCallsFragment = new NewCallLogFragment(CallLogQueryHandler.CALL_TYPE_ALL);
+ return mAllCallsFragment;
+ case TAB_INDEX_MISSED:
+ mMissedCallsFragment = new NewCallLogFragment(Calls.MISSED_TYPE);
+ return mMissedCallsFragment;
+ case TAB_INDEX_VOICEMAIL:
+ mVoicemailsFragment = new NewCallLogFragment(Calls.VOICEMAIL_TYPE);
+ return mVoicemailsFragment;
+ }
+ throw new IllegalStateException("No fragment at position " + position);
+ }
+
+ @Override
+ public int getCount() {
+ return TAB_INDEX_COUNT;
+ }
+ }
+
+ private final TabListener mTabListener = new TabListener() {
+ @Override
+ public void onTabUnselected(Tab tab, FragmentTransaction ft) {
+ }
+
+ @Override
+ public void onTabSelected(Tab tab, FragmentTransaction ft) {
+ if (mViewPager != null && mViewPager.getCurrentItem() != tab.getPosition()) {
+ mViewPager.setCurrentItem(tab.getPosition(), true);
+ }
+ }
+
+ @Override
+ public void onTabReselected(Tab tab, FragmentTransaction ft) {
+ }
+ };
+
+ private final OnPageChangeListener mOnPageChangeListener = new OnPageChangeListener() {
+
+ @Override
+ public void onPageScrolled(
+ int position, float positionOffset, int positionOffsetPixels) {}
+
+ @Override
+ public void onPageSelected(int position) {
+ final ActionBar actionBar = getActionBar();
+ actionBar.selectTab(actionBar.getTabAt(position));
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int arg0) {
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.call_log_activity_new);
+
+ final ActionBar actionBar = getActionBar();
+ actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
+ actionBar.setDisplayShowHomeEnabled(true);
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowTitleEnabled(true);
+
+ final Tab allTab = actionBar.newTab();
+ final String allTitle = getString(R.string.call_log_all_title);
+ allTab.setContentDescription(allTitle);
+ allTab.setText(allTitle);
+ allTab.setTabListener(mTabListener);
+ actionBar.addTab(allTab);
+
+ final Tab missedTab = actionBar.newTab();
+ final String missedTitle = getString(R.string.call_log_missed_title);
+ missedTab.setContentDescription(missedTitle);
+ missedTab.setText(missedTitle);
+ missedTab.setTabListener(mTabListener);
+ actionBar.addTab(missedTab);
+
+ final Tab voicemailTab = actionBar.newTab();
+ final String voicemailTitle = getString(R.string.call_log_voicemail_title);
+ voicemailTab.setContentDescription(voicemailTitle);
+ voicemailTab.setText(voicemailTitle);
+ voicemailTab.setTabListener(mTabListener);
+ actionBar.addTab(voicemailTab);
+
+ mViewPager = (ViewPager) findViewById(R.id.call_log_pager);
+ mViewPagerAdapter = new ViewPagerAdapter(getFragmentManager());
+ mViewPager.setAdapter(mViewPagerAdapter);
+ mViewPager.setOnPageChangeListener(mOnPageChangeListener);
+ mViewPager.setOffscreenPageLimit(2);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ final MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.call_log_options_new, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ final MenuItem itemDeleteAll = menu.findItem(R.id.delete_all);
+
+ final NewCallLogAdapter adapter = mAllCallsFragment.getAdapter();
+ // 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 (itemDeleteAll != null) {
+ itemDeleteAll.setEnabled(adapter != null && !adapter.isEmpty());
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ final Intent intent = new Intent(this, NewDialtactsActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intent);
+ return true;
+ case R.id.delete_all:
+ ClearCallLogDialog.show(getFragmentManager());
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+}
diff --git a/src/com/android/dialer/calllog/NewCallLogAdapter.java b/src/com/android/dialer/calllog/NewCallLogAdapter.java
index 079919f59..c2f7c71e6 100644
--- a/src/com/android/dialer/calllog/NewCallLogAdapter.java
+++ b/src/com/android/dialer/calllog/NewCallLogAdapter.java
@@ -46,7 +46,7 @@ import java.util.LinkedList;
/**
* Adapter class to fill in data for the Call Log.
*/
-/*package*/ class NewCallLogAdapter extends GroupingListAdapter
+public class NewCallLogAdapter extends GroupingListAdapter
implements ViewTreeObserver.OnPreDrawListener, CallLogGroupBuilder.GroupCreator {
/** Interface used to initiate a refresh of the content. */
public interface CallFetcher {
@@ -166,7 +166,7 @@ import java.util.LinkedList;
private QueryThread mCallerIdThread;
/** Instance of helper class for managing views. */
- private final CallLogListItemHelper mCallLogViewsHelper;
+ private final NewCallLogListItemHelper mCallLogViewsHelper;
/** Helper to set up contact photos. */
private final ContactPhotoManager mContactPhotoManager;
@@ -227,7 +227,7 @@ import java.util.LinkedList;
}
};
- NewCallLogAdapter(Context context, CallFetcher callFetcher,
+ public NewCallLogAdapter(Context context, CallFetcher callFetcher,
ContactInfoHelper contactInfoHelper) {
super(context);
@@ -246,7 +246,7 @@ import java.util.LinkedList;
PhoneCallDetailsHelper phoneCallDetailsHelper = new PhoneCallDetailsHelper(
resources, callTypeHelper, mPhoneNumberHelper);
mCallLogViewsHelper =
- new CallLogListItemHelper(
+ new NewCallLogListItemHelper(
phoneCallDetailsHelper, mPhoneNumberHelper, resources);
mCallLogGroupBuilder = new CallLogGroupBuilder(this);
}
@@ -259,7 +259,7 @@ import java.util.LinkedList;
mCallFetcher.fetchCalls();
}
- void setLoading(boolean loading) {
+ public void setLoading(boolean loading) {
mLoading = loading;
}
@@ -444,7 +444,7 @@ import java.util.LinkedList;
protected View newStandAloneView(Context context, ViewGroup parent) {
LayoutInflater inflater =
(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- View view = inflater.inflate(R.layout.call_log_list_item, parent, false);
+ View view = inflater.inflate(R.layout.new_call_log_list_item, parent, false);
findAndCacheViews(view);
return view;
}
@@ -458,7 +458,7 @@ import java.util.LinkedList;
protected View newChildView(Context context, ViewGroup parent) {
LayoutInflater inflater =
(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- View view = inflater.inflate(R.layout.call_log_list_item, parent, false);
+ View view = inflater.inflate(R.layout.new_call_log_list_item, parent, false);
findAndCacheViews(view);
return view;
}
@@ -472,7 +472,7 @@ import java.util.LinkedList;
protected View newGroupView(Context context, ViewGroup parent) {
LayoutInflater inflater =
(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- View view = inflater.inflate(R.layout.call_log_list_item, parent, false);
+ View view = inflater.inflate(R.layout.new_call_log_list_item, parent, false);
findAndCacheViews(view);
return view;
}
@@ -515,9 +515,10 @@ import java.util.LinkedList;
final ContactInfo cachedContactInfo = getContactInfoFromCallLog(c);
- /*views.primaryActionView.setTag(
+ views.primaryActionView.setTag(
IntentProvider.getCallDetailIntentProvider(
- this, c.getPosition(), c.getLong(CallLogQuery.ID), count));*/
+ getCursor(), c.getPosition(), c.getLong(CallLogQuery.ID), count));
+
// Store away the voicemail information so we can play it directly.
if (callType == Calls.VOICEMAIL_TYPE) {
String voicemailUri = c.getString(CallLogQuery.VOICEMAIL_URI);
@@ -715,7 +716,7 @@ import java.util.LinkedList;
private void setPhoto(CallLogListItemViews views, long photoId, Uri contactUri) {
views.quickContactView.assignContactUri(contactUri);
- mContactPhotoManager.loadThumbnail(views.quickContactView, photoId, true);
+ mContactPhotoManager.loadThumbnail(views.quickContactView, photoId, false /* darkTheme */);
}
/**
diff --git a/src/com/android/dialer/calllog/NewCallLogFragment.java b/src/com/android/dialer/calllog/NewCallLogFragment.java
index d5b17952c..c470c55d4 100644
--- a/src/com/android/dialer/calllog/NewCallLogFragment.java
+++ b/src/com/android/dialer/calllog/NewCallLogFragment.java
@@ -34,7 +34,6 @@ import android.provider.ContactsContract;
import android.telephony.PhoneNumberUtils;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
-import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -59,10 +58,11 @@ import com.google.common.annotations.VisibleForTesting;
import java.util.List;
/**
- * Displays a list of call log entries.
+ * Displays a list of call log entries. To filter for a particular kind of call
+ * (all, missed or voicemails), specify it in the constructor.
*/
public class NewCallLogFragment extends ListFragment
- implements CallLogQueryHandler.Listener, CallLogAdapter.CallFetcher {
+ implements CallLogQueryHandler.Listener, NewCallLogAdapter.CallFetcher {
private static final String TAG = "CallLogFragment";
/**
@@ -70,7 +70,7 @@ public class NewCallLogFragment extends ListFragment
*/
private static final int EMPTY_LOADER_ID = 0;
- private CallLogAdapter mAdapter;
+ private NewCallLogAdapter mAdapter;
private CallLogQueryHandler mCallLogQueryHandler;
private boolean mScrollToTop;
@@ -81,7 +81,6 @@ public class NewCallLogFragment extends ListFragment
private View mStatusMessageView;
private TextView mStatusMessageText;
private TextView mStatusMessageAction;
- private TextView mFilterStatusView;
private KeyguardManager mKeyguardManager;
private boolean mEmptyLoaderRunning;
@@ -114,11 +113,30 @@ public class NewCallLogFragment extends ListFragment
// Default to all calls.
private int mCallTypeFilter = CallLogQueryHandler.CALL_TYPE_ALL;
+ // Log limit - if no limit is specified, then the default in {@link NewCallLogQueryHandler}
+ // will be used.
+ private int mLogLimit = -1;
+
+ public NewCallLogFragment() {
+ this(CallLogQueryHandler.CALL_TYPE_ALL, -1);
+ }
+
+ public NewCallLogFragment(int filterType) {
+ this(filterType, -1);
+ }
+
+ public NewCallLogFragment(int filterType, int logLimit) {
+ super();
+ mCallTypeFilter = filterType;
+ mLogLimit = logLimit;
+ }
+
@Override
public void onCreate(Bundle state) {
super.onCreate(state);
- mCallLogQueryHandler = new CallLogQueryHandler(getActivity().getContentResolver(), this);
+ mCallLogQueryHandler = new CallLogQueryHandler(getActivity().getContentResolver(),
+ this, mLogLimit);
mKeyguardManager =
(KeyguardManager) getActivity().getSystemService(Context.KEYGUARD_SERVICE);
getActivity().getContentResolver().registerContentObserver(
@@ -126,6 +144,7 @@ public class NewCallLogFragment extends ListFragment
getActivity().getContentResolver().registerContentObserver(
ContactsContract.Contacts.CONTENT_URI, true, mContactsObserver);
setHasOptionsMenu(true);
+ updateCallList(mCallTypeFilter);
}
/** Called by the CallLogQueryHandler when the list of calls has been fetched or updated. */
@@ -205,20 +224,20 @@ public class NewCallLogFragment extends ListFragment
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
- View view = inflater.inflate(R.layout.call_log_fragment, container, false);
+ View view = inflater.inflate(R.layout.new_call_log_fragment, container, false);
mVoicemailStatusHelper = new VoicemailStatusHelperImpl();
mStatusMessageView = view.findViewById(R.id.voicemail_status);
mStatusMessageText = (TextView) view.findViewById(R.id.voicemail_status_message);
mStatusMessageAction = (TextView) view.findViewById(R.id.voicemail_status_action);
- mFilterStatusView = (TextView) view.findViewById(R.id.filter_status);
return view;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
+ updateEmptyMessage(mCallTypeFilter);
String currentCountryIso = GeoUtil.getCurrentCountryIso(getActivity());
- mAdapter = new CallLogAdapter(getActivity(), this,
+ mAdapter = new NewCallLogAdapter(getActivity(), this,
new ContactInfoHelper(getActivity(), currentCountryIso));
setListAdapter(mAdapter);
getListView().setItemsCanFocus(true);
@@ -250,6 +269,7 @@ public class NewCallLogFragment extends ListFragment
@Override
public void onResume() {
+ Log.d(TAG, "Call Log Fragment resume");
super.onResume();
refreshData();
}
@@ -320,132 +340,30 @@ public class NewCallLogFragment extends ListFragment
mCallLogQueryHandler.fetchVoicemailStatus();
}
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- super.onCreateOptionsMenu(menu, inflater);
- inflater.inflate(R.menu.call_log_options, menu);
- }
-
- @Override
- public void onPrepareOptionsMenu(Menu menu) {
- final MenuItem itemDeleteAll = menu.findItem(R.id.delete_all);
- // 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 (itemDeleteAll != null) {
- itemDeleteAll.setEnabled(mAdapter != null && !mAdapter.isEmpty());
-
- showAllFilterMenuOptions(menu);
- hideCurrentFilterMenuOption(menu);
-
- // Only hide if not available. Let the above calls handle showing.
- if (!mVoicemailSourcesAvailable) {
- menu.findItem(R.id.show_voicemails_only).setVisible(false);
- }
+ private void updateCallList(int filterType) {
+ if (filterType == CallLogQueryHandler.CALL_TYPE_ALL) {
+ unregisterPhoneCallReceiver();
+ } else {
+ // TODO krelease: Make this work
+ //registerPhoneCallReceiver();
}
+ mCallLogQueryHandler.fetchCalls(filterType);
}
- private void hideCurrentFilterMenuOption(Menu menu) {
- MenuItem item = null;
- switch (mCallTypeFilter) {
- case CallLogQueryHandler.CALL_TYPE_ALL:
- item = menu.findItem(R.id.show_all_calls);
- break;
- case Calls.INCOMING_TYPE:
- item = menu.findItem(R.id.show_incoming_only);
- break;
- case Calls.OUTGOING_TYPE:
- item = menu.findItem(R.id.show_outgoing_only);
- break;
+ private void updateEmptyMessage(int filterType) {
+ final String message;
+ switch (filterType) {
case Calls.MISSED_TYPE:
- item = menu.findItem(R.id.show_missed_only);
+ message = getString(R.string.recentMissed_empty);
break;
case Calls.VOICEMAIL_TYPE:
- menu.findItem(R.id.show_voicemails_only);
+ message = getString(R.string.recentVoicemails_empty);
break;
- }
- if (item != null) {
- item.setVisible(false);
- }
- }
-
- private void showAllFilterMenuOptions(Menu menu) {
- menu.findItem(R.id.show_all_calls).setVisible(true);
- menu.findItem(R.id.show_incoming_only).setVisible(true);
- menu.findItem(R.id.show_outgoing_only).setVisible(true);
- menu.findItem(R.id.show_missed_only).setVisible(true);
- menu.findItem(R.id.show_voicemails_only).setVisible(true);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.delete_all:
- ClearCallLogDialog.show(getFragmentManager());
- return true;
-
- case R.id.show_outgoing_only:
- // We only need the phone call receiver when there is an active call type filter.
- // Not many people may use the filters so don't register the receiver until now .
- registerPhoneCallReceiver();
- mCallLogQueryHandler.fetchCalls(Calls.OUTGOING_TYPE);
- updateFilterTypeAndHeader(Calls.OUTGOING_TYPE);
- return true;
-
- case R.id.show_incoming_only:
- registerPhoneCallReceiver();
- mCallLogQueryHandler.fetchCalls(Calls.INCOMING_TYPE);
- updateFilterTypeAndHeader(Calls.INCOMING_TYPE);
- return true;
-
- case R.id.show_missed_only:
- registerPhoneCallReceiver();
- mCallLogQueryHandler.fetchCalls(Calls.MISSED_TYPE);
- updateFilterTypeAndHeader(Calls.MISSED_TYPE);
- return true;
-
- case R.id.show_voicemails_only:
- registerPhoneCallReceiver();
- mCallLogQueryHandler.fetchCalls(Calls.VOICEMAIL_TYPE);
- updateFilterTypeAndHeader(Calls.VOICEMAIL_TYPE);
- return true;
-
- case R.id.show_all_calls:
- // Filter is being turned off, receiver no longer needed.
- unregisterPhoneCallReceiver();
- mCallLogQueryHandler.fetchCalls(CallLogQueryHandler.CALL_TYPE_ALL);
- updateFilterTypeAndHeader(CallLogQueryHandler.CALL_TYPE_ALL);
- return true;
-
default:
- return false;
- }
- }
-
- private void updateFilterTypeAndHeader(int filterType) {
- mCallTypeFilter = filterType;
-
- switch (filterType) {
- case CallLogQueryHandler.CALL_TYPE_ALL:
- mFilterStatusView.setVisibility(View.GONE);
- break;
- case Calls.INCOMING_TYPE:
- showFilterStatus(R.string.call_log_incoming_header);
- break;
- case Calls.OUTGOING_TYPE:
- showFilterStatus(R.string.call_log_outgoing_header);
- break;
- case Calls.MISSED_TYPE:
- showFilterStatus(R.string.call_log_missed_header);
- break;
- case Calls.VOICEMAIL_TYPE:
- showFilterStatus(R.string.call_log_voicemail_header);
+ message = getString(R.string.recentCalls_empty);
break;
}
- }
-
- private void showFilterStatus(int resId) {
- mFilterStatusView.setText(resId);
- mFilterStatusView.setVisibility(View.VISIBLE);
+ ((TextView) getListView().getEmptyView()).setText(message);
}
public void callSelectedEntry() {
@@ -489,8 +407,7 @@ public class NewCallLogFragment extends ListFragment
}
}
- @VisibleForTesting
- CallLogAdapter getAdapter() {
+ NewCallLogAdapter getAdapter() {
return mAdapter;
}
@@ -547,6 +464,8 @@ public class NewCallLogFragment extends ListFragment
updateOnTransition(true);
}
+ // TODO krelease: Figure out if we still need this. If so, it should be probably be moved to
+ // the call log activity instead, or done only in a single call log fragment.
private void updateOnTransition(boolean onEntry) {
// We don't want to update any call data when keyguard is on because the user has likely not
// seen the new calls yet.
@@ -570,9 +489,13 @@ public class NewCallLogFragment extends ListFragment
getActivity().startService(serviceIntent);
}
+ // TODO krelease: Make the ViewPager switch to the correct tab (All) when a phone call is
+ // placed.
+ // This should probably be moved to the call log activity.
/**
* Register a phone call filter to reset the call type when a phone call is place.
*/
+ /*
private void registerPhoneCallReceiver() {
if (mPhoneStateListener != null) {
return; // Already registered.
@@ -592,13 +515,13 @@ public class NewCallLogFragment extends ListFragment
if (getActivity() == null || getActivity().isFinishing()) {
return;
}
- updateFilterTypeAndHeader(CallLogQueryHandler.CALL_TYPE_ALL);
}
});
}
};
mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
}
+ */
/**
* Un-registers the phone call receiver.
diff --git a/src/com/android/dialer/calllog/NewCallLogListItemHelper.java b/src/com/android/dialer/calllog/NewCallLogListItemHelper.java
index 371094d34..6cb80a051 100644
--- a/src/com/android/dialer/calllog/NewCallLogListItemHelper.java
+++ b/src/com/android/dialer/calllog/NewCallLogListItemHelper.java
@@ -27,8 +27,12 @@ import com.android.dialer.R;
/**
* Helper class to fill in the views of a call log entry.
+ * TODO krelease: The only difference between this and the original is that we don't touch
+ * divider views, which are not present in the new dialer. Once the new dialer replaces
+ * the old one, we can replace it entirely. Otherwise we would have redundant divider=null
+ * checks all over the place.
*/
-/*package*/ class NewCallLogListItemHelper {
+/* package */class NewCallLogListItemHelper {
/** Helper for populating the details of a phone call. */
private final PhoneCallDetailsHelper mPhoneCallDetailsHelper;
/** Helper for handling phone numbers. */
@@ -67,15 +71,12 @@ import com.android.dialer.R;
if (canPlay) {
// Playback action takes preference.
configurePlaySecondaryAction(views, isHighlighted);
- views.dividerView.setVisibility(View.VISIBLE);
} else if (canCall) {
// Call is the secondary action.
configureCallSecondaryAction(views, details);
- views.dividerView.setVisibility(View.VISIBLE);
} else {
// No action available.
views.secondaryActionView.setVisibility(View.GONE);
- views.dividerView.setVisibility(View.GONE);
}
}
diff --git a/src/com/android/dialer/database/DialerDatabaseHelper.java b/src/com/android/dialer/database/DialerDatabaseHelper.java
index fd402c9ec..a802825fc 100644
--- a/src/com/android/dialer/database/DialerDatabaseHelper.java
+++ b/src/com/android/dialer/database/DialerDatabaseHelper.java
@@ -31,12 +31,12 @@ import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.Directory;
-import com.android.contacts.common.test.NeededForTesting;
import android.util.Log;
import com.android.contacts.common.util.StopWatch;
import com.android.dialer.dialpad.SmartDialNameMatcher;
import com.android.dialer.dialpad.SmartDialPrefix;
+
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
@@ -68,7 +68,7 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
* 0-98 KeyLimePie
* </pre>
*/
- private static final int DATABASE_VERSION = 1;
+ private static final int DATABASE_VERSION = 2;
private static final String SMARTDIAL_DATABASE_NAME = "dialer.db";
/**
@@ -77,7 +77,7 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_LAST_CREATED_SHARED_PREF = "com.android.dialer_smartdial";
private static final String LAST_UPDATED_MILLIS = "last_updated_millis";
- private static final int MAX_ENTRIES = 3;
+ private static final int MAX_ENTRIES = 20;
public interface Tables {
/** Saves the necessary smart dial information of all contacts. */
@@ -88,10 +88,12 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
public interface SmartDialDbColumns {
static final String _ID = "id";
+ static final String DATA_ID = "data_id";
static final String NUMBER = "phone_number";
static final String CONTACT_ID = "contact_id";
static final String LOOKUP_KEY = "lookup_key";
static final String DISPLAY_NAME_PRIMARY = "display_name";
+ static final String PHOTO_ID = "photo_id";
static final String LAST_TIME_USED = "last_time_used";
static final String TIMES_USED = "times_used";
static final String STARRED = "starred";
@@ -122,12 +124,13 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
Phone.CONTACT_ID, // 4
Phone.LOOKUP_KEY, // 5
Phone.DISPLAY_NAME_PRIMARY, // 6
- Data.LAST_TIME_USED, // 7
- Data.TIMES_USED, // 8
- Contacts.STARRED, // 9
- Data.IS_SUPER_PRIMARY, // 10
- Contacts.IN_VISIBLE_GROUP, // 11
- Data.IS_PRIMARY, // 12
+ Phone.PHOTO_ID, // 7
+ Data.LAST_TIME_USED, // 8
+ Data.TIMES_USED, // 9
+ Contacts.STARRED, // 10
+ Data.IS_SUPER_PRIMARY, // 11
+ Contacts.IN_VISIBLE_GROUP, // 12
+ Data.IS_PRIMARY, // 13
};
static final int PHONE_ID = 0;
@@ -137,12 +140,13 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
static final int PHONE_CONTACT_ID = 4;
static final int PHONE_LOOKUP_KEY = 5;
static final int PHONE_DISPLAY_NAME = 6;
- static final int PHONE_LAST_TIME_USED = 7;
- static final int PHONE_TIMES_USED = 8;
- static final int PHONE_STARRED = 9;
- static final int PHONE_IS_SUPER_PRIMARY = 10;
- static final int PHONE_IN_VISIBLE_GROUP = 11;
- static final int PHONE_IS_PRIMARY = 12;
+ static final int PHONE_PHOTO_ID = 7;
+ static final int PHONE_LAST_TIME_USED = 8;
+ static final int PHONE_TIMES_USED = 9;
+ static final int PHONE_STARRED = 10;
+ static final int PHONE_IS_SUPER_PRIMARY = 11;
+ static final int PHONE_IN_VISIBLE_GROUP = 12;
+ static final int PHONE_IS_PRIMARY = 13;
/** Selects only rows that have been updated after a certain time stamp.*/
static final String SELECT_UPDATED_CLAUSE =
@@ -210,21 +214,26 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
* smart dial interface.
*/
public static class ContactNumber {
- public final String displayName;
- public final String lookupKey;
public final long id;
+ public final long dataId;
+ public final String displayName;
public final String phoneNumber;
+ public final String lookupKey;
+ public final long photoId;
- public ContactNumber(long id, String displayName, String phoneNumber, String lookupKey) {
- this.displayName = displayName;
- this.lookupKey = lookupKey;
+ public ContactNumber(long id, long dataID, String displayName, String phoneNumber,
+ String lookupKey, long photoId) {
+ this.dataId = dataID;
this.id = id;
+ this.displayName = displayName;
this.phoneNumber = phoneNumber;
+ this.lookupKey = lookupKey;
+ this.photoId = photoId;
}
@Override
public int hashCode() {
- return Objects.hashCode(displayName, id, lookupKey, phoneNumber);
+ return Objects.hashCode(id, dataId, displayName, phoneNumber, lookupKey, photoId);
}
@Override
@@ -234,10 +243,12 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
}
if (object instanceof ContactNumber) {
final ContactNumber that = (ContactNumber) object;
- return Objects.equal(this.displayName, that.displayName)
- && Objects.equal(this.id, that.id)
+ return Objects.equal(this.id, that.id)
+ && Objects.equal(this.dataId, that.dataId)
+ && Objects.equal(this.displayName, that.displayName)
+ && Objects.equal(this.phoneNumber, that.phoneNumber)
&& Objects.equal(this.lookupKey, that.lookupKey)
- && Objects.equal(this.phoneNumber, that.phoneNumber);
+ && Objects.equal(this.photoId, that.photoId);
}
return false;
}
@@ -290,7 +301,7 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
/**
* Returns a new instance for unit tests. The database will be created in memory.
*/
- @NeededForTesting
+ @VisibleForTesting
static DialerDatabaseHelper getNewInstanceForTest(Context context) {
return new DialerDatabaseHelper(context, null);
}
@@ -309,10 +320,12 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + Tables.SMARTDIAL_TABLE + " (" +
SmartDialDbColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
+ SmartDialDbColumns.DATA_ID + " INTEGER, " +
SmartDialDbColumns.NUMBER + " TEXT," +
SmartDialDbColumns.CONTACT_ID + " INTEGER," +
SmartDialDbColumns.LOOKUP_KEY + " TEXT," +
SmartDialDbColumns.DISPLAY_NAME_PRIMARY + " TEXT, " +
+ SmartDialDbColumns.PHOTO_ID + " INTEGER, " +
SmartDialDbColumns.LAST_SMARTDIAL_UPDATE_TIME + " LONG, " +
SmartDialDbColumns.LAST_TIME_USED + " LONG, " +
SmartDialDbColumns.TIMES_USED + " INTEGER, " +
@@ -329,6 +342,21 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
");");
}
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ Log.w(TAG, oldVersion + " to " + newVersion + ", rebuilding table");
+
+ final SharedPreferences databaseLastUpdateSharedPref = mContext.getSharedPreferences(
+ DATABASE_LAST_CREATED_SHARED_PREF, Context.MODE_PRIVATE);
+ final SharedPreferences.Editor editor = databaseLastUpdateSharedPref.edit();
+ editor.putLong(LAST_UPDATED_MILLIS, 0);
+ editor.commit();
+
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.PREFIX_TABLE);
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.SMARTDIAL_TABLE);
+ onCreate(db);
+ }
+
/**
* Starts the database upgrade process in the background.
*/
@@ -468,10 +496,12 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
db.beginTransaction();
try {
final String sqlInsert = "INSERT INTO " + Tables.SMARTDIAL_TABLE + " (" +
+ SmartDialDbColumns.DATA_ID + ", " +
SmartDialDbColumns.NUMBER + ", " +
SmartDialDbColumns.CONTACT_ID + ", " +
SmartDialDbColumns.LOOKUP_KEY + ", " +
SmartDialDbColumns.DISPLAY_NAME_PRIMARY + ", " +
+ SmartDialDbColumns.PHOTO_ID + ", " +
SmartDialDbColumns.LAST_TIME_USED + ", " +
SmartDialDbColumns.TIMES_USED + ", " +
SmartDialDbColumns.STARRED + ", " +
@@ -479,7 +509,7 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
SmartDialDbColumns.IN_VISIBLE_GROUP+ ", " +
SmartDialDbColumns.IS_PRIMARY + ", " +
SmartDialDbColumns.LAST_SMARTDIAL_UPDATE_TIME + ") " +
- " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
final SQLiteStatement insert = db.compileStatement(sqlInsert);
final String numberSqlInsert = "INSERT INTO " + Tables.PREFIX_TABLE + " (" +
@@ -490,17 +520,19 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
updatedContactCursor.moveToPosition(-1);
while (updatedContactCursor.moveToNext()) {
- insert.bindString(1, updatedContactCursor.getString(PhoneQuery.PHONE_NUMBER));
- insert.bindLong(2, updatedContactCursor.getLong(PhoneQuery.PHONE_CONTACT_ID));
- insert.bindString(3, updatedContactCursor.getString(PhoneQuery.PHONE_LOOKUP_KEY));
- insert.bindString(4, updatedContactCursor.getString(PhoneQuery.PHONE_DISPLAY_NAME));
- insert.bindLong(5, updatedContactCursor.getLong(PhoneQuery.PHONE_LAST_TIME_USED));
- insert.bindLong(6, updatedContactCursor.getInt(PhoneQuery.PHONE_TIMES_USED));
- insert.bindLong(7, updatedContactCursor.getInt(PhoneQuery.PHONE_STARRED));
- insert.bindLong(8, updatedContactCursor.getInt(PhoneQuery.PHONE_IS_SUPER_PRIMARY));
- insert.bindLong(9, updatedContactCursor.getInt(PhoneQuery.PHONE_IN_VISIBLE_GROUP));
- insert.bindLong(10, updatedContactCursor.getInt(PhoneQuery.PHONE_IS_PRIMARY));
- insert.bindLong(11, currentMillis);
+ insert.bindLong(1, updatedContactCursor.getLong(PhoneQuery.PHONE_ID));
+ insert.bindString(2, updatedContactCursor.getString(PhoneQuery.PHONE_NUMBER));
+ insert.bindLong(3, updatedContactCursor.getLong(PhoneQuery.PHONE_CONTACT_ID));
+ insert.bindString(4, updatedContactCursor.getString(PhoneQuery.PHONE_LOOKUP_KEY));
+ insert.bindString(5, updatedContactCursor.getString(PhoneQuery.PHONE_DISPLAY_NAME));
+ insert.bindLong(6, updatedContactCursor.getLong(PhoneQuery.PHONE_PHOTO_ID));
+ insert.bindLong(7, updatedContactCursor.getLong(PhoneQuery.PHONE_LAST_TIME_USED));
+ insert.bindLong(8, updatedContactCursor.getInt(PhoneQuery.PHONE_TIMES_USED));
+ insert.bindLong(9, updatedContactCursor.getInt(PhoneQuery.PHONE_STARRED));
+ insert.bindLong(10, updatedContactCursor.getInt(PhoneQuery.PHONE_IS_SUPER_PRIMARY));
+ insert.bindLong(11, updatedContactCursor.getInt(PhoneQuery.PHONE_IN_VISIBLE_GROUP));
+ insert.bindLong(12, updatedContactCursor.getInt(PhoneQuery.PHONE_IS_PRIMARY));
+ insert.bindLong(13, currentMillis);
insert.executeInsert();
insert.clearBindings();
@@ -719,7 +751,6 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
}
}
-
/**
* Returns a list of candidate contacts where the query is a prefix of the dialpad index of
* the contact's name or phone number.
@@ -747,7 +778,9 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
/** Queries the database to find contacts that have an index matching the query prefix. */
final Cursor cursor = db.rawQuery("SELECT " +
+ SmartDialDbColumns.DATA_ID + ", " +
SmartDialDbColumns.DISPLAY_NAME_PRIMARY + ", " +
+ SmartDialDbColumns.PHOTO_ID + ", " +
SmartDialDbColumns.NUMBER + ", " +
SmartDialDbColumns.CONTACT_ID + ", " +
SmartDialDbColumns.LOOKUP_KEY +
@@ -765,10 +798,12 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
}
/** Gets the column ID from the cursor.*/
- final int columnDisplayNamePrimary = 0;
- final int columnNumber = 1;
- final int columnId = 2;
- final int columnLookupKey = 3;
+ final int columnDataId = 0;
+ final int columnDisplayNamePrimary = 1;
+ final int columnPhotoId = 2;
+ final int columnNumber = 3;
+ final int columnId = 4;
+ final int columnLookupKey = 5;
if (DEBUG) {
stopWatch.lap("Found column IDs");
}
@@ -781,9 +816,11 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
}
/** Iterates the cursor to find top contact suggestions without duplication.*/
while ((cursor.moveToNext()) && (counter < MAX_ENTRIES)) {
+ final long dataID = cursor.getLong(columnDataId);
final String displayName = cursor.getString(columnDisplayNamePrimary);
final String phoneNumber = cursor.getString(columnNumber);
final long id = cursor.getLong(columnId);
+ final long photoId = cursor.getLong(columnPhotoId);
final String lookupKey = cursor.getString(columnLookupKey);
/** If a contact already exists and another phone number of the contact is being
@@ -798,11 +835,14 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
* If the contact has either the name or number that matches the query, add to the
* result.
*/
- if (nameMatcher.matches(displayName) ||
- nameMatcher.matchesNumber(phoneNumber, query) != null) {
+ final boolean nameMatches = nameMatcher.matches(displayName);
+ final boolean numberMatches =
+ (nameMatcher.matchesNumber(phoneNumber, query) != null);
+ if (nameMatches || numberMatches) {
/** If a contact has not been added, add it to the result and the hash set.*/
duplicates.add(contactMatch);
- result.add(new ContactNumber(id, displayName, phoneNumber, lookupKey));
+ result.add(new ContactNumber(id, dataID, displayName, phoneNumber, lookupKey,
+ photoId));
counter++;
if (DEBUG) {
stopWatch.lap("Added one result");
@@ -818,9 +858,4 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper {
}
return result;
}
-
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-
- }
}
diff --git a/src/com/android/dialer/dialpad/NewDialpadFragment.java b/src/com/android/dialer/dialpad/NewDialpadFragment.java
index 707651726..46f5d06cb 100644
--- a/src/com/android/dialer/dialpad/NewDialpadFragment.java
+++ b/src/com/android/dialer/dialpad/NewDialpadFragment.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,15 @@ 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.TextView;
import com.android.contacts.common.CallUtil;
@@ -76,6 +78,8 @@ 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.DialtactsActivity;
+import com.android.dialer.NeededForReflection;
+import com.android.dialer.NewDialtactsActivity;
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,10 +98,42 @@ public class NewDialpadFragment extends Fragment
View.OnLongClickListener, View.OnKeyListener,
AdapterView.OnItemClickListener, TextWatcher,
PopupMenu.OnMenuItemClickListener,
- DialpadImageButton.OnPressedListener,
- SmartDialLoaderTask.SmartDialLoaderCallback {
+ DialpadImageButton.OnPressedListener {
private static final String TAG = NewDialpadFragment.class.getSimpleName();
+ /**
+ * 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 = DialtactsActivity.DEBUG;
private static final String EMPTY_NUMBER = "";
@@ -118,6 +152,10 @@ public class NewDialpadFragment extends Fragment
private ContactsPreferences mContactsPrefs;
+ private OnDialpadQueryChangedListener mDialpadQueryListener;
+
+ private View mFragmentView;
+
/**
* View (usually FrameLayout) containing mDigits field. This can be null, in which mDigits
* isn't enclosed by the container.
@@ -145,25 +183,6 @@ public class NewDialpadFragment 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 +306,10 @@ public class NewDialpadFragment extends Fragment
mDigits.setCursorVisible(false);
}
+ if (mDialpadQueryListener != null) {
+ mDialpadQueryListener.onDialpadQueryChanged(mDigits.getText().toString());
+ }
updateDialAndDeleteButtonEnabledState();
- loadSmartDialEntries();
}
@Override
@@ -308,8 +329,6 @@ public class NewDialpadFragment extends Fragment
Log.e(TAG, "Vibrate control bool missing.", nfe);
}
- setHasOptionsMenu(true);
-
mProhibitedPhoneNumberRegexp = getResources().getString(
R.string.config_prohibited_phone_number_regexp);
@@ -320,7 +339,29 @@ public class NewDialpadFragment extends Fragment
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
- View fragmentView = inflater.inflate(R.layout.dialpad_fragment, container, false);
+ View fragmentView = inflater.inflate(R.layout.new_dialpad_fragment, container, false);
+ mFragmentView = fragmentView;
+ mFragmentView.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 = mFragmentView.getViewTreeObserver();
+ final OnPreDrawListener preDrawListener = new OnPreDrawListener() {
+
+ @Override
+ public boolean onPreDraw() {
+ if (isHidden()) return true;
+ if (mFragmentView.getTranslationY() == 0) {
+ ((DialpadSlidingLinearLayout) mFragmentView).setYFraction(0.67f);
+ }
+ final ViewTreeObserver vto = mFragmentView.getViewTreeObserver();
+ vto.removeOnPreDrawListener(this);
+ return true;
+ }
+
+ };
+
+ vto.addOnPreDrawListener(preDrawListener);
// Load up the resources for the text field.
Resources r = getResources();
@@ -379,15 +420,6 @@ public class NewDialpadFragment 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);
-
- if (mSmartDialContainer != null) {
- mSmartDialAdapter = new SmartDialController(getActivity(), mSmartDialContainer,
- new OnSmartDialShortClick(), new OnSmartDialLongClick());
- }
return fragmentView;
}
@@ -560,6 +592,9 @@ public class NewDialpadFragment extends Fragment
public void onResume() {
super.onResume();
+ final NewDialtactsActivity activity = (NewDialtactsActivity) getActivity();
+ mDialpadQueryListener = activity;
+
final StopWatch stopWatch = StopWatch.start("Dialpad.onResume");
// Query the last dialed number. Do it first because hitting
@@ -574,10 +609,6 @@ public class NewDialpadFragment extends Fragment
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 +709,7 @@ public class NewDialpadFragment extends Fragment
@Override
public void onStop() {
super.onStop();
+
if (mClearDigitsOnStop) {
mClearDigitsOnStop = false;
mDigits.getText().clear();
@@ -690,28 +722,6 @@ public class NewDialpadFragment 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);
@@ -1481,24 +1491,6 @@ public class NewDialpadFragment 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) {
- 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;
- }
- }
-
@Override
public boolean onMenuItemClick(MenuItem item) {
return onOptionsItemSelected(item);
@@ -1507,6 +1499,7 @@ public class NewDialpadFragment extends Fragment
/**
* Updates the dial string (mDigits) after inserting a Pause character (,)
* or Wait character (;).
+ * TODO krelease: add new dialpad buttons to add PAUSE and WAIT characters
*/
private void updateDialString(char newDigit) {
if(newDigit != WAIT && newDigit != PAUSE) {
@@ -1654,79 +1647,22 @@ public class NewDialpadFragment 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);
- }
- }
- }
-
- 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;
- }
+ // TODO krelease: This should probably be moved to somewhere more appropriate, maybe
+ // into DialtactsActivity
+ mDialerDatabaseHelper.startSmartDialUpdateThread();
}
- 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 DialtactsActivity ?
- ((DialtactsActivity) getActivity()).getCallOrigin() : null));
- startActivity(intent);
- mClearDigitsOnStop = true;
+ @Override
+ public void onHiddenChanged(boolean hidden) {
+ super.onHiddenChanged(hidden);
+ final NewDialtactsActivity activity = (NewDialtactsActivity) getActivity();
+ if (activity == null) return;
+ if (hidden) {
+ activity.showSearchBar();
+ } else {
+ activity.hideSearchBar();
}
}
}
diff --git a/src/com/android/dialer/dialpad/SmartDialCursorLoader.java b/src/com/android/dialer/dialpad/SmartDialCursorLoader.java
new file mode 100644
index 000000000..715f1e7cc
--- /dev/null
+++ b/src/com/android/dialer/dialpad/SmartDialCursorLoader.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2013 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.AsyncTaskLoader;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.telephony.PhoneNumberUtils;
+import android.util.Log;
+
+import com.android.dialer.database.DialerDatabaseHelper;
+import com.android.dialer.database.DialerDatabaseHelper.ContactNumber;
+
+import java.util.ArrayList;
+
+/**
+ * Implements a Loader<Cursor> class to asynchronously load SmartDial search results.
+ */
+public class SmartDialCursorLoader extends AsyncTaskLoader<Cursor> {
+
+ private final String TAG = SmartDialCursorLoader.class.getSimpleName();
+ private final boolean DEBUG = false;
+
+ private final Context mContext;
+
+ private Cursor mCursor;
+
+ private String mQuery;
+ private SmartDialNameMatcher mNameMatcher;
+
+ /** Constructs the columns of the cursor to be used. */
+ public static class SmartDialPhoneQuery {
+ public static final String[] PROJECTION_PRIMARY = new String[] {
+ Phone._ID, // 0
+ Phone.TYPE, // 1
+ Phone.LABEL, // 2
+ Phone.NUMBER, // 3
+ Phone.CONTACT_ID, // 4
+ Phone.LOOKUP_KEY, // 5
+ Phone.PHOTO_ID, // 6
+ Phone.DISPLAY_NAME_PRIMARY, // 7
+ };
+
+ public static final int SMARTDIAL_ID = 0;
+ public static final int SMARTDIAL_TYPE = 1;
+ public static final int SMARTDIAL_LABEL = 2;
+ public static final int SMARTDIAL_NUMBER = 3;
+ public static final int SMARTDIAL_CONTACT_ID = 4;
+ public static final int SMARTDIAL_LOOKUP_KEY = 5;
+ public static final int SMARTDIAL_PHOTO_ID = 6;
+ public static final int SMARTDIAL_DISPLAY_NAME = 7;
+ }
+
+ public SmartDialCursorLoader(Context context) {
+ super(context);
+ mContext = context;
+ }
+
+ /**
+ * Configures the query string to be used to find SmartDial matches.
+ * @param query The query string user typed.
+ */
+ public void configureQuery(String query) {
+ if (DEBUG) {
+ Log.v(TAG, "Configure new query to be " + query);
+ }
+ mQuery = query;
+
+ /** Constructs a name matcher object for matching names. */
+ mNameMatcher = new SmartDialNameMatcher(PhoneNumberUtils.normalizeNumber(query),
+ SmartDialPrefix.getMap());
+ }
+
+ /**
+ * Queries the SmartDial database and loads results in background.
+ * @return Cursor of contacts that matches the SmartDial query.
+ */
+ @Override
+ public Cursor loadInBackground() {
+ if (DEBUG) {
+ Log.v(TAG, "Load in background " + mQuery);
+ }
+
+ /** Loads results from the database helper. */
+ DialerDatabaseHelper dialerDatabaseHelper = DialerDatabaseHelper.getInstance(mContext);
+ final ArrayList<ContactNumber> allMatches = dialerDatabaseHelper.getLooseMatches(mQuery,
+ mNameMatcher);
+
+ if (DEBUG) {
+ Log.v(TAG, "Loaded matches " + String.valueOf(allMatches.size()));
+ }
+
+ /** Constructs a cursor for the returned array of results. */
+ final MatrixCursor cursor = new MatrixCursor(SmartDialPhoneQuery.PROJECTION_PRIMARY);
+ for (ContactNumber contact : allMatches) {
+ cursor.addRow(new Object[] {contact.dataId, null, null, contact.phoneNumber, contact.id,
+ contact.lookupKey, contact.photoId, contact.displayName});
+ }
+ return cursor;
+ }
+
+ @Override
+ public void deliverResult(Cursor cursor) {
+ if (isReset()) {
+ /** The Loader has been reset; ignore the result and invalidate the data. */
+ releaseResources(cursor);
+ return;
+ }
+
+ /** Hold a reference to the old data so it doesn't get garbage collected. */
+ Cursor oldCursor = mCursor;
+ mCursor = cursor;
+
+ if (isStarted()) {
+ /** If the Loader is in a started state, deliver the results to the client. */
+ super.deliverResult(cursor);
+ }
+
+ /** Invalidate the old data as we don't need it any more. */
+ if (oldCursor != null && oldCursor != cursor) {
+ releaseResources(oldCursor);
+ }
+ }
+
+ @Override
+ protected void onStartLoading() {
+ if (mCursor != null) {
+ /** Deliver any previously loaded data immediately. */
+ deliverResult(mCursor);
+ }
+ if (mCursor == null) {
+ /** Force loads every time as our results change with queries. */
+ forceLoad();
+ }
+ }
+
+ @Override
+ protected void onStopLoading() {
+ /** The Loader is in a stopped state, so we should attempt to cancel the current load. */
+ cancelLoad();
+ }
+
+ @Override
+ protected void onReset() {
+ /** Ensure the loader has been stopped. */
+ onStopLoading();
+
+ /** Release all previously saved query results. */
+ if (mCursor != null) {
+ releaseResources(mCursor);
+ mCursor = null;
+ }
+ }
+
+ @Override
+ public void onCanceled(Cursor cursor) {
+ super.onCanceled(cursor);
+
+ /** The load has been canceled, so we should release the resources associated with 'data'.*/
+ releaseResources(cursor);
+ }
+
+ private void releaseResources(Cursor cursor) {
+ cursor.close();
+ }
+}
diff --git a/src/com/android/dialer/dialpad/SmartDialMatchPosition.java b/src/com/android/dialer/dialpad/SmartDialMatchPosition.java
index 434874646..452ac9110 100644
--- a/src/com/android/dialer/dialpad/SmartDialMatchPosition.java
+++ b/src/com/android/dialer/dialpad/SmartDialMatchPosition.java
@@ -28,7 +28,7 @@ import java.util.ArrayList;
* 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.
*/
-class SmartDialMatchPosition {
+public class SmartDialMatchPosition {
public int start;
public int end;
diff --git a/src/com/android/dialer/dialpad/SmartDialNameMatcher.java b/src/com/android/dialer/dialpad/SmartDialNameMatcher.java
index fe88e930d..c160bd258 100644
--- a/src/com/android/dialer/dialpad/SmartDialNameMatcher.java
+++ b/src/com/android/dialer/dialpad/SmartDialNameMatcher.java
@@ -45,10 +45,13 @@ public class SmartDialNameMatcher {
private final ArrayList<SmartDialMatchPosition> mMatchPositions = Lists.newArrayList();
- private static final SmartDialMap LATIN_SMART_DIAL_MAP = new LatinSmartDialMap();
+ public static final SmartDialMap LATIN_SMART_DIAL_MAP = new LatinSmartDialMap();
private final SmartDialMap mMap;
+ private String mNameMatchMask = "";
+ private String mPhoneNumberMatchMask = "";
+
@VisibleForTesting
public SmartDialNameMatcher(String query) {
this(query, LATIN_SMART_DIAL_MAP);
@@ -60,6 +63,29 @@ public class SmartDialNameMatcher {
}
/**
+ * Constructs empty highlight mask. Bit 0 at a position means there is no match, Bit 1 means
+ * there is a match and should be highlighted in the TextView.
+ * @param builder StringBuilder object
+ * @param length Length of the desired mask.
+ */
+ private void constructEmptyMask(StringBuilder builder, int length) {
+ for (int i = 0; i < length; ++i) {
+ builder.append("0");
+ }
+ }
+
+ /**
+ * Replaces the 0-bit at a position with 1-bit, indicating that there is a match.
+ * @param builder StringBuilder object.
+ * @param matchPos Match Positions to mask as 1.
+ */
+ private void replaceBitInMask(StringBuilder builder, SmartDialMatchPosition matchPos) {
+ for (int i = matchPos.start; i < matchPos.end; ++i) {
+ builder.replace(i, i + 1, "1");
+ }
+ }
+
+ /**
* Strips a phone number of unnecessary characters (spaces, dashes, etc.)
*
* @param number Phone number we want to normalize
@@ -98,6 +124,10 @@ public class SmartDialNameMatcher {
*/
@VisibleForTesting
public SmartDialMatchPosition matchesNumber(String phoneNumber, String query, boolean useNanp) {
+ StringBuilder builder = new StringBuilder();
+ constructEmptyMask(builder, phoneNumber.length());
+ mPhoneNumberMatchMask = builder.toString();
+
// Try matching the number as is
SmartDialMatchPosition matchPos = matchesNumberWithOffset(phoneNumber, query, 0);
if (matchPos == null) {
@@ -105,6 +135,10 @@ public class SmartDialNameMatcher {
SmartDialPrefix.parsePhoneNumber(phoneNumber);
if (phoneNumberTokens == null) {
+ if (matchPos != null) {
+ replaceBitInMask(builder, matchPos);
+ mPhoneNumberMatchMask = builder.toString();
+ }
return matchPos;
}
if (phoneNumberTokens.countryCodeOffset != 0) {
@@ -116,10 +150,26 @@ public class SmartDialNameMatcher {
phoneNumberTokens.nanpCodeOffset);
}
}
+ if (matchPos != null) {
+ replaceBitInMask(builder, matchPos);
+ mPhoneNumberMatchMask = builder.toString();
+ }
return matchPos;
}
/**
+ * Matches a phone number against the saved query, taking care of formatting characters and also
+ * taking into account country code prefixes and special NANP number treatment.
+ *
+ * @param phoneNumber - Raw phone number
+ * @return {@literal null} if the number and the query don't match, a valid
+ * SmartDialMatchPosition with the matching positions otherwise
+ */
+ public SmartDialMatchPosition matchesNumber(String phoneNumber) {
+ return matchesNumber(phoneNumber, mQuery, true);
+ }
+
+ /**
* Matches a phone number against a query, taking care of formatting characters and also
* taking into account country code prefixes and special NANP number treatment.
*
@@ -210,6 +260,9 @@ public class SmartDialNameMatcher {
@VisibleForTesting
boolean matchesCombination(String displayName, String query,
ArrayList<SmartDialMatchPosition> matchList) {
+ StringBuilder builder = new StringBuilder();
+ constructEmptyMask(builder, displayName.length());
+ mNameMatchMask = builder.toString();
final int nameLength = displayName.length();
final int queryLength = query.length();
@@ -286,6 +339,10 @@ public class SmartDialNameMatcher {
// one so if we find a full token match, we can return right away
matchList.add(new SmartDialMatchPosition(
tokenStart, queryLength + tokenStart + seperatorCount));
+ for (SmartDialMatchPosition match : matchList) {
+ replaceBitInMask(builder, match);
+ }
+ mNameMatchMask = builder.toString();
return true;
} else if (ALLOW_INITIAL_MATCH && queryStart < INITIAL_LENGTH_LIMIT) {
// we matched the first character.
@@ -343,6 +400,10 @@ public class SmartDialNameMatcher {
// then partial will always be empty.
if (!partial.isEmpty()) {
matchList.addAll(partial);
+ for (SmartDialMatchPosition match : matchList) {
+ replaceBitInMask(builder, match);
+ }
+ mNameMatchMask = builder.toString();
return true;
}
return false;
@@ -359,6 +420,14 @@ public class SmartDialNameMatcher {
return new ArrayList<SmartDialMatchPosition>(mMatchPositions);
}
+ public String getNameMatchPositionsInString() {
+ return mNameMatchMask;
+ }
+
+ public String getNumberMatchPositionsInString() {
+ return mPhoneNumberMatchMask;
+ }
+
public String getQuery() {
return mQuery;
}
diff --git a/src/com/android/dialer/list/NewPhoneFavoriteFragment.java b/src/com/android/dialer/list/NewPhoneFavoriteFragment.java
index e694d6067..db2999ccf 100644
--- a/src/com/android/dialer/list/NewPhoneFavoriteFragment.java
+++ b/src/com/android/dialer/list/NewPhoneFavoriteFragment.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2013 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.
@@ -27,14 +27,10 @@ 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;
@@ -47,18 +43,16 @@ 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.GeoUtil;
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.DialtactsActivity;
+import com.android.dialer.NewDialtactsActivity;
import com.android.dialer.R;
+import com.android.dialer.calllog.ContactInfoHelper;
+import com.android.dialer.calllog.NewCallLogAdapter;
+import com.android.dialer.calllog.CallLogQueryHandler;
/**
* Fragment for Phone UI's favorite screen.
@@ -68,7 +62,8 @@ import com.android.dialer.R;
* {@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 NewPhoneFavoriteFragment extends Fragment implements OnItemClickListener {
+public class NewPhoneFavoriteFragment extends Fragment implements OnItemClickListener,
+ CallLogQueryHandler.Listener, NewCallLogAdapter.CallFetcher {
private static final String TAG = NewPhoneFavoriteFragment.class.getSimpleName();
private static final boolean DEBUG = false;
@@ -78,9 +73,9 @@ public class NewPhoneFavoriteFragment extends Fragment implements OnItemClickLis
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 OnPhoneFavoriteFragmentStartedListener {
+ public void onPhoneFavoriteFragmentStarted();
+ }
public interface Listener {
public void onContactSelected(Uri contactUri);
@@ -113,11 +108,7 @@ public class NewPhoneFavoriteFragment extends Fragment implements OnItemClickLis
mAllContactsLoaderStarted = true;
// Show the filter header with "loading" state.
- updateFilterHeaderView();
mAccountFilterHeader.setVisibility(View.VISIBLE);
-
- // invalidate the options menu if needed
- invalidateOptionsMenuIfNeeded();
}
@Override
@@ -139,7 +130,6 @@ public class NewPhoneFavoriteFragment extends Fragment implements OnItemClickLis
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);
}
@@ -171,16 +161,6 @@ public class NewPhoneFavoriteFragment extends Fragment implements OnItemClickLis
}
}
- private class FilterHeaderClickListener implements OnClickListener {
- @Override
- public void onClick(View view) {
- AccountFilterUtil.startAccountFilterActivityForResult(
- NewPhoneFavoriteFragment.this,
- REQUEST_CODE_ACCOUNT_FILTER,
- mFilter);
- }
- }
-
private class ContactsPreferenceChangeListener
implements ContactsPreferences.ChangeListener {
@Override
@@ -204,10 +184,13 @@ public class NewPhoneFavoriteFragment extends Fragment implements OnItemClickLis
mListView.setFastScrollAlwaysVisible(shouldShow);
mShouldShowFastScroller = shouldShow;
}
+
+
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
+ mActivityScrollListener.onListFragmentScrollStateChange(scrollState);
}
}
@@ -225,10 +208,15 @@ public class NewPhoneFavoriteFragment extends Fragment implements OnItemClickLis
};
private Listener mListener;
- private PhoneFavoriteMergedAdapter mAdapter;
- private ContactTileAdapter mContactTileAdapter;
+
+ private OnListFragmentScrolledListener mActivityScrollListener;
+ private NewPhoneFavoriteMergedAdapter mAdapter;
+ private PhoneFavoritesTileAdapter mContactTileAdapter;
private PhoneNumberListAdapter mAllContactsAdapter;
+ private NewCallLogAdapter mCallLogAdapter;
+ private CallLogQueryHandler mCallLogQueryHandler;
+
/**
* true when the loader for {@link PhoneNumberListAdapter} has started already.
*/
@@ -241,7 +229,6 @@ public class NewPhoneFavoriteFragment extends Fragment implements OnItemClickLis
private boolean mAllContactsForceReload;
private ContactsPreferences mContactsPrefs;
- private ContactListFilter mFilter;
private TextView mEmptyView;
private ListView mListView;
@@ -263,7 +250,6 @@ public class NewPhoneFavoriteFragment extends Fragment implements OnItemClickLis
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();
@@ -281,9 +267,9 @@ public class NewPhoneFavoriteFragment extends Fragment implements OnItemClickLis
// 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 = new PhoneFavoritesTileAdapter(activity, mContactTileAdapterListener,
+ getResources().getInteger(R.integer.contact_tile_column_count_in_favorites_new),
+ 1);
mContactTileAdapter.setPhotoLoader(ContactPhotoManager.getInstance(activity));
// Setup the "all" adapter manually. See also the setup logic in ContactEntryListFragment.
@@ -293,7 +279,7 @@ public class NewPhoneFavoriteFragment extends Fragment implements OnItemClickLis
mAllContactsAdapter.setSearchMode(false);
mAllContactsAdapter.setIncludeProfile(false);
mAllContactsAdapter.setSelectionVisible(false);
- mAllContactsAdapter.setDarkTheme(true);
+ mAllContactsAdapter.setDarkTheme(false);
mAllContactsAdapter.setPhotoLoader(ContactPhotoManager.getInstance(activity));
// Disable directory header.
mAllContactsAdapter.setHasHeader(0, false);
@@ -318,27 +304,27 @@ public class NewPhoneFavoriteFragment extends Fragment implements OnItemClickLis
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);
- }
- }
+ mCallLogQueryHandler = new CallLogQueryHandler(getActivity().getContentResolver(),
+ this, 1);
+ final String currentCountryIso = GeoUtil.getCurrentCountryIso(getActivity());
+ mCallLogAdapter = new NewCallLogAdapter(getActivity(), this,
+ new ContactInfoHelper(getActivity(), currentCountryIso));
setHasOptionsMenu(true);
}
@Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putParcelable(KEY_FILTER, mFilter);
+ public void onResume() {
+ super.onResume();
+ mCallLogQueryHandler.fetchCalls(CallLogQueryHandler.CALL_TYPE_ALL);
+ mCallLogAdapter.setLoading(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View listLayout = inflater.inflate(
- R.layout.phone_contact_tile_list, container, false);
+ R.layout.new_phone_favorites_fragment, container, false);
mListView = (ListView) listLayout.findViewById(R.id.contact_tile_list);
mListView.setItemsCanFocus(true);
@@ -347,18 +333,18 @@ public class NewPhoneFavoriteFragment extends Fragment implements OnItemClickLis
mListView.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_RIGHT);
mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
+ // TODO krelease: Don't show this header anymore
// 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(),
+ mAdapter = new NewPhoneFavoriteMergedAdapter(getActivity(),
mContactTileAdapter, mAccountFilterHeaderContainer, mAllContactsAdapter,
- mLoadingView);
+ mCallLogAdapter, mLoadingView);
mListView.setAdapter(mAdapter);
@@ -370,68 +356,48 @@ public class NewPhoneFavoriteFragment extends Fragment implements OnItemClickLis
mEmptyView.setText(getString(R.string.listTotalAllContactsZero));
mListView.setEmptyView(mEmptyView);
- updateFilterHeaderView();
-
return listLayout;
}
+ // TODO krelease: update the options menu when displaying the popup menu instead. We could
+ // possibly get rid of this method entirely.
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);
- }
-
+ // TODO krelease: Configure the menu items properly. Since the menu items show up as a PopupMenu
+ // rather than a normal actionbar menu, the initialization should be done there.
+ /*
@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,
- DialtactsActivity.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();
+ final Activity activity = getActivity();
+
+ try {
+ ((OnPhoneFavoriteFragmentStartedListener) activity).onPhoneFavoriteFragmentStarted();
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString()
+ + " must implement OnPhoneFavoriteFragmentStartedListener");
+ }
+
+ try {
+ mActivityScrollListener = (OnListFragmentScrolledListener) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString()
+ + " must implement OnListFragmentScrolledListener");
+ }
mContactsPrefs.registerChangeListener(mContactsPreferenceChangeListener);
// If ContactsPreferences has changed, we need to reload "all" contacts with the new
@@ -478,18 +444,6 @@ public class NewPhoneFavoriteFragment extends Fragment implements OnItemClickLis
}
}
- @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;
@@ -536,38 +490,24 @@ public class NewPhoneFavoriteFragment extends Fragment implements OnItemClickLis
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 void setListener(Listener listener) {
+ mListener = listener;
}
- public ContactListFilter getFilter() {
- return mFilter;
+ // TODO krelease: Implement this
+ @Override
+ public void onVoicemailStatusFetched(Cursor statusCursor) {
}
- 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();
- }
+ @Override
+ public void onCallsFetched(Cursor cursor) {
+ mCallLogAdapter.setLoading(false);
+ mCallLogAdapter.changeCursor(cursor);
+ mAdapter.notifyDataSetChanged();
}
- public void setListener(Listener listener) {
- mListener = listener;
+ // TODO krelease: Implement this
+ @Override
+ public void fetchCalls() {
}
}
diff --git a/src/com/android/dialer/list/NewPhoneFavoriteMergedAdapter.java b/src/com/android/dialer/list/NewPhoneFavoriteMergedAdapter.java
index 047609f7d..49d46a851 100644
--- a/src/com/android/dialer/list/NewPhoneFavoriteMergedAdapter.java
+++ b/src/com/android/dialer/list/NewPhoneFavoriteMergedAdapter.java
@@ -27,13 +27,15 @@ import android.widget.SectionIndexer;
import com.android.contacts.common.list.ContactEntryListAdapter;
import com.android.contacts.common.list.ContactListItemView;
-import com.android.contacts.common.list.ContactTileAdapter;
import com.android.dialer.R;
+import com.android.dialer.calllog.NewCallLogAdapter;
/**
* An adapter that combines items from {@link com.android.contacts.common.list.ContactTileAdapter} and
- * {@link com.android.contacts.common.list.ContactEntryListAdapter} into a single list. In between those two results,
- * an account filter header will be inserted.
+ * {@link com.android.contacts.common.list.ContactEntryListAdapter} into a single list.
+ * In between those two results, an account filter header will be inserted.
+ *
+ * Has one extra view at the top: The most recent call/voicemail/missed call.
*/
public class NewPhoneFavoriteMergedAdapter extends BaseAdapter implements SectionIndexer {
@@ -44,38 +46,41 @@ public class NewPhoneFavoriteMergedAdapter extends BaseAdapter implements Sectio
}
}
- private final ContactTileAdapter mContactTileAdapter;
+ private static final String TAG = NewPhoneFavoriteMergedAdapter.class.getSimpleName();
+
+ private final PhoneFavoritesTileAdapter mContactTileAdapter;
private final ContactEntryListAdapter mContactEntryListAdapter;
+ private final NewCallLogAdapter mCallLogAdapter;
private final View mAccountFilterHeaderContainer;
private final View mLoadingView;
+ // TODO krelease: Add a setting to toggle mShowAllContacts, and really handle it
+ // properly below.
+ private boolean mShowAllContacts = false;
+
private final int mItemPaddingLeft;
private final int mItemPaddingRight;
- // Make frequent header consistent with account filter header.
- private final int mFrequentHeaderPaddingTop;
-
private final DataSetObserver mObserver;
public NewPhoneFavoriteMergedAdapter(Context context,
- ContactTileAdapter contactTileAdapter,
+ PhoneFavoritesTileAdapter contactTileAdapter,
View accountFilterHeaderContainer,
ContactEntryListAdapter contactEntryListAdapter,
+ NewCallLogAdapter callLogAdapter,
View loadingView) {
- Resources resources = context.getResources();
+ final Resources resources = context.getResources();
mItemPaddingLeft = resources.getDimensionPixelSize(R.dimen.detail_item_side_margin);
mItemPaddingRight = resources.getDimensionPixelSize(R.dimen.list_visible_scrollbar_padding);
- mFrequentHeaderPaddingTop = resources.getDimensionPixelSize(
- R.dimen.contact_browser_list_top_margin);
mContactTileAdapter = contactTileAdapter;
mContactEntryListAdapter = contactEntryListAdapter;
+ mCallLogAdapter = callLogAdapter;
mAccountFilterHeaderContainer = accountFilterHeaderContainer;
mObserver = new CustomDataSetObserver();
mContactTileAdapter.registerDataSetObserver(mObserver);
mContactEntryListAdapter.registerDataSetObserver(mObserver);
-
mLoadingView = loadingView;
}
@@ -83,34 +88,52 @@ public class NewPhoneFavoriteMergedAdapter extends BaseAdapter implements Sectio
public boolean isEmpty() {
// Cannot use the super's method here because we add extra rows in getCount() to account
// for headers
- return mContactTileAdapter.getCount() + mContactEntryListAdapter.getCount() == 0;
+ return mCallLogAdapter.getCount() + mContactTileAdapter.getCount() +
+ mContactEntryListAdapter.getCount() == 0;
}
@Override
public int getCount() {
final int contactTileAdapterCount = mContactTileAdapter.getCount();
- final int contactEntryListAdapterCount = mContactEntryListAdapter.getCount();
- if (mContactEntryListAdapter.isLoading()) {
+ final int contactEntryListAdapterCount = mShowAllContacts ?
+ mContactEntryListAdapter.getCount() : 0;
+
+ final int callLogAdapterCount = mCallLogAdapter.getCount();
+
+ if (mShowAllContacts && mContactEntryListAdapter.isLoading()) {
// Hide "all" contacts during its being loaded. Instead show "loading" view.
//
// "+2" for mAccountFilterHeaderContainer and mLoadingView
- return contactTileAdapterCount + 2;
+ return contactTileAdapterCount + 1 + (mShowAllContacts ? 1 : 0) +
+ callLogAdapterCount;
} else {
// "+1" for mAccountFilterHeaderContainer
- return contactTileAdapterCount + contactEntryListAdapterCount + 1;
+ return contactTileAdapterCount + contactEntryListAdapterCount +
+ (mShowAllContacts ? 1 : 0) + callLogAdapterCount;
}
}
@Override
public Object getItem(int position) {
final int contactTileAdapterCount = mContactTileAdapter.getCount();
- final int contactEntryListAdapterCount = mContactEntryListAdapter.getCount();
+ final int contactEntryListAdapterCount = mShowAllContacts ?
+ mContactEntryListAdapter.getCount() : 0;
+
+ final int callLogAdapterCount = mCallLogAdapter.getCount();
+
+ // TODO krelease: Calculate the position properly.
+ if (callLogAdapterCount > 0) {
+ if (position < callLogAdapterCount) {
+ return mCallLogAdapter.getItem(position);
+ }
+ position -= callLogAdapterCount;
+ }
+
if (position < contactTileAdapterCount) { // For "tile" and "frequent" sections
return mContactTileAdapter.getItem(position);
- } else if (position == contactTileAdapterCount) { // For "all" section's account header
- return mAccountFilterHeaderContainer;
} else { // For "all" section
- if (mContactEntryListAdapter.isLoading()) { // "All" section is being loaded.
+ if (mShowAllContacts && mContactEntryListAdapter.isLoading()) {
+ // "All" section is being loaded.
return mLoadingView;
} else {
// "-1" for mAccountFilterHeaderContainer
@@ -127,16 +150,31 @@ public class NewPhoneFavoriteMergedAdapter extends BaseAdapter implements Sectio
@Override
public int getViewTypeCount() {
- // "+2" for mAccountFilterHeaderContainer and mLoadingView
+ // "+1" for mLoadingView
return (mContactTileAdapter.getViewTypeCount()
+ mContactEntryListAdapter.getViewTypeCount()
- + 2);
+ + 1
+ + mCallLogAdapter.getViewTypeCount());
}
@Override
public int getItemViewType(int position) {
final int contactTileAdapterCount = mContactTileAdapter.getCount();
- final int contactEntryListAdapterCount = mContactEntryListAdapter.getCount();
+
+ final int contactEntryListAdapterCount = mShowAllContacts ?
+ mContactEntryListAdapter.getCount() : 0;
+
+ final int callLogAdapterCount = mCallLogAdapter.getCount();
+
+ if (callLogAdapterCount > 0) {
+ if (position == 0) {
+ return mContactTileAdapter.getViewTypeCount() +
+ mContactEntryListAdapter.getViewTypeCount() + 2;
+ }
+ // Ignore the first position when calculating view types of all other items
+ position -= callLogAdapterCount;
+ }
+
// There should be four kinds of types that are usually used, and one more exceptional
// type (IGNORE_ITEM_VIEW_TYPE), which sometimes comes from mContactTileAdapter.
//
@@ -190,19 +228,28 @@ public class NewPhoneFavoriteMergedAdapter extends BaseAdapter implements Sectio
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final int contactTileAdapterCount = mContactTileAdapter.getCount();
- final int contactEntryListAdapterCount = mContactEntryListAdapter.getCount();
+
+ final int contactEntryListAdapterCount = mShowAllContacts ?
+ mContactEntryListAdapter.getCount() : 0;
+
+ final int callLogAdapterCount = mCallLogAdapter.getCount();
+
+ // TODO krelease: Handle the new callLogAdapterCount and position offsets properly
+ if (callLogAdapterCount > 0) {
+ if (position == 0) {
+ final View view = mCallLogAdapter.getView(position, convertView, parent);
+ return view;
+ }
+ position -= callLogAdapterCount;
+ }
// Obtain a View relevant for that position, and adjust its horizontal padding. Each
// View has different implementation, so we use different way to control those padding.
if (position < contactTileAdapterCount) { // For "tile" and "frequent" sections
final View view = mContactTileAdapter.getView(position, convertView, parent);
final int frequentHeaderPosition = mContactTileAdapter.getFrequentHeaderPosition();
- if (position < frequentHeaderPosition) { // "starred" contacts
- // No padding adjustment.
- } else if (position == frequentHeaderPosition) {
- view.setPadding(mItemPaddingLeft, mFrequentHeaderPaddingTop,
- mItemPaddingRight, view.getPaddingBottom());
- } else {
+ // TODO krelease: Get rid of frequent header position, we don't need it anymore
+ if (position >= frequentHeaderPosition) {
// Views for "frequent" contacts use FrameLayout's margins instead of padding.
final FrameLayout frameLayout = (FrameLayout) view;
final View child = frameLayout.getChildAt(0);
@@ -213,28 +260,16 @@ public class NewPhoneFavoriteMergedAdapter extends BaseAdapter implements Sectio
child.setLayoutParams(params);
}
return view;
- } else if (position == contactTileAdapterCount) { // For "all" section's account header
- mAccountFilterHeaderContainer.setPadding(mItemPaddingLeft,
- mAccountFilterHeaderContainer.getPaddingTop(),
- mItemPaddingRight,
- mAccountFilterHeaderContainer.getPaddingBottom());
-
- // Show a single "No Contacts" label under the "all" section account header
- // if no contacts are displayed.
- mAccountFilterHeaderContainer.findViewById(
- R.id.contact_list_all_empty).setVisibility(
- contactEntryListAdapterCount == 0 ? View.VISIBLE : View.GONE);
- return mAccountFilterHeaderContainer;
} else { // For "all" section
- if (mContactEntryListAdapter.isLoading()) { // "All" section is being loaded.
+ if (mShowAllContacts && mContactEntryListAdapter.isLoading()) {
+ // "All" section is being loaded.
mLoadingView.setPadding(mItemPaddingLeft,
mLoadingView.getPaddingTop(),
mItemPaddingRight,
mLoadingView.getPaddingBottom());
return mLoadingView;
} else {
- // "-1" for mAccountFilterHeaderContainer
- final int localPosition = position - contactTileAdapterCount - 1;
+ final int localPosition = position - contactTileAdapterCount;
final ContactListItemView itemView = (ContactListItemView)
mContactEntryListAdapter.getView(localPosition, convertView, null);
itemView.setPadding(mItemPaddingLeft, itemView.getPaddingTop(),
@@ -278,9 +313,10 @@ public class NewPhoneFavoriteMergedAdapter extends BaseAdapter implements Sectio
@Override
public int getPositionForSection(int sectionIndex) {
+ // frequent header view.
final int contactTileAdapterCount = mContactTileAdapter.getCount();
final int localPosition = mContactEntryListAdapter.getPositionForSection(sectionIndex);
- return contactTileAdapterCount + 1 + localPosition;
+ return contactTileAdapterCount + localPosition;
}
@Override
@@ -289,8 +325,7 @@ public class NewPhoneFavoriteMergedAdapter extends BaseAdapter implements Sectio
if (position <= contactTileAdapterCount) {
return 0;
} else {
- // "-1" for mAccountFilterHeaderContainer
- final int localPosition = position - contactTileAdapterCount - 1;
+ final int localPosition = position - contactTileAdapterCount;
return mContactEntryListAdapter.getSectionForPosition(localPosition);
}
}
diff --git a/src/com/android/dialer/list/OnListFragmentScrolledListener.java b/src/com/android/dialer/list/OnListFragmentScrolledListener.java
new file mode 100644
index 000000000..cc5f3cd3a
--- /dev/null
+++ b/src/com/android/dialer/list/OnListFragmentScrolledListener.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ * Licensed to 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;
+
+/*
+ * Interface to provide callback to activity when a child fragment is scrolled
+ */
+public interface OnListFragmentScrolledListener {
+ public void onListFragmentScrollStateChange(int scrollState);
+}
diff --git a/src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java b/src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java
index ba291a00f..bb758a710 100644
--- a/src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java
+++ b/src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java
@@ -130,7 +130,7 @@ public class PhoneFavoriteMergedAdapter extends BaseAdapter implements SectionIn
// "+2" for mAccountFilterHeaderContainer and mLoadingView
return (mContactTileAdapter.getViewTypeCount()
+ mContactEntryListAdapter.getViewTypeCount()
- + 2);
+ + 1);
}
@Override
diff --git a/src/com/android/dialer/list/PhoneFavoriteRegularRowView.java b/src/com/android/dialer/list/PhoneFavoriteRegularRowView.java
new file mode 100644
index 000000000..2f5921eaf
--- /dev/null
+++ b/src/com/android/dialer/list/PhoneFavoriteRegularRowView.java
@@ -0,0 +1,82 @@
+/*
+ * 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.content.Context;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.android.contacts.common.MoreContactUtils;
+import com.android.contacts.common.list.ContactEntry;
+import com.android.contacts.common.list.ContactTileView;
+import com.android.contacts.common.util.ViewUtil;
+
+/**
+ * A light version of the {@link com.android.contacts.common.list.ContactTileView} that is used in Dialtacts
+ * for frequently called contacts. Slightly different behavior from superclass...
+ * when you tap it, you want to call the frequently-called number for the
+ * contact, even if that is not the default number for that contact.
+ */
+public class PhoneFavoriteRegularRowView extends ContactTileView {
+ private String mPhoneNumberString;
+
+ public PhoneFavoriteRegularRowView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected boolean isDarkTheme() {
+ return false;
+ }
+
+ @Override
+ protected int getApproximateImageSize() {
+ return ViewUtil.getConstantPreLayoutWidth(getQuickContact());
+ }
+
+ @Override
+ public void loadFromContact(ContactEntry entry) {
+ super.loadFromContact(entry);
+ mPhoneNumberString = null; // ... in case we're reusing the view
+ if (entry != null) {
+ // Grab the phone-number to call directly... see {@link onClick()}
+ mPhoneNumberString = entry.phoneNumber;
+ }
+ }
+
+ @Override
+ protected OnClickListener createClickListener() {
+ return new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mListener == null) return;
+ if (TextUtils.isEmpty(mPhoneNumberString)) {
+ // Copy "superclass" implementation
+ mListener.onContactSelected(getLookupUri(), MoreContactUtils
+ .getTargetRectFromView(
+ mContext, PhoneFavoriteRegularRowView.this));
+ } else {
+ // When you tap a frequently-called contact, you want to
+ // call them at the number that you usually talk to them
+ // at (i.e. the one displayed in the UI), regardless of
+ // whether that's their default number.
+ mListener.onCallNumberDirectly(mPhoneNumberString);
+ }
+ }
+ };
+ }
+}
diff --git a/src/com/android/dialer/list/PhoneFavoriteTileView.java b/src/com/android/dialer/list/PhoneFavoriteTileView.java
new file mode 100644
index 000000000..d87e2a837
--- /dev/null
+++ b/src/com/android/dialer/list/PhoneFavoriteTileView.java
@@ -0,0 +1,65 @@
+/*
+ * 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.content.Context;
+import android.content.Intent;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageButton;
+
+import com.android.contacts.common.R;
+import com.android.contacts.common.list.ContactTileView;
+
+/**
+ * Displays the contact's picture overlayed with their name
+ * in a perfect square. It also has an additional touch target for a secondary action.
+ */
+public class PhoneFavoriteTileView extends ContactTileView {
+ private ImageButton mSecondaryButton;
+
+ public PhoneFavoriteTileView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mSecondaryButton = (ImageButton) findViewById(R.id.contact_tile_secondary_button);
+ mSecondaryButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_VIEW, getLookupUri());
+ // Secondary target will be visible only from phone's favorite screen, then
+ // we want to launch it as a separate People task.
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ getContext().startActivity(intent);
+ }
+ });
+ }
+
+ @Override
+ protected boolean isDarkTheme() {
+ return false;
+ }
+
+ @Override
+ protected int getApproximateImageSize() {
+ // The picture is the full size of the tile (minus some padding, but we can be generous)
+ return mListener.getApproximateTileWidth();
+ }
+}
diff --git a/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java b/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java
new file mode 100644
index 000000000..36fc34608
--- /dev/null
+++ b/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java
@@ -0,0 +1,515 @@
+/*
+ * Copyright (C) 2013 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.content.ContentUris;
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.Contacts;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.FrameLayout;
+
+import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.ContactTileLoaderFactory;
+import com.android.contacts.common.R;
+import com.android.contacts.common.list.ContactEntry;
+import com.android.contacts.common.list.ContactTileAdapter;
+import com.android.contacts.common.list.ContactTileView;
+
+import java.util.ArrayList;
+
+/**
+ * Also allows for a configurable number of columns as well as a maximum row of tiled contacts.
+ *
+ * This adapter has been rewritten to only support a maximum of one row for favorites.
+ *
+ * TODO Krelease: Move to PhoneContactTileAdapter in Dialer package.
+ */
+public class PhoneFavoritesTileAdapter extends BaseAdapter {
+ private static final String TAG = ContactTileAdapter.class.getSimpleName();
+
+ public static final int NO_ROW_LIMIT = -1;
+
+ private ContactTileView.Listener mListener;
+ private Context mContext;
+ private Resources mResources;
+ protected Cursor mContactCursor = null;
+ private ContactPhotoManager mPhotoManager;
+ protected int mNumFrequents;
+
+ /**
+ * Index of the first NON starred contact in the {@link Cursor}
+ * Only valid when {@link DisplayType#STREQUENT} is true
+ */
+ private int mDividerPosition;
+ protected int mColumnCount;
+ private int mMaxTiledRows = NO_ROW_LIMIT;
+ private int mStarredIndex;
+
+ protected int mIdIndex;
+ protected int mLookupIndex;
+ protected int mPhotoUriIndex;
+ protected int mNameIndex;
+ protected int mPresenceIndex;
+ protected int mStatusIndex;
+
+ /**
+ * Only valid when {@link DisplayType#STREQUENT_PHONE_ONLY} is true
+ */
+ private int mPhoneNumberIndex;
+ private int mPhoneNumberTypeIndex;
+ private int mPhoneNumberLabelIndex;
+
+ private boolean mIsQuickContactEnabled = false;
+ private final int mPaddingInPixels;
+
+ public PhoneFavoritesTileAdapter(Context context, ContactTileView.Listener listener, int numCols) {
+ this(context, listener, numCols, NO_ROW_LIMIT);
+ }
+
+ public PhoneFavoritesTileAdapter(Context context, ContactTileView.Listener listener, int numCols,
+ int maxTiledRows) {
+ mListener = listener;
+ mContext = context;
+ mResources = context.getResources();
+ mColumnCount = numCols;
+ mNumFrequents = 0;
+ mMaxTiledRows = maxTiledRows;
+
+ // Converting padding in dips to padding in pixels
+ mPaddingInPixels = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.contact_tile_divider_padding);
+ bindColumnIndices();
+ }
+
+ public void setPhotoLoader(ContactPhotoManager photoLoader) {
+ mPhotoManager = photoLoader;
+ }
+
+ public void setMaxRowCount(int maxRows) {
+ mMaxTiledRows = maxRows;
+ }
+
+ public void setColumnCount(int columnCount) {
+ mColumnCount = columnCount;
+ }
+
+ public void enableQuickContact(boolean enableQuickContact) {
+ mIsQuickContactEnabled = enableQuickContact;
+ }
+
+ /**
+ * Sets the column indices for expected {@link Cursor}
+ * based on {@link DisplayType}.
+ */
+ protected void bindColumnIndices() {
+ mIdIndex = ContactTileLoaderFactory.CONTACT_ID;
+ mLookupIndex = ContactTileLoaderFactory.LOOKUP_KEY;
+ mPhotoUriIndex = ContactTileLoaderFactory.PHOTO_URI;
+ mNameIndex = ContactTileLoaderFactory.DISPLAY_NAME;
+ mStarredIndex = ContactTileLoaderFactory.STARRED;
+ mPresenceIndex = ContactTileLoaderFactory.CONTACT_PRESENCE;
+ mStatusIndex = ContactTileLoaderFactory.CONTACT_STATUS;
+
+ mPhoneNumberIndex = ContactTileLoaderFactory.PHONE_NUMBER;
+ mPhoneNumberTypeIndex = ContactTileLoaderFactory.PHONE_NUMBER_TYPE;
+ mPhoneNumberLabelIndex = ContactTileLoaderFactory.PHONE_NUMBER_LABEL;
+ }
+
+ /**
+ * Gets the number of frequents from the passed in cursor.
+ *
+ * This methods is needed so the GroupMemberTileAdapter can override this.
+ *
+ * @param cursor The cursor to get number of frequents from.
+ */
+ protected void saveNumFrequentsFromCursor(Cursor cursor) {
+ mNumFrequents = cursor.getCount() - mDividerPosition;
+ }
+
+ /**
+ * Creates {@link ContactTileView}s for each item in {@link Cursor}.
+ *
+ * Else use {@link ContactTileLoaderFactory}
+ */
+ public void setContactCursor(Cursor cursor) {
+ mContactCursor = cursor;
+ mDividerPosition = getDividerPosition(cursor);
+
+ saveNumFrequentsFromCursor(cursor);
+
+ // cause a refresh of any views that rely on this data
+ notifyDataSetChanged();
+ }
+
+ /**
+ * Iterates over the {@link Cursor}
+ * Returns position of the first NON Starred Contact
+ * Returns -1 if {@link DisplayType#STARRED_ONLY}
+ * Returns 0 if {@link DisplayType#FREQUENT_ONLY}
+ */
+ protected int getDividerPosition(Cursor cursor) {
+ if (cursor == null || cursor.isClosed()) {
+ throw new IllegalStateException("Unable to access cursor");
+ }
+
+ cursor.moveToPosition(-1);
+ while (cursor.moveToNext()) {
+ if (cursor.getInt(mStarredIndex) == 0) {
+ return cursor.getPosition();
+ }
+ }
+
+ // There are not NON Starred contacts in cursor
+ // Set divider positon to end
+ return cursor.getCount();
+ }
+
+ protected ContactEntry createContactEntryFromCursor(Cursor cursor, int position) {
+ // If the loader was canceled we will be given a null cursor.
+ // In that case, show an empty list of contacts.
+ if (cursor == null || cursor.isClosed() || cursor.getCount() <= position) return null;
+
+ cursor.moveToPosition(position);
+ long id = cursor.getLong(mIdIndex);
+ String photoUri = cursor.getString(mPhotoUriIndex);
+ String lookupKey = cursor.getString(mLookupIndex);
+
+ ContactEntry contact = new ContactEntry();
+ String name = cursor.getString(mNameIndex);
+ contact.name = (name != null) ? name : mResources.getString(R.string.missing_name);
+ contact.status = cursor.getString(mStatusIndex);
+ contact.photoUri = (photoUri != null ? Uri.parse(photoUri) : null);
+ contact.lookupKey = ContentUris.withAppendedId(
+ Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), id);
+
+ // Set phone number and label
+ int phoneNumberType = cursor.getInt(mPhoneNumberTypeIndex);
+ String phoneNumberCustomLabel = cursor.getString(mPhoneNumberLabelIndex);
+ contact.phoneLabel = (String) Phone.getTypeLabel(mResources, phoneNumberType,
+ phoneNumberCustomLabel);
+ contact.phoneNumber = cursor.getString(mPhoneNumberIndex);
+
+ return contact;
+ }
+
+ /**
+ * Returns the number of frequents that will be displayed in the list.
+ */
+ public int getNumFrequents() {
+ return mNumFrequents;
+ }
+
+ @Override
+ public int getCount() {
+ if (mContactCursor == null || mContactCursor.isClosed()) {
+ return 0;
+ }
+
+ // Takes numbers of rows the Starred Contacts Occupy
+ int starredRowCount = getRowCount(mDividerPosition) +
+ (mMaxTiledRows == NO_ROW_LIMIT ? 0 : Math.max(0, mDividerPosition -
+ mMaxTiledRows * mColumnCount));
+
+ // Compute the frequent row count which is 1 plus the number of frequents
+ // (to account for the divider) or 0 if there are no frequents.
+ int frequentRowCount = mNumFrequents == 0 ? 0 : mNumFrequents;
+
+ // Return the number of starred plus frequent rows
+ return starredRowCount + frequentRowCount;
+ }
+
+ /**
+ * Returns the number of rows required to show the provided number of entries
+ * with the current number of columns.
+ */
+ protected int getRowCount(int entryCount) {
+ if (entryCount == 0) return 0;
+ final int nonLimitedRows = ((entryCount - 1) / mColumnCount) + 1;
+ return mMaxTiledRows == NO_ROW_LIMIT ? nonLimitedRows : Math.min(mMaxTiledRows,
+ nonLimitedRows);
+ }
+
+ public int getColumnCount() {
+ return mColumnCount;
+ }
+
+ /**
+ * Returns an ArrayList of the {@link ContactEntry}s that are to appear
+ * on the row for the given position.
+ */
+ @Override
+ public ArrayList<ContactEntry> getItem(int position) {
+ ArrayList<ContactEntry> resultList = new ArrayList<ContactEntry>(mColumnCount);
+ int contactIndex = position * mColumnCount;
+ if (position < getRowCount(mDividerPosition)) {
+ for (int columnCounter = 0; columnCounter < mColumnCount &&
+ contactIndex != mDividerPosition; columnCounter++) {
+ resultList.add(createContactEntryFromCursor(mContactCursor, contactIndex));
+ contactIndex++;
+ }
+ } else {
+ /*
+ * Current position minus how many rows are before the divider and Minus 1 for the
+ * divider itself provides the relative index of the frequent contact being displayed.
+ * Then add the dividerPostion to give the offset into the contacts cursor to get the
+ * absolute index.
+ */
+ final int rowCount = getRowCount(mDividerPosition);
+ contactIndex = position - rowCount + Math.min(mDividerPosition,
+ rowCount * mColumnCount);
+ resultList.add(createContactEntryFromCursor(mContactCursor, contactIndex));
+ }
+ return resultList;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ // As we show several selectable items for each ListView row,
+ // we can not determine a stable id. But as we don't rely on ListView's selection,
+ // this should not be a problem.
+ return position;
+ }
+
+ @Override
+ public boolean areAllItemsEnabled() {
+ // No dividers, so all items are enabled.
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ return position != getRowCount(mDividerPosition);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ int itemViewType = getItemViewType(position);
+
+ ContactTileRow contactTileRowView = (ContactTileRow) convertView;
+ ArrayList<ContactEntry> contactList = getItem(position);
+
+ if (contactTileRowView == null) {
+ // Creating new row if needed
+ contactTileRowView = new ContactTileRow(mContext, itemViewType);
+ }
+
+ contactTileRowView.configureRow(contactList, position == getCount() - 1);
+ return contactTileRowView;
+ }
+
+ private int getLayoutResourceId(int viewType) {
+ switch (viewType) {
+ case ViewTypes.FREQUENT:
+ return R.layout.phone_favorite_regular_row_view;
+ case ViewTypes.STARRED_PHONE:
+ return R.layout.phone_favorite_tile_view;
+ default:
+ throw new IllegalArgumentException("Unrecognized viewType " + viewType);
+ }
+ }
+ @Override
+ public int getViewTypeCount() {
+ return ViewTypes.COUNT;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ if (position < getRowCount(mDividerPosition)) {
+ return ViewTypes.STARRED_PHONE;
+ } else {
+ return ViewTypes.FREQUENT;
+ }
+ }
+
+ /**
+ * Returns the "frequent header" position. Only available when STREQUENT or
+ * STREQUENT_PHONE_ONLY is used for its display type.
+ */
+ public int getFrequentHeaderPosition() {
+ return getRowCount(mDividerPosition);
+ }
+
+ /**
+ * Acts as a row item composed of {@link ContactTileView}
+ *
+ * TODO: FREQUENT doesn't really need it. Just let {@link #getView} return
+ */
+ private class ContactTileRow extends FrameLayout {
+ private int mItemViewType;
+ private int mLayoutResId;
+
+ public ContactTileRow(Context context, int itemViewType) {
+ super(context);
+ mItemViewType = itemViewType;
+ mLayoutResId = getLayoutResourceId(mItemViewType);
+
+ // Remove row (but not children) from accessibility node tree.
+ setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+ }
+
+ /**
+ * Configures the row to add {@link ContactEntry}s information to the views
+ */
+ public void configureRow(ArrayList<ContactEntry> list, boolean isLastRow) {
+ int columnCount = mItemViewType == ViewTypes.FREQUENT ? 1 : mColumnCount;
+
+ // Adding tiles to row and filling in contact information
+ for (int columnCounter = 0; columnCounter < columnCount; columnCounter++) {
+ ContactEntry entry =
+ columnCounter < list.size() ? list.get(columnCounter) : null;
+ addTileFromEntry(entry, columnCounter, isLastRow);
+ }
+ }
+
+ private void addTileFromEntry(ContactEntry entry, int childIndex, boolean isLastRow) {
+ final ContactTileView contactTile;
+
+ if (getChildCount() <= childIndex) {
+
+ contactTile = (ContactTileView) inflate(mContext, mLayoutResId, null);
+ // Note: the layoutparam set here is only actually used for FREQUENT.
+ // We override onMeasure() for STARRED and we don't care the layout param there.
+ Resources resources = mContext.getResources();
+ FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+
+ params.setMargins(
+ resources.getDimensionPixelSize(R.dimen.detail_item_side_margin),
+ 0,
+ resources.getDimensionPixelSize(R.dimen.detail_item_side_margin),
+ 0);
+ contactTile.setLayoutParams(params);
+ contactTile.setPhotoManager(mPhotoManager);
+ contactTile.setListener(mListener);
+ addView(contactTile);
+ } else {
+ contactTile = (ContactTileView) getChildAt(childIndex);
+ }
+ contactTile.loadFromContact(entry);
+ switch (mItemViewType) {
+ case ViewTypes.STARRED_PHONE:
+ // Setting divider visibilities
+ contactTile.setPaddingRelative(0, 0,
+ childIndex >= mColumnCount - 1 ? 0 : mPaddingInPixels,
+ isLastRow ? 0 : mPaddingInPixels);
+ break;
+ case ViewTypes.FREQUENT:
+ contactTile.setHorizontalDividerVisibility(
+ isLastRow ? View.GONE : View.VISIBLE);
+ break;
+ default:
+ break;
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ switch (mItemViewType) {
+ case ViewTypes.STARRED_PHONE:
+ onLayoutForTiles();
+ return;
+ default:
+ super.onLayout(changed, left, top, right, bottom);
+ return;
+ }
+ }
+
+ private void onLayoutForTiles() {
+ final int count = getChildCount();
+
+ // Just line up children horizontally.
+ int childLeft = 0;
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+
+ // Note MeasuredWidth includes the padding.
+ final int childWidth = child.getMeasuredWidth();
+ child.layout(childLeft, 0, childLeft + childWidth, child.getMeasuredHeight());
+ childLeft += childWidth;
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ switch (mItemViewType) {
+ case ViewTypes.STARRED_PHONE:
+ case ViewTypes.STARRED:
+ onMeasureForTiles(widthMeasureSpec);
+ return;
+ default:
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ return;
+ }
+ }
+
+ private void onMeasureForTiles(int widthMeasureSpec) {
+ final int width = MeasureSpec.getSize(widthMeasureSpec);
+
+ final int childCount = getChildCount();
+ if (childCount == 0) {
+ // Just in case...
+ setMeasuredDimension(width, 0);
+ return;
+ }
+
+ // 1. Calculate image size.
+ // = ([total width] - [total padding]) / [child count]
+ //
+ // 2. Set it to width/height of each children.
+ // If we have a remainder, some tiles will have 1 pixel larger width than its height.
+ //
+ // 3. Set the dimensions of itself.
+ // Let width = given width.
+ // Let height = image size + bottom paddding.
+
+ final int totalPaddingsInPixels = (mColumnCount - 1) * mPaddingInPixels;
+
+ // Preferred width / height for images (excluding the padding).
+ // The actual width may be 1 pixel larger than this if we have a remainder.
+ final int imageSize = (width - totalPaddingsInPixels) / mColumnCount;
+ final int remainder = width - (imageSize * mColumnCount) - totalPaddingsInPixels;
+
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+ final int childWidth = imageSize + child.getPaddingRight()
+ // Compensate for the remainder
+ + (i < remainder ? 1 : 0);
+ final int childHeight = imageSize + child.getPaddingBottom();
+ child.measure(
+ MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY)
+ );
+ }
+ setMeasuredDimension(width, imageSize + getChildAt(0).getPaddingBottom());
+ }
+ }
+
+ protected static class ViewTypes {
+ public static final int COUNT = 3;
+ public static final int STARRED = 0;
+ public static final int FREQUENT = 1;
+ public static final int STARRED_PHONE = 2;
+ }
+}
diff --git a/src/com/android/dialer/list/SmartDialNumberListAdapter.java b/src/com/android/dialer/list/SmartDialNumberListAdapter.java
new file mode 100644
index 000000000..0413c4ee5
--- /dev/null
+++ b/src/com/android/dialer/list/SmartDialNumberListAdapter.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2013 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.content.ContentUris;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Callable;
+import android.telephony.PhoneNumberUtils;
+import android.util.Log;
+
+import com.android.contacts.common.list.ContactListItemView;
+import com.android.contacts.common.list.PhoneNumberListAdapter;
+import com.android.dialer.dialpad.SmartDialCursorLoader;
+import com.android.dialer.dialpad.SmartDialCursorLoader.SmartDialPhoneQuery;
+import com.android.dialer.dialpad.SmartDialNameMatcher;
+import com.android.dialer.dialpad.SmartDialPrefix;
+import com.android.dialer.dialpad.SmartDialMatchPosition;
+
+import java.util.ArrayList;
+
+/**
+ * List adapter to display the SmartDial search results.
+ */
+public class SmartDialNumberListAdapter extends PhoneNumberListAdapter{
+
+ private static final String TAG = SmartDialNumberListAdapter.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
+ private SmartDialNameMatcher mNameMatcher;
+
+ public SmartDialNumberListAdapter(Context context) {
+ super(context);
+ if (DEBUG) {
+ Log.v(TAG, "Constructing List Adapter");
+ }
+ }
+
+ /**
+ * Sets query for the SmartDialCursorLoader.
+ */
+ public void configureLoader(SmartDialCursorLoader loader) {
+ if (DEBUG) {
+ Log.v(TAG, "Congifugure Loader with query" + getQueryString());
+ }
+
+ if (getQueryString() == null) {
+ mNameMatcher = new SmartDialNameMatcher("", SmartDialPrefix.getMap());
+ loader.configureQuery("");
+ } else {
+ loader.configureQuery(getQueryString());
+ mNameMatcher = new SmartDialNameMatcher(PhoneNumberUtils.normalizeNumber(
+ getQueryString()), SmartDialPrefix.getMap());
+ }
+ }
+
+ /**
+ * Sets highlight options for a List item in the SmartDial search results.
+ * @param view ContactListItemView where the result will be displayed.
+ * @param cursor Object containing information of the associated List item.
+ */
+ @Override
+ protected void setHighlight(ContactListItemView view, Cursor cursor) {
+ view.clearHighlightSequences();
+
+ if (mNameMatcher.matches(cursor.getString(SmartDialPhoneQuery.SMARTDIAL_DISPLAY_NAME))) {
+ final ArrayList<SmartDialMatchPosition> nameMatches = mNameMatcher.getMatchPositions();
+ for (SmartDialMatchPosition match:nameMatches) {
+ view.addNameHighlightSequence(match.start, match.end);
+ if (DEBUG) {
+ Log.v(TAG, cursor.getString(SmartDialPhoneQuery.SMARTDIAL_DISPLAY_NAME) + " " +
+ mNameMatcher.getQuery() + " " + String.valueOf(match.start));
+ }
+ }
+ }
+
+ final SmartDialMatchPosition numberMatch = mNameMatcher.matchesNumber(cursor.getString(
+ SmartDialPhoneQuery.SMARTDIAL_NUMBER));
+ if (numberMatch != null) {
+ view.addNumberHighlightSequence(numberMatch.start, numberMatch.end);
+ }
+ }
+
+ /**
+ * Gets Uri for the list item at the given position.
+ * @param position Location of the data of interest.
+ * @return Data Uri of the entry.
+ */
+ public Uri getDataUri(int position) {
+ Cursor cursor = ((Cursor)getItem(position));
+ if (cursor != null) {
+ long id = cursor.getLong(SmartDialPhoneQuery.SMARTDIAL_ID);
+ return ContentUris.withAppendedId(ContactsContract.Data.CONTENT_URI, id);
+ } else {
+ Log.w(TAG, "Cursor was null in getDataUri() call. Returning null instead.");
+ return null;
+ }
+ }
+}
diff --git a/src/com/android/dialer/list/SmartDialNumberPickerFragment.java b/src/com/android/dialer/list/SmartDialNumberPickerFragment.java
new file mode 100644
index 000000000..0892de64a
--- /dev/null
+++ b/src/com/android/dialer/list/SmartDialNumberPickerFragment.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2013 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.content.Loader;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract.Directory;
+import android.util.Log;
+
+import com.android.contacts.common.list.ContactEntryListAdapter;
+import com.android.contacts.common.list.PhoneNumberPickerFragment;
+import com.android.dialer.dialpad.SmartDialCursorLoader;
+
+/**
+ * Implements a fragment to load and display SmartDial search results.
+ */
+public class SmartDialNumberPickerFragment extends PhoneNumberPickerFragment {
+
+ private static final String TAG = SmartDialNumberPickerFragment.class.getSimpleName();
+
+ /**
+ * Creates a SmartDialListAdapter to display and operate on search results.
+ * @return
+ */
+ @Override
+ protected ContactEntryListAdapter createListAdapter() {
+ SmartDialNumberListAdapter adapter = new SmartDialNumberListAdapter(getActivity());
+ adapter.setDisplayPhotos(true);
+ adapter.setUseCallableUri(super.usesCallableUri());
+ return adapter;
+ }
+
+ /**
+ * Creates a SmartDialCursorLoader object to load query results.
+ */
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ /** SmartDial does not support Directory Load, falls back to normal search instead. */
+ if (id == getDirectoryLoaderId()) {
+ Log.v(TAG, "Directory load");
+ return super.onCreateLoader(id, args);
+ } else {
+ Log.v(TAG, "Creating loader");
+ final SmartDialNumberListAdapter adapter = (SmartDialNumberListAdapter) getAdapter();
+ SmartDialCursorLoader loader = new SmartDialCursorLoader(super.getContext());
+ adapter.configureLoader(loader);
+ return loader;
+ }
+ }
+
+ /**
+ * Gets the Phone Uri of an entry for calling.
+ * @param position Location of the data of interest.
+ * @return Phone Uri to establish a phone call.
+ */
+ @Override
+ protected Uri getPhoneUri(int position) {
+ final SmartDialNumberListAdapter adapter = (SmartDialNumberListAdapter) getAdapter();
+ return adapter.getDataUri(position);
+ }
+}
diff --git a/src/com/android/dialer/list/SmartDialSearchFragment.java b/src/com/android/dialer/list/SmartDialSearchFragment.java
new file mode 100644
index 000000000..3c1e51343
--- /dev/null
+++ b/src/com/android/dialer/list/SmartDialSearchFragment.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2013 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.content.Loader;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract.Directory;
+import android.util.Log;
+import android.widget.AbsListView;
+import android.widget.AbsListView.OnScrollListener;
+
+import com.android.contacts.common.list.ContactEntryListAdapter;
+import com.android.contacts.common.list.ContactListItemView;
+import com.android.contacts.common.list.PhoneNumberPickerFragment;
+import com.android.dialer.dialpad.SmartDialCursorLoader;
+
+/**
+ * Implements a fragment to load and display SmartDial search results.
+ */
+public class SmartDialSearchFragment extends PhoneNumberPickerFragment {
+ private static final String TAG = SmartDialSearchFragment.class.getSimpleName();
+
+ private OnListFragmentScrolledListener mActivityScrollListener;
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+
+ setQuickContactEnabled(true);
+ setDarkTheme(false);
+ setPhotoPosition(ContactListItemView.getDefaultPhotoPosition(true /* opposite */));
+ setUseCallableUri(true);
+
+ try {
+ mActivityScrollListener = (OnListFragmentScrolledListener) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString()
+ + " must implement OnListFragmentScrolledListener");
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ getListView().setOnScrollListener(new OnScrollListener() {
+ @Override
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ mActivityScrollListener.onListFragmentScrollStateChange(scrollState);
+ }
+
+ @Override
+ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
+ int totalItemCount) {
+ }
+ });
+ }
+
+ /**
+ * Creates a SmartDialListAdapter to display and operate on search results.
+ */
+ @Override
+ protected ContactEntryListAdapter createListAdapter() {
+ SmartDialNumberListAdapter adapter = new SmartDialNumberListAdapter(getActivity());
+ adapter.setUseCallableUri(super.usesCallableUri());
+ adapter.setQuickContactEnabled(true);
+ return adapter;
+ }
+
+ /**
+ * Creates a SmartDialCursorLoader object to load query results.
+ */
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ // Smart dialing does not support Directory Load, falls back to normal search instead.
+ if (id == getDirectoryLoaderId()) {
+ Log.v(TAG, "Directory load");
+ return super.onCreateLoader(id, args);
+ } else {
+ Log.v(TAG, "Creating loader");
+ final SmartDialNumberListAdapter adapter = (SmartDialNumberListAdapter) getAdapter();
+ SmartDialCursorLoader loader = new SmartDialCursorLoader(super.getContext());
+ adapter.configureLoader(loader);
+ return loader;
+ }
+ }
+
+ /**
+ * Gets the Phone Uri of an entry for calling.
+ * @param position Location of the data of interest.
+ * @return Phone Uri to establish a phone call.
+ */
+ @Override
+ protected Uri getPhoneUri(int position) {
+ final SmartDialNumberListAdapter adapter = (SmartDialNumberListAdapter) getAdapter();
+ return adapter.getDataUri(position);
+ }
+
+ @Override
+ protected void setSearchMode(boolean flag) {
+ super.setSearchMode(flag);
+ // This hides the "All contacts with phone numbers" header in the search fragment
+ getAdapter().setHasHeader(0, false);
+ }
+}