Merge "Add an original-package tag that matches the real package name."
diff --git a/res/layout-finger/contacts_list_content.xml b/res/layout-finger/contacts_list_content.xml
index fe65da0..e2ade92 100644
--- a/res/layout-finger/contacts_list_content.xml
+++ b/res/layout-finger/contacts_list_content.xml
@@ -14,7 +14,9 @@
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/pinned_header_list_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
@@ -23,10 +25,12 @@
layout="@layout/search_bar"
android:visibility="gone"/>
- <ListView android:id="@android:id/list"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:fastScrollEnabled="true"
+ <view
+ class="com.android.contacts.PinnedHeaderListView"
+ android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fastScrollEnabled="true"
/>
<ScrollView android:id="@android:id/empty"
@@ -46,4 +50,5 @@
android:lineSpacingMultiplier="0.92"
/>
</ScrollView>
+
</LinearLayout>
diff --git a/res/layout-finger/contacts_list_content_join.xml b/res/layout-finger/contacts_list_content_join.xml
index b50713b..f4704cd 100644
--- a/res/layout-finger/contacts_list_content_join.xml
+++ b/res/layout-finger/contacts_list_content_join.xml
@@ -20,6 +20,10 @@
android:layout_height="match_parent"
android:orientation="vertical">
+ <include android:id="@+id/searchView"
+ layout="@layout/search_bar"
+ android:visibility="gone"/>
+
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 3ba7106..32d07c5 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -23,4 +23,5 @@
<color name="edit_divider">#ff666666</color>
<color name="background_secondary">#ff202020</color>
+ <color name="pinned_header_background">#ff202020</color>
</resources>
diff --git a/src/com/android/contacts/ContactsListActivity.java b/src/com/android/contacts/ContactsListActivity.java
index 2fdc458..29f059d 100644
--- a/src/com/android/contacts/ContactsListActivity.java
+++ b/src/com/android/contacts/ContactsListActivity.java
@@ -40,6 +40,7 @@
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.UriMatcher;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.database.CharArrayBuffer;
import android.database.Cursor;
@@ -47,6 +48,7 @@
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
@@ -496,6 +498,15 @@
private View mSearchView;
private SearchEditText mSearchEditText;
+ /**
+ * An approximation of the background color of the pinned header. This color
+ * is used when the pinned header is being pushed up. At that point the header
+ * "fades away". Rather than computing a faded bitmap based on the 9-patch
+ * normally used for the background, we will use a solid color, which will
+ * provide better performance and reduced complexity.
+ */
+ private int mPinnedHeaderBackgroundColor;
+
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
@@ -718,6 +729,8 @@
private void setupListView() {
final ListView list = getListView();
+ final LayoutInflater inflater = getLayoutInflater();
+
mHighlightingAnimation =
new NameHighlightingAnimation(list, TEXT_HIGHLIGHTING_ANIMATION_DURATION);
@@ -729,7 +742,6 @@
if ((mMode & MODE_MASK_CREATE_NEW) != 0) {
// Add the header for creating a new contact
- final LayoutInflater inflater = getLayoutInflater();
View header = inflater.inflate(R.layout.create_new_contact, list, false);
list.addHeaderView(header);
}
@@ -739,6 +751,15 @@
mAdapter = new ContactItemListAdapter(this);
setListAdapter(mAdapter);
+
+ if (list instanceof PinnedHeaderListView) {
+ mPinnedHeaderBackgroundColor =
+ getResources().getColor(R.color.pinned_header_background);
+ PinnedHeaderListView pinnedHeaderList = (PinnedHeaderListView)list;
+ View pinnedHeader = inflater.inflate(R.layout.list_section, list, false);
+ pinnedHeaderList.setPinnedHeaderView(pinnedHeader);
+ }
+
list.setOnScrollListener(mAdapter);
list.setOnKeyListener(this);
@@ -750,13 +771,15 @@
* Configures search UI.
*/
private void setupSearchView() {
- mSearchView = findViewById(R.id.searchView);
- mSearchEditText = (SearchEditText)mSearchView.findViewById(R.id.search_src_text);
- mSearchEditText.addTextChangedListener(this);
- mSearchEditText.setOnEditorActionListener(this);
+ if ((mMode & MODE_MASK_NO_FILTER) == 0) {
+ mSearchView = findViewById(R.id.searchView);
+ mSearchEditText = (SearchEditText)mSearchView.findViewById(R.id.search_src_text);
+ mSearchEditText.addTextChangedListener(this);
+ mSearchEditText.setOnEditorActionListener(this);
- ImageButton searchButton = (ImageButton)mSearchView.findViewById(R.id.search_btn);
- searchButton.setOnClickListener(this);
+ ImageButton searchButton = (ImageButton)mSearchView.findViewById(R.id.search_btn);
+ searchButton.setOnClickListener(this);
+ }
}
private boolean isPickerMode() {
@@ -905,11 +928,16 @@
}
private String getTextFilter() {
- return mSearchEditText.getText().toString();
+ if (mSearchEditText != null) {
+ return mSearchEditText.getText().toString();
+ }
+ return null;
}
private void setTextFilter(String filterText) {
- mSearchEditText.setText(filterText);
+ if (mSearchEditText != null) {
+ mSearchEditText.setText(filterText);
+ }
}
@Override
@@ -2411,8 +2439,14 @@
public QuickContactBadge photoView;
}
+ final static class PinnedHeaderCache {
+ public TextView titleView;
+ public ColorStateList textColor;
+ public Drawable background;
+ }
+
private final class ContactItemListAdapter extends ResourceCursorAdapter
- implements SectionIndexer, OnScrollListener {
+ implements SectionIndexer, OnScrollListener, PinnedHeaderListView.PinnedHeaderAdapter {
private SectionIndexer mIndexer;
private String mAlphabet;
private boolean mLoading = true;
@@ -3254,7 +3288,9 @@
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
- // no op
+ if (view instanceof PinnedHeaderListView) {
+ ((PinnedHeaderListView)view).configureHeaderView(firstVisibleItem);
+ }
}
public void onScrollStateChanged(AbsListView view, int scrollState) {
@@ -3320,5 +3356,69 @@
mHandler.clearImageFecthing();
}
+
+ /**
+ * Computes the state of the pinned header. It can be invisible, fully
+ * visible or partially pushed up out of the view.
+ */
+ public int getPinnedHeaderState(int position) {
+ if (mIndexer == null || mCursor == null || mCursor.getCount() == 0) {
+ return PINNED_HEADER_GONE;
+ }
+
+ int realPosition = getRealPosition(position);
+ if (realPosition < 0) {
+ return PINNED_HEADER_GONE;
+ }
+
+ // The header should get pushed up if the top item shown
+ // is the last item in a section for a particular letter.
+ int section = getSectionForPosition(realPosition);
+ int nextSectionPosition = getPositionForSection(section + 1);
+ if (nextSectionPosition != -1 && realPosition == nextSectionPosition - 1) {
+ return PINNED_HEADER_PUSHED_UP;
+ }
+
+ return PINNED_HEADER_VISIBLE;
+ }
+
+ /**
+ * Configures the pinned header by setting the appropriate text label
+ * and also adjusting color if necessary. The color needs to be
+ * adjusted when the pinned header is being pushed up from the view.
+ */
+ public void configurePinnedHeader(View header, int position, int alpha) {
+ PinnedHeaderCache cache = (PinnedHeaderCache)header.getTag();
+ if (cache == null) {
+ cache = new PinnedHeaderCache();
+ cache.titleView = (TextView)header.findViewById(R.id.header_text);
+ cache.textColor = cache.titleView.getTextColors();
+ cache.background = header.getBackground();
+ header.setTag(cache);
+ }
+
+ int realPosition = getRealPosition(position);
+ int section = getSectionForPosition(realPosition);
+
+ String title = mIndexer.getSections()[section].toString().trim();
+ cache.titleView.setText(title);
+
+ if (alpha == 255) {
+ // Opaque: use the default background, and the original text color
+ header.setBackgroundDrawable(cache.background);
+ cache.titleView.setTextColor(cache.textColor);
+ } else {
+ // Faded: use a solid color approximation of the background, and
+ // a translucent text color
+ header.setBackgroundColor(Color.rgb(
+ Color.red(mPinnedHeaderBackgroundColor) * alpha / 255,
+ Color.green(mPinnedHeaderBackgroundColor) * alpha / 255,
+ Color.blue(mPinnedHeaderBackgroundColor) * alpha / 255));
+
+ int textColor = cache.textColor.getDefaultColor();
+ cache.titleView.setTextColor(Color.argb(alpha,
+ Color.red(textColor), Color.green(textColor), Color.blue(textColor)));
+ }
+ }
}
}
diff --git a/src/com/android/contacts/FocusRequestingListView.java b/src/com/android/contacts/FocusRequestingListView.java
deleted file mode 100644
index 7461d70..0000000
--- a/src/com/android/contacts/FocusRequestingListView.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2009 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;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.ListView;
-
-/* Subclass of ListView that requests focus after it is layed out for the first time. */
-public class FocusRequestingListView extends ListView {
-
- private boolean mFirstLayoutDone = false;
-
- public FocusRequestingListView(Context context) {
- super(context);
- }
-
- public FocusRequestingListView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public FocusRequestingListView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- if (!mFirstLayoutDone) {
- setFocusable(true);
- requestFocus();
- }
- mFirstLayoutDone = true;
- }
-}
diff --git a/src/com/android/contacts/PinnedHeaderListView.java b/src/com/android/contacts/PinnedHeaderListView.java
new file mode 100644
index 0000000..9d1391b
--- /dev/null
+++ b/src/com/android/contacts/PinnedHeaderListView.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2010 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;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+
+/**
+ * A ListView that maintains a header pinned at the top of the list. The
+ * pinned header can be pushed up and dissolved as needed.
+ */
+public class PinnedHeaderListView extends ListView {
+
+ /**
+ * Adapter interface. The list adapter must implement this interface.
+ */
+ public interface PinnedHeaderAdapter {
+
+ /**
+ * Pinned header state: don't show the header.
+ */
+ public static final int PINNED_HEADER_GONE = 0;
+
+ /**
+ * Pinned header state: show the header at the top of the list.
+ */
+ public static final int PINNED_HEADER_VISIBLE = 1;
+
+ /**
+ * Pinned header state: show the header. If the header extends beyond
+ * the bottom of the first shown element, push it up and clip.
+ */
+ public static final int PINNED_HEADER_PUSHED_UP = 2;
+
+ /**
+ * Computes the desired state of the pinned header for the given
+ * position of the first visible list item. Allowed return values are
+ * {@link #PINNED_HEADER_GONE}, {@link #PINNED_HEADER_VISIBLE} or
+ * {@link #PINNED_HEADER_PUSHED_UP}.
+ */
+ int getPinnedHeaderState(int position);
+
+ /**
+ * Configures the pinned header view to match the first visible list item.
+ *
+ * @param header pinned header view.
+ * @param position position of the first visible list item.
+ * @param alpha fading of the header view, between 0 and 255.
+ */
+ void configurePinnedHeader(View header, int position, int alpha);
+ }
+
+ private static final int MAX_ALPHA = 255;
+
+ private PinnedHeaderAdapter mAdapter;
+ private View mHeaderView;
+ private boolean mHeaderViewVisible;
+
+ private int mHeaderViewWidth;
+
+ private int mHeaderViewHeight;
+
+ public PinnedHeaderListView(Context context) {
+ super(context);
+ }
+
+ public PinnedHeaderListView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public PinnedHeaderListView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ public void setPinnedHeaderView(View view) {
+ mHeaderView = view;
+
+ // Disable vertical fading when the pinned header is present
+ // TODO change ListView to allow separate measures for top and bottom fading edge;
+ // in this particular case we would like to disable the top, but not the bottom edge.
+ if (mHeaderView != null) {
+ setFadingEdgeLength(0);
+ }
+ requestLayout();
+ }
+
+ @Override
+ public void setAdapter(ListAdapter adapter) {
+ super.setAdapter(adapter);
+ mAdapter = (PinnedHeaderAdapter)adapter;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ if (mHeaderView != null) {
+ measureChild(mHeaderView, widthMeasureSpec, heightMeasureSpec);
+ mHeaderViewWidth = mHeaderView.getMeasuredWidth();
+ mHeaderViewHeight = mHeaderView.getMeasuredHeight();
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ if (mHeaderView != null) {
+ mHeaderView.layout(0, 0, mHeaderViewWidth, mHeaderViewHeight);
+ configureHeaderView(getFirstVisiblePosition());
+ }
+ }
+
+ public void configureHeaderView(int position) {
+ if (mHeaderView == null) {
+ return;
+ }
+
+ int state = mAdapter.getPinnedHeaderState(position);
+ switch (state) {
+ case PinnedHeaderAdapter.PINNED_HEADER_GONE: {
+ mHeaderViewVisible = false;
+ break;
+ }
+
+ case PinnedHeaderAdapter.PINNED_HEADER_VISIBLE: {
+ mAdapter.configurePinnedHeader(mHeaderView, position, MAX_ALPHA);
+ if (mHeaderView.getTop() != 0) {
+ mHeaderView.layout(0, 0, mHeaderViewWidth, mHeaderViewHeight);
+ }
+ mHeaderViewVisible = true;
+ break;
+ }
+
+ case PinnedHeaderAdapter.PINNED_HEADER_PUSHED_UP: {
+ View firstView = getChildAt(0);
+ int bottom = firstView.getBottom();
+ int itemHeight = firstView.getHeight();
+ int headerHeight = mHeaderView.getHeight();
+ int y;
+ int alpha;
+ if (bottom < headerHeight) {
+ y = (bottom - headerHeight);
+ alpha = MAX_ALPHA * (headerHeight + y) / headerHeight;
+ } else {
+ y = 0;
+ alpha = MAX_ALPHA;
+ }
+ mAdapter.configurePinnedHeader(mHeaderView, position, alpha);
+ if (mHeaderView.getTop() != y) {
+ mHeaderView.layout(0, y, mHeaderViewWidth, mHeaderViewHeight + y);
+ }
+ mHeaderViewVisible = true;
+ break;
+ }
+ }
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(canvas);
+ if (mHeaderViewVisible) {
+ drawChild(canvas, mHeaderView, getDrawingTime());
+ }
+ }
+}