FastScroller.java revision 39bed695cecdbefc9fae785692fe468cd9007c35
19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.widget;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
190ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.animation.Animator;
200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.animation.Animator.AnimatorListener;
210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.animation.AnimatorListenerAdapter;
220ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.animation.AnimatorSet;
230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.animation.ObjectAnimator;
240ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.animation.PropertyValuesHolder;
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context;
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.res.ColorStateList;
27e918a48df92fd16375c46624df74e6fd4609e016Alan Viveretteimport android.content.res.Resources;
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;
320ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.text.TextUtils.TruncateAt;
330ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.util.IntProperty;
340ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.util.MathUtils;
350ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.util.Property;
360ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.view.Gravity;
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.MotionEvent;
3820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powellimport android.view.View;
390ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.view.View.MeasureSpec;
40af5280cc71e155758c2f6625eb999cabb37e937fAdam Powellimport android.view.ViewConfiguration;
410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.view.ViewGroup.LayoutParams;
420ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport android.view.ViewGroupOverlay;
43d43bd48fe311fa6320e46ca9b9ce25241c476ac6Adam Powellimport android.widget.AbsListView.OnScrollListener;
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viveretteimport com.android.internal.R;
460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Helper class for AbsListView to draw and control the Fast Scroll thumb
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectclass FastScroller {
510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Duration of fade-out animation. */
520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static final int DURATION_FADE_OUT = 300;
530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Duration of fade-in animation. */
550ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static final int DURATION_FADE_IN = 150;
560ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Duration of transition cross-fade animation. */
580ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static final int DURATION_CROSS_FADE = 50;
590ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
600ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Duration of transition resize animation. */
610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static final int DURATION_RESIZE = 100;
620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
630ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Inactivity timeout before fading controls. */
640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static final long FADE_TIMEOUT = 1500;
65e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
660ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Minimum number of pages to justify showing a fast scroll thumb. */
670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static final int MIN_PAGES = 4;
680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Scroll thumb and preview not showing. */
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int STATE_NONE = 0;
7120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Scroll thumb visible and moving along with the scrollbar. */
730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static final int STATE_VISIBLE = 1;
740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Scroll thumb and preview being dragged by user. */
760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static final int STATE_DRAGGING = 2;
7720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
780ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Styleable attributes. */
7920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    private static final int[] ATTRS = new int[] {
80b2e551711aa93a3d296ced6368e895743d43da80Adam Powell        android.R.attr.fastScrollTextColor,
81128b6ba93d0549fd2beff4482678e1229dc1cf3dAdam Powell        android.R.attr.fastScrollThumbDrawable,
82128b6ba93d0549fd2beff4482678e1229dc1cf3dAdam Powell        android.R.attr.fastScrollTrackDrawable,
83128b6ba93d0549fd2beff4482678e1229dc1cf3dAdam Powell        android.R.attr.fastScrollPreviewBackgroundLeft,
84128b6ba93d0549fd2beff4482678e1229dc1cf3dAdam Powell        android.R.attr.fastScrollPreviewBackgroundRight,
85128b6ba93d0549fd2beff4482678e1229dc1cf3dAdam Powell        android.R.attr.fastScrollOverlayPosition
8620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    };
8720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
880ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    // Styleable attribute indices.
89b2e551711aa93a3d296ced6368e895743d43da80Adam Powell    private static final int TEXT_COLOR = 0;
9020232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    private static final int THUMB_DRAWABLE = 1;
9120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    private static final int TRACK_DRAWABLE = 2;
9220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    private static final int PREVIEW_BACKGROUND_LEFT = 3;
9320232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    private static final int PREVIEW_BACKGROUND_RIGHT = 4;
9420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    private static final int OVERLAY_POSITION = 5;
9520232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    // Positions for preview image and text.
9720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    private static final int OVERLAY_FLOATING = 0;
9820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    private static final int OVERLAY_AT_THUMB = 1;
99e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
1000ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    // Indices for mPreviewResId.
1010ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static final int PREVIEW_LEFT = 0;
1020ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static final int PREVIEW_RIGHT = 1;
1030ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Delay before considering a tap in the thumb area to be a drag. */
1050ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static final long TAP_TIMEOUT = ViewConfiguration.getTapTimeout();
1060ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1070ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final Rect mTempBounds = new Rect();
1080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final Rect mTempMargins = new Rect();
10920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
1100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final AbsListView mList;
1110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final ViewGroupOverlay mOverlay;
1120ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final TextView mPrimaryText;
1130ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final TextView mSecondaryText;
1140ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final ImageView mThumbImage;
1150ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final ImageView mTrackImage;
1160ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final ImageView mPreviewImage;
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1180ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
1190ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Preview image resource IDs for left- and right-aligned layouts. See
1200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * {@link #PREVIEW_LEFT} and {@link #PREVIEW_RIGHT}.
1210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
1220ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final int[] mPreviewResId = new int[2];
1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1240ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
1250ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Padding in pixels around the preview text. Applied as layout margins to
1260ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * the preview text and padding to the preview image.
1270ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
1280ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final int mPreviewPadding;
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1300ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Whether there is a track image to display. */
1310ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final boolean mHasTrackImage;
1320ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1330ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Set containing decoration transition animations. */
1340ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private AnimatorSet mDecorAnimation;
1350ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1360ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Set containing preview text transition animations. */
1370ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private AnimatorSet mPreviewAnimation;
1380ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1390ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Whether the primary text is showing. */
1400ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private boolean mShowingPrimary;
1410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1420ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Whether we're waiting for completion of scrollTo(). */
1430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private boolean mScrollCompleted;
1440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** The position of the first visible item in the list. */
1460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private int mFirstVisibleItem;
1470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** The number of headers at the top of the view. */
1490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private int mHeaderCount;
1500ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** The number of items in the list. */
1524df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project    private int mItemCount = -1;
1530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** The index of the current section. */
1550ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private int mCurrentSection = -1;
1560ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Whether the list is long enough to need a fast scroller. */
1584df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project    private boolean mLongList;
159e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
1600ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private Object[] mSections;
161e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
1620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
1630ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Current decoration state, one of:
1640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * <ul>
1650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * <li>{@link #STATE_NONE}, nothing visible
1660ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * <li>{@link #STATE_VISIBLE}, showing track and thumb
1670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * <li>{@link #STATE_DRAGGING}, visible and showing preview
1680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * </ul>
1690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mState;
171e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
1720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private BaseAdapter mListAdapter;
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private SectionIndexer mSectionIndexer;
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Whether decorations should be laid out from right to left. */
1760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private boolean mLayoutFromRight;
17720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
178447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    /** Whether the fast scroller is enabled. */
179447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    private boolean mEnabled;
180447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette
1810ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Whether the scrollbar and decorations should always be shown. */
18220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    private boolean mAlwaysShow;
18320232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
1840ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
1850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Position for the preview image and text. One of:
1860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * <ul>
1870ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * <li>{@link #OVERLAY_AT_THUMB}
1880ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * <li>{@link #OVERLAY_FLOATING}
1890ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * </ul>
1900ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
19120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    private int mOverlayPosition;
19220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
1930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Whether to precisely match the thumb position to the list. */
194568ccd88d7efd33da0c535e07b7ea7cf476be22aAdam Powell    private boolean mMatchDragPosition;
195568ccd88d7efd33da0c535e07b7ea7cf476be22aAdam Powell
1960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private float mInitialTouchY;
1970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private boolean mHasPendingDrag;
198af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell    private int mScaledTouchSlop;
199af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
200af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell    private final Runnable mDeferStartDrag = new Runnable() {
201e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette        @Override
202af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        public void run() {
203af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell            if (mList.mIsAttached) {
204af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                beginDrag();
205af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
2060ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                final float pos = getPosFromMotionEvent(mInitialTouchY);
2070ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                scrollTo(pos);
208af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell            }
209af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
2100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mHasPendingDrag = false;
2110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
2120ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    };
2130ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
2140ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
2150ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Used to delay hiding fast scroll decorations.
2160ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
2170ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final Runnable mDeferHide = new Runnable() {
2180ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
2190ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public void run() {
2200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            setState(STATE_NONE);
2210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
2220ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    };
2230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
2240ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
2250ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Used to effect a transition from primary to secondary text.
2260ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
2270ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final AnimatorListener mSwitchPrimaryListener = new AnimatorListenerAdapter() {
2280ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
2290ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public void onAnimationEnd(Animator animation) {
2300ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mShowingPrimary = !mShowingPrimary;
231af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        }
232af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell    };
233af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
23439bed695cecdbefc9fae785692fe468cd9007c35Alan Viverette    public FastScroller(AbsListView listView) {
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mList = listView;
2360ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mOverlay = listView.getOverlay();
2370ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
23839bed695cecdbefc9fae785692fe468cd9007c35Alan Viverette        final Context context = listView.getContext();
2390ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
2400ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
2410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Resources res = context.getResources();
2420ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final TypedArray ta = context.getTheme().obtainStyledAttributes(ATTRS);
2430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
2440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mTrackImage = new ImageView(context);
2450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
2460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Add track to overlay if it has an image.
2470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int trackResId = ta.getResourceId(TRACK_DRAWABLE, 0);
2480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (trackResId != 0) {
2490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mHasTrackImage = true;
2500ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mTrackImage.setBackgroundResource(trackResId);
2510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mOverlay.add(mTrackImage);
2520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else {
2530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mHasTrackImage = false;
2540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
2550ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
2560ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mThumbImage = new ImageView(context);
2570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
2580ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Add thumb to overlay if it has an image.
2590ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Drawable thumbDrawable = ta.getDrawable(THUMB_DRAWABLE);
2600ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (thumbDrawable != null) {
2610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mThumbImage.setImageDrawable(thumbDrawable);
2620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mOverlay.add(mThumbImage);
2630ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
2640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
2650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // If necessary, apply minimum thumb width and height.
2660ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (thumbDrawable.getIntrinsicWidth() <= 0 || thumbDrawable.getIntrinsicHeight() <= 0) {
2670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mThumbImage.setMinimumWidth(res.getDimensionPixelSize(R.dimen.fastscroll_thumb_width));
2680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mThumbImage.setMinimumHeight(
2690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    res.getDimensionPixelSize(R.dimen.fastscroll_thumb_height));
2700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
2710ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
2720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int previewSize = res.getDimensionPixelSize(R.dimen.fastscroll_overlay_size);
2730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mPreviewImage = new ImageView(context);
2740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mPreviewImage.setMinimumWidth(previewSize);
2750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mPreviewImage.setMinimumHeight(previewSize);
2760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mPreviewImage.setAlpha(0f);
2770ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mOverlay.add(mPreviewImage);
2780ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
2790ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mPreviewPadding = res.getDimensionPixelSize(R.dimen.fastscroll_overlay_padding);
2800ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
2816b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        final int textMinSize = Math.max(0, previewSize - mPreviewPadding);
2820ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mPrimaryText = createPreviewTextView(context, ta);
2836b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        mPrimaryText.setMinimumWidth(textMinSize);
2846b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        mPrimaryText.setMinimumHeight(textMinSize);
2850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mOverlay.add(mPrimaryText);
2860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mSecondaryText = createPreviewTextView(context, ta);
2876b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        mSecondaryText.setMinimumWidth(textMinSize);
2886b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        mSecondaryText.setMinimumHeight(textMinSize);
2890ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mOverlay.add(mSecondaryText);
2900ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
2910ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mPreviewResId[PREVIEW_LEFT] = ta.getResourceId(PREVIEW_BACKGROUND_LEFT, 0);
2920ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mPreviewResId[PREVIEW_RIGHT] = ta.getResourceId(PREVIEW_BACKGROUND_RIGHT, 0);
2930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mOverlayPosition = ta.getInt(OVERLAY_POSITION, OVERLAY_FLOATING);
2940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        ta.recycle();
2950ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
2960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mScrollCompleted = true;
2970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mState = STATE_VISIBLE;
2980ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mMatchDragPosition =
2990ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB;
3000ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
3010ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        getSectionsFromIndexer();
3020ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        refreshDrawablePressedState();
3030ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        setScrollbarPosition(mList.getVerticalScrollbarPosition());
304a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette        postAutoHide();
3059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3070ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
308447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette     * Removes this FastScroller overlay from the host view.
309447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette     */
310447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    public void remove() {
311447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        mOverlay.remove(mTrackImage);
312447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        mOverlay.remove(mThumbImage);
313447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        mOverlay.remove(mPreviewImage);
314447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        mOverlay.remove(mPrimaryText);
315447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        mOverlay.remove(mSecondaryText);
316447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    }
317447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette
318447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    /**
319447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette     * @param enabled Whether the fast scroll thumb is enabled.
320447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette     */
321447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    public void setEnabled(boolean enabled) {
322447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        mEnabled = enabled;
323447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette
324447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        if (enabled) {
325447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette            if (mAlwaysShow) {
326447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette                setState(STATE_VISIBLE);
327447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette            }
328447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        } else {
329447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette            stop();
330447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        }
331447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    }
332447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette
333447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    /**
334447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette     * @return Whether the fast scroll thumb is enabled.
335447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette     */
336447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    public boolean isEnabled() {
337447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        return mEnabled;
338447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    }
339447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette
340447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    /**
3410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param alwaysShow Whether the fast scroll thumb should always be shown
3420ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
34320232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    public void setAlwaysShow(boolean alwaysShow) {
34420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell        mAlwaysShow = alwaysShow;
3450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
34620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell        if (alwaysShow) {
34720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell            setState(STATE_VISIBLE);
34820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell        } else if (mState == STATE_VISIBLE) {
349a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette            postAutoHide();
35020232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell        }
35120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    }
35220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
3530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
3540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @return Whether the fast scroll thumb will always be shown
3550ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @see #setAlwaysShow(boolean)
3560ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
35720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    public boolean isAlwaysShowEnabled() {
35820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell        return mAlwaysShow;
35920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    }
36020232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
3610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
3620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Immediately transitions the fast scroller decorations to a hidden state.
3630ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
3640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    public void stop() {
3650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        setState(STATE_NONE);
3660ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
36720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
36820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    public void setScrollbarPosition(int position) {
369c23ee46c8fac06b1982d115368f772fea399a4e0Fabrice Di Meglio        if (position == View.SCROLLBAR_POSITION_DEFAULT) {
370c23ee46c8fac06b1982d115368f772fea399a4e0Fabrice Di Meglio            position = mList.isLayoutRtl() ?
371c23ee46c8fac06b1982d115368f772fea399a4e0Fabrice Di Meglio                    View.SCROLLBAR_POSITION_LEFT : View.SCROLLBAR_POSITION_RIGHT;
372c23ee46c8fac06b1982d115368f772fea399a4e0Fabrice Di Meglio        }
3730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
3740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mLayoutFromRight = position != View.SCROLLBAR_POSITION_LEFT;
3750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
3760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int previewResId = mPreviewResId[mLayoutFromRight ? PREVIEW_RIGHT : PREVIEW_LEFT];
3770ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mPreviewImage.setBackgroundResource(previewResId);
3780ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
3790ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Add extra padding for text.
3800ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Drawable background = mPreviewImage.getBackground();
3810ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (background != null) {
3820ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final Rect padding = mTempBounds;
3830ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            background.getPadding(padding);
3840ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            padding.offset(mPreviewPadding, mPreviewPadding);
3850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mPreviewImage.setPadding(padding.left, padding.top, padding.right, padding.bottom);
38620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell        }
3870ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
3880ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        updateLayout();
38920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    }
39020232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
39120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    public int getWidth() {
3920ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        return mThumbImage.getWidth();
39320232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    }
39420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
3950ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    public void onSizeChanged(int w, int h, int oldw, int oldh) {
3960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        updateLayout();
3970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
3980ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
3990ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    public void onItemCountChanged(int oldTotalItemCount, int totalItemCount) {
4000ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int visibleItemCount = mList.getChildCount();
4010ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final boolean hasMoreItems = totalItemCount - visibleItemCount > 0;
4020ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (hasMoreItems && mState != STATE_DRAGGING) {
4030ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final int firstVisibleItem = mList.getFirstVisiblePosition();
4040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            setThumbPos(getPosFromItemCount(firstVisibleItem, visibleItemCount, totalItemCount));
4059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
407e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
4080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
4090ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Creates a view into which preview text can be placed.
4100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
4110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private TextView createPreviewTextView(Context context, TypedArray ta) {
4120ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final LayoutParams params = new LayoutParams(
4130ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
4140ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Resources res = context.getResources();
4150ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int minSize = res.getDimensionPixelSize(R.dimen.fastscroll_overlay_size);
4160ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final ColorStateList textColor = ta.getColorStateList(TEXT_COLOR);
4170ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float textSize = res.getDimension(R.dimen.fastscroll_overlay_text_size);
4180ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final TextView textView = new TextView(context);
4190ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        textView.setLayoutParams(params);
4200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        textView.setTextColor(textColor);
4210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        textView.setTextSize(textSize);
4220ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        textView.setSingleLine(true);
4230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        textView.setEllipsize(TruncateAt.MIDDLE);
4240ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        textView.setGravity(Gravity.CENTER);
4250ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        textView.setAlpha(0f);
4260ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
4270ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Manually propagate inherited layout direction.
4280ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        textView.setLayoutDirection(mList.getLayoutDirection());
4290ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
4300ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        return textView;
4319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
432e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
4330ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
4340ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Measures and layouts the scrollbar and decorations.
4350ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
4360ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void updateLayout() {
4370ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        layoutThumb();
4380ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        layoutTrack();
4390ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
4400ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Rect bounds = mTempBounds;
4410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        measurePreview(mPrimaryText, bounds);
4420ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        applyLayout(mPrimaryText, bounds);
4430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        measurePreview(mSecondaryText, bounds);
4440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        applyLayout(mSecondaryText, bounds);
4450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
4460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mPreviewImage != null) {
4470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            // Apply preview image padding.
4480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            bounds.left -= mPreviewImage.getPaddingLeft();
4490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            bounds.top -= mPreviewImage.getPaddingTop();
4500ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            bounds.right += mPreviewImage.getPaddingRight();
4510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            bounds.bottom += mPreviewImage.getPaddingBottom();
4520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            applyLayout(mPreviewImage, bounds);
45320232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell        }
4549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
455e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
4560ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
4570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Layouts a view within the specified bounds and pins the pivot point to
4580ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * the appropriate edge.
4590ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *
4600ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param view The view to layout.
4610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param bounds Bounds at which to layout the view.
4620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
4630ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void applyLayout(View view, Rect bounds) {
4640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        view.layout(bounds.left, bounds.top, bounds.right, bounds.bottom);
4650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        view.setPivotX(mLayoutFromRight ? bounds.right - bounds.left : 0);
4660ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
4670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
4680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
4690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Measures the preview text bounds, taking preview image padding into
4700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * account. This method should only be called after {@link #layoutThumb()}
4710ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * and {@link #layoutTrack()} have both been called at least once.
4720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *
4730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param v The preview text view to measure.
4740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param out Rectangle into which measured bounds are placed.
4750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
4760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void measurePreview(View v, Rect out) {
4770ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Apply the preview image's padding as layout margins.
4780ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Rect margins = mTempMargins;
4790ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        margins.left = mPreviewImage.getPaddingLeft();
4800ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        margins.top = mPreviewImage.getPaddingTop();
4810ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        margins.right = mPreviewImage.getPaddingRight();
4820ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        margins.bottom = mPreviewImage.getPaddingBottom();
4830ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
4840ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mOverlayPosition == OVERLAY_AT_THUMB) {
4850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            measureViewToSide(v, mThumbImage, margins, out);
48620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell        } else {
4870ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            measureFloating(v, margins, out);
48820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell        }
4899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4910ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
4920ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Measures the bounds for a view that should be laid out against the edge
4930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * of an adjacent view. If no adjacent view is provided, lays out against
4940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * the list edge.
4950ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *
4960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param view The view to measure for layout.
4970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param adjacent (Optional) The adjacent view, may be null to align to the
4980ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *            list edge.
4990ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param margins Layout margins to apply to the view.
5000ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param out Rectangle into which measured bounds are placed.
5010ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
5020ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void measureViewToSide(View view, View adjacent, Rect margins, Rect out) {
5030ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int marginLeft;
5040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int marginTop;
5050ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int marginRight;
5060ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (margins == null) {
5070ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginLeft = 0;
5080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginTop = 0;
5090ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginRight = 0;
5100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else {
5110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginLeft = margins.left;
5120ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginTop = margins.top;
5130ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginRight = margins.right;
5140ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
5151ffaebc41bfd47472543d1ff86c8412c868fc4c9Amith Yamasani
5160ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int listWidth = mList.getWidth();
5170ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int maxWidth;
5180ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (adjacent == null) {
5190ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            maxWidth = listWidth;
5200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else if (mLayoutFromRight) {
5210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            maxWidth = adjacent.getLeft();
5220ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else {
5230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            maxWidth = listWidth - adjacent.getRight();
5240ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
52520232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
5260ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int adjMaxWidth = maxWidth - marginLeft - marginRight;
5270ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(adjMaxWidth, MeasureSpec.AT_MOST);
5280ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
5290ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        view.measure(widthMeasureSpec, heightMeasureSpec);
5300ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
5310ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Align to the left or right.
5320ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int width = view.getMeasuredWidth();
5330ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int left;
5340ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int right;
5350ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mLayoutFromRight) {
5360ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            right = (adjacent == null ? listWidth : adjacent.getLeft()) - marginRight;
5370ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            left = right - width;
5380ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else {
5390ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            left = (adjacent == null ? 0 : adjacent.getRight()) + marginLeft;
5400ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            right = left + width;
5410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
54220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
5430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Don't adjust the vertical position.
5440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int top = marginTop;
5450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int bottom = top + view.getMeasuredHeight();
5460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        out.set(left, top, right, bottom);
5470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
548af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
5490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void measureFloating(View preview, Rect margins, Rect out) {
5500ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int marginLeft;
5510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int marginTop;
5520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int marginRight;
5530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (margins == null) {
5540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginLeft = 0;
5550ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginTop = 0;
5560ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginRight = 0;
5570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else {
5580ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginLeft = margins.left;
5590ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginTop = margins.top;
5600ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginRight = margins.right;
5610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
562568ccd88d7efd33da0c535e07b7ea7cf476be22aAdam Powell
5630ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final View list = mList;
5640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int listWidth = list.getWidth();
5650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int adjMaxWidth = listWidth - marginLeft - marginRight;
5660ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(adjMaxWidth, MeasureSpec.AT_MOST);
5670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
5680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        preview.measure(widthMeasureSpec, heightMeasureSpec);
5690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
5700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Align at the vertical center, 10% from the top.
5710ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int width = preview.getMeasuredWidth();
5720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int top = list.getHeight() / 10 + marginTop;
5730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int bottom = top + preview.getMeasuredHeight();
5740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int left = (listWidth - width) / 2;
5750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int right = left + width;
5760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        out.set(left, top, right, bottom);
5779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
578e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
5790ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
5800ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Lays out the thumb according to the current scrollbar position.
5810ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
5820ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void layoutThumb() {
5830ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Rect bounds = mTempBounds;
5840ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        measureViewToSide(mThumbImage, null, null, bounds);
5850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        applyLayout(mThumbImage, bounds);
5869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
587e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
5880ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
5890ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Lays out the track centered on the thumb, if available, or against the
5900ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * edge if no thumb is available. Must be called after {@link #layoutThumb}.
5910ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
5920ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void layoutTrack() {
5930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final View track = mTrackImage;
5940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final View thumb = mThumbImage;
5950ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final View list = mList;
5960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int listWidth = list.getWidth();
5970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(listWidth, MeasureSpec.AT_MOST);
5980ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
5990ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        track.measure(widthMeasureSpec, heightMeasureSpec);
6000ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
6010ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int trackWidth = track.getMeasuredWidth();
6020ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int thumbHalfHeight = thumb == null ? 0 : thumb.getHeight() / 2;
6030ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int left = thumb == null ? listWidth - trackWidth :
6040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            thumb.getLeft() + (thumb.getWidth() - trackWidth) / 2;
6050ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int right = left + trackWidth;
6060ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int top = thumbHalfHeight;
6070ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int bottom = list.getHeight() - thumbHalfHeight;
6080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        track.layout(left, top, right, bottom);
6099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
610e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
6110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void setState(int state) {
6120ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mList.removeCallbacks(mDeferHide);
613e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
6140ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mAlwaysShow && state == STATE_NONE) {
6150ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            state = STATE_VISIBLE;
6169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6180ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (state == mState) {
6190ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            return;
6200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
6219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6220ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        switch (state) {
6230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            case STATE_NONE:
6240ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                transitionToHidden();
6250ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                break;
6260ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            case STATE_VISIBLE:
6270ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                transitionToVisible();
6280ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                break;
6290ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            case STATE_DRAGGING:
6306b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette                if (transitionPreviewLayout(mCurrentSection)) {
6316b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette                    transitionToDragging();
6326b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette                } else {
6336b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette                    transitionToVisible();
6346b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette                }
6350ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                break;
6360ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
63720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
6380ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mState = state;
639e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
6400ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        refreshDrawablePressedState();
6410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
642e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
6430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void refreshDrawablePressedState() {
6440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final boolean isPressed = mState == STATE_DRAGGING;
6450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mThumbImage.setPressed(isPressed);
6460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mTrackImage.setPressed(isPressed);
6470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
648e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
6490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
6500ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Shows nothing.
6510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
6520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void transitionToHidden() {
6530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mDecorAnimation != null) {
6540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mDecorAnimation.cancel();
6559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6560ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
6570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator fadeOut = groupAnimatorOfFloat(View.ALPHA, 0f, mThumbImage, mTrackImage,
6580ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                mPreviewImage, mPrimaryText, mSecondaryText).setDuration(DURATION_FADE_OUT);
6590ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
6600ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Push the thumb and track outside the list bounds.
6610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float offset = mLayoutFromRight ? mThumbImage.getWidth() : -mThumbImage.getWidth();
6620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator slideOut = groupAnimatorOfFloat(
6630ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                View.TRANSLATION_X, offset, mThumbImage, mTrackImage)
6640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                .setDuration(DURATION_FADE_OUT);
6650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
6660ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation = new AnimatorSet();
6670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation.playTogether(fadeOut, slideOut);
6680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation.start();
6699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6710ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
6720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Shows the thumb and track.
6730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
6740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void transitionToVisible() {
6750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mDecorAnimation != null) {
6760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mDecorAnimation.cancel();
6779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6780ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
6790ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator fadeIn = groupAnimatorOfFloat(View.ALPHA, 1f, mThumbImage, mTrackImage)
6800ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                .setDuration(DURATION_FADE_IN);
6810ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator fadeOut = groupAnimatorOfFloat(
6820ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                View.ALPHA, 0f, mPreviewImage, mPrimaryText, mSecondaryText)
6830ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                .setDuration(DURATION_FADE_OUT);
6840ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator slideIn = groupAnimatorOfFloat(
6850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                View.TRANSLATION_X, 0f, mThumbImage, mTrackImage).setDuration(DURATION_FADE_IN);
6860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
6870ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation = new AnimatorSet();
6880ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation.playTogether(fadeIn, fadeOut, slideIn);
6890ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation.start();
6909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6912c6196aa557e4eaf09d1ec3ad1347742f7f9f7b5Adam Powell
6920ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
6930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Shows the thumb, preview, and track.
6940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
6950ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void transitionToDragging() {
6960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mDecorAnimation != null) {
6970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mDecorAnimation.cancel();
6982c6196aa557e4eaf09d1ec3ad1347742f7f9f7b5Adam Powell        }
6990ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
7000ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator fadeIn = groupAnimatorOfFloat(
7010ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                View.ALPHA, 1f, mThumbImage, mTrackImage, mPreviewImage)
7020ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                .setDuration(DURATION_FADE_IN);
7030ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator slideIn = groupAnimatorOfFloat(
7040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                View.TRANSLATION_X, 0f, mThumbImage, mTrackImage).setDuration(DURATION_FADE_IN);
7050ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
7060ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation = new AnimatorSet();
7070ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation.playTogether(fadeIn, slideIn);
7080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation.start();
7092c6196aa557e4eaf09d1ec3ad1347742f7f9f7b5Adam Powell    }
7102c6196aa557e4eaf09d1ec3ad1347742f7f9f7b5Adam Powell
711a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette    private void postAutoHide() {
712a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette        mList.removeCallbacks(mDeferHide);
713a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette        mList.postDelayed(mDeferHide, FADE_TIMEOUT);
714a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette    }
715a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette
7160ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private boolean isLongList(int visibleItemCount, int totalItemCount) {
7170ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Are there enough pages to require fast scroll? Recompute only if
7180ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // total count changes.
7194df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project        if (mItemCount != totalItemCount && visibleItemCount > 0) {
7204df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project            mItemCount = totalItemCount;
72132c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell            mLongList = mItemCount / visibleItemCount >= MIN_PAGES;
72232c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell        }
7230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
7240ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        return mLongList;
7250ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
7260ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
7270ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    public void onScroll(int firstVisibleItem, int visibleItemCount, int totalItemCount) {
728447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        if (!mEnabled || !mAlwaysShow && !isLongList(visibleItemCount, totalItemCount)) {
7290ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            setState(STATE_NONE);
7309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
7319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7320ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
7330ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final boolean hasMoreItems = totalItemCount - visibleItemCount > 0;
7340ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (hasMoreItems && mState != STATE_DRAGGING) {
7350ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            setThumbPos(getPosFromItemCount(firstVisibleItem, visibleItemCount, totalItemCount));
7369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7370ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
7389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mScrollCompleted = true;
7399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7400ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mFirstVisibleItem != firstVisibleItem) {
7410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mFirstVisibleItem = firstVisibleItem;
742d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
7430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            // Show the thumb, if necessary, and set up auto-fade.
7440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            if (mState != STATE_DRAGGING) {
7450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                setState(STATE_VISIBLE);
746a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette                postAutoHide();
7470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            }
748d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        }
749d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy    }
750d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
7510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void getSectionsFromIndexer() {
7529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSectionIndexer = null;
7530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
7540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        Adapter adapter = mList.getAdapter();
7559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (adapter instanceof HeaderViewListAdapter) {
7560ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mHeaderCount = ((HeaderViewListAdapter) adapter).getHeadersCount();
7570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            adapter = ((HeaderViewListAdapter) adapter).getWrappedAdapter();
7589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7590ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
7609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (adapter instanceof ExpandableListConnector) {
7610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final ExpandableListAdapter expAdapter = ((ExpandableListConnector) adapter)
7620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    .getAdapter();
7639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (expAdapter instanceof SectionIndexer) {
7649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mSectionIndexer = (SectionIndexer) expAdapter;
7659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mListAdapter = (BaseAdapter) adapter;
7669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mSections = mSectionIndexer.getSections();
7679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
7680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else if (adapter instanceof SectionIndexer) {
7690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mListAdapter = (BaseAdapter) adapter;
7700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mSectionIndexer = (SectionIndexer) adapter;
7710ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mSections = mSectionIndexer.getSections();
7729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
7730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mListAdapter = (BaseAdapter) adapter;
7740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mSections = null;
7759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
778b1f498afc379ec84b86137e91afda6eccb8f4c0aAdam Powell    public void onSectionsChanged() {
779b1f498afc379ec84b86137e91afda6eccb8f4c0aAdam Powell        mListAdapter = null;
780b1f498afc379ec84b86137e91afda6eccb8f4c0aAdam Powell    }
781b1f498afc379ec84b86137e91afda6eccb8f4c0aAdam Powell
7820ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
7830ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Scrolls to a specific position within the section
7840ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param position
7850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
7860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void scrollTo(float position) {
7879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mScrollCompleted = false;
7880ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
7890ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int count = mList.getCount();
7909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final Object[] sections = mSections;
7910ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int sectionCount = sections == null ? 0 : sections.length;
7929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int sectionIndex;
7930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (sections != null && sectionCount > 1) {
7940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final int exactSection = MathUtils.constrain(
7950ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    (int) (position * sectionCount), 0, sectionCount - 1);
7960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            int targetSection = exactSection;
7970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            int targetIndex = mSectionIndexer.getPositionForSection(targetSection);
7980ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            sectionIndex = targetSection;
7990ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
8009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Given the expected section and index, the following code will
8019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // try to account for missing sections (no names starting with..)
8029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // It will compute the scroll space of surrounding empty sections
8039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // and interpolate the currently visible letter's range across the
8049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // available space, so that there is always some list movement while
8059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // the user moves the thumb.
8069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int nextIndex = count;
8070ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            int prevIndex = targetIndex;
8080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            int prevSection = targetSection;
8090ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            int nextSection = targetSection + 1;
8100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
8119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Assume the next section is unique
8120ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            if (targetSection < sectionCount - 1) {
8130ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                nextIndex = mSectionIndexer.getPositionForSection(targetSection + 1);
8149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
815e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
8169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Find the previous index if we're slicing the previous section
8170ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            if (nextIndex == targetIndex) {
8189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Non-existent letter
8190ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                while (targetSection > 0) {
8200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    targetSection--;
8210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    prevIndex = mSectionIndexer.getPositionForSection(targetSection);
8220ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    if (prevIndex != targetIndex) {
8230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        prevSection = targetSection;
8240ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        sectionIndex = targetSection;
825b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project                        break;
8260ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    } else if (targetSection == 0) {
827b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project                        // When section reaches 0 here, sectionIndex must follow it.
828b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project                        // Assuming mSectionIndexer.getPositionForSection(0) == 0.
829b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project                        sectionIndex = 0;
830b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project                        break;
831b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project                    }
8329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
8339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8340ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
8359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Find the next index, in case the assumed next index is not
836e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette            // unique. For instance, if there is no P, then request for P's
8379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // position actually returns Q's. So we need to look ahead to make
838e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette            // sure that there is really a Q at Q's position. If not, move
8399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // further down...
8409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int nextNextSection = nextSection + 1;
8410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            while (nextNextSection < sectionCount &&
8429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mSectionIndexer.getPositionForSection(nextNextSection) == nextIndex) {
8439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                nextNextSection++;
8449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                nextSection++;
8459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
8479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Compute the beginning and ending scroll range percentage of the
8480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            // currently visible section. This could be equal to or greater than
8490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            // (1 / nSections). If the target position is near the previous
8500ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            // position, snap to the previous position.
8510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final float prevPosition = (float) prevSection / sectionCount;
8520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final float nextPosition = (float) nextSection / sectionCount;
8530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final float snapThreshold = (count == 0) ? Float.MAX_VALUE : .125f / count;
8540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            if (prevSection == exactSection && position - prevPosition < snapThreshold) {
8550ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                targetIndex = prevIndex;
8569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
8570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                targetIndex = prevIndex + (int) ((nextIndex - prevIndex) * (position - prevPosition)
8580ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    / (nextPosition - prevPosition));
8599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8600ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
8610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            // Clamp to valid positions.
8620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            targetIndex = MathUtils.constrain(targetIndex, 0, count - 1);
863e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
8649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mList instanceof ExpandableListView) {
8650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                final ExpandableListView expList = (ExpandableListView) mList;
8669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                expList.setSelectionFromTop(expList.getFlatListPosition(
8670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        ExpandableListView.getPackedPositionForGroup(targetIndex + mHeaderCount)),
8680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        0);
8699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (mList instanceof ListView) {
8700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                ((ListView) mList).setSelectionFromTop(targetIndex + mHeaderCount, 0);
8719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
8720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                mList.setSelection(targetIndex + mHeaderCount);
8739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
8750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final int index = MathUtils.constrain((int) (position * count), 0, count - 1);
8767ee1ff1450a3ff83b6b8663bd7f44dc6f30775afAdam Powell
8779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mList instanceof ExpandableListView) {
8789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ExpandableListView expList = (ExpandableListView) mList;
8799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                expList.setSelectionFromTop(expList.getFlatListPosition(
8800ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        ExpandableListView.getPackedPositionForGroup(index + mHeaderCount)), 0);
8819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (mList instanceof ListView) {
8820ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                ((ListView)mList).setSelectionFromTop(index + mHeaderCount, 0);
8839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
8840ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                mList.setSelection(index + mHeaderCount);
8859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
8879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sectionIndex = -1;
8889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8906b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        if (mCurrentSection != sectionIndex) {
8916b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            mCurrentSection = sectionIndex;
8926b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette
8936b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            if (transitionPreviewLayout(sectionIndex)) {
8940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                transitionToDragging();
8956b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            } else {
8966b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette                transitionToVisible();
8970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            }
8989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9010ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
9026b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette     * Transitions the preview text to a new section. Handles animation,
9036b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette     * measurement, and layout. If the new preview text is empty, returns false.
9040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *
9056b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette     * @param sectionIndex The section index to which the preview should
9066b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette     *            transition.
9076b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette     * @return False if the new preview text is empty.
9080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
9096b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette    private boolean transitionPreviewLayout(int sectionIndex) {
9106b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        final Object[] sections = mSections;
9116b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        String text = null;
9126b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        if (sections != null && sectionIndex >= 0 && sectionIndex < sections.length) {
9136b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            final Object section = sections[sectionIndex];
9146b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            if (section != null) {
9156b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette                text = section.toString();
9166b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            }
9176b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        }
9186b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette
9190ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Rect bounds = mTempBounds;
9200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final ImageView preview = mPreviewImage;
9210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final TextView showing;
9220ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final TextView target;
9230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mShowingPrimary) {
9240ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            showing = mPrimaryText;
9250ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            target = mSecondaryText;
9260ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else {
9270ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            showing = mSecondaryText;
9280ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            target = mPrimaryText;
9290ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
9300ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
9310ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Set and layout target immediately.
9320ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        target.setText(text);
9330ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        measurePreview(target, bounds);
9340ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        applyLayout(target, bounds);
9350ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
9360ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mPreviewAnimation != null) {
9370ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mPreviewAnimation.cancel();
9380ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
9390ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
9400ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Cross-fade preview text.
9410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator showTarget = animateAlpha(target, 1f).setDuration(DURATION_CROSS_FADE);
9420ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator hideShowing = animateAlpha(showing, 0f).setDuration(DURATION_CROSS_FADE);
9430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        hideShowing.addListener(mSwitchPrimaryListener);
9440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
9450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Apply preview image padding and animate bounds, if necessary.
9460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        bounds.left -= mPreviewImage.getPaddingLeft();
9470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        bounds.top -= mPreviewImage.getPaddingTop();
9480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        bounds.right += mPreviewImage.getPaddingRight();
9490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        bounds.bottom += mPreviewImage.getPaddingBottom();
9500ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator resizePreview = animateBounds(preview, bounds);
9510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        resizePreview.setDuration(DURATION_RESIZE);
9520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
9530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mPreviewAnimation = new AnimatorSet();
9540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final AnimatorSet.Builder builder = mPreviewAnimation.play(hideShowing).with(showTarget);
9550ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        builder.with(resizePreview);
9560ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
9570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // The current preview size is unaffected by hidden or showing. It's
9580ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // used to set starting scales for things that need to be scaled down.
9590ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int previewWidth = preview.getWidth() - preview.getPaddingLeft()
9600ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                - preview.getPaddingRight();
9610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
9620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // If target is too large, shrink it immediately to fit and expand to
9630ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // target size. Otherwise, start at target size.
9640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int targetWidth = target.getWidth();
9650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (targetWidth > previewWidth) {
9660ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            target.setScaleX((float) previewWidth / targetWidth);
9670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final Animator scaleAnim = animateScaleX(target, 1f).setDuration(DURATION_RESIZE);
9680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            builder.with(scaleAnim);
9690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else {
9700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            target.setScaleX(1f);
9710ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
9720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
9730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // If showing is larger than target, shrink to target size.
9740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int showingWidth = showing.getWidth();
9750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (showingWidth > targetWidth) {
9760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final float scale = (float) targetWidth / showingWidth;
9770ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final Animator scaleAnim = animateScaleX(showing, scale).setDuration(DURATION_RESIZE);
9780ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            builder.with(scaleAnim);
9790ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
9800ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
9810ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mPreviewAnimation.start();
9826b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette
9836b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        return (text != null && text.length() > 0);
9840ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
9850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
9860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
9870ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Positions the thumb and preview widgets.
9880ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *
9890ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param position The position, between 0 and 1, along the track at which
9900ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *            to place the thumb.
9910ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
9920ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void setThumbPos(float position) {
9930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int top = 0;
9940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int bottom = mList.getHeight();
9950ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
9960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float thumbHalfHeight = mThumbImage.getHeight() / 2f;
9970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float min = top + thumbHalfHeight;
9980ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float max = bottom - thumbHalfHeight;
9990ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float offset = min;
10000ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float range = max - min;
10010ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float thumbMiddle = position * range + offset;
10020ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mThumbImage.setTranslationY(thumbMiddle - thumbHalfHeight);
10030ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
10040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Center the preview on the thumb, constrained to the list bounds.
10050ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float previewHalfHeight = mPreviewImage.getHeight() / 2f;
10060ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float minP = top + previewHalfHeight;
10070ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float maxP = bottom - previewHalfHeight;
10080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float previewMiddle = MathUtils.constrain(thumbMiddle, minP, maxP);
10090ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float previewTop = previewMiddle - previewHalfHeight;
10100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
10110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mPreviewImage.setTranslationY(previewTop);
10120ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mPrimaryText.setTranslationY(previewTop);
10130ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mSecondaryText.setTranslationY(previewTop);
10140ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
10150ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
10160ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private float getPosFromMotionEvent(float y) {
10170ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int top = 0;
10180ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int bottom = mList.getHeight();
10190ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
10200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float thumbHalfHeight = mThumbImage.getHeight() / 2f;
10210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float min = top + thumbHalfHeight;
10220ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float max = bottom - thumbHalfHeight;
10230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float offset = min;
10240ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float range = max - min;
10250ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
10260ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // If the list is the same height as the thumbnail or shorter,
10270ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // effectively disable scrolling.
10280ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (range <= 0) {
10290ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            return 0f;
10300ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
10310ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
10320ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        return MathUtils.constrain((y - offset) / range, 0f, 1f);
10330ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
10340ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
10350ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private float getPosFromItemCount(
10360ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            int firstVisibleItem, int visibleItemCount, int totalItemCount) {
103735948b7ed502d3f126acc8f2d61e099fa5deb0c7Adam Powell        if (mSectionIndexer == null || mListAdapter == null) {
103832c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell            getSectionsFromIndexer();
103932c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell        }
10400ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
10410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final boolean hasSections = mSectionIndexer != null && mSections != null
10420ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                && mSections.length > 0;
10430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (!hasSections || !mMatchDragPosition) {
10446b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            return (float) firstVisibleItem / (totalItemCount - visibleItemCount);
104532c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell        }
104632c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell
10476b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        // Ignore headers.
10480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        firstVisibleItem -= mHeaderCount;
104932c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell        if (firstVisibleItem < 0) {
105032c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell            return 0;
105132c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell        }
10520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        totalItemCount -= mHeaderCount;
105332c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell
10546b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        // Hidden portion of the first visible row.
10556b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        final View child = mList.getChildAt(0);
10566b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        final float incrementalPos;
10576b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        if (child == null || child.getHeight() == 0) {
10586b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            incrementalPos = 0;
10596b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        } else {
10606b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            incrementalPos = (float) (mList.getPaddingTop() - child.getTop()) / child.getHeight();
10616b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        }
10626b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette
10636b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        // Number of rows in this section.
106432c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell        final int section = mSectionIndexer.getSectionForPosition(firstVisibleItem);
106532c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell        final int sectionPos = mSectionIndexer.getPositionForSection(section);
1066f49971ec06e65ee2a0f0fd48a615b89c677086e7Adam Powell        final int sectionCount = mSections.length;
10676b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        final int positionsInSection;
10686b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        if (section < sectionCount - 1) {
1069414b02380d73a4ced03b1b51233ce9b339fb52cfJean-Baptiste Queru            final int nextSectionPos;
1070414b02380d73a4ced03b1b51233ce9b339fb52cfJean-Baptiste Queru            if (section + 1 < sectionCount) {
1071414b02380d73a4ced03b1b51233ce9b339fb52cfJean-Baptiste Queru                nextSectionPos = mSectionIndexer.getPositionForSection(section + 1);
1072414b02380d73a4ced03b1b51233ce9b339fb52cfJean-Baptiste Queru            } else {
1073414b02380d73a4ced03b1b51233ce9b339fb52cfJean-Baptiste Queru                nextSectionPos = totalItemCount - 1;
1074414b02380d73a4ced03b1b51233ce9b339fb52cfJean-Baptiste Queru            }
10756b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            positionsInSection = nextSectionPos - sectionPos;
10766b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        } else {
10776b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            positionsInSection = totalItemCount - sectionPos;
10786b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        }
10796b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette
10806b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        // Position within this section.
10816b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        final float posWithinSection;
10826b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        if (positionsInSection == 0) {
10836b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            posWithinSection = 0;
10846b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        } else {
10856b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            posWithinSection = (firstVisibleItem + incrementalPos - sectionPos)
10866b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette                    / positionsInSection;
10876b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        }
108832c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell
10890ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        return (section + posWithinSection) / sectionCount;
109032c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell    }
109132c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell
10920ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
10930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Cancels an ongoing fling event by injecting a
10940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * {@link MotionEvent#ACTION_CANCEL} into the host view.
10950ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
10969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void cancelFling() {
10970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final MotionEvent cancelFling = MotionEvent.obtain(
10980ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                0, 0, MotionEvent.ACTION_CANCEL, 0, 0, 0);
10999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mList.onTouchEvent(cancelFling);
11009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        cancelFling.recycle();
11019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1102e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
11030ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
11040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Cancels a pending drag.
11050ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *
11060ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @see #startPendingDrag()
11070ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
11080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void cancelPendingDrag() {
1109af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        mList.removeCallbacks(mDeferStartDrag);
11100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mHasPendingDrag = false;
1111af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell    }
1112af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
11130ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
11140ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Delays dragging until after the framework has determined that the user is
11150ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * scrolling, rather than tapping.
11160ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
11170ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void startPendingDrag() {
11180ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mHasPendingDrag = true;
11190ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mList.postDelayed(mDeferStartDrag, TAP_TIMEOUT);
1120af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell    }
1121af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
11220ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void beginDrag() {
1123af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        setState(STATE_DRAGGING);
11240ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1125af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        if (mListAdapter == null && mList != null) {
1126af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell            getSectionsFromIndexer();
1127af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        }
11280ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1129af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        if (mList != null) {
1130af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell            mList.requestDisallowInterceptTouchEvent(true);
1131af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell            mList.reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
1132af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        }
1133af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
1134af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        cancelFling();
1135af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell    }
1136af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
11370ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    public boolean onInterceptTouchEvent(MotionEvent ev) {
1138447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        if (!mEnabled) {
1139447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette            return false;
1140447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        }
1141447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette
1142af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        switch (ev.getActionMasked()) {
1143af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell            case MotionEvent.ACTION_DOWN:
11440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                if (isPointInside(ev.getX(), ev.getY())) {
11450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // If the parent has requested that its children delay
11460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // pressed state (e.g. is a scrolling container) then we
11470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // need to allow the parent time to decide whether it wants
11480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // to intercept events. If it does, we will receive a CANCEL
11490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // event.
11500ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    if (mList.isInScrollingContainer()) {
11510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        mInitialTouchY = ev.getY();
11520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        startPendingDrag();
11530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        return false;
1154af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                    }
11550ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
11560ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    beginDrag();
11570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    return true;
1158af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                }
1159af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                break;
1160af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell            case MotionEvent.ACTION_UP:
1161af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell            case MotionEvent.ACTION_CANCEL:
1162af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                cancelPendingDrag();
1163af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                break;
11649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
11669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return false;
11679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1169a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette    public boolean onInterceptHoverEvent(MotionEvent ev) {
1170a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette        if (!mEnabled) {
1171a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette            return false;
1172a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette        }
1173a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette
1174a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette        final int actionMasked = ev.getActionMasked();
1175a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette        if ((actionMasked == MotionEvent.ACTION_HOVER_ENTER
1176a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette                || actionMasked == MotionEvent.ACTION_HOVER_MOVE) && mState == STATE_NONE
1177a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette                && isPointInside(ev.getX(), ev.getY())) {
1178a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette            setState(STATE_VISIBLE);
1179a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette            postAutoHide();
1180a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette        }
1181a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette
1182a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette        return false;
1183a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette    }
1184a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette
11850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    public boolean onTouchEvent(MotionEvent me) {
1186447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        if (!mEnabled) {
1187447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette            return false;
1188447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        }
1189447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette
11900ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        switch (me.getActionMasked()) {
11910ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            case MotionEvent.ACTION_DOWN: {
11920ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                if (isPointInside(me.getX(), me.getY())) {
1193af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                    beginDrag();
1194af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                    return true;
11959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
11960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            } break;
1197af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
11980ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            case MotionEvent.ACTION_UP: {
11990ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                if (mHasPendingDrag) {
12000ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // Allow a tap to scroll.
12010ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    beginDrag();
12029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12030ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    final float pos = getPosFromMotionEvent(me.getY());
12040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    setThumbPos(pos);
12050ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    scrollTo(pos);
12060ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
12070ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    cancelPendingDrag();
12080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // Will hit the STATE_DRAGGING check below
1209d43fe07852a23c78da3b51b45469687c20be919aMarco Nelissen                }
12100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
12110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                if (mState == STATE_DRAGGING) {
12120ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    if (mList != null) {
12130ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        // ViewGroup does the right thing already, but there might
12140ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        // be other classes that don't properly reset on touch-up,
12150ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        // so do this explicitly just in case.
12160ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        mList.requestDisallowInterceptTouchEvent(false);
12170ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        mList.reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
12180ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    }
12190ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
12200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    setState(STATE_VISIBLE);
1221a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette                    postAutoHide();
12220ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
12230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    return true;
122420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell                }
12250ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            } break;
122620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
12270ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            case MotionEvent.ACTION_MOVE: {
12280ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                if (mHasPendingDrag && Math.abs(me.getY() - mInitialTouchY) > mScaledTouchSlop) {
1229af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                    setState(STATE_DRAGGING);
12300ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1231af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                    if (mListAdapter == null && mList != null) {
1232af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                        getSectionsFromIndexer();
1233af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                    }
12340ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1235af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                    if (mList != null) {
1236af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                        mList.requestDisallowInterceptTouchEvent(true);
1237af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                        mList.reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
1238af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                    }
1239af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
1240af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                    cancelFling();
1241af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                    cancelPendingDrag();
1242af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                    // Will hit the STATE_DRAGGING check below
1243af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                }
12440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
12450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                if (mState == STATE_DRAGGING) {
12460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // TODO: Ignore jitter.
12470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    final float pos = getPosFromMotionEvent(me.getY());
12480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    setThumbPos(pos);
12490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
12500ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // If the previous scrollTo is still pending
12510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    if (mScrollCompleted) {
12520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        scrollTo(pos);
12530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    }
12540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
12559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return true;
12569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
12570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            } break;
12580ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
12590ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            case MotionEvent.ACTION_CANCEL: {
12600ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                cancelPendingDrag();
12610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            } break;
12629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12630ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
12649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return false;
12659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1266d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
12670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
12680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Returns whether a coordinate is inside the scroller's activation area. If
12690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * there is a track image, touching anywhere within the thumb-width of the
12700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * track activates scrolling. Otherwise, the user has to touch inside thumb
12710ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * itself.
12720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *
12730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param x The x-coordinate.
12740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param y The y-coordinate.
12750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @return Whether the coordinate is inside the scroller's activation area.
12760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
12770ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private boolean isPointInside(float x, float y) {
12780ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        return isPointInsideX(x) && (mHasTrackImage || isPointInsideY(y));
12790ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
12800ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
12810ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private boolean isPointInsideX(float x) {
12820ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mLayoutFromRight) {
12830ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            return x >= mThumbImage.getLeft();
12840ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else {
12850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            return x <= mThumbImage.getRight();
12860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
12870ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
12880ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
12890ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private boolean isPointInsideY(float y) {
12900ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        return y >= mThumbImage.getTop() && y <= mThumbImage.getBottom();
12910ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
12920ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
12930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
12940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Constructs an animator for the specified property on a group of views.
12950ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * See {@link ObjectAnimator#ofFloat(Object, String, float...)} for
12960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * implementation details.
12970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *
12980ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param property The property being animated.
12990ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param value The value to which that property should animate.
13000ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param views The target views to animate.
13010ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @return An animator for all the specified views.
13020ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
13030ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static Animator groupAnimatorOfFloat(
13040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            Property<View, Float> property, float value, View... views) {
13050ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        AnimatorSet animSet = new AnimatorSet();
13060ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        AnimatorSet.Builder builder = null;
13070ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
13080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        for (int i = views.length - 1; i >= 0; i--) {
13090ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final Animator anim = ObjectAnimator.ofFloat(views[i], property, value);
13100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            if (builder == null) {
13110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                builder = animSet.play(anim);
13120ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            } else {
13130ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                builder.with(anim);
13140ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            }
131520232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell        }
131620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
13170ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        return animSet;
131882f3495b146b267f3786997752cef25310176349Romain Guy    }
131982f3495b146b267f3786997752cef25310176349Romain Guy
13200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
13210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Returns an animator for the view's scaleX value.
13220ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
13230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static Animator animateScaleX(View v, float target) {
13240ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        return ObjectAnimator.ofFloat(v, View.SCALE_X, target);
13250ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
1326e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
13270ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
13280ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Returns an animator for the view's alpha value.
13290ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
13300ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static Animator animateAlpha(View v, float alpha) {
13310ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        return ObjectAnimator.ofFloat(v, View.ALPHA, alpha);
13320ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
1333e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
13340ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
13350ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * A Property wrapper around the <code>left</code> functionality handled by the
13360ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * {@link View#setLeft(int)} and {@link View#getLeft()} methods.
13370ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
13380ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static Property<View, Integer> LEFT = new IntProperty<View>("left") {
13390ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
13400ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public void setValue(View object, int value) {
13410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            object.setLeft(value);
13429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1343e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
13440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
13450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public Integer get(View object) {
13460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            return object.getLeft();
13479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    };
1349e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
13500ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
13510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * A Property wrapper around the <code>top</code> functionality handled by the
13520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * {@link View#setTop(int)} and {@link View#getTop()} methods.
13530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
13540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static Property<View, Integer> TOP = new IntProperty<View>("top") {
1355e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette        @Override
13560ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public void setValue(View object, int value) {
13570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            object.setTop(value);
13580ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
1359e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
13600ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
13610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public Integer get(View object) {
13620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            return object.getTop();
13630ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
13640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    };
13650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
13660ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
13670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * A Property wrapper around the <code>right</code> functionality handled by the
13680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * {@link View#setRight(int)} and {@link View#getRight()} methods.
13690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
13700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static Property<View, Integer> RIGHT = new IntProperty<View>("right") {
13710ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
13720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public void setValue(View object, int value) {
13730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            object.setRight(value);
13740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
13750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
13760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
13770ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public Integer get(View object) {
13780ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            return object.getRight();
13799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13800ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    };
13810ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
13820ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
13830ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * A Property wrapper around the <code>bottom</code> functionality handled by the
13840ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * {@link View#setBottom(int)} and {@link View#getBottom()} methods.
13850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
13860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static Property<View, Integer> BOTTOM = new IntProperty<View>("bottom") {
13870ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
13880ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public void setValue(View object, int value) {
13890ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            object.setBottom(value);
13900ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
13910ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
13920ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
13930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public Integer get(View object) {
13940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            return object.getBottom();
13950ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
13960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    };
13970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
13980ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
13990ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Returns an animator for the view's bounds.
14000ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
14010ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static Animator animateBounds(View v, Rect bounds) {
14020ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final PropertyValuesHolder left = PropertyValuesHolder.ofInt(LEFT, bounds.left);
14030ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final PropertyValuesHolder top = PropertyValuesHolder.ofInt(TOP, bounds.top);
14040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final PropertyValuesHolder right = PropertyValuesHolder.ofInt(RIGHT, bounds.right);
14050ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final PropertyValuesHolder bottom = PropertyValuesHolder.ofInt(BOTTOM, bounds.bottom);
14060ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        return ObjectAnimator.ofPropertyValuesHolder(v, left, top, right, bottom);
14079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
14089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
1409