FastScroller.java revision 8ac22b0ada459d8f97787d4f12973894778f03e1
19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.widget;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
190ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.animation.Animator;
200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.animation.Animator.AnimatorListener;
210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.animation.AnimatorListenerAdapter;
220ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.animation.AnimatorSet;
230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.animation.ObjectAnimator;
240ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.animation.PropertyValuesHolder;
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context;
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.res.ColorStateList;
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.res.TypedArray;
2820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powellimport android.graphics.Rect;
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.drawable.Drawable;
300ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.os.Build;
314cfeedb6aea057606cbc461fd1019f71b8aca843Alan Viveretteimport android.os.SystemClock;
32f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viveretteimport android.text.TextUtils;
330ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.text.TextUtils.TruncateAt;
340ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.util.IntProperty;
350ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.util.MathUtils;
360ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.util.Property;
377b63063625467baff1db048e84c5781408a5c86dAlan Viveretteimport android.util.TypedValue;
380ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.view.Gravity;
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.MotionEvent;
4020232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powellimport android.view.View;
410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.view.View.MeasureSpec;
42af5280cc71e155758c2f6625eb999cabb37e937fAdam Powellimport android.view.ViewConfiguration;
430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.view.ViewGroup.LayoutParams;
440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.view.ViewGroupOverlay;
45d43bd48fe311fa6320e46ca9b9ce25241c476ac6Adam Powellimport android.widget.AbsListView.OnScrollListener;
468636ace69640566768f89c2d92d2851ff064a2e9Alan Viveretteimport android.widget.ImageView.ScaleType;
470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Helper class for AbsListView to draw and control the Fast Scroll thumb
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectclass FastScroller {
520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Duration of fade-out animation. */
530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static final int DURATION_FADE_OUT = 300;
540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
550ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Duration of fade-in animation. */
560ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static final int DURATION_FADE_IN = 150;
570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
580ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Duration of transition cross-fade animation. */
590ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static final int DURATION_CROSS_FADE = 50;
600ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Duration of transition resize animation. */
620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static final int DURATION_RESIZE = 100;
630ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Inactivity timeout before fading controls. */
650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static final long FADE_TIMEOUT = 1500;
66e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Minimum number of pages to justify showing a fast scroll thumb. */
680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static final int MIN_PAGES = 4;
690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Scroll thumb and preview not showing. */
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int STATE_NONE = 0;
7220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Scroll thumb visible and moving along with the scrollbar. */
740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static final int STATE_VISIBLE = 1;
750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Scroll thumb and preview being dragged by user. */
770ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static final int STATE_DRAGGING = 2;
7820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
790ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    // Positions for preview image and text.
8020232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    private static final int OVERLAY_FLOATING = 0;
8120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    private static final int OVERLAY_AT_THUMB = 1;
82e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
830ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    // Indices for mPreviewResId.
840ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static final int PREVIEW_LEFT = 0;
850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static final int PREVIEW_RIGHT = 1;
860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
870ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Delay before considering a tap in the thumb area to be a drag. */
880ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static final long TAP_TIMEOUT = ViewConfiguration.getTapTimeout();
890ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
900ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final Rect mTempBounds = new Rect();
910ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final Rect mTempMargins = new Rect();
92fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette    private final Rect mContainerRect = new Rect();
9320232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final AbsListView mList;
950ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final ViewGroupOverlay mOverlay;
960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final TextView mPrimaryText;
970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final TextView mSecondaryText;
980ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final ImageView mThumbImage;
990ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final ImageView mTrackImage;
1008636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    private final View mPreviewImage;
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1020ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
1030ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Preview image resource IDs for left- and right-aligned layouts. See
1040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * {@link #PREVIEW_LEFT} and {@link #PREVIEW_RIGHT}.
1050ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
1060ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final int[] mPreviewResId = new int[2];
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
1090ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Padding in pixels around the preview text. Applied as layout margins to
1100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * the preview text and padding to the preview image.
1110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
1128636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    private int mPreviewPadding;
1138636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette
1148636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    private int mPreviewMinWidth;
1158636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    private int mPreviewMinHeight;
1168636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    private int mThumbMinWidth;
1178636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    private int mThumbMinHeight;
1188636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette
1198636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    /** Theme-specified text size. Used only if text appearance is not set. */
1208636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    private float mTextSize;
1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1228636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    /** Theme-specified text color. Used only if text appearance is not set. */
1238636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    private ColorStateList mTextColor;
1248636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette
1258636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    private Drawable mThumbDrawable;
1268636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    private Drawable mTrackDrawable;
1278636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    private int mTextAppearance;
1280ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
12926bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette    /** Total width of decorations. */
1308636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    private int mWidth;
13126bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette
1320ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Set containing decoration transition animations. */
1330ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private AnimatorSet mDecorAnimation;
1340ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1350ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Set containing preview text transition animations. */
1360ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private AnimatorSet mPreviewAnimation;
1370ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1380ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Whether the primary text is showing. */
1390ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private boolean mShowingPrimary;
1400ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Whether we're waiting for completion of scrollTo(). */
1420ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private boolean mScrollCompleted;
1430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** The position of the first visible item in the list. */
1450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private int mFirstVisibleItem;
1460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** The number of headers at the top of the view. */
1480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private int mHeaderCount;
1490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1500ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** The index of the current section. */
1510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private int mCurrentSection = -1;
1520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
15326bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette    /** The current scrollbar position. */
15426bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette    private int mScrollbarPosition = -1;
15526bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette
1560ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Whether the list is long enough to need a fast scroller. */
1574df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project    private boolean mLongList;
158e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
1590ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private Object[] mSections;
160e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
161fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette    /** Whether this view is currently performing layout. */
162fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette    private boolean mUpdatingLayout;
163fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette
1640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
1650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Current decoration state, one of:
1660ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * <ul>
1670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * <li>{@link #STATE_NONE}, nothing visible
1680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * <li>{@link #STATE_VISIBLE}, showing track and thumb
1690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * <li>{@link #STATE_DRAGGING}, visible and showing preview
1700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * </ul>
1710ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mState;
173e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
174f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette    /** Whether the preview image is visible. */
175f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette    private boolean mShowingPreview;
176f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette
1770ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private BaseAdapter mListAdapter;
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private SectionIndexer mSectionIndexer;
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1800ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Whether decorations should be laid out from right to left. */
1810ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private boolean mLayoutFromRight;
18220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
183447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    /** Whether the fast scroller is enabled. */
184447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    private boolean mEnabled;
185447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette
1860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Whether the scrollbar and decorations should always be shown. */
18720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    private boolean mAlwaysShow;
18820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
1890ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
1900ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Position for the preview image and text. One of:
1910ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * <ul>
1920ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * <li>{@link #OVERLAY_AT_THUMB}
1930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * <li>{@link #OVERLAY_FLOATING}
1940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * </ul>
1950ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
19620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    private int mOverlayPosition;
19720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
19826bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette    /** Current scrollbar style, including inset and overlay properties. */
19926bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette    private int mScrollBarStyle;
20026bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette
2010ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Whether to precisely match the thumb position to the list. */
202568ccd88d7efd33da0c535e07b7ea7cf476be22aAdam Powell    private boolean mMatchDragPosition;
203568ccd88d7efd33da0c535e07b7ea7cf476be22aAdam Powell
2040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private float mInitialTouchY;
2054cfeedb6aea057606cbc461fd1019f71b8aca843Alan Viverette    private long mPendingDrag = -1;
206af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell    private int mScaledTouchSlop;
207af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
2080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
2090ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Used to delay hiding fast scroll decorations.
2100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
2110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final Runnable mDeferHide = new Runnable() {
2120ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
2130ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public void run() {
2140ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            setState(STATE_NONE);
2150ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
2160ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    };
2170ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
2180ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
2190ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Used to effect a transition from primary to secondary text.
2200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
2210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final AnimatorListener mSwitchPrimaryListener = new AnimatorListenerAdapter() {
2220ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
2230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public void onAnimationEnd(Animator animation) {
2240ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mShowingPrimary = !mShowingPrimary;
225af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        }
226af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell    };
227af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
2288636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    public FastScroller(AbsListView listView, int styleResId) {
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mList = listView;
2300ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
23139bed695cecdbefc9fae785692fe468cd9007c35Alan Viverette        final Context context = listView.getContext();
2320ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
2338636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mScrollBarStyle = listView.getScrollBarStyle();
2348636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette
2358636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mScrollCompleted = true;
2368636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mState = STATE_VISIBLE;
2378636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mMatchDragPosition =
2388636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB;
2398636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette
2408636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mTrackImage = new ImageView(context);
2418636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mTrackImage.setScaleType(ScaleType.FIT_XY);
2428636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mThumbImage = new ImageView(context);
2438636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mThumbImage.setScaleType(ScaleType.FIT_XY);
2448636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mPreviewImage = new View(context);
2458636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mPreviewImage.setAlpha(0f);
2468636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette
2478636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mPrimaryText = createPreviewTextView(context);
2488636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mSecondaryText = createPreviewTextView(context);
2498636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette
2508636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        setStyle(styleResId);
2510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
2528636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        final ViewGroupOverlay overlay = listView.getOverlay();
2538636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mOverlay = overlay;
2548636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        overlay.add(mTrackImage);
2558636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        overlay.add(mThumbImage);
2568636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        overlay.add(mPreviewImage);
2578636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        overlay.add(mPrimaryText);
2588636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        overlay.add(mSecondaryText);
2590ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
2608636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        getSectionsFromIndexer();
2618636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        updateLongList(listView.getChildCount(), listView.getCount());
2628636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        setScrollbarPosition(listView.getVerticalScrollbarPosition());
2638636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        postAutoHide();
2648636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    }
26526bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette
2668636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    private void updateAppearance() {
2678636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        final Context context = mList.getContext();
26826bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette        int width = 0;
2690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
2700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Add track to overlay if it has an image.
2718636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mTrackImage.setImageDrawable(mTrackDrawable);
2728636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        if (mTrackDrawable != null) {
2738636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette            width = Math.max(width, mTrackDrawable.getIntrinsicWidth());
2740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
2750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
2760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Add thumb to overlay if it has an image.
2778636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mThumbImage.setImageDrawable(mThumbDrawable);
2788636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mThumbImage.setMinimumWidth(mThumbMinWidth);
2798636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mThumbImage.setMinimumHeight(mThumbMinHeight);
2808636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        if (mThumbDrawable != null) {
2818636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette            width = Math.max(width, mThumbDrawable.getIntrinsicWidth());
2820ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
2830ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
2848636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        // Account for minimum thumb width.
2858636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mWidth = Math.max(width, mThumbMinWidth);
2860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
2878636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mPreviewImage.setMinimumWidth(mPreviewMinWidth);
2888636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mPreviewImage.setMinimumHeight(mPreviewMinHeight);
28926bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette
2908636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        if (mTextAppearance != 0) {
2918636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette            mPrimaryText.setTextAppearance(context, mTextAppearance);
2928636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette            mSecondaryText.setTextAppearance(context, mTextAppearance);
2938636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        }
2940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
2958636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        if (mTextColor != null) {
2968636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette            mPrimaryText.setTextColor(mTextColor);
2978636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette            mSecondaryText.setTextColor(mTextColor);
2988636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        }
2998636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette
3008636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        if (mTextSize > 0) {
3018636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette            mPrimaryText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);
3028636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette            mSecondaryText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);
3038636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        }
3040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
3058636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        final int textMinSize = Math.max(0, mPreviewMinHeight);
3066b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        mPrimaryText.setMinimumWidth(textMinSize);
3076b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        mPrimaryText.setMinimumHeight(textMinSize);
3086b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        mSecondaryText.setMinimumWidth(textMinSize);
3096b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        mSecondaryText.setMinimumHeight(textMinSize);
3100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
3118636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        refreshDrawablePressedState();
3128636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    }
3130ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
3148636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    public void setStyle(int resId) {
3158636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        final Context context = mList.getContext();
3168636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        final TypedArray ta = context.obtainStyledAttributes(null,
3178636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                com.android.internal.R.styleable.FastScroll, android.R.attr.fastScrollStyle, resId);
3188636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        final int N = ta.getIndexCount();
3198636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        for (int i = 0; i < N; i++) {
3208636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette            final int index = ta.getIndex(i);
3218636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette            switch (index) {
3228636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                case com.android.internal.R.styleable.FastScroll_position:
3238636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    mOverlayPosition = ta.getInt(index, OVERLAY_FLOATING);
3248636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    break;
3258636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                case com.android.internal.R.styleable.FastScroll_backgroundLeft:
3268636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    mPreviewResId[PREVIEW_LEFT] = ta.getResourceId(index, 0);
3278636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    break;
3288636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                case com.android.internal.R.styleable.FastScroll_backgroundRight:
3298636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    mPreviewResId[PREVIEW_RIGHT] = ta.getResourceId(index, 0);
3308636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    break;
3318636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                case com.android.internal.R.styleable.FastScroll_thumbDrawable:
3328636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    mThumbDrawable = ta.getDrawable(index);
3338636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    break;
3348636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                case com.android.internal.R.styleable.FastScroll_trackDrawable:
3358636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    mTrackDrawable = ta.getDrawable(index);
3368636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    break;
3378636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                case com.android.internal.R.styleable.FastScroll_textAppearance:
3388636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    mTextAppearance = ta.getResourceId(index, 0);
3398636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    break;
3408636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                case com.android.internal.R.styleable.FastScroll_textColor:
3418636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    mTextColor = ta.getColorStateList(index);
3428636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    break;
3438636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                case com.android.internal.R.styleable.FastScroll_textSize:
3448636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    mTextSize = ta.getDimensionPixelSize(index, 0);
3458636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    break;
3468636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                case com.android.internal.R.styleable.FastScroll_minWidth:
3478636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    mPreviewMinWidth = ta.getDimensionPixelSize(index, 0);
3488636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    break;
3498636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                case com.android.internal.R.styleable.FastScroll_minHeight:
3508636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    mPreviewMinHeight = ta.getDimensionPixelSize(index, 0);
3518636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    break;
3528636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                case com.android.internal.R.styleable.FastScroll_thumbMinWidth:
3538636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    mThumbMinWidth = ta.getDimensionPixelSize(index, 0);
3548636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    break;
3558636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                case com.android.internal.R.styleable.FastScroll_thumbMinHeight:
3568636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    mThumbMinHeight = ta.getDimensionPixelSize(index, 0);
3578636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    break;
3588636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                case com.android.internal.R.styleable.FastScroll_padding:
3598636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    mPreviewPadding = ta.getDimensionPixelSize(index, 0);
3608636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    break;
3618636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette            }
3628636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        }
3630ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
3648636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        updateAppearance();
3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
368447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette     * Removes this FastScroller overlay from the host view.
369447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette     */
370447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    public void remove() {
371447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        mOverlay.remove(mTrackImage);
372447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        mOverlay.remove(mThumbImage);
373447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        mOverlay.remove(mPreviewImage);
374447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        mOverlay.remove(mPrimaryText);
375447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        mOverlay.remove(mSecondaryText);
376447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    }
377447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette
378447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    /**
379447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette     * @param enabled Whether the fast scroll thumb is enabled.
380447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette     */
381447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    public void setEnabled(boolean enabled) {
382b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        if (mEnabled != enabled) {
383b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette            mEnabled = enabled;
384447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette
3858ac22b0ada459d8f97787d4f12973894778f03e1Alan Viverette            onStateDependencyChanged(true);
386447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        }
387447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    }
388447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette
389447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    /**
390447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette     * @return Whether the fast scroll thumb is enabled.
391447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette     */
392447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    public boolean isEnabled() {
393b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        return mEnabled && (mLongList || mAlwaysShow);
394447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    }
395447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette
396447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    /**
3970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param alwaysShow Whether the fast scroll thumb should always be shown
3980ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
39920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    public void setAlwaysShow(boolean alwaysShow) {
400b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        if (mAlwaysShow != alwaysShow) {
401b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette            mAlwaysShow = alwaysShow;
4020ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
4038ac22b0ada459d8f97787d4f12973894778f03e1Alan Viverette            onStateDependencyChanged(false);
40420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell        }
40520232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    }
40620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
4070ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
4080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @return Whether the fast scroll thumb will always be shown
4090ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @see #setAlwaysShow(boolean)
4100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
41120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    public boolean isAlwaysShowEnabled() {
41220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell        return mAlwaysShow;
41320232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    }
41420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
415b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette    /**
416b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette     * Called when one of the variables affecting enabled state changes.
4178ac22b0ada459d8f97787d4f12973894778f03e1Alan Viverette     *
4188ac22b0ada459d8f97787d4f12973894778f03e1Alan Viverette     * @param peekIfEnabled whether the thumb should peek, if enabled
419b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette     */
4208ac22b0ada459d8f97787d4f12973894778f03e1Alan Viverette    private void onStateDependencyChanged(boolean peekIfEnabled) {
421b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        if (isEnabled()) {
422b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette            if (isAlwaysShowEnabled()) {
423b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                setState(STATE_VISIBLE);
424b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette            } else if (mState == STATE_VISIBLE) {
425b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                postAutoHide();
4268ac22b0ada459d8f97787d4f12973894778f03e1Alan Viverette            } else if (peekIfEnabled) {
4278ac22b0ada459d8f97787d4f12973894778f03e1Alan Viverette                setState(STATE_VISIBLE);
4288ac22b0ada459d8f97787d4f12973894778f03e1Alan Viverette                postAutoHide();
429b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette            }
430b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        } else {
431b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette            stop();
432b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        }
433b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette
434b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        mList.resolvePadding();
435b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette    }
436b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette
43726bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette    public void setScrollBarStyle(int style) {
43826bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette        if (mScrollBarStyle != style) {
43926bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            mScrollBarStyle = style;
44026bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette
44126bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            updateLayout();
44226bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette        }
44326bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette    }
44426bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette
4450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
4460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Immediately transitions the fast scroller decorations to a hidden state.
4470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
4480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    public void stop() {
4490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        setState(STATE_NONE);
4500ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
45120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
45220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    public void setScrollbarPosition(int position) {
453c23ee46c8fac06b1982d115368f772fea399a4e0Fabrice Di Meglio        if (position == View.SCROLLBAR_POSITION_DEFAULT) {
454c23ee46c8fac06b1982d115368f772fea399a4e0Fabrice Di Meglio            position = mList.isLayoutRtl() ?
455c23ee46c8fac06b1982d115368f772fea399a4e0Fabrice Di Meglio                    View.SCROLLBAR_POSITION_LEFT : View.SCROLLBAR_POSITION_RIGHT;
456c23ee46c8fac06b1982d115368f772fea399a4e0Fabrice Di Meglio        }
4570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
45826bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette        if (mScrollbarPosition != position) {
45926bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            mScrollbarPosition = position;
46026bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            mLayoutFromRight = position != View.SCROLLBAR_POSITION_LEFT;
4610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
46226bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            final int previewResId = mPreviewResId[mLayoutFromRight ? PREVIEW_RIGHT : PREVIEW_LEFT];
46326bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            mPreviewImage.setBackgroundResource(previewResId);
4640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
46526bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            // Add extra padding for text.
46626bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            final Drawable background = mPreviewImage.getBackground();
46726bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            if (background != null) {
46826bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette                final Rect padding = mTempBounds;
46926bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette                background.getPadding(padding);
47026bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette                padding.offset(mPreviewPadding, mPreviewPadding);
47126bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette                mPreviewImage.setPadding(padding.left, padding.top, padding.right, padding.bottom);
47226bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            }
4730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
47426bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            // Requires re-layout.
47526bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            updateLayout();
47626bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette        }
47720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    }
47820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
47920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    public int getWidth() {
48026bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette        return mWidth;
48120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    }
48220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
4830ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    public void onSizeChanged(int w, int h, int oldw, int oldh) {
4840ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        updateLayout();
4850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
4860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
4875db566f16a1b8f36e84a9be00cde40482d48466bAdam Powell    public void onItemCountChanged(int totalItemCount) {
4880ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int visibleItemCount = mList.getChildCount();
4890ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final boolean hasMoreItems = totalItemCount - visibleItemCount > 0;
4900ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (hasMoreItems && mState != STATE_DRAGGING) {
4910ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final int firstVisibleItem = mList.getFirstVisiblePosition();
4920ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            setThumbPos(getPosFromItemCount(firstVisibleItem, visibleItemCount, totalItemCount));
4939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
494b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette
495b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        updateLongList(visibleItemCount, totalItemCount);
496b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette    }
497b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette
498b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette    private void updateLongList(int visibleItemCount, int totalItemCount) {
499b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        final boolean longList = visibleItemCount > 0
500b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                && totalItemCount / visibleItemCount >= MIN_PAGES;
501b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        if (mLongList != longList) {
502b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette            mLongList = longList;
503b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette
5048ac22b0ada459d8f97787d4f12973894778f03e1Alan Viverette            onStateDependencyChanged(false);
505b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        }
5069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
507e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
5080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
5090ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Creates a view into which preview text can be placed.
5100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
5118636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    private TextView createPreviewTextView(Context context) {
5120ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final LayoutParams params = new LayoutParams(
5130ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
5140ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final TextView textView = new TextView(context);
5150ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        textView.setLayoutParams(params);
5160ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        textView.setSingleLine(true);
5170ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        textView.setEllipsize(TruncateAt.MIDDLE);
5180ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        textView.setGravity(Gravity.CENTER);
5190ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        textView.setAlpha(0f);
5200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
5210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Manually propagate inherited layout direction.
5220ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        textView.setLayoutDirection(mList.getLayoutDirection());
5230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
5240ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        return textView;
5259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
526e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
5270ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
5280ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Measures and layouts the scrollbar and decorations.
5290ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
53026bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette    public void updateLayout() {
531fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        // Prevent re-entry when RTL properties change as a side-effect of
532fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        // resolving padding.
533fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        if (mUpdatingLayout) {
534fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette            return;
535fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        }
536fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette
537fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        mUpdatingLayout = true;
538fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette
539fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        updateContainerRect();
540fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette
5410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        layoutThumb();
5420ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        layoutTrack();
5430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
5440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Rect bounds = mTempBounds;
5450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        measurePreview(mPrimaryText, bounds);
5460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        applyLayout(mPrimaryText, bounds);
5470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        measurePreview(mSecondaryText, bounds);
5480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        applyLayout(mSecondaryText, bounds);
5490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
5500ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mPreviewImage != null) {
5510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            // Apply preview image padding.
5520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            bounds.left -= mPreviewImage.getPaddingLeft();
5530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            bounds.top -= mPreviewImage.getPaddingTop();
5540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            bounds.right += mPreviewImage.getPaddingRight();
5550ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            bounds.bottom += mPreviewImage.getPaddingBottom();
5560ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            applyLayout(mPreviewImage, bounds);
55720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell        }
558fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette
559fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        mUpdatingLayout = false;
5609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
561e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
5620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
5630ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Layouts a view within the specified bounds and pins the pivot point to
5640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * the appropriate edge.
5650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *
5660ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param view The view to layout.
5670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param bounds Bounds at which to layout the view.
5680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
5690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void applyLayout(View view, Rect bounds) {
5700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        view.layout(bounds.left, bounds.top, bounds.right, bounds.bottom);
5710ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        view.setPivotX(mLayoutFromRight ? bounds.right - bounds.left : 0);
5720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
5730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
5740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
5750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Measures the preview text bounds, taking preview image padding into
5760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * account. This method should only be called after {@link #layoutThumb()}
5770ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * and {@link #layoutTrack()} have both been called at least once.
5780ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *
5790ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param v The preview text view to measure.
5800ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param out Rectangle into which measured bounds are placed.
5810ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
5820ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void measurePreview(View v, Rect out) {
5830ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Apply the preview image's padding as layout margins.
5840ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Rect margins = mTempMargins;
5850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        margins.left = mPreviewImage.getPaddingLeft();
5860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        margins.top = mPreviewImage.getPaddingTop();
5870ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        margins.right = mPreviewImage.getPaddingRight();
5880ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        margins.bottom = mPreviewImage.getPaddingBottom();
5890ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
5900ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mOverlayPosition == OVERLAY_AT_THUMB) {
5910ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            measureViewToSide(v, mThumbImage, margins, out);
59220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell        } else {
5930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            measureFloating(v, margins, out);
59420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell        }
5959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
5980ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Measures the bounds for a view that should be laid out against the edge
5990ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * of an adjacent view. If no adjacent view is provided, lays out against
6000ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * the list edge.
6010ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *
6020ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param view The view to measure for layout.
6030ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param adjacent (Optional) The adjacent view, may be null to align to the
6040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *            list edge.
6050ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param margins Layout margins to apply to the view.
6060ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param out Rectangle into which measured bounds are placed.
6070ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
6080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void measureViewToSide(View view, View adjacent, Rect margins, Rect out) {
6090ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int marginLeft;
6100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int marginTop;
6110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int marginRight;
6120ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (margins == null) {
6130ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginLeft = 0;
6140ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginTop = 0;
6150ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginRight = 0;
6160ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else {
6170ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginLeft = margins.left;
6180ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginTop = margins.top;
6190ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginRight = margins.right;
6200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
6211ffaebc41bfd47472543d1ff86c8412c868fc4c9Amith Yamasani
622fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final Rect container = mContainerRect;
623fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int containerWidth = container.width();
6240ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int maxWidth;
6250ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (adjacent == null) {
626fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette            maxWidth = containerWidth;
6270ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else if (mLayoutFromRight) {
6280ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            maxWidth = adjacent.getLeft();
6290ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else {
630fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette            maxWidth = containerWidth - adjacent.getRight();
6310ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
63220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
6330ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int adjMaxWidth = maxWidth - marginLeft - marginRight;
6340ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(adjMaxWidth, MeasureSpec.AT_MOST);
6350ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
6360ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        view.measure(widthMeasureSpec, heightMeasureSpec);
6370ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
6380ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Align to the left or right.
6398636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        final int width = Math.min(adjMaxWidth, view.getMeasuredWidth());
6400ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int left;
6410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int right;
6420ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mLayoutFromRight) {
643fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette            right = (adjacent == null ? container.right : adjacent.getLeft()) - marginRight;
6440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            left = right - width;
6450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else {
646fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette            left = (adjacent == null ? container.left : adjacent.getRight()) + marginLeft;
6470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            right = left + width;
6480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
64920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
6500ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Don't adjust the vertical position.
6510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int top = marginTop;
6520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int bottom = top + view.getMeasuredHeight();
6530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        out.set(left, top, right, bottom);
6540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
655af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
6560ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void measureFloating(View preview, Rect margins, Rect out) {
6570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int marginLeft;
6580ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int marginTop;
6590ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int marginRight;
6600ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (margins == null) {
6610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginLeft = 0;
6620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginTop = 0;
6630ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginRight = 0;
6640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else {
6650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginLeft = margins.left;
6660ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginTop = margins.top;
6670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginRight = margins.right;
6680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
669568ccd88d7efd33da0c535e07b7ea7cf476be22aAdam Powell
670fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final Rect container = mContainerRect;
671fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int containerWidth = container.width();
672fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int adjMaxWidth = containerWidth - marginLeft - marginRight;
6730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(adjMaxWidth, MeasureSpec.AT_MOST);
6740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
6750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        preview.measure(widthMeasureSpec, heightMeasureSpec);
6760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
6770ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Align at the vertical center, 10% from the top.
678fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int containerHeight = container.height();
6790ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int width = preview.getMeasuredWidth();
680fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int top = containerHeight / 10 + marginTop + container.top;
6810ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int bottom = top + preview.getMeasuredHeight();
682fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int left = (containerWidth - width) / 2 + container.left;
6830ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int right = left + width;
6840ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        out.set(left, top, right, bottom);
6859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
686e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
68726bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette    /**
68826bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette     * Updates the container rectangle used for layout.
68926bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette     */
690fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette    private void updateContainerRect() {
691fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final AbsListView list = mList;
69226bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette        list.resolvePadding();
69326bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette
694fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final Rect container = mContainerRect;
695fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        container.left = 0;
696fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        container.top = 0;
697fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        container.right = list.getWidth();
698fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        container.bottom = list.getHeight();
699fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette
70026bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette        final int scrollbarStyle = mScrollBarStyle;
701fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        if (scrollbarStyle == View.SCROLLBARS_INSIDE_INSET
702fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette                || scrollbarStyle == View.SCROLLBARS_INSIDE_OVERLAY) {
703fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette            container.left += list.getPaddingLeft();
704fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette            container.top += list.getPaddingTop();
705fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette            container.right -= list.getPaddingRight();
706fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette            container.bottom -= list.getPaddingBottom();
70726bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette
70826bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            // In inset mode, we need to adjust for padded scrollbar width.
70926bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            if (scrollbarStyle == View.SCROLLBARS_INSIDE_INSET) {
71026bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette                final int width = getWidth();
71126bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette                if (mScrollbarPosition == View.SCROLLBAR_POSITION_RIGHT) {
71226bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette                    container.right += width;
71326bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette                } else {
71426bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette                    container.left -= width;
71526bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette                }
71626bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            }
717fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        }
718fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette    }
719fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette
7200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
7210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Lays out the thumb according to the current scrollbar position.
7220ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
7230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void layoutThumb() {
7240ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Rect bounds = mTempBounds;
7250ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        measureViewToSide(mThumbImage, null, null, bounds);
7260ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        applyLayout(mThumbImage, bounds);
7279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
728e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
7290ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
730fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette     * Lays out the track centered on the thumb. Must be called after
731fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette     * {@link #layoutThumb}.
7320ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
7330ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void layoutTrack() {
7340ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final View track = mTrackImage;
7350ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final View thumb = mThumbImage;
736fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final Rect container = mContainerRect;
737fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int containerWidth = container.width();
738fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(containerWidth, MeasureSpec.AT_MOST);
7390ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
7400ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        track.measure(widthMeasureSpec, heightMeasureSpec);
7410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
7420ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int trackWidth = track.getMeasuredWidth();
7430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int thumbHalfHeight = thumb == null ? 0 : thumb.getHeight() / 2;
744fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int left = thumb.getLeft() + (thumb.getWidth() - trackWidth) / 2;
7450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int right = left + trackWidth;
746fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int top = container.top + thumbHalfHeight;
747fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int bottom = container.bottom - thumbHalfHeight;
7480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        track.layout(left, top, right, bottom);
7499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
750e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
7510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void setState(int state) {
7520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mList.removeCallbacks(mDeferHide);
753e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
7540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mAlwaysShow && state == STATE_NONE) {
7550ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            state = STATE_VISIBLE;
7569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7580ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (state == mState) {
7590ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            return;
7600ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
7619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        switch (state) {
7630ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            case STATE_NONE:
7640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                transitionToHidden();
7650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                break;
7660ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            case STATE_VISIBLE:
7670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                transitionToVisible();
7680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                break;
7690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            case STATE_DRAGGING:
7706b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette                if (transitionPreviewLayout(mCurrentSection)) {
7716b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette                    transitionToDragging();
7726b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette                } else {
7736b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette                    transitionToVisible();
7746b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette                }
7750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                break;
7760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
77720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
7780ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mState = state;
779e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
7800ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        refreshDrawablePressedState();
7810ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
782e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
7830ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void refreshDrawablePressedState() {
7840ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final boolean isPressed = mState == STATE_DRAGGING;
7850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mThumbImage.setPressed(isPressed);
7860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mTrackImage.setPressed(isPressed);
7870ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
788e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
7890ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
7900ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Shows nothing.
7910ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
7920ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void transitionToHidden() {
7930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mDecorAnimation != null) {
7940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mDecorAnimation.cancel();
7959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
7970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator fadeOut = groupAnimatorOfFloat(View.ALPHA, 0f, mThumbImage, mTrackImage,
7980ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                mPreviewImage, mPrimaryText, mSecondaryText).setDuration(DURATION_FADE_OUT);
7990ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
8000ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Push the thumb and track outside the list bounds.
8010ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float offset = mLayoutFromRight ? mThumbImage.getWidth() : -mThumbImage.getWidth();
8020ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator slideOut = groupAnimatorOfFloat(
8030ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                View.TRANSLATION_X, offset, mThumbImage, mTrackImage)
8040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                .setDuration(DURATION_FADE_OUT);
8050ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
8060ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation = new AnimatorSet();
8070ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation.playTogether(fadeOut, slideOut);
8080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation.start();
809f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette
810f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette        mShowingPreview = false;
8119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8130ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
8140ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Shows the thumb and track.
8150ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
8160ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void transitionToVisible() {
8170ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mDecorAnimation != null) {
8180ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mDecorAnimation.cancel();
8199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
8210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator fadeIn = groupAnimatorOfFloat(View.ALPHA, 1f, mThumbImage, mTrackImage)
8220ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                .setDuration(DURATION_FADE_IN);
8230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator fadeOut = groupAnimatorOfFloat(
8240ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                View.ALPHA, 0f, mPreviewImage, mPrimaryText, mSecondaryText)
8250ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                .setDuration(DURATION_FADE_OUT);
8260ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator slideIn = groupAnimatorOfFloat(
8270ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                View.TRANSLATION_X, 0f, mThumbImage, mTrackImage).setDuration(DURATION_FADE_IN);
8280ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
8290ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation = new AnimatorSet();
8300ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation.playTogether(fadeIn, fadeOut, slideIn);
8310ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation.start();
832f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette
833f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette        mShowingPreview = false;
8349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8352c6196aa557e4eaf09d1ec3ad1347742f7f9f7b5Adam Powell
8360ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
8370ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Shows the thumb, preview, and track.
8380ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
8390ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void transitionToDragging() {
8400ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mDecorAnimation != null) {
8410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mDecorAnimation.cancel();
8422c6196aa557e4eaf09d1ec3ad1347742f7f9f7b5Adam Powell        }
8430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
8440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator fadeIn = groupAnimatorOfFloat(
8450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                View.ALPHA, 1f, mThumbImage, mTrackImage, mPreviewImage)
8460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                .setDuration(DURATION_FADE_IN);
8470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator slideIn = groupAnimatorOfFloat(
8480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                View.TRANSLATION_X, 0f, mThumbImage, mTrackImage).setDuration(DURATION_FADE_IN);
8490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
8500ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation = new AnimatorSet();
8510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation.playTogether(fadeIn, slideIn);
8520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation.start();
853f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette
854f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette        mShowingPreview = true;
8552c6196aa557e4eaf09d1ec3ad1347742f7f9f7b5Adam Powell    }
8562c6196aa557e4eaf09d1ec3ad1347742f7f9f7b5Adam Powell
857a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette    private void postAutoHide() {
858a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette        mList.removeCallbacks(mDeferHide);
859a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette        mList.postDelayed(mDeferHide, FADE_TIMEOUT);
860a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette    }
861a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette
8620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    public void onScroll(int firstVisibleItem, int visibleItemCount, int totalItemCount) {
863b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        if (!isEnabled()) {
8640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            setState(STATE_NONE);
8659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
8669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
8680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final boolean hasMoreItems = totalItemCount - visibleItemCount > 0;
8690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (hasMoreItems && mState != STATE_DRAGGING) {
8700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            setThumbPos(getPosFromItemCount(firstVisibleItem, visibleItemCount, totalItemCount));
8719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
8739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mScrollCompleted = true;
8749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mFirstVisibleItem != firstVisibleItem) {
8760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mFirstVisibleItem = firstVisibleItem;
877d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
8780ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            // Show the thumb, if necessary, and set up auto-fade.
8790ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            if (mState != STATE_DRAGGING) {
8800ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                setState(STATE_VISIBLE);
881a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette                postAutoHide();
8820ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            }
883d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        }
884d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy    }
885d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
8860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void getSectionsFromIndexer() {
8879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSectionIndexer = null;
8880ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
8890ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        Adapter adapter = mList.getAdapter();
8909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (adapter instanceof HeaderViewListAdapter) {
8910ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mHeaderCount = ((HeaderViewListAdapter) adapter).getHeadersCount();
8920ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            adapter = ((HeaderViewListAdapter) adapter).getWrappedAdapter();
8939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
8959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (adapter instanceof ExpandableListConnector) {
8960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final ExpandableListAdapter expAdapter = ((ExpandableListConnector) adapter)
8970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    .getAdapter();
8989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (expAdapter instanceof SectionIndexer) {
8999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mSectionIndexer = (SectionIndexer) expAdapter;
9009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mListAdapter = (BaseAdapter) adapter;
9019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mSections = mSectionIndexer.getSections();
9029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9030ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else if (adapter instanceof SectionIndexer) {
9040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mListAdapter = (BaseAdapter) adapter;
9050ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mSectionIndexer = (SectionIndexer) adapter;
9060ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mSections = mSectionIndexer.getSections();
9079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
9080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mListAdapter = (BaseAdapter) adapter;
9090ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mSections = null;
9109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
913b1f498afc379ec84b86137e91afda6eccb8f4c0aAdam Powell    public void onSectionsChanged() {
914b1f498afc379ec84b86137e91afda6eccb8f4c0aAdam Powell        mListAdapter = null;
915b1f498afc379ec84b86137e91afda6eccb8f4c0aAdam Powell    }
916b1f498afc379ec84b86137e91afda6eccb8f4c0aAdam Powell
9170ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
9180ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Scrolls to a specific position within the section
9190ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param position
9200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
9210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void scrollTo(float position) {
9229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mScrollCompleted = false;
9230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
9240ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int count = mList.getCount();
9259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final Object[] sections = mSections;
9260ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int sectionCount = sections == null ? 0 : sections.length;
9279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int sectionIndex;
9280ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (sections != null && sectionCount > 1) {
9290ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final int exactSection = MathUtils.constrain(
9300ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    (int) (position * sectionCount), 0, sectionCount - 1);
9310ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            int targetSection = exactSection;
9320ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            int targetIndex = mSectionIndexer.getPositionForSection(targetSection);
9330ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            sectionIndex = targetSection;
9340ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
9359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Given the expected section and index, the following code will
9369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // try to account for missing sections (no names starting with..)
9379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // It will compute the scroll space of surrounding empty sections
9389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // and interpolate the currently visible letter's range across the
9399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // available space, so that there is always some list movement while
9409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // the user moves the thumb.
9419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int nextIndex = count;
9420ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            int prevIndex = targetIndex;
9430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            int prevSection = targetSection;
9440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            int nextSection = targetSection + 1;
9450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
9469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Assume the next section is unique
9470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            if (targetSection < sectionCount - 1) {
9480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                nextIndex = mSectionIndexer.getPositionForSection(targetSection + 1);
9499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
950e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
9519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Find the previous index if we're slicing the previous section
9520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            if (nextIndex == targetIndex) {
9539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Non-existent letter
9540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                while (targetSection > 0) {
9550ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    targetSection--;
9560ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    prevIndex = mSectionIndexer.getPositionForSection(targetSection);
9570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    if (prevIndex != targetIndex) {
9580ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        prevSection = targetSection;
9590ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        sectionIndex = targetSection;
960b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project                        break;
9610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    } else if (targetSection == 0) {
962b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project                        // When section reaches 0 here, sectionIndex must follow it.
963b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project                        // Assuming mSectionIndexer.getPositionForSection(0) == 0.
964b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project                        sectionIndex = 0;
965b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project                        break;
966b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project                    }
9679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
9689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
9709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Find the next index, in case the assumed next index is not
971e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette            // unique. For instance, if there is no P, then request for P's
9729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // position actually returns Q's. So we need to look ahead to make
973e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette            // sure that there is really a Q at Q's position. If not, move
9749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // further down...
9759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int nextNextSection = nextSection + 1;
9760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            while (nextNextSection < sectionCount &&
9779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mSectionIndexer.getPositionForSection(nextNextSection) == nextIndex) {
9789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                nextNextSection++;
9799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                nextSection++;
9809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9810ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
9829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Compute the beginning and ending scroll range percentage of the
9830ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            // currently visible section. This could be equal to or greater than
9840ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            // (1 / nSections). If the target position is near the previous
9850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            // position, snap to the previous position.
9860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final float prevPosition = (float) prevSection / sectionCount;
9870ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final float nextPosition = (float) nextSection / sectionCount;
9880ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final float snapThreshold = (count == 0) ? Float.MAX_VALUE : .125f / count;
9890ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            if (prevSection == exactSection && position - prevPosition < snapThreshold) {
9900ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                targetIndex = prevIndex;
9919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
9920ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                targetIndex = prevIndex + (int) ((nextIndex - prevIndex) * (position - prevPosition)
9930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    / (nextPosition - prevPosition));
9949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9950ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
9960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            // Clamp to valid positions.
9970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            targetIndex = MathUtils.constrain(targetIndex, 0, count - 1);
998e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
9999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mList instanceof ExpandableListView) {
10000ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                final ExpandableListView expList = (ExpandableListView) mList;
10019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                expList.setSelectionFromTop(expList.getFlatListPosition(
10020ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        ExpandableListView.getPackedPositionForGroup(targetIndex + mHeaderCount)),
10030ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        0);
10049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (mList instanceof ListView) {
10050ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                ((ListView) mList).setSelectionFromTop(targetIndex + mHeaderCount, 0);
10069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
10070ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                mList.setSelection(targetIndex + mHeaderCount);
10089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
10100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final int index = MathUtils.constrain((int) (position * count), 0, count - 1);
10117ee1ff1450a3ff83b6b8663bd7f44dc6f30775afAdam Powell
10129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mList instanceof ExpandableListView) {
10139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ExpandableListView expList = (ExpandableListView) mList;
10149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                expList.setSelectionFromTop(expList.getFlatListPosition(
10150ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        ExpandableListView.getPackedPositionForGroup(index + mHeaderCount)), 0);
10169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (mList instanceof ListView) {
10170ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                ((ListView)mList).setSelectionFromTop(index + mHeaderCount, 0);
10189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
10190ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                mList.setSelection(index + mHeaderCount);
10209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
10229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sectionIndex = -1;
10239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10256b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        if (mCurrentSection != sectionIndex) {
10266b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            mCurrentSection = sectionIndex;
10276b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette
1028f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette            final boolean hasPreview = transitionPreviewLayout(sectionIndex);
1029f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette            if (!mShowingPreview && hasPreview) {
10300ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                transitionToDragging();
1031f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette            } else if (mShowingPreview && !hasPreview) {
10326b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette                transitionToVisible();
10330ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            }
10349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10370ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
10386b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette     * Transitions the preview text to a new section. Handles animation,
10396b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette     * measurement, and layout. If the new preview text is empty, returns false.
10400ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *
10416b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette     * @param sectionIndex The section index to which the preview should
10426b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette     *            transition.
10436b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette     * @return False if the new preview text is empty.
10440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
10456b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette    private boolean transitionPreviewLayout(int sectionIndex) {
10466b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        final Object[] sections = mSections;
10476b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        String text = null;
10486b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        if (sections != null && sectionIndex >= 0 && sectionIndex < sections.length) {
10496b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            final Object section = sections[sectionIndex];
10506b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            if (section != null) {
10516b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette                text = section.toString();
10526b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            }
10536b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        }
10546b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette
10550ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Rect bounds = mTempBounds;
10568636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        final View preview = mPreviewImage;
10570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final TextView showing;
10580ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final TextView target;
10590ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mShowingPrimary) {
10600ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            showing = mPrimaryText;
10610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            target = mSecondaryText;
10620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else {
10630ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            showing = mSecondaryText;
10640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            target = mPrimaryText;
10650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
10660ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
10670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Set and layout target immediately.
10680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        target.setText(text);
10690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        measurePreview(target, bounds);
10700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        applyLayout(target, bounds);
10710ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
10720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mPreviewAnimation != null) {
10730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mPreviewAnimation.cancel();
10740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
10750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
10760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Cross-fade preview text.
10770ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator showTarget = animateAlpha(target, 1f).setDuration(DURATION_CROSS_FADE);
10780ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator hideShowing = animateAlpha(showing, 0f).setDuration(DURATION_CROSS_FADE);
10790ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        hideShowing.addListener(mSwitchPrimaryListener);
10800ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
10810ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Apply preview image padding and animate bounds, if necessary.
10828636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        bounds.left -= preview.getPaddingLeft();
10838636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        bounds.top -= preview.getPaddingTop();
10848636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        bounds.right += preview.getPaddingRight();
10858636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        bounds.bottom += preview.getPaddingBottom();
10860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator resizePreview = animateBounds(preview, bounds);
10870ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        resizePreview.setDuration(DURATION_RESIZE);
10880ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
10890ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mPreviewAnimation = new AnimatorSet();
10900ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final AnimatorSet.Builder builder = mPreviewAnimation.play(hideShowing).with(showTarget);
10910ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        builder.with(resizePreview);
10920ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
10930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // The current preview size is unaffected by hidden or showing. It's
10940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // used to set starting scales for things that need to be scaled down.
10950ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int previewWidth = preview.getWidth() - preview.getPaddingLeft()
10960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                - preview.getPaddingRight();
10970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
10980ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // If target is too large, shrink it immediately to fit and expand to
10990ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // target size. Otherwise, start at target size.
11000ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int targetWidth = target.getWidth();
11010ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (targetWidth > previewWidth) {
11020ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            target.setScaleX((float) previewWidth / targetWidth);
11030ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final Animator scaleAnim = animateScaleX(target, 1f).setDuration(DURATION_RESIZE);
11040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            builder.with(scaleAnim);
11050ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else {
11060ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            target.setScaleX(1f);
11070ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
11080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
11090ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // If showing is larger than target, shrink to target size.
11100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int showingWidth = showing.getWidth();
11110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (showingWidth > targetWidth) {
11120ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final float scale = (float) targetWidth / showingWidth;
11130ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final Animator scaleAnim = animateScaleX(showing, scale).setDuration(DURATION_RESIZE);
11140ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            builder.with(scaleAnim);
11150ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
11160ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
11170ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mPreviewAnimation.start();
11186b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette
1119f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette        return !TextUtils.isEmpty(text);
11200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
11210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
11220ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
11230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Positions the thumb and preview widgets.
11240ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *
11250ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param position The position, between 0 and 1, along the track at which
11260ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *            to place the thumb.
11270ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
11280ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void setThumbPos(float position) {
1129fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final Rect container = mContainerRect;
1130fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int top = container.top;
1131fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int bottom = container.bottom;
1132fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette
11338636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        final View trackImage = mTrackImage;
11348636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        final View thumbImage = mThumbImage;
1135fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final float min = trackImage.getTop();
1136fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final float max = trackImage.getBottom();
11370ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float offset = min;
11380ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float range = max - min;
11390ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float thumbMiddle = position * range + offset;
1140fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        thumbImage.setTranslationY(thumbMiddle - thumbImage.getHeight() / 2);
11410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
11425db566f16a1b8f36e84a9be00cde40482d48466bAdam Powell        final float previewPos = mOverlayPosition == OVERLAY_AT_THUMB ? thumbMiddle : 0;
11435db566f16a1b8f36e84a9be00cde40482d48466bAdam Powell
11440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Center the preview on the thumb, constrained to the list bounds.
11458636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        final View previewImage = mPreviewImage;
1146fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final float previewHalfHeight = previewImage.getHeight() / 2f;
11470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float minP = top + previewHalfHeight;
11480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float maxP = bottom - previewHalfHeight;
11495db566f16a1b8f36e84a9be00cde40482d48466bAdam Powell        final float previewMiddle = MathUtils.constrain(previewPos, minP, maxP);
11500ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float previewTop = previewMiddle - previewHalfHeight;
1151fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        previewImage.setTranslationY(previewTop);
11520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
11530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mPrimaryText.setTranslationY(previewTop);
11540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mSecondaryText.setTranslationY(previewTop);
11550ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
11560ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
11570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private float getPosFromMotionEvent(float y) {
11588636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        final View trackImage = mTrackImage;
1159fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final float min = trackImage.getTop();
1160fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final float max = trackImage.getBottom();
11610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float offset = min;
11620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float range = max - min;
11630ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
11640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // If the list is the same height as the thumbnail or shorter,
11650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // effectively disable scrolling.
11660ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (range <= 0) {
11670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            return 0f;
11680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
11690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
11700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        return MathUtils.constrain((y - offset) / range, 0f, 1f);
11710ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
11720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
11730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private float getPosFromItemCount(
11740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            int firstVisibleItem, int visibleItemCount, int totalItemCount) {
117535948b7ed502d3f126acc8f2d61e099fa5deb0c7Adam Powell        if (mSectionIndexer == null || mListAdapter == null) {
117632c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell            getSectionsFromIndexer();
117732c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell        }
11780ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
11790ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final boolean hasSections = mSectionIndexer != null && mSections != null
11800ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                && mSections.length > 0;
11810ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (!hasSections || !mMatchDragPosition) {
11826b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            return (float) firstVisibleItem / (totalItemCount - visibleItemCount);
118332c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell        }
118432c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell
11856b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        // Ignore headers.
11860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        firstVisibleItem -= mHeaderCount;
118732c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell        if (firstVisibleItem < 0) {
118832c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell            return 0;
118932c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell        }
11900ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        totalItemCount -= mHeaderCount;
119132c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell
11926b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        // Hidden portion of the first visible row.
11936b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        final View child = mList.getChildAt(0);
11946b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        final float incrementalPos;
11956b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        if (child == null || child.getHeight() == 0) {
11966b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            incrementalPos = 0;
11976b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        } else {
11986b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            incrementalPos = (float) (mList.getPaddingTop() - child.getTop()) / child.getHeight();
11996b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        }
12006b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette
12016b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        // Number of rows in this section.
120232c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell        final int section = mSectionIndexer.getSectionForPosition(firstVisibleItem);
120332c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell        final int sectionPos = mSectionIndexer.getPositionForSection(section);
1204f49971ec06e65ee2a0f0fd48a615b89c677086e7Adam Powell        final int sectionCount = mSections.length;
12056b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        final int positionsInSection;
12066b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        if (section < sectionCount - 1) {
1207414b02380d73a4ced03b1b51233ce9b339fb52cfJean-Baptiste Queru            final int nextSectionPos;
1208414b02380d73a4ced03b1b51233ce9b339fb52cfJean-Baptiste Queru            if (section + 1 < sectionCount) {
1209414b02380d73a4ced03b1b51233ce9b339fb52cfJean-Baptiste Queru                nextSectionPos = mSectionIndexer.getPositionForSection(section + 1);
1210414b02380d73a4ced03b1b51233ce9b339fb52cfJean-Baptiste Queru            } else {
1211414b02380d73a4ced03b1b51233ce9b339fb52cfJean-Baptiste Queru                nextSectionPos = totalItemCount - 1;
1212414b02380d73a4ced03b1b51233ce9b339fb52cfJean-Baptiste Queru            }
12136b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            positionsInSection = nextSectionPos - sectionPos;
12146b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        } else {
12156b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            positionsInSection = totalItemCount - sectionPos;
12166b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        }
12176b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette
12186b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        // Position within this section.
12196b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        final float posWithinSection;
12206b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        if (positionsInSection == 0) {
12216b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            posWithinSection = 0;
12226b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        } else {
12236b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            posWithinSection = (firstVisibleItem + incrementalPos - sectionPos)
12246b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette                    / positionsInSection;
12256b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        }
122632c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell
1227f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette        float result = (section + posWithinSection) / sectionCount;
1228f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette
1229f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette        // Fake out the scroll bar for the last item. Since the section indexer
1230f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette        // won't ever actually move the list in this end space, make scrolling
1231f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette        // across the last item account for whatever space is remaining.
1232f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette        if (firstVisibleItem > 0 && firstVisibleItem + visibleItemCount == totalItemCount) {
1233f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette            final View lastChild = mList.getChildAt(visibleItemCount - 1);
1234f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette            final float lastItemVisible = (float) (mList.getHeight() - mList.getPaddingBottom()
1235f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette                    - lastChild.getTop()) / lastChild.getHeight();
1236f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette            result += (1 - result) * lastItemVisible;
1237f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette        }
1238f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette
1239f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette        return result;
124032c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell    }
124132c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell
12420ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
12430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Cancels an ongoing fling event by injecting a
12440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * {@link MotionEvent#ACTION_CANCEL} into the host view.
12450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
12469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void cancelFling() {
12470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final MotionEvent cancelFling = MotionEvent.obtain(
12480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                0, 0, MotionEvent.ACTION_CANCEL, 0, 0, 0);
12499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mList.onTouchEvent(cancelFling);
12509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        cancelFling.recycle();
12519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1252e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
12530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
12540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Cancels a pending drag.
12550ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *
12560ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @see #startPendingDrag()
12570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
12580ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void cancelPendingDrag() {
12594cfeedb6aea057606cbc461fd1019f71b8aca843Alan Viverette        mPendingDrag = -1;
1260af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell    }
1261af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
12620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
12630ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Delays dragging until after the framework has determined that the user is
12640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * scrolling, rather than tapping.
12650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
12660ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void startPendingDrag() {
12674cfeedb6aea057606cbc461fd1019f71b8aca843Alan Viverette        mPendingDrag = SystemClock.uptimeMillis() + TAP_TIMEOUT;
1268af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell    }
1269af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
12700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void beginDrag() {
12714cfeedb6aea057606cbc461fd1019f71b8aca843Alan Viverette        mPendingDrag = -1;
12724cfeedb6aea057606cbc461fd1019f71b8aca843Alan Viverette
1273af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        setState(STATE_DRAGGING);
12740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1275af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        if (mListAdapter == null && mList != null) {
1276af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell            getSectionsFromIndexer();
1277af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        }
12780ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1279af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        if (mList != null) {
1280af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell            mList.requestDisallowInterceptTouchEvent(true);
1281af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell            mList.reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
1282af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        }
1283af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
1284af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        cancelFling();
1285af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell    }
1286af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
12870ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    public boolean onInterceptTouchEvent(MotionEvent ev) {
1288b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        if (!isEnabled()) {
1289447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette            return false;
1290447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        }
1291447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette
1292af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        switch (ev.getActionMasked()) {
1293af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell            case MotionEvent.ACTION_DOWN:
12940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                if (isPointInside(ev.getX(), ev.getY())) {
12950ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // If the parent has requested that its children delay
12960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // pressed state (e.g. is a scrolling container) then we
12970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // need to allow the parent time to decide whether it wants
12980ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // to intercept events. If it does, we will receive a CANCEL
12990ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // event.
1300b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                    if (!mList.isInScrollingContainer()) {
1301b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                        beginDrag();
1302b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                        return true;
1303af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                    }
13040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1305b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                    mInitialTouchY = ev.getY();
1306b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                    startPendingDrag();
1307b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                }
1308b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                break;
1309b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette            case MotionEvent.ACTION_MOVE:
1310b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                if (!isPointInside(ev.getX(), ev.getY())) {
1311b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                    cancelPendingDrag();
13124cfeedb6aea057606cbc461fd1019f71b8aca843Alan Viverette                } else if (mPendingDrag >= 0 && mPendingDrag <= SystemClock.uptimeMillis()) {
13134cfeedb6aea057606cbc461fd1019f71b8aca843Alan Viverette                    beginDrag();
13144cfeedb6aea057606cbc461fd1019f71b8aca843Alan Viverette
13154cfeedb6aea057606cbc461fd1019f71b8aca843Alan Viverette                    final float pos = getPosFromMotionEvent(mInitialTouchY);
13164cfeedb6aea057606cbc461fd1019f71b8aca843Alan Viverette                    scrollTo(pos);
13174cfeedb6aea057606cbc461fd1019f71b8aca843Alan Viverette
13184cfeedb6aea057606cbc461fd1019f71b8aca843Alan Viverette                    return onTouchEvent(ev);
1319af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                }
1320af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                break;
1321af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell            case MotionEvent.ACTION_UP:
1322af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell            case MotionEvent.ACTION_CANCEL:
1323af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                cancelPendingDrag();
1324af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                break;
13259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13260ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
13279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return false;
13289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1330a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette    public boolean onInterceptHoverEvent(MotionEvent ev) {
1331b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        if (!isEnabled()) {
1332a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette            return false;
1333a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette        }
1334a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette
1335a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette        final int actionMasked = ev.getActionMasked();
1336a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette        if ((actionMasked == MotionEvent.ACTION_HOVER_ENTER
1337a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette                || actionMasked == MotionEvent.ACTION_HOVER_MOVE) && mState == STATE_NONE
1338a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette                && isPointInside(ev.getX(), ev.getY())) {
1339a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette            setState(STATE_VISIBLE);
1340a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette            postAutoHide();
1341a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette        }
1342a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette
1343a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette        return false;
1344a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette    }
1345a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette
13460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    public boolean onTouchEvent(MotionEvent me) {
1347b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        if (!isEnabled()) {
1348447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette            return false;
1349447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        }
1350447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette
13510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        switch (me.getActionMasked()) {
13520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            case MotionEvent.ACTION_UP: {
13534cfeedb6aea057606cbc461fd1019f71b8aca843Alan Viverette                if (mPendingDrag >= 0) {
13540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // Allow a tap to scroll.
13550ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    beginDrag();
13569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    final float pos = getPosFromMotionEvent(me.getY());
13580ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    setThumbPos(pos);
13590ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    scrollTo(pos);
13600ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
13610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // Will hit the STATE_DRAGGING check below
1362d43fe07852a23c78da3b51b45469687c20be919aMarco Nelissen                }
13630ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
13640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                if (mState == STATE_DRAGGING) {
13650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    if (mList != null) {
13660ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        // ViewGroup does the right thing already, but there might
13670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        // be other classes that don't properly reset on touch-up,
13680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        // so do this explicitly just in case.
13690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        mList.requestDisallowInterceptTouchEvent(false);
13700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        mList.reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
13710ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    }
13720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
13730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    setState(STATE_VISIBLE);
1374a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette                    postAutoHide();
13750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
13760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    return true;
137720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell                }
13780ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            } break;
137920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
13800ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            case MotionEvent.ACTION_MOVE: {
13814cfeedb6aea057606cbc461fd1019f71b8aca843Alan Viverette                if (mPendingDrag >= 0 && Math.abs(me.getY() - mInitialTouchY) > mScaledTouchSlop) {
13824cfeedb6aea057606cbc461fd1019f71b8aca843Alan Viverette                    beginDrag();
1383af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
1384af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                    // Will hit the STATE_DRAGGING check below
1385af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                }
13860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
13870ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                if (mState == STATE_DRAGGING) {
13880ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // TODO: Ignore jitter.
13890ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    final float pos = getPosFromMotionEvent(me.getY());
13900ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    setThumbPos(pos);
13910ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
13920ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // If the previous scrollTo is still pending
13930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    if (mScrollCompleted) {
13940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        scrollTo(pos);
13950ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    }
13960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
13979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return true;
13989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
13990ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            } break;
14000ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
14010ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            case MotionEvent.ACTION_CANCEL: {
14020ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                cancelPendingDrag();
14030ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            } break;
14049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14050ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
14069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return false;
14079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1408d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
14090ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
14100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Returns whether a coordinate is inside the scroller's activation area. If
14110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * there is a track image, touching anywhere within the thumb-width of the
14120ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * track activates scrolling. Otherwise, the user has to touch inside thumb
14130ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * itself.
14140ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *
14150ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param x The x-coordinate.
14160ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param y The y-coordinate.
14170ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @return Whether the coordinate is inside the scroller's activation area.
14180ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
14190ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private boolean isPointInside(float x, float y) {
14208636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        return isPointInsideX(x) && (mTrackDrawable != null || isPointInsideY(y));
14210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
14220ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
14230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private boolean isPointInsideX(float x) {
14240ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mLayoutFromRight) {
14250ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            return x >= mThumbImage.getLeft();
14260ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else {
14270ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            return x <= mThumbImage.getRight();
14280ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
14290ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
14300ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
14310ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private boolean isPointInsideY(float y) {
1432d8273896843b20ac70eea06abdf7378f132dfa4bAdam Powell        final float offset = mThumbImage.getTranslationY();
1433d8273896843b20ac70eea06abdf7378f132dfa4bAdam Powell        final float top = mThumbImage.getTop() + offset;
1434d8273896843b20ac70eea06abdf7378f132dfa4bAdam Powell        final float bottom = mThumbImage.getBottom() + offset;
1435d8273896843b20ac70eea06abdf7378f132dfa4bAdam Powell        return y >= top && y <= bottom;
14360ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
14370ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
14380ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
14390ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Constructs an animator for the specified property on a group of views.
14400ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * See {@link ObjectAnimator#ofFloat(Object, String, float...)} for
14410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * implementation details.
14420ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *
14430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param property The property being animated.
14440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param value The value to which that property should animate.
14450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param views The target views to animate.
14460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @return An animator for all the specified views.
14470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
14480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static Animator groupAnimatorOfFloat(
14490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            Property<View, Float> property, float value, View... views) {
14500ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        AnimatorSet animSet = new AnimatorSet();
14510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        AnimatorSet.Builder builder = null;
14520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
14530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        for (int i = views.length - 1; i >= 0; i--) {
14540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final Animator anim = ObjectAnimator.ofFloat(views[i], property, value);
14550ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            if (builder == null) {
14560ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                builder = animSet.play(anim);
14570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            } else {
14580ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                builder.with(anim);
14590ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            }
146020232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell        }
146120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
14620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        return animSet;
146382f3495b146b267f3786997752cef25310176349Romain Guy    }
146482f3495b146b267f3786997752cef25310176349Romain Guy
14650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
14660ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Returns an animator for the view's scaleX value.
14670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
14680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static Animator animateScaleX(View v, float target) {
14690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        return ObjectAnimator.ofFloat(v, View.SCALE_X, target);
14700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
1471e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
14720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
14730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Returns an animator for the view's alpha value.
14740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
14750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static Animator animateAlpha(View v, float alpha) {
14760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        return ObjectAnimator.ofFloat(v, View.ALPHA, alpha);
14770ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
1478e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
14790ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
14800ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * A Property wrapper around the <code>left</code> functionality handled by the
14810ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * {@link View#setLeft(int)} and {@link View#getLeft()} methods.
14820ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
14830ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static Property<View, Integer> LEFT = new IntProperty<View>("left") {
14840ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
14850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public void setValue(View object, int value) {
14860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            object.setLeft(value);
14879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1488e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
14890ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
14900ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public Integer get(View object) {
14910ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            return object.getLeft();
14929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    };
1494e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
14950ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
14960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * A Property wrapper around the <code>top</code> functionality handled by the
14970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * {@link View#setTop(int)} and {@link View#getTop()} methods.
14980ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
14990ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static Property<View, Integer> TOP = new IntProperty<View>("top") {
1500e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette        @Override
15010ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public void setValue(View object, int value) {
15020ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            object.setTop(value);
15030ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
1504e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
15050ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
15060ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public Integer get(View object) {
15070ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            return object.getTop();
15080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
15090ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    };
15100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
15110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
15120ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * A Property wrapper around the <code>right</code> functionality handled by the
15130ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * {@link View#setRight(int)} and {@link View#getRight()} methods.
15140ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
15150ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static Property<View, Integer> RIGHT = new IntProperty<View>("right") {
15160ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
15170ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public void setValue(View object, int value) {
15180ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            object.setRight(value);
15190ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
15200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
15210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
15220ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public Integer get(View object) {
15230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            return object.getRight();
15249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
15250ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    };
15260ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
15270ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
15280ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * A Property wrapper around the <code>bottom</code> functionality handled by the
15290ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * {@link View#setBottom(int)} and {@link View#getBottom()} methods.
15300ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
15310ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static Property<View, Integer> BOTTOM = new IntProperty<View>("bottom") {
15320ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
15330ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public void setValue(View object, int value) {
15340ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            object.setBottom(value);
15350ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
15360ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
15370ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
15380ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public Integer get(View object) {
15390ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            return object.getBottom();
15400ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
15410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    };
15420ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
15430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
15440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Returns an animator for the view's bounds.
15450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
15460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static Animator animateBounds(View v, Rect bounds) {
15470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final PropertyValuesHolder left = PropertyValuesHolder.ofInt(LEFT, bounds.left);
15480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final PropertyValuesHolder top = PropertyValuesHolder.ofInt(TOP, bounds.top);
15490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final PropertyValuesHolder right = PropertyValuesHolder.ofInt(RIGHT, bounds.right);
15500ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final PropertyValuesHolder bottom = PropertyValuesHolder.ofInt(BOTTOM, bounds.bottom);
15510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        return ObjectAnimator.ofPropertyValuesHolder(v, left, top, right, bottom);
15529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
15539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
1554