Turn the Aizy into a scrollbar again, similar to the old FastScroll
Change-Id: I8818554940eba9cc16b9f9ec0c4687a16db76101
diff --git a/res/drawable-hdpi/aizy_bottom.png b/res/drawable-hdpi/temp_aizy_bottom.png
similarity index 100%
rename from res/drawable-hdpi/aizy_bottom.png
rename to res/drawable-hdpi/temp_aizy_bottom.png
Binary files differ
diff --git a/res/drawable-mdpi/aizy_bottom.png b/res/drawable-mdpi/temp_aizy_bottom.png
similarity index 100%
rename from res/drawable-mdpi/aizy_bottom.png
rename to res/drawable-mdpi/temp_aizy_bottom.png
Binary files differ
diff --git a/res/drawable-mdpi/temp_aizy_knob.png b/res/drawable-mdpi/temp_aizy_knob.png
new file mode 100644
index 0000000..daa8242
--- /dev/null
+++ b/res/drawable-mdpi/temp_aizy_knob.png
Binary files differ
diff --git a/res/layout/aizy_popup_window.xml b/res/layout/aizy_popup_window.xml
index 801b481..5e83766 100644
--- a/res/layout/aizy_popup_window.xml
+++ b/res/layout/aizy_popup_window.xml
@@ -18,7 +18,7 @@
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:background="@drawable/aizy_bottom"
+ android:background="@drawable/temp_aizy_bottom"
>
<TextView
diff --git a/res/values-xlarge/colors.xml b/res/values-xlarge/colors.xml
index 4eaa941..0f8f8de 100644
--- a/res/values-xlarge/colors.xml
+++ b/res/values-xlarge/colors.xml
@@ -16,12 +16,6 @@
<resources>
- <!-- Color used in the Aizy visual scroll control for empty section -->
- <color name="aizy_empty_section">#ffcccccc</color>
-
- <!-- Color used in the Aizy visual scroll control for non-empty sections -->
- <color name="aizy_non_empty_section">#ff9490a0</color>
-
<!-- Color used for the letter in the A-Z section header -->
<color name="section_header_text_color">#ff000000</color>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 9da6a61..cea3573 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -27,10 +27,7 @@
<color name="translucent_search_background">#cc000000</color>
<!-- Color used in the Aizy visual scroll control for empty section -->
- <color name="aizy_empty_section">#ff666666</color>
-
- <!-- Color used in the Aizy visual scroll control for non-empty sections -->
- <color name="aizy_non_empty_section">#ffcccccc</color>
+ <color name="aizy_line_color">#ff689afe</color>
<!-- Color used for the letter in the A-Z section header -->
<color name="section_header_text_color">#ff999999</color>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a15619a..47bb0a1 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1308,16 +1308,6 @@
<!-- The button next to the message about multiple contact aggregation suggestions in Contact editor. [CHAR LIMIT=12]-->
<string name="aggregation_suggestion_view_button">View</string>
- <!-- Primary alphabet of this language. Each of these characters always has its own section in
- the visual scroll control next to the contact list. These letters must be uppercase.
- While there is no hard limit on the number of characters, there should not be more than
- 40. If the language requires more, this string should instead be empty so that the
- visual scroll control adapts to the contents.
- This text must only contain letters that can appear as sections, otherwise they would
- only be empty.
- Translations require extensive QA! -->
- <string name="visualScrollerAlphabet">A;B;C;D;E;F;G;H;I;J;K;L;M;N;O;P;Q;R;S;T;U;V;W;X;Y;Z</string>
-
<!-- The menu item (or button) that creates a local copy of a corporate contact. [CHAR LIMIT=40]-->
<string name="menu_copyContact">Copy to my contacts</string>
diff --git a/src/com/android/contacts/list/ContactEntryListFragment.java b/src/com/android/contacts/list/ContactEntryListFragment.java
index afb748e..98c01d4 100644
--- a/src/com/android/contacts/list/ContactEntryListFragment.java
+++ b/src/com/android/contacts/list/ContactEntryListFragment.java
@@ -417,7 +417,7 @@
mAdapter.changeCursor(partitionIndex, data);
showCount(partitionIndex, data);
if (partitionIndex == mAdapter.getIndexedPartition()) {
- mAizy.readFromIndexer(mAdapter.getIndexer());
+ mAizy.setIndexer(mAdapter.getIndexer(), data.getCount());
}
// TODO should probably only restore instance state after all directories are loaded
@@ -676,8 +676,7 @@
mAizy.setListener(new ContactListAizyView.Listener() {
@Override
public void onScroll(int position) {
- mListView.smoothScrollToPositionFromTop(
- position + mListView.getHeaderViewsCount(),
+ mListView.smoothScrollToPositionFromTop(position + mListView.getHeaderViewsCount(),
0);
}
});
@@ -734,6 +733,9 @@
@Override
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
index d2cc279..69a10cf 100644
--- a/src/com/android/contacts/list/ContactListAizyView.java
+++ b/src/com/android/contacts/list/ContactListAizyView.java
@@ -17,16 +17,12 @@
package com.android.contacts.list;
import com.android.contacts.R;
-import com.android.contacts.util.PhonebookCollatorFactory;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
-import android.graphics.Paint.Align;
-import android.graphics.Paint.FontMetrics;
-import android.graphics.Rect;
-import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -36,9 +32,6 @@
import android.widget.SectionIndexer;
import android.widget.TextView;
-import java.text.Collator;
-import java.util.ArrayList;
-
/**
* 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
@@ -48,51 +41,19 @@
public class ContactListAizyView extends View {
private static final String TAG = "ContactListAizyView";
- private static final int PREVIEW_TIME_DELAY_MS = 400;
+ private SectionIndexer mIndexer;
+ private int mItemCount;
+ private int[] mSectionPositions;
private Listener mListener;
+ private float mPosition;
private PopupWindow mPreviewPopupWindow;
private TextView mPreviewPopupTextView;
+ private boolean mPreviewPopupVisible;
+ private final int[] mWindowOffset = new int[2];
private ResourceValues mResourceValues;
- /**
- * True if the popup window is currently visible.
- */
- private boolean mPreviewPopupVisible;
-
- /**
- * Time when the user started tapping. This is used to calculate the time delay before fading
- * in the PopupWindow
- */
- private long mPreviewPopupStartTime;
-
- /**
- * Needed only inside {@link #onTouchEvent(MotionEvent)} to get the location of touch events.
- */
- private int[] mWindowOffset;
-
- /**
- * Needed to measure text. Used inside {@link #onDraw(Canvas)}
- */
- private final Rect bounds = new Rect();
-
- /**
- * Used and cached inside {@link #onDraw(Canvas)}
- */
- private FontMetrics mFontMetrics;
-
- /**
- * Used and cached inside {@link #onDraw(Canvas)}
- */
- private Paint mPaint;
-
- /**
- * The list of displayed sections. "Virtual" sections can be empty and therefore don't show
- * up as regular sections
- */
- private final ArrayList<VirtualSection> mVirtualSections = new ArrayList<VirtualSection>();
-
public ContactListAizyView(Context context) {
super(context);
}
@@ -109,78 +70,37 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- mResourceValues = new ResourceValues(getResources());
+ mResourceValues = new ResourceValues(getContext().getResources());
final LayoutInflater inflater =
(LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mPreviewPopupWindow = new PopupWindow(
inflater.inflate(R.layout.aizy_popup_window, null, false),
- (int) mResourceValues.previewWidth, (int) mResourceValues.previewHeight);
- mPreviewPopupWindow.setAnimationStyle(android.R.style.Animation_Toast);
+ mResourceValues.getPreviewWidth(), mResourceValues.getPreviewHeight());
mPreviewPopupTextView =
(TextView) mPreviewPopupWindow.getContentView().findViewById(R.id.caption);
}
- /**
- * Sets up the Aizy based on the indexer and completely reads its contents.
- * This function has to be called everytime the data is changed.
- */
- public void readFromIndexer(SectionIndexer indexer) {
- mVirtualSections.clear();
- final String alphabetString = getResources().getString(R.string.visualScrollerAlphabet);
- final String[] alphabet = alphabetString.split(";");
-
- // We expect to get 10 additional items that the base alphabet
- mVirtualSections.ensureCapacity(alphabet.length + 10);
-
- if (indexer != null) {
- // Add the real sections
- final Object[] sections = indexer.getSections();
- for (int sectionIndex = 0; sectionIndex < sections.length; sectionIndex++) {
- final Object section = sections[sectionIndex];
- final String caption = section == null ? "" : section.toString();
- final int position = indexer.getPositionForSection(sectionIndex);
- mVirtualSections.add(new VirtualSection(caption, sectionIndex, position));
- }
+ public void setIndexer(SectionIndexer indexer, int itemCount) {
+ mIndexer = indexer;
+ mItemCount = itemCount;
+ if (mIndexer == null) {
+ mSectionPositions = null;
+ return;
}
- final Collator collator = PhonebookCollatorFactory.getCollator();
-
- // Add the base alphabet if missing
- for (String caption : alphabet) {
- boolean insertAtEnd = true;
- VirtualSection previousVirtualSection = null;
- for (int i = 0; i < mVirtualSections.size(); i++) {
- final VirtualSection virtualSection = mVirtualSections.get(i);
- final String virtualSectionCaption = virtualSection.getCaption();
- final int comparison = collator.compare(virtualSectionCaption, caption);
- if (comparison == 0) {
- // element is already in the list.
- insertAtEnd = false;
- break;
- }
- if (comparison > 0) {
- // we stepped too far. the element belongs before the element at i
- insertAtEnd = false;
- final int realSectionPosition = previousVirtualSection == null ? 0
- : previousVirtualSection.getRealSectionPosition();
- mVirtualSections.add(i, new VirtualSection(caption, -1, realSectionPosition));
- break;
- }
- previousVirtualSection = virtualSection;
- }
- if (insertAtEnd) {
- final int realSectionPosition = previousVirtualSection == null ? 0
- : previousVirtualSection.getRealSectionPosition();
- mVirtualSections.add(new VirtualSection(caption, -1, realSectionPosition));
- }
+ // Read the section positions
+ final Object[] sections = mIndexer.getSections();
+ final int sectionCount = sections.length;
+ if (mSectionPositions == null || mSectionPositions.length != sectionCount) {
+ mSectionPositions = new int[sectionCount];
}
- invalidate();
+ for (int i = 0; i < sectionCount; i++) {
+ mSectionPositions[i] = mIndexer.getPositionForSection(i);
+ }
+
}
- /**
- * Sets the Listener that is called everytime the user taps on this control.
- */
public void setListener(Listener listener) {
mListener = listener;
}
@@ -192,75 +112,19 @@
@Override
protected void onDraw(Canvas canvas) {
- if (mPaint == null) {
- mPaint = new Paint();
- mPaint.setTextSize(mResourceValues.textSize);
- mPaint.setAntiAlias(true);
- mPaint.setTextAlign(Align.CENTER);
- mPaint.setTypeface(Typeface.DEFAULT_BOLD);
- }
- if (mFontMetrics == null) {
- mFontMetrics = mPaint.getFontMetrics();
- }
- final float fontHeight = mFontMetrics.descent - mFontMetrics.ascent;
- final int halfWidth = getWidth() / 2;
- // Draw
- float lastVisibleY = Float.NEGATIVE_INFINITY;
- final float sectionHeight = (float) getHeight() / mVirtualSections.size();
- for (int i = 0; i < mVirtualSections.size(); i++) {
- final VirtualSection virtualSection = mVirtualSections.get(i);
- final String caption = virtualSection.getCaption();
- if (!virtualSection.isMeasured()) {
- mPaint.getTextBounds(caption, 0, caption.length(), bounds);
- virtualSection.setMeasuredSize(-bounds.top);
- }
- final float y = i * sectionHeight;
- if (lastVisibleY + fontHeight < y) {
- mPaint.setColor(virtualSection.getRealSectionIndex() != -1
- ? mResourceValues.nonEmptySectionColor : mResourceValues.emptySectionColor);
+ if (mIndexer == null) return;
- canvas.drawText(caption, halfWidth,
- y + sectionHeight / 2 + virtualSection.getMeasuredSize() / 2, mPaint);
- lastVisibleY = y;
- }
- }
+ drawLineAndText(canvas);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
- if (mWindowOffset == null) {
- mWindowOffset = new int[2];
- getLocationInWindow(mWindowOffset);
- }
+ getLocationInWindow(mWindowOffset);
- // Not initialized? Ignore
- if (mVirtualSections.size() == 0) return true;
-
- // Scroll the list itself
- final int boundedY = Math.min(Math.max(0, (int) (event.getY())), getHeight() - 1);
- final int index = boundedY * mVirtualSections.size() / getHeight();
- final VirtualSection virtualSection = mVirtualSections.get(index);
- final int sectionY = index * getHeight() / mVirtualSections.size();
- mPreviewPopupTextView.setText(virtualSection.getCaption());
- mPreviewPopupTextView.setTextColor(virtualSection.getRealSectionIndex() != -1
- ? mResourceValues.nonEmptySectionColor : mResourceValues.emptySectionColor);
-
- // Draw popup window
final int previewX = mWindowOffset[0] + getWidth();
- final float sectionHeight = (float) getHeight() / mVirtualSections.size();
- final int previewY = (int) (sectionY + mWindowOffset[1] + (sectionHeight -
- mPreviewPopupWindow.getHeight()) / 2);
- final int actionMasked = event.getActionMasked();
- final boolean fingerIsDown = actionMasked == MotionEvent.ACTION_DOWN;
- if (fingerIsDown) {
- mPreviewPopupStartTime = System.currentTimeMillis();
- }
- final boolean fingerIsDownOrScrubbing =
- actionMasked == MotionEvent.ACTION_MOVE || actionMasked == MotionEvent.ACTION_DOWN;
-
- final boolean previewPopupVisible = fingerIsDownOrScrubbing &&
- (System.currentTimeMillis() > mPreviewPopupStartTime + PREVIEW_TIME_DELAY_MS);
-
+ 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,
@@ -272,70 +136,88 @@
} else {
mPreviewPopupWindow.update(previewX, previewY, -1, -1);
}
+ final float yFactor = (float) getHeight() / mItemCount;
+ final int position = Math.max(0, (int) (event.getY() / yFactor));
+ 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);
+ }
- // Perform the actual scrolling
- if (mListener != null) mListener.onScroll(virtualSection.getRealSectionPosition());
+ if (mListener != null) mListener.onScroll(position);
+ super.onTouchEvent(event);
return true;
}
- /**
- * Reads an provides all values from the resource files
- */
- private static class ResourceValues {
- private final int emptySectionColor;
- private final int nonEmptySectionColor;
- private final float textSize;
- private final float previewWidth;
- private final float previewHeight;
+ private void drawLineAndText(Canvas canvas) {
+ final float yFactor = (float) getHeight() / mItemCount;
- private ResourceValues(Resources resources) {
- emptySectionColor = resources.getColor(R.color.aizy_empty_section);
- nonEmptySectionColor = resources.getColor(R.color.aizy_non_empty_section);
- textSize = resources.getDimension(R.dimen.aizy_text_size);
- previewWidth = resources.getDimension(R.dimen.aizy_preview_width);
- previewHeight = resources.getDimension(R.dimen.aizy_preview_height);
+ final Paint paint = new Paint();
+
+ paint.setColor(mResourceValues.getLineColor());
+ paint.setAntiAlias(true);
+
+ // Draw sections
+ final float centerX = getWidth() * 0.5f;
+ for (int i = 1; i < mSectionPositions.length; i++) {
+ final float y1 = mSectionPositions[i - 1] * yFactor;
+ final float y2 = mSectionPositions[i] * yFactor;
+ canvas.drawLine(
+ centerX, y1 + 1.0f,
+ centerX, y2 - 1.0f,
+ paint);
}
+
+ // Draw knob
+ final Drawable knob = mResourceValues.getKnobDrawable();
+ final int w = knob.getIntrinsicWidth();
+ final int h = knob.getIntrinsicWidth();
+ final float y = mPosition * yFactor;
+ knob.setBounds(
+ (int) (centerX - w / 2.0f), (int) (y - h / 2.0f),
+ (int) (centerX + w / 2.0f), (int) (y + h / 2.0f));
+ knob.draw(canvas);
}
- private static class VirtualSection {
- private final String mCaption;
- private final int mRealSectionIndex;
- private final int mRealSectionPosition;
- private float mMeasuredSize = Float.NaN;
+ public void listOnScroll(int firstVisibleItem) {
+ mPosition = firstVisibleItem;
+ invalidate();
+ }
- public String getCaption() {
- return mCaption;
+ private static class ResourceValues {
+ private int mLineColor;
+ private Drawable mKnobDrawable;
+ private int mPreviewWidth;
+ private int mPreviewHeight;
+
+ public int getLineColor() {
+ return mLineColor;
}
- public int getRealSectionIndex() {
- return mRealSectionIndex;
+ public Drawable getKnobDrawable() {
+ return mKnobDrawable;
}
- public int getRealSectionPosition() {
- return mRealSectionPosition;
+ public int getPreviewWidth() {
+ return mPreviewWidth;
}
- public boolean isMeasured() {
- return mMeasuredSize == Float.NaN;
+ public int getPreviewHeight() {
+ return mPreviewHeight;
}
- public void setMeasuredSize(float value) {
- mMeasuredSize = value;
- }
-
- public float getMeasuredSize() {
- return mMeasuredSize;
- }
-
- public VirtualSection(String caption, int realSectionIndex, int realSectionPosition) {
- mCaption = caption;
- mRealSectionIndex = realSectionIndex;
- mRealSectionPosition = realSectionPosition;
+ public ResourceValues(Resources resources) {
+ mLineColor = resources.getColor(R.color.aizy_line_color);
+ mKnobDrawable = resources.getDrawable(R.drawable.temp_aizy_knob);
+ mPreviewWidth = resources.getDimensionPixelSize(R.dimen.aizy_preview_width);
+ mPreviewHeight = resources.getDimensionPixelSize(R.dimen.aizy_preview_height);
}
}
public interface Listener {
void onScroll(int position);
}
-}
+}
\ No newline at end of file