AdapterViewAnimator.java revision 78db1aa9118edd71c2da28a2c23a0d875d1a707a
13db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen/*
23db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * Copyright (C) 2010 The Android Open Source Project
33db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen *
43db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * Licensed under the Apache License, Version 2.0 (the "License");
53db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * you may not use this file except in compliance with the License.
63db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * You may obtain a copy of the License at
73db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen *
83db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen *      http://www.apache.org/licenses/LICENSE-2.0
93db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen *
103db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * Unless required by applicable law or agreed to in writing, software
113db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * distributed under the License is distributed on an "AS IS" BASIS,
123db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * See the License for the specific language governing permissions and
143db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * limitations under the License.
153db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen */
163db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
173db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohenpackage android.widget;
183db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
1944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenimport java.util.ArrayList;
201b065cd1401253f999caa5d0ac12909407cef00eAdam Cohenimport java.util.HashMap;
2144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
22ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohenimport android.animation.AnimatorInflater;
23a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haaseimport android.animation.ObjectAnimator;
243db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohenimport android.content.Context;
253db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohenimport android.content.Intent;
263db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohenimport android.content.res.TypedArray;
273db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohenimport android.os.Handler;
283db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohenimport android.os.Looper;
29b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohenimport android.os.Parcel;
30b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohenimport android.os.Parcelable;
313db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohenimport android.util.AttributeSet;
32a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohenimport android.view.MotionEvent;
333db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohenimport android.view.View;
34a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohenimport android.view.ViewConfiguration;
3544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenimport android.view.ViewGroup;
363db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
373db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen/**
383db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * Base class for a {@link AdapterView} that will perform animations
393db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * when switching between its views.
403db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen *
413db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @attr ref android.R.styleable#AdapterViewAnimator_inAnimation
423db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @attr ref android.R.styleable#AdapterViewAnimator_outAnimation
433db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @attr ref android.R.styleable#AdapterViewAnimator_animateFirstView
441b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen * @attr ref android.R.styleable#AdapterViewAnimator_loopViews
453db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen */
4644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenpublic abstract class AdapterViewAnimator extends AdapterView<Adapter>
47a02fdf1ba03fad71cc80a89dfc74b17456d5b4a5Adam Cohen        implements RemoteViewsAdapter.RemoteAdapterConnectionCallback, Advanceable {
483db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    private static final String TAG = "RemoteViewAnimator";
493db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
5044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
5144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * The index of the current child, which appears anywhere from the beginning
5244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * to the end of the current set of children, as specified by {@link #mActiveOffset}
5344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
543db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    int mWhichChild = 0;
5544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
5644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
5744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * Whether or not the first view(s) should be animated in
5844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
593db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    boolean mAnimateFirstTime = true;
603db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
6144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
6244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *  Represents where the in the current window of
6344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *  views the current <code>mDisplayedChild</code> sits
6444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
6544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    int mActiveOffset = 0;
6644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
6744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
6844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * The number of views that the {@link AdapterViewAnimator} keeps as children at any
6944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * given time (not counting views that are pending removal, see {@link #mPreviousViews}).
7044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
7196d8d56302da81b24333b204e6d7f15064538036Adam Cohen    int mMaxNumActiveViews = 1;
7244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
7344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
741b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen     * Map of the children of the {@link AdapterViewAnimator}.
7544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
7696d8d56302da81b24333b204e6d7f15064538036Adam Cohen    HashMap<Integer, ViewAndIndex> mViewsMap = new HashMap<Integer, ViewAndIndex>();
7744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
7844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
7944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * List of views pending removal from the {@link AdapterViewAnimator}
8044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
811b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen    ArrayList<Integer> mPreviousViews;
8244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
8344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
8444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * The index, relative to the adapter, of the beginning of the window of views
8544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
8644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    int mCurrentWindowStart = 0;
8744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
8844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
8944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * The index, relative to the adapter, of the end of the window of views
9044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
9144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    int mCurrentWindowEnd = -1;
9244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
9344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
9444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * The same as {@link #mCurrentWindowStart}, except when the we have bounded
9544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * {@link #mCurrentWindowStart} to be non-negative
9644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
9744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    int mCurrentWindowStartUnbounded = 0;
9844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
9944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
10044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * Handler to post events to the main thread
10144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
10244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    Handler mMainQueue;
10344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
10444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
10544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * Listens for data changes from the adapter
10644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
1073db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    AdapterDataSetObserver mDataSetObserver;
1083db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
10944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
11044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * The {@link Adapter} for this {@link AdapterViewAnimator}
11144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
11244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    Adapter mAdapter;
11344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
11444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
11544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * The {@link RemoteViewsAdapter} for this {@link AdapterViewAnimator}
11644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
11744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    RemoteViewsAdapter mRemoteViewsAdapter;
11844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
11944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
12044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * Specifies whether this is the first time the animator is showing views
12144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
12244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    boolean mFirstTime = true;
1233db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
12444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
125b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen     * Specifies if the animator should wrap from 0 to the end and vice versa
126b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen     * or have hard boundaries at the beginning and end
127b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen     */
1281b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen    boolean mLoopViews = true;
129b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen
130b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen    /**
131839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen     * The width and height of some child, used as a size reference in-case our
132839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen     * dimensions are unspecified by the parent.
133839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen     */
134839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen    int mReferenceChildWidth = -1;
135839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen    int mReferenceChildHeight = -1;
136839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen
137839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen    /**
138ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen     * In and out animations.
13944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
1402794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase    ObjectAnimator mInAnimation;
1412794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase    ObjectAnimator mOutAnimation;
142ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen
143a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen    /**
144a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen     * Current touch state.
145a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen     */
146a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen    private int mTouchMode = TOUCH_MODE_NONE;
147a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen
148a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen    /**
149a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen     * Private touch states.
150a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen     */
151a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen    static final int TOUCH_MODE_NONE = 0;
152a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen    static final int TOUCH_MODE_DOWN_IN_CURRENT_VIEW = 1;
153a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen    static final int TOUCH_MODE_HANDLED = 2;
154a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen
155a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen    private Runnable mPendingCheckForTap;
156a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen
157ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen    private static final int DEFAULT_ANIMATION_DURATION = 200;
158ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen
1593db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public AdapterViewAnimator(Context context) {
1603db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        super(context);
1615b53f9186e7812c93bc578d18e92cb123481fcbcRomain Guy        initViewAnimator();
1623db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
1633db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
1643db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public AdapterViewAnimator(Context context, AttributeSet attrs) {
1653db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        super(context, attrs);
1663db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
16744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        TypedArray a = context.obtainStyledAttributes(attrs,
1681b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                com.android.internal.R.styleable.AdapterViewAnimator);
16944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        int resource = a.getResourceId(
1701b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                com.android.internal.R.styleable.AdapterViewAnimator_inAnimation, 0);
1713db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        if (resource > 0) {
1723db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            setInAnimation(context, resource);
173ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen        } else {
174ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen            setInAnimation(getDefaultInAnimation());
1753db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        }
1763db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
1771b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        resource = a.getResourceId(com.android.internal.R.styleable.AdapterViewAnimator_outAnimation, 0);
1783db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        if (resource > 0) {
1793db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            setOutAnimation(context, resource);
180ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen        } else {
181ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen            setOutAnimation(getDefaultOutAnimation());
1823db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        }
1833db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
18444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        boolean flag = a.getBoolean(
1851b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                com.android.internal.R.styleable.AdapterViewAnimator_animateFirstView, true);
1863db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        setAnimateFirstView(flag);
1873db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
1881b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        mLoopViews = a.getBoolean(
1891b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                com.android.internal.R.styleable.AdapterViewAnimator_loopViews, false);
1901b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen
1913db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        a.recycle();
1923db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
1935b53f9186e7812c93bc578d18e92cb123481fcbcRomain Guy        initViewAnimator();
1943db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
1953db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
1963db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
1973db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Initialize this {@link AdapterViewAnimator}
1983db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
1995b53f9186e7812c93bc578d18e92cb123481fcbcRomain Guy    private void initViewAnimator() {
2003db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        mMainQueue = new Handler(Looper.myLooper());
2011b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        mPreviousViews = new ArrayList<Integer>();
20244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    }
20344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
20496d8d56302da81b24333b204e6d7f15064538036Adam Cohen    class ViewAndIndex {
2051b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        ViewAndIndex(View v, int i) {
2061b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen            view = v;
2071b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen            index = i;
2081b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        }
2091b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        View view;
2101b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        int index;
2111b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen    }
2121b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen
21344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
21444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * This method is used by subclasses to configure the animator to display the
21544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * desired number of views, and specify the offset
21644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *
21744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * @param numVisibleViews The number of views the animator keeps in the {@link ViewGroup}
2185b53f9186e7812c93bc578d18e92cb123481fcbcRomain Guy     * @param activeOffset This parameter specifies where the current index ({@link #mWhichChild})
21944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *        sits within the window. For example if activeOffset is 1, and numVisibleViews is 3,
2205b53f9186e7812c93bc578d18e92cb123481fcbcRomain Guy     *        and {@link #setDisplayedChild(int)} is called with 10, then the effective window will
2215b53f9186e7812c93bc578d18e92cb123481fcbcRomain Guy     *        be the indexes 9, 10, and 11. In the same example, if activeOffset were 0, then the
22244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *        window would instead contain indexes 10, 11 and 12.
223b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen     * @param shouldLoop If the animator is show view 0, and setPrevious() is called, do we
224b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen     *        we loop back to the end, or do we do nothing
22544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
2261b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen     void configureViewAnimator(int numVisibleViews, int activeOffset) {
22744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        if (activeOffset > numVisibleViews - 1) {
22844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // Throw an exception here.
22944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
23096d8d56302da81b24333b204e6d7f15064538036Adam Cohen        mMaxNumActiveViews = numVisibleViews;
23144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mActiveOffset = activeOffset;
23244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mPreviousViews.clear();
2331b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        mViewsMap.clear();
23444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        removeAllViewsInLayout();
23544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mCurrentWindowStart = 0;
23644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mCurrentWindowEnd = -1;
23744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    }
23844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
23944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
24044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * This class should be overridden by subclasses to customize view transitions within
24144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * the set of visible views
24244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *
24344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * @param fromIndex The relative index within the window that the view was in, -1 if it wasn't
24444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *        in the window
24544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * @param toIndex The relative index within the window that the view is going to, -1 if it is
24644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *        being removed
24744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * @param view The view that is being animated
24844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
24978db1aa9118edd71c2da28a2c23a0d875d1a707aAdam Cohen    void transformViewForTransition(int fromIndex, int toIndex, View view, boolean animate) {
25044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        if (fromIndex == -1) {
251ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen            mInAnimation.setTarget(view);
252ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen            mInAnimation.start();
25344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        } else if (toIndex == -1) {
254ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen            mOutAnimation.setTarget(view);
255ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen            mOutAnimation.start();
25644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
2573db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
2583db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
2592794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase    ObjectAnimator getDefaultInAnimation() {
2602794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase        ObjectAnimator anim = ObjectAnimator.ofFloat(null, "alpha", 0.0f, 1.0f);
2612794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase        anim.setDuration(DEFAULT_ANIMATION_DURATION);
2622794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase        return anim;
263ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen    }
264ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen
2652794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase    ObjectAnimator getDefaultOutAnimation() {
2662794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase        ObjectAnimator anim = ObjectAnimator.ofFloat(null, "alpha", 1.0f, 0.0f);
2672794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase        anim.setDuration(DEFAULT_ANIMATION_DURATION);
2682794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase        return anim;
269ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen    }
270ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen
2713db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
2723db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Sets which child view will be displayed.
2733db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
2743db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @param whichChild the index of the child view to display
2753db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
2763db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void setDisplayedChild(int whichChild) {
2773db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        if (mAdapter != null) {
2783db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            mWhichChild = whichChild;
27996d8d56302da81b24333b204e6d7f15064538036Adam Cohen            if (whichChild >= getWindowSize()) {
28096d8d56302da81b24333b204e6d7f15064538036Adam Cohen                mWhichChild = mLoopViews ? 0 : getWindowSize() - 1;
2813db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            } else if (whichChild < 0) {
28296d8d56302da81b24333b204e6d7f15064538036Adam Cohen                mWhichChild = mLoopViews ? getWindowSize() - 1 : 0;
2833db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            }
2843db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
2853db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            boolean hasFocus = getFocusedChild() != null;
2863db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            // This will clear old focus if we had it
2873db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            showOnly(mWhichChild);
2883db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            if (hasFocus) {
2893db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen                // Try to retake focus if we had it
2903db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen                requestFocus(FOCUS_FORWARD);
2913db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            }
2923db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        }
2933db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
2943db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
2953db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
29644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * To be overridden by subclasses. This method applies a view / index specific
29744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * transform to the child view.
29844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *
29944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * @param child
30044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * @param relativeIndex
30144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
30244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    void applyTransformForChildAtIndex(View child, int relativeIndex) {
30344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    }
30444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
30544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
3063db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Returns the index of the currently displayed child view.
3073db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
3083db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public int getDisplayedChild() {
3093db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        return mWhichChild;
3103db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
3113db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
3123db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
3133db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Manually shows the next child.
3143db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
3153db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void showNext() {
3163db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        setDisplayedChild(mWhichChild + 1);
3173db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
3183db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
3193db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
3203db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Manually shows the previous child.
3213db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
3223db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void showPrevious() {
3233db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        setDisplayedChild(mWhichChild - 1);
3243db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
3253db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
32696d8d56302da81b24333b204e6d7f15064538036Adam Cohen    int modulo(int pos, int size) {
3273042944c6ec68210ba1746540b53789e70d15ef4Adam Cohen        if (size > 0) {
3283042944c6ec68210ba1746540b53789e70d15ef4Adam Cohen            return (size + (pos % size)) % size;
3293042944c6ec68210ba1746540b53789e70d15ef4Adam Cohen        } else {
3303042944c6ec68210ba1746540b53789e70d15ef4Adam Cohen            return 0;
3313042944c6ec68210ba1746540b53789e70d15ef4Adam Cohen        }
3323db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
3333db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
33444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
33544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * Get the view at this index relative to the current window's start
33644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *
33744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * @param relativeIndex Position relative to the current window's start
33844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * @return View at this index, null if the index is outside the bounds
33944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
34044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    View getViewAtRelativeIndex(int relativeIndex) {
34196d8d56302da81b24333b204e6d7f15064538036Adam Cohen        if (relativeIndex >= 0 && relativeIndex <= getNumActiveViews() - 1 && mAdapter != null) {
34296d8d56302da81b24333b204e6d7f15064538036Adam Cohen            int i = modulo(mCurrentWindowStartUnbounded + relativeIndex, getWindowSize());
3436f279627cfa3286e6901a8dc2ed8361576ce226dAdam Cohen            if (mViewsMap.get(i) != null) {
3446f279627cfa3286e6901a8dc2ed8361576ce226dAdam Cohen                return mViewsMap.get(i).view;
3456f279627cfa3286e6901a8dc2ed8361576ce226dAdam Cohen            }
34644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
34744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        return null;
34844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    }
3493db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
35096d8d56302da81b24333b204e6d7f15064538036Adam Cohen    int getNumActiveViews() {
35196d8d56302da81b24333b204e6d7f15064538036Adam Cohen        if (mAdapter != null) {
352ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen            return Math.min(getCount() + 1, mMaxNumActiveViews);
35396d8d56302da81b24333b204e6d7f15064538036Adam Cohen        } else {
35496d8d56302da81b24333b204e6d7f15064538036Adam Cohen            return mMaxNumActiveViews;
35596d8d56302da81b24333b204e6d7f15064538036Adam Cohen        }
35696d8d56302da81b24333b204e6d7f15064538036Adam Cohen    }
35796d8d56302da81b24333b204e6d7f15064538036Adam Cohen
35896d8d56302da81b24333b204e6d7f15064538036Adam Cohen    int getWindowSize() {
35996d8d56302da81b24333b204e6d7f15064538036Adam Cohen        if (mAdapter != null) {
360ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen            int adapterCount = getCount();
36196d8d56302da81b24333b204e6d7f15064538036Adam Cohen            if (adapterCount <= getNumActiveViews() && mLoopViews) {
36296d8d56302da81b24333b204e6d7f15064538036Adam Cohen                return adapterCount*mMaxNumActiveViews;
36396d8d56302da81b24333b204e6d7f15064538036Adam Cohen            } else {
36496d8d56302da81b24333b204e6d7f15064538036Adam Cohen                return adapterCount;
36596d8d56302da81b24333b204e6d7f15064538036Adam Cohen            }
36696d8d56302da81b24333b204e6d7f15064538036Adam Cohen        } else {
36796d8d56302da81b24333b204e6d7f15064538036Adam Cohen            return 0;
36896d8d56302da81b24333b204e6d7f15064538036Adam Cohen        }
36996d8d56302da81b24333b204e6d7f15064538036Adam Cohen    }
37096d8d56302da81b24333b204e6d7f15064538036Adam Cohen
3719b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen    LayoutParams createOrReuseLayoutParams(View v) {
3725b53f9186e7812c93bc578d18e92cb123481fcbcRomain Guy        final ViewGroup.LayoutParams currentLp = v.getLayoutParams();
3739b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen        if (currentLp instanceof ViewGroup.LayoutParams) {
374b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen            LayoutParams lp = (LayoutParams) currentLp;
375b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen            return lp;
37644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
3779b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen        return new ViewGroup.LayoutParams(0, 0);
37844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    }
3793db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
3806364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung    void refreshChildren() {
381a9238c89a43500ed0bcdeaee182be08ff991c627Adam Cohen        if (mAdapter == null) return;
382bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen        for (int i = mCurrentWindowStart; i <= mCurrentWindowEnd; i++) {
383a9238c89a43500ed0bcdeaee182be08ff991c627Adam Cohen            int index = modulo(i, getWindowSize());
384bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen
385ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen            int adapterCount = getCount();
386bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen            // get the fresh child from the adapter
387ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen            final View updatedChild = mAdapter.getView(modulo(i, adapterCount), null, this);
388bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen
3891b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen            if (mViewsMap.containsKey(index)) {
390ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen                final FrameLayout fl = (FrameLayout) mViewsMap.get(index).view;
391bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen                // add the new child to the frame, if it exists
392bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen                if (updatedChild != null) {
393ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen                    // flush out the old child
394ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen                    fl.removeAllViewsInLayout();
395bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen                    fl.addView(updatedChild);
396bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen                }
397bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen            }
398bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen        }
399bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen    }
400bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen
401dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen    /**
402dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen     * This method can be overridden so that subclasses can provide a custom frame in which their
403dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen     * children can live. For example, StackView adds padding to its childrens' frames so as to
404dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen     * accomodate for the highlight effect.
405dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen     *
406dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen     * @return The FrameLayout into which children can be placed.
407dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen     */
408dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen    FrameLayout getFrameForChild() {
409dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen        return new FrameLayout(mContext);
410dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen    }
411dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen
412ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen    /**
413ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen     * Shows only the specified child. The other displays Views exit the screen,
414ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen     * optionally with the with the {@link #getOutAnimation() out animation} and
415ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen     * the specified child enters the screen, optionally with the
416ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen     * {@link #getInAnimation() in animation}.
417ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen     *
418ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen     * @param childIndex The index of the child to be shown.
419ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen     * @param animate Whether or not to use the in and out animations, defaults
420ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen     *            to true.
421ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen     */
422ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen    void showOnly(int childIndex, boolean animate) {
42344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        if (mAdapter == null) return;
424ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen        final int adapterCount = getCount();
4253042944c6ec68210ba1746540b53789e70d15ef4Adam Cohen        if (adapterCount == 0) return;
4263db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
42744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        for (int i = 0; i < mPreviousViews.size(); i++) {
4281b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen            View viewToRemove = mViewsMap.get(mPreviousViews.get(i)).view;
4291b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen            mViewsMap.remove(mPreviousViews.get(i));
43044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            viewToRemove.clearAnimation();
4313d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen            if (viewToRemove instanceof ViewGroup) {
4323d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen                ViewGroup vg = (ViewGroup) viewToRemove;
4333d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen                vg.removeAllViewsInLayout();
4343d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen            }
43544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // applyTransformForChildAtIndex here just allows for any cleanup
43644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // associated with this view that may need to be done by a subclass
43744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            applyTransformForChildAtIndex(viewToRemove, -1);
4383d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen
43944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            removeViewInLayout(viewToRemove);
44044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
44144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mPreviousViews.clear();
44244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        int newWindowStartUnbounded = childIndex - mActiveOffset;
44396d8d56302da81b24333b204e6d7f15064538036Adam Cohen        int newWindowEndUnbounded = newWindowStartUnbounded + getNumActiveViews() - 1;
44444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        int newWindowStart = Math.max(0, newWindowStartUnbounded);
4451b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        int newWindowEnd = Math.min(adapterCount - 1, newWindowEndUnbounded);
44644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
4471b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        if (mLoopViews) {
4481b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen            newWindowStart = newWindowStartUnbounded;
4491b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen            newWindowEnd = newWindowEndUnbounded;
4501b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        }
45196d8d56302da81b24333b204e6d7f15064538036Adam Cohen        int rangeStart = modulo(newWindowStart, getWindowSize());
45296d8d56302da81b24333b204e6d7f15064538036Adam Cohen        int rangeEnd = modulo(newWindowEnd, getWindowSize());
4531b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen
4541b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        boolean wrap = false;
4551b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        if (rangeStart > rangeEnd) {
4561b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen            wrap = true;
4571b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        }
4581b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen
4591b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        // This section clears out any items that are in our active views list
46044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        // but are outside the effective bounds of our window (this is becomes an issue
46144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        // at the extremities of the list, eg. where newWindowStartUnbounded < 0 or
462ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen        // newWindowEndUnbounded > adapterCount - 1
4631b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        for (Integer index : mViewsMap.keySet()) {
4641b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen            boolean remove = false;
4651b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen            if (!wrap && (index < rangeStart || index > rangeEnd)) {
4661b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                remove = true;
4671b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen            } else if (wrap && (index > rangeEnd && index < rangeStart)) {
4681b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                remove = true;
4691b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen            }
4701b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen
4711b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen            if (remove) {
4721b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                View previousView = mViewsMap.get(index).view;
4731b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                int oldRelativeIndex = mViewsMap.get(index).index;
4741b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen
4751b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                mPreviousViews.add(index);
47678db1aa9118edd71c2da28a2c23a0d875d1a707aAdam Cohen                transformViewForTransition(oldRelativeIndex, -1, previousView, animate);
4773db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            }
47844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
4793db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
48044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        // If the window has changed
48196d8d56302da81b24333b204e6d7f15064538036Adam Cohen        if (!(newWindowStart == mCurrentWindowStart && newWindowEnd == mCurrentWindowEnd &&
48296d8d56302da81b24333b204e6d7f15064538036Adam Cohen              newWindowStartUnbounded == mCurrentWindowStartUnbounded)) {
48344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // Run through the indices in the new range
48444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            for (int i = newWindowStart; i <= newWindowEnd; i++) {
48544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
48696d8d56302da81b24333b204e6d7f15064538036Adam Cohen                int index = modulo(i, getWindowSize());
4871b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                int oldRelativeIndex;
4881b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                if (mViewsMap.containsKey(index)) {
4891b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                    oldRelativeIndex = mViewsMap.get(index).index;
4901b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                } else {
4911b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                    oldRelativeIndex = -1;
4921b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                }
49344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                int newRelativeIndex = i - newWindowStartUnbounded;
49444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
49544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                // If this item is in the current window, great, we just need to apply
49644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                // the transform for it's new relative position in the window, and animate
49744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                // between it's current and new relative positions
4981b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                boolean inOldRange = mViewsMap.containsKey(index) && !mPreviousViews.contains(index);
4991b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen
5001b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                if (inOldRange) {
5011b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                    View view = mViewsMap.get(index).view;
5021b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                    mViewsMap.get(index).index = newRelativeIndex;
50344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    applyTransformForChildAtIndex(view, newRelativeIndex);
50478db1aa9118edd71c2da28a2c23a0d875d1a707aAdam Cohen                    transformViewForTransition(oldRelativeIndex, newRelativeIndex, view, animate);
50544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
5061b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                // Otherwise this view is new to the window
50744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                } else {
5081b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                    // Get the new view from the adapter, add it and apply any transform / animation
5091b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                    View newView = mAdapter.getView(modulo(i, adapterCount), null, this);
510bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen
511bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen                    // We wrap the new view in a FrameLayout so as to respect the contract
512bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen                    // with the adapter, that is, that we don't modify this view directly
513dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen                    FrameLayout fl = getFrameForChild();
514bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen
515bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen                    // If the view from the adapter is null, we still keep an empty frame in place
51644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    if (newView != null) {
517bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen                       fl.addView(newView);
51844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    }
5191b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                    mViewsMap.put(index, new ViewAndIndex(fl, newRelativeIndex));
520bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen                    addChild(fl);
521bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen                    applyTransformForChildAtIndex(fl, newRelativeIndex);
52278db1aa9118edd71c2da28a2c23a0d875d1a707aAdam Cohen                    transformViewForTransition(-1, newRelativeIndex, fl, animate);
5233db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen                }
5241b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                mViewsMap.get(index).view.bringToFront();
5253db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            }
52644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            mCurrentWindowStart = newWindowStart;
52744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            mCurrentWindowEnd = newWindowEnd;
52844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            mCurrentWindowStartUnbounded = newWindowStartUnbounded;
52944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
530ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen        requestLayout();
531ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen        invalidate();
5323db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
5333db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
534839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen    private void addChild(View child) {
535839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        addViewInLayout(child, -1, createOrReuseLayoutParams(child));
536839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen
537839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        // This code is used to obtain a reference width and height of a child in case we need
538839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        // to decide our own size. TODO: Do we want to update the size of the child that we're
539839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        // using for reference size? If so, when?
540839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        if (mReferenceChildWidth == -1 || mReferenceChildHeight == -1) {
541839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen            int measureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
542839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen            child.measure(measureSpec, measureSpec);
543839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen            mReferenceChildWidth = child.getMeasuredWidth();
544839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen            mReferenceChildHeight = child.getMeasuredHeight();
545839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        }
546839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen    }
547839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen
548a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen    void showTapFeedback(View v) {
549a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen        v.setPressed(true);
550a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen    }
551a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen
552a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen    void hideTapFeedback(View v) {
553a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen        v.setPressed(false);
554a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen    }
555a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen
556a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen    void cancelHandleClick() {
557a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen        View v = getCurrentView();
558a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen        if (v != null) {
559a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen            hideTapFeedback(v);
560a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen        }
561a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen        mTouchMode = TOUCH_MODE_NONE;
562a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen    }
563a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen
564a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen    final class CheckForTap implements Runnable {
565a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen        public void run() {
566a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen            if (mTouchMode == TOUCH_MODE_DOWN_IN_CURRENT_VIEW) {
567a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                View v = getCurrentView();
568a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                showTapFeedback(v);
569a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen            }
570a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen        }
571a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen    }
572a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen
573a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen    @Override
574a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen    public boolean onTouchEvent(MotionEvent ev) {
575a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen        int action = ev.getAction();
576a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen        boolean handled = false;
577a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen        switch (action) {
578a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen            case MotionEvent.ACTION_DOWN: {
579a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                View v = getCurrentView();
580a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                if (v != null) {
581a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                    if (isTransformedTouchPointInView(ev.getX(), ev.getY(), v, null)) {
582a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                        if (mPendingCheckForTap == null) {
583a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                            mPendingCheckForTap = new CheckForTap();
584a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                        }
585a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                        mTouchMode = TOUCH_MODE_DOWN_IN_CURRENT_VIEW;
586a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                        postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
587a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                    }
588a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                }
589a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                break;
590a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen            }
591a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen            case MotionEvent.ACTION_MOVE: break;
592a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen            case MotionEvent.ACTION_POINTER_UP: break;
593a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen            case MotionEvent.ACTION_UP: {
594a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                if (mTouchMode == TOUCH_MODE_DOWN_IN_CURRENT_VIEW) {
595a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                    final View v = getCurrentView();
596a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                    if (v != null) {
597a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                        if (isTransformedTouchPointInView(ev.getX(), ev.getY(), v, null)) {
598a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                            final Handler handler = getHandler();
599a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                            if (handler != null) {
600a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                                handler.removeCallbacks(mPendingCheckForTap);
601a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                            }
602a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                            showTapFeedback(v);
603a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                            postDelayed(new Runnable() {
604a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                                public void run() {
605a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                                    hideTapFeedback(v);
606a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                                    post(new Runnable() {
607a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                                        public void run() {
608a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                                            performItemClick(v, 0, 0);
609a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                                        }
610a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                                    });
611a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                                }
612a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                            }, ViewConfiguration.getPressedStateDuration());
613a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                            handled = true;
614a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                        }
615a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                    }
616a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                }
617a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                mTouchMode = TOUCH_MODE_NONE;
618a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                break;
619a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen            }
620a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen            case MotionEvent.ACTION_CANCEL: {
621a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                View v = getCurrentView();
622a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                if (v != null) {
623a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                    hideTapFeedback(v);
624a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                }
625a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                mTouchMode = TOUCH_MODE_NONE;
626a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen            }
627a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen        }
628a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen        return handled;
629a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen    }
630a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen
631839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen    private void measureChildren() {
632839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        final int count = getChildCount();
633189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn        final int childWidth = getMeasuredWidth() - mPaddingLeft - mPaddingRight;
634189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn        final int childHeight = getMeasuredHeight() - mPaddingTop - mPaddingBottom;
635839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen
636839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        for (int i = 0; i < count; i++) {
637839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen            final View child = getChildAt(i);
638839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen            child.measure(MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),
639839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen                    MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
640839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        }
641839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen    }
642839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen
643839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen    @Override
644839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
645839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
646839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
647839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
648839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
649839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen
650839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        boolean haveChildRefSize = (mReferenceChildWidth != -1 && mReferenceChildHeight != -1);
651839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen
652839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        // We need to deal with the case where our parent hasn't told us how
653839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        // big we should be. In this case we try to use the desired size of the first
654839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        // child added.
655839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        if (heightSpecMode == MeasureSpec.UNSPECIFIED) {
656839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen            heightSpecSize = haveChildRefSize ? mReferenceChildHeight + mPaddingTop +
657839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen                    mPaddingBottom : 0;
658839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
659189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn            if (haveChildRefSize) {
660189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn                int height = mReferenceChildHeight + mPaddingTop + mPaddingBottom;
661189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn                if (height > heightSpecSize) {
662189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn                    heightSpecSize |= MEASURED_STATE_TOO_SMALL;
663189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn                } else {
664189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn                    heightSpecSize = height;
665189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn                }
666189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn            }
667839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        }
668839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen
669839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        if (widthSpecMode == MeasureSpec.UNSPECIFIED) {
670839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen            widthSpecSize = haveChildRefSize ? mReferenceChildWidth + mPaddingLeft +
671839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen                    mPaddingRight : 0;
672839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
673189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn            if (haveChildRefSize) {
674189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn                int width = mReferenceChildWidth + mPaddingLeft + mPaddingRight;
675189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn                if (width > widthSpecSize) {
676189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn                    widthSpecSize |= MEASURED_STATE_TOO_SMALL;
677189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn                } else {
678189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn                    widthSpecSize = width;
679189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn                }
680189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn            }
681839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        }
682839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen
683839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        setMeasuredDimension(widthSpecSize, heightSpecSize);
684839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        measureChildren();
685839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen    }
686839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen
687ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen    void checkForAndHandleDataChanged() {
6883db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        boolean dataChanged = mDataChanged;
6893db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        if (dataChanged) {
690ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen            post(new Runnable() {
691ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen                public void run() {
692ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen                    handleDataChanged();
693ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen                    // if the data changes, mWhichChild might be out of the bounds of the adapter
694ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen                    // in this case, we reset mWhichChild to the beginning
695ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen                    if (mWhichChild >= getWindowSize()) {
696ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen                        mWhichChild = 0;
6973db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
69878db1aa9118edd71c2da28a2c23a0d875d1a707aAdam Cohen                        showOnly(mWhichChild, false);
699e86ff4d56145d875c13a1637833f9f55d79febc9Adam Cohen                    } else if (mOldItemCount != getCount()) {
70078db1aa9118edd71c2da28a2c23a0d875d1a707aAdam Cohen                        showOnly(mWhichChild, false);
701ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen                    }
702ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen                    refreshChildren();
703ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen                    requestLayout();
704ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen                }
705ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen            });
7063db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        }
707ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen        mDataChanged = false;
708ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen    }
709ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen
710ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen    @Override
711ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
712ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen        checkForAndHandleDataChanged();
7133db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
7143db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        final int childCount = getChildCount();
7153db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        for (int i = 0; i < childCount; i++) {
7163db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            final View child = getChildAt(i);
7173db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
7183db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            int childRight = mPaddingLeft + child.getMeasuredWidth();
7193db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            int childBottom = mPaddingTop + child.getMeasuredHeight();
7203db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
721839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen            child.layout(mPaddingLeft, mPaddingTop, childRight, childBottom);
7223db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        }
7233db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
7243db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
725b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen    static class SavedState extends BaseSavedState {
726b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        int whichChild;
727b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen
728b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        /**
729b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen         * Constructor called from {@link AdapterViewAnimator#onSaveInstanceState()}
730b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen         */
731b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        SavedState(Parcelable superState, int whichChild) {
732b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen            super(superState);
733b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen            this.whichChild = whichChild;
734b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        }
735b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen
736b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        /**
737b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen         * Constructor called from {@link #CREATOR}
738b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen         */
739b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        private SavedState(Parcel in) {
740b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen            super(in);
7413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            this.whichChild = in.readInt();
742b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        }
743b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen
744b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        @Override
745b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        public void writeToParcel(Parcel out, int flags) {
746b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen            super.writeToParcel(out, flags);
7473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            out.writeInt(this.whichChild);
748b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        }
749b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen
750b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        @Override
751b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        public String toString() {
7523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return "AdapterViewAnimator.SavedState{ whichChild = " + this.whichChild + " }";
753b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        }
754b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen
755b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        public static final Parcelable.Creator<SavedState> CREATOR
756b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen                = new Parcelable.Creator<SavedState>() {
757b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen            public SavedState createFromParcel(Parcel in) {
758b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen                return new SavedState(in);
759b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen            }
760b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen
761b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen            public SavedState[] newArray(int size) {
762b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen                return new SavedState[size];
763b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen            }
764b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        };
765b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen    }
766b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen
767b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen    @Override
768b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen    public Parcelable onSaveInstanceState() {
769b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        Parcelable superState = super.onSaveInstanceState();
770b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        return new SavedState(superState, mWhichChild);
771b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen    }
772b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen
773b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen    @Override
774b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen    public void onRestoreInstanceState(Parcelable state) {
775b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        SavedState ss = (SavedState) state;
776b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        super.onRestoreInstanceState(ss.getSuperState());
777b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen
778b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        // Here we set mWhichChild in addition to setDisplayedChild
779b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        // We do the former in case mAdapter is null, and hence setDisplayedChild won't
780b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        // set mWhichChild
781b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        mWhichChild = ss.whichChild;
78269d66e00136f67332c992326a7b2eb334eeb32a2Adam Cohen
783b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        setDisplayedChild(mWhichChild);
784b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen    }
785b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen
7863db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
7873db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Shows only the specified child. The other displays Views exit the screen
7883db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * with the {@link #getOutAnimation() out animation} and the specified child
7893db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * enters the screen with the {@link #getInAnimation() in animation}.
7903db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
7913db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @param childIndex The index of the child to be shown.
7923db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
7933db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    void showOnly(int childIndex) {
7943db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        final boolean animate = (!mFirstTime || mAnimateFirstTime);
7953db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        showOnly(childIndex, animate);
7963db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
7973db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
7983db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
7993db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Returns the View corresponding to the currently displayed child.
8003db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
8013db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @return The View currently displayed.
8023db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
8033db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @see #getDisplayedChild()
8043db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
8053db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public View getCurrentView() {
80644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        return getViewAtRelativeIndex(mActiveOffset);
8073db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
8083db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
8093db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
8103db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Returns the current animation used to animate a View that enters the screen.
8113db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
8123db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @return An Animation or null if none is set.
8133db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
8142794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase     * @see #setInAnimation(android.animation.ObjectAnimator)
8153db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @see #setInAnimation(android.content.Context, int)
8163db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
8172794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase    public ObjectAnimator getInAnimation() {
8183db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        return mInAnimation;
8193db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
8203db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
8213db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
8223db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Specifies the animation used to animate a View that enters the screen.
8233db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
8243db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @param inAnimation The animation started when a View enters the screen.
8253db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
8263db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @see #getInAnimation()
8273db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @see #setInAnimation(android.content.Context, int)
8283db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
8292794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase    public void setInAnimation(ObjectAnimator inAnimation) {
8303db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        mInAnimation = inAnimation;
8313db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
8323db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
8333db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
8343db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Returns the current animation used to animate a View that exits the screen.
8353db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
8363db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @return An Animation or null if none is set.
8373db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
8382794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase     * @see #setOutAnimation(android.animation.ObjectAnimator)
8393db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @see #setOutAnimation(android.content.Context, int)
8403db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
8412794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase    public ObjectAnimator getOutAnimation() {
8423db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        return mOutAnimation;
8433db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
8443db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
8453db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
8463db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Specifies the animation used to animate a View that exit the screen.
8473db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
8483db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @param outAnimation The animation started when a View exit the screen.
8493db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
8503db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @see #getOutAnimation()
8513db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @see #setOutAnimation(android.content.Context, int)
8523db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
8532794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase    public void setOutAnimation(ObjectAnimator outAnimation) {
8543db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        mOutAnimation = outAnimation;
8553db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
8563db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
8573db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
8583db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Specifies the animation used to animate a View that enters the screen.
8593db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
8603db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @param context The application's environment.
8613db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @param resourceID The resource id of the animation.
8623db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
8633db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @see #getInAnimation()
8642794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase     * @see #setInAnimation(android.animation.ObjectAnimator)
8653db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
8663db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void setInAnimation(Context context, int resourceID) {
8672794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase        setInAnimation((ObjectAnimator) AnimatorInflater.loadAnimator(context, resourceID));
8683db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
8693db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
8703db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
8713db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Specifies the animation used to animate a View that exit the screen.
8723db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
8733db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @param context The application's environment.
8743db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @param resourceID The resource id of the animation.
8753db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
8763db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @see #getOutAnimation()
8772794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase     * @see #setOutAnimation(android.animation.ObjectAnimator)
8783db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
8793db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void setOutAnimation(Context context, int resourceID) {
8802794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase        setOutAnimation((ObjectAnimator) AnimatorInflater.loadAnimator(context, resourceID));
8813db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
8823db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
8833db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
8843db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Indicates whether the current View should be animated the first time
8853db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * the ViewAnimation is displayed.
8863db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
8873db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @param animate True to animate the current View the first time it is displayed,
8883db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *                false otherwise.
8893db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
8903db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void setAnimateFirstView(boolean animate) {
8913db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        mAnimateFirstTime = animate;
8923db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
8933db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
8943db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    @Override
8953db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public int getBaseline() {
8963db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        return (getCurrentView() != null) ? getCurrentView().getBaseline() : super.getBaseline();
8973db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
8983db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
8993db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    @Override
9003db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public Adapter getAdapter() {
9013db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        return mAdapter;
9023db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
9033db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
9043db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    @Override
9053db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void setAdapter(Adapter adapter) {
9068322834a2544a673a35c5d4ad0d5909b3ca37600Adam Cohen        if (mAdapter != null && mDataSetObserver != null) {
9078322834a2544a673a35c5d4ad0d5909b3ca37600Adam Cohen            mAdapter.unregisterDataSetObserver(mDataSetObserver);
9088322834a2544a673a35c5d4ad0d5909b3ca37600Adam Cohen        }
9098322834a2544a673a35c5d4ad0d5909b3ca37600Adam Cohen
9103db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        mAdapter = adapter;
9111480fddea874a42adb43b4bcdac6704e4c3e110bAdam Cohen        checkFocus();
9123db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
9133db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        if (mAdapter != null) {
9143db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            mDataSetObserver = new AdapterDataSetObserver();
9153db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            mAdapter.registerDataSetObserver(mDataSetObserver);
916ef17dd497edc14ca753616862efaa3457e1df5daAdam Cohen            mItemCount = mAdapter.getCount();
9173db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        }
91844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        setFocusable(true);
91978db1aa9118edd71c2da28a2c23a0d875d1a707aAdam Cohen        mWhichChild = 0;
92078db1aa9118edd71c2da28a2c23a0d875d1a707aAdam Cohen        showOnly(mWhichChild, false);
9213db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
9223db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
9233db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
92444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * Sets up this AdapterViewAnimator to use a remote views adapter which connects to a
92544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * RemoteViewsService through the specified intent.
92644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *
92744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * @param intent the intent used to identify the RemoteViewsService for the adapter to
92844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *        connect to.
9293db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
9303db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    @android.view.RemotableViewMethod
9313db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void setRemoteViewsAdapter(Intent intent) {
9329b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung        // Ensure that we don't already have a RemoteViewsAdapter that is bound to an existing
9339b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung        // service handling the specified intent.
9343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        if (mRemoteViewsAdapter != null) {
9353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            Intent.FilterComparison fcNew = new Intent.FilterComparison(intent);
9363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            Intent.FilterComparison fcOld = new Intent.FilterComparison(
9373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    mRemoteViewsAdapter.getRemoteViewsServiceIntent());
9383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (fcNew.equals(fcOld)) {
9393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                return;
9403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            }
9419b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung        }
9429b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung
9439b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung        // Otherwise, create a new RemoteViewsAdapter for binding
9443db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        mRemoteViewsAdapter = new RemoteViewsAdapter(getContext(), intent, this);
9453db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
9463db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
9473db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    @Override
9483db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void setSelection(int position) {
9493db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        setDisplayedChild(position);
9503db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
9513db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
9523db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    @Override
9533db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public View getSelectedView() {
95444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        return getViewAtRelativeIndex(mActiveOffset);
9553db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
9563db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
9573db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
9583db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Called back when the adapter connects to the RemoteViewsService.
9593db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
9603db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void onRemoteAdapterConnected() {
9613db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        if (mRemoteViewsAdapter != mAdapter) {
9623db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            setAdapter(mRemoteViewsAdapter);
963fb60386b46d0c6216c765c10bd33ac42ca780917Adam Cohen        } else if (mRemoteViewsAdapter != null) {
964fb60386b46d0c6216c765c10bd33ac42ca780917Adam Cohen            mRemoteViewsAdapter.superNotifyDataSetChanged();
9653db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        }
9663db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
9673db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
9683db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
9693db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Called back when the adapter disconnects from the RemoteViewsService.
9703db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
9713db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void onRemoteAdapterDisconnected() {
972fb60386b46d0c6216c765c10bd33ac42ca780917Adam Cohen        // If the remote adapter disconnects, we keep it around
973fb60386b46d0c6216c765c10bd33ac42ca780917Adam Cohen        // since the currently displayed items are still cached.
974fb60386b46d0c6216c765c10bd33ac42ca780917Adam Cohen        // Further, we want the service to eventually reconnect
975fb60386b46d0c6216c765c10bd33ac42ca780917Adam Cohen        // when necessary, as triggered by this view requesting
976fb60386b46d0c6216c765c10bd33ac42ca780917Adam Cohen        // items from the Adapter.
9773db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
978a02fdf1ba03fad71cc80a89dfc74b17456d5b4a5Adam Cohen
9790e2de6d7187ef67ec00a2f2544450caa4a239c39Adam Cohen    /**
9800e2de6d7187ef67ec00a2f2544450caa4a239c39Adam Cohen     * Called by an {@link android.appwidget.AppWidgetHost} in order to advance the current view when
9810e2de6d7187ef67ec00a2f2544450caa4a239c39Adam Cohen     * it is being used within an app widget.
9820e2de6d7187ef67ec00a2f2544450caa4a239c39Adam Cohen     */
983a02fdf1ba03fad71cc80a89dfc74b17456d5b4a5Adam Cohen    public void advance() {
984a02fdf1ba03fad71cc80a89dfc74b17456d5b4a5Adam Cohen        showNext();
985a02fdf1ba03fad71cc80a89dfc74b17456d5b4a5Adam Cohen    }
986a02fdf1ba03fad71cc80a89dfc74b17456d5b4a5Adam Cohen
9870e2de6d7187ef67ec00a2f2544450caa4a239c39Adam Cohen    /**
9880e2de6d7187ef67ec00a2f2544450caa4a239c39Adam Cohen     * Called by an {@link android.appwidget.AppWidgetHost} to indicate that it will be
9890e2de6d7187ef67ec00a2f2544450caa4a239c39Adam Cohen     * automatically advancing the views of this {@link AdapterViewAnimator} by calling
9900e2de6d7187ef67ec00a2f2544450caa4a239c39Adam Cohen     * {@link AdapterViewAnimator#advance()} at some point in the future. This allows subclasses to
9910e2de6d7187ef67ec00a2f2544450caa4a239c39Adam Cohen     * perform any required setup, for example, to stop automatically advancing their children.
9920e2de6d7187ef67ec00a2f2544450caa4a239c39Adam Cohen     */
9930e2de6d7187ef67ec00a2f2544450caa4a239c39Adam Cohen    public void fyiWillBeAdvancedByHostKThx() {
994a02fdf1ba03fad71cc80a89dfc74b17456d5b4a5Adam Cohen    }
995ec84c3a189e4aa70aa6ea8ba712e5a4f260a153bPatrick Dubroy
996ec84c3a189e4aa70aa6ea8ba712e5a4f260a153bPatrick Dubroy    @Override
997ec84c3a189e4aa70aa6ea8ba712e5a4f260a153bPatrick Dubroy    protected void onDetachedFromWindow() {
998ec84c3a189e4aa70aa6ea8ba712e5a4f260a153bPatrick Dubroy        mAdapter = null;
999ec84c3a189e4aa70aa6ea8ba712e5a4f260a153bPatrick Dubroy        super.onDetachedFromWindow();
1000ec84c3a189e4aa70aa6ea8ba712e5a4f260a153bPatrick Dubroy    }
10013db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen}
1002