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;
21faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.animation.AnimatorSet;
22faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.animation.ObjectAnimator;
23faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.animation.RectEvaluator;
24faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.animation.ValueAnimator;
25faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.graphics.Bitmap;
26faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.graphics.Canvas;
27faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.graphics.Rect;
28faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.graphics.drawable.BitmapDrawable;
29faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.util.Log;
30faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.view.SurfaceView;
31faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.view.TextureView;
32faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.view.View;
33faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.view.ViewGroup;
344f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haaseimport android.view.ViewOverlay;
35faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
3608735185f8105710e18ad02297461bec9268e514Chet Haaseimport java.util.Map;
37faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
38faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase/**
39faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * This transition captures bitmap representations of target views before and
40faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * after the scene change and fades between them.
41faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase *
42faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * <p>Note: This transition is not compatible with {@link TextureView}
43faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * or {@link SurfaceView}.</p>
44d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase *
45d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * @hide
46faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase */
47faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haasepublic class Crossfade extends Transition {
48faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    // TODO: Add a hook that lets a Transition call user code to query whether it should run on
49faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    // a given target view. This would save bitmap comparisons in this transition, for example.
50faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
51faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    private static final String LOG_TAG = "Crossfade";
52faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
53faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    private static final String PROPNAME_BITMAP = "android:crossfade:bitmap";
54faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    private static final String PROPNAME_DRAWABLE = "android:crossfade:drawable";
55faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    private static final String PROPNAME_BOUNDS = "android:crossfade:bounds";
56faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
57faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    private static RectEvaluator sRectEvaluator = new RectEvaluator();
58faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
594f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase    private int mFadeBehavior = FADE_BEHAVIOR_REVEAL;
604f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase    private int mResizeBehavior = RESIZE_BEHAVIOR_SCALE;
614f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase
624f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase    /**
634f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * Flag specifying that the fading animation should cross-fade
644f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * between the old and new representation of all affected target
654f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * views. This means that the old representation will fade out
664f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * while the new one fades in. This effect may work well on views
674f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * without solid backgrounds, such as TextViews.
684f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     *
694f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * @see #setFadeBehavior(int)
704f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     */
714f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase    public static final int FADE_BEHAVIOR_CROSSFADE = 0;
724f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase    /**
734f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * Flag specifying that the fading animation should reveal the
744f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * new representation of all affected target views. This means
754f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * that the old representation will fade out, gradually
764f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * revealing the new representation, which remains opaque
774f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * the whole time. This effect may work well on views
784f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * with solid backgrounds, such as ImageViews.
794f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     *
804f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * @see #setFadeBehavior(int)
814f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     */
824f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase    public static final int FADE_BEHAVIOR_REVEAL = 1;
83c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase    /**
84c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase     * Flag specifying that the fading animation should first fade
85c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase     * out the original representation completely and then fade in the
86c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase     * new one. This effect may be more suitable than the other
87c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase     * fade behaviors for views with.
88c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase     *
89c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase     * @see #setFadeBehavior(int)
90c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase     */
91c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase    public static final int FADE_BEHAVIOR_OUT_IN = 2;
924f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase
934f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase    /**
944f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * Flag specifying that the transition should not animate any
954f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * changes in size between the old and new target views.
964f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * This means that no scaling will take place as a result of
974f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * this transition
984f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     *
994f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * @see #setResizeBehavior(int)
1004f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     */
1014f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase    public static final int RESIZE_BEHAVIOR_NONE = 0;
1024f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase    /**
1034f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * Flag specifying that the transition should animate any
1044f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * changes in size between the old and new target views.
1054f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * This means that the animation will scale the start/end
1064f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * representations of affected views from the starting size
1074f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * to the ending size over the course of the animation.
1084f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * This effect may work well on images, but is not recommended
1094f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * for text.
1104f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     *
1114f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * @see #setResizeBehavior(int)
1124f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     */
1134f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase    public static final int RESIZE_BEHAVIOR_SCALE = 1;
1144f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase
1154f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase    // TODO: Add fade/resize behaviors to xml resources
1164f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase
1174f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase    /**
1184f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * Sets the type of fading animation that will be run, one of
1194f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * {@link #FADE_BEHAVIOR_CROSSFADE} and {@link #FADE_BEHAVIOR_REVEAL}.
1204f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     *
1214f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * @param fadeBehavior The type of fading animation to use when this
1224f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * transition is run.
1234f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     */
124d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    public Crossfade setFadeBehavior(int fadeBehavior) {
125c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase        if (fadeBehavior >= FADE_BEHAVIOR_CROSSFADE && fadeBehavior <= FADE_BEHAVIOR_OUT_IN) {
1264f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase            mFadeBehavior = fadeBehavior;
1274f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase        }
128d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase        return this;
1294f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase    }
1304f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase
131d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    /**
132d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase     * Returns the fading behavior of the animation.
133d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase     *
134d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase     * @return This crossfade object.
135d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase     * @see #setFadeBehavior(int)
136d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase     */
1374f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase    public int getFadeBehavior() {
1384f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase        return mFadeBehavior;
1394f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase    }
1404f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase
1414f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase    /**
1424f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * Sets the type of resizing behavior that will be used during the
1434f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * transition animation, one of {@link #RESIZE_BEHAVIOR_NONE} and
1444f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * {@link #RESIZE_BEHAVIOR_SCALE}.
1454f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     *
1464f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * @param resizeBehavior The type of resizing behavior to use when this
1474f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     * transition is run.
1484f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase     */
149d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    public Crossfade setResizeBehavior(int resizeBehavior) {
1504f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase        if (resizeBehavior >= RESIZE_BEHAVIOR_NONE && resizeBehavior <= RESIZE_BEHAVIOR_SCALE) {
1514f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase            mResizeBehavior = resizeBehavior;
1524f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase        }
153d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase        return this;
1544f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase    }
1554f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase
156d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    /**
157d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase     * Returns the resizing behavior of the animation.
158d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase     *
159d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase     * @return This crossfade object.
160d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase     * @see #setResizeBehavior(int)
161d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase     */
1624f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase    public int getResizeBehavior() {
1634f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase        return mResizeBehavior;
1644f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase    }
1654f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase
166faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    @Override
167d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
168faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            TransitionValues endValues) {
169faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        if (startValues == null || endValues == null) {
1702ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase            return null;
171faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        }
172c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase        final boolean useParentOverlay = mFadeBehavior != FADE_BEHAVIOR_REVEAL;
1734f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase        final View view = endValues.view;
17408735185f8105710e18ad02297461bec9268e514Chet Haase        Map<String, Object> startVals = startValues.values;
17508735185f8105710e18ad02297461bec9268e514Chet Haase        Map<String, Object> endVals = endValues.values;
1762ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase        Rect startBounds = (Rect) startVals.get(PROPNAME_BOUNDS);
1772ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase        Rect endBounds = (Rect) endVals.get(PROPNAME_BOUNDS);
178faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        Bitmap startBitmap = (Bitmap) startVals.get(PROPNAME_BITMAP);
179faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        Bitmap endBitmap = (Bitmap) endVals.get(PROPNAME_BITMAP);
1802ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase        final BitmapDrawable startDrawable = (BitmapDrawable) startVals.get(PROPNAME_DRAWABLE);
1812ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase        final BitmapDrawable endDrawable = (BitmapDrawable) endVals.get(PROPNAME_DRAWABLE);
182faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        if (Transition.DBG) {
183faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            Log.d(LOG_TAG, "StartBitmap.sameAs(endBitmap) = " + startBitmap.sameAs(endBitmap) +
184faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    " for start, end: " + startBitmap + ", " + endBitmap);
185faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        }
186faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        if (startDrawable != null && endDrawable != null && !startBitmap.sameAs(endBitmap)) {
187c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase            ViewOverlay overlay = useParentOverlay ?
188c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase                    ((ViewGroup) view.getParent()).getOverlay() : view.getOverlay();
1894f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase            if (mFadeBehavior == FADE_BEHAVIOR_REVEAL) {
1904f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase                overlay.add(endDrawable);
1914f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase            }
1924f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase            overlay.add(startDrawable);
1932ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase            // The transition works by placing the end drawable under the start drawable and
1942ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase            // gradually fading out the start drawable. So it's not really a cross-fade, but rather
1952ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase            // a reveal of the end scene over time. Also, animate the bounds of both drawables
1962ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase            // to mimic the change in the size of the view itself between scenes.
197c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase            ObjectAnimator anim;
198c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase            if (mFadeBehavior == FADE_BEHAVIOR_OUT_IN) {
199c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase                // Fade out completely halfway through the transition
200c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase                anim = ObjectAnimator.ofInt(startDrawable, "alpha", 255, 0, 0);
201c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase            } else {
202c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase                anim = ObjectAnimator.ofInt(startDrawable, "alpha", 0);
203c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase            }
2042ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase            anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
2052ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                @Override
2062ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                public void onAnimationUpdate(ValueAnimator animation) {
2072ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                    // TODO: some way to auto-invalidate views based on drawable changes? callbacks?
2082ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                    view.invalidate(startDrawable.getBounds());
2094f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase                }
2102ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase            });
2112ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase            ObjectAnimator anim1 = null;
212c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase            if (mFadeBehavior == FADE_BEHAVIOR_OUT_IN) {
213c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase                // start fading in halfway through the transition
214c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase                anim1 = ObjectAnimator.ofFloat(view, View.ALPHA, 0, 0, 1);
215c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase            } else if (mFadeBehavior == FADE_BEHAVIOR_CROSSFADE) {
2162ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                anim1 = ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1);
217faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            }
218faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            if (Transition.DBG) {
2192ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                Log.d(LOG_TAG, "Crossfade: created anim " + anim + " for start, end values " +
2202ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                        startValues + ", " + endValues);
2212ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase            }
2222ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase            anim.addListener(new AnimatorListenerAdapter() {
2232ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                @Override
2242ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                public void onAnimationEnd(Animator animation) {
225c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase                    ViewOverlay overlay = useParentOverlay ?
226c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase                            ((ViewGroup) view.getParent()).getOverlay() : view.getOverlay();
2272ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                    overlay.remove(startDrawable);
2282ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                    if (mFadeBehavior == FADE_BEHAVIOR_REVEAL) {
2292ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                        overlay.remove(endDrawable);
2302ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                    }
2312ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                }
2322ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase            });
2332ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase            AnimatorSet set = new AnimatorSet();
2342ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase            set.playTogether(anim);
2352ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase            if (anim1 != null) {
2362ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                set.playTogether(anim1);
237faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            }
2382ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase            if (mResizeBehavior == RESIZE_BEHAVIOR_SCALE && !startBounds.equals(endBounds)) {
2392ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                if (Transition.DBG) {
2402ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                    Log.d(LOG_TAG, "animating from startBounds to endBounds: " +
2412ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                            startBounds + ", " + endBounds);
2422ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                }
2432ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                Animator anim2 = ObjectAnimator.ofObject(startDrawable, "bounds",
2444f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase                        sRectEvaluator, startBounds, endBounds);
2452ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                set.playTogether(anim2);
2462ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                if (mResizeBehavior == RESIZE_BEHAVIOR_SCALE) {
2472ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                    // TODO: How to handle resizing with a CROSSFADE (vs. REVEAL) effect
2482ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                    // when we are animating the view directly?
2492ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                    Animator anim3 = ObjectAnimator.ofObject(endDrawable, "bounds",
2502ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                            sRectEvaluator, startBounds, endBounds);
2512ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                    set.playTogether(anim3);
2522ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase                }
2534f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase            }
2542ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase            return set;
2552ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase        } else {
2562ea7f8b9c5f903050d42c1af57406bf528979f45Chet Haase            return null;
257faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        }
258faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    }
259faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
260d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    private void captureValues(TransitionValues transitionValues) {
261d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase        View view = transitionValues.view;
2624f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase        Rect bounds = new Rect(0, 0, view.getWidth(), view.getHeight());
263c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase        if (mFadeBehavior != FADE_BEHAVIOR_REVEAL) {
2644f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase            bounds.offset(view.getLeft(), view.getTop());
2654f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase        }
266d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase        transitionValues.values.put(PROPNAME_BOUNDS, bounds);
267faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
268faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        if (Transition.DBG) {
269d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase            Log.d(LOG_TAG, "Captured bounds " + transitionValues.values.get(PROPNAME_BOUNDS));
270faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        }
271faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),
272faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                Bitmap.Config.ARGB_8888);
273faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        if (view instanceof TextureView) {
274faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            bitmap = ((TextureView) view).getBitmap();
275faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        } else {
276faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            Canvas c = new Canvas(bitmap);
277faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            view.draw(c);
278faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        }
279d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase        transitionValues.values.put(PROPNAME_BITMAP, bitmap);
280faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        // TODO: I don't have resources, can't call the non-deprecated method?
281faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        BitmapDrawable drawable = new BitmapDrawable(bitmap);
282faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        // TODO: lrtb will be wrong if the view has transXY set
2834f0c4672ac7fd6b8135e27d8f816da2ebaf7d63aChet Haase        drawable.setBounds(bounds);
284d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase        transitionValues.values.put(PROPNAME_DRAWABLE, drawable);
285faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    }
286faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
287d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    @Override
288d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    public void captureStartValues(TransitionValues transitionValues) {
289d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase        captureValues(transitionValues);
290d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    }
291d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase
292d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    @Override
293d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    public void captureEndValues(TransitionValues transitionValues) {
294d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase        captureValues(transitionValues);
295d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    }
296faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase}
297