1d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount/*
2d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * Copyright (C) 2014 The Android Open Source Project
3d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount *
4d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * Licensed under the Apache License, Version 2.0 (the "License");
5d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * you may not use this file except in compliance with the License.
6d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * You may obtain a copy of the License at
7d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount *
8d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount *      http://www.apache.org/licenses/LICENSE-2.0
9d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount *
10d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * Unless required by applicable law or agreed to in writing, software
11d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * distributed under the License is distributed on an "AS IS" BASIS,
12d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * See the License for the specific language governing permissions and
14d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * limitations under the License.
15d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount */
16d6107a3170df61d9e776fcd5666acfc9135c6f16George Mountpackage android.transition;
17d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount
181f8c01181bcdef6e7e7e359ecde003939523b718George Mountimport com.android.internal.R;
191f8c01181bcdef6e7e7e359ecde003939523b718George Mount
20d6107a3170df61d9e776fcd5666acfc9135c6f16George Mountimport android.animation.Animator;
21d6107a3170df61d9e776fcd5666acfc9135c6f16George Mountimport android.animation.TimeInterpolator;
22ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mountimport android.content.Context;
23d6107a3170df61d9e776fcd5666acfc9135c6f16George Mountimport android.graphics.Rect;
24ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mountimport android.util.AttributeSet;
25d6107a3170df61d9e776fcd5666acfc9135c6f16George Mountimport android.view.View;
26d6107a3170df61d9e776fcd5666acfc9135c6f16George Mountimport android.view.ViewGroup;
27d6107a3170df61d9e776fcd5666acfc9135c6f16George Mountimport android.view.animation.AccelerateInterpolator;
28d6107a3170df61d9e776fcd5666acfc9135c6f16George Mountimport android.view.animation.DecelerateInterpolator;
29d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount/**
30d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * This transition tracks changes to the visibility of target views in the
31d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * start and end scenes and moves views in or out from the edges of the
32d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * scene. Visibility is determined by both the
33d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * {@link View#setVisibility(int)} state of the view as well as whether it
34d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * is parented in the current view hierarchy. Disappearing Views are
35d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * limited as described in {@link Visibility#onDisappear(android.view.ViewGroup,
36d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * TransitionValues, int, TransitionValues, int)}.
37d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * <p>Views move away from the focal View or the center of the Scene if
38d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * no epicenter was provided.</p>
39d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount */
40d6107a3170df61d9e776fcd5666acfc9135c6f16George Mountpublic class Explode extends Visibility {
41d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount    private static final TimeInterpolator sDecelerate = new DecelerateInterpolator();
42d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount    private static final TimeInterpolator sAccelerate = new AccelerateInterpolator();
43d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount    private static final String TAG = "Explode";
441f8c01181bcdef6e7e7e359ecde003939523b718George Mount    private static final String PROPNAME_SCREEN_BOUNDS = "android:explode:screenBounds";
45d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount
46d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount    private int[] mTempLoc = new int[2];
47d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount
48d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount    public Explode() {
49d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount        setPropagation(new CircularPropagation());
50d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount    }
51d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount
52ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount    public Explode(Context context, AttributeSet attrs) {
53ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount        super(context, attrs);
54964c3cf5b25ccbb5b07c1bda6eb147c912e96a7bGeorge Mount        setPropagation(new CircularPropagation());
55ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount    }
56ecd857be3946283ebb4306e2c03ae70f5c5bb152George Mount
57d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount    private void captureValues(TransitionValues transitionValues) {
58d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount        View view = transitionValues.view;
59d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount        view.getLocationOnScreen(mTempLoc);
601f8c01181bcdef6e7e7e359ecde003939523b718George Mount        int left = mTempLoc[0];
611f8c01181bcdef6e7e7e359ecde003939523b718George Mount        int top = mTempLoc[1];
62d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount        int right = left + view.getWidth();
63d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount        int bottom = top + view.getHeight();
64d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount        transitionValues.values.put(PROPNAME_SCREEN_BOUNDS, new Rect(left, top, right, bottom));
65d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount    }
66d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount
67d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount    @Override
68d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount    public void captureStartValues(TransitionValues transitionValues) {
69d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount        super.captureStartValues(transitionValues);
70d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount        captureValues(transitionValues);
71d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount    }
72d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount
73d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount    @Override
74d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount    public void captureEndValues(TransitionValues transitionValues) {
75d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount        super.captureEndValues(transitionValues);
76d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount        captureValues(transitionValues);
77d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount    }
78d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount
79d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount    @Override
80d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount    public Animator onAppear(ViewGroup sceneRoot, View view,
81d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount            TransitionValues startValues, TransitionValues endValues) {
82d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount        if (endValues == null) {
83d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount            return null;
84d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount        }
85d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount        Rect bounds = (Rect) endValues.values.get(PROPNAME_SCREEN_BOUNDS);
861f8c01181bcdef6e7e7e359ecde003939523b718George Mount        float endX = view.getTranslationX();
871f8c01181bcdef6e7e7e359ecde003939523b718George Mount        float endY = view.getTranslationY();
88d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount        calculateOut(sceneRoot, bounds, mTempLoc);
891f8c01181bcdef6e7e7e359ecde003939523b718George Mount        float startX = endX + mTempLoc[0];
901f8c01181bcdef6e7e7e359ecde003939523b718George Mount        float startY = endY + mTempLoc[1];
91d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount
921f8c01181bcdef6e7e7e359ecde003939523b718George Mount        return TranslationAnimationCreator.createAnimation(view, endValues, bounds.left, bounds.top,
93727a6cf6ad06bbb43c0c309c33776394a0012895George Mount                startX, startY, endX, endY, sDecelerate, this);
94d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount    }
95d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount
96d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount    @Override
97d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount    public Animator onDisappear(ViewGroup sceneRoot, View view,
98d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount            TransitionValues startValues, TransitionValues endValues) {
991f8c01181bcdef6e7e7e359ecde003939523b718George Mount        if (startValues == null) {
1001f8c01181bcdef6e7e7e359ecde003939523b718George Mount            return null;
1011f8c01181bcdef6e7e7e359ecde003939523b718George Mount        }
102d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount        Rect bounds = (Rect) startValues.values.get(PROPNAME_SCREEN_BOUNDS);
1031f8c01181bcdef6e7e7e359ecde003939523b718George Mount        int viewPosX = bounds.left;
1041f8c01181bcdef6e7e7e359ecde003939523b718George Mount        int viewPosY = bounds.top;
1051f8c01181bcdef6e7e7e359ecde003939523b718George Mount        float startX = view.getTranslationX();
1061f8c01181bcdef6e7e7e359ecde003939523b718George Mount        float startY = view.getTranslationY();
1071f8c01181bcdef6e7e7e359ecde003939523b718George Mount        float endX = startX;
1081f8c01181bcdef6e7e7e359ecde003939523b718George Mount        float endY = startY;
1091f8c01181bcdef6e7e7e359ecde003939523b718George Mount        int[] interruptedPosition = (int[]) startValues.view.getTag(R.id.transitionPosition);
1101f8c01181bcdef6e7e7e359ecde003939523b718George Mount        if (interruptedPosition != null) {
1111f8c01181bcdef6e7e7e359ecde003939523b718George Mount            // We want to have the end position relative to the interrupted position, not
1121f8c01181bcdef6e7e7e359ecde003939523b718George Mount            // the position it was supposed to start at.
1131f8c01181bcdef6e7e7e359ecde003939523b718George Mount            endX += interruptedPosition[0] - bounds.left;
1141f8c01181bcdef6e7e7e359ecde003939523b718George Mount            endY += interruptedPosition[1] - bounds.top;
1151f8c01181bcdef6e7e7e359ecde003939523b718George Mount            bounds.offsetTo(interruptedPosition[0], interruptedPosition[1]);
1161f8c01181bcdef6e7e7e359ecde003939523b718George Mount        }
117d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount        calculateOut(sceneRoot, bounds, mTempLoc);
1181f8c01181bcdef6e7e7e359ecde003939523b718George Mount        endX += mTempLoc[0];
1191f8c01181bcdef6e7e7e359ecde003939523b718George Mount        endY += mTempLoc[1];
120d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount
1211f8c01181bcdef6e7e7e359ecde003939523b718George Mount        return TranslationAnimationCreator.createAnimation(view, startValues,
122727a6cf6ad06bbb43c0c309c33776394a0012895George Mount                viewPosX, viewPosY, startX, startY, endX, endY, sAccelerate, this);
123d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount    }
124d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount
125d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount    private void calculateOut(View sceneRoot, Rect bounds, int[] outVector) {
126d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount        sceneRoot.getLocationOnScreen(mTempLoc);
127d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount        int sceneRootX = mTempLoc[0];
128d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount        int sceneRootY = mTempLoc[1];
129d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount        int focalX;
130d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount        int focalY;
131d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount
132d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount        Rect epicenter = getEpicenter();
133d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount        if (epicenter == null) {
134d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount            focalX = sceneRootX + (sceneRoot.getWidth() / 2)
135d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount                    + Math.round(sceneRoot.getTranslationX());
136d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount            focalY = sceneRootY + (sceneRoot.getHeight() / 2)
137d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount                    + Math.round(sceneRoot.getTranslationY());
138d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount        } else {
139d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount            focalX = epicenter.centerX();
140d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount            focalY = epicenter.centerY();
141d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount        }
142d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount
143d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount        int centerX = bounds.centerX();
144d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount        int centerY = bounds.centerY();
145e573aa9371fc507075219cd078117f96ba8b3b02Neil Fuller        double xVector = centerX - focalX;
146e573aa9371fc507075219cd078117f96ba8b3b02Neil Fuller        double yVector = centerY - focalY;
147d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount
148d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount        if (xVector == 0 && yVector == 0) {
149d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount            // Random direction when View is centered on focal View.
150e573aa9371fc507075219cd078117f96ba8b3b02Neil Fuller            xVector = (Math.random() * 2) - 1;
151e573aa9371fc507075219cd078117f96ba8b3b02Neil Fuller            yVector = (Math.random() * 2) - 1;
152d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount        }
153e573aa9371fc507075219cd078117f96ba8b3b02Neil Fuller        double vectorSize = Math.hypot(xVector, yVector);
154d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount        xVector /= vectorSize;
155d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount        yVector /= vectorSize;
156d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount
157e573aa9371fc507075219cd078117f96ba8b3b02Neil Fuller        double maxDistance =
158d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount                calculateMaxDistance(sceneRoot, focalX - sceneRootX, focalY - sceneRootY);
159d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount
160e573aa9371fc507075219cd078117f96ba8b3b02Neil Fuller        outVector[0] = (int) Math.round(maxDistance * xVector);
161e573aa9371fc507075219cd078117f96ba8b3b02Neil Fuller        outVector[1] = (int) Math.round(maxDistance * yVector);
162d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount    }
163d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount
164e573aa9371fc507075219cd078117f96ba8b3b02Neil Fuller    private static double calculateMaxDistance(View sceneRoot, int focalX, int focalY) {
165d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount        int maxX = Math.max(focalX, sceneRoot.getWidth() - focalX);
166d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount        int maxY = Math.max(focalY, sceneRoot.getHeight() - focalY);
167e573aa9371fc507075219cd078117f96ba8b3b02Neil Fuller        return Math.hypot(maxX, maxY);
168d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount    }
169d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount
170d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount}
171