14ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette/*
24ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette * Copyright (C) 2015 The Android Open Source Project
34ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette *
44ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette * Licensed under the Apache License, Version 2.0 (the "License");
54ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette * you may not use this file except in compliance with the License.
64ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette * You may obtain a copy of the License at
74ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette *
84ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette *      http://www.apache.org/licenses/LICENSE-2.0
94ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette *
104ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette * Unless required by applicable law or agreed to in writing, software
114ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette * distributed under the License is distributed on an "AS IS" BASIS,
124ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette * See the License for the specific language governing permissions and
144ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette * limitations under the License.
154ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette */
164ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverettepackage com.android.internal.transition;
174ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
184ea5ee233022877c2e34ae83a48c24630f83713eAlan Viveretteimport android.animation.Animator;
194ea5ee233022877c2e34ae83a48c24630f83713eAlan Viveretteimport android.animation.AnimatorListenerAdapter;
204ea5ee233022877c2e34ae83a48c24630f83713eAlan Viveretteimport android.animation.AnimatorSet;
214ea5ee233022877c2e34ae83a48c24630f83713eAlan Viveretteimport android.animation.ObjectAnimator;
224ea5ee233022877c2e34ae83a48c24630f83713eAlan Viveretteimport android.animation.TimeInterpolator;
234ea5ee233022877c2e34ae83a48c24630f83713eAlan Viveretteimport android.animation.TypeEvaluator;
244ea5ee233022877c2e34ae83a48c24630f83713eAlan Viveretteimport android.content.Context;
254ea5ee233022877c2e34ae83a48c24630f83713eAlan Viveretteimport android.content.res.TypedArray;
264ea5ee233022877c2e34ae83a48c24630f83713eAlan Viveretteimport android.graphics.Rect;
274ea5ee233022877c2e34ae83a48c24630f83713eAlan Viveretteimport android.transition.TransitionValues;
284ea5ee233022877c2e34ae83a48c24630f83713eAlan Viveretteimport android.transition.Visibility;
294ea5ee233022877c2e34ae83a48c24630f83713eAlan Viveretteimport android.util.AttributeSet;
304ea5ee233022877c2e34ae83a48c24630f83713eAlan Viveretteimport android.util.Property;
314ea5ee233022877c2e34ae83a48c24630f83713eAlan Viveretteimport android.view.View;
324ea5ee233022877c2e34ae83a48c24630f83713eAlan Viveretteimport android.view.ViewGroup;
334ea5ee233022877c2e34ae83a48c24630f83713eAlan Viveretteimport android.view.animation.AnimationUtils;
344ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
354ea5ee233022877c2e34ae83a48c24630f83713eAlan Viveretteimport com.android.internal.R;
364ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
374ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette/**
384ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette * EpicenterTranslateClipReveal captures the clip bounds and translation values
394ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette * before and after the scene change and animates between those and the
404ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette * epicenter bounds during a visibility transition.
414ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette */
424ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverettepublic class EpicenterTranslateClipReveal extends Visibility {
434ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    private static final String PROPNAME_CLIP = "android:epicenterReveal:clip";
444ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    private static final String PROPNAME_BOUNDS = "android:epicenterReveal:bounds";
454ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    private static final String PROPNAME_TRANSLATE_X = "android:epicenterReveal:translateX";
464ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    private static final String PROPNAME_TRANSLATE_Y = "android:epicenterReveal:translateY";
474ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    private static final String PROPNAME_TRANSLATE_Z = "android:epicenterReveal:translateZ";
484ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    private static final String PROPNAME_Z = "android:epicenterReveal:z";
494ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
504ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    private final TimeInterpolator mInterpolatorX;
514ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    private final TimeInterpolator mInterpolatorY;
524ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    private final TimeInterpolator mInterpolatorZ;
534ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
544ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    public EpicenterTranslateClipReveal() {
554ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        mInterpolatorX = null;
564ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        mInterpolatorY = null;
574ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        mInterpolatorZ = null;
584ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    }
594ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
604ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    public EpicenterTranslateClipReveal(Context context, AttributeSet attrs) {
614ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        super(context, attrs);
624ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
634ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final TypedArray a = context.obtainStyledAttributes(attrs,
644ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette                R.styleable.EpicenterTranslateClipReveal, 0, 0);
654ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
664ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final int interpolatorX = a.getResourceId(
674ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette                R.styleable.EpicenterTranslateClipReveal_interpolatorX, 0);
684ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        if (interpolatorX != 0) {
694ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            mInterpolatorX = AnimationUtils.loadInterpolator(context, interpolatorX);
704ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        } else {
714ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            mInterpolatorX = TransitionConstants.LINEAR_OUT_SLOW_IN;
724ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        }
734ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
744ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final int interpolatorY = a.getResourceId(
754ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette                R.styleable.EpicenterTranslateClipReveal_interpolatorY, 0);
764ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        if (interpolatorY != 0) {
774ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            mInterpolatorY = AnimationUtils.loadInterpolator(context, interpolatorY);
784ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        } else {
794ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            mInterpolatorY = TransitionConstants.FAST_OUT_SLOW_IN;
804ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        }
814ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
824ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final int interpolatorZ = a.getResourceId(
834ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette                R.styleable.EpicenterTranslateClipReveal_interpolatorZ, 0);
844ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        if (interpolatorZ != 0) {
854ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            mInterpolatorZ = AnimationUtils.loadInterpolator(context, interpolatorZ);
864ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        } else {
874ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            mInterpolatorZ = TransitionConstants.FAST_OUT_SLOW_IN;
884ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        }
894ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
904ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        a.recycle();
914ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    }
924ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
934ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    @Override
944ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    public void captureStartValues(TransitionValues transitionValues) {
954ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        super.captureStartValues(transitionValues);
964ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        captureValues(transitionValues);
974ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    }
984ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
994ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    @Override
1004ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    public void captureEndValues(TransitionValues transitionValues) {
1014ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        super.captureEndValues(transitionValues);
1024ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        captureValues(transitionValues);
1034ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    }
1044ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
1054ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    private void captureValues(TransitionValues values) {
1064ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final View view = values.view;
1074ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        if (view.getVisibility() == View.GONE) {
1084ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            return;
1094ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        }
1104ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
1114ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final Rect bounds = new Rect(0, 0, view.getWidth(), view.getHeight());
1124ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        values.values.put(PROPNAME_BOUNDS, bounds);
1134ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        values.values.put(PROPNAME_TRANSLATE_X, view.getTranslationX());
1144ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        values.values.put(PROPNAME_TRANSLATE_Y, view.getTranslationY());
1154ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        values.values.put(PROPNAME_TRANSLATE_Z, view.getTranslationZ());
1164ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        values.values.put(PROPNAME_Z, view.getZ());
1174ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
1184ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final Rect clip = view.getClipBounds();
1194ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        values.values.put(PROPNAME_CLIP, clip);
1204ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    }
1214ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
1224ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    @Override
1234ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    public Animator onAppear(ViewGroup sceneRoot, View view,
1244ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            TransitionValues startValues, TransitionValues endValues) {
1254ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        if (endValues == null) {
1264ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            return null;
1274ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        }
1284ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
1294ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS);
1304ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final Rect startBounds = getEpicenterOrCenter(endBounds);
1314ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final float startX = startBounds.centerX() - endBounds.centerX();
1324ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final float startY = startBounds.centerY() - endBounds.centerY();
1334ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final float startZ = 0 - (float) endValues.values.get(PROPNAME_Z);
1344ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
1354ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        // Translate the view to be centered on the epicenter.
1364ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        view.setTranslationX(startX);
1374ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        view.setTranslationY(startY);
1384ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        view.setTranslationZ(startZ);
1394ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
1404ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final float endX = (float) endValues.values.get(PROPNAME_TRANSLATE_X);
1414ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final float endY = (float) endValues.values.get(PROPNAME_TRANSLATE_Y);
1424ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final float endZ = (float) endValues.values.get(PROPNAME_TRANSLATE_Z);
1434ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
1444ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final Rect endClip = getBestRect(endValues);
1454ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final Rect startClip = getEpicenterOrCenter(endClip);
1464ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
1474ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        // Prepare the view.
1484ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        view.setClipBounds(startClip);
1494ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
1504ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final State startStateX = new State(startClip.left, startClip.right, startX);
1514ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final State endStateX = new State(endClip.left, endClip.right, endX);
1524ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final State startStateY = new State(startClip.top, startClip.bottom, startY);
1534ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final State endStateY = new State(endClip.top, endClip.bottom, endY);
1544ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
1554ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        return createRectAnimator(view, startStateX, startStateY, startZ, endStateX, endStateY,
1564ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette                endZ, endValues, mInterpolatorX, mInterpolatorY, mInterpolatorZ);
1574ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    }
1584ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
1594ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    @Override
1604ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    public Animator onDisappear(ViewGroup sceneRoot, View view,
1614ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            TransitionValues startValues, TransitionValues endValues) {
1624ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        if (startValues == null) {
1634ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            return null;
1644ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        }
1654ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
1664ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final Rect startBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS);
1674ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final Rect endBounds = getEpicenterOrCenter(startBounds);
1684ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final float endX = endBounds.centerX() - startBounds.centerX();
1694ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final float endY = endBounds.centerY() - startBounds.centerY();
1704ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final float endZ = 0 - (float) startValues.values.get(PROPNAME_Z);
1714ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
1724ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final float startX = (float) endValues.values.get(PROPNAME_TRANSLATE_X);
1734ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final float startY = (float) endValues.values.get(PROPNAME_TRANSLATE_Y);
1744ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final float startZ = (float) endValues.values.get(PROPNAME_TRANSLATE_Z);
1754ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
1764ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final Rect startClip = getBestRect(startValues);
1774ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final Rect endClip = getEpicenterOrCenter(startClip);
1784ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
1794ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        // Prepare the view.
1804ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        view.setClipBounds(startClip);
1814ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
1824ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final State startStateX = new State(startClip.left, startClip.right, startX);
1834ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final State endStateX = new State(endClip.left, endClip.right, endX);
1844ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final State startStateY = new State(startClip.top, startClip.bottom, startY);
1854ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final State endStateY = new State(endClip.top, endClip.bottom, endY);
1864ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
1874ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        return createRectAnimator(view, startStateX, startStateY, startZ, endStateX, endStateY,
1884ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette                endZ, endValues, mInterpolatorX, mInterpolatorY, mInterpolatorZ);
1894ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    }
1904ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
1914ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    private Rect getEpicenterOrCenter(Rect bestRect) {
1924ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final Rect epicenter = getEpicenter();
1934ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        if (epicenter != null) {
1944ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            return epicenter;
1954ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        }
1964ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
1974ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final int centerX = bestRect.centerX();
1984ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final int centerY = bestRect.centerY();
1994ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        return new Rect(centerX, centerY, centerX, centerY);
2004ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    }
2014ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
2024ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    private Rect getBestRect(TransitionValues values) {
2034ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final Rect clipRect = (Rect) values.values.get(PROPNAME_CLIP);
2044ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        if (clipRect == null) {
2054ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            return (Rect) values.values.get(PROPNAME_BOUNDS);
2064ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        }
2074ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        return clipRect;
2084ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    }
2094ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
2104ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    private static Animator createRectAnimator(final View view, State startX, State startY,
2114ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            float startZ, State endX, State endY, float endZ, TransitionValues endValues,
2124ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            TimeInterpolator interpolatorX, TimeInterpolator interpolatorY,
2134ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            TimeInterpolator interpolatorZ) {
2144ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final StateEvaluator evaluator = new StateEvaluator();
2154ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
2164ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final ObjectAnimator animZ = ObjectAnimator.ofFloat(view, View.TRANSLATION_Z, startZ, endZ);
2174ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        if (interpolatorZ != null) {
2184ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            animZ.setInterpolator(interpolatorZ);
2194ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        }
2204ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
2214ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final StateProperty propX = new StateProperty(StateProperty.TARGET_X);
2224ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final ObjectAnimator animX = ObjectAnimator.ofObject(view, propX, evaluator, startX, endX);
2234ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        if (interpolatorX != null) {
2244ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            animX.setInterpolator(interpolatorX);
2254ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        }
2264ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
2274ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final StateProperty propY = new StateProperty(StateProperty.TARGET_Y);
2284ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final ObjectAnimator animY = ObjectAnimator.ofObject(view, propY, evaluator, startY, endY);
2294ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        if (interpolatorY != null) {
2304ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            animY.setInterpolator(interpolatorY);
2314ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        }
2324ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
2334ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final Rect terminalClip = (Rect) endValues.values.get(PROPNAME_CLIP);
2344ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final AnimatorListenerAdapter animatorListener = new AnimatorListenerAdapter() {
2354ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            @Override
2364ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            public void onAnimationEnd(Animator animation) {
2374ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette                view.setClipBounds(terminalClip);
2384ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            }
2394ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        };
2404ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
2414ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        final AnimatorSet animSet = new AnimatorSet();
2424ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        animSet.playTogether(animX, animY, animZ);
2434ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        animSet.addListener(animatorListener);
2444ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        return animSet;
2454ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    }
2464ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
2474ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    private static class State {
2484ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        int lower;
2494ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        int upper;
2504ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        float trans;
2514ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
2524ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        public State() {}
2534ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
2544ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        public State(int lower, int upper, float trans) {
2554ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            this.lower = lower;
2564ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            this.upper = upper;
2574ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            this.trans = trans;
2584ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        }
2594ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    }
2604ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
2614ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    private static class StateEvaluator implements TypeEvaluator<State> {
2624ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        private final State mTemp = new State();
2634ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
2644ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        @Override
2654ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        public State evaluate(float fraction, State startValue, State endValue) {
2664ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            mTemp.upper = startValue.upper + (int) ((endValue.upper - startValue.upper) * fraction);
2674ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            mTemp.lower = startValue.lower + (int) ((endValue.lower - startValue.lower) * fraction);
2684ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            mTemp.trans = startValue.trans + (int) ((endValue.trans - startValue.trans) * fraction);
2694ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            return mTemp;
2704ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        }
2714ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    }
2724ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
2734ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    private static class StateProperty extends Property<View, State> {
2744ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        public static final char TARGET_X = 'x';
2754ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        public static final char TARGET_Y = 'y';
2764ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
2774ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        private final Rect mTempRect = new Rect();
2784ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        private final State mTempState = new State();
2794ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
2804ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        private final int mTargetDimension;
2814ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
2824ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        public StateProperty(char targetDimension) {
2834ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            super(State.class, "state_" + targetDimension);
2844ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
2854ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            mTargetDimension = targetDimension;
2864ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        }
2874ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
2884ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        @Override
2894ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        public State get(View object) {
2904ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            final Rect tempRect = mTempRect;
2914ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            if (!object.getClipBounds(tempRect)) {
2924ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette                tempRect.setEmpty();
2934ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            }
2944ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            final State tempState = mTempState;
2954ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            if (mTargetDimension == TARGET_X) {
2964ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette                tempState.trans = object.getTranslationX();
2974ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette                tempState.lower = tempRect.left + (int) tempState.trans;
2984ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette                tempState.upper = tempRect.right + (int) tempState.trans;
2994ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            } else {
3004ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette                tempState.trans = object.getTranslationY();
3014ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette                tempState.lower = tempRect.top + (int) tempState.trans;
3024ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette                tempState.upper = tempRect.bottom + (int) tempState.trans;
3034ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            }
3044ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            return tempState;
3054ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        }
3064ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
3074ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        @Override
3084ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        public void set(View object, State value) {
3094ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            final Rect tempRect = mTempRect;
3104ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            if (object.getClipBounds(tempRect)) {
3114ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette                if (mTargetDimension == TARGET_X) {
3124ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette                    tempRect.left = value.lower - (int) value.trans;
3134ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette                    tempRect.right = value.upper - (int) value.trans;
3144ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette                } else {
3154ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette                    tempRect.top = value.lower - (int) value.trans;
3164ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette                    tempRect.bottom = value.upper - (int) value.trans;
3174ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette                }
3184ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette                object.setClipBounds(tempRect);
3194ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            }
3204ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette
3214ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            if (mTargetDimension == TARGET_X) {
3224ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette                object.setTranslationX(value.trans);
3234ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            } else {
3244ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette                object.setTranslationY(value.trans);
3254ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette            }
3264ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette        }
3274ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette    }
3284ea5ee233022877c2e34ae83a48c24630f83713eAlan Viverette}
329