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
19faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.animation.Animator;
20faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.animation.AnimatorListenerAdapter;
2167e2ae86396c6d0f989285275cbf908dee5e71f7Aurimas Liutikasimport android.animation.AnimatorSet;
22faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.animation.ObjectAnimator;
23faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.animation.PropertyValuesHolder;
24faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.animation.RectEvaluator;
2567e2ae86396c6d0f989285275cbf908dee5e71f7Aurimas Liutikasimport android.content.Context;
2667e2ae86396c6d0f989285275cbf908dee5e71f7Aurimas Liutikasimport android.content.res.TypedArray;
27faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.graphics.Bitmap;
28faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.graphics.Canvas;
29ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mountimport android.graphics.Path;
3067e2ae86396c6d0f989285275cbf908dee5e71f7Aurimas Liutikasimport android.graphics.PointF;
31faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.graphics.Rect;
32faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.graphics.drawable.BitmapDrawable;
33ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mountimport android.graphics.drawable.Drawable;
34ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mountimport android.util.AttributeSet;
35ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mountimport android.util.Property;
36faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.view.View;
37faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.view.ViewGroup;
38faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
39b7573c2dbaff442a0f9f814bdc05aaa685574870George Mountimport com.android.internal.R;
40b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount
4108735185f8105710e18ad02297461bec9268e514Chet Haaseimport java.util.Map;
42faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
43faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase/**
44faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * This transition captures the layout bounds of target views before and after
45faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * the scene change and animates those changes during the transition.
46d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase *
47d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * <p>A ChangeBounds transition can be described in a resource file by using the
48b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount * tag <code>changeBounds</code>, using its attributes of
49b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount * {@link android.R.styleable#ChangeBounds} along with the other standard
50d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * attributes of {@link android.R.styleable#Transition}.</p>
51faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase */
52d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haasepublic class ChangeBounds extends Transition {
53faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
54d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    private static final String PROPNAME_BOUNDS = "android:changeBounds:bounds";
55b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount    private static final String PROPNAME_CLIP = "android:changeBounds:clip";
56d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    private static final String PROPNAME_PARENT = "android:changeBounds:parent";
57d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    private static final String PROPNAME_WINDOW_X = "android:changeBounds:windowX";
58d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    private static final String PROPNAME_WINDOW_Y = "android:changeBounds:windowY";
59af78bdd615ecd5ce9d41a6160ce9f53fa269b119Chet Haase    private static final String[] sTransitionProperties = {
60199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase            PROPNAME_BOUNDS,
61b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount            PROPNAME_CLIP,
62199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase            PROPNAME_PARENT,
63199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase            PROPNAME_WINDOW_X,
64199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase            PROPNAME_WINDOW_Y
65199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase    };
66199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase
67ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount    private static final Property<Drawable, PointF> DRAWABLE_ORIGIN_PROPERTY =
68ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount            new Property<Drawable, PointF>(PointF.class, "boundsOrigin") {
69ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                private Rect mBounds = new Rect();
70ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount
71ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                @Override
72ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                public void set(Drawable object, PointF value) {
73ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                    object.copyBounds(mBounds);
74ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                    mBounds.offsetTo(Math.round(value.x), Math.round(value.y));
75ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                    object.setBounds(mBounds);
76ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                }
77ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount
78ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                @Override
79ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                public PointF get(Drawable object) {
80ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                    object.copyBounds(mBounds);
81ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                    return new PointF(mBounds.left, mBounds.top);
82ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                }
83ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount    };
84ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount
85d359952459f96a9b57f50a7434b8660836c6e987George Mount    private static final Property<ViewBounds, PointF> TOP_LEFT_PROPERTY =
86d359952459f96a9b57f50a7434b8660836c6e987George Mount            new Property<ViewBounds, PointF>(PointF.class, "topLeft") {
87d359952459f96a9b57f50a7434b8660836c6e987George Mount                @Override
88d359952459f96a9b57f50a7434b8660836c6e987George Mount                public void set(ViewBounds viewBounds, PointF topLeft) {
89d359952459f96a9b57f50a7434b8660836c6e987George Mount                    viewBounds.setTopLeft(topLeft);
90d359952459f96a9b57f50a7434b8660836c6e987George Mount                }
91d359952459f96a9b57f50a7434b8660836c6e987George Mount
92d359952459f96a9b57f50a7434b8660836c6e987George Mount                @Override
93d359952459f96a9b57f50a7434b8660836c6e987George Mount                public PointF get(ViewBounds viewBounds) {
94d359952459f96a9b57f50a7434b8660836c6e987George Mount                    return null;
95d359952459f96a9b57f50a7434b8660836c6e987George Mount                }
96d359952459f96a9b57f50a7434b8660836c6e987George Mount            };
97d359952459f96a9b57f50a7434b8660836c6e987George Mount
98d359952459f96a9b57f50a7434b8660836c6e987George Mount    private static final Property<ViewBounds, PointF> BOTTOM_RIGHT_PROPERTY =
99d359952459f96a9b57f50a7434b8660836c6e987George Mount            new Property<ViewBounds, PointF>(PointF.class, "bottomRight") {
100d359952459f96a9b57f50a7434b8660836c6e987George Mount                @Override
101d359952459f96a9b57f50a7434b8660836c6e987George Mount                public void set(ViewBounds viewBounds, PointF bottomRight) {
102d359952459f96a9b57f50a7434b8660836c6e987George Mount                    viewBounds.setBottomRight(bottomRight);
103d359952459f96a9b57f50a7434b8660836c6e987George Mount                }
104d359952459f96a9b57f50a7434b8660836c6e987George Mount
105d359952459f96a9b57f50a7434b8660836c6e987George Mount                @Override
106d359952459f96a9b57f50a7434b8660836c6e987George Mount                public PointF get(ViewBounds viewBounds) {
107d359952459f96a9b57f50a7434b8660836c6e987George Mount                    return null;
108d359952459f96a9b57f50a7434b8660836c6e987George Mount                }
109d359952459f96a9b57f50a7434b8660836c6e987George Mount            };
110d359952459f96a9b57f50a7434b8660836c6e987George Mount
111b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount    private static final Property<View, PointF> BOTTOM_RIGHT_ONLY_PROPERTY =
112b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount            new Property<View, PointF>(PointF.class, "bottomRight") {
113b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                @Override
114b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                public void set(View view, PointF bottomRight) {
115b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    int left = view.getLeft();
116b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    int top = view.getTop();
117b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    int right = Math.round(bottomRight.x);
118b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    int bottom = Math.round(bottomRight.y);
119b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    view.setLeftTopRightBottom(left, top, right, bottom);
120b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                }
121b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount
122b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                @Override
123b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                public PointF get(View view) {
124b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    return null;
125b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                }
126b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount            };
127b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount
128b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount    private static final Property<View, PointF> TOP_LEFT_ONLY_PROPERTY =
129b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount            new Property<View, PointF>(PointF.class, "topLeft") {
130b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                @Override
131b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                public void set(View view, PointF topLeft) {
132b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    int left = Math.round(topLeft.x);
133b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    int top = Math.round(topLeft.y);
134b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    int right = view.getRight();
135b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    int bottom = view.getBottom();
136b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    view.setLeftTopRightBottom(left, top, right, bottom);
137b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                }
138b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount
139b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                @Override
140b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                public PointF get(View view) {
141b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    return null;
142b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                }
143b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount            };
144b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount
145b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount    private static final Property<View, PointF> POSITION_PROPERTY =
146b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount            new Property<View, PointF>(PointF.class, "position") {
147b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                @Override
148b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                public void set(View view, PointF topLeft) {
149b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    int left = Math.round(topLeft.x);
150b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    int top = Math.round(topLeft.y);
151b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    int right = left + view.getWidth();
152b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    int bottom = top + view.getHeight();
153b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    view.setLeftTopRightBottom(left, top, right, bottom);
154b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                }
155b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount
156b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                @Override
157b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                public PointF get(View view) {
158b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    return null;
159b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                }
160b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount            };
161b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount
162faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    int[] tempLocation = new int[2];
163faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    boolean mResizeClip = false;
164faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    boolean mReparent = false;
165d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    private static final String LOG_TAG = "ChangeBounds";
166faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
167faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    private static RectEvaluator sRectEvaluator = new RectEvaluator();
168faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
169ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount    public ChangeBounds() {}
170ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount
171ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount    public ChangeBounds(Context context, AttributeSet attrs) {
172ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount        super(context, attrs);
173b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount
174b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ChangeBounds);
175b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount        boolean resizeClip = a.getBoolean(R.styleable.ChangeBounds_resizeClip, false);
176b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount        a.recycle();
177b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount        setResizeClip(resizeClip);
178ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount    }
179ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount
180199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase    @Override
181199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase    public String[] getTransitionProperties() {
182199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase        return sTransitionProperties;
183199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase    }
184199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase
185b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount    /**
186b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount     * When <code>resizeClip</code> is true, ChangeBounds resizes the view using the clipBounds
187b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount     * instead of changing the dimensions of the view during the animation. When
188b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount     * <code>resizeClip</code> is false, ChangeBounds resizes the View by changing its dimensions.
189b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount     *
190b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount     * <p>When resizeClip is set to true, the clip bounds is modified by ChangeBounds. Therefore,
191b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount     * {@link android.transition.ChangeClipBounds} is not compatible with ChangeBounds
192b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount     * in this mode.</p>
193b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount     *
194b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount     * @param resizeClip Used to indicate whether the view bounds should be modified or the
195b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount     *                   clip bounds should be modified by ChangeBounds.
196b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount     * @see android.view.View#setClipBounds(android.graphics.Rect)
197b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount     * @attr ref android.R.styleable#ChangeBounds_resizeClip
198b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount     */
199faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    public void setResizeClip(boolean resizeClip) {
200faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        mResizeClip = resizeClip;
201faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    }
202faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
203faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    /**
204b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount     * Returns true when the ChangeBounds will resize by changing the clip bounds during the
205b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount     * view animation or false when bounds are changed. The default value is false.
206b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount     *
207b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount     * @return true when the ChangeBounds will resize by changing the clip bounds during the
208b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount     * view animation or false when bounds are changed. The default value is false.
209b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount     * @attr ref android.R.styleable#ChangeBounds_resizeClip
210b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount     */
211b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount    public boolean getResizeClip() {
212b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount        return mResizeClip;
213b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount    }
214b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount
215b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount    /**
216d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase     * Setting this flag tells ChangeBounds to track the before/after parent
217faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * of every view using this transition. The flag is not enabled by
218faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * default because it requires the parent instances to be the same
219faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * in the two scenes or else all parents must use ids to allow
220faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * the transition to determine which parents are the same.
221faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     *
222faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @param reparent true if the transition should track the parent
223faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * container of target views and animate parent changes.
224c94e2b393f6eba684ee2c84eaa50746fc1459d0fDake Gu     * @deprecated Use {@link android.transition.ChangeTransform} to handle
225c94e2b393f6eba684ee2c84eaa50746fc1459d0fDake Gu     * transitions between different parents.
226faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     */
227514c5ef8d5774d8820ed1bf90fe53af1606cf106Aurimas Liutikas    @Deprecated
228faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    public void setReparent(boolean reparent) {
229faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        mReparent = reparent;
230faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    }
231faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
232d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    private void captureValues(TransitionValues values) {
233faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        View view = values.view;
234f791703f9bc566bee4f4809910d09dd415342078Chris Craik
2355ac9b20b41f72baabcb8a7d114bcaac3ed37e352George Mount        if (view.isLaidOut() || view.getWidth() != 0 || view.getHeight() != 0) {
236f791703f9bc566bee4f4809910d09dd415342078Chris Craik            values.values.put(PROPNAME_BOUNDS, new Rect(view.getLeft(), view.getTop(),
237f791703f9bc566bee4f4809910d09dd415342078Chris Craik                    view.getRight(), view.getBottom()));
238f791703f9bc566bee4f4809910d09dd415342078Chris Craik            values.values.put(PROPNAME_PARENT, values.view.getParent());
239f791703f9bc566bee4f4809910d09dd415342078Chris Craik            if (mReparent) {
240f791703f9bc566bee4f4809910d09dd415342078Chris Craik                values.view.getLocationInWindow(tempLocation);
241f791703f9bc566bee4f4809910d09dd415342078Chris Craik                values.values.put(PROPNAME_WINDOW_X, tempLocation[0]);
242f791703f9bc566bee4f4809910d09dd415342078Chris Craik                values.values.put(PROPNAME_WINDOW_Y, tempLocation[1]);
243f791703f9bc566bee4f4809910d09dd415342078Chris Craik            }
244b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount            if (mResizeClip) {
245b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                values.values.put(PROPNAME_CLIP, view.getClipBounds());
246b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount            }
2474d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount        }
248faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    }
249faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
250faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    @Override
251d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    public void captureStartValues(TransitionValues transitionValues) {
252d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase        captureValues(transitionValues);
253d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    }
254d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase
255d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    @Override
256d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    public void captureEndValues(TransitionValues transitionValues) {
257d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase        captureValues(transitionValues);
258d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    }
259d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase
2604d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount    private boolean parentMatches(View startParent, View endParent) {
2614d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount        boolean parentMatches = true;
2624d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount        if (mReparent) {
2634d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount            TransitionValues endValues = getMatchedTransitionValues(startParent, true);
2644d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount            if (endValues == null) {
2654d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount                parentMatches = startParent == endParent;
2664d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount            } else {
2674d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount                parentMatches = endParent == endValues.view;
2684d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount            }
2694d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount        }
2704d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount        return parentMatches;
2714d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount    }
2724d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount
273d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    @Override
274d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    public Animator createAnimator(final ViewGroup sceneRoot, TransitionValues startValues,
275faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            TransitionValues endValues) {
276faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        if (startValues == null || endValues == null) {
277faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            return null;
278faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        }
2792ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase        Map<String, Object> startParentVals = startValues.values;
2802ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase        Map<String, Object> endParentVals = endValues.values;
2812ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase        ViewGroup startParent = (ViewGroup) startParentVals.get(PROPNAME_PARENT);
2822ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase        ViewGroup endParent = (ViewGroup) endParentVals.get(PROPNAME_PARENT);
283faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        if (startParent == null || endParent == null) {
284faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            return null;
285faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        }
2862ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase        final View view = endValues.view;
2874d1ecf5daf6ad0b5420f3f3657e2cbaeea630240George Mount        if (parentMatches(startParent, endParent)) {
288faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            Rect startBounds = (Rect) startValues.values.get(PROPNAME_BOUNDS);
289faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS);
290b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount            final int startLeft = startBounds.left;
291b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount            final int endLeft = endBounds.left;
292b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount            final int startTop = startBounds.top;
293b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount            final int endTop = endBounds.top;
294b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount            final int startRight = startBounds.right;
295b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount            final int endRight = endBounds.right;
296b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount            final int startBottom = startBounds.bottom;
297b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount            final int endBottom = endBounds.bottom;
298b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount            final int startWidth = startRight - startLeft;
299b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount            final int startHeight = startBottom - startTop;
300b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount            final int endWidth = endRight - endLeft;
301b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount            final int endHeight = endBottom - endTop;
302b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount            Rect startClip = (Rect) startValues.values.get(PROPNAME_CLIP);
303b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount            Rect endClip = (Rect) endValues.values.get(PROPNAME_CLIP);
304faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            int numChanges = 0;
3052cacfe3a98ff4f777503cedca5e6fc44e6df269fGeorge Mount            if ((startWidth != 0 && startHeight != 0) || (endWidth != 0 && endHeight != 0)) {
306ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                if (startLeft != endLeft || startTop != endTop) ++numChanges;
307ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                if (startRight != endRight || startBottom != endBottom) ++numChanges;
308faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            }
309b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount            if ((startClip != null && !startClip.equals(endClip)) ||
310b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    (startClip == null && endClip != null)) {
311b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                ++numChanges;
312b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount            }
313faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            if (numChanges > 0) {
314b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                Animator anim;
315faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                if (!mResizeClip) {
316d359952459f96a9b57f50a7434b8660836c6e987George Mount                    view.setLeftTopRightBottom(startLeft, startTop, startRight, startBottom);
317b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    if (numChanges == 2) {
318b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                        if (startWidth == endWidth && startHeight == endHeight) {
319b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                            Path topLeftPath = getPathMotion().getPath(startLeft, startTop, endLeft,
320b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                                    endTop);
321b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                            anim = ObjectAnimator.ofObject(view, POSITION_PROPERTY, null,
322b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                                    topLeftPath);
323b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                        } else {
324b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                            final ViewBounds viewBounds = new ViewBounds(view);
325b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                            Path topLeftPath = getPathMotion().getPath(startLeft, startTop,
326b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                                    endLeft, endTop);
327b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                            ObjectAnimator topLeftAnimator = ObjectAnimator
328b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                                    .ofObject(viewBounds, TOP_LEFT_PROPERTY, null, topLeftPath);
329b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount
330b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                            Path bottomRightPath = getPathMotion().getPath(startRight, startBottom,
331b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                                    endRight, endBottom);
332b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                            ObjectAnimator bottomRightAnimator = ObjectAnimator.ofObject(viewBounds,
333b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                                    BOTTOM_RIGHT_PROPERTY, null, bottomRightPath);
334b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                            AnimatorSet set = new AnimatorSet();
335b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                            set.playTogether(topLeftAnimator, bottomRightAnimator);
336b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                            anim = set;
337b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                            set.addListener(new AnimatorListenerAdapter() {
338b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                                // We need a strong reference to viewBounds until the
339b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                                // animator ends.
340b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                                private ViewBounds mViewBounds = viewBounds;
341b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                            });
342b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                        }
343b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    } else if (startLeft != endLeft || startTop != endTop) {
344b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                        Path topLeftPath = getPathMotion().getPath(startLeft, startTop,
345b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                                endLeft, endTop);
346b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                        anim = ObjectAnimator.ofObject(view, TOP_LEFT_ONLY_PROPERTY, null,
347b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                                topLeftPath);
348b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    } else {
349b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                        Path bottomRight = getPathMotion().getPath(startRight, startBottom,
350b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                                endRight, endBottom);
351b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                        anim = ObjectAnimator.ofObject(view, BOTTOM_RIGHT_ONLY_PROPERTY, null,
352b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                                bottomRight);
353faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    }
354faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                } else {
355b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    int maxWidth = Math.max(startWidth, endWidth);
356b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    int maxHeight = Math.max(startHeight, endHeight);
357b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount
358b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    view.setLeftTopRightBottom(startLeft, startTop, startLeft + maxWidth,
359b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                            startTop + maxHeight);
360b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount
361b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    ObjectAnimator positionAnimator = null;
362b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    if (startLeft != endLeft || startTop != endTop) {
363b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                        Path topLeftPath = getPathMotion().getPath(startLeft, startTop, endLeft,
364b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                                endTop);
365b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                        positionAnimator = ObjectAnimator.ofObject(view, POSITION_PROPERTY, null,
366b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                                topLeftPath);
367b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    }
368b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    final Rect finalClip = endClip;
369b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    if (startClip == null) {
370b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                        startClip = new Rect(0, 0, startWidth, startHeight);
371b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    }
372b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    if (endClip == null) {
373b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                        endClip = new Rect(0, 0, endWidth, endHeight);
374faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    }
375ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                    ObjectAnimator clipAnimator = null;
376b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    if (!startClip.equals(endClip)) {
377b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                        view.setClipBounds(startClip);
378ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                        clipAnimator = ObjectAnimator.ofObject(view, "clipBounds", sRectEvaluator,
379b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                                startClip, endClip);
380b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                        clipAnimator.addListener(new AnimatorListenerAdapter() {
381b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                            private boolean mIsCanceled;
382199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase
383faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                            @Override
384b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                            public void onAnimationCancel(Animator animation) {
385b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                                mIsCanceled = true;
386199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                            }
387199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase
388199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                            @Override
389b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                            public void onAnimationEnd(Animator animation) {
390b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                                if (!mIsCanceled) {
391b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                                    view.setClipBounds(finalClip);
392b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                                    view.setLeftTopRightBottom(endLeft, endTop, endRight,
393b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                                            endBottom);
394199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                                }
395199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                            }
396b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                        });
397b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    }
398b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    anim = TransitionUtils.mergeAnimators(positionAnimator,
399b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                            clipAnimator);
400b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                }
401b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                if (view.getParent() instanceof ViewGroup) {
402b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    final ViewGroup parent = (ViewGroup) view.getParent();
403b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    parent.suppressLayout(true);
404b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    TransitionListener transitionListener = new TransitionListenerAdapter() {
405b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                        boolean mCanceled = false;
406199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase
407b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                        @Override
408b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                        public void onTransitionCancel(Transition transition) {
409b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                            parent.suppressLayout(false);
410b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                            mCanceled = true;
411b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                        }
412b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount
413b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                        @Override
414b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                        public void onTransitionEnd(Transition transition) {
415b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                            if (!mCanceled) {
416199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                                parent.suppressLayout(false);
417199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                            }
41825f98a4c0156ec40d1d92cfe5a24459d9ffa6f91George Mount                            transition.removeListener(this);
419b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                        }
420b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount
421b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                        @Override
422b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                        public void onTransitionPause(Transition transition) {
423b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                            parent.suppressLayout(false);
424b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                        }
425199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase
426faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                        @Override
427b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                        public void onTransitionResume(Transition transition) {
428b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                            parent.suppressLayout(true);
429faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                        }
430b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    };
431b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                    addListener(transitionListener);
432faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                }
433b7573c2dbaff442a0f9f814bdc05aaa685574870George Mount                return anim;
434faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            }
435faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        } else {
436f2a3703696f5dc8b1a0d243051e0db65c2145452George Mount            sceneRoot.getLocationInWindow(tempLocation);
437f2a3703696f5dc8b1a0d243051e0db65c2145452George Mount            int startX = (Integer) startValues.values.get(PROPNAME_WINDOW_X) - tempLocation[0];
438f2a3703696f5dc8b1a0d243051e0db65c2145452George Mount            int startY = (Integer) startValues.values.get(PROPNAME_WINDOW_Y) - tempLocation[1];
439f2a3703696f5dc8b1a0d243051e0db65c2145452George Mount            int endX = (Integer) endValues.values.get(PROPNAME_WINDOW_X) - tempLocation[0];
440f2a3703696f5dc8b1a0d243051e0db65c2145452George Mount            int endY = (Integer) endValues.values.get(PROPNAME_WINDOW_Y) - tempLocation[1];
441faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            // TODO: also handle size changes: check bounds and animate size changes
442faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            if (startX != endX || startY != endY) {
443f2a3703696f5dc8b1a0d243051e0db65c2145452George Mount                final int width = view.getWidth();
444f2a3703696f5dc8b1a0d243051e0db65c2145452George Mount                final int height = view.getHeight();
445f2a3703696f5dc8b1a0d243051e0db65c2145452George Mount                Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
446faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                Canvas canvas = new Canvas(bitmap);
447faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                view.draw(canvas);
448faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                final BitmapDrawable drawable = new BitmapDrawable(bitmap);
449f2a3703696f5dc8b1a0d243051e0db65c2145452George Mount                drawable.setBounds(startX, startY, startX + width, startY + height);
450b5ef7f8c6d4629b2998de6c0b27bc1a4779b3e49George Mount                final float transitionAlpha = view.getTransitionAlpha();
451b5ef7f8c6d4629b2998de6c0b27bc1a4779b3e49George Mount                view.setTransitionAlpha(0);
452faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                sceneRoot.getOverlay().add(drawable);
453f2a3703696f5dc8b1a0d243051e0db65c2145452George Mount                Path topLeftPath = getPathMotion().getPath(startX, startY, endX, endY);
454ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                PropertyValuesHolder origin = PropertyValuesHolder.ofObject(
455ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                        DRAWABLE_ORIGIN_PROPERTY, null, topLeftPath);
456ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount                ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(drawable, origin);
457faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                anim.addListener(new AnimatorListenerAdapter() {
458faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    @Override
459faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    public void onAnimationEnd(Animator animation) {
460faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                        sceneRoot.getOverlay().remove(drawable);
461b5ef7f8c6d4629b2998de6c0b27bc1a4779b3e49George Mount                        view.setTransitionAlpha(transitionAlpha);
462faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    }
463faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                });
4642ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                return anim;
465faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            }
466faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        }
4672ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase        return null;
468faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    }
469ff8e6d85dc5ef03b8d7c94b014fa4da800b5402cGeorge Mount
470d359952459f96a9b57f50a7434b8660836c6e987George Mount    private static class ViewBounds {
471d359952459f96a9b57f50a7434b8660836c6e987George Mount        private int mLeft;
472d359952459f96a9b57f50a7434b8660836c6e987George Mount        private int mTop;
473d359952459f96a9b57f50a7434b8660836c6e987George Mount        private int mRight;
474d359952459f96a9b57f50a7434b8660836c6e987George Mount        private int mBottom;
475d359952459f96a9b57f50a7434b8660836c6e987George Mount        private View mView;
476f31efa38f86f41965089f748ddb1085b35912b30George Mount        private int mTopLeftCalls;
477f31efa38f86f41965089f748ddb1085b35912b30George Mount        private int mBottomRightCalls;
478d359952459f96a9b57f50a7434b8660836c6e987George Mount
479d359952459f96a9b57f50a7434b8660836c6e987George Mount        public ViewBounds(View view) {
480d359952459f96a9b57f50a7434b8660836c6e987George Mount            mView = view;
481ff8e6d85dc5ef03b8d7c94b014fa4da800b5402cGeorge Mount        }
482ff8e6d85dc5ef03b8d7c94b014fa4da800b5402cGeorge Mount
483d359952459f96a9b57f50a7434b8660836c6e987George Mount        public void setTopLeft(PointF topLeft) {
484d359952459f96a9b57f50a7434b8660836c6e987George Mount            mLeft = Math.round(topLeft.x);
485d359952459f96a9b57f50a7434b8660836c6e987George Mount            mTop = Math.round(topLeft.y);
486f31efa38f86f41965089f748ddb1085b35912b30George Mount            mTopLeftCalls++;
487f31efa38f86f41965089f748ddb1085b35912b30George Mount            if (mTopLeftCalls == mBottomRightCalls) {
488d359952459f96a9b57f50a7434b8660836c6e987George Mount                setLeftTopRightBottom();
489d359952459f96a9b57f50a7434b8660836c6e987George Mount            }
490ff8e6d85dc5ef03b8d7c94b014fa4da800b5402cGeorge Mount        }
491ff8e6d85dc5ef03b8d7c94b014fa4da800b5402cGeorge Mount
492d359952459f96a9b57f50a7434b8660836c6e987George Mount        public void setBottomRight(PointF bottomRight) {
493d359952459f96a9b57f50a7434b8660836c6e987George Mount            mRight = Math.round(bottomRight.x);
494d359952459f96a9b57f50a7434b8660836c6e987George Mount            mBottom = Math.round(bottomRight.y);
495f31efa38f86f41965089f748ddb1085b35912b30George Mount            mBottomRightCalls++;
496f31efa38f86f41965089f748ddb1085b35912b30George Mount            if (mTopLeftCalls == mBottomRightCalls) {
497d359952459f96a9b57f50a7434b8660836c6e987George Mount                setLeftTopRightBottom();
498d359952459f96a9b57f50a7434b8660836c6e987George Mount            }
499ff8e6d85dc5ef03b8d7c94b014fa4da800b5402cGeorge Mount        }
500ff8e6d85dc5ef03b8d7c94b014fa4da800b5402cGeorge Mount
501d359952459f96a9b57f50a7434b8660836c6e987George Mount        private void setLeftTopRightBottom() {
502d359952459f96a9b57f50a7434b8660836c6e987George Mount            mView.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);
503f31efa38f86f41965089f748ddb1085b35912b30George Mount            mTopLeftCalls = 0;
504f31efa38f86f41965089f748ddb1085b35912b30George Mount            mBottomRightCalls = 0;
505ff8e6d85dc5ef03b8d7c94b014fa4da800b5402cGeorge Mount        }
506ff8e6d85dc5ef03b8d7c94b014fa4da800b5402cGeorge Mount    }
507faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase}
508