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