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;
25417ee5ba89f7e0fe5efd34fce74bf1ee5923d976Tor Norbyeimport android.annotation.StyleRes;
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context;
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.res.ColorStateList;
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.res.TypedArray;
2920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powellimport android.graphics.Rect;
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.drawable.Drawable;
310ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.os.Build;
324cfeedb6aea057606cbc461fd1019f71b8aca843Alan Viveretteimport android.os.SystemClock;
33f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viveretteimport android.text.TextUtils;
340ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.text.TextUtils.TruncateAt;
350ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.util.IntProperty;
360ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.util.MathUtils;
370ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.util.Property;
387b63063625467baff1db048e84c5781408a5c86dAlan Viveretteimport android.util.TypedValue;
390ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.view.Gravity;
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.MotionEvent;
4111372fa466580bac916a330bb51ca8d274c6dca5Vladislav Kaznacheevimport android.view.PointerIcon;
4220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powellimport android.view.View;
430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.view.View.MeasureSpec;
44af5280cc71e155758c2f6625eb999cabb37e937fAdam Powellimport android.view.ViewConfiguration;
450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.view.ViewGroup.LayoutParams;
460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.view.ViewGroupOverlay;
47d43bd48fe311fa6320e46ca9b9ce25241c476ac6Adam Powellimport android.widget.AbsListView.OnScrollListener;
488636ace69640566768f89c2d92d2851ff064a2e9Alan Viveretteimport android.widget.ImageView.ScaleType;
490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
50e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viveretteimport com.android.internal.R;
51e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Helper class for AbsListView to draw and control the Fast Scroll thumb
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectclass FastScroller {
560ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Duration of fade-out animation. */
570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static final int DURATION_FADE_OUT = 300;
580ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
590ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Duration of fade-in animation. */
600ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static final int DURATION_FADE_IN = 150;
610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Duration of transition cross-fade animation. */
630ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static final int DURATION_CROSS_FADE = 50;
640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Duration of transition resize animation. */
660ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static final int DURATION_RESIZE = 100;
670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Inactivity timeout before fading controls. */
690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static final long FADE_TIMEOUT = 1500;
70e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
710ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Minimum number of pages to justify showing a fast scroll thumb. */
720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static final int MIN_PAGES = 4;
730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Scroll thumb and preview not showing. */
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int STATE_NONE = 0;
7620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
770ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Scroll thumb visible and moving along with the scrollbar. */
780ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static final int STATE_VISIBLE = 1;
790ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
800ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Scroll thumb and preview being dragged by user. */
810ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static final int STATE_DRAGGING = 2;
8220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
830ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    // Positions for preview image and text.
8420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    private static final int OVERLAY_FLOATING = 0;
8520232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    private static final int OVERLAY_AT_THUMB = 1;
86e8311ac850374c5362e8c1e48b81eb4b0211c70bAlan Viverette    private static final int OVERLAY_ABOVE_THUMB = 2;
87e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
88e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette    // Positions for thumb in relation to track.
89e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette    private static final int THUMB_POSITION_MIDPOINT = 0;
90e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette    private static final int THUMB_POSITION_INSIDE = 1;
91e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette
920ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    // Indices for mPreviewResId.
930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static final int PREVIEW_LEFT = 0;
940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static final int PREVIEW_RIGHT = 1;
950ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Delay before considering a tap in the thumb area to be a drag. */
970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static final long TAP_TIMEOUT = ViewConfiguration.getTapTimeout();
980ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
990ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final Rect mTempBounds = new Rect();
1000ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final Rect mTempMargins = new Rect();
101fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette    private final Rect mContainerRect = new Rect();
10220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
1030ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final AbsListView mList;
1040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final ViewGroupOverlay mOverlay;
1050ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final TextView mPrimaryText;
1060ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final TextView mSecondaryText;
1070ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final ImageView mThumbImage;
1080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final ImageView mTrackImage;
1098636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    private final View mPreviewImage;
1100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
1110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Preview image resource IDs for left- and right-aligned layouts. See
1120ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * {@link #PREVIEW_LEFT} and {@link #PREVIEW_RIGHT}.
1130ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
1140ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final int[] mPreviewResId = new int[2];
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1167d5bcd7990704dd89c596a85429be661607b6dbcAlan Viverette    /** The minimum touch target size in pixels. */
1177d5bcd7990704dd89c596a85429be661607b6dbcAlan Viverette    private final int mMinimumTouchTarget;
1187d5bcd7990704dd89c596a85429be661607b6dbcAlan Viverette
1190ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
1200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Padding in pixels around the preview text. Applied as layout margins to
1210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * the preview text and padding to the preview image.
1220ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
1238636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    private int mPreviewPadding;
1248636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette
1258636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    private int mPreviewMinWidth;
1268636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    private int mPreviewMinHeight;
1278636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    private int mThumbMinWidth;
1288636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    private int mThumbMinHeight;
1298636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette
1308636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    /** Theme-specified text size. Used only if text appearance is not set. */
1318636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    private float mTextSize;
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1338636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    /** Theme-specified text color. Used only if text appearance is not set. */
1348636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    private ColorStateList mTextColor;
1358636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette
1368636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    private Drawable mThumbDrawable;
1378636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    private Drawable mTrackDrawable;
1388636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    private int mTextAppearance;
139e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette    private int mThumbPosition;
140e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette
141e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette    // Used to convert between y-coordinate and thumb position within track.
142e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette    private float mThumbOffset;
143e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette    private float mThumbRange;
1440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
14526bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette    /** Total width of decorations. */
1468636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    private int mWidth;
14726bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette
1480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Set containing decoration transition animations. */
1490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private AnimatorSet mDecorAnimation;
1500ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Set containing preview text transition animations. */
1520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private AnimatorSet mPreviewAnimation;
1530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Whether the primary text is showing. */
1550ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private boolean mShowingPrimary;
1560ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Whether we're waiting for completion of scrollTo(). */
1580ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private boolean mScrollCompleted;
1590ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1600ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** The position of the first visible item in the list. */
1610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private int mFirstVisibleItem;
1620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1630ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** The number of headers at the top of the view. */
1640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private int mHeaderCount;
1650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1660ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** The index of the current section. */
1670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private int mCurrentSection = -1;
1680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
16926bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette    /** The current scrollbar position. */
17026bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette    private int mScrollbarPosition = -1;
17126bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette
1720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Whether the list is long enough to need a fast scroller. */
1734df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project    private boolean mLongList;
174e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
1750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private Object[] mSections;
176e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
177fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette    /** Whether this view is currently performing layout. */
178fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette    private boolean mUpdatingLayout;
179fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette
1800ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
1810ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Current decoration state, one of:
1820ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * <ul>
1830ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * <li>{@link #STATE_NONE}, nothing visible
1840ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * <li>{@link #STATE_VISIBLE}, showing track and thumb
1850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * <li>{@link #STATE_DRAGGING}, visible and showing preview
1860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * </ul>
1870ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mState;
189e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
190f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette    /** Whether the preview image is visible. */
191f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette    private boolean mShowingPreview;
192f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette
1934f434c7fb40b75d94e3ca2e4a905808ca34296e5Alan Viverette    private Adapter mListAdapter;
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private SectionIndexer mSectionIndexer;
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Whether decorations should be laid out from right to left. */
1970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private boolean mLayoutFromRight;
19820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
199447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    /** Whether the fast scroller is enabled. */
200447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    private boolean mEnabled;
201447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette
2020ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Whether the scrollbar and decorations should always be shown. */
20320232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    private boolean mAlwaysShow;
20420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
2050ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
2060ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Position for the preview image and text. One of:
2070ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * <ul>
2080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * <li>{@link #OVERLAY_FLOATING}
209e8311ac850374c5362e8c1e48b81eb4b0211c70bAlan Viverette     * <li>{@link #OVERLAY_AT_THUMB}
210e8311ac850374c5362e8c1e48b81eb4b0211c70bAlan Viverette     * <li>{@link #OVERLAY_ABOVE_THUMB}
2110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * </ul>
2120ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
21320232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    private int mOverlayPosition;
21420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
21526bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette    /** Current scrollbar style, including inset and overlay properties. */
21626bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette    private int mScrollBarStyle;
21726bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette
2180ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Whether to precisely match the thumb position to the list. */
219568ccd88d7efd33da0c535e07b7ea7cf476be22aAdam Powell    private boolean mMatchDragPosition;
220568ccd88d7efd33da0c535e07b7ea7cf476be22aAdam Powell
2210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private float mInitialTouchY;
2224cfeedb6aea057606cbc461fd1019f71b8aca843Alan Viverette    private long mPendingDrag = -1;
223af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell    private int mScaledTouchSlop;
224af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
2254b95cc7ef7f1364799eba3bfa8830515282dcab7Alan Viverette    private int mOldItemCount;
2264b95cc7ef7f1364799eba3bfa8830515282dcab7Alan Viverette    private int mOldChildCount;
2274b95cc7ef7f1364799eba3bfa8830515282dcab7Alan Viverette
2280ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
2290ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Used to delay hiding fast scroll decorations.
2300ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
2310ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final Runnable mDeferHide = new Runnable() {
2320ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
2330ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public void run() {
2340ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            setState(STATE_NONE);
2350ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
2360ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    };
2370ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
2380ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
2390ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Used to effect a transition from primary to secondary text.
2400ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
2410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final AnimatorListener mSwitchPrimaryListener = new AnimatorListenerAdapter() {
2420ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
2430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public void onAnimationEnd(Animator animation) {
2440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mShowingPrimary = !mShowingPrimary;
245af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        }
246af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell    };
247af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
2488636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    public FastScroller(AbsListView listView, int styleResId) {
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mList = listView;
2504b95cc7ef7f1364799eba3bfa8830515282dcab7Alan Viverette        mOldItemCount = listView.getCount();
2514b95cc7ef7f1364799eba3bfa8830515282dcab7Alan Viverette        mOldChildCount = listView.getChildCount();
2520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
25339bed695cecdbefc9fae785692fe468cd9007c35Alan Viverette        final Context context = listView.getContext();
2540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
2558636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mScrollBarStyle = listView.getScrollBarStyle();
2568636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette
2578636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mScrollCompleted = true;
2588636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mState = STATE_VISIBLE;
2598636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mMatchDragPosition =
2608636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB;
2618636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette
2628636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mTrackImage = new ImageView(context);
2638636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mTrackImage.setScaleType(ScaleType.FIT_XY);
2648636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mThumbImage = new ImageView(context);
2658636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mThumbImage.setScaleType(ScaleType.FIT_XY);
2668636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mPreviewImage = new View(context);
2678636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mPreviewImage.setAlpha(0f);
2688636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette
2698636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mPrimaryText = createPreviewTextView(context);
2708636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mSecondaryText = createPreviewTextView(context);
2718636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette
2727d5bcd7990704dd89c596a85429be661607b6dbcAlan Viverette        mMinimumTouchTarget = listView.getResources().getDimensionPixelSize(
2737d5bcd7990704dd89c596a85429be661607b6dbcAlan Viverette                com.android.internal.R.dimen.fast_scroller_minimum_touch_target);
2747d5bcd7990704dd89c596a85429be661607b6dbcAlan Viverette
2758636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        setStyle(styleResId);
2760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
2778636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        final ViewGroupOverlay overlay = listView.getOverlay();
2788636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mOverlay = overlay;
2798636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        overlay.add(mTrackImage);
2808636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        overlay.add(mThumbImage);
2818636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        overlay.add(mPreviewImage);
2828636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        overlay.add(mPrimaryText);
2838636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        overlay.add(mSecondaryText);
2840ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
2858636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        getSectionsFromIndexer();
2864b95cc7ef7f1364799eba3bfa8830515282dcab7Alan Viverette        updateLongList(mOldChildCount, mOldItemCount);
2878636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        setScrollbarPosition(listView.getVerticalScrollbarPosition());
2888636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        postAutoHide();
2898636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    }
29026bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette
2918636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    private void updateAppearance() {
29226bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette        int width = 0;
2930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
2940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Add track to overlay if it has an image.
2958636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mTrackImage.setImageDrawable(mTrackDrawable);
2968636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        if (mTrackDrawable != null) {
2978636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette            width = Math.max(width, mTrackDrawable.getIntrinsicWidth());
2980ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
2990ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
3000ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Add thumb to overlay if it has an image.
3018636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mThumbImage.setImageDrawable(mThumbDrawable);
3028636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mThumbImage.setMinimumWidth(mThumbMinWidth);
3038636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mThumbImage.setMinimumHeight(mThumbMinHeight);
3048636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        if (mThumbDrawable != null) {
3058636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette            width = Math.max(width, mThumbDrawable.getIntrinsicWidth());
3060ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
3070ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
3088636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        // Account for minimum thumb width.
3098636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        mWidth = Math.max(width, mThumbMinWidth);
3100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
3118636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        if (mTextAppearance != 0) {
312e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette            mPrimaryText.setTextAppearance(mTextAppearance);
313e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette            mSecondaryText.setTextAppearance(mTextAppearance);
3148636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        }
3150ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
3168636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        if (mTextColor != null) {
3178636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette            mPrimaryText.setTextColor(mTextColor);
3188636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette            mSecondaryText.setTextColor(mTextColor);
3198636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        }
3208636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette
3218636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        if (mTextSize > 0) {
3228636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette            mPrimaryText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);
3238636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette            mSecondaryText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);
3248636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        }
3250ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
326e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette        final int padding = mPreviewPadding;
327e8311ac850374c5362e8c1e48b81eb4b0211c70bAlan Viverette        mPrimaryText.setIncludeFontPadding(false);
328e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette        mPrimaryText.setPadding(padding, padding, padding, padding);
329e8311ac850374c5362e8c1e48b81eb4b0211c70bAlan Viverette        mSecondaryText.setIncludeFontPadding(false);
330e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette        mSecondaryText.setPadding(padding, padding, padding, padding);
3310ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
3328636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        refreshDrawablePressedState();
3338636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    }
3340ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
335417ee5ba89f7e0fe5efd34fce74bf1ee5923d976Tor Norbye    public void setStyle(@StyleRes int resId) {
3368636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        final Context context = mList.getContext();
3378636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        final TypedArray ta = context.obtainStyledAttributes(null,
338e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette                R.styleable.FastScroll, R.attr.fastScrollStyle, resId);
3398636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        final int N = ta.getIndexCount();
3408636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        for (int i = 0; i < N; i++) {
3418636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette            final int index = ta.getIndex(i);
3428636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette            switch (index) {
343e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette                case R.styleable.FastScroll_position:
3448636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    mOverlayPosition = ta.getInt(index, OVERLAY_FLOATING);
3458636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    break;
346e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette                case R.styleable.FastScroll_backgroundLeft:
3478636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    mPreviewResId[PREVIEW_LEFT] = ta.getResourceId(index, 0);
3488636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    break;
349e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette                case R.styleable.FastScroll_backgroundRight:
3508636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    mPreviewResId[PREVIEW_RIGHT] = ta.getResourceId(index, 0);
3518636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    break;
352e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette                case R.styleable.FastScroll_thumbDrawable:
3538636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    mThumbDrawable = ta.getDrawable(index);
3548636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    break;
355e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette                case R.styleable.FastScroll_trackDrawable:
3568636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    mTrackDrawable = ta.getDrawable(index);
3578636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    break;
358e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette                case R.styleable.FastScroll_textAppearance:
3598636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    mTextAppearance = ta.getResourceId(index, 0);
3608636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    break;
361e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette                case R.styleable.FastScroll_textColor:
3628636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    mTextColor = ta.getColorStateList(index);
3638636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    break;
364e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette                case R.styleable.FastScroll_textSize:
3658636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    mTextSize = ta.getDimensionPixelSize(index, 0);
3668636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    break;
367e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette                case R.styleable.FastScroll_minWidth:
3688636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    mPreviewMinWidth = ta.getDimensionPixelSize(index, 0);
3698636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    break;
370e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette                case R.styleable.FastScroll_minHeight:
3718636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    mPreviewMinHeight = ta.getDimensionPixelSize(index, 0);
3728636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    break;
373e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette                case R.styleable.FastScroll_thumbMinWidth:
3748636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    mThumbMinWidth = ta.getDimensionPixelSize(index, 0);
3758636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    break;
376e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette                case R.styleable.FastScroll_thumbMinHeight:
3778636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    mThumbMinHeight = ta.getDimensionPixelSize(index, 0);
3788636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    break;
379e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette                case R.styleable.FastScroll_padding:
3808636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    mPreviewPadding = ta.getDimensionPixelSize(index, 0);
3818636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette                    break;
382e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette                case R.styleable.FastScroll_thumbPosition:
383e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette                    mThumbPosition = ta.getInt(index, THUMB_POSITION_MIDPOINT);
384e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette                    break;
3858636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette            }
3868636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        }
38786c94cadddf4de58487825b845c38b045159114cferro_chang        ta.recycle();
3880ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
3898636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        updateAppearance();
3909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3920ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
393447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette     * Removes this FastScroller overlay from the host view.
394447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette     */
395447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    public void remove() {
396447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        mOverlay.remove(mTrackImage);
397447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        mOverlay.remove(mThumbImage);
398447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        mOverlay.remove(mPreviewImage);
399447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        mOverlay.remove(mPrimaryText);
400447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        mOverlay.remove(mSecondaryText);
401447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    }
402447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette
403447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    /**
404447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette     * @param enabled Whether the fast scroll thumb is enabled.
405447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette     */
406447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    public void setEnabled(boolean enabled) {
407b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        if (mEnabled != enabled) {
408b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette            mEnabled = enabled;
409447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette
4108ac22b0ada459d8f97787d4f12973894778f03e1Alan Viverette            onStateDependencyChanged(true);
411447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        }
412447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    }
413447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette
414447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    /**
415447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette     * @return Whether the fast scroll thumb is enabled.
416447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette     */
417447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    public boolean isEnabled() {
418b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        return mEnabled && (mLongList || mAlwaysShow);
419447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    }
420447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette
421447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    /**
4220ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param alwaysShow Whether the fast scroll thumb should always be shown
4230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
42420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    public void setAlwaysShow(boolean alwaysShow) {
425b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        if (mAlwaysShow != alwaysShow) {
426b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette            mAlwaysShow = alwaysShow;
4270ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
4288ac22b0ada459d8f97787d4f12973894778f03e1Alan Viverette            onStateDependencyChanged(false);
42920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell        }
43020232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    }
43120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
4320ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
4330ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @return Whether the fast scroll thumb will always be shown
4340ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @see #setAlwaysShow(boolean)
4350ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
43620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    public boolean isAlwaysShowEnabled() {
43720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell        return mAlwaysShow;
43820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    }
43920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
440b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette    /**
441b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette     * Called when one of the variables affecting enabled state changes.
4428ac22b0ada459d8f97787d4f12973894778f03e1Alan Viverette     *
4438ac22b0ada459d8f97787d4f12973894778f03e1Alan Viverette     * @param peekIfEnabled whether the thumb should peek, if enabled
444b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette     */
4458ac22b0ada459d8f97787d4f12973894778f03e1Alan Viverette    private void onStateDependencyChanged(boolean peekIfEnabled) {
446b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        if (isEnabled()) {
447b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette            if (isAlwaysShowEnabled()) {
448b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                setState(STATE_VISIBLE);
449b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette            } else if (mState == STATE_VISIBLE) {
450b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                postAutoHide();
4518ac22b0ada459d8f97787d4f12973894778f03e1Alan Viverette            } else if (peekIfEnabled) {
4528ac22b0ada459d8f97787d4f12973894778f03e1Alan Viverette                setState(STATE_VISIBLE);
4538ac22b0ada459d8f97787d4f12973894778f03e1Alan Viverette                postAutoHide();
454b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette            }
455b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        } else {
456b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette            stop();
457b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        }
458b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette
459b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        mList.resolvePadding();
460b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette    }
461b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette
46226bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette    public void setScrollBarStyle(int style) {
46326bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette        if (mScrollBarStyle != style) {
46426bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            mScrollBarStyle = style;
46526bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette
46626bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            updateLayout();
46726bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette        }
46826bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette    }
46926bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette
4700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
4710ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Immediately transitions the fast scroller decorations to a hidden state.
4720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
4730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    public void stop() {
4740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        setState(STATE_NONE);
4750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
47620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
47720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    public void setScrollbarPosition(int position) {
478c23ee46c8fac06b1982d115368f772fea399a4e0Fabrice Di Meglio        if (position == View.SCROLLBAR_POSITION_DEFAULT) {
479c23ee46c8fac06b1982d115368f772fea399a4e0Fabrice Di Meglio            position = mList.isLayoutRtl() ?
480c23ee46c8fac06b1982d115368f772fea399a4e0Fabrice Di Meglio                    View.SCROLLBAR_POSITION_LEFT : View.SCROLLBAR_POSITION_RIGHT;
481c23ee46c8fac06b1982d115368f772fea399a4e0Fabrice Di Meglio        }
4820ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
48326bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette        if (mScrollbarPosition != position) {
48426bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            mScrollbarPosition = position;
48526bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            mLayoutFromRight = position != View.SCROLLBAR_POSITION_LEFT;
4860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
48726bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            final int previewResId = mPreviewResId[mLayoutFromRight ? PREVIEW_RIGHT : PREVIEW_LEFT];
48826bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            mPreviewImage.setBackgroundResource(previewResId);
4890ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
490e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette            // Propagate padding to text min width/height.
491e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette            final int textMinWidth = Math.max(0, mPreviewMinWidth - mPreviewImage.getPaddingLeft()
492e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette                    - mPreviewImage.getPaddingRight());
493e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette            mPrimaryText.setMinimumWidth(textMinWidth);
494e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette            mSecondaryText.setMinimumWidth(textMinWidth);
495e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette
496e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette            final int textMinHeight = Math.max(0, mPreviewMinHeight - mPreviewImage.getPaddingTop()
497e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette                    - mPreviewImage.getPaddingBottom());
498e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette            mPrimaryText.setMinimumHeight(textMinHeight);
499e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette            mSecondaryText.setMinimumHeight(textMinHeight);
5000ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
50126bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            // Requires re-layout.
50226bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            updateLayout();
50326bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette        }
50420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    }
50520232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
50620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    public int getWidth() {
50726bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette        return mWidth;
50820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    }
50920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
5100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    public void onSizeChanged(int w, int h, int oldw, int oldh) {
5110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        updateLayout();
5120ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
5130ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
5144b95cc7ef7f1364799eba3bfa8830515282dcab7Alan Viverette    public void onItemCountChanged(int childCount, int itemCount) {
5154b95cc7ef7f1364799eba3bfa8830515282dcab7Alan Viverette        if (mOldItemCount != itemCount || mOldChildCount != childCount) {
5164b95cc7ef7f1364799eba3bfa8830515282dcab7Alan Viverette            mOldItemCount = itemCount;
5174b95cc7ef7f1364799eba3bfa8830515282dcab7Alan Viverette            mOldChildCount = childCount;
518b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette
5194b95cc7ef7f1364799eba3bfa8830515282dcab7Alan Viverette            final boolean hasMoreItems = itemCount - childCount > 0;
5204b95cc7ef7f1364799eba3bfa8830515282dcab7Alan Viverette            if (hasMoreItems && mState != STATE_DRAGGING) {
5214b95cc7ef7f1364799eba3bfa8830515282dcab7Alan Viverette                final int firstVisibleItem = mList.getFirstVisiblePosition();
5224b95cc7ef7f1364799eba3bfa8830515282dcab7Alan Viverette                setThumbPos(getPosFromItemCount(firstVisibleItem, childCount, itemCount));
5234b95cc7ef7f1364799eba3bfa8830515282dcab7Alan Viverette            }
5244b95cc7ef7f1364799eba3bfa8830515282dcab7Alan Viverette
5254b95cc7ef7f1364799eba3bfa8830515282dcab7Alan Viverette            updateLongList(childCount, itemCount);
5264b95cc7ef7f1364799eba3bfa8830515282dcab7Alan Viverette        }
527b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette    }
528b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette
5294b95cc7ef7f1364799eba3bfa8830515282dcab7Alan Viverette    private void updateLongList(int childCount, int itemCount) {
5304b95cc7ef7f1364799eba3bfa8830515282dcab7Alan Viverette        final boolean longList = childCount > 0 && itemCount / childCount >= MIN_PAGES;
531b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        if (mLongList != longList) {
532b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette            mLongList = longList;
533b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette
5348ac22b0ada459d8f97787d4f12973894778f03e1Alan Viverette            onStateDependencyChanged(false);
535b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        }
5369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
537e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
5380ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
5390ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Creates a view into which preview text can be placed.
5400ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
5418636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette    private TextView createPreviewTextView(Context context) {
5420ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final LayoutParams params = new LayoutParams(
5430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
5440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final TextView textView = new TextView(context);
5450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        textView.setLayoutParams(params);
5460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        textView.setSingleLine(true);
5470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        textView.setEllipsize(TruncateAt.MIDDLE);
5480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        textView.setGravity(Gravity.CENTER);
5490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        textView.setAlpha(0f);
5500ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
5510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Manually propagate inherited layout direction.
5520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        textView.setLayoutDirection(mList.getLayoutDirection());
5530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
5540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        return textView;
5559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
556e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
5570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
5580ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Measures and layouts the scrollbar and decorations.
5590ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
56026bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette    public void updateLayout() {
561fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        // Prevent re-entry when RTL properties change as a side-effect of
562fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        // resolving padding.
563fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        if (mUpdatingLayout) {
564fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette            return;
565fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        }
566fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette
567fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        mUpdatingLayout = true;
568fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette
569fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        updateContainerRect();
570fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette
5710ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        layoutThumb();
5720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        layoutTrack();
5730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
574e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette        updateOffsetAndRange();
575e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette
5760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Rect bounds = mTempBounds;
5770ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        measurePreview(mPrimaryText, bounds);
5780ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        applyLayout(mPrimaryText, bounds);
5790ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        measurePreview(mSecondaryText, bounds);
5800ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        applyLayout(mSecondaryText, bounds);
5810ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
5820ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mPreviewImage != null) {
5830ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            // Apply preview image padding.
5840ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            bounds.left -= mPreviewImage.getPaddingLeft();
5850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            bounds.top -= mPreviewImage.getPaddingTop();
5860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            bounds.right += mPreviewImage.getPaddingRight();
5870ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            bounds.bottom += mPreviewImage.getPaddingBottom();
5880ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            applyLayout(mPreviewImage, bounds);
58920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell        }
590fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette
591fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        mUpdatingLayout = false;
5929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
593e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
5940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
5950ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Layouts a view within the specified bounds and pins the pivot point to
5960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * the appropriate edge.
5970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *
5980ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param view The view to layout.
5990ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param bounds Bounds at which to layout the view.
6000ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
6010ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void applyLayout(View view, Rect bounds) {
6020ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        view.layout(bounds.left, bounds.top, bounds.right, bounds.bottom);
6030ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        view.setPivotX(mLayoutFromRight ? bounds.right - bounds.left : 0);
6040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
6050ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
6060ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
6070ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Measures the preview text bounds, taking preview image padding into
6080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * account. This method should only be called after {@link #layoutThumb()}
6090ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * and {@link #layoutTrack()} have both been called at least once.
6100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *
6110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param v The preview text view to measure.
6120ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param out Rectangle into which measured bounds are placed.
6130ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
6140ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void measurePreview(View v, Rect out) {
6150ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Apply the preview image's padding as layout margins.
6160ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Rect margins = mTempMargins;
6170ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        margins.left = mPreviewImage.getPaddingLeft();
6180ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        margins.top = mPreviewImage.getPaddingTop();
6190ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        margins.right = mPreviewImage.getPaddingRight();
6200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        margins.bottom = mPreviewImage.getPaddingBottom();
6210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
622e8311ac850374c5362e8c1e48b81eb4b0211c70bAlan Viverette        if (mOverlayPosition == OVERLAY_FLOATING) {
6230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            measureFloating(v, margins, out);
624e8311ac850374c5362e8c1e48b81eb4b0211c70bAlan Viverette        } else {
625e8311ac850374c5362e8c1e48b81eb4b0211c70bAlan Viverette            measureViewToSide(v, mThumbImage, margins, out);
62620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell        }
6279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6290ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
6300ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Measures the bounds for a view that should be laid out against the edge
6310ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * of an adjacent view. If no adjacent view is provided, lays out against
6320ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * the list edge.
6330ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *
6340ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param view The view to measure for layout.
6350ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param adjacent (Optional) The adjacent view, may be null to align to the
6360ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *            list edge.
6370ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param margins Layout margins to apply to the view.
6380ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param out Rectangle into which measured bounds are placed.
6390ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
6400ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void measureViewToSide(View view, View adjacent, Rect margins, Rect out) {
6410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int marginLeft;
6420ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int marginTop;
6430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int marginRight;
6440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (margins == null) {
6450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginLeft = 0;
6460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginTop = 0;
6470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginRight = 0;
6480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else {
6490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginLeft = margins.left;
6500ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginTop = margins.top;
6510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginRight = margins.right;
6520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
6531ffaebc41bfd47472543d1ff86c8412c868fc4c9Amith Yamasani
654fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final Rect container = mContainerRect;
655fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int containerWidth = container.width();
6560ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int maxWidth;
6570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (adjacent == null) {
658fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette            maxWidth = containerWidth;
6590ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else if (mLayoutFromRight) {
6600ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            maxWidth = adjacent.getLeft();
6610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else {
662fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette            maxWidth = containerWidth - adjacent.getRight();
6630ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
66420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
665de14ad55f6b74a96687cd88c208352c30b67cbeeAlan Viverette        final int adjMaxHeight = Math.max(0, container.height());
666c98922ba2fa3b35e1395bd68d8525d51af4ac11fAlan Viverette        final int adjMaxWidth = Math.max(0, maxWidth - marginLeft - marginRight);
6670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(adjMaxWidth, MeasureSpec.AT_MOST);
668de14ad55f6b74a96687cd88c208352c30b67cbeeAlan Viverette        final int heightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(
669de14ad55f6b74a96687cd88c208352c30b67cbeeAlan Viverette                adjMaxHeight, MeasureSpec.UNSPECIFIED);
6700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        view.measure(widthMeasureSpec, heightMeasureSpec);
6710ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
6720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Align to the left or right.
6738636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        final int width = Math.min(adjMaxWidth, view.getMeasuredWidth());
6740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int left;
6750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int right;
6760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mLayoutFromRight) {
677fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette            right = (adjacent == null ? container.right : adjacent.getLeft()) - marginRight;
6780ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            left = right - width;
6790ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else {
680fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette            left = (adjacent == null ? container.left : adjacent.getRight()) + marginLeft;
6810ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            right = left + width;
6820ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
68320232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
6840ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Don't adjust the vertical position.
6850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int top = marginTop;
6860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int bottom = top + view.getMeasuredHeight();
6870ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        out.set(left, top, right, bottom);
6880ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
689af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
6900ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void measureFloating(View preview, Rect margins, Rect out) {
6910ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int marginLeft;
6920ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int marginTop;
6930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int marginRight;
6940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (margins == null) {
6950ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginLeft = 0;
6960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginTop = 0;
6970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginRight = 0;
6980ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else {
6990ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginLeft = margins.left;
7000ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginTop = margins.top;
7010ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginRight = margins.right;
7020ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
703568ccd88d7efd33da0c535e07b7ea7cf476be22aAdam Powell
704fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final Rect container = mContainerRect;
705fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int containerWidth = container.width();
706de14ad55f6b74a96687cd88c208352c30b67cbeeAlan Viverette        final int adjMaxHeight = Math.max(0, container.height());
707de14ad55f6b74a96687cd88c208352c30b67cbeeAlan Viverette        final int adjMaxWidth = Math.max(0, containerWidth - marginLeft - marginRight);
7080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(adjMaxWidth, MeasureSpec.AT_MOST);
709de14ad55f6b74a96687cd88c208352c30b67cbeeAlan Viverette        final int heightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(
710de14ad55f6b74a96687cd88c208352c30b67cbeeAlan Viverette                adjMaxHeight, MeasureSpec.UNSPECIFIED);
7110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        preview.measure(widthMeasureSpec, heightMeasureSpec);
7120ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
7130ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Align at the vertical center, 10% from the top.
714fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int containerHeight = container.height();
7150ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int width = preview.getMeasuredWidth();
716fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int top = containerHeight / 10 + marginTop + container.top;
7170ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int bottom = top + preview.getMeasuredHeight();
718fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int left = (containerWidth - width) / 2 + container.left;
7190ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int right = left + width;
7200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        out.set(left, top, right, bottom);
7219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
722e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
72326bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette    /**
72426bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette     * Updates the container rectangle used for layout.
72526bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette     */
726fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette    private void updateContainerRect() {
727fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final AbsListView list = mList;
72826bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette        list.resolvePadding();
72926bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette
730fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final Rect container = mContainerRect;
731fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        container.left = 0;
732fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        container.top = 0;
733fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        container.right = list.getWidth();
734fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        container.bottom = list.getHeight();
735fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette
73626bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette        final int scrollbarStyle = mScrollBarStyle;
737fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        if (scrollbarStyle == View.SCROLLBARS_INSIDE_INSET
738fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette                || scrollbarStyle == View.SCROLLBARS_INSIDE_OVERLAY) {
739fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette            container.left += list.getPaddingLeft();
740fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette            container.top += list.getPaddingTop();
741fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette            container.right -= list.getPaddingRight();
742fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette            container.bottom -= list.getPaddingBottom();
74326bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette
74426bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            // In inset mode, we need to adjust for padded scrollbar width.
74526bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            if (scrollbarStyle == View.SCROLLBARS_INSIDE_INSET) {
74626bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette                final int width = getWidth();
74726bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette                if (mScrollbarPosition == View.SCROLLBAR_POSITION_RIGHT) {
74826bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette                    container.right += width;
74926bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette                } else {
75026bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette                    container.left -= width;
75126bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette                }
75226bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            }
753fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        }
754fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette    }
755fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette
7560ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
7570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Lays out the thumb according to the current scrollbar position.
7580ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
7590ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void layoutThumb() {
7600ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Rect bounds = mTempBounds;
7610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        measureViewToSide(mThumbImage, null, null, bounds);
7620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        applyLayout(mThumbImage, bounds);
7639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
764e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
7650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
766fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette     * Lays out the track centered on the thumb. Must be called after
767fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette     * {@link #layoutThumb}.
7680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
7690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void layoutTrack() {
7700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final View track = mTrackImage;
7710ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final View thumb = mThumbImage;
772fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final Rect container = mContainerRect;
773de14ad55f6b74a96687cd88c208352c30b67cbeeAlan Viverette        final int maxWidth = Math.max(0, container.width());
774de14ad55f6b74a96687cd88c208352c30b67cbeeAlan Viverette        final int maxHeight = Math.max(0, container.height());
775a0f0494059d0648fb8176da64529ec5cabb5bf51Alan Viverette        final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.AT_MOST);
776de14ad55f6b74a96687cd88c208352c30b67cbeeAlan Viverette        final int heightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(
777de14ad55f6b74a96687cd88c208352c30b67cbeeAlan Viverette                maxHeight, MeasureSpec.UNSPECIFIED);
7780ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        track.measure(widthMeasureSpec, heightMeasureSpec);
7790ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
780e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette        final int top;
781e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette        final int bottom;
782e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette        if (mThumbPosition == THUMB_POSITION_INSIDE) {
783e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette            top = container.top;
784e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette            bottom = container.bottom;
785e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette        } else {
786e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette            final int thumbHalfHeight = thumb.getHeight() / 2;
787e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette            top = container.top + thumbHalfHeight;
788e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette            bottom = container.bottom - thumbHalfHeight;
789e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette        }
790e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette
7910ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int trackWidth = track.getMeasuredWidth();
792fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int left = thumb.getLeft() + (thumb.getWidth() - trackWidth) / 2;
7930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int right = left + trackWidth;
7940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        track.layout(left, top, right, bottom);
7959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
796e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
797e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette    /**
798e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette     * Updates the offset and range used to convert from absolute y-position to
799e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette     * thumb position within the track.
800e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette     */
801e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette    private void updateOffsetAndRange() {
802e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette        final View trackImage = mTrackImage;
803e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette        final View thumbImage = mThumbImage;
804e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette        final float min;
805e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette        final float max;
806e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette        if (mThumbPosition == THUMB_POSITION_INSIDE) {
807e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette            final float halfThumbHeight = thumbImage.getHeight() / 2f;
808e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette            min = trackImage.getTop() + halfThumbHeight;
809e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette            max = trackImage.getBottom() - halfThumbHeight;
810e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette        } else{
811e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette            min = trackImage.getTop();
812e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette            max = trackImage.getBottom();
813e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette        }
814e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette
815e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette        mThumbOffset = min;
816e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette        mThumbRange = max - min;
817e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette    }
818e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette
8190ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void setState(int state) {
8200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mList.removeCallbacks(mDeferHide);
821e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
8220ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mAlwaysShow && state == STATE_NONE) {
8230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            state = STATE_VISIBLE;
8249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8260ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (state == mState) {
8270ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            return;
8280ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
8299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8300ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        switch (state) {
8310ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            case STATE_NONE:
8320ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                transitionToHidden();
8330ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                break;
8340ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            case STATE_VISIBLE:
8350ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                transitionToVisible();
8360ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                break;
8370ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            case STATE_DRAGGING:
8386b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette                if (transitionPreviewLayout(mCurrentSection)) {
8396b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette                    transitionToDragging();
8406b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette                } else {
8416b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette                    transitionToVisible();
8426b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette                }
8430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                break;
8440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
84520232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
8460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mState = state;
847e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
8480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        refreshDrawablePressedState();
8490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
850e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
8510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void refreshDrawablePressedState() {
8520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final boolean isPressed = mState == STATE_DRAGGING;
8530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mThumbImage.setPressed(isPressed);
8540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mTrackImage.setPressed(isPressed);
8550ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
856e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
8570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
8580ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Shows nothing.
8590ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
8600ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void transitionToHidden() {
8610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mDecorAnimation != null) {
8620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mDecorAnimation.cancel();
8639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
8650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator fadeOut = groupAnimatorOfFloat(View.ALPHA, 0f, mThumbImage, mTrackImage,
8660ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                mPreviewImage, mPrimaryText, mSecondaryText).setDuration(DURATION_FADE_OUT);
8670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
8680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Push the thumb and track outside the list bounds.
8690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float offset = mLayoutFromRight ? mThumbImage.getWidth() : -mThumbImage.getWidth();
8700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator slideOut = groupAnimatorOfFloat(
8710ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                View.TRANSLATION_X, offset, mThumbImage, mTrackImage)
8720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                .setDuration(DURATION_FADE_OUT);
8730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
8740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation = new AnimatorSet();
8750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation.playTogether(fadeOut, slideOut);
8760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation.start();
877f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette
878f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette        mShowingPreview = false;
8799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8810ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
8820ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Shows the thumb and track.
8830ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
8840ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void transitionToVisible() {
8850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mDecorAnimation != null) {
8860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mDecorAnimation.cancel();
8879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8880ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
8890ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator fadeIn = groupAnimatorOfFloat(View.ALPHA, 1f, mThumbImage, mTrackImage)
8900ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                .setDuration(DURATION_FADE_IN);
8910ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator fadeOut = groupAnimatorOfFloat(
8920ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                View.ALPHA, 0f, mPreviewImage, mPrimaryText, mSecondaryText)
8930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                .setDuration(DURATION_FADE_OUT);
8940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator slideIn = groupAnimatorOfFloat(
8950ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                View.TRANSLATION_X, 0f, mThumbImage, mTrackImage).setDuration(DURATION_FADE_IN);
8960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
8970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation = new AnimatorSet();
8980ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation.playTogether(fadeIn, fadeOut, slideIn);
8990ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation.start();
900f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette
901f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette        mShowingPreview = false;
9029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9032c6196aa557e4eaf09d1ec3ad1347742f7f9f7b5Adam Powell
9040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
9050ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Shows the thumb, preview, and track.
9060ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
9070ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void transitionToDragging() {
9080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mDecorAnimation != null) {
9090ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mDecorAnimation.cancel();
9102c6196aa557e4eaf09d1ec3ad1347742f7f9f7b5Adam Powell        }
9110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
9120ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator fadeIn = groupAnimatorOfFloat(
9130ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                View.ALPHA, 1f, mThumbImage, mTrackImage, mPreviewImage)
9140ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                .setDuration(DURATION_FADE_IN);
9150ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator slideIn = groupAnimatorOfFloat(
9160ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                View.TRANSLATION_X, 0f, mThumbImage, mTrackImage).setDuration(DURATION_FADE_IN);
9170ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
9180ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation = new AnimatorSet();
9190ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation.playTogether(fadeIn, slideIn);
9200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation.start();
921f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette
922f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette        mShowingPreview = true;
9232c6196aa557e4eaf09d1ec3ad1347742f7f9f7b5Adam Powell    }
9242c6196aa557e4eaf09d1ec3ad1347742f7f9f7b5Adam Powell
925a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette    private void postAutoHide() {
926a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette        mList.removeCallbacks(mDeferHide);
927a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette        mList.postDelayed(mDeferHide, FADE_TIMEOUT);
928a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette    }
929a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette
9300ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    public void onScroll(int firstVisibleItem, int visibleItemCount, int totalItemCount) {
931b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        if (!isEnabled()) {
9320ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            setState(STATE_NONE);
9339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
9349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9350ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
9360ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final boolean hasMoreItems = totalItemCount - visibleItemCount > 0;
9370ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (hasMoreItems && mState != STATE_DRAGGING) {
9380ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            setThumbPos(getPosFromItemCount(firstVisibleItem, visibleItemCount, totalItemCount));
9399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9400ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
9419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mScrollCompleted = true;
9429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mFirstVisibleItem != firstVisibleItem) {
9440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mFirstVisibleItem = firstVisibleItem;
945d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
9460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            // Show the thumb, if necessary, and set up auto-fade.
9470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            if (mState != STATE_DRAGGING) {
9480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                setState(STATE_VISIBLE);
949a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette                postAutoHide();
9500ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            }
951d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        }
952d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy    }
953d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
9540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void getSectionsFromIndexer() {
9559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSectionIndexer = null;
9560ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
9570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        Adapter adapter = mList.getAdapter();
9589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (adapter instanceof HeaderViewListAdapter) {
9590ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mHeaderCount = ((HeaderViewListAdapter) adapter).getHeadersCount();
9600ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            adapter = ((HeaderViewListAdapter) adapter).getWrappedAdapter();
9619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
9639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (adapter instanceof ExpandableListConnector) {
9640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final ExpandableListAdapter expAdapter = ((ExpandableListConnector) adapter)
9650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    .getAdapter();
9669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (expAdapter instanceof SectionIndexer) {
9679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mSectionIndexer = (SectionIndexer) expAdapter;
9684f434c7fb40b75d94e3ca2e4a905808ca34296e5Alan Viverette                mListAdapter = adapter;
9699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mSections = mSectionIndexer.getSections();
9709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9710ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else if (adapter instanceof SectionIndexer) {
9724f434c7fb40b75d94e3ca2e4a905808ca34296e5Alan Viverette            mListAdapter = adapter;
9730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mSectionIndexer = (SectionIndexer) adapter;
9740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mSections = mSectionIndexer.getSections();
9759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
9764f434c7fb40b75d94e3ca2e4a905808ca34296e5Alan Viverette            mListAdapter = adapter;
9770ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mSections = null;
9789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
981b1f498afc379ec84b86137e91afda6eccb8f4c0aAdam Powell    public void onSectionsChanged() {
982b1f498afc379ec84b86137e91afda6eccb8f4c0aAdam Powell        mListAdapter = null;
983b1f498afc379ec84b86137e91afda6eccb8f4c0aAdam Powell    }
984b1f498afc379ec84b86137e91afda6eccb8f4c0aAdam Powell
9850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
9860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Scrolls to a specific position within the section
9870ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param position
9880ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
9890ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void scrollTo(float position) {
9909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mScrollCompleted = false;
9910ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
9920ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int count = mList.getCount();
9939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final Object[] sections = mSections;
9940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int sectionCount = sections == null ? 0 : sections.length;
9959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int sectionIndex;
9960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (sections != null && sectionCount > 1) {
9970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final int exactSection = MathUtils.constrain(
9980ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    (int) (position * sectionCount), 0, sectionCount - 1);
9990ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            int targetSection = exactSection;
10000ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            int targetIndex = mSectionIndexer.getPositionForSection(targetSection);
10010ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            sectionIndex = targetSection;
10020ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
10039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Given the expected section and index, the following code will
10049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // try to account for missing sections (no names starting with..)
10059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // It will compute the scroll space of surrounding empty sections
10069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // and interpolate the currently visible letter's range across the
10079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // available space, so that there is always some list movement while
10089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // the user moves the thumb.
10099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int nextIndex = count;
10100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            int prevIndex = targetIndex;
10110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            int prevSection = targetSection;
10120ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            int nextSection = targetSection + 1;
10130ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
10149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Assume the next section is unique
10150ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            if (targetSection < sectionCount - 1) {
10160ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                nextIndex = mSectionIndexer.getPositionForSection(targetSection + 1);
10179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1018e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
10199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Find the previous index if we're slicing the previous section
10200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            if (nextIndex == targetIndex) {
10219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Non-existent letter
10220ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                while (targetSection > 0) {
10230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    targetSection--;
10240ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    prevIndex = mSectionIndexer.getPositionForSection(targetSection);
10250ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    if (prevIndex != targetIndex) {
10260ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        prevSection = targetSection;
10270ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        sectionIndex = targetSection;
1028b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project                        break;
10290ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    } else if (targetSection == 0) {
1030b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project                        // When section reaches 0 here, sectionIndex must follow it.
1031b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project                        // Assuming mSectionIndexer.getPositionForSection(0) == 0.
1032b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project                        sectionIndex = 0;
1033b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project                        break;
1034b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project                    }
10359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
10369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10370ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
10389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Find the next index, in case the assumed next index is not
1039e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette            // unique. For instance, if there is no P, then request for P's
10409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // position actually returns Q's. So we need to look ahead to make
1041e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette            // sure that there is really a Q at Q's position. If not, move
10429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // further down...
10439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int nextNextSection = nextSection + 1;
10440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            while (nextNextSection < sectionCount &&
10459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mSectionIndexer.getPositionForSection(nextNextSection) == nextIndex) {
10469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                nextNextSection++;
10479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                nextSection++;
10489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
10509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Compute the beginning and ending scroll range percentage of the
10510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            // currently visible section. This could be equal to or greater than
10520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            // (1 / nSections). If the target position is near the previous
10530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            // position, snap to the previous position.
10540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final float prevPosition = (float) prevSection / sectionCount;
10550ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final float nextPosition = (float) nextSection / sectionCount;
10560ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final float snapThreshold = (count == 0) ? Float.MAX_VALUE : .125f / count;
10570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            if (prevSection == exactSection && position - prevPosition < snapThreshold) {
10580ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                targetIndex = prevIndex;
10599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
10600ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                targetIndex = prevIndex + (int) ((nextIndex - prevIndex) * (position - prevPosition)
10610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    / (nextPosition - prevPosition));
10629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10630ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
10640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            // Clamp to valid positions.
10650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            targetIndex = MathUtils.constrain(targetIndex, 0, count - 1);
1066e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
10679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mList instanceof ExpandableListView) {
10680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                final ExpandableListView expList = (ExpandableListView) mList;
10699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                expList.setSelectionFromTop(expList.getFlatListPosition(
10700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        ExpandableListView.getPackedPositionForGroup(targetIndex + mHeaderCount)),
10710ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        0);
10729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (mList instanceof ListView) {
10730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                ((ListView) mList).setSelectionFromTop(targetIndex + mHeaderCount, 0);
10749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
10750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                mList.setSelection(targetIndex + mHeaderCount);
10769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
10780ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final int index = MathUtils.constrain((int) (position * count), 0, count - 1);
10797ee1ff1450a3ff83b6b8663bd7f44dc6f30775afAdam Powell
10809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mList instanceof ExpandableListView) {
10819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ExpandableListView expList = (ExpandableListView) mList;
10829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                expList.setSelectionFromTop(expList.getFlatListPosition(
10830ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        ExpandableListView.getPackedPositionForGroup(index + mHeaderCount)), 0);
10849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (mList instanceof ListView) {
10850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                ((ListView)mList).setSelectionFromTop(index + mHeaderCount, 0);
10869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
10870ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                mList.setSelection(index + mHeaderCount);
10889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10890ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
10909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sectionIndex = -1;
10919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10936b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        if (mCurrentSection != sectionIndex) {
10946b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            mCurrentSection = sectionIndex;
10956b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette
1096f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette            final boolean hasPreview = transitionPreviewLayout(sectionIndex);
1097f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette            if (!mShowingPreview && hasPreview) {
10980ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                transitionToDragging();
1099f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette            } else if (mShowingPreview && !hasPreview) {
11006b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette                transitionToVisible();
11010ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            }
11029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11050ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
11066b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette     * Transitions the preview text to a new section. Handles animation,
11076b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette     * measurement, and layout. If the new preview text is empty, returns false.
11080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *
11096b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette     * @param sectionIndex The section index to which the preview should
11106b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette     *            transition.
11116b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette     * @return False if the new preview text is empty.
11120ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
11136b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette    private boolean transitionPreviewLayout(int sectionIndex) {
11146b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        final Object[] sections = mSections;
11156b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        String text = null;
11166b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        if (sections != null && sectionIndex >= 0 && sectionIndex < sections.length) {
11176b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            final Object section = sections[sectionIndex];
11186b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            if (section != null) {
11196b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette                text = section.toString();
11206b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            }
11216b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        }
11226b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette
11230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Rect bounds = mTempBounds;
11248636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        final View preview = mPreviewImage;
11250ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final TextView showing;
11260ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final TextView target;
11270ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mShowingPrimary) {
11280ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            showing = mPrimaryText;
11290ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            target = mSecondaryText;
11300ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else {
11310ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            showing = mSecondaryText;
11320ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            target = mPrimaryText;
11330ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
11340ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
11350ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Set and layout target immediately.
11360ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        target.setText(text);
11370ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        measurePreview(target, bounds);
11380ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        applyLayout(target, bounds);
11390ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
11400ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mPreviewAnimation != null) {
11410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mPreviewAnimation.cancel();
11420ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
11430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
11440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Cross-fade preview text.
11450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator showTarget = animateAlpha(target, 1f).setDuration(DURATION_CROSS_FADE);
11460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator hideShowing = animateAlpha(showing, 0f).setDuration(DURATION_CROSS_FADE);
11470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        hideShowing.addListener(mSwitchPrimaryListener);
11480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
11490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Apply preview image padding and animate bounds, if necessary.
11508636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        bounds.left -= preview.getPaddingLeft();
11518636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        bounds.top -= preview.getPaddingTop();
11528636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        bounds.right += preview.getPaddingRight();
11538636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        bounds.bottom += preview.getPaddingBottom();
11540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator resizePreview = animateBounds(preview, bounds);
11550ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        resizePreview.setDuration(DURATION_RESIZE);
11560ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
11570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mPreviewAnimation = new AnimatorSet();
11580ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final AnimatorSet.Builder builder = mPreviewAnimation.play(hideShowing).with(showTarget);
11590ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        builder.with(resizePreview);
11600ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
11610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // The current preview size is unaffected by hidden or showing. It's
11620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // used to set starting scales for things that need to be scaled down.
11630ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int previewWidth = preview.getWidth() - preview.getPaddingLeft()
11640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                - preview.getPaddingRight();
11650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
11660ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // If target is too large, shrink it immediately to fit and expand to
11670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // target size. Otherwise, start at target size.
11680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int targetWidth = target.getWidth();
11690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (targetWidth > previewWidth) {
11700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            target.setScaleX((float) previewWidth / targetWidth);
11710ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final Animator scaleAnim = animateScaleX(target, 1f).setDuration(DURATION_RESIZE);
11720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            builder.with(scaleAnim);
11730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else {
11740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            target.setScaleX(1f);
11750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
11760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
11770ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // If showing is larger than target, shrink to target size.
11780ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int showingWidth = showing.getWidth();
11790ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (showingWidth > targetWidth) {
11800ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final float scale = (float) targetWidth / showingWidth;
11810ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final Animator scaleAnim = animateScaleX(showing, scale).setDuration(DURATION_RESIZE);
11820ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            builder.with(scaleAnim);
11830ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
11840ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
11850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mPreviewAnimation.start();
11866b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette
1187f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette        return !TextUtils.isEmpty(text);
11880ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
11890ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
11900ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
11910ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Positions the thumb and preview widgets.
11920ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *
11930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param position The position, between 0 and 1, along the track at which
11940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *            to place the thumb.
11950ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
11960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void setThumbPos(float position) {
1197e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette        final float thumbMiddle = position * mThumbRange + mThumbOffset;
1198e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette        mThumbImage.setTranslationY(thumbMiddle - mThumbImage.getHeight() / 2f);
11990ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
12008636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        final View previewImage = mPreviewImage;
1201fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final float previewHalfHeight = previewImage.getHeight() / 2f;
1202e8311ac850374c5362e8c1e48b81eb4b0211c70bAlan Viverette        final float previewPos;
1203e8311ac850374c5362e8c1e48b81eb4b0211c70bAlan Viverette        switch (mOverlayPosition) {
1204e8311ac850374c5362e8c1e48b81eb4b0211c70bAlan Viverette            case OVERLAY_AT_THUMB:
1205e8311ac850374c5362e8c1e48b81eb4b0211c70bAlan Viverette                previewPos = thumbMiddle;
1206e8311ac850374c5362e8c1e48b81eb4b0211c70bAlan Viverette                break;
1207e8311ac850374c5362e8c1e48b81eb4b0211c70bAlan Viverette            case OVERLAY_ABOVE_THUMB:
1208e8311ac850374c5362e8c1e48b81eb4b0211c70bAlan Viverette                previewPos = thumbMiddle - previewHalfHeight;
1209e8311ac850374c5362e8c1e48b81eb4b0211c70bAlan Viverette                break;
1210e8311ac850374c5362e8c1e48b81eb4b0211c70bAlan Viverette            case OVERLAY_FLOATING:
1211e8311ac850374c5362e8c1e48b81eb4b0211c70bAlan Viverette            default:
1212e8311ac850374c5362e8c1e48b81eb4b0211c70bAlan Viverette                previewPos = 0;
1213e8311ac850374c5362e8c1e48b81eb4b0211c70bAlan Viverette                break;
1214e8311ac850374c5362e8c1e48b81eb4b0211c70bAlan Viverette        }
1215e8311ac850374c5362e8c1e48b81eb4b0211c70bAlan Viverette
1216e8311ac850374c5362e8c1e48b81eb4b0211c70bAlan Viverette        // Center the preview on the thumb, constrained to the list bounds.
1217e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette        final Rect container = mContainerRect;
1218e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette        final int top = container.top;
1219e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette        final int bottom = container.bottom;
12200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float minP = top + previewHalfHeight;
12210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float maxP = bottom - previewHalfHeight;
12225db566f16a1b8f36e84a9be00cde40482d48466bAdam Powell        final float previewMiddle = MathUtils.constrain(previewPos, minP, maxP);
12230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float previewTop = previewMiddle - previewHalfHeight;
1224fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        previewImage.setTranslationY(previewTop);
12250ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
12260ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mPrimaryText.setTranslationY(previewTop);
12270ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mSecondaryText.setTranslationY(previewTop);
12280ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
12290ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
12300ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private float getPosFromMotionEvent(float y) {
12310ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // If the list is the same height as the thumbnail or shorter,
12320ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // effectively disable scrolling.
1233e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette        if (mThumbRange <= 0) {
12340ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            return 0f;
12350ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
12360ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1237e5f1fff6dbf2b0093f8383523c87caeba2875a01Alan Viverette        return MathUtils.constrain((y - mThumbOffset) / mThumbRange, 0f, 1f);
12380ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
12390ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1240827015edb3037cc44c5133188684da0ec56f4027Alan Viverette    /**
1241827015edb3037cc44c5133188684da0ec56f4027Alan Viverette     * Calculates the thumb position based on the visible items.
1242827015edb3037cc44c5133188684da0ec56f4027Alan Viverette     *
1243827015edb3037cc44c5133188684da0ec56f4027Alan Viverette     * @param firstVisibleItem First visible item, >= 0.
1244827015edb3037cc44c5133188684da0ec56f4027Alan Viverette     * @param visibleItemCount Number of visible items, >= 0.
1245827015edb3037cc44c5133188684da0ec56f4027Alan Viverette     * @param totalItemCount Total number of items, >= 0.
1246827015edb3037cc44c5133188684da0ec56f4027Alan Viverette     * @return
1247827015edb3037cc44c5133188684da0ec56f4027Alan Viverette     */
12480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private float getPosFromItemCount(
12490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            int firstVisibleItem, int visibleItemCount, int totalItemCount) {
1250827015edb3037cc44c5133188684da0ec56f4027Alan Viverette        final SectionIndexer sectionIndexer = mSectionIndexer;
1251827015edb3037cc44c5133188684da0ec56f4027Alan Viverette        if (sectionIndexer == null || mListAdapter == null) {
125232c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell            getSectionsFromIndexer();
125332c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell        }
12540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1255827015edb3037cc44c5133188684da0ec56f4027Alan Viverette        if (visibleItemCount == 0 || totalItemCount == 0) {
1256827015edb3037cc44c5133188684da0ec56f4027Alan Viverette            // No items are visible.
1257827015edb3037cc44c5133188684da0ec56f4027Alan Viverette            return 0;
1258827015edb3037cc44c5133188684da0ec56f4027Alan Viverette        }
1259827015edb3037cc44c5133188684da0ec56f4027Alan Viverette
1260827015edb3037cc44c5133188684da0ec56f4027Alan Viverette        final boolean hasSections = sectionIndexer != null && mSections != null
12610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                && mSections.length > 0;
12620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (!hasSections || !mMatchDragPosition) {
1263827015edb3037cc44c5133188684da0ec56f4027Alan Viverette            if (visibleItemCount == totalItemCount) {
1264827015edb3037cc44c5133188684da0ec56f4027Alan Viverette                // All items are visible.
1265827015edb3037cc44c5133188684da0ec56f4027Alan Viverette                return 0;
1266827015edb3037cc44c5133188684da0ec56f4027Alan Viverette            } else {
1267827015edb3037cc44c5133188684da0ec56f4027Alan Viverette                return (float) firstVisibleItem / (totalItemCount - visibleItemCount);
1268827015edb3037cc44c5133188684da0ec56f4027Alan Viverette            }
126932c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell        }
1270827015edb3037cc44c5133188684da0ec56f4027Alan Viverette
12716b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        // Ignore headers.
12720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        firstVisibleItem -= mHeaderCount;
127332c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell        if (firstVisibleItem < 0) {
127432c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell            return 0;
127532c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell        }
12760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        totalItemCount -= mHeaderCount;
127732c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell
12786b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        // Hidden portion of the first visible row.
12796b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        final View child = mList.getChildAt(0);
12806b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        final float incrementalPos;
12816b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        if (child == null || child.getHeight() == 0) {
12826b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            incrementalPos = 0;
12836b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        } else {
12846b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            incrementalPos = (float) (mList.getPaddingTop() - child.getTop()) / child.getHeight();
12856b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        }
12866b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette
12876b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        // Number of rows in this section.
1288827015edb3037cc44c5133188684da0ec56f4027Alan Viverette        final int section = sectionIndexer.getSectionForPosition(firstVisibleItem);
1289827015edb3037cc44c5133188684da0ec56f4027Alan Viverette        final int sectionPos = sectionIndexer.getPositionForSection(section);
1290f49971ec06e65ee2a0f0fd48a615b89c677086e7Adam Powell        final int sectionCount = mSections.length;
12916b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        final int positionsInSection;
12926b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        if (section < sectionCount - 1) {
1293414b02380d73a4ced03b1b51233ce9b339fb52cfJean-Baptiste Queru            final int nextSectionPos;
1294414b02380d73a4ced03b1b51233ce9b339fb52cfJean-Baptiste Queru            if (section + 1 < sectionCount) {
1295827015edb3037cc44c5133188684da0ec56f4027Alan Viverette                nextSectionPos = sectionIndexer.getPositionForSection(section + 1);
1296414b02380d73a4ced03b1b51233ce9b339fb52cfJean-Baptiste Queru            } else {
1297414b02380d73a4ced03b1b51233ce9b339fb52cfJean-Baptiste Queru                nextSectionPos = totalItemCount - 1;
1298414b02380d73a4ced03b1b51233ce9b339fb52cfJean-Baptiste Queru            }
12996b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            positionsInSection = nextSectionPos - sectionPos;
13006b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        } else {
13016b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            positionsInSection = totalItemCount - sectionPos;
13026b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        }
13036b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette
13046b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        // Position within this section.
13056b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        final float posWithinSection;
13066b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        if (positionsInSection == 0) {
13076b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            posWithinSection = 0;
13086b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        } else {
13096b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            posWithinSection = (firstVisibleItem + incrementalPos - sectionPos)
13106b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette                    / positionsInSection;
13116b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        }
131232c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell
1313f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette        float result = (section + posWithinSection) / sectionCount;
1314f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette
1315f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette        // Fake out the scroll bar for the last item. Since the section indexer
1316f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette        // won't ever actually move the list in this end space, make scrolling
1317f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette        // across the last item account for whatever space is remaining.
1318f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette        if (firstVisibleItem > 0 && firstVisibleItem + visibleItemCount == totalItemCount) {
1319f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette            final View lastChild = mList.getChildAt(visibleItemCount - 1);
132040c6c555e9497bde30e5b1c1fc9026c784b29fa5Yigit Boyar            final int bottomPadding = mList.getPaddingBottom();
132140c6c555e9497bde30e5b1c1fc9026c784b29fa5Yigit Boyar            final int maxSize;
132240c6c555e9497bde30e5b1c1fc9026c784b29fa5Yigit Boyar            final int currentVisibleSize;
132340c6c555e9497bde30e5b1c1fc9026c784b29fa5Yigit Boyar            if (mList.getClipToPadding()) {
132440c6c555e9497bde30e5b1c1fc9026c784b29fa5Yigit Boyar                maxSize = lastChild.getHeight();
132540c6c555e9497bde30e5b1c1fc9026c784b29fa5Yigit Boyar                currentVisibleSize = mList.getHeight() - bottomPadding - lastChild.getTop();
132640c6c555e9497bde30e5b1c1fc9026c784b29fa5Yigit Boyar            } else {
132740c6c555e9497bde30e5b1c1fc9026c784b29fa5Yigit Boyar                maxSize = lastChild.getHeight() + bottomPadding;
132840c6c555e9497bde30e5b1c1fc9026c784b29fa5Yigit Boyar                currentVisibleSize = mList.getHeight() - lastChild.getTop();
132940c6c555e9497bde30e5b1c1fc9026c784b29fa5Yigit Boyar            }
133040c6c555e9497bde30e5b1c1fc9026c784b29fa5Yigit Boyar            if (currentVisibleSize > 0 && maxSize > 0) {
133140c6c555e9497bde30e5b1c1fc9026c784b29fa5Yigit Boyar                result += (1 - result) * ((float) currentVisibleSize / maxSize );
133240c6c555e9497bde30e5b1c1fc9026c784b29fa5Yigit Boyar            }
1333f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette        }
1334f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette
1335f9af7b250e9f12bd6955101919f3fd603d135e01Alan Viverette        return result;
133632c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell    }
133732c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell
13380ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
13390ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Cancels an ongoing fling event by injecting a
13400ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * {@link MotionEvent#ACTION_CANCEL} into the host view.
13410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
13429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void cancelFling() {
13430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final MotionEvent cancelFling = MotionEvent.obtain(
13440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                0, 0, MotionEvent.ACTION_CANCEL, 0, 0, 0);
13459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mList.onTouchEvent(cancelFling);
13469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        cancelFling.recycle();
13479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1348e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
13490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
13500ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Cancels a pending drag.
13510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *
13520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @see #startPendingDrag()
13530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
13540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void cancelPendingDrag() {
13554cfeedb6aea057606cbc461fd1019f71b8aca843Alan Viverette        mPendingDrag = -1;
1356af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell    }
1357af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
13580ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
13590ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Delays dragging until after the framework has determined that the user is
13600ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * scrolling, rather than tapping.
13610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
13620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void startPendingDrag() {
13634cfeedb6aea057606cbc461fd1019f71b8aca843Alan Viverette        mPendingDrag = SystemClock.uptimeMillis() + TAP_TIMEOUT;
1364af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell    }
1365af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
13660ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void beginDrag() {
13674cfeedb6aea057606cbc461fd1019f71b8aca843Alan Viverette        mPendingDrag = -1;
13684cfeedb6aea057606cbc461fd1019f71b8aca843Alan Viverette
1369af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        setState(STATE_DRAGGING);
13700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1371af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        if (mListAdapter == null && mList != null) {
1372af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell            getSectionsFromIndexer();
1373af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        }
13740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1375af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        if (mList != null) {
1376af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell            mList.requestDisallowInterceptTouchEvent(true);
1377af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell            mList.reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
1378af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        }
1379af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
1380af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        cancelFling();
1381af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell    }
1382af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
13830ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    public boolean onInterceptTouchEvent(MotionEvent ev) {
1384b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        if (!isEnabled()) {
1385447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette            return false;
1386447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        }
1387447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette
1388af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        switch (ev.getActionMasked()) {
1389af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell            case MotionEvent.ACTION_DOWN:
13900ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                if (isPointInside(ev.getX(), ev.getY())) {
13910ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // If the parent has requested that its children delay
13920ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // pressed state (e.g. is a scrolling container) then we
13930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // need to allow the parent time to decide whether it wants
13940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // to intercept events. If it does, we will receive a CANCEL
13950ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // event.
1396b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                    if (!mList.isInScrollingContainer()) {
1397fb99ba895e9921f46af38d5fe8c27c88676f7a65Alan Viverette                        // This will get dispatched to onTouchEvent(). Start
1398fb99ba895e9921f46af38d5fe8c27c88676f7a65Alan Viverette                        // dragging there.
1399b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                        return true;
1400af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                    }
14010ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1402b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                    mInitialTouchY = ev.getY();
1403b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                    startPendingDrag();
1404b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                }
1405b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                break;
1406b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette            case MotionEvent.ACTION_MOVE:
1407b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                if (!isPointInside(ev.getX(), ev.getY())) {
1408b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                    cancelPendingDrag();
14094cfeedb6aea057606cbc461fd1019f71b8aca843Alan Viverette                } else if (mPendingDrag >= 0 && mPendingDrag <= SystemClock.uptimeMillis()) {
14104cfeedb6aea057606cbc461fd1019f71b8aca843Alan Viverette                    beginDrag();
14114cfeedb6aea057606cbc461fd1019f71b8aca843Alan Viverette
14124cfeedb6aea057606cbc461fd1019f71b8aca843Alan Viverette                    final float pos = getPosFromMotionEvent(mInitialTouchY);
14134cfeedb6aea057606cbc461fd1019f71b8aca843Alan Viverette                    scrollTo(pos);
14144cfeedb6aea057606cbc461fd1019f71b8aca843Alan Viverette
1415fb99ba895e9921f46af38d5fe8c27c88676f7a65Alan Viverette                    // This may get dispatched to onTouchEvent(), but it
1416fb99ba895e9921f46af38d5fe8c27c88676f7a65Alan Viverette                    // doesn't really matter since we'll already be in a drag.
14174cfeedb6aea057606cbc461fd1019f71b8aca843Alan Viverette                    return onTouchEvent(ev);
1418af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                }
1419af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                break;
1420af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell            case MotionEvent.ACTION_UP:
1421af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell            case MotionEvent.ACTION_CANCEL:
1422af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                cancelPendingDrag();
1423af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                break;
14249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14250ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
14269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return false;
14279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
14289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1429a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette    public boolean onInterceptHoverEvent(MotionEvent ev) {
1430b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        if (!isEnabled()) {
1431a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette            return false;
1432a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette        }
1433a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette
1434a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette        final int actionMasked = ev.getActionMasked();
1435a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette        if ((actionMasked == MotionEvent.ACTION_HOVER_ENTER
1436a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette                || actionMasked == MotionEvent.ACTION_HOVER_MOVE) && mState == STATE_NONE
1437a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette                && isPointInside(ev.getX(), ev.getY())) {
1438a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette            setState(STATE_VISIBLE);
1439a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette            postAutoHide();
1440a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette        }
1441a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette
1442a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette        return false;
1443a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette    }
1444a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette
144511372fa466580bac916a330bb51ca8d274c6dca5Vladislav Kaznacheev    public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) {
144611372fa466580bac916a330bb51ca8d274c6dca5Vladislav Kaznacheev        if (mState == STATE_DRAGGING || isPointInside(event.getX(), event.getY())) {
144711372fa466580bac916a330bb51ca8d274c6dca5Vladislav Kaznacheev            return PointerIcon.getSystemIcon(mList.getContext(), PointerIcon.TYPE_ARROW);
144811372fa466580bac916a330bb51ca8d274c6dca5Vladislav Kaznacheev        }
144911372fa466580bac916a330bb51ca8d274c6dca5Vladislav Kaznacheev        return null;
145011372fa466580bac916a330bb51ca8d274c6dca5Vladislav Kaznacheev    }
145111372fa466580bac916a330bb51ca8d274c6dca5Vladislav Kaznacheev
14520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    public boolean onTouchEvent(MotionEvent me) {
1453b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        if (!isEnabled()) {
1454447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette            return false;
1455447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        }
1456447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette
14570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        switch (me.getActionMasked()) {
1458fb99ba895e9921f46af38d5fe8c27c88676f7a65Alan Viverette            case MotionEvent.ACTION_DOWN: {
1459fb99ba895e9921f46af38d5fe8c27c88676f7a65Alan Viverette                if (isPointInside(me.getX(), me.getY())) {
1460fb99ba895e9921f46af38d5fe8c27c88676f7a65Alan Viverette                    if (!mList.isInScrollingContainer()) {
1461fb99ba895e9921f46af38d5fe8c27c88676f7a65Alan Viverette                        beginDrag();
1462fb99ba895e9921f46af38d5fe8c27c88676f7a65Alan Viverette                        return true;
1463fb99ba895e9921f46af38d5fe8c27c88676f7a65Alan Viverette                    }
1464fb99ba895e9921f46af38d5fe8c27c88676f7a65Alan Viverette                }
1465fb99ba895e9921f46af38d5fe8c27c88676f7a65Alan Viverette            } break;
1466fb99ba895e9921f46af38d5fe8c27c88676f7a65Alan Viverette
14670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            case MotionEvent.ACTION_UP: {
14684cfeedb6aea057606cbc461fd1019f71b8aca843Alan Viverette                if (mPendingDrag >= 0) {
14690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // Allow a tap to scroll.
14700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    beginDrag();
14719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    final float pos = getPosFromMotionEvent(me.getY());
14730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    setThumbPos(pos);
14740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    scrollTo(pos);
14750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
14760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // Will hit the STATE_DRAGGING check below
1477d43fe07852a23c78da3b51b45469687c20be919aMarco Nelissen                }
14780ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
14790ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                if (mState == STATE_DRAGGING) {
14800ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    if (mList != null) {
14810ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        // ViewGroup does the right thing already, but there might
14820ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        // be other classes that don't properly reset on touch-up,
14830ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        // so do this explicitly just in case.
14840ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        mList.requestDisallowInterceptTouchEvent(false);
14850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        mList.reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
14860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    }
14870ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
14880ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    setState(STATE_VISIBLE);
1489a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette                    postAutoHide();
14900ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
14910ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    return true;
149220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell                }
14930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            } break;
149420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
14950ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            case MotionEvent.ACTION_MOVE: {
14964cfeedb6aea057606cbc461fd1019f71b8aca843Alan Viverette                if (mPendingDrag >= 0 && Math.abs(me.getY() - mInitialTouchY) > mScaledTouchSlop) {
14974cfeedb6aea057606cbc461fd1019f71b8aca843Alan Viverette                    beginDrag();
1498af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
1499af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                    // Will hit the STATE_DRAGGING check below
1500af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                }
15010ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
15020ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                if (mState == STATE_DRAGGING) {
15030ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // TODO: Ignore jitter.
15040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    final float pos = getPosFromMotionEvent(me.getY());
15050ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    setThumbPos(pos);
15060ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
15070ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // If the previous scrollTo is still pending
15080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    if (mScrollCompleted) {
15090ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        scrollTo(pos);
15100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    }
15110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
15129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return true;
15139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
15140ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            } break;
15150ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
15160ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            case MotionEvent.ACTION_CANCEL: {
15170ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                cancelPendingDrag();
15180ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            } break;
15199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
15200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
15219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return false;
15229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1523d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
15240ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
15250ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Returns whether a coordinate is inside the scroller's activation area. If
15260ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * there is a track image, touching anywhere within the thumb-width of the
15270ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * track activates scrolling. Otherwise, the user has to touch inside thumb
15280ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * itself.
15290ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *
15300ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param x The x-coordinate.
15310ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param y The y-coordinate.
15320ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @return Whether the coordinate is inside the scroller's activation area.
15330ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
15340ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private boolean isPointInside(float x, float y) {
15358636ace69640566768f89c2d92d2851ff064a2e9Alan Viverette        return isPointInsideX(x) && (mTrackDrawable != null || isPointInsideY(y));
15360ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
15370ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
15380ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private boolean isPointInsideX(float x) {
15397d5bcd7990704dd89c596a85429be661607b6dbcAlan Viverette        final float offset = mThumbImage.getTranslationX();
15407d5bcd7990704dd89c596a85429be661607b6dbcAlan Viverette        final float left = mThumbImage.getLeft() + offset;
15417d5bcd7990704dd89c596a85429be661607b6dbcAlan Viverette        final float right = mThumbImage.getRight() + offset;
15427d5bcd7990704dd89c596a85429be661607b6dbcAlan Viverette
15437d5bcd7990704dd89c596a85429be661607b6dbcAlan Viverette        // Apply the minimum touch target size.
15447d5bcd7990704dd89c596a85429be661607b6dbcAlan Viverette        final float targetSizeDiff = mMinimumTouchTarget - (right - left);
15457d5bcd7990704dd89c596a85429be661607b6dbcAlan Viverette        final float adjust = targetSizeDiff > 0 ? targetSizeDiff : 0;
15467d5bcd7990704dd89c596a85429be661607b6dbcAlan Viverette
15470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mLayoutFromRight) {
15487d5bcd7990704dd89c596a85429be661607b6dbcAlan Viverette            return x >= mThumbImage.getLeft() - adjust;
15490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else {
15507d5bcd7990704dd89c596a85429be661607b6dbcAlan Viverette            return x <= mThumbImage.getRight() + adjust;
15510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
15520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
15530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
15540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private boolean isPointInsideY(float y) {
1555d8273896843b20ac70eea06abdf7378f132dfa4bAdam Powell        final float offset = mThumbImage.getTranslationY();
1556d8273896843b20ac70eea06abdf7378f132dfa4bAdam Powell        final float top = mThumbImage.getTop() + offset;
1557d8273896843b20ac70eea06abdf7378f132dfa4bAdam Powell        final float bottom = mThumbImage.getBottom() + offset;
15587d5bcd7990704dd89c596a85429be661607b6dbcAlan Viverette
15597d5bcd7990704dd89c596a85429be661607b6dbcAlan Viverette        // Apply the minimum touch target size.
15607d5bcd7990704dd89c596a85429be661607b6dbcAlan Viverette        final float targetSizeDiff = mMinimumTouchTarget - (bottom - top);
15617d5bcd7990704dd89c596a85429be661607b6dbcAlan Viverette        final float adjust = targetSizeDiff > 0 ? targetSizeDiff / 2 : 0;
15627d5bcd7990704dd89c596a85429be661607b6dbcAlan Viverette
15637d5bcd7990704dd89c596a85429be661607b6dbcAlan Viverette        return y >= (top - adjust) && y <= (bottom + adjust);
15640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
15650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
15660ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
15670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Constructs an animator for the specified property on a group of views.
15680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * See {@link ObjectAnimator#ofFloat(Object, String, float...)} for
15690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * implementation details.
15700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *
15710ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param property The property being animated.
15720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param value The value to which that property should animate.
15730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param views The target views to animate.
15740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @return An animator for all the specified views.
15750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
15760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static Animator groupAnimatorOfFloat(
15770ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            Property<View, Float> property, float value, View... views) {
15780ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        AnimatorSet animSet = new AnimatorSet();
15790ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        AnimatorSet.Builder builder = null;
15800ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
15810ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        for (int i = views.length - 1; i >= 0; i--) {
15820ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final Animator anim = ObjectAnimator.ofFloat(views[i], property, value);
15830ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            if (builder == null) {
15840ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                builder = animSet.play(anim);
15850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            } else {
15860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                builder.with(anim);
15870ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            }
158820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell        }
158920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
15900ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        return animSet;
159182f3495b146b267f3786997752cef25310176349Romain Guy    }
159282f3495b146b267f3786997752cef25310176349Romain Guy
15930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
15940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Returns an animator for the view's scaleX value.
15950ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
15960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static Animator animateScaleX(View v, float target) {
15970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        return ObjectAnimator.ofFloat(v, View.SCALE_X, target);
15980ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
1599e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
16000ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
16010ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Returns an animator for the view's alpha value.
16020ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
16030ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static Animator animateAlpha(View v, float alpha) {
16040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        return ObjectAnimator.ofFloat(v, View.ALPHA, alpha);
16050ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
1606e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
16070ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
16080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * A Property wrapper around the <code>left</code> functionality handled by the
16090ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * {@link View#setLeft(int)} and {@link View#getLeft()} methods.
16100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
16110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static Property<View, Integer> LEFT = new IntProperty<View>("left") {
16120ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
16130ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public void setValue(View object, int value) {
16140ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            object.setLeft(value);
16159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1616e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
16170ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
16180ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public Integer get(View object) {
16190ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            return object.getLeft();
16209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    };
1622e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
16230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
16240ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * A Property wrapper around the <code>top</code> functionality handled by the
16250ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * {@link View#setTop(int)} and {@link View#getTop()} methods.
16260ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
16270ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static Property<View, Integer> TOP = new IntProperty<View>("top") {
1628e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette        @Override
16290ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public void setValue(View object, int value) {
16300ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            object.setTop(value);
16310ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
1632e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
16330ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
16340ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public Integer get(View object) {
16350ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            return object.getTop();
16360ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
16370ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    };
16380ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
16390ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
16400ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * A Property wrapper around the <code>right</code> functionality handled by the
16410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * {@link View#setRight(int)} and {@link View#getRight()} methods.
16420ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
16430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static Property<View, Integer> RIGHT = new IntProperty<View>("right") {
16440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
16450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public void setValue(View object, int value) {
16460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            object.setRight(value);
16470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
16480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
16490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
16500ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public Integer get(View object) {
16510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            return object.getRight();
16529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    };
16540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
16550ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
16560ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * A Property wrapper around the <code>bottom</code> functionality handled by the
16570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * {@link View#setBottom(int)} and {@link View#getBottom()} methods.
16580ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
16590ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static Property<View, Integer> BOTTOM = new IntProperty<View>("bottom") {
16600ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
16610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public void setValue(View object, int value) {
16620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            object.setBottom(value);
16630ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
16640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
16650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
16660ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public Integer get(View object) {
16670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            return object.getBottom();
16680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
16690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    };
16700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
16710ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
16720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Returns an animator for the view's bounds.
16730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
16740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static Animator animateBounds(View v, Rect bounds) {
16750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final PropertyValuesHolder left = PropertyValuesHolder.ofInt(LEFT, bounds.left);
16760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final PropertyValuesHolder top = PropertyValuesHolder.ofInt(TOP, bounds.top);
16770ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final PropertyValuesHolder right = PropertyValuesHolder.ofInt(RIGHT, bounds.right);
16780ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final PropertyValuesHolder bottom = PropertyValuesHolder.ofInt(BOTTOM, bounds.bottom);
16790ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        return ObjectAnimator.ofPropertyValuesHolder(v, left, top, right, bottom);
16809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
16819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
1682