Merge "[Phone] New phone favorite tab."
diff --git a/res/drawable-hdpi/ic_dial_action_search.png b/res/drawable-hdpi/ic_dial_action_search.png
new file mode 100644
index 0000000..898ce11
--- /dev/null
+++ b/res/drawable-hdpi/ic_dial_action_search.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_dial_action_search.png b/res/drawable-mdpi/ic_dial_action_search.png
new file mode 100644
index 0000000..88cba92
--- /dev/null
+++ b/res/drawable-mdpi/ic_dial_action_search.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_dial_action_search.png b/res/drawable-xhdpi/ic_dial_action_search.png
new file mode 100644
index 0000000..fe3aa24
--- /dev/null
+++ b/res/drawable-xhdpi/ic_dial_action_search.png
Binary files differ
diff --git a/res/layout/account_filter_header.xml b/res/layout/account_filter_header.xml
new file mode 100644
index 0000000..4d45d25
--- /dev/null
+++ b/res/layout/account_filter_header.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- Layout showing the type of account filter
+ (e.g. All contacts filter, custom filter, etc.),
+ which is the header of all contact lists. -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/account_filter_header_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?attr/list_item_header_height"
+ android:orientation="vertical"
+ android:paddingTop="@dimen/contact_browser_list_top_margin"
+ android:layout_marginLeft="@dimen/contact_browser_list_header_left_margin"
+ android:layout_marginRight="@dimen/contact_browser_list_header_right_margin"
+ android:background="?android:attr/selectableItemBackground"
+ android:visibility="gone">
+ <TextView
+ android:id="@+id/account_filter_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:textStyle="bold"
+ android:textAllCaps="true"
+ android:paddingLeft="@dimen/contact_browser_list_item_text_indent"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary" />
+ <View
+ android:id="@+id/account_filter_header_bottom_divider"
+ android:layout_height="1dip"
+ style="@style/SectionDivider" />
+</LinearLayout>
diff --git a/res/layout/contacts_list_content.xml b/res/layout/contacts_list_content.xml
index dfa99f6..05f1930 100644
--- a/res/layout/contacts_list_content.xml
+++ b/res/layout/contacts_list_content.xml
@@ -29,33 +29,9 @@
<!-- Shown only when an Account filter is set.
- paddingTop should be here to show "shade" effect correctly. -->
- <LinearLayout
+ <include
android:id="@+id/account_filter_header_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="?attr/list_item_header_height"
- android:orientation="vertical"
- android:paddingTop="@dimen/contact_browser_list_top_margin"
- android:layout_marginLeft="@dimen/contact_browser_list_header_left_margin"
- android:layout_marginRight="@dimen/contact_browser_list_header_right_margin"
- android:background="?android:attr/selectableItemBackground"
- android:visibility="gone">
- <TextView
- android:id="@+id/account_filter_header"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="end"
- android:textStyle="bold"
- android:textAllCaps="true"
- android:paddingLeft="@dimen/contact_browser_list_item_text_indent"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:attr/textColorSecondary" />
- <View
- android:id="@+id/account_filter_header_bottom_divider"
- android:layout_height="1dip"
- style="@style/SectionDivider" />
- </LinearLayout>
+ layout="@layout/account_filter_header" />
<view
class="com.android.contacts.widget.PinnedHeaderListView"
diff --git a/res/layout/dialpad_additional_buttons.xml b/res/layout/dialpad_additional_buttons.xml
index 2d42ad0..836187e 100644
--- a/res/layout/dialpad_additional_buttons.xml
+++ b/res/layout/dialpad_additional_buttons.xml
@@ -34,7 +34,7 @@
android:state_enabled="false"
android:background="?android:attr/selectableItemBackground"
android:contentDescription="@string/description_search_button"
- android:src="@drawable/ic_see_contacts_holo_dark"/>
+ android:src="@drawable/ic_dial_action_search"/>
<View
android:layout_width="1dip"
diff --git a/res/menu/dialtacts_options.xml b/res/menu/dialtacts_options.xml
index f653242..54ca086 100644
--- a/res/menu/dialtacts_options.xml
+++ b/res/menu/dialtacts_options.xml
@@ -17,6 +17,7 @@
<item
android:id="@+id/search_on_action_bar"
android:title="@string/menu_all_contacts"
+ android:icon="@drawable/ic_dial_action_search"
android:showAsAction="ifRoom" />
<!-- This should come after the other menus in CallLog and Dialpad -->
diff --git a/src/com/android/contacts/activities/DialtactsActivity.java b/src/com/android/contacts/activities/DialtactsActivity.java
index baa4b4b..f06b227 100644
--- a/src/com/android/contacts/activities/DialtactsActivity.java
+++ b/src/com/android/contacts/activities/DialtactsActivity.java
@@ -27,6 +27,7 @@
import com.android.contacts.list.ContactTileAdapter.DisplayType;
import com.android.contacts.list.ContactTileListFragment;
import com.android.contacts.list.OnPhoneNumberPickerActionListener;
+import com.android.contacts.list.PhoneFavoriteFragment;
import com.android.contacts.list.PhoneNumberPickerFragment;
import com.android.internal.telephony.ITelephony;
@@ -120,7 +121,7 @@
public class ViewPagerAdapter extends FragmentPagerAdapter {
private DialpadFragment mDialpadFragment;
private CallLogFragment mCallLogFragment;
- private ContactTileListFragment mContactTileListFragment;
+ private PhoneFavoriteFragment mPhoneFavoriteFragment;
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
@@ -140,10 +141,10 @@
}
return mCallLogFragment;
case TAB_INDEX_FAVORITES:
- if (mContactTileListFragment == null) {
- mContactTileListFragment = new ContactTileListFragment();
+ if (mPhoneFavoriteFragment == null) {
+ mPhoneFavoriteFragment = new PhoneFavoriteFragment();
}
- return mContactTileListFragment;
+ return mPhoneFavoriteFragment;
}
throw new IllegalStateException("No fragment at position " + position);
}
@@ -206,14 +207,13 @@
}
private String mFilterText;
- private Uri mDialUri;
/** Enables horizontal swipe between Fragments. */
private ViewPager mViewPager;
private final PageChangeListener mPageChangeListener = new PageChangeListener();
private DialpadFragment mDialpadFragment;
private CallLogFragment mCallLogFragment;
- private ContactTileListFragment mStrequentFragment;
+ private PhoneFavoriteFragment mPhoneFavoriteFragment;
private final TabListener mTabListener = new TabListener() {
@Override
@@ -373,13 +373,23 @@
mContactListFilterController.addListener(new ContactListFilterListener() {
@Override
public void onContactListFilterChanged() {
- if (mSearchFragment == null || !mSearchFragment.isAdded()) {
- Log.w(TAG, "Search Fragment isn't available when ContactListFilter is changed");
- return;
- }
- mSearchFragment.setFilter(mContactListFilterController.getFilter());
+ boolean doInvalidateOptionsMenu = false;
- invalidateOptionsMenu();
+ 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();
+ }
}
});
@@ -423,6 +433,9 @@
// ContactListFilterController and they should be able to receive filter change event
// from the same controller (Bug 5165507)
mContactListFilterController.onStart(true);
+ if (mPhoneFavoriteFragment != null) {
+ mPhoneFavoriteFragment.setFilter(mContactListFilterController.getFilter());
+ }
if (mSearchFragment != null) {
mSearchFragment.setFilter(mContactListFilterController.getFilter());
}
@@ -480,11 +493,13 @@
if (currentPosition == TAB_INDEX_CALL_LOG) {
mCallLogFragment.onVisibilityChanged(true);
}
- } else if (fragment instanceof ContactTileListFragment) {
- mStrequentFragment = (ContactTileListFragment) fragment;
- mStrequentFragment.enableQuickContact(false);
- mStrequentFragment.setListener(mStrequentListener);
- mStrequentFragment.setDisplayType(DisplayType.STREQUENT_PHONE_ONLY);
+ } 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);
@@ -699,8 +714,8 @@
}
};
- private ContactTileListFragment.Listener mStrequentListener =
- new ContactTileListFragment.Listener() {
+ private PhoneFavoriteFragment.Listener mPhoneFavoriteListener =
+ new PhoneFavoriteFragment.Listener() {
@Override
public void onContactSelected(Uri contactUri) {
PhoneNumberInteraction.startInteractionForPhoneCall(
@@ -750,7 +765,13 @@
searchMenuItem.setOnMenuItemClickListener(mSearchMenuItemClickListener);
showCallSettingsMenu = true;
}
- filterOptionMenuItem.setVisible(false);
+ if (tab != null && tab.getPosition() == TAB_INDEX_FAVORITES) {
+ filterOptionMenuItem.setVisible(true);
+ filterOptionMenuItem.setOnMenuItemClickListener(
+ mFilterOptionsMenuItemClickListener);
+ } else {
+ filterOptionMenuItem.setVisible(false);
+ }
addContactOptionMenuItem.setVisible(false);
if (showCallSettingsMenu) {
@@ -814,9 +835,6 @@
// layout instead of asking the search menu item to take care of SearchView.
mSearchView.onActionViewExpanded();
mInSearchUi = true;
-
- // Clear focus and suppress keyboard show-up.
- mSearchView.clearFocus();
}
private void showInputMethod(View view) {
@@ -872,7 +890,7 @@
case TAB_INDEX_CALL_LOG:
return mCallLogFragment;
case TAB_INDEX_FAVORITES:
- return mStrequentFragment;
+ return mPhoneFavoriteFragment;
default:
throw new IllegalStateException("Unknown fragment index: " + position);
}
diff --git a/src/com/android/contacts/list/ContactListItemView.java b/src/com/android/contacts/list/ContactListItemView.java
index 70493c5..295c2a1 100644
--- a/src/com/android/contacts/list/ContactListItemView.java
+++ b/src/com/android/contacts/list/ContactListItemView.java
@@ -74,10 +74,6 @@
// Style values for layout and appearance
private final int mPreferredHeight;
private final int mVerticalDividerMargin;
- private final int mPaddingTop;
- private final int mPaddingRight;
- private final int mPaddingBottom;
- private final int mPaddingLeft;
private final int mGapBetweenImageAndText;
private final int mGapBetweenLabelAndData;
private final int mCallButtonPadding;
@@ -93,11 +89,31 @@
private final int mTextIndent;
private Drawable mActivatedBackgroundDrawable;
+ // In the future we may need to merge these local padding to View's mPaddingXXX
+ private final int mExtraPaddingTop;
+ private final int mExtraPaddingBottom;
+ private int mExtraPaddingLeft;
+ private int mExtraPaddingRight;
+
+ // Will be used with adjustListItemSelectionBounds().
+ private int mSelectionBoundsMarginLeft;
+ private int mSelectionBoundsMarginRight;
+
// Horizontal divider between contact views.
private boolean mHorizontalDividerVisible = true;
private Drawable mHorizontalDividerDrawable;
private int mHorizontalDividerHeight;
+ /**
+ * Where to put contact photo. This affects the other Views' layout or look-and-feel.
+ */
+ public enum PhotoPosition {
+ LEFT,
+ RIGHT
+ }
+ public static final PhotoPosition DEFAULT_PHOTO_POSITION = PhotoPosition.RIGHT;
+ private PhotoPosition mPhotoPosition = DEFAULT_PHOTO_POSITION;
+
// Vertical divider between the call icon and the text.
private boolean mVerticalDividerVisible;
private Drawable mVerticalDividerDrawable;
@@ -209,13 +225,13 @@
R.styleable.ContactListItemView_list_item_divider);
mVerticalDividerMargin = a.getDimensionPixelOffset(
R.styleable.ContactListItemView_list_item_vertical_divider_margin, 0);
- mPaddingTop = a.getDimensionPixelOffset(
+ mExtraPaddingTop = a.getDimensionPixelOffset(
R.styleable.ContactListItemView_list_item_padding_top, 0);
- mPaddingBottom = a.getDimensionPixelOffset(
+ mExtraPaddingBottom = a.getDimensionPixelOffset(
R.styleable.ContactListItemView_list_item_padding_bottom, 0);
- mPaddingLeft = a.getDimensionPixelOffset(
+ mExtraPaddingLeft = a.getDimensionPixelOffset(
R.styleable.ContactListItemView_list_item_padding_left, 0);
- mPaddingRight = a.getDimensionPixelOffset(
+ mExtraPaddingRight = a.getDimensionPixelOffset(
R.styleable.ContactListItemView_list_item_padding_right, 0);
mGapBetweenImageAndText = a.getDimensionPixelOffset(
R.styleable.ContactListItemView_list_item_gap_between_image_and_text, 0);
@@ -336,7 +352,8 @@
// Calculate height including padding
height += mNameTextViewHeight + mPhoneticNameTextViewHeight + mLabelTextViewHeight +
- mSnippetTextViewHeight + mStatusTextViewHeight + mPaddingTop + mPaddingBottom;
+ mSnippetTextViewHeight + mStatusTextViewHeight +
+ mExtraPaddingTop + mExtraPaddingBottom;
if (isVisible(mCallButton)) {
mCallButton.measure(0, 0);
@@ -344,7 +361,7 @@
// Make sure the height is at least as high as the photo
ensurePhotoViewSize();
- height = Math.max(height, mPhotoViewHeight + mPaddingBottom + mPaddingTop);
+ height = Math.max(height, mPhotoViewHeight + mExtraPaddingBottom + mExtraPaddingTop);
// Add horizontal divider height
if (mHorizontalDividerVisible) {
@@ -375,29 +392,30 @@
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- int height = bottom - top;
- int width = right - left;
+ final int height = bottom - top;
+ final int width = right - left;
// Determine the vertical bounds by laying out the header first.
int topBound = 0;
int bottomBound = height;
- int leftBound = mPaddingLeft;
+ int leftBound = mExtraPaddingLeft;
+ int rightBound = width - mExtraPaddingRight;
// Put the header in the top of the contact view (Text + underline view)
if (mHeaderVisible) {
mHeaderTextView.layout(leftBound + mHeaderTextIndent,
0,
- width - mPaddingRight,
+ rightBound,
mHeaderBackgroundHeight);
if (mCountView != null) {
- mCountView.layout(width - mPaddingRight - mCountView.getMeasuredWidth(),
+ mCountView.layout(width - mExtraPaddingRight - mCountView.getMeasuredWidth(),
0,
- width - mPaddingRight,
+ rightBound,
mHeaderBackgroundHeight);
}
mHeaderDivider.layout(leftBound,
mHeaderBackgroundHeight,
- width - mPaddingRight,
+ rightBound,
mHeaderBackgroundHeight + mHeaderUnderlineHeight);
topBound += (mHeaderBackgroundHeight + mHeaderUnderlineHeight);
}
@@ -407,7 +425,7 @@
mHorizontalDividerDrawable.setBounds(
leftBound,
height - mHorizontalDividerHeight,
- width - mPaddingRight,
+ rightBound,
height);
bottomBound -= mHorizontalDividerHeight;
}
@@ -418,26 +436,55 @@
mActivatedBackgroundDrawable.setBounds(mBoundsWithoutHeader);
}
- // Set the top/bottom paddings
- topBound += mPaddingTop;
- bottomBound -= mPaddingBottom;
+ // Set the top/bottom padding
+ topBound += mExtraPaddingTop;
+ bottomBound -= mExtraPaddingBottom;
- // Set contact layout:
- // Photo on the right, call button to the left of the photo
- // Text aligned to the left along with the presence status.
+ final View photoView = mQuickContact != null ? mQuickContact : mPhotoView;
+ if (mPhotoPosition == PhotoPosition.LEFT) {
+ // Photo is the left most view. All the other Views should on the right of the photo.
+ if (photoView != null) {
+ // Center the photo vertically
+ final int photoTop = topBound + (bottomBound - topBound - mPhotoViewHeight) / 2;
+ photoView.layout(
+ leftBound,
+ photoTop,
+ leftBound + mPhotoViewWidth,
+ photoTop + mPhotoViewHeight);
+ leftBound += mPhotoViewWidth + mGapBetweenImageAndText;
+ } else if (mKeepHorizontalPaddingForPhotoView) {
+ // Draw nothing but keep the padding.
+ leftBound += mPhotoViewWidth + mGapBetweenImageAndText;
+ }
+ } else {
+ // Photo is the right most view. Right bound should be adjusted that way.
+ if (photoView != null) {
+ // Center the photo vertically
+ final int photoTop = topBound + (bottomBound - topBound - mPhotoViewHeight) / 2;
+ photoView.layout(
+ rightBound - mPhotoViewWidth,
+ photoTop,
+ rightBound,
+ photoTop + mPhotoViewHeight);
+ rightBound -= (mPhotoViewWidth + mGapBetweenImageAndText);
+ }
- // layout the photo and call button.
- int rightBound = layoutRightSide(height, topBound, bottomBound, width - mPaddingRight);
+ // Add indent between left-most padding and texts.
+ leftBound += mTextIndent;
+ }
+
+ // Layout the call button.
+ rightBound = layoutRightSide(height, topBound, bottomBound, rightBound);
// Center text vertically
- int totalTextHeight = mNameTextViewHeight + mPhoneticNameTextViewHeight +
+ final int totalTextHeight = mNameTextViewHeight + mPhoneticNameTextViewHeight +
mLabelTextViewHeight + mSnippetTextViewHeight + mStatusTextViewHeight;
int textTopBound = (bottomBound + topBound - totalTextHeight) / 2;
// Layout all text view and presence icon
// Put name TextView first
if (isVisible(mNameTextView)) {
- mNameTextView.layout(leftBound + mTextIndent,
+ mNameTextView.layout(leftBound,
textTopBound,
rightBound,
textTopBound + mNameTextViewHeight);
@@ -445,13 +492,13 @@
}
// Presence and status
- int statusLeftBound = leftBound + mTextIndent;
+ int statusLeftBound = leftBound;
if (isVisible(mPresenceIcon)) {
int iconWidth = mPresenceIcon.getMeasuredWidth();
mPresenceIcon.layout(
- leftBound + mTextIndent,
+ leftBound,
textTopBound,
- leftBound + iconWidth + mTextIndent,
+ leftBound + iconWidth,
textTopBound + mStatusTextViewHeight);
statusLeftBound += (iconWidth + mPresenceIconMargin);
}
@@ -470,7 +517,7 @@
// Rest of text views
int dataLeftBound = leftBound;
if (isVisible(mPhoneticNameTextView)) {
- mPhoneticNameTextView.layout(leftBound + mTextIndent,
+ mPhoneticNameTextView.layout(leftBound,
textTopBound,
rightBound,
textTopBound + mPhoneticNameTextViewHeight);
@@ -478,12 +525,21 @@
}
if (isVisible(mLabelView)) {
- dataLeftBound = leftBound + mLabelView.getMeasuredWidth() + mTextIndent;
- mLabelView.layout(leftBound + mTextIndent,
- textTopBound,
- dataLeftBound,
- textTopBound + mLabelTextViewHeight);
- dataLeftBound += mGapBetweenLabelAndData;
+ if (mPhotoPosition == PhotoPosition.LEFT) {
+ // When photo is on left, label is placed on the right edge of the list item.
+ mLabelView.layout(rightBound - mLabelView.getMeasuredWidth(),
+ textTopBound,
+ rightBound,
+ textTopBound + mLabelTextViewHeight);
+ } else {
+ // When photo is on right, label is placed on the left of data view.
+ dataLeftBound = leftBound + mLabelView.getMeasuredWidth();
+ mLabelView.layout(leftBound,
+ textTopBound,
+ dataLeftBound,
+ textTopBound + mLabelTextViewHeight);
+ dataLeftBound += mGapBetweenLabelAndData;
+ }
}
if (isVisible(mDataView)) {
@@ -497,7 +553,7 @@
}
if (isVisible(mSnippetView)) {
- mSnippetView.layout(leftBound + mTextIndent,
+ mSnippetView.layout(leftBound,
textTopBound,
rightBound,
textTopBound + mSnippetTextViewHeight);
@@ -510,21 +566,6 @@
* @return new right boundary
*/
protected int layoutRightSide(int height, int topBound, int bottomBound, int rightBound) {
-
- // Photo is the right most view, set it up
-
- View photoView = mQuickContact != null ? mQuickContact : mPhotoView;
- if (photoView != null) {
- // Center the photo vertically
- int photoTop = topBound + (bottomBound - topBound - mPhotoViewHeight) / 2;
- photoView.layout(
- rightBound - mPhotoViewWidth,
- photoTop,
- rightBound,
- photoTop + mPhotoViewHeight);
- rightBound -= (mPhotoViewWidth + mGapBetweenImageAndText);
- }
-
// Put call button and vertical divider
if (isVisible(mCallButton)) {
int buttonWidth = mCallButton.getMeasuredWidth();
@@ -553,6 +594,8 @@
public void adjustListItemSelectionBounds(Rect bounds) {
bounds.top += mBoundsWithoutHeader.top;
bounds.bottom = bounds.top + mBoundsWithoutHeader.height();
+ bounds.left += mSelectionBoundsMarginLeft;
+ bounds.right -= mSelectionBoundsMarginRight;
}
protected boolean isVisible(View view) {
@@ -874,7 +917,12 @@
mLabelView.setSingleLine(true);
mLabelView.setEllipsize(getTextEllipsis());
mLabelView.setTextAppearance(mContext, android.R.style.TextAppearance_Small);
- mLabelView.setTypeface(mLabelView.getTypeface(), Typeface.BOLD);
+ if (mPhotoPosition == PhotoPosition.LEFT) {
+ mLabelView.setTextSize(TypedValue.COMPLEX_UNIT_SP, mCountViewTextSize);
+ mLabelView.setAllCaps(true);
+ } else {
+ mLabelView.setTypeface(mLabelView.getTypeface(), Typeface.BOLD);
+ }
mLabelView.setActivated(isActivated());
addView(mLabelView);
}
@@ -1175,4 +1223,44 @@
// view (ListView).
forceLayout();
}
+
+ public void setPhotoPosition(PhotoPosition photoPosition) {
+ mPhotoPosition = photoPosition;
+ }
+
+ public PhotoPosition getPhotoPosition() {
+ return mPhotoPosition;
+ }
+
+ /**
+ * Sets custom padding inside this object. Do not use this method without any strong reason.
+ *
+ * Detail: we cannot simply override {@link #setPadding(int, int, int, int)}. {@link View}
+ * does *not* know this view's local padding but has completely different ones.
+ * See View#mPaddingLeft and View#mPaddingRight. View also has View#mUserPaddingLeft, and
+ * View#mUserPaddingRight in addition to View#mPaddingLeft and View#mPaddingRight, to handle
+ * {@link View#setPadding(int, int, int, int)} correctly. If setPadding() is overridden to
+ * reset our {@link #mExtraPaddingLeft} and {@link #mExtraPaddingRight} carelessly, the whole
+ * View layout gets confused.
+ *
+ * To simplify our implementation, this method just modify the local two padding without
+ * confusing its parent.
+ *
+ * If we want to fix this multiple padding issue correctly, we should merge local padding
+ * in this class into View's ones. Also we should remove "list_item_padding_left" and
+ * "list_item_padding_right" attributes, using "android:paddingLeft" and "android:paddingRight".
+ */
+ public void setExtraPadding(int left, int right) {
+ mExtraPaddingLeft = left;
+ mExtraPaddingRight = right;
+ }
+
+ /**
+ * Specifies left and right margin for selection bounds. See also
+ * {@link #adjustListItemSelectionBounds(Rect)}.
+ */
+ public void setSelectionBoundsHorizontalMargin(int left, int right) {
+ mSelectionBoundsMarginLeft = left;
+ mSelectionBoundsMarginRight = right;
+ }
}
diff --git a/src/com/android/contacts/list/ContactTileAdapter.java b/src/com/android/contacts/list/ContactTileAdapter.java
index a12b127..8de7fd5 100644
--- a/src/com/android/contacts/list/ContactTileAdapter.java
+++ b/src/com/android/contacts/list/ContactTileAdapter.java
@@ -201,6 +201,7 @@
switch (mDisplayType) {
case STREQUENT:
case STREQUENT_PHONE_ONLY:
+ cursor.moveToPosition(-1);
while (cursor.moveToNext()) {
if (cursor.getInt(mStarredIndex) == 0) {
return cursor.getPosition();
@@ -409,7 +410,7 @@
text.setText(mDisplayType == DisplayType.STREQUENT_PHONE_ONLY ?
mContext.getString(R.string.favoritesFrequentCalled) :
mContext.getString(R.string.favoritesFrequentContacted));
- return dividerView;
+ return dividerView;
}
private int getLayoutResourceId(int viewType) {
@@ -468,6 +469,14 @@
}
}
+ /**
+ * 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);
+ }
+
private ContactTileView.Listener mContactTileListener = new ContactTileView.Listener() {
@Override
public void onClick(ContactTileView contactTileView) {
diff --git a/src/com/android/contacts/list/PhoneFavoriteFragment.java b/src/com/android/contacts/list/PhoneFavoriteFragment.java
new file mode 100644
index 0000000..860e368
--- /dev/null
+++ b/src/com/android/contacts/list/PhoneFavoriteFragment.java
@@ -0,0 +1,436 @@
+/*
+ * 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.contacts.list;
+
+import com.android.contacts.ContactPhotoManager;
+import com.android.contacts.ContactTileLoaderFactory;
+import com.android.contacts.R;
+import com.android.contacts.preference.ContactsPreferences;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.LoaderManager;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.content.Intent;
+import android.content.Loader;
+import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Directory;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ListView;
+import android.widget.TextView;
+
+/**
+ * Fragment for Phone UI's favorite screen.
+ *
+ * This fragment contains three kinds of contacts in one screen: "starred", "frequent", and "all"
+ * contacts. To show them at once, this merges results from {@link ContactTileAdapter} and
+ * {@link PhoneNumberListAdapter} into one unified list using {@link PhoneFavoriteMergedAdapter}.
+ * A contact filter header is also inserted between those adapters' results.
+ */
+public class PhoneFavoriteFragment extends Fragment implements OnItemClickListener {
+ private static final String TAG = PhoneFavoriteFragment.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
+ /**
+ * Used with LoaderManager.
+ */
+ private static int LOADER_ID_CONTACT_TILE = 1;
+ private static int LOADER_ID_ALL_CONTACTS = 2;
+
+ private static final String KEY_FILTER = "filter";
+
+ public interface Listener {
+ public void onContactSelected(Uri contactUri);
+ }
+
+ private class ContactTileLoaderListener implements LoaderManager.LoaderCallbacks<Cursor> {
+ @Override
+ public CursorLoader onCreateLoader(int id, Bundle args) {
+ if (DEBUG) Log.d(TAG, "ContactTileLoaderListener#onCreateLoader.");
+ return ContactTileLoaderFactory.createStrequentPhoneOnlyLoader(getActivity());
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ if (DEBUG) Log.d(TAG, "ContactTileLoaderListener#onLoadFinished");
+ mContactTileAdapter.setContactCursor(data);
+
+ if (mAllContactsForceReload) {
+ mAllContactsAdapter.onDataReload();
+ // Use restartLoader() to make LoaderManager to load the section again.
+ getLoaderManager().restartLoader(
+ LOADER_ID_ALL_CONTACTS, null, mAllContactsLoaderListener);
+ } else if (!mAllContactsLoaderStarted) {
+ // Load "all" contacts if not loaded yet.
+ getLoaderManager().initLoader(
+ LOADER_ID_ALL_CONTACTS, null, mAllContactsLoaderListener);
+ }
+ mAllContactsForceReload = false;
+ mAllContactsLoaderStarted = true;
+
+ // Show the filter header with "loading" state.
+ updateFilterHeaderView();
+ mAccountFilterHeaderContainer.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {
+ if (DEBUG) Log.d(TAG, "ContactTileLoaderListener#onLoaderReset. ");
+ }
+ }
+
+ private class AllContactsLoaderListener implements LoaderManager.LoaderCallbacks<Cursor> {
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ if (DEBUG) Log.d(TAG, "AllContactsLoaderListener#onCreateLoader");
+ CursorLoader loader = new CursorLoader(getActivity(), null, null, null, null, null);
+ mAllContactsAdapter.configureLoader(loader, Directory.DEFAULT);
+ return loader;
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ if (DEBUG) Log.d(TAG, "AllContactsLoaderListener#onLoadFinished");
+ mAllContactsAdapter.changeCursor(0, data);
+ updateFilterHeaderView();
+ mAccountFilterHeaderContainer.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {
+ if (DEBUG) Log.d(TAG, "AllContactsLoaderListener#onLoaderReset. ");
+ }
+ }
+
+ private class ContactTileAdapterListener implements ContactTileAdapter.Listener {
+ @Override
+ public void onContactSelected(Uri contactUri) {
+ if (mListener != null) {
+ mListener.onContactSelected(contactUri);
+ }
+ }
+ }
+
+ private class FilterHeaderClickListener implements OnClickListener {
+ @Override
+ public void onClick(View view) {
+ final Activity activity = getActivity();
+ if (activity != null) {
+ final Intent intent = new Intent(activity, AccountFilterActivity.class);
+ activity.startActivityForResult(
+ intent, AccountFilterActivity.DEFAULT_REQUEST_CODE);
+ }
+ }
+ }
+
+ private class ContactsPreferenceChangeListener
+ implements ContactsPreferences.ChangeListener {
+ @Override
+ public void onChange() {
+ if (loadContactsPreferences()) {
+ requestReloadAllContacts();
+ }
+ }
+ }
+
+ private Listener mListener;
+ private PhoneFavoriteMergedAdapter mAdapter;
+ private ContactTileAdapter mContactTileAdapter;
+ private PhoneNumberListAdapter mAllContactsAdapter;
+
+ /**
+ * true when the loader for {@link PhoneNumberListAdapter} has started already.
+ */
+ private boolean mAllContactsLoaderStarted;
+ /**
+ * true when the loader for {@link PhoneNumberListAdapter} must reload "all" contacts again.
+ * It typically happens when {@link ContactsPreferences} has changed its settings
+ * (display order and sort order)
+ */
+ private boolean mAllContactsForceReload;
+
+ private SharedPreferences mPrefs;
+ private ContactsPreferences mContactsPrefs;
+ private ContactListFilter mFilter;
+
+ private TextView mEmptyView;
+ private ListView mListView;
+ private View mAccountFilterHeaderContainer;
+ private TextView mAccountFilterHeaderView;
+
+ private final ContactTileAdapter.Listener mContactTileAdapterListener =
+ new ContactTileAdapterListener();
+ private final LoaderManager.LoaderCallbacks<Cursor> mContactTileLoaderListener =
+ new ContactTileLoaderListener();
+ private final LoaderManager.LoaderCallbacks<Cursor> mAllContactsLoaderListener =
+ new AllContactsLoaderListener();
+ private final OnClickListener mFilterHeaderClickListener = new FilterHeaderClickListener();
+ private final ContactsPreferenceChangeListener mContactsPreferenceChangeListener =
+ new ContactsPreferenceChangeListener();
+
+ @Override
+ public void onCreate(Bundle savedState) {
+ super.onCreate(savedState);
+ if (savedState != null) {
+ mFilter = savedState.getParcelable(KEY_FILTER);
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putParcelable(KEY_FILTER, mFilter);
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+
+ mPrefs = PreferenceManager.getDefaultSharedPreferences(activity);
+ mContactsPrefs = new ContactsPreferences(activity);
+
+ // Create the account filter header but keep it hidden until "all" contacts are loaded.
+ mAccountFilterHeaderContainer = View.inflate(
+ activity, R.layout.account_filter_header, null);
+ mAccountFilterHeaderView =
+ (TextView) mAccountFilterHeaderContainer.findViewById(R.id.account_filter_header);
+ mAccountFilterHeaderContainer.setOnClickListener(mFilterHeaderClickListener);
+ mAccountFilterHeaderContainer.setVisibility(View.GONE);
+
+ initAdapters(activity);
+
+ mAllContactsAdapter.setFilter(mFilter);
+ mAllContactsForceReload = true;
+ updateFilterHeaderView();
+ }
+
+ /**
+ * Constructs and initializes {@link #mContactTileAdapter}, {@link #mAllContactsAdapter}, and
+ * {@link #mAllContactsAdapter}.
+ */
+ private void initAdapters(Context context) {
+ mContactTileAdapter = new ContactTileAdapter(context, mContactTileAdapterListener,
+ getResources().getInteger(R.integer.contact_tile_column_count),
+ ContactTileAdapter.DisplayType.STREQUENT_PHONE_ONLY);
+ mContactTileAdapter.setPhotoLoader(ContactPhotoManager.getInstance(context));
+
+ // Setup the "all" adapter manually. See also the setup logic in ContactEntryListFragment.
+ mAllContactsAdapter = new PhoneNumberListAdapter(context);
+ mAllContactsAdapter.setDisplayPhotos(true);
+ mAllContactsAdapter.setQuickContactEnabled(true);
+ mAllContactsAdapter.setSearchMode(false);
+ mAllContactsAdapter.setIncludeProfile(false);
+ mAllContactsAdapter.setSelectionVisible(false);
+ mAllContactsAdapter.setDarkTheme(true);
+ mAllContactsAdapter.setPhotoLoader(ContactPhotoManager.getInstance(context));
+ // Disable directory header.
+ mAllContactsAdapter.setHasHeader(0, false);
+ // Show A-Z section index.
+ mAllContactsAdapter.setSectionHeaderDisplayEnabled(true);
+ // Disable pinned header. It doesn't work with this fragment.
+ mAllContactsAdapter.setPinnedPartitionHeadersEnabled(false);
+ // Put photos on left for consistency with "frequent" contacts section.
+ mAllContactsAdapter.setPhotoPosition(ContactListItemView.PhotoPosition.LEFT);
+
+ mAdapter = new PhoneFavoriteMergedAdapter(
+ context, mContactTileAdapter, mAccountFilterHeaderContainer, mAllContactsAdapter);
+ }
+
+ @Override
+ public void onDetach() {
+ super.onDetach();
+ mPrefs = null;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ final View listLayout = inflater.inflate(R.layout.contact_tile_list, container, false);
+
+ mListView = (ListView) listLayout.findViewById(R.id.contact_tile_list);
+
+ mListView.setItemsCanFocus(true);
+ mListView.setAdapter(mAdapter);
+ mListView.setOnItemClickListener(this);
+ mListView.setFastScrollEnabled(true);
+ // We want to hide the scroll bar after a while.
+ mListView.setFastScrollAlwaysVisible(false);
+ mListView.setVerticalScrollBarEnabled(true);
+ mListView.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_RIGHT);
+ mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
+
+ mEmptyView = (TextView) listLayout.findViewById(R.id.contact_tile_list_empty);
+ mEmptyView.setText(getString(R.string.listTotalAllContactsZero));
+ mListView.setEmptyView(mEmptyView);
+
+ updateFilterHeaderView();
+
+ return listLayout;
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ mContactsPrefs.registerChangeListener(mContactsPreferenceChangeListener);
+
+ // If ContactsPreferences has changed, we need to reload "all" contacts with the new
+ // settings. If mAllContactsFoarceReload is already true, it should be kept.
+ if (loadContactsPreferences()) {
+ mAllContactsForceReload = true;
+ }
+
+ // Use initLoader() instead of reloadLoader() to refraing unnecessary reload.
+ // This method call implicitly assures ContactTileLoaderListener's onLoadFinished() will
+ // be called, on which we'll check if "all" contacts should be reloaded again or not.
+ getLoaderManager().initLoader(LOADER_ID_CONTACT_TILE, null, mContactTileLoaderListener);
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ mContactsPrefs.unregisterChangeListener();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * This is only effective for elements provided by {@link #mContactTileAdapter}.
+ * {@link #mContactTileAdapter} has its own logic for click events.
+ */
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ final int contactTileAdapterCount = mContactTileAdapter.getCount();
+ if (position <= contactTileAdapterCount) {
+ Log.e(TAG, "onItemClick() event for unexpected position. "
+ + "The position " + position + " is before \"all\" section. Ignored.");
+ } else {
+ final int localPosition = position - mContactTileAdapter.getCount() - 1;
+ if (mListener != null) {
+ mListener.onContactSelected(mAllContactsAdapter.getDataUri(localPosition));
+ }
+ }
+ }
+
+ private boolean loadContactsPreferences() {
+ if (mContactsPrefs == null || mAllContactsAdapter == null) {
+ return false;
+ }
+
+ boolean changed = false;
+ if (mAllContactsAdapter.getContactNameDisplayOrder() != mContactsPrefs.getDisplayOrder()) {
+ mAllContactsAdapter.setContactNameDisplayOrder(mContactsPrefs.getDisplayOrder());
+ changed = true;
+ }
+
+ if (mAllContactsAdapter.getSortOrder() != mContactsPrefs.getSortOrder()) {
+ mAllContactsAdapter.setSortOrder(mContactsPrefs.getSortOrder());
+ changed = true;
+ }
+
+ return changed;
+ }
+
+ /**
+ * Requests to reload "all" contacts. If the section is already loaded, this method will
+ * force reloading it now. If the section isn't loaded yet, the actual load may be done later
+ * (on {@link #onStart()}.
+ */
+ private void requestReloadAllContacts() {
+ if (DEBUG) {
+ Log.d(TAG, "requestReloadAllContacts()"
+ + " mAllContactsAdapter: " + mAllContactsAdapter
+ + ", mAllContactsLoaderStarted: " + mAllContactsLoaderStarted);
+ }
+
+ if (mAllContactsAdapter == null || !mAllContactsLoaderStarted) {
+ // Remember this request until next load on onStart().
+ mAllContactsForceReload = true;
+ return;
+ }
+
+ if (DEBUG) Log.d(TAG, "Reload \"all\" contacts now.");
+
+ mAllContactsAdapter.onDataReload();
+ // Use restartLoader() to make LoaderManager to load the section again.
+ getLoaderManager().restartLoader(LOADER_ID_ALL_CONTACTS, null, mAllContactsLoaderListener);
+ }
+
+ private void updateFilterHeaderView() {
+ if (mAccountFilterHeaderContainer == null || mAllContactsAdapter == null) {
+ return;
+ }
+
+ final ContactListFilter filter = getFilter();
+ if (mAllContactsAdapter.isLoading()) {
+ mAccountFilterHeaderView.setText(R.string.contact_list_loading);
+ } else if (filter != null) {
+ if (filter.filterType == ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS) {
+ mAccountFilterHeaderView.setText(R.string.list_filter_phones);
+ } else if (filter.filterType == ContactListFilter.FILTER_TYPE_ACCOUNT) {
+ mAccountFilterHeaderView.setText(getString(
+ R.string.listAllContactsInAccount, filter.accountName));
+ } else if (filter.filterType == ContactListFilter.FILTER_TYPE_CUSTOM) {
+ mAccountFilterHeaderView.setText(R.string.listCustomView);
+ } else {
+ Log.w(TAG, "Filter type \"" + filter.filterType + "\" isn't expected.");
+ }
+ } else {
+ Log.w(TAG, "Filter is null.");
+ }
+ }
+
+ public ContactListFilter getFilter() {
+ return mFilter;
+ }
+
+ public void setFilter(ContactListFilter filter) {
+ if ((mFilter == null && filter == null) || (mFilter != null && mFilter.equals(filter))) {
+ return;
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "setFilter(). old filter (" + mFilter
+ + ") will be replaced with new filter (" + filter + ")");
+ }
+
+ mFilter = filter;
+ if (mPrefs != null) {
+ // Save the preference now.
+ ContactListFilter.storeToPreferences(mPrefs, mFilter);
+ }
+
+ mAllContactsAdapter.setFilter(mFilter);
+ requestReloadAllContacts();
+ updateFilterHeaderView();
+ }
+
+ public void setListener(Listener listener) {
+ mListener = listener;
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/contacts/list/PhoneFavoriteMergedAdapter.java b/src/com/android/contacts/list/PhoneFavoriteMergedAdapter.java
new file mode 100644
index 0000000..c3c96ec
--- /dev/null
+++ b/src/com/android/contacts/list/PhoneFavoriteMergedAdapter.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2011 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.contacts.list;
+
+import com.android.contacts.R;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.DataSetObserver;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.FrameLayout;
+import android.widget.SectionIndexer;
+
+/**
+ * An adapter that combines items from {@link ContactTileAdapter} and
+ * {@link ContactEntryListAdapter} into a single list. In between those two results,
+ * an account filter header will be inserted.
+ */
+public class PhoneFavoriteMergedAdapter extends BaseAdapter implements SectionIndexer {
+
+ // Should show nothing as SectionIndex.
+ // " " will suppress the section indexer itself: nothing will be shown during user's scroll.
+ private static final String SECTION_STRING_STARRED = " ";
+ private static final String SECTION_STRING_FREQUENT = " ";
+
+ private class CustomDataSetObserver extends DataSetObserver {
+ @Override
+ public void onChanged() {
+ notifyDataSetChanged();
+ }
+ }
+
+ private final ContactTileAdapter mContactTileAdapter;
+ private final ContactEntryListAdapter mContactEntryListAdapter;
+ private final View mAccountFilterHeaderContainer;
+
+ private final int mItemPaddingLeft;
+ private final int mItemPaddingRight;
+
+ private final DataSetObserver mObserver;
+
+ public PhoneFavoriteMergedAdapter(Context context,
+ ContactTileAdapter contactTileAdapter,
+ View accountFilterHeaderContainer,
+ ContactEntryListAdapter contactEntryListAdapter) {
+ Resources resources = context.getResources();
+ mItemPaddingLeft = resources.getDimensionPixelSize(R.dimen.detail_item_side_margin);
+ mItemPaddingRight = resources.getDimensionPixelSize(R.dimen.list_visible_scrollbar_padding);
+ mContactTileAdapter = contactTileAdapter;
+ mContactEntryListAdapter = contactEntryListAdapter;
+
+ mAccountFilterHeaderContainer = accountFilterHeaderContainer;
+
+ mObserver = new CustomDataSetObserver();
+ mContactTileAdapter.registerDataSetObserver(mObserver);
+ mContactEntryListAdapter.registerDataSetObserver(mObserver);
+ }
+
+ @Override
+ public int getCount() {
+ final int contactTileAdapterCount = mContactTileAdapter.getCount();
+ final int contactEntryListAdapterCount = mContactEntryListAdapter.getCount();
+ if (mContactEntryListAdapter.isLoading()) {
+ // Hide "all" contacts during its being loaded.
+ return contactTileAdapterCount + 1;
+ } else {
+ return contactTileAdapterCount + contactEntryListAdapterCount + 1;
+ }
+ }
+
+ @Override
+ public Object getItem(int position) {
+ final int contactTileAdapterCount = mContactTileAdapter.getCount();
+ final int contactEntryListAdapterCount = mContactEntryListAdapter.getCount();
+ if (position < contactTileAdapterCount) {
+ return mContactTileAdapter.getItem(position);
+ } else if (position == contactTileAdapterCount) {
+ return mAccountFilterHeaderContainer;
+ } else {
+ final int localPosition = position - contactTileAdapterCount - 1;
+ return mContactTileAdapter.getItem(localPosition);
+ }
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return (mContactTileAdapter.getViewTypeCount()
+ + mContactEntryListAdapter.getViewTypeCount()
+ + 1);
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ final int contactTileAdapterCount = mContactTileAdapter.getCount();
+ final int contactEntryListAdapterCount = mContactEntryListAdapter.getCount();
+ if (position < contactTileAdapterCount) {
+ return mContactTileAdapter.getItemViewType(position);
+ } else if (position == contactTileAdapterCount) {
+ return mContactTileAdapter.getViewTypeCount()
+ + mContactEntryListAdapter.getViewTypeCount();
+ } else {
+ final int localPosition = position - contactTileAdapterCount - 1;
+ final int type = mContactEntryListAdapter.getItemViewType(localPosition);
+ // IGNORE_ITEM_VIEW_TYPE must be handled differently.
+ return (type < 0) ? type : type + mContactTileAdapter.getViewTypeCount();
+ }
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ final int contactTileAdapterCount = mContactTileAdapter.getCount();
+ final int contactEntryListAdapterCount = mContactEntryListAdapter.getCount();
+
+ // 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) {
+ 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, view.getPaddingTop(),
+ mItemPaddingRight, view.getPaddingBottom());
+ } else {
+ // Views for "frequent" contacts use FrameLayout's margins instead of padding.
+ final FrameLayout frameLayout = (FrameLayout) view;
+ final View child = frameLayout.getChildAt(0);
+ FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.WRAP_CONTENT,
+ FrameLayout.LayoutParams.WRAP_CONTENT);
+ params.setMargins(mItemPaddingLeft, 0, mItemPaddingRight, 0);
+ child.setLayoutParams(params);
+ }
+ return view;
+ } else if (position == contactTileAdapterCount) {
+ mAccountFilterHeaderContainer.setPadding(mItemPaddingLeft,
+ mAccountFilterHeaderContainer.getPaddingTop(),
+ mItemPaddingRight,
+ mAccountFilterHeaderContainer.getPaddingBottom());
+ return mAccountFilterHeaderContainer;
+ } else {
+ final int localPosition = position - contactTileAdapterCount - 1;
+ final ContactListItemView itemView = (ContactListItemView)
+ mContactEntryListAdapter.getView(localPosition, convertView, null);
+ // We cannot simply use setPadding() because of ContactListItemView's restriction.
+ // See comments for setExtraPaddingPadding().
+ itemView.setExtraPadding(mItemPaddingLeft, mItemPaddingRight);
+ itemView.setSelectionBoundsHorizontalMargin(mItemPaddingLeft, mItemPaddingRight);
+ return itemView;
+ }
+ }
+
+ @Override
+ public boolean areAllItemsEnabled() {
+ return (mContactTileAdapter.areAllItemsEnabled()
+ && mAccountFilterHeaderContainer.isEnabled()
+ && mContactEntryListAdapter.areAllItemsEnabled());
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ final int contactTileAdapterCount = mContactTileAdapter.getCount();
+ final int contactEntryListAdapterCount = mContactEntryListAdapter.getCount();
+ if (position < contactTileAdapterCount) {
+ return mContactTileAdapter.isEnabled(position);
+ } else if (position == contactTileAdapterCount) {
+ return mAccountFilterHeaderContainer.isEnabled();
+ } else {
+ final int localPosition = position - contactTileAdapterCount + 1;
+ return mContactEntryListAdapter.isEnabled(localPosition);
+ }
+ }
+
+ @Override
+ public int getPositionForSection(int sectionIndex) {
+ final int contactTileAdapterCount = mContactTileAdapter.getCount();
+ if (sectionIndex == 0) { // "starred" section
+ return 0;
+ } else if (sectionIndex == 1) { // "frequent" section
+ return mContactTileAdapter.getFrequentHeaderPosition();
+ } else {
+ final int localSection = sectionIndex - 2;
+ final int localPosition = mContactEntryListAdapter.getPositionForSection(localSection);
+ return contactTileAdapterCount + 1 + localPosition;
+ }
+ }
+
+ @Override
+ public int getSectionForPosition(int position) {
+ final int contactTileAdapterCount = mContactTileAdapter.getCount();
+ if (position < contactTileAdapterCount) {
+ return 0;
+ } else if (position == contactTileAdapterCount) {
+ return 1;
+ } else {
+ final int localPosition = position - contactTileAdapterCount - 1;
+ final int localSection = mContactEntryListAdapter.getSectionForPosition(localPosition);
+ return localSection + 2;
+ }
+ }
+
+ @Override
+ public Object[] getSections() {
+ // Copy sections from "all" contacts, and add two additional sections for "starred", and
+ // "frequent". Those new sections should not show anything as an indexer, but should
+ // let users select those sections with a vertical scroll.
+ final Object[] contactEntrySections = mContactEntryListAdapter.getSections();
+ final Object[] ret = new Object[contactEntrySections.length + 2];
+ System.arraycopy(contactEntrySections, 0, ret, 2, contactEntrySections.length);
+
+ ret[0] = SECTION_STRING_STARRED;
+ ret[1] = SECTION_STRING_FREQUENT;
+ return ret;
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/contacts/list/PhoneNumberListAdapter.java b/src/com/android/contacts/list/PhoneNumberListAdapter.java
index 5c12dd6..b16b10c 100644
--- a/src/com/android/contacts/list/PhoneNumberListAdapter.java
+++ b/src/com/android/contacts/list/PhoneNumberListAdapter.java
@@ -71,6 +71,8 @@
private int mDisplayNameColumnIndex;
private int mAlternativeDisplayNameColumnIndex;
+ private ContactListItemView.PhotoPosition mPhotoPosition;
+
public PhoneNumberListAdapter(Context context) {
super(context);
@@ -217,6 +219,7 @@
final ContactListItemView view = new ContactListItemView(context, null);
view.setUnknownNameText(mUnknownNameText);
view.setQuickContactEnabled(isQuickContactEnabled());
+ view.setPhotoPosition(mPhotoPosition);
return view;
}
@@ -315,4 +318,12 @@
getPhotoLoader().loadPhoto(view.getPhotoView(), photoId, false, false);
}
+
+ public void setPhotoPosition(ContactListItemView.PhotoPosition photoPosition) {
+ mPhotoPosition = photoPosition;
+ }
+
+ public ContactListItemView.PhotoPosition getPhotoPosition() {
+ return mPhotoPosition;
+ }
}