ChangeBounds.java revision 4d1ecf5daf6ad0b5420f3f3657e2cbaeea630240
1faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase/*
2faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * Copyright (C) 2013 The Android Open Source Project
3faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase *
4faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * Licensed under the Apache License, Version 2.0 (the "License");
5faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * you may not use this file except in compliance with the License.
6faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * You may obtain a copy of the License at
7faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase *
8faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase *      http://www.apache.org/licenses/LICENSE-2.0
9faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase *
10faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * Unless required by applicable law or agreed to in writing, software
11faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * distributed under the License is distributed on an "AS IS" BASIS,
12faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * See the License for the specific language governing permissions and
14faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * limitations under the License.
15faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase */
166ebe3de331efd00ba23bc4191d4a82cfa4c39160Chet Haase
17d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haasepackage android.transition;
18faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
19ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mountimport android.content.Context;
20ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mountimport android.graphics.PointF;
21ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount
22faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.animation.Animator;
23faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.animation.AnimatorListenerAdapter;
24faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.animation.ObjectAnimator;
25faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.animation.PropertyValuesHolder;
26faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.animation.RectEvaluator;
27faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.graphics.Bitmap;
28faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.graphics.Canvas;
29ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mountimport android.graphics.Path;
30faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.graphics.Rect;
31faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.graphics.drawable.BitmapDrawable;
32ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mountimport android.graphics.drawable.Drawable;
33ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mountimport android.util.AttributeSet;
34ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mountimport android.util.Property;
35faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.view.View;
36faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.view.ViewGroup;
37faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
3808735185f8105710e18ad02297461bec9268e514Chet Haaseimport java.util.Map;
39faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
40faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase/**
41faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * This transition captures the layout bounds of target views before and after
42faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * the scene change and animates those changes during the transition.
43d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase *
44d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * <p>A ChangeBounds transition can be described in a resource file by using the
45d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * tag <code>changeBounds</code>, along with the other standard
46d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * attributes of {@link android.R.styleable#Transition}.</p>
47faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase */
48d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haasepublic class ChangeBounds extends Transition {
49faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
50d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    private static final String PROPNAME_BOUNDS = "android:changeBounds:bounds";
51d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    private static final String PROPNAME_PARENT = "android:changeBounds:parent";
52d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    private static final String PROPNAME_WINDOW_X = "android:changeBounds:windowX";
53d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    private static final String PROPNAME_WINDOW_Y = "android:changeBounds:windowY";
54af78bdd615ecd5ce9d41a6160ce9f53fa269b119Chet Haase    private static final String[] sTransitionProperties = {
55199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase            PROPNAME_BOUNDS,
56199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase            PROPNAME_PARENT,
57199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase            PROPNAME_WINDOW_X,
58199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase            PROPNAME_WINDOW_Y
59199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase    };
60199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase
61ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount    private static final Property<Drawable, PointF> DRAWABLE_ORIGIN_PROPERTY =
62ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount            new Property<Drawable, PointF>(PointF.class, "boundsOrigin") {
63ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                private Rect mBounds = new Rect();
64ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount
65ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                @Override
66ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                public void set(Drawable object, PointF value) {
67ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                    object.copyBounds(mBounds);
68ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                    mBounds.offsetTo(Math.round(value.x), Math.round(value.y));
69ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                    object.setBounds(mBounds);
70ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                }
71ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount
72ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                @Override
73ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                public PointF get(Drawable object) {
74ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                    object.copyBounds(mBounds);
75ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                    return new PointF(mBounds.left, mBounds.top);
76ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                }
77ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount    };
78ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount
79faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    int[] tempLocation = new int[2];
80faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    boolean mResizeClip = false;
81faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    boolean mReparent = false;
82d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    private static final String LOG_TAG = "ChangeBounds";
83faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
84faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    private static RectEvaluator sRectEvaluator = new RectEvaluator();
85faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
86ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount    public ChangeBounds() {}
87ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount
88ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount    public ChangeBounds(Context context, AttributeSet attrs) {
89ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount        super(context, attrs);
90ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount    }
91ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount
92199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase    @Override
93199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase    public String[] getTransitionProperties() {
94199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase        return sTransitionProperties;
95199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase    }
96199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase
97faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    public void setResizeClip(boolean resizeClip) {
98faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        mResizeClip = resizeClip;
99faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    }
100faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
101faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    /**
102d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase     * Setting this flag tells ChangeBounds to track the before/after parent
103faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * of every view using this transition. The flag is not enabled by
104faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * default because it requires the parent instances to be the same
105faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * in the two scenes or else all parents must use ids to allow
106faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * the transition to determine which parents are the same.
107faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     *
108faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @param reparent true if the transition should track the parent
109faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * container of target views and animate parent changes.
110faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     */
111faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    public void setReparent(boolean reparent) {
112faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        mReparent = reparent;
113faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    }
114faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
115d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    private void captureValues(TransitionValues values) {
116faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        View view = values.view;
117faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        values.values.put(PROPNAME_BOUNDS, new Rect(view.getLeft(), view.getTop(),
118faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                view.getRight(), view.getBottom()));
119faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        values.values.put(PROPNAME_PARENT, values.view.getParent());
1204d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount        if (mReparent) {
1214d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount            values.view.getLocationInWindow(tempLocation);
1224d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount            values.values.put(PROPNAME_WINDOW_X, tempLocation[0]);
1234d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount            values.values.put(PROPNAME_WINDOW_Y, tempLocation[1]);
1244d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount        }
125faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    }
126faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
127faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    @Override
128d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    public void captureStartValues(TransitionValues transitionValues) {
129d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase        captureValues(transitionValues);
130d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    }
131d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase
132d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    @Override
133d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    public void captureEndValues(TransitionValues transitionValues) {
134d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase        captureValues(transitionValues);
135d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    }
136d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase
1374d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount    private boolean parentMatches(View startParent, View endParent) {
1384d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount        boolean parentMatches = true;
1394d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount        if (mReparent) {
1404d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount            TransitionValues endValues = getMatchedTransitionValues(startParent, true);
1414d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount            if (endValues == null) {
1424d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount                parentMatches = startParent == endParent;
1434d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount            } else {
1444d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount                parentMatches = endParent == endValues.view;
1454d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount            }
1464d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount        }
1474d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount        return parentMatches;
1484d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount    }
1494d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount
150d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    @Override
151d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    public Animator createAnimator(final ViewGroup sceneRoot, TransitionValues startValues,
152faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            TransitionValues endValues) {
153faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        if (startValues == null || endValues == null) {
154faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            return null;
155faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        }
1562ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase        Map<String, Object> startParentVals = startValues.values;
1572ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase        Map<String, Object> endParentVals = endValues.values;
1582ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase        ViewGroup startParent = (ViewGroup) startParentVals.get(PROPNAME_PARENT);
1592ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase        ViewGroup endParent = (ViewGroup) endParentVals.get(PROPNAME_PARENT);
160faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        if (startParent == null || endParent == null) {
161faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            return null;
162faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        }
1632ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase        final View view = endValues.view;
1644d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount        if (parentMatches(startParent, endParent)) {
165faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            Rect startBounds = (Rect) startValues.values.get(PROPNAME_BOUNDS);
166faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS);
167faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            int startLeft = startBounds.left;
168faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            int endLeft = endBounds.left;
169faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            int startTop = startBounds.top;
170faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            int endTop = endBounds.top;
171faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            int startRight = startBounds.right;
172faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            int endRight = endBounds.right;
173faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            int startBottom = startBounds.bottom;
174faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            int endBottom = endBounds.bottom;
175faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            int startWidth = startRight - startLeft;
176faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            int startHeight = startBottom - startTop;
177faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            int endWidth = endRight - endLeft;
178faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            int endHeight = endBottom - endTop;
179faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            int numChanges = 0;
1802cacfe3a98ff4f777503cedca5e6fc44e6df269fGeorge Mount            if ((startWidth != 0 && startHeight != 0) || (endWidth != 0 && endHeight != 0)) {
181ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                if (startLeft != endLeft || startTop != endTop) ++numChanges;
182ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                if (startRight != endRight || startBottom != endBottom) ++numChanges;
183faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            }
184faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            if (numChanges > 0) {
185faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                if (!mResizeClip) {
1862ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                    if (startLeft != endLeft) view.setLeft(startLeft);
1872ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                    if (startTop != endTop) view.setTop(startTop);
1882ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                    if (startRight != endRight) view.setRight(startRight);
1892ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                    if (startBottom != endBottom) view.setBottom(startBottom);
190ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                    ObjectAnimator topLeftAnimator = null;
191ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                    if (startLeft != endLeft || startTop != endTop) {
192ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                        Path topLeftPath = getPathMotion().getPath(startLeft, startTop,
193ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                                endLeft, endTop);
194ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                        topLeftAnimator = ObjectAnimator.ofInt(view, "left", "top", topLeftPath);
195faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    }
196ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                    ObjectAnimator bottomRightAnimator = null;
197ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                    if (startRight != endRight || startBottom != endBottom) {
198ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                        Path bottomRightPath = getPathMotion().getPath(startRight, startBottom,
199ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                                endRight, endBottom);
200ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                        bottomRightAnimator = ObjectAnimator.ofInt(view, "right", "bottom",
201ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                                bottomRightPath);
202faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    }
2032dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos                    Animator anim = TransitionUtils.mergeAnimators(topLeftAnimator,
2042dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos                            bottomRightAnimator);
205faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    if (view.getParent() instanceof ViewGroup) {
206faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                        final ViewGroup parent = (ViewGroup) view.getParent();
207faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                        parent.suppressLayout(true);
208199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                        TransitionListener transitionListener = new TransitionListenerAdapter() {
209199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                            boolean mCanceled = false;
210199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase
211faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                            @Override
212199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                            public void onTransitionCancel(Transition transition) {
213faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                                parent.suppressLayout(false);
214199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                                mCanceled = true;
215199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                            }
216199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase
217199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                            @Override
218199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                            public void onTransitionEnd(Transition transition) {
219199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                                if (!mCanceled) {
220199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                                    parent.suppressLayout(false);
221199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                                }
222199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                            }
223199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase
224199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                            @Override
225199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                            public void onTransitionPause(Transition transition) {
226199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                                parent.suppressLayout(false);
227199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                            }
228199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase
229199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                            @Override
230199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                            public void onTransitionResume(Transition transition) {
231199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                                parent.suppressLayout(true);
232faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                            }
233199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                        };
234199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                        addListener(transitionListener);
235faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    }
236faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    return anim;
237faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                } else {
2382ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                    if (startWidth != endWidth) view.setRight(endLeft +
2392ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                            Math.max(startWidth, endWidth));
2402ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                    if (startHeight != endHeight) view.setBottom(endTop +
2412ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                            Math.max(startHeight, endHeight));
2422ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                    // TODO: don't clobber TX/TY
2432ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                    if (startLeft != endLeft) view.setTranslationX(startLeft - endLeft);
2442ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                    if (startTop != endTop) view.setTranslationY(startTop - endTop);
245faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    // Animate location with translationX/Y and size with clip bounds
246faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    float transXDelta = endLeft - startLeft;
247faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    float transYDelta = endTop - startTop;
248faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    int widthDelta = endWidth - startWidth;
249faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    int heightDelta = endHeight - startHeight;
250faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    numChanges = 0;
251faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    if (transXDelta != 0) numChanges++;
252faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    if (transYDelta != 0) numChanges++;
253faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    if (widthDelta != 0 || heightDelta != 0) numChanges++;
254ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                    ObjectAnimator translationAnimator = null;
255ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                    if (transXDelta != 0 || transYDelta != 0) {
256ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                        Path topLeftPath = getPathMotion().getPath(0, 0, transXDelta, transYDelta);
257ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                        translationAnimator = ObjectAnimator.ofFloat(view, View.TRANSLATION_X,
258ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                                View.TRANSLATION_Y, topLeftPath);
259faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    }
260ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                    ObjectAnimator clipAnimator = null;
261faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    if (widthDelta != 0 || heightDelta != 0) {
262faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                        Rect tempStartBounds = new Rect(0, 0, startWidth, startHeight);
263faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                        Rect tempEndBounds = new Rect(0, 0, endWidth, endHeight);
264ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                        clipAnimator = ObjectAnimator.ofObject(view, "clipBounds", sRectEvaluator,
265ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                                tempStartBounds, tempEndBounds);
266faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    }
2672dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos                    Animator anim = TransitionUtils.mergeAnimators(translationAnimator,
2682dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos                            clipAnimator);
269faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    if (view.getParent() instanceof ViewGroup) {
270faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                        final ViewGroup parent = (ViewGroup) view.getParent();
271faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                        parent.suppressLayout(true);
272199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                        TransitionListener transitionListener = new TransitionListenerAdapter() {
273199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                            boolean mCanceled = false;
274199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase
275faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                            @Override
276199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                            public void onTransitionCancel(Transition transition) {
277faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                                parent.suppressLayout(false);
278199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                                mCanceled = true;
279199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                            }
280199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase
281199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                            @Override
282199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                            public void onTransitionEnd(Transition transition) {
283199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                                if (!mCanceled) {
284199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                                    parent.suppressLayout(false);
285199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                                }
286199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                            }
287199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase
288199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                            @Override
289199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                            public void onTransitionPause(Transition transition) {
290199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                                parent.suppressLayout(false);
291199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                            }
292199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase
293199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                            @Override
294199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                            public void onTransitionResume(Transition transition) {
295199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                                parent.suppressLayout(true);
296faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                            }
297199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                        };
298199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                        addListener(transitionListener);
299faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    }
300faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    anim.addListener(new AnimatorListenerAdapter() {
301faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                        @Override
302faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                        public void onAnimationEnd(Animator animation) {
303faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                            view.setClipBounds(null);
304faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                        }
305faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    });
306faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    return anim;
307faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                }
308faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            }
309faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        } else {
310faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            int startX = (Integer) startValues.values.get(PROPNAME_WINDOW_X);
311faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            int startY = (Integer) startValues.values.get(PROPNAME_WINDOW_Y);
312faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            int endX = (Integer) endValues.values.get(PROPNAME_WINDOW_X);
313faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            int endY = (Integer) endValues.values.get(PROPNAME_WINDOW_Y);
314faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            // TODO: also handle size changes: check bounds and animate size changes
315faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            if (startX != endX || startY != endY) {
316faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                sceneRoot.getLocationInWindow(tempLocation);
317faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),
318faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                        Bitmap.Config.ARGB_8888);
319faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                Canvas canvas = new Canvas(bitmap);
320faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                view.draw(canvas);
321faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                final BitmapDrawable drawable = new BitmapDrawable(bitmap);
322b5ef7f8c6d4629b2998de6c0b27bc1a4779b3e49George Mount                final float transitionAlpha = view.getTransitionAlpha();
323b5ef7f8c6d4629b2998de6c0b27bc1a4779b3e49George Mount                view.setTransitionAlpha(0);
324faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                sceneRoot.getOverlay().add(drawable);
325ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                Path topLeftPath = getPathMotion().getPath(startX - tempLocation[0],
326ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                        startY - tempLocation[1], endX - tempLocation[0], endY - tempLocation[1]);
327ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                PropertyValuesHolder origin = PropertyValuesHolder.ofObject(
328ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                        DRAWABLE_ORIGIN_PROPERTY, null, topLeftPath);
329ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(drawable, origin);
330faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                anim.addListener(new AnimatorListenerAdapter() {
331faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    @Override
332faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    public void onAnimationEnd(Animator animation) {
333faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                        sceneRoot.getOverlay().remove(drawable);
334b5ef7f8c6d4629b2998de6c0b27bc1a4779b3e49George Mount                        view.setTransitionAlpha(transitionAlpha);
335faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    }
336faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                });
3372ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                return anim;
338faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            }
339faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        }
3402ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase        return null;
341faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    }
342faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase}
343