diff options
author | Daniel Lehmann <lehmannd@google.com> | 2010-06-14 18:26:41 -0700 |
---|---|---|
committer | Daniel Lehmann <lehmannd@google.com> | 2010-06-14 18:28:54 -0700 |
commit | 899df4a5a48e8a579ca842d303679975c3cef947 (patch) | |
tree | 6836dbb6891096156b84e81e30ab3fd4e421e5bd | |
parent | 173ffe1300afa6a88a7f0a924adade121e564274 (diff) | |
download | packages_apps_Contacts-899df4a5a48e8a579ca842d303679975c3cef947.tar.gz packages_apps_Contacts-899df4a5a48e8a579ca842d303679975c3cef947.tar.bz2 packages_apps_Contacts-899df4a5a48e8a579ca842d303679975c3cef947.zip |
A-Z scroller in Contact List
Change-Id: Iab421c26a54371e2cb543bcbe53c712b47f52d06
-rw-r--r-- | res/drawable-hdpi/aizy_bottom.png | bin | 0 -> 3467 bytes | |||
-rw-r--r-- | res/layout-finger/contacts_list_content.xml | 42 | ||||
-rw-r--r-- | res/layout/aizy_popup_window.xml | 33 | ||||
-rw-r--r-- | src/com/android/contacts/list/ContactEntryListFragment.java | 34 | ||||
-rw-r--r-- | src/com/android/contacts/list/ContactListAizyView.java | 223 | ||||
-rw-r--r-- | src/com/android/contacts/list/DefaultContactBrowseListFragment.java | 1 | ||||
-rw-r--r-- | src/com/android/contacts/widget/IndexerListAdapter.java | 4 |
7 files changed, 323 insertions, 14 deletions
diff --git a/res/drawable-hdpi/aizy_bottom.png b/res/drawable-hdpi/aizy_bottom.png Binary files differnew file mode 100644 index 000000000..1f3d332a6 --- /dev/null +++ b/res/drawable-hdpi/aizy_bottom.png diff --git a/res/layout-finger/contacts_list_content.xml b/res/layout-finger/contacts_list_content.xml index 9ea1c6885..f5bb78536 100644 --- a/res/layout-finger/contacts_list_content.xml +++ b/res/layout-finger/contacts_list_content.xml @@ -19,23 +19,37 @@ android:id="@+id/pinned_header_list_layout" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical" + android:orientation="horizontal" > - <view - class="com.android.contacts.ContactEntryListView" - android:id="@android:id/list" - android:layout_width="match_parent" - android:layout_height="0dip" - android:fastScrollEnabled="true" - android:layout_weight="1" + <com.android.contacts.list.ContactListAizyView + android:id="@+id/contacts_list_aizy" + android:layout_width="50dip" + android:layout_height="match_parent" /> - <include layout="@layout/contacts_list_empty"/> + <LinearLayout + android:layout_width="0px" + android:layout_height="match_parent" + android:orientation="vertical" + android:layout_weight="1" + > - <ViewStub android:id="@+id/footer_stub" - android:layout="@layout/footer_panel" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - /> + <view + class="com.android.contacts.ContactEntryListView" + android:id="@android:id/list" + android:layout_width="match_parent" + android:layout_height="0dip" + android:fastScrollEnabled="true" + android:layout_weight="1" + /> + + <include layout="@layout/contacts_list_empty"/> + + <ViewStub android:id="@+id/footer_stub" + android:layout="@layout/footer_panel" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + /> + </LinearLayout> </LinearLayout> diff --git a/res/layout/aizy_popup_window.xml b/res/layout/aizy_popup_window.xml new file mode 100644 index 000000000..470f11694 --- /dev/null +++ b/res/layout/aizy_popup_window.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="@drawable/aizy_bottom" + > + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/caption" + android:width="75dip" + android:textSize="50sp" + android:paddingLeft="20dip" + android:gravity="center" /> + +</LinearLayout>
\ No newline at end of file diff --git a/src/com/android/contacts/list/ContactEntryListFragment.java b/src/com/android/contacts/list/ContactEntryListFragment.java index 9f398c0ff..0da9a36fd 100644 --- a/src/com/android/contacts/list/ContactEntryListFragment.java +++ b/src/com/android/contacts/list/ContactEntryListFragment.java @@ -91,11 +91,13 @@ public abstract class ContactEntryListFragment<T extends ContactEntryListAdapter private boolean mPhotoLoaderEnabled; private boolean mSearchMode; private boolean mSearchResultsMode; + private boolean mAizyEnabled; private String mQueryString; private T mAdapter; private View mView; private ListView mListView; + private ContactListAizyView mAizy; /** * Used for keeping track of the scroll state of the list. @@ -306,6 +308,9 @@ public abstract class ContactEntryListFragment<T extends ContactEntryListAdapter mAdapter.changeCursor(partitionIndex, data); showCount(partitionIndex, data); } + if (partitionIndex == mAdapter.getIndexedPartition()) { + mAizy.setIndexer(mAdapter.getIndexer()); + } } private DirectoryPartition createDirectoryPartition(int partitionIndex, Cursor cursor) { @@ -399,12 +404,34 @@ public abstract class ContactEntryListFragment<T extends ContactEntryListAdapter if (mAdapter != null) { mAdapter.setSectionHeaderDisplayEnabled(flag); } + configureAizy(); } public boolean isSectionHeaderDisplayEnabled() { return mSectionHeaderDisplayEnabled; } + public void setAizyEnabled(boolean flag) { + mAizyEnabled = flag; + configureAizy(); + } + + public boolean isAizyEnabled() { + return mAizyEnabled; + } + + private void configureAizy() { + boolean hasAisy = isAizyEnabled() && isSectionHeaderDisplayEnabled(); + + if (mListView != null) { + mListView.setFastScrollEnabled(!hasAisy); + mListView.setVerticalScrollBarEnabled(!hasAisy); + } + if (mAizy != null) { + mAizy.setVisibility(hasAisy ? View.VISIBLE : View.GONE); + } + } + public void setPhotoLoaderEnabled(boolean flag) { mPhotoLoaderEnabled = flag; configurePhotoLoader(); @@ -569,6 +596,10 @@ public abstract class ContactEntryListFragment<T extends ContactEntryListAdapter mListView.setOnCreateContextMenuListener(mContextMenuAdapter); } + mAizy = (ContactListAizyView) mView.findViewById(R.id.contacts_list_aizy); + mAizy.setListView(mListView); + + configureAizy(); configurePhotoLoader(); configureSearchResultText(); } @@ -625,6 +656,9 @@ public abstract class ContactEntryListFragment<T extends ContactEntryListAdapter public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { + if (isAizyEnabled()) { + mAizy.listOnScroll(firstVisibleItem); + } } public void onScrollStateChanged(AbsListView view, int scrollState) { diff --git a/src/com/android/contacts/list/ContactListAizyView.java b/src/com/android/contacts/list/ContactListAizyView.java new file mode 100644 index 000000000..07ce12c4a --- /dev/null +++ b/src/com/android/contacts/list/ContactListAizyView.java @@ -0,0 +1,223 @@ +/* + * 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.list; + +import com.android.contacts.R; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.widget.ListView; +import android.widget.PopupWindow; +import android.widget.SectionIndexer; +import android.widget.TextView; + +/** + * A View that displays the sections given by an Indexer and their relative sizes. For + * English and similar languages, this is an A to Z list (where only the used letters are + * displayed). As the sections are shown in their relative sizes, this View can be used as a + * scrollbar. + */ +public class ContactListAizyView extends View { + private static final String TAG = "ContactListAizyView"; + + // TODO: Put these into resource files or create from image resources + private static final int TEXT_WIDTH = 20; + private static final int CIRCLE_DIAMETER = 30; + private static final int PREVIEW_WIDTH = 130; + private static final int PREVIEW_HEIGHT = 115; + + private SectionIndexer mIndexer; + + private boolean mCalculateYCoordinates; + private ListView mListView; + private float mPosition; + private float mFactor; + private PopupWindow mPreviewPopupWindow; + private TextView mPreviewPopupTextView; + private boolean mPreviewPopupVisible; + private int[] mWindowOffset; + private float[] yPositions = null; + + public ContactListAizyView(Context context) { + super(context); + } + + public ContactListAizyView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + public ContactListAizyView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + final LayoutInflater inflater = + (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); + mPreviewPopupWindow = new PopupWindow( + inflater.inflate(R.layout.aizy_popup_window, null, false), + PREVIEW_WIDTH, PREVIEW_HEIGHT); + mPreviewPopupTextView = + (TextView) mPreviewPopupWindow.getContentView().findViewById(R.id.caption); + } + + public void setIndexer(SectionIndexer indexer) { + mIndexer = indexer; + mCalculateYCoordinates = true; + } + + public void setListView(ListView listView) { + mListView = listView; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + setMeasuredDimension(TEXT_WIDTH + CIRCLE_DIAMETER, resolveSize(0, heightMeasureSpec)); + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + mCalculateYCoordinates = true; + } + + @Override + protected void onDraw(Canvas canvas) { + if (mIndexer == null) return; + + calcYCoordinates(); + + drawLineAndText(canvas); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (mWindowOffset == null) { + mWindowOffset = new int[2]; + getLocationInWindow(mWindowOffset); + } + + final int previewX = mWindowOffset[0] + getWidth(); + final int previewY = (int) event.getY() + mWindowOffset[1] + - mPreviewPopupWindow.getHeight() / 2; + final boolean previewPopupVisible = event.getActionMasked() == MotionEvent.ACTION_MOVE; + if (previewPopupVisible != mPreviewPopupVisible) { + if (previewPopupVisible) { + mPreviewPopupWindow.showAtLocation(this, Gravity.LEFT | Gravity.TOP, + previewX, previewY); + } else { + mPreviewPopupWindow.dismiss(); + } + mPreviewPopupVisible = previewPopupVisible; + } else { + mPreviewPopupWindow.update(previewX, previewY, -1, -1); + } + final int position = Math.max(0, (int) (event.getY() / mFactor)); + if (mIndexer != null) { + final int index = mIndexer.getSectionForPosition(position); + final Object[] sections = mIndexer.getSections(); + final String caption = + (index != -1 && index < sections.length) ? sections[index].toString() : ""; + mPreviewPopupTextView.setText(caption); + } + if (mListView != null) { + mListView.setSelectionFromTop(position, 0); + } + + super.onTouchEvent(event); + return true; + } + + private void calcYCoordinates() { + if (!mCalculateYCoordinates) return; + mCalculateYCoordinates = false; + + // Get a String[] of the sections. + final Object[] sectionObjects = mIndexer.getSections(); + final int sectionCount = sectionObjects.length; + final String[] sections; + if (sectionObjects instanceof String[]) { + sections = (String[]) sectionObjects; + } else { + sections = new String[sectionCount]; + for (int i = 0; i < sectionCount; i++) { + sections[i] = sectionObjects[i] == null ? null : sectionObjects[i].toString(); + } + } + + mFactor = (float) getHeight() / mListView.getCount(); + } + + private void drawLineAndText(Canvas canvas) { + // TODO: Figure out how to set the text size and fetch the height in pixels. This + // behaviour is OK for prototypes, but has to be refined later + final float textSize = 20.0f; + + // Move A down, Z up + final Paint paint = new Paint(); + paint.setColor(Color.LTGRAY); + paint.setTextSize(textSize); + paint.setAntiAlias(true); + final Object[] sections = mIndexer.getSections(); + canvas.drawLine( + TEXT_WIDTH + CIRCLE_DIAMETER * 0.5f, 0.0f, + TEXT_WIDTH + CIRCLE_DIAMETER * 0.5f, getHeight(), + paint); + final int sectionCount = sections.length; + if (yPositions == null || yPositions.length != sectionCount) { + yPositions = new float[sectionCount]; + } + + // Calculate Positions + for (int i = 0; i < sectionCount; i++) { + yPositions[i] = mIndexer.getPositionForSection(i) * mFactor; + } + + // Draw + float lastVisibleY = Float.MAX_VALUE; + for (int i = sectionCount - 1; i >= 0; i--) { + final float y = yPositions[i]; + if (lastVisibleY - textSize > y) { + canvas.drawText(sections[i].toString(), 0.0f, y + 0.5f * textSize, paint); + lastVisibleY = y; + } + canvas.drawLine( + TEXT_WIDTH + CIRCLE_DIAMETER * 0.5f - 2, y, + TEXT_WIDTH + CIRCLE_DIAMETER * 0.5f + 2, y, + paint); + } + + paint.setColor(Color.YELLOW); + canvas.drawLine( + TEXT_WIDTH + CIRCLE_DIAMETER * 0.0f, mPosition * mFactor, + TEXT_WIDTH + CIRCLE_DIAMETER * 1.0f, mPosition * mFactor, + paint); + } + + public void listOnScroll(int firstVisibleItem) { + mPosition = firstVisibleItem; + invalidate(); + } +} diff --git a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java index 13361e31b..6c5559fa8 100644 --- a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java +++ b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java @@ -43,6 +43,7 @@ public class DefaultContactBrowseListFragment extends ContactBrowseListFragment public DefaultContactBrowseListFragment() { setPhotoLoaderEnabled(true); setSectionHeaderDisplayEnabled(true); + setAizyEnabled(true); } @Override diff --git a/src/com/android/contacts/widget/IndexerListAdapter.java b/src/com/android/contacts/widget/IndexerListAdapter.java index 35ce9a608..c39e2f2c3 100644 --- a/src/com/android/contacts/widget/IndexerListAdapter.java +++ b/src/com/android/contacts/widget/IndexerListAdapter.java @@ -68,6 +68,10 @@ public abstract class IndexerListAdapter extends PinnedHeaderListAdapter impleme this.mIndexedPartition = partition; } + public SectionIndexer getIndexer() { + return mIndexer; + } + public void setIndexer(SectionIndexer indexer) { mIndexer = indexer; } |