FastScroller.java revision b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3
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();
109fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette    private final Rect mContainerRect = new Rect();
11020232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
1110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final AbsListView mList;
1120ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final ViewGroupOverlay mOverlay;
1130ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final TextView mPrimaryText;
1140ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final TextView mSecondaryText;
1150ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final ImageView mThumbImage;
1160ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final ImageView mTrackImage;
1170ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final ImageView mPreviewImage;
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1190ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
1200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Preview image resource IDs for left- and right-aligned layouts. See
1210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * {@link #PREVIEW_LEFT} and {@link #PREVIEW_RIGHT}.
1220ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
1230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final int[] mPreviewResId = new int[2];
1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1250ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
1260ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Padding in pixels around the preview text. Applied as layout margins to
1270ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * the preview text and padding to the preview image.
1280ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
1290ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final int mPreviewPadding;
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1310ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Whether there is a track image to display. */
1320ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final boolean mHasTrackImage;
1330ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
13426bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette    /** Total width of decorations. */
13526bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette    private final int mWidth;
13626bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette
1370ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Set containing decoration transition animations. */
1380ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private AnimatorSet mDecorAnimation;
1390ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1400ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Set containing preview text transition animations. */
1410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private AnimatorSet mPreviewAnimation;
1420ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Whether the primary text is showing. */
1440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private boolean mShowingPrimary;
1450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Whether we're waiting for completion of scrollTo(). */
1470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private boolean mScrollCompleted;
1480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** The position of the first visible item in the list. */
1500ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private int mFirstVisibleItem;
1510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** The number of headers at the top of the view. */
1530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private int mHeaderCount;
1540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1550ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** The index of the current section. */
1560ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private int mCurrentSection = -1;
1570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
15826bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette    /** The current scrollbar position. */
15926bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette    private int mScrollbarPosition = -1;
16026bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette
1610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Whether the list is long enough to need a fast scroller. */
1624df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project    private boolean mLongList;
163e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
1640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private Object[] mSections;
165e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
166fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette    /** Whether this view is currently performing layout. */
167fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette    private boolean mUpdatingLayout;
168fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette
1690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
1700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Current decoration state, one of:
1710ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * <ul>
1720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * <li>{@link #STATE_NONE}, nothing visible
1730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * <li>{@link #STATE_VISIBLE}, showing track and thumb
1740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * <li>{@link #STATE_DRAGGING}, visible and showing preview
1750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * </ul>
1760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mState;
178e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
1790ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private BaseAdapter mListAdapter;
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private SectionIndexer mSectionIndexer;
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1820ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Whether decorations should be laid out from right to left. */
1830ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private boolean mLayoutFromRight;
18420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
185447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    /** Whether the fast scroller is enabled. */
186447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    private boolean mEnabled;
187447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette
1880ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Whether the scrollbar and decorations should always be shown. */
18920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    private boolean mAlwaysShow;
19020232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
1910ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
1920ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Position for the preview image and text. One of:
1930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * <ul>
1940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * <li>{@link #OVERLAY_AT_THUMB}
1950ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * <li>{@link #OVERLAY_FLOATING}
1960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * </ul>
1970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
19820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    private int mOverlayPosition;
19920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
20026bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette    /** Current scrollbar style, including inset and overlay properties. */
20126bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette    private int mScrollBarStyle;
20226bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette
2030ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /** Whether to precisely match the thumb position to the list. */
204568ccd88d7efd33da0c535e07b7ea7cf476be22aAdam Powell    private boolean mMatchDragPosition;
205568ccd88d7efd33da0c535e07b7ea7cf476be22aAdam Powell
2060ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private float mInitialTouchY;
2070ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private boolean mHasPendingDrag;
208af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell    private int mScaledTouchSlop;
209af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
210af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell    private final Runnable mDeferStartDrag = new Runnable() {
211e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette        @Override
212af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        public void run() {
213af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell            if (mList.mIsAttached) {
214af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                beginDrag();
215af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
2160ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                final float pos = getPosFromMotionEvent(mInitialTouchY);
2170ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                scrollTo(pos);
218af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell            }
219af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
2200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mHasPendingDrag = false;
2210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
2220ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    };
2230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
2240ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
2250ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Used to delay hiding fast scroll decorations.
2260ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
2270ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final Runnable mDeferHide = new Runnable() {
2280ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
2290ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public void run() {
2300ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            setState(STATE_NONE);
2310ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
2320ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    };
2330ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
2340ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
2350ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Used to effect a transition from primary to secondary text.
2360ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
2370ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private final AnimatorListener mSwitchPrimaryListener = new AnimatorListenerAdapter() {
2380ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
2390ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public void onAnimationEnd(Animator animation) {
2400ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mShowingPrimary = !mShowingPrimary;
241af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        }
242af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell    };
243af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
24439bed695cecdbefc9fae785692fe468cd9007c35Alan Viverette    public FastScroller(AbsListView listView) {
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mList = listView;
2460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mOverlay = listView.getOverlay();
2470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
24839bed695cecdbefc9fae785692fe468cd9007c35Alan Viverette        final Context context = listView.getContext();
2490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
2500ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
2510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Resources res = context.getResources();
2520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final TypedArray ta = context.getTheme().obtainStyledAttributes(ATTRS);
2530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
25426bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette        final ImageView trackImage = new ImageView(context);
25526bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette        mTrackImage = trackImage;
25626bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette
25726bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette        int width = 0;
2580ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
2590ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Add track to overlay if it has an image.
26026bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette        final Drawable trackDrawable = ta.getDrawable(TRACK_DRAWABLE);
26126bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette        if (trackDrawable != null) {
2620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mHasTrackImage = true;
26326bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            trackImage.setBackground(trackDrawable);
26426bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            mOverlay.add(trackImage);
26526bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            width = Math.max(width, trackDrawable.getIntrinsicWidth());
2660ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else {
2670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mHasTrackImage = false;
2680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
2690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
27026bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette        final ImageView thumbImage = new ImageView(context);
27126bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette        mThumbImage = thumbImage;
2720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
2730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Add thumb to overlay if it has an image.
2740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Drawable thumbDrawable = ta.getDrawable(THUMB_DRAWABLE);
2750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (thumbDrawable != null) {
27626bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            thumbImage.setImageDrawable(thumbDrawable);
27726bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            mOverlay.add(thumbImage);
27826bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            width = Math.max(width, thumbDrawable.getIntrinsicWidth());
2790ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
2800ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
2810ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // If necessary, apply minimum thumb width and height.
2820ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (thumbDrawable.getIntrinsicWidth() <= 0 || thumbDrawable.getIntrinsicHeight() <= 0) {
28326bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            final int minWidth = res.getDimensionPixelSize(R.dimen.fastscroll_thumb_width);
28426bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            thumbImage.setMinimumWidth(minWidth);
28526bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            thumbImage.setMinimumHeight(
2860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    res.getDimensionPixelSize(R.dimen.fastscroll_thumb_height));
28726bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            width = Math.max(width, minWidth);
2880ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
2890ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
29026bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette        mWidth = width;
29126bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette
2920ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int previewSize = res.getDimensionPixelSize(R.dimen.fastscroll_overlay_size);
2930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mPreviewImage = new ImageView(context);
2940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mPreviewImage.setMinimumWidth(previewSize);
2950ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mPreviewImage.setMinimumHeight(previewSize);
2960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mPreviewImage.setAlpha(0f);
2970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mOverlay.add(mPreviewImage);
2980ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
2990ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mPreviewPadding = res.getDimensionPixelSize(R.dimen.fastscroll_overlay_padding);
3000ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
3016b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        final int textMinSize = Math.max(0, previewSize - mPreviewPadding);
3020ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mPrimaryText = createPreviewTextView(context, ta);
3036b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        mPrimaryText.setMinimumWidth(textMinSize);
3046b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        mPrimaryText.setMinimumHeight(textMinSize);
3050ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mOverlay.add(mPrimaryText);
3060ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mSecondaryText = createPreviewTextView(context, ta);
3076b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        mSecondaryText.setMinimumWidth(textMinSize);
3086b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        mSecondaryText.setMinimumHeight(textMinSize);
3090ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mOverlay.add(mSecondaryText);
3100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
3110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mPreviewResId[PREVIEW_LEFT] = ta.getResourceId(PREVIEW_BACKGROUND_LEFT, 0);
3120ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mPreviewResId[PREVIEW_RIGHT] = ta.getResourceId(PREVIEW_BACKGROUND_RIGHT, 0);
3130ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mOverlayPosition = ta.getInt(OVERLAY_POSITION, OVERLAY_FLOATING);
3140ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        ta.recycle();
3150ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
31626bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette        mScrollBarStyle = listView.getScrollBarStyle();
3170ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mScrollCompleted = true;
3180ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mState = STATE_VISIBLE;
31926bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette        mMatchDragPosition = context.getApplicationInfo().targetSdkVersion
32026bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette                >= Build.VERSION_CODES.HONEYCOMB;
3210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
3220ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        getSectionsFromIndexer();
3230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        refreshDrawablePressedState();
324b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        updateLongList(listView.getChildCount(), listView.getCount());
3250ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        setScrollbarPosition(mList.getVerticalScrollbarPosition());
326a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette        postAutoHide();
3279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3290ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
330447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette     * Removes this FastScroller overlay from the host view.
331447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette     */
332447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    public void remove() {
333447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        mOverlay.remove(mTrackImage);
334447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        mOverlay.remove(mThumbImage);
335447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        mOverlay.remove(mPreviewImage);
336447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        mOverlay.remove(mPrimaryText);
337447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        mOverlay.remove(mSecondaryText);
338447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    }
339447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette
340447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    /**
341447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette     * @param enabled Whether the fast scroll thumb is enabled.
342447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette     */
343447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    public void setEnabled(boolean enabled) {
344b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        if (mEnabled != enabled) {
345b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette            mEnabled = enabled;
346447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette
347b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette            onStateDependencyChanged();
348447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        }
349447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    }
350447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette
351447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    /**
352447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette     * @return Whether the fast scroll thumb is enabled.
353447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette     */
354447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    public boolean isEnabled() {
355b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        return mEnabled && (mLongList || mAlwaysShow);
356447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    }
357447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette
358447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette    /**
3590ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param alwaysShow Whether the fast scroll thumb should always be shown
3600ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
36120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    public void setAlwaysShow(boolean alwaysShow) {
362b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        if (mAlwaysShow != alwaysShow) {
363b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette            mAlwaysShow = alwaysShow;
3640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
365b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette            onStateDependencyChanged();
36620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell        }
36720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    }
36820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
3690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
3700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @return Whether the fast scroll thumb will always be shown
3710ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @see #setAlwaysShow(boolean)
3720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
37320232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    public boolean isAlwaysShowEnabled() {
37420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell        return mAlwaysShow;
37520232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    }
37620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
377b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette    /**
378b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette     * Called when one of the variables affecting enabled state changes.
379b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette     */
380b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette    private void onStateDependencyChanged() {
381b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        if (isEnabled()) {
382b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette            if (isAlwaysShowEnabled()) {
383b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                setState(STATE_VISIBLE);
384b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette            } else if (mState == STATE_VISIBLE) {
385b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                postAutoHide();
386b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette            }
387b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        } else {
388b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette            stop();
389b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        }
390b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette
391b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        mList.resolvePadding();
392b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette    }
393b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette
39426bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette    public void setScrollBarStyle(int style) {
39526bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette        if (mScrollBarStyle != style) {
39626bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            mScrollBarStyle = style;
39726bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette
39826bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            updateLayout();
39926bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette        }
40026bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette    }
40126bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette
4020ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
4030ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Immediately transitions the fast scroller decorations to a hidden state.
4040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
4050ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    public void stop() {
4060ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        setState(STATE_NONE);
4070ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
40820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
40920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    public void setScrollbarPosition(int position) {
410c23ee46c8fac06b1982d115368f772fea399a4e0Fabrice Di Meglio        if (position == View.SCROLLBAR_POSITION_DEFAULT) {
411c23ee46c8fac06b1982d115368f772fea399a4e0Fabrice Di Meglio            position = mList.isLayoutRtl() ?
412c23ee46c8fac06b1982d115368f772fea399a4e0Fabrice Di Meglio                    View.SCROLLBAR_POSITION_LEFT : View.SCROLLBAR_POSITION_RIGHT;
413c23ee46c8fac06b1982d115368f772fea399a4e0Fabrice Di Meglio        }
4140ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
41526bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette        if (mScrollbarPosition != position) {
41626bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            mScrollbarPosition = position;
41726bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            mLayoutFromRight = position != View.SCROLLBAR_POSITION_LEFT;
4180ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
41926bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            final int previewResId = mPreviewResId[mLayoutFromRight ? PREVIEW_RIGHT : PREVIEW_LEFT];
42026bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            mPreviewImage.setBackgroundResource(previewResId);
4210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
42226bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            // Add extra padding for text.
42326bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            final Drawable background = mPreviewImage.getBackground();
42426bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            if (background != null) {
42526bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette                final Rect padding = mTempBounds;
42626bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette                background.getPadding(padding);
42726bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette                padding.offset(mPreviewPadding, mPreviewPadding);
42826bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette                mPreviewImage.setPadding(padding.left, padding.top, padding.right, padding.bottom);
42926bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            }
4300ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
43126bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            // Requires re-layout.
43226bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            updateLayout();
43326bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette        }
43420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    }
43520232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
43620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    public int getWidth() {
43726bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette        return mWidth;
43820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell    }
43920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
4400ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    public void onSizeChanged(int w, int h, int oldw, int oldh) {
4410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        updateLayout();
4420ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
4430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
4440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    public void onItemCountChanged(int oldTotalItemCount, int totalItemCount) {
4450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int visibleItemCount = mList.getChildCount();
4460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final boolean hasMoreItems = totalItemCount - visibleItemCount > 0;
4470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (hasMoreItems && mState != STATE_DRAGGING) {
4480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final int firstVisibleItem = mList.getFirstVisiblePosition();
4490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            setThumbPos(getPosFromItemCount(firstVisibleItem, visibleItemCount, totalItemCount));
4509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
451b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette
452b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        updateLongList(visibleItemCount, totalItemCount);
453b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette    }
454b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette
455b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette    private void updateLongList(int visibleItemCount, int totalItemCount) {
456b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        final boolean longList = visibleItemCount > 0
457b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                && totalItemCount / visibleItemCount >= MIN_PAGES;
458b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        if (mLongList != longList) {
459b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette            mLongList = longList;
460b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette
461b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette            onStateDependencyChanged();
462b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        }
4639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
464e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
4650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
4660ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Creates a view into which preview text can be placed.
4670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
4680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private TextView createPreviewTextView(Context context, TypedArray ta) {
4690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final LayoutParams params = new LayoutParams(
4700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
4710ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Resources res = context.getResources();
4720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int minSize = res.getDimensionPixelSize(R.dimen.fastscroll_overlay_size);
4730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final ColorStateList textColor = ta.getColorStateList(TEXT_COLOR);
4740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float textSize = res.getDimension(R.dimen.fastscroll_overlay_text_size);
4750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final TextView textView = new TextView(context);
4760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        textView.setLayoutParams(params);
4770ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        textView.setTextColor(textColor);
4780ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        textView.setTextSize(textSize);
4790ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        textView.setSingleLine(true);
4800ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        textView.setEllipsize(TruncateAt.MIDDLE);
4810ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        textView.setGravity(Gravity.CENTER);
4820ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        textView.setAlpha(0f);
4830ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
4840ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Manually propagate inherited layout direction.
4850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        textView.setLayoutDirection(mList.getLayoutDirection());
4860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
4870ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        return textView;
4889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
489e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
4900ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
4910ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Measures and layouts the scrollbar and decorations.
4920ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
49326bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette    public void updateLayout() {
494fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        // Prevent re-entry when RTL properties change as a side-effect of
495fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        // resolving padding.
496fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        if (mUpdatingLayout) {
497fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette            return;
498fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        }
499fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette
500fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        mUpdatingLayout = true;
501fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette
502fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        updateContainerRect();
503fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette
5040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        layoutThumb();
5050ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        layoutTrack();
5060ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
5070ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Rect bounds = mTempBounds;
5080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        measurePreview(mPrimaryText, bounds);
5090ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        applyLayout(mPrimaryText, bounds);
5100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        measurePreview(mSecondaryText, bounds);
5110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        applyLayout(mSecondaryText, bounds);
5120ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
5130ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mPreviewImage != null) {
5140ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            // Apply preview image padding.
5150ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            bounds.left -= mPreviewImage.getPaddingLeft();
5160ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            bounds.top -= mPreviewImage.getPaddingTop();
5170ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            bounds.right += mPreviewImage.getPaddingRight();
5180ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            bounds.bottom += mPreviewImage.getPaddingBottom();
5190ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            applyLayout(mPreviewImage, bounds);
52020232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell        }
521fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette
522fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        mUpdatingLayout = false;
5239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
524e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
5250ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
5260ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Layouts a view within the specified bounds and pins the pivot point to
5270ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * the appropriate edge.
5280ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *
5290ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param view The view to layout.
5300ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param bounds Bounds at which to layout the view.
5310ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
5320ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void applyLayout(View view, Rect bounds) {
5330ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        view.layout(bounds.left, bounds.top, bounds.right, bounds.bottom);
5340ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        view.setPivotX(mLayoutFromRight ? bounds.right - bounds.left : 0);
5350ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
5360ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
5370ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
5380ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Measures the preview text bounds, taking preview image padding into
5390ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * account. This method should only be called after {@link #layoutThumb()}
5400ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * and {@link #layoutTrack()} have both been called at least once.
5410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *
5420ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param v The preview text view to measure.
5430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param out Rectangle into which measured bounds are placed.
5440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
5450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void measurePreview(View v, Rect out) {
5460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Apply the preview image's padding as layout margins.
5470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Rect margins = mTempMargins;
5480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        margins.left = mPreviewImage.getPaddingLeft();
5490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        margins.top = mPreviewImage.getPaddingTop();
5500ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        margins.right = mPreviewImage.getPaddingRight();
5510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        margins.bottom = mPreviewImage.getPaddingBottom();
5520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
5530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mOverlayPosition == OVERLAY_AT_THUMB) {
5540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            measureViewToSide(v, mThumbImage, margins, out);
55520232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell        } else {
5560ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            measureFloating(v, margins, out);
55720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell        }
5589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5600ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
5610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Measures the bounds for a view that should be laid out against the edge
5620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * of an adjacent view. If no adjacent view is provided, lays out against
5630ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * the list edge.
5640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *
5650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param view The view to measure for layout.
5660ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param adjacent (Optional) The adjacent view, may be null to align to the
5670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *            list edge.
5680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param margins Layout margins to apply to the view.
5690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param out Rectangle into which measured bounds are placed.
5700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
5710ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void measureViewToSide(View view, View adjacent, Rect margins, Rect out) {
5720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int marginLeft;
5730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int marginTop;
5740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int marginRight;
5750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (margins == null) {
5760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginLeft = 0;
5770ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginTop = 0;
5780ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginRight = 0;
5790ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else {
5800ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginLeft = margins.left;
5810ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginTop = margins.top;
5820ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginRight = margins.right;
5830ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
5841ffaebc41bfd47472543d1ff86c8412c868fc4c9Amith Yamasani
585fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final Rect container = mContainerRect;
586fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int containerWidth = container.width();
5870ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int maxWidth;
5880ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (adjacent == null) {
589fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette            maxWidth = containerWidth;
5900ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else if (mLayoutFromRight) {
5910ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            maxWidth = adjacent.getLeft();
5920ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else {
593fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette            maxWidth = containerWidth - adjacent.getRight();
5940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
59520232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
5960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int adjMaxWidth = maxWidth - marginLeft - marginRight;
5970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(adjMaxWidth, MeasureSpec.AT_MOST);
5980ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
5990ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        view.measure(widthMeasureSpec, heightMeasureSpec);
6000ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
6010ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Align to the left or right.
6020ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int width = view.getMeasuredWidth();
6030ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int left;
6040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int right;
6050ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mLayoutFromRight) {
606fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette            right = (adjacent == null ? container.right : adjacent.getLeft()) - marginRight;
6070ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            left = right - width;
6080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else {
609fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette            left = (adjacent == null ? container.left : adjacent.getRight()) + marginLeft;
6100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            right = left + width;
6110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
61220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
6130ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Don't adjust the vertical position.
6140ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int top = marginTop;
6150ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int bottom = top + view.getMeasuredHeight();
6160ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        out.set(left, top, right, bottom);
6170ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
618af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
6190ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void measureFloating(View preview, Rect margins, Rect out) {
6200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int marginLeft;
6210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int marginTop;
6220ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int marginRight;
6230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (margins == null) {
6240ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginLeft = 0;
6250ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginTop = 0;
6260ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginRight = 0;
6270ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else {
6280ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginLeft = margins.left;
6290ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginTop = margins.top;
6300ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            marginRight = margins.right;
6310ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
632568ccd88d7efd33da0c535e07b7ea7cf476be22aAdam Powell
633fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final Rect container = mContainerRect;
634fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int containerWidth = container.width();
635fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int adjMaxWidth = containerWidth - marginLeft - marginRight;
6360ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(adjMaxWidth, MeasureSpec.AT_MOST);
6370ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
6380ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        preview.measure(widthMeasureSpec, heightMeasureSpec);
6390ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
6400ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Align at the vertical center, 10% from the top.
641fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int containerHeight = container.height();
6420ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int width = preview.getMeasuredWidth();
643fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int top = containerHeight / 10 + marginTop + container.top;
6440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int bottom = top + preview.getMeasuredHeight();
645fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int left = (containerWidth - width) / 2 + container.left;
6460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int right = left + width;
6470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        out.set(left, top, right, bottom);
6489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
649e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
65026bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette    /**
65126bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette     * Updates the container rectangle used for layout.
65226bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette     */
653fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette    private void updateContainerRect() {
654fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final AbsListView list = mList;
65526bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette        list.resolvePadding();
65626bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette
657fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final Rect container = mContainerRect;
658fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        container.left = 0;
659fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        container.top = 0;
660fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        container.right = list.getWidth();
661fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        container.bottom = list.getHeight();
662fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette
66326bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette        final int scrollbarStyle = mScrollBarStyle;
664fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        if (scrollbarStyle == View.SCROLLBARS_INSIDE_INSET
665fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette                || scrollbarStyle == View.SCROLLBARS_INSIDE_OVERLAY) {
666fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette            container.left += list.getPaddingLeft();
667fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette            container.top += list.getPaddingTop();
668fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette            container.right -= list.getPaddingRight();
669fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette            container.bottom -= list.getPaddingBottom();
67026bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette
67126bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            // In inset mode, we need to adjust for padded scrollbar width.
67226bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            if (scrollbarStyle == View.SCROLLBARS_INSIDE_INSET) {
67326bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette                final int width = getWidth();
67426bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette                if (mScrollbarPosition == View.SCROLLBAR_POSITION_RIGHT) {
67526bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette                    container.right += width;
67626bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette                } else {
67726bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette                    container.left -= width;
67826bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette                }
67926bb253b4001b7d4d876656b0c6bd11b6aab4131Alan Viverette            }
680fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        }
681fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette    }
682fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette
6830ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
6840ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Lays out the thumb according to the current scrollbar position.
6850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
6860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void layoutThumb() {
6870ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Rect bounds = mTempBounds;
6880ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        measureViewToSide(mThumbImage, null, null, bounds);
6890ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        applyLayout(mThumbImage, bounds);
6909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
691e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
6920ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
693fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette     * Lays out the track centered on the thumb. Must be called after
694fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette     * {@link #layoutThumb}.
6950ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
6960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void layoutTrack() {
6970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final View track = mTrackImage;
6980ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final View thumb = mThumbImage;
699fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final Rect container = mContainerRect;
700fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int containerWidth = container.width();
701fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(containerWidth, MeasureSpec.AT_MOST);
7020ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
7030ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        track.measure(widthMeasureSpec, heightMeasureSpec);
7040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
7050ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int trackWidth = track.getMeasuredWidth();
7060ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int thumbHalfHeight = thumb == null ? 0 : thumb.getHeight() / 2;
707fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int left = thumb.getLeft() + (thumb.getWidth() - trackWidth) / 2;
7080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int right = left + trackWidth;
709fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int top = container.top + thumbHalfHeight;
710fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int bottom = container.bottom - thumbHalfHeight;
7110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        track.layout(left, top, right, bottom);
7129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
713e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
7140ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void setState(int state) {
7150ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mList.removeCallbacks(mDeferHide);
716e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
7170ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mAlwaysShow && state == STATE_NONE) {
7180ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            state = STATE_VISIBLE;
7199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (state == mState) {
7220ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            return;
7230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
7249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7250ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        switch (state) {
7260ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            case STATE_NONE:
7270ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                transitionToHidden();
7280ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                break;
7290ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            case STATE_VISIBLE:
7300ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                transitionToVisible();
7310ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                break;
7320ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            case STATE_DRAGGING:
7336b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette                if (transitionPreviewLayout(mCurrentSection)) {
7346b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette                    transitionToDragging();
7356b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette                } else {
7366b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette                    transitionToVisible();
7376b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette                }
7380ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                break;
7390ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
74020232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
7410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mState = state;
742e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
7430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        refreshDrawablePressedState();
7440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
745e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
7460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void refreshDrawablePressedState() {
7470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final boolean isPressed = mState == STATE_DRAGGING;
7480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mThumbImage.setPressed(isPressed);
7490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mTrackImage.setPressed(isPressed);
7500ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
751e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
7520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
7530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Shows nothing.
7540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
7550ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void transitionToHidden() {
7560ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mDecorAnimation != null) {
7570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mDecorAnimation.cancel();
7589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7590ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
7600ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator fadeOut = groupAnimatorOfFloat(View.ALPHA, 0f, mThumbImage, mTrackImage,
7610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                mPreviewImage, mPrimaryText, mSecondaryText).setDuration(DURATION_FADE_OUT);
7620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
7630ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Push the thumb and track outside the list bounds.
7640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float offset = mLayoutFromRight ? mThumbImage.getWidth() : -mThumbImage.getWidth();
7650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator slideOut = groupAnimatorOfFloat(
7660ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                View.TRANSLATION_X, offset, mThumbImage, mTrackImage)
7670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                .setDuration(DURATION_FADE_OUT);
7680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
7690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation = new AnimatorSet();
7700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation.playTogether(fadeOut, slideOut);
7710ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation.start();
7729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
7750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Shows the thumb and track.
7760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
7770ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void transitionToVisible() {
7780ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mDecorAnimation != null) {
7790ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mDecorAnimation.cancel();
7809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7810ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
7820ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator fadeIn = groupAnimatorOfFloat(View.ALPHA, 1f, mThumbImage, mTrackImage)
7830ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                .setDuration(DURATION_FADE_IN);
7840ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator fadeOut = groupAnimatorOfFloat(
7850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                View.ALPHA, 0f, mPreviewImage, mPrimaryText, mSecondaryText)
7860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                .setDuration(DURATION_FADE_OUT);
7870ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator slideIn = groupAnimatorOfFloat(
7880ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                View.TRANSLATION_X, 0f, mThumbImage, mTrackImage).setDuration(DURATION_FADE_IN);
7890ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
7900ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation = new AnimatorSet();
7910ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation.playTogether(fadeIn, fadeOut, slideIn);
7920ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation.start();
7939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7942c6196aa557e4eaf09d1ec3ad1347742f7f9f7b5Adam Powell
7950ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
7960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Shows the thumb, preview, and track.
7970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
7980ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void transitionToDragging() {
7990ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mDecorAnimation != null) {
8000ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mDecorAnimation.cancel();
8012c6196aa557e4eaf09d1ec3ad1347742f7f9f7b5Adam Powell        }
8020ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
8030ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator fadeIn = groupAnimatorOfFloat(
8040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                View.ALPHA, 1f, mThumbImage, mTrackImage, mPreviewImage)
8050ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                .setDuration(DURATION_FADE_IN);
8060ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator slideIn = groupAnimatorOfFloat(
8070ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                View.TRANSLATION_X, 0f, mThumbImage, mTrackImage).setDuration(DURATION_FADE_IN);
8080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
8090ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation = new AnimatorSet();
8100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation.playTogether(fadeIn, slideIn);
8110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mDecorAnimation.start();
8122c6196aa557e4eaf09d1ec3ad1347742f7f9f7b5Adam Powell    }
8132c6196aa557e4eaf09d1ec3ad1347742f7f9f7b5Adam Powell
814a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette    private void postAutoHide() {
815a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette        mList.removeCallbacks(mDeferHide);
816a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette        mList.postDelayed(mDeferHide, FADE_TIMEOUT);
817a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette    }
818a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette
8190ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    public void onScroll(int firstVisibleItem, int visibleItemCount, int totalItemCount) {
820b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        if (!isEnabled()) {
8210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            setState(STATE_NONE);
8229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
8239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8240ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
8250ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final boolean hasMoreItems = totalItemCount - visibleItemCount > 0;
8260ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (hasMoreItems && mState != STATE_DRAGGING) {
8270ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            setThumbPos(getPosFromItemCount(firstVisibleItem, visibleItemCount, totalItemCount));
8289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8290ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
8309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mScrollCompleted = true;
8319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8320ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mFirstVisibleItem != firstVisibleItem) {
8330ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mFirstVisibleItem = firstVisibleItem;
834d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
8350ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            // Show the thumb, if necessary, and set up auto-fade.
8360ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            if (mState != STATE_DRAGGING) {
8370ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                setState(STATE_VISIBLE);
838a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette                postAutoHide();
8390ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            }
840d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        }
841d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy    }
842d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
8430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void getSectionsFromIndexer() {
8449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSectionIndexer = null;
8450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
8460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        Adapter adapter = mList.getAdapter();
8479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (adapter instanceof HeaderViewListAdapter) {
8480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mHeaderCount = ((HeaderViewListAdapter) adapter).getHeadersCount();
8490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            adapter = ((HeaderViewListAdapter) adapter).getWrappedAdapter();
8509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
8529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (adapter instanceof ExpandableListConnector) {
8530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final ExpandableListAdapter expAdapter = ((ExpandableListConnector) adapter)
8540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    .getAdapter();
8559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (expAdapter instanceof SectionIndexer) {
8569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mSectionIndexer = (SectionIndexer) expAdapter;
8579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mListAdapter = (BaseAdapter) adapter;
8589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mSections = mSectionIndexer.getSections();
8599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8600ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else if (adapter instanceof SectionIndexer) {
8610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mListAdapter = (BaseAdapter) adapter;
8620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mSectionIndexer = (SectionIndexer) adapter;
8630ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mSections = mSectionIndexer.getSections();
8649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
8650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mListAdapter = (BaseAdapter) adapter;
8660ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mSections = null;
8679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
870b1f498afc379ec84b86137e91afda6eccb8f4c0aAdam Powell    public void onSectionsChanged() {
871b1f498afc379ec84b86137e91afda6eccb8f4c0aAdam Powell        mListAdapter = null;
872b1f498afc379ec84b86137e91afda6eccb8f4c0aAdam Powell    }
873b1f498afc379ec84b86137e91afda6eccb8f4c0aAdam Powell
8740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
8750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Scrolls to a specific position within the section
8760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param position
8770ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
8780ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void scrollTo(float position) {
8799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mScrollCompleted = false;
8800ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
8810ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int count = mList.getCount();
8829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final Object[] sections = mSections;
8830ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int sectionCount = sections == null ? 0 : sections.length;
8849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int sectionIndex;
8850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (sections != null && sectionCount > 1) {
8860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final int exactSection = MathUtils.constrain(
8870ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    (int) (position * sectionCount), 0, sectionCount - 1);
8880ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            int targetSection = exactSection;
8890ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            int targetIndex = mSectionIndexer.getPositionForSection(targetSection);
8900ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            sectionIndex = targetSection;
8910ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
8929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Given the expected section and index, the following code will
8939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // try to account for missing sections (no names starting with..)
8949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // It will compute the scroll space of surrounding empty sections
8959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // and interpolate the currently visible letter's range across the
8969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // available space, so that there is always some list movement while
8979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // the user moves the thumb.
8989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int nextIndex = count;
8990ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            int prevIndex = targetIndex;
9000ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            int prevSection = targetSection;
9010ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            int nextSection = targetSection + 1;
9020ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
9039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Assume the next section is unique
9040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            if (targetSection < sectionCount - 1) {
9050ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                nextIndex = mSectionIndexer.getPositionForSection(targetSection + 1);
9069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
907e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
9089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Find the previous index if we're slicing the previous section
9090ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            if (nextIndex == targetIndex) {
9109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Non-existent letter
9110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                while (targetSection > 0) {
9120ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    targetSection--;
9130ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    prevIndex = mSectionIndexer.getPositionForSection(targetSection);
9140ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    if (prevIndex != targetIndex) {
9150ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        prevSection = targetSection;
9160ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        sectionIndex = targetSection;
917b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project                        break;
9180ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    } else if (targetSection == 0) {
919b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project                        // When section reaches 0 here, sectionIndex must follow it.
920b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project                        // Assuming mSectionIndexer.getPositionForSection(0) == 0.
921b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project                        sectionIndex = 0;
922b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project                        break;
923b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project                    }
9249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
9259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9260ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
9279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Find the next index, in case the assumed next index is not
928e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette            // unique. For instance, if there is no P, then request for P's
9299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // position actually returns Q's. So we need to look ahead to make
930e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette            // sure that there is really a Q at Q's position. If not, move
9319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // further down...
9329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int nextNextSection = nextSection + 1;
9330ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            while (nextNextSection < sectionCount &&
9349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mSectionIndexer.getPositionForSection(nextNextSection) == nextIndex) {
9359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                nextNextSection++;
9369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                nextSection++;
9379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9380ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
9399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Compute the beginning and ending scroll range percentage of the
9400ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            // currently visible section. This could be equal to or greater than
9410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            // (1 / nSections). If the target position is near the previous
9420ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            // position, snap to the previous position.
9430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final float prevPosition = (float) prevSection / sectionCount;
9440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final float nextPosition = (float) nextSection / sectionCount;
9450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final float snapThreshold = (count == 0) ? Float.MAX_VALUE : .125f / count;
9460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            if (prevSection == exactSection && position - prevPosition < snapThreshold) {
9470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                targetIndex = prevIndex;
9489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
9490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                targetIndex = prevIndex + (int) ((nextIndex - prevIndex) * (position - prevPosition)
9500ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    / (nextPosition - prevPosition));
9519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
9530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            // Clamp to valid positions.
9540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            targetIndex = MathUtils.constrain(targetIndex, 0, count - 1);
955e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
9569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mList instanceof ExpandableListView) {
9570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                final ExpandableListView expList = (ExpandableListView) mList;
9589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                expList.setSelectionFromTop(expList.getFlatListPosition(
9590ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        ExpandableListView.getPackedPositionForGroup(targetIndex + mHeaderCount)),
9600ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        0);
9619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (mList instanceof ListView) {
9620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                ((ListView) mList).setSelectionFromTop(targetIndex + mHeaderCount, 0);
9639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
9640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                mList.setSelection(targetIndex + mHeaderCount);
9659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
9670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final int index = MathUtils.constrain((int) (position * count), 0, count - 1);
9687ee1ff1450a3ff83b6b8663bd7f44dc6f30775afAdam Powell
9699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mList instanceof ExpandableListView) {
9709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ExpandableListView expList = (ExpandableListView) mList;
9719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                expList.setSelectionFromTop(expList.getFlatListPosition(
9720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        ExpandableListView.getPackedPositionForGroup(index + mHeaderCount)), 0);
9739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (mList instanceof ListView) {
9740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                ((ListView)mList).setSelectionFromTop(index + mHeaderCount, 0);
9759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
9760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                mList.setSelection(index + mHeaderCount);
9779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9780ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
9799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sectionIndex = -1;
9809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9826b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        if (mCurrentSection != sectionIndex) {
9836b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            mCurrentSection = sectionIndex;
9846b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette
9856b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            if (transitionPreviewLayout(sectionIndex)) {
9860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                transitionToDragging();
9876b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            } else {
9886b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette                transitionToVisible();
9890ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            }
9909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
9946b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette     * Transitions the preview text to a new section. Handles animation,
9956b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette     * measurement, and layout. If the new preview text is empty, returns false.
9960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *
9976b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette     * @param sectionIndex The section index to which the preview should
9986b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette     *            transition.
9996b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette     * @return False if the new preview text is empty.
10000ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
10016b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette    private boolean transitionPreviewLayout(int sectionIndex) {
10026b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        final Object[] sections = mSections;
10036b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        String text = null;
10046b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        if (sections != null && sectionIndex >= 0 && sectionIndex < sections.length) {
10056b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            final Object section = sections[sectionIndex];
10066b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            if (section != null) {
10076b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette                text = section.toString();
10086b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            }
10096b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        }
10106b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette
10110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Rect bounds = mTempBounds;
10120ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final ImageView preview = mPreviewImage;
10130ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final TextView showing;
10140ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final TextView target;
10150ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mShowingPrimary) {
10160ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            showing = mPrimaryText;
10170ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            target = mSecondaryText;
10180ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else {
10190ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            showing = mSecondaryText;
10200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            target = mPrimaryText;
10210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
10220ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
10230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Set and layout target immediately.
10240ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        target.setText(text);
10250ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        measurePreview(target, bounds);
10260ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        applyLayout(target, bounds);
10270ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
10280ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mPreviewAnimation != null) {
10290ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            mPreviewAnimation.cancel();
10300ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
10310ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
10320ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Cross-fade preview text.
10330ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator showTarget = animateAlpha(target, 1f).setDuration(DURATION_CROSS_FADE);
10340ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator hideShowing = animateAlpha(showing, 0f).setDuration(DURATION_CROSS_FADE);
10350ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        hideShowing.addListener(mSwitchPrimaryListener);
10360ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
10370ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Apply preview image padding and animate bounds, if necessary.
10380ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        bounds.left -= mPreviewImage.getPaddingLeft();
10390ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        bounds.top -= mPreviewImage.getPaddingTop();
10400ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        bounds.right += mPreviewImage.getPaddingRight();
10410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        bounds.bottom += mPreviewImage.getPaddingBottom();
10420ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final Animator resizePreview = animateBounds(preview, bounds);
10430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        resizePreview.setDuration(DURATION_RESIZE);
10440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
10450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mPreviewAnimation = new AnimatorSet();
10460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final AnimatorSet.Builder builder = mPreviewAnimation.play(hideShowing).with(showTarget);
10470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        builder.with(resizePreview);
10480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
10490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // The current preview size is unaffected by hidden or showing. It's
10500ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // used to set starting scales for things that need to be scaled down.
10510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int previewWidth = preview.getWidth() - preview.getPaddingLeft()
10520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                - preview.getPaddingRight();
10530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
10540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // If target is too large, shrink it immediately to fit and expand to
10550ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // target size. Otherwise, start at target size.
10560ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int targetWidth = target.getWidth();
10570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (targetWidth > previewWidth) {
10580ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            target.setScaleX((float) previewWidth / targetWidth);
10590ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final Animator scaleAnim = animateScaleX(target, 1f).setDuration(DURATION_RESIZE);
10600ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            builder.with(scaleAnim);
10610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else {
10620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            target.setScaleX(1f);
10630ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
10640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
10650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // If showing is larger than target, shrink to target size.
10660ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final int showingWidth = showing.getWidth();
10670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (showingWidth > targetWidth) {
10680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final float scale = (float) targetWidth / showingWidth;
10690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final Animator scaleAnim = animateScaleX(showing, scale).setDuration(DURATION_RESIZE);
10700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            builder.with(scaleAnim);
10710ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
10720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
10730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mPreviewAnimation.start();
10746b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette
10756b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        return (text != null && text.length() > 0);
10760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
10770ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
10780ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
10790ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Positions the thumb and preview widgets.
10800ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *
10810ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param position The position, between 0 and 1, along the track at which
10820ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *            to place the thumb.
10830ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
10840ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void setThumbPos(float position) {
1085fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final Rect container = mContainerRect;
1086fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int top = container.top;
1087fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int bottom = container.bottom;
1088fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette
1089fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final ImageView trackImage = mTrackImage;
1090fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final ImageView thumbImage = mThumbImage;
1091fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final float min = trackImage.getTop();
1092fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final float max = trackImage.getBottom();
10930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float offset = min;
10940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float range = max - min;
10950ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float thumbMiddle = position * range + offset;
1096fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        thumbImage.setTranslationY(thumbMiddle - thumbImage.getHeight() / 2);
10970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
10980ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // Center the preview on the thumb, constrained to the list bounds.
1099fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final ImageView previewImage = mPreviewImage;
1100fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final float previewHalfHeight = previewImage.getHeight() / 2f;
11010ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float minP = top + previewHalfHeight;
11020ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float maxP = bottom - previewHalfHeight;
11030ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float previewMiddle = MathUtils.constrain(thumbMiddle, minP, maxP);
11040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float previewTop = previewMiddle - previewHalfHeight;
1105fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        previewImage.setTranslationY(previewTop);
11060ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
11070ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mPrimaryText.setTranslationY(previewTop);
11080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mSecondaryText.setTranslationY(previewTop);
11090ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
11100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
11110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private float getPosFromMotionEvent(float y) {
1112fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final Rect container = mContainerRect;
1113fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int top = container.top;
1114fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final int bottom = container.bottom;
11150ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1116fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final ImageView trackImage = mTrackImage;
1117fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final float min = trackImage.getTop();
1118fb66415655c288be2ac2078cd0a5e20c7040266eAlan Viverette        final float max = trackImage.getBottom();
11190ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float offset = min;
11200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final float range = max - min;
11210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
11220ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // If the list is the same height as the thumbnail or shorter,
11230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        // effectively disable scrolling.
11240ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (range <= 0) {
11250ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            return 0f;
11260ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
11270ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
11280ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        return MathUtils.constrain((y - offset) / range, 0f, 1f);
11290ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
11300ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
11310ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private float getPosFromItemCount(
11320ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            int firstVisibleItem, int visibleItemCount, int totalItemCount) {
113335948b7ed502d3f126acc8f2d61e099fa5deb0c7Adam Powell        if (mSectionIndexer == null || mListAdapter == null) {
113432c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell            getSectionsFromIndexer();
113532c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell        }
11360ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
11370ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final boolean hasSections = mSectionIndexer != null && mSections != null
11380ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                && mSections.length > 0;
11390ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (!hasSections || !mMatchDragPosition) {
11406b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            return (float) firstVisibleItem / (totalItemCount - visibleItemCount);
114132c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell        }
114232c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell
11436b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        // Ignore headers.
11440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        firstVisibleItem -= mHeaderCount;
114532c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell        if (firstVisibleItem < 0) {
114632c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell            return 0;
114732c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell        }
11480ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        totalItemCount -= mHeaderCount;
114932c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell
11506b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        // Hidden portion of the first visible row.
11516b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        final View child = mList.getChildAt(0);
11526b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        final float incrementalPos;
11536b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        if (child == null || child.getHeight() == 0) {
11546b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            incrementalPos = 0;
11556b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        } else {
11566b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            incrementalPos = (float) (mList.getPaddingTop() - child.getTop()) / child.getHeight();
11576b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        }
11586b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette
11596b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        // Number of rows in this section.
116032c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell        final int section = mSectionIndexer.getSectionForPosition(firstVisibleItem);
116132c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell        final int sectionPos = mSectionIndexer.getPositionForSection(section);
1162f49971ec06e65ee2a0f0fd48a615b89c677086e7Adam Powell        final int sectionCount = mSections.length;
11636b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        final int positionsInSection;
11646b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        if (section < sectionCount - 1) {
1165414b02380d73a4ced03b1b51233ce9b339fb52cfJean-Baptiste Queru            final int nextSectionPos;
1166414b02380d73a4ced03b1b51233ce9b339fb52cfJean-Baptiste Queru            if (section + 1 < sectionCount) {
1167414b02380d73a4ced03b1b51233ce9b339fb52cfJean-Baptiste Queru                nextSectionPos = mSectionIndexer.getPositionForSection(section + 1);
1168414b02380d73a4ced03b1b51233ce9b339fb52cfJean-Baptiste Queru            } else {
1169414b02380d73a4ced03b1b51233ce9b339fb52cfJean-Baptiste Queru                nextSectionPos = totalItemCount - 1;
1170414b02380d73a4ced03b1b51233ce9b339fb52cfJean-Baptiste Queru            }
11716b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            positionsInSection = nextSectionPos - sectionPos;
11726b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        } else {
11736b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            positionsInSection = totalItemCount - sectionPos;
11746b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        }
11756b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette
11766b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        // Position within this section.
11776b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        final float posWithinSection;
11786b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        if (positionsInSection == 0) {
11796b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            posWithinSection = 0;
11806b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        } else {
11816b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette            posWithinSection = (firstVisibleItem + incrementalPos - sectionPos)
11826b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette                    / positionsInSection;
11836b40cc7f8a01ed31202aaf05c10bfac42ee87539Alan Viverette        }
118432c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell
11850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        return (section + posWithinSection) / sectionCount;
118632c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell    }
118732c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell
11880ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
11890ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Cancels an ongoing fling event by injecting a
11900ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * {@link MotionEvent#ACTION_CANCEL} into the host view.
11910ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
11929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void cancelFling() {
11930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final MotionEvent cancelFling = MotionEvent.obtain(
11940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                0, 0, MotionEvent.ACTION_CANCEL, 0, 0, 0);
11959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mList.onTouchEvent(cancelFling);
11969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        cancelFling.recycle();
11979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1198e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
11990ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
12000ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Cancels a pending drag.
12010ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *
12020ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @see #startPendingDrag()
12030ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
12040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void cancelPendingDrag() {
1205af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        mList.removeCallbacks(mDeferStartDrag);
12060ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mHasPendingDrag = false;
1207af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell    }
1208af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
12090ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
12100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Delays dragging until after the framework has determined that the user is
12110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * scrolling, rather than tapping.
12120ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
12130ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void startPendingDrag() {
12140ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mHasPendingDrag = true;
12150ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        mList.postDelayed(mDeferStartDrag, TAP_TIMEOUT);
1216af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell    }
1217af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
12180ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private void beginDrag() {
1219af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        setState(STATE_DRAGGING);
12200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1221af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        if (mListAdapter == null && mList != null) {
1222af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell            getSectionsFromIndexer();
1223af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        }
12240ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1225af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        if (mList != null) {
1226af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell            mList.requestDisallowInterceptTouchEvent(true);
1227af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell            mList.reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
1228af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        }
1229af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
1230af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        cancelFling();
1231af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell    }
1232af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
12330ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    public boolean onInterceptTouchEvent(MotionEvent ev) {
1234b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        if (!isEnabled()) {
1235447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette            return false;
1236447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        }
1237447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette
1238af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell        switch (ev.getActionMasked()) {
1239af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell            case MotionEvent.ACTION_DOWN:
12400ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                if (isPointInside(ev.getX(), ev.getY())) {
12410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // If the parent has requested that its children delay
12420ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // pressed state (e.g. is a scrolling container) then we
12430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // need to allow the parent time to decide whether it wants
12440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // to intercept events. If it does, we will receive a CANCEL
12450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // event.
1246b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                    if (!mList.isInScrollingContainer()) {
1247b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                        beginDrag();
1248b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                        return true;
1249af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                    }
12500ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1251b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                    mInitialTouchY = ev.getY();
1252b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                    startPendingDrag();
1253b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                }
1254b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                break;
1255b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette            case MotionEvent.ACTION_MOVE:
1256b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                if (!isPointInside(ev.getX(), ev.getY())) {
1257b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette                    cancelPendingDrag();
1258af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                }
1259af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                break;
1260af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell            case MotionEvent.ACTION_UP:
1261af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell            case MotionEvent.ACTION_CANCEL:
1262af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                cancelPendingDrag();
1263af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                break;
12649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
12669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return false;
12679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1269a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette    public boolean onInterceptHoverEvent(MotionEvent ev) {
1270b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        if (!isEnabled()) {
1271a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette            return false;
1272a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette        }
1273a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette
1274a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette        final int actionMasked = ev.getActionMasked();
1275a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette        if ((actionMasked == MotionEvent.ACTION_HOVER_ENTER
1276a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette                || actionMasked == MotionEvent.ACTION_HOVER_MOVE) && mState == STATE_NONE
1277a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette                && isPointInside(ev.getX(), ev.getY())) {
1278a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette            setState(STATE_VISIBLE);
1279a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette            postAutoHide();
1280a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette        }
1281a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette
1282a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette        return false;
1283a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette    }
1284a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette
12850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    public boolean onTouchEvent(MotionEvent me) {
1286b9f2722f6f32b275731fc7e4aeb367cdf9c30dd3Alan Viverette        if (!isEnabled()) {
1287447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette            return false;
1288447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette        }
1289447cdf2c069a05d9f8b5aaa4269be43e8f5e1d89Alan Viverette
12900ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        switch (me.getActionMasked()) {
12910ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            case MotionEvent.ACTION_UP: {
12920ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                if (mHasPendingDrag) {
12930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // Allow a tap to scroll.
12940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    beginDrag();
12959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    final float pos = getPosFromMotionEvent(me.getY());
12970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    setThumbPos(pos);
12980ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    scrollTo(pos);
12990ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
13000ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    cancelPendingDrag();
13010ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // Will hit the STATE_DRAGGING check below
1302d43fe07852a23c78da3b51b45469687c20be919aMarco Nelissen                }
13030ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
13040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                if (mState == STATE_DRAGGING) {
13050ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    if (mList != null) {
13060ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        // ViewGroup does the right thing already, but there might
13070ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        // be other classes that don't properly reset on touch-up,
13080ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        // so do this explicitly just in case.
13090ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        mList.requestDisallowInterceptTouchEvent(false);
13100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        mList.reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
13110ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    }
13120ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
13130ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    setState(STATE_VISIBLE);
1314a709b37eab535180f41d2bb9d029244aa84ee1f6Alan Viverette                    postAutoHide();
13150ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
13160ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    return true;
131720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell                }
13180ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            } break;
131920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
13200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            case MotionEvent.ACTION_MOVE: {
13210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                if (mHasPendingDrag && Math.abs(me.getY() - mInitialTouchY) > mScaledTouchSlop) {
1322af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                    setState(STATE_DRAGGING);
13230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1324af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                    if (mListAdapter == null && mList != null) {
1325af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                        getSectionsFromIndexer();
1326af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                    }
13270ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
1328af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                    if (mList != null) {
1329af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                        mList.requestDisallowInterceptTouchEvent(true);
1330af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                        mList.reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
1331af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                    }
1332af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell
1333af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                    cancelFling();
1334af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                    cancelPendingDrag();
1335af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                    // Will hit the STATE_DRAGGING check below
1336af5280cc71e155758c2f6625eb999cabb37e937fAdam Powell                }
13370ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
13380ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                if (mState == STATE_DRAGGING) {
13390ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // TODO: Ignore jitter.
13400ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    final float pos = getPosFromMotionEvent(me.getY());
13410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    setThumbPos(pos);
13420ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
13430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    // If the previous scrollTo is still pending
13440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    if (mScrollCompleted) {
13450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                        scrollTo(pos);
13460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                    }
13470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
13489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return true;
13499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
13500ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            } break;
13510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
13520ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            case MotionEvent.ACTION_CANCEL: {
13530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                cancelPendingDrag();
13540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            } break;
13559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13560ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
13579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return false;
13589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1359d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
13600ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
13610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Returns whether a coordinate is inside the scroller's activation area. If
13620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * there is a track image, touching anywhere within the thumb-width of the
13630ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * track activates scrolling. Otherwise, the user has to touch inside thumb
13640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * itself.
13650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *
13660ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param x The x-coordinate.
13670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param y The y-coordinate.
13680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @return Whether the coordinate is inside the scroller's activation area.
13690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
13700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private boolean isPointInside(float x, float y) {
13710ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        return isPointInsideX(x) && (mHasTrackImage || isPointInsideY(y));
13720ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
13730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
13740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private boolean isPointInsideX(float x) {
13750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        if (mLayoutFromRight) {
13760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            return x >= mThumbImage.getLeft();
13770ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        } else {
13780ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            return x <= mThumbImage.getRight();
13790ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
13800ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
13810ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
13820ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private boolean isPointInsideY(float y) {
13830ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        return y >= mThumbImage.getTop() && y <= mThumbImage.getBottom();
13840ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
13850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
13860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
13870ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Constructs an animator for the specified property on a group of views.
13880ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * See {@link ObjectAnimator#ofFloat(Object, String, float...)} for
13890ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * implementation details.
13900ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     *
13910ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param property The property being animated.
13920ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param value The value to which that property should animate.
13930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @param views The target views to animate.
13940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * @return An animator for all the specified views.
13950ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
13960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static Animator groupAnimatorOfFloat(
13970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            Property<View, Float> property, float value, View... views) {
13980ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        AnimatorSet animSet = new AnimatorSet();
13990ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        AnimatorSet.Builder builder = null;
14000ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
14010ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        for (int i = views.length - 1; i >= 0; i--) {
14020ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            final Animator anim = ObjectAnimator.ofFloat(views[i], property, value);
14030ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            if (builder == null) {
14040ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                builder = animSet.play(anim);
14050ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            } else {
14060ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette                builder.with(anim);
14070ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            }
140820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell        }
140920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell
14100ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        return animSet;
141182f3495b146b267f3786997752cef25310176349Romain Guy    }
141282f3495b146b267f3786997752cef25310176349Romain Guy
14130ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
14140ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Returns an animator for the view's scaleX value.
14150ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
14160ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static Animator animateScaleX(View v, float target) {
14170ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        return ObjectAnimator.ofFloat(v, View.SCALE_X, target);
14180ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
1419e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
14200ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
14210ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Returns an animator for the view's alpha value.
14220ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
14230ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static Animator animateAlpha(View v, float alpha) {
14240ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        return ObjectAnimator.ofFloat(v, View.ALPHA, alpha);
14250ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    }
1426e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
14270ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
14280ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * A Property wrapper around the <code>left</code> functionality handled by the
14290ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * {@link View#setLeft(int)} and {@link View#getLeft()} methods.
14300ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
14310ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static Property<View, Integer> LEFT = new IntProperty<View>("left") {
14320ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
14330ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public void setValue(View object, int value) {
14340ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            object.setLeft(value);
14359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1436e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
14370ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
14380ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public Integer get(View object) {
14390ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            return object.getLeft();
14409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14410ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    };
1442e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
14430ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
14440ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * A Property wrapper around the <code>top</code> functionality handled by the
14450ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * {@link View#setTop(int)} and {@link View#getTop()} methods.
14460ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
14470ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static Property<View, Integer> TOP = new IntProperty<View>("top") {
1448e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette        @Override
14490ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public void setValue(View object, int value) {
14500ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            object.setTop(value);
14510ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
1452e918a48df92fd16375c46624df74e6fd4609e016Alan Viverette
14530ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
14540ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public Integer get(View object) {
14550ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            return object.getTop();
14560ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
14570ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    };
14580ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
14590ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
14600ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * A Property wrapper around the <code>right</code> functionality handled by the
14610ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * {@link View#setRight(int)} and {@link View#getRight()} methods.
14620ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
14630ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static Property<View, Integer> RIGHT = new IntProperty<View>("right") {
14640ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
14650ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public void setValue(View object, int value) {
14660ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            object.setRight(value);
14670ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
14680ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
14690ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
14700ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public Integer get(View object) {
14710ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            return object.getRight();
14729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14730ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    };
14740ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
14750ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
14760ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * A Property wrapper around the <code>bottom</code> functionality handled by the
14770ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * {@link View#setBottom(int)} and {@link View#getBottom()} methods.
14780ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
14790ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static Property<View, Integer> BOTTOM = new IntProperty<View>("bottom") {
14800ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
14810ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public void setValue(View object, int value) {
14820ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            object.setBottom(value);
14830ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
14840ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
14850ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        @Override
14860ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        public Integer get(View object) {
14870ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette            return object.getBottom();
14880ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        }
14890ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    };
14900ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette
14910ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    /**
14920ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     * Returns an animator for the view's bounds.
14930ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette     */
14940ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette    private static Animator animateBounds(View v, Rect bounds) {
14950ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final PropertyValuesHolder left = PropertyValuesHolder.ofInt(LEFT, bounds.left);
14960ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final PropertyValuesHolder top = PropertyValuesHolder.ofInt(TOP, bounds.top);
14970ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final PropertyValuesHolder right = PropertyValuesHolder.ofInt(RIGHT, bounds.right);
14980ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        final PropertyValuesHolder bottom = PropertyValuesHolder.ofInt(BOTTOM, bounds.bottom);
14990ebe81e8b1f2b9db8d41b72a6dae8d6848b51cc5Alan Viverette        return ObjectAnimator.ofPropertyValuesHolder(v, left, top, right, bottom);
15009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
15019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
1502