AdapterViewAnimator.java revision 6f279627cfa3286e6901a8dc2ed8361576ce226d
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;
323db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohenimport android.view.View;
3344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenimport android.view.ViewGroup;
34839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohenimport android.view.ViewGroup.LayoutParams;
353db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohenimport android.view.animation.Animation;
363db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohenimport android.view.animation.AnimationUtils;
373db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
383db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen/**
393db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * Base class for a {@link AdapterView} that will perform animations
403db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * when switching between its views.
413db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen *
423db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @attr ref android.R.styleable#AdapterViewAnimator_inAnimation
433db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @attr ref android.R.styleable#AdapterViewAnimator_outAnimation
443db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @attr ref android.R.styleable#AdapterViewAnimator_animateFirstView
451b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen * @attr ref android.R.styleable#AdapterViewAnimator_loopViews
463db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen */
4744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenpublic abstract class AdapterViewAnimator extends AdapterView<Adapter>
48dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen        implements RemoteViewsAdapter.RemoteAdapterConnectionCallback {
493db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    private static final String TAG = "RemoteViewAnimator";
503db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
5144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
5244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * The index of the current child, which appears anywhere from the beginning
5344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * to the end of the current set of children, as specified by {@link #mActiveOffset}
5444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
553db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    int mWhichChild = 0;
5644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
5744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
5844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * Whether or not the first view(s) should be animated in
5944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
603db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    boolean mAnimateFirstTime = true;
613db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
6244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
6344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *  Represents where the in the current window of
6444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *  views the current <code>mDisplayedChild</code> sits
6544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
6644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    int mActiveOffset = 0;
6744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
6844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
6944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * The number of views that the {@link AdapterViewAnimator} keeps as children at any
7044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * given time (not counting views that are pending removal, see {@link #mPreviousViews}).
7144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
7244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    int mNumActiveViews = 1;
7344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
7444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
751b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen     * Map of the children of the {@link AdapterViewAnimator}.
7644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
771b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen    private HashMap<Integer, ViewAndIndex> mViewsMap = new HashMap<Integer, ViewAndIndex>();
7844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
7944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
8044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * List of views pending removal from the {@link AdapterViewAnimator}
8144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
821b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen    ArrayList<Integer> mPreviousViews;
8344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
8444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
8544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * The index, relative to the adapter, of the beginning of the window of views
8644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
8744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    int mCurrentWindowStart = 0;
8844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
8944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
9044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * The index, relative to the adapter, of the end of the window of views
9144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
9244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    int mCurrentWindowEnd = -1;
9344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
9444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
9544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * The same as {@link #mCurrentWindowStart}, except when the we have bounded
9644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * {@link #mCurrentWindowStart} to be non-negative
9744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
9844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    int mCurrentWindowStartUnbounded = 0;
9944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
10044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
10144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * Handler to post events to the main thread
10244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
10344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    Handler mMainQueue;
10444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
10544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
10644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * Listens for data changes from the adapter
10744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
1083db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    AdapterDataSetObserver mDataSetObserver;
1093db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
11044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
11144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * The {@link Adapter} for this {@link AdapterViewAnimator}
11244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
11344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    Adapter mAdapter;
11444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
11544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
11644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * The {@link RemoteViewsAdapter} for this {@link AdapterViewAnimator}
11744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
11844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    RemoteViewsAdapter mRemoteViewsAdapter;
11944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
12044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
12144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * Specifies whether this is the first time the animator is showing views
12244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
12344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    boolean mFirstTime = true;
1243db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
12544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
126b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen     * Specifies if the animator should wrap from 0 to the end and vice versa
127b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen     * or have hard boundaries at the beginning and end
128b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen     */
1291b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen    boolean mLoopViews = true;
130b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen
131b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen    /**
132839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen     * The width and height of some child, used as a size reference in-case our
133839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen     * dimensions are unspecified by the parent.
134839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen     */
135839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen    int mReferenceChildWidth = -1;
136839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen    int mReferenceChildHeight = -1;
137839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen
138839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen    /**
139ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen     * In and out animations.
14044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
1412794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase    ObjectAnimator mInAnimation;
1422794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase    ObjectAnimator mOutAnimation;
143ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen
14444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    private  ArrayList<View> mViewsToBringToFront;
1453db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
146ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen    private static final int DEFAULT_ANIMATION_DURATION = 200;
147ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen
1483db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public AdapterViewAnimator(Context context) {
1493db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        super(context);
1505b53f9186e7812c93bc578d18e92cb123481fcbcRomain Guy        initViewAnimator();
1513db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
1523db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
1533db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public AdapterViewAnimator(Context context, AttributeSet attrs) {
1543db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        super(context, attrs);
1553db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
15644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        TypedArray a = context.obtainStyledAttributes(attrs,
1571b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                com.android.internal.R.styleable.AdapterViewAnimator);
15844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        int resource = a.getResourceId(
1591b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                com.android.internal.R.styleable.AdapterViewAnimator_inAnimation, 0);
1603db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        if (resource > 0) {
1613db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            setInAnimation(context, resource);
162ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen        } else {
163ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen            setInAnimation(getDefaultInAnimation());
1643db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        }
1653db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
1661b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        resource = a.getResourceId(com.android.internal.R.styleable.AdapterViewAnimator_outAnimation, 0);
1673db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        if (resource > 0) {
1683db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            setOutAnimation(context, resource);
169ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen        } else {
170ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen            setOutAnimation(getDefaultOutAnimation());
1713db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        }
1723db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
17344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        boolean flag = a.getBoolean(
1741b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                com.android.internal.R.styleable.AdapterViewAnimator_animateFirstView, true);
1753db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        setAnimateFirstView(flag);
1763db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
1771b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        mLoopViews = a.getBoolean(
1781b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                com.android.internal.R.styleable.AdapterViewAnimator_loopViews, false);
1791b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen
1803db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        a.recycle();
1813db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
1825b53f9186e7812c93bc578d18e92cb123481fcbcRomain Guy        initViewAnimator();
1833db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
1843db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
1853db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
1863db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Initialize this {@link AdapterViewAnimator}
1873db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
1885b53f9186e7812c93bc578d18e92cb123481fcbcRomain Guy    private void initViewAnimator() {
1893db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        mMainQueue = new Handler(Looper.myLooper());
1901b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        mPreviousViews = new ArrayList<Integer>();
19144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mViewsToBringToFront = new ArrayList<View>();
19244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    }
19344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
1941b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen    private class ViewAndIndex {
1951b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        ViewAndIndex(View v, int i) {
1961b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen            view = v;
1971b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen            index = i;
1981b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        }
1991b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        View view;
2001b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        int index;
2011b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen    }
2021b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen
20344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
20444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * This method is used by subclasses to configure the animator to display the
20544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * desired number of views, and specify the offset
20644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *
20744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * @param numVisibleViews The number of views the animator keeps in the {@link ViewGroup}
2085b53f9186e7812c93bc578d18e92cb123481fcbcRomain Guy     * @param activeOffset This parameter specifies where the current index ({@link #mWhichChild})
20944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *        sits within the window. For example if activeOffset is 1, and numVisibleViews is 3,
2105b53f9186e7812c93bc578d18e92cb123481fcbcRomain Guy     *        and {@link #setDisplayedChild(int)} is called with 10, then the effective window will
2115b53f9186e7812c93bc578d18e92cb123481fcbcRomain Guy     *        be the indexes 9, 10, and 11. In the same example, if activeOffset were 0, then the
21244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *        window would instead contain indexes 10, 11 and 12.
213b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen     * @param shouldLoop If the animator is show view 0, and setPrevious() is called, do we
214b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen     *        we loop back to the end, or do we do nothing
21544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
2161b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen     void configureViewAnimator(int numVisibleViews, int activeOffset) {
21744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        if (activeOffset > numVisibleViews - 1) {
21844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // Throw an exception here.
21944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
22044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mNumActiveViews = numVisibleViews;
22144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mActiveOffset = activeOffset;
22244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mPreviousViews.clear();
2231b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        mViewsMap.clear();
22444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        removeAllViewsInLayout();
22544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mCurrentWindowStart = 0;
22644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mCurrentWindowEnd = -1;
22744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    }
22844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
22944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
23044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * This class should be overridden by subclasses to customize view transitions within
23144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * the set of visible views
23244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *
23344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * @param fromIndex The relative index within the window that the view was in, -1 if it wasn't
23444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *        in the window
23544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * @param toIndex The relative index within the window that the view is going to, -1 if it is
23644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *        being removed
23744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * @param view The view that is being animated
23844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
23944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    void animateViewForTransition(int fromIndex, int toIndex, View view) {
24044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        if (fromIndex == -1) {
241ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen            mInAnimation.setTarget(view);
242ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen            mInAnimation.start();
24344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        } else if (toIndex == -1) {
244ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen            mOutAnimation.setTarget(view);
245ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen            mOutAnimation.start();
24644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
2473db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
2483db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
2492794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase    ObjectAnimator getDefaultInAnimation() {
2502794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase        ObjectAnimator anim = ObjectAnimator.ofFloat(null, "alpha", 0.0f, 1.0f);
2512794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase        anim.setDuration(DEFAULT_ANIMATION_DURATION);
2522794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase        return anim;
253ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen    }
254ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen
2552794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase    ObjectAnimator getDefaultOutAnimation() {
2562794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase        ObjectAnimator anim = ObjectAnimator.ofFloat(null, "alpha", 1.0f, 0.0f);
2572794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase        anim.setDuration(DEFAULT_ANIMATION_DURATION);
2582794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase        return anim;
259ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen    }
260ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen
2613db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
2623db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Sets which child view will be displayed.
2633db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
2643db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @param whichChild the index of the child view to display
2653db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
2663db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void setDisplayedChild(int whichChild) {
2673db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        if (mAdapter != null) {
2683db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            mWhichChild = whichChild;
2693db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            if (whichChild >= mAdapter.getCount()) {
2701b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                mWhichChild = mLoopViews ? 0 : mAdapter.getCount() - 1;
2713db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            } else if (whichChild < 0) {
2721b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                mWhichChild = mLoopViews ? mAdapter.getCount() - 1 : 0;
2733db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            }
2743db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
2753db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            boolean hasFocus = getFocusedChild() != null;
2763db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            // This will clear old focus if we had it
2773db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            showOnly(mWhichChild);
2783db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            if (hasFocus) {
2793db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen                // Try to retake focus if we had it
2803db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen                requestFocus(FOCUS_FORWARD);
2813db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            }
2823db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        }
2833db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
2843db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
2853db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
28644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * To be overridden by subclasses. This method applies a view / index specific
28744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * transform to the child view.
28844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *
28944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * @param child
29044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * @param relativeIndex
29144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
29244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    void applyTransformForChildAtIndex(View child, int relativeIndex) {
29344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    }
29444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
29544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
2963db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Returns the index of the currently displayed child view.
2973db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
2983db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public int getDisplayedChild() {
2993db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        return mWhichChild;
3003db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
3013db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
3023db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
3033db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Manually shows the next child.
3043db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
3053db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void showNext() {
3063db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        setDisplayedChild(mWhichChild + 1);
3073db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
3083db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
3093db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
3103db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Manually shows the previous child.
3113db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
3123db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void showPrevious() {
3133db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        setDisplayedChild(mWhichChild - 1);
3143db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
3153db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
3163db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
3173db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Shows only the specified child. The other displays Views exit the screen,
3183db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * optionally with the with the {@link #getOutAnimation() out animation} and
3193db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * the specified child enters the screen, optionally with the
3203db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * {@link #getInAnimation() in animation}.
3213db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
3223db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @param childIndex The index of the child to be shown.
3233db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @param animate Whether or not to use the in and out animations, defaults
3243db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *            to true.
3253db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
3263db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    void showOnly(int childIndex, boolean animate) {
3273db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        showOnly(childIndex, animate, false);
3283db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
3293db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
33044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    private int modulo(int pos, int size) {
3313042944c6ec68210ba1746540b53789e70d15ef4Adam Cohen        if (size > 0) {
3323042944c6ec68210ba1746540b53789e70d15ef4Adam Cohen            return (size + (pos % size)) % size;
3333042944c6ec68210ba1746540b53789e70d15ef4Adam Cohen        } else {
3343042944c6ec68210ba1746540b53789e70d15ef4Adam Cohen            return 0;
3353042944c6ec68210ba1746540b53789e70d15ef4Adam Cohen        }
3363db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
3373db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
33844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
33944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * Get the view at this index relative to the current window's start
34044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *
34144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * @param relativeIndex Position relative to the current window's start
34244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * @return View at this index, null if the index is outside the bounds
34344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
34444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    View getViewAtRelativeIndex(int relativeIndex) {
3451b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        if (relativeIndex >= 0 && relativeIndex <= mNumActiveViews - 1 && mAdapter != null) {
3461b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen            int adapterCount =  mAdapter.getCount();
3471b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen            int i = modulo(mCurrentWindowStartUnbounded + relativeIndex, adapterCount);
3486f279627cfa3286e6901a8dc2ed8361576ce226dAdam Cohen            if (mViewsMap.get(i) != null) {
3496f279627cfa3286e6901a8dc2ed8361576ce226dAdam Cohen                return mViewsMap.get(i).view;
3506f279627cfa3286e6901a8dc2ed8361576ce226dAdam Cohen            }
35144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
35244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        return null;
35344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    }
3543db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
3559b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen    LayoutParams createOrReuseLayoutParams(View v) {
3565b53f9186e7812c93bc578d18e92cb123481fcbcRomain Guy        final ViewGroup.LayoutParams currentLp = v.getLayoutParams();
3579b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen        if (currentLp instanceof ViewGroup.LayoutParams) {
358b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen            LayoutParams lp = (LayoutParams) currentLp;
359b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen            return lp;
36044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
3619b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen        return new ViewGroup.LayoutParams(0, 0);
36244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    }
3633db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
3646364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung    void refreshChildren() {
365bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen        for (int i = mCurrentWindowStart; i <= mCurrentWindowEnd; i++) {
366bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen            int index = modulo(i, mNumActiveViews);
367bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen
368bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen            // get the fresh child from the adapter
369bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen            View updatedChild = mAdapter.getView(i, null, this);
370bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen
3711b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen            if (mViewsMap.containsKey(index)) {
3721b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                FrameLayout fl = (FrameLayout) mViewsMap.get(index).view;
373bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen                // flush out the old child
374bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen                fl.removeAllViewsInLayout();
375bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen                // add the new child to the frame, if it exists
376bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen                if (updatedChild != null) {
377bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen                    fl.addView(updatedChild);
378bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen                }
379bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen            }
380bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen        }
381bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen    }
382bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen
383dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen    /**
384dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen     * This method can be overridden so that subclasses can provide a custom frame in which their
385dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen     * children can live. For example, StackView adds padding to its childrens' frames so as to
386dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen     * accomodate for the highlight effect.
387dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen     *
388dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen     * @return The FrameLayout into which children can be placed.
389dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen     */
390dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen    FrameLayout getFrameForChild() {
391dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen        return new FrameLayout(mContext);
392dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen    }
393dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen
39444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    void showOnly(int childIndex, boolean animate, boolean onLayout) {
39544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        if (mAdapter == null) return;
3963042944c6ec68210ba1746540b53789e70d15ef4Adam Cohen        final int adapterCount = mAdapter.getCount();
3973042944c6ec68210ba1746540b53789e70d15ef4Adam Cohen        if (adapterCount == 0) return;
3983db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
39944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        for (int i = 0; i < mPreviousViews.size(); i++) {
4001b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen            View viewToRemove = mViewsMap.get(mPreviousViews.get(i)).view;
4011b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen            mViewsMap.remove(mPreviousViews.get(i));
40244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            viewToRemove.clearAnimation();
4033d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen            if (viewToRemove instanceof ViewGroup) {
4043d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen                ViewGroup vg = (ViewGroup) viewToRemove;
4053d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen                vg.removeAllViewsInLayout();
4063d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen            }
40744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // applyTransformForChildAtIndex here just allows for any cleanup
40844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // associated with this view that may need to be done by a subclass
40944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            applyTransformForChildAtIndex(viewToRemove, -1);
4103d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen
41144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            removeViewInLayout(viewToRemove);
41244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
41344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mPreviousViews.clear();
41444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        int newWindowStartUnbounded = childIndex - mActiveOffset;
41544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        int newWindowEndUnbounded = newWindowStartUnbounded + mNumActiveViews - 1;
41644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        int newWindowStart = Math.max(0, newWindowStartUnbounded);
4171b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        int newWindowEnd = Math.min(adapterCount - 1, newWindowEndUnbounded);
41844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
4191b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        if (mLoopViews) {
4201b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen            newWindowStart = newWindowStartUnbounded;
4211b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen            newWindowEnd = newWindowEndUnbounded;
4221b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        }
4231b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        int rangeStart = modulo(newWindowStart, adapterCount);
4241b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        int rangeEnd = modulo(newWindowEnd, adapterCount);
4251b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen
4261b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        boolean wrap = false;
4271b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        if (rangeStart > rangeEnd) {
4281b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen            wrap = true;
4291b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        }
4301b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen
4311b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        // This section clears out any items that are in our active views list
43244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        // but are outside the effective bounds of our window (this is becomes an issue
43344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        // at the extremities of the list, eg. where newWindowStartUnbounded < 0 or
43444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        // newWindowEndUnbounded > mAdapter.getCount() - 1
4351b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        for (Integer index : mViewsMap.keySet()) {
4361b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen            boolean remove = false;
4371b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen            if (!wrap && (index < rangeStart || index > rangeEnd)) {
4381b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                remove = true;
4391b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen            } else if (wrap && (index > rangeEnd && index < rangeStart)) {
4401b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                remove = true;
4411b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen            }
4421b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen
4431b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen            if (remove) {
4441b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                View previousView = mViewsMap.get(index).view;
4451b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                int oldRelativeIndex = mViewsMap.get(index).index;
4461b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen
4471b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                mPreviousViews.add(index);
4481b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                animateViewForTransition(oldRelativeIndex, -1, previousView);
4493db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            }
45044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
4513db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
45244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        // If the window has changed
4531b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen        if (!(newWindowStart == mCurrentWindowStart && newWindowEnd == mCurrentWindowEnd)) {
45444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // Run through the indices in the new range
45544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            for (int i = newWindowStart; i <= newWindowEnd; i++) {
45644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
4571b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                int index = modulo(i, adapterCount);
4581b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                int oldRelativeIndex;
4591b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                if (mViewsMap.containsKey(index)) {
4601b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                    oldRelativeIndex = mViewsMap.get(index).index;
4611b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                } else {
4621b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                    oldRelativeIndex = -1;
4631b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                }
46444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                int newRelativeIndex = i - newWindowStartUnbounded;
46544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
46644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                // If this item is in the current window, great, we just need to apply
46744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                // the transform for it's new relative position in the window, and animate
46844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                // between it's current and new relative positions
4691b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                boolean inOldRange = mViewsMap.containsKey(index) && !mPreviousViews.contains(index);
4701b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen
4711b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                if (inOldRange) {
4721b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                    View view = mViewsMap.get(index).view;
4731b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                    mViewsMap.get(index).index = newRelativeIndex;
47444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    applyTransformForChildAtIndex(view, newRelativeIndex);
47544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    animateViewForTransition(oldRelativeIndex, newRelativeIndex, view);
47644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
4771b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                // Otherwise this view is new to the window
47844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                } else {
4791b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                    // Get the new view from the adapter, add it and apply any transform / animation
4801b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                    View newView = mAdapter.getView(modulo(i, adapterCount), null, this);
481bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen
482bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen                    // We wrap the new view in a FrameLayout so as to respect the contract
483bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen                    // with the adapter, that is, that we don't modify this view directly
484dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen                    FrameLayout fl = getFrameForChild();
485bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen
486bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen                    // If the view from the adapter is null, we still keep an empty frame in place
48744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    if (newView != null) {
488bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen                       fl.addView(newView);
48944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    }
4901b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                    mViewsMap.put(index, new ViewAndIndex(fl, newRelativeIndex));
491bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen                    addChild(fl);
492bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen                    applyTransformForChildAtIndex(fl, newRelativeIndex);
493bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen                    animateViewForTransition(-1, newRelativeIndex, fl);
4943db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen                }
4951b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen                mViewsMap.get(index).view.bringToFront();
4963db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            }
4973db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
49844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            for (int i = 0; i < mViewsToBringToFront.size(); i++) {
49944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                View v = mViewsToBringToFront.get(i);
50044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                v.bringToFront();
5013db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            }
50244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            mViewsToBringToFront.clear();
5033db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
50444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            mCurrentWindowStart = newWindowStart;
50544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            mCurrentWindowEnd = newWindowEnd;
50644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            mCurrentWindowStartUnbounded = newWindowStartUnbounded;
50744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
50844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
50944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mFirstTime = false;
51044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        if (!onLayout) {
51144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            requestLayout();
51244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            invalidate();
51344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        } else {
51444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // If the Adapter tries to layout the current view when we get it using getView
51544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // above the layout will end up being ignored since we are currently laying out, so
51644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // we post a delayed requestLayout and invalidate
51744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            mMainQueue.post(new Runnable() {
51844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                @Override
51944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                public void run() {
52044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    requestLayout();
52144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    invalidate();
52244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                }
52344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            });
5243db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        }
5253db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
5263db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
527839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen    private void addChild(View child) {
528839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        addViewInLayout(child, -1, createOrReuseLayoutParams(child));
529839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen
530839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        // This code is used to obtain a reference width and height of a child in case we need
531839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        // to decide our own size. TODO: Do we want to update the size of the child that we're
532839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        // using for reference size? If so, when?
533839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        if (mReferenceChildWidth == -1 || mReferenceChildHeight == -1) {
534839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen            int measureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
535839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen            child.measure(measureSpec, measureSpec);
536839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen            mReferenceChildWidth = child.getMeasuredWidth();
537839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen            mReferenceChildHeight = child.getMeasuredHeight();
538839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        }
539839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen    }
540839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen
541839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen    private void measureChildren() {
542839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        final int count = getChildCount();
543839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        final int childWidth = mMeasuredWidth - mPaddingLeft - mPaddingRight;
544839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        final int childHeight = mMeasuredHeight - mPaddingTop - mPaddingBottom;
545839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen
546839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        for (int i = 0; i < count; i++) {
547839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen            final View child = getChildAt(i);
548839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen            child.measure(MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),
549839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen                    MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
550839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        }
551839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen    }
552839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen
553839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen    @Override
554839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
555839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
556839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
557839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
558839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
559839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen
560839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        boolean haveChildRefSize = (mReferenceChildWidth != -1 && mReferenceChildHeight != -1);
561839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen
562839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        // We need to deal with the case where our parent hasn't told us how
563839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        // big we should be. In this case we try to use the desired size of the first
564839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        // child added.
565839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        if (heightSpecMode == MeasureSpec.UNSPECIFIED) {
566839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen            heightSpecSize = haveChildRefSize ? mReferenceChildHeight + mPaddingTop +
567839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen                    mPaddingBottom : 0;
568839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
569839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen            heightSpecSize = haveChildRefSize ? Math.min(mReferenceChildHeight + mPaddingTop +
570839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen                    mPaddingBottom, heightSpecSize) : 0;
571839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        }
572839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen
573839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        if (widthSpecMode == MeasureSpec.UNSPECIFIED) {
574839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen            widthSpecSize = haveChildRefSize ? mReferenceChildWidth + mPaddingLeft +
575839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen                    mPaddingRight : 0;
576839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
577839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen            widthSpecSize = haveChildRefSize ? Math.min(mReferenceChildWidth + mPaddingLeft +
578839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen                    mPaddingRight, widthSpecSize) : 0;
579839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        }
580839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen
581839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        setMeasuredDimension(widthSpecSize, heightSpecSize);
582839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen        measureChildren();
583839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen    }
584839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen
5853db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    @Override
5863db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
5873db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        boolean dataChanged = mDataChanged;
5883db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        if (dataChanged) {
5893db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            handleDataChanged();
5903db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
5913db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            // if the data changes, mWhichChild might be out of the bounds of the adapter
5923db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            // in this case, we reset mWhichChild to the beginning
593bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen            if (mWhichChild >= mAdapter.getCount()) {
5943db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen                mWhichChild = 0;
5953db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
596bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen                showOnly(mWhichChild, true, true);
597bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen            }
598bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen            refreshChildren();
5993db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        }
6003db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
6013db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        final int childCount = getChildCount();
6023db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        for (int i = 0; i < childCount; i++) {
6033db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            final View child = getChildAt(i);
6043db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
6053db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            int childRight = mPaddingLeft + child.getMeasuredWidth();
6063db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            int childBottom = mPaddingTop + child.getMeasuredHeight();
6073db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
608839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen            child.layout(mPaddingLeft, mPaddingTop, childRight, childBottom);
6093db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        }
6103db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        mDataChanged = false;
6113db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
6123db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
613b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen    static class SavedState extends BaseSavedState {
614b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        int whichChild;
615b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen
616b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        /**
617b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen         * Constructor called from {@link AdapterViewAnimator#onSaveInstanceState()}
618b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen         */
619b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        SavedState(Parcelable superState, int whichChild) {
620b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen            super(superState);
621b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen            this.whichChild = whichChild;
622b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        }
623b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen
624b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        /**
625b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen         * Constructor called from {@link #CREATOR}
626b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen         */
627b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        private SavedState(Parcel in) {
628b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen            super(in);
6293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            this.whichChild = in.readInt();
630b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        }
631b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen
632b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        @Override
633b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        public void writeToParcel(Parcel out, int flags) {
634b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen            super.writeToParcel(out, flags);
6353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            out.writeInt(this.whichChild);
636b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        }
637b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen
638b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        @Override
639b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        public String toString() {
6403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return "AdapterViewAnimator.SavedState{ whichChild = " + this.whichChild + " }";
641b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        }
642b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen
643b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        public static final Parcelable.Creator<SavedState> CREATOR
644b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen                = new Parcelable.Creator<SavedState>() {
645b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen            public SavedState createFromParcel(Parcel in) {
646b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen                return new SavedState(in);
647b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen            }
648b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen
649b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen            public SavedState[] newArray(int size) {
650b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen                return new SavedState[size];
651b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen            }
652b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        };
653b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen    }
654b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen
655b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen    @Override
656b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen    public Parcelable onSaveInstanceState() {
657b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        Parcelable superState = super.onSaveInstanceState();
658b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        return new SavedState(superState, mWhichChild);
659b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen    }
660b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen
661b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen    @Override
662b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen    public void onRestoreInstanceState(Parcelable state) {
663b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        SavedState ss = (SavedState) state;
664b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        super.onRestoreInstanceState(ss.getSuperState());
665b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen
666b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        // Here we set mWhichChild in addition to setDisplayedChild
667b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        // We do the former in case mAdapter is null, and hence setDisplayedChild won't
668b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        // set mWhichChild
669b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        mWhichChild = ss.whichChild;
670b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen        setDisplayedChild(mWhichChild);
671b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen    }
672b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen
6733db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
6743db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Shows only the specified child. The other displays Views exit the screen
6753db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * with the {@link #getOutAnimation() out animation} and the specified child
6763db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * enters the screen with the {@link #getInAnimation() in animation}.
6773db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
6783db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @param childIndex The index of the child to be shown.
6793db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
6803db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    void showOnly(int childIndex) {
6813db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        final boolean animate = (!mFirstTime || mAnimateFirstTime);
6823db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        showOnly(childIndex, animate);
6833db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
6843db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
6853db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
6863db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Returns the View corresponding to the currently displayed child.
6873db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
6883db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @return The View currently displayed.
6893db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
6903db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @see #getDisplayedChild()
6913db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
6923db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public View getCurrentView() {
69344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        return getViewAtRelativeIndex(mActiveOffset);
6943db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
6953db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
6963db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
6973db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Returns the current animation used to animate a View that enters the screen.
6983db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
6993db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @return An Animation or null if none is set.
7003db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
7012794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase     * @see #setInAnimation(android.animation.ObjectAnimator)
7023db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @see #setInAnimation(android.content.Context, int)
7033db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
7042794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase    public ObjectAnimator getInAnimation() {
7053db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        return mInAnimation;
7063db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
7073db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
7083db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
7093db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Specifies the animation used to animate a View that enters the screen.
7103db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
7113db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @param inAnimation The animation started when a View enters the screen.
7123db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
7133db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @see #getInAnimation()
7143db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @see #setInAnimation(android.content.Context, int)
7153db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
7162794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase    public void setInAnimation(ObjectAnimator inAnimation) {
7173db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        mInAnimation = inAnimation;
7183db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
7193db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
7203db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
7213db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Returns the current animation used to animate a View that exits the screen.
7223db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
7233db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @return An Animation or null if none is set.
7243db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
7252794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase     * @see #setOutAnimation(android.animation.ObjectAnimator)
7263db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @see #setOutAnimation(android.content.Context, int)
7273db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
7282794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase    public ObjectAnimator getOutAnimation() {
7293db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        return mOutAnimation;
7303db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
7313db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
7323db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
7333db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Specifies the animation used to animate a View that exit the screen.
7343db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
7353db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @param outAnimation The animation started when a View exit the screen.
7363db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
7373db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @see #getOutAnimation()
7383db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @see #setOutAnimation(android.content.Context, int)
7393db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
7402794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase    public void setOutAnimation(ObjectAnimator outAnimation) {
7413db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        mOutAnimation = outAnimation;
7423db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
7433db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
7443db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
7453db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Specifies the animation used to animate a View that enters the screen.
7463db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
7473db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @param context The application's environment.
7483db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @param resourceID The resource id of the animation.
7493db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
7503db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @see #getInAnimation()
7512794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase     * @see #setInAnimation(android.animation.ObjectAnimator)
7523db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
7533db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void setInAnimation(Context context, int resourceID) {
7542794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase        setInAnimation((ObjectAnimator) AnimatorInflater.loadAnimator(context, resourceID));
7553db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
7563db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
7573db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
7583db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Specifies the animation used to animate a View that exit the screen.
7593db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
7603db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @param context The application's environment.
7613db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @param resourceID The resource id of the animation.
7623db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
7633db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @see #getOutAnimation()
7642794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase     * @see #setOutAnimation(android.animation.ObjectAnimator)
7653db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
7663db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void setOutAnimation(Context context, int resourceID) {
7672794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase        setOutAnimation((ObjectAnimator) AnimatorInflater.loadAnimator(context, resourceID));
7683db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
7693db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
7703db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
7713db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Indicates whether the current View should be animated the first time
7723db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * the ViewAnimation is displayed.
7733db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
7743db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @param animate True to animate the current View the first time it is displayed,
7753db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *                false otherwise.
7763db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
7773db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void setAnimateFirstView(boolean animate) {
7783db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        mAnimateFirstTime = animate;
7793db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
7803db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
7813db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    @Override
7823db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public int getBaseline() {
7833db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        return (getCurrentView() != null) ? getCurrentView().getBaseline() : super.getBaseline();
7843db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
7853db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
7863db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    @Override
7873db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public Adapter getAdapter() {
7883db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        return mAdapter;
7893db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
7903db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
7913db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    @Override
7923db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void setAdapter(Adapter adapter) {
7938322834a2544a673a35c5d4ad0d5909b3ca37600Adam Cohen        if (mAdapter != null && mDataSetObserver != null) {
7948322834a2544a673a35c5d4ad0d5909b3ca37600Adam Cohen            mAdapter.unregisterDataSetObserver(mDataSetObserver);
7958322834a2544a673a35c5d4ad0d5909b3ca37600Adam Cohen        }
7968322834a2544a673a35c5d4ad0d5909b3ca37600Adam Cohen
7973db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        mAdapter = adapter;
7981480fddea874a42adb43b4bcdac6704e4c3e110bAdam Cohen        checkFocus();
7993db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
8003db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        if (mAdapter != null) {
8013db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            mDataSetObserver = new AdapterDataSetObserver();
8023db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            mAdapter.registerDataSetObserver(mDataSetObserver);
8033db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        }
80444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        setFocusable(true);
8053db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
8063db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
8073db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
80844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * Sets up this AdapterViewAnimator to use a remote views adapter which connects to a
80944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * RemoteViewsService through the specified intent.
81044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *
81144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * @param intent the intent used to identify the RemoteViewsService for the adapter to
81244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *        connect to.
8133db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
8143db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    @android.view.RemotableViewMethod
8153db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void setRemoteViewsAdapter(Intent intent) {
8169b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung        // Ensure that we don't already have a RemoteViewsAdapter that is bound to an existing
8179b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung        // service handling the specified intent.
8183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        if (mRemoteViewsAdapter != null) {
8193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            Intent.FilterComparison fcNew = new Intent.FilterComparison(intent);
8203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            Intent.FilterComparison fcOld = new Intent.FilterComparison(
8213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    mRemoteViewsAdapter.getRemoteViewsServiceIntent());
8223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (fcNew.equals(fcOld)) {
8233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                return;
8243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            }
8259b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung        }
8269b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung
8279b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung        // Otherwise, create a new RemoteViewsAdapter for binding
8283db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        mRemoteViewsAdapter = new RemoteViewsAdapter(getContext(), intent, this);
8293db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
8303db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
8313db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    @Override
8323db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void setSelection(int position) {
8333db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        setDisplayedChild(position);
8343db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
8353db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
8363db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    @Override
8373db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public View getSelectedView() {
83844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        return getViewAtRelativeIndex(mActiveOffset);
8393db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
8403db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
8413db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
8423db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Called back when the adapter connects to the RemoteViewsService.
8433db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
8443db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void onRemoteAdapterConnected() {
8453db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        if (mRemoteViewsAdapter != mAdapter) {
8463db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            setAdapter(mRemoteViewsAdapter);
8473db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        }
8483db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
8493db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
8503db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
8513db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Called back when the adapter disconnects from the RemoteViewsService.
8523db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
8533db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void onRemoteAdapterDisconnected() {
8543db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        if (mRemoteViewsAdapter != mAdapter) {
8553db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            mRemoteViewsAdapter = null;
8563db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            setAdapter(mRemoteViewsAdapter);
8573db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        }
8583db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
8593db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen}
860