summaryrefslogtreecommitdiff
path: root/src/com/android/dialer/list
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/dialer/list')
-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
10 files changed, 1153 insertions, 182 deletions
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);
+ }
+}