summaryrefslogtreecommitdiffstats
path: root/src/com/android/launcher3/AppDrawerScrubber.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/launcher3/AppDrawerScrubber.java')
-rw-r--r--src/com/android/launcher3/AppDrawerScrubber.java239
1 files changed, 192 insertions, 47 deletions
diff --git a/src/com/android/launcher3/AppDrawerScrubber.java b/src/com/android/launcher3/AppDrawerScrubber.java
index 0ace60da8..1670934d6 100644
--- a/src/com/android/launcher3/AppDrawerScrubber.java
+++ b/src/com/android/launcher3/AppDrawerScrubber.java
@@ -19,26 +19,30 @@ package com.android.launcher3;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;
-public class AppDrawerScrubber extends LinearLayout {
-
- private final int SCRUBBER_INDICATOR_DISPLAY_DURATION = 200;
- private final float SCRUBBER_INDICATOR_DISPLAY_TRANSLATIONY = 20f;
+import java.util.ArrayList;
+public class AppDrawerScrubber extends LinearLayout {
private AppDrawerListAdapter mAdapter;
private RecyclerView mListView;
private TextView mScrubberIndicator;
private SeekBar mSeekBar;
- private String[] mSections;
+ private AutoExpandTextView mScrubberText;
+ private SectionContainer mSectionContainer;
private LinearLayoutManager mLayoutManager;
+ private ScrubberAnimationState mScrubberAnimationState;
public AppDrawerScrubber(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -50,9 +54,85 @@ public class AppDrawerScrubber extends LinearLayout {
init(context);
}
+ /**
+ * Simple container class that tries to abstract out the knowledge of complex sections vs
+ * simple string sections
+ */
+ private static class SectionContainer {
+ private ArrayList<AppDrawerScrubberSections> mSections;
+ private String[] mHeaders;
+
+ public SectionContainer(String[] headers) {
+ mSections = AppDrawerScrubberSections.createSections(headers);
+ mHeaders = headers;
+ }
+
+ public int size() {
+ return showLetters() ? mSections.size() : mHeaders.length;
+ }
+
+ public String getHeader(int idx) {
+ return showLetters() ? mSections.get(idx).getText() : mHeaders[idx];
+ }
+
+ /**
+ * Because the list section headers is not necessarily the same size as the scrubber
+ * letters, we need to map from the larger list to the smaller list.
+ * In the case that curIdx is not highlighted, it will use the directional index to
+ * determine the adapter index
+ * @return the mHeaders index (aka the underlying adapter index).
+ */
+ public int getAdapterIndex(int prevIdx, int curIdx) {
+ if (!showLetters()) {
+ return curIdx;
+ }
+
+ // because we have some unhighlighted letters, we need to first get the directional
+ // index before getting the adapter index
+ return mSections.get(getDirectionalIndex(prevIdx, curIdx)).getAdapterIndex();
+ }
+
+ /**
+ * Given the direction the user is scrolling in, return the closest index which is a
+ * highlighted index
+ */
+ public int getDirectionalIndex(int prevIdx, int curIdx) {
+ if (!showLetters() || mSections.get(curIdx).getHighlight()) {
+ return curIdx;
+ }
+
+ if (prevIdx < curIdx) {
+ return mSections.get(curIdx).getNextIndex();
+ } else {
+ return mSections.get(curIdx).getPreviousIndex();
+ }
+ }
+
+ /**
+ * @return true if the scrubber is showing characters as opposed to a line
+ */
+ public boolean showLetters() {
+ return mSections != null;
+ }
+
+ /**
+ * Initializes the scrubber text with the proper characters
+ */
+ public void initializeScrubberText(AutoExpandTextView scrubberText) {
+ scrubberText.setSections(AppDrawerScrubberSections.getHighlightText(mSections));
+ }
+ }
+
public void updateSections() {
- mSections = (String[]) mAdapter.getSections();
- mSeekBar.setMax(mSections.length - 1);
+ mSectionContainer = new SectionContainer((String[]) mAdapter.getSections());
+ mSectionContainer.initializeScrubberText(mScrubberText);
+ mSeekBar.setMax(mSectionContainer.size() - 1);
+
+ // show a white line if there are no letters, otherwise show transparent
+ Drawable d = mSectionContainer.showLetters() ? new ColorDrawable(Color.TRANSPARENT)
+ : getContext().getResources().getDrawable(R.drawable.seek_back);
+ ((ViewGroup)mSeekBar.getParent()).setBackground(d);
+
}
public void setSource(RecyclerView listView) {
@@ -68,63 +148,128 @@ public class AppDrawerScrubber extends LinearLayout {
private boolean isReady() {
return mListView != null &&
mAdapter != null &&
- mSections != null;
+ mSectionContainer != null;
}
private void init(Context context) {
LayoutInflater.from(context).inflate(R.layout.scrub_layout, this);
+ mScrubberAnimationState = new ScrubberAnimationState();
mSeekBar = (SeekBar) findViewById(R.id.scrubber);
+ mScrubberText = (AutoExpandTextView) findViewById(R.id.scrubberText);
+ mSeekBar.setOnSeekBarChangeListener(mScrubberAnimationState);
+ }
- mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
- @Override
- public void onProgressChanged(SeekBar seekBar, final int progress, boolean fromUser) {
- if (!isReady()) {
- return;
- }
- resetScrubber();
+ /**
+ * Handles the animations of the scrubber indicator
+ */
+ private class ScrubberAnimationState implements SeekBar.OnSeekBarChangeListener {
+ private static final long SCRUBBER_DISPLAY_DURATION = 150;
+ private static final float SCRUBBER_SCALE_START = 0f;
+ private static final float SCRUBBER_SCALE_END = 1f;
+ private static final float SCRUBBER_ALPHA_START = 0f;
+ private static final float SCRUBBER_ALPHA_END = 1f;
+
+ private boolean mTouchingTrack = false;
+ private boolean mAnimatingIn = false;
+ private int mLastIndex = -1;
- String section = String.valueOf(mSections[progress]);
+ private void touchTrack(boolean touching) {
+ mTouchingTrack = touching;
- if (mScrubberIndicator != null) {
- float translateX = (progress * seekBar.getWidth()) / mSections.length;
- translateX -= (mScrubberIndicator.getWidth() / 6); // offset for alignment
- mScrubberIndicator.setTranslationX(translateX);
- mScrubberIndicator.setText(section);
+ if (mScrubberIndicator != null) {
+ if (mTouchingTrack) {
+ animateIn();
+ } else if (!mAnimatingIn) { // finish animating in before animating out
+ animateOut();
}
- mLayoutManager.smoothScrollToPosition(mListView, null,
- mAdapter.getPositionForSection(progress));
+ mAdapter.setDragging(mTouchingTrack);
}
+ }
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
- resetScrubber();
- if (mScrubberIndicator != null) {
- mScrubberIndicator.setAlpha(1f);
- mScrubberIndicator.setVisibility(View.VISIBLE);
- }
- }
+ private void animateIn() {
+ // start from a scratch position when animating in
+ mScrubberIndicator.animate().cancel();
+ mScrubberIndicator.setPivotX(mScrubberIndicator.getMeasuredWidth() / 2);
+ mScrubberIndicator.setPivotY(mScrubberIndicator.getMeasuredHeight() * 0.8f);
+ mScrubberIndicator.setAlpha(SCRUBBER_ALPHA_START);
+ mScrubberIndicator.setScaleX(SCRUBBER_SCALE_START);
+ mScrubberIndicator.setScaleY(SCRUBBER_SCALE_START);
+ mScrubberIndicator.setVisibility(View.VISIBLE);
+ mAnimatingIn = true;
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
- resetScrubber();
- if (mScrubberIndicator != null) {
- mScrubberIndicator.animate().alpha(0f).translationYBy(20f)
- .setDuration(200).setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mScrubberIndicator.setVisibility(View.INVISIBLE);
+ mScrubberIndicator.animate()
+ .alpha(SCRUBBER_ALPHA_END)
+ .scaleX(SCRUBBER_SCALE_END)
+ .scaleY(SCRUBBER_SCALE_END)
+ .setDuration(SCRUBBER_DISPLAY_DURATION)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mAnimatingIn = false;
+ // if the user has stopped touching the seekbar, animate back out
+ if (!mTouchingTrack) {
+ animateOut();
}
- });
- }
+ }
+ })
+ .start();
+ }
+
+ private void animateOut() {
+ mScrubberIndicator.animate()
+ .alpha(SCRUBBER_ALPHA_START)
+ .scaleX(SCRUBBER_SCALE_START)
+ .scaleY(SCRUBBER_SCALE_START)
+ .setDuration(SCRUBBER_DISPLAY_DURATION)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mScrubberIndicator.setVisibility(View.INVISIBLE);
+ }
+ });
+ }
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int index, boolean fromUser) {
+ if (!isReady()) {
+ return;
}
- private void resetScrubber() {
- if (mScrubberIndicator != null) {
- mScrubberIndicator.animate().cancel();
- mScrubberIndicator.setTranslationY(0f);
+ if (mScrubberIndicator != null) {
+ // get the index based on the direction the user is scrolling
+ int directionalIndex = mSectionContainer.getDirectionalIndex(mLastIndex, index);
+ String sectionText = mSectionContainer.getHeader(directionalIndex);
+
+ float translateX = (index * seekBar.getWidth()) / (float)mSectionContainer.size();
+ // if we are showing letters, grab the position based on the text view
+ if (mSectionContainer.showLetters()) {
+ translateX = mScrubberText.getPositionOfSection(index);
}
+
+ // center the x position
+ translateX -= mScrubberIndicator.getMeasuredWidth() / 2;
+
+ mScrubberIndicator.setTranslationX(translateX);
+ mScrubberIndicator.setText(sectionText);
}
- });
+
+ // get the index of the underlying list
+ int adapterIndex = mSectionContainer.getAdapterIndex(mLastIndex, index);
+ mLayoutManager.smoothScrollToPosition(mListView, null,
+ mAdapter.getPositionForSection(adapterIndex));
+
+ mLastIndex = index;
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ touchTrack(true);
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ touchTrack(false);
+ }
}
} \ No newline at end of file