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 18d6107a3170df61d9e776fcd5666acfc9135c6f16George Mountimport android.graphics.Rect; 19d6107a3170df61d9e776fcd5666acfc9135c6f16George Mountimport android.util.FloatMath; 20d6107a3170df61d9e776fcd5666acfc9135c6f16George Mountimport android.util.Log; 21dc21d3b2804c24fe29ec860796d11185901364c4George Mountimport android.view.Gravity; 22d6107a3170df61d9e776fcd5666acfc9135c6f16George Mountimport android.view.View; 23d6107a3170df61d9e776fcd5666acfc9135c6f16George Mountimport android.view.ViewGroup; 24d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount 25d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount/** 26d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * A <code>TransitionPropagation</code> that propagates based on the distance to the side 27d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * and, orthogonally, the distance to epicenter. If the transitioning View is visible in 28d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * the start of the transition, then it will transition sooner when closer to the side and 29d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * later when farther. If the view is not visible in the start of the transition, then 30d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * it will transition later when closer to the side and sooner when farther from the edge. 31d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * This is the default TransitionPropagation used with {@link android.transition.Slide}. 32d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount */ 33d6107a3170df61d9e776fcd5666acfc9135c6f16George Mountpublic class SidePropagation extends VisibilityPropagation { 34d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount private static final String TAG = "SlidePropagation"; 35d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount 36265f209d551092a08f2969ec5fed94292d7741b6George Mount private float mPropagationSpeed = 3.0f; 37dc21d3b2804c24fe29ec860796d11185901364c4George Mount private int mSide = Gravity.BOTTOM; 38d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount 39d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount /** 40d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * Sets the side that is used to calculate the transition propagation. If the transitioning 41d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * View is visible in the start of the transition, then it will transition sooner when 42d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * closer to the side and later when farther. If the view is not visible in the start of 43d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * the transition, then it will transition later when closer to the side and sooner when 44dc21d3b2804c24fe29ec860796d11185901364c4George Mount * farther from the edge. The default is {@link Gravity#BOTTOM}. 45d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * 46d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * @param side The side that is used to calculate the transition propagation. Must be one of 472db3bf5c49ebcd56f02f10145aa40b2445f6be06George Mount * {@link Gravity#LEFT}, {@link Gravity#TOP}, {@link Gravity#RIGHT}, 482db3bf5c49ebcd56f02f10145aa40b2445f6be06George Mount * {@link Gravity#BOTTOM}, {@link Gravity#START}, or {@link Gravity#END}. 49d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount */ 50d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount public void setSide(int side) { 51d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount mSide = side; 52d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount } 53d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount 54d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount /** 55d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * Sets the speed at which transition propagation happens, relative to the duration of the 56d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * Transition. A <code>propagationSpeed</code> of 1 means that a View centered at the side 57d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * set in {@link #setSide(int)} and View centered at the opposite edge will have a difference 58d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * in start delay of approximately the duration of the Transition. A speed of 2 means the 59d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * start delay difference will be approximately half of the duration of the transition. A 60d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * value of 0 is illegal, but negative values will invert the propagation. 61d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * 62d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * @param propagationSpeed The speed at which propagation occurs, relative to the duration 63d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * of the transition. A speed of 4 means it works 4 times as fast 64d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount * as the duration of the transition. May not be 0. 65d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount */ 66d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount public void setPropagationSpeed(float propagationSpeed) { 67d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount if (propagationSpeed == 0) { 68d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount throw new IllegalArgumentException("propagationSpeed may not be 0"); 69d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount } 70d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount mPropagationSpeed = propagationSpeed; 71d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount } 72d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount 73d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount @Override 74d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount public long getStartDelay(ViewGroup sceneRoot, Transition transition, 75d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount TransitionValues startValues, TransitionValues endValues) { 76d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount if (startValues == null && endValues == null) { 77d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount return 0; 78d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount } 79d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount int directionMultiplier = 1; 80d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount Rect epicenter = transition.getEpicenter(); 81d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount TransitionValues positionValues; 82d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount if (endValues == null || getViewVisibility(startValues) == View.VISIBLE) { 83d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount positionValues = startValues; 84d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount directionMultiplier = -1; 85d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount } else { 86d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount positionValues = endValues; 87d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount } 88d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount 89d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount int viewCenterX = getViewX(positionValues); 90d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount int viewCenterY = getViewY(positionValues); 91d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount 92d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount int[] loc = new int[2]; 93d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount sceneRoot.getLocationOnScreen(loc); 94d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount int left = loc[0] + Math.round(sceneRoot.getTranslationX()); 95d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount int top = loc[1] + Math.round(sceneRoot.getTranslationY()); 96d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount int right = left + sceneRoot.getWidth(); 97d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount int bottom = top + sceneRoot.getHeight(); 98d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount 99d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount int epicenterX; 100d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount int epicenterY; 101d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount if (epicenter != null) { 102d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount epicenterX = epicenter.centerX(); 103d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount epicenterY = epicenter.centerY(); 104d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount } else { 105d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount epicenterX = (left + right) / 2; 106d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount epicenterY = (top + bottom) / 2; 107d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount } 108d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount 1092db3bf5c49ebcd56f02f10145aa40b2445f6be06George Mount float distance = distance(sceneRoot, viewCenterX, viewCenterY, epicenterX, epicenterY, 110d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount left, top, right, bottom); 111d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount float maxDistance = getMaxDistance(sceneRoot); 112d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount float distanceFraction = distance/maxDistance; 113d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount 114265f209d551092a08f2969ec5fed94292d7741b6George Mount long duration = transition.getDuration(); 115265f209d551092a08f2969ec5fed94292d7741b6George Mount if (duration < 0) { 116265f209d551092a08f2969ec5fed94292d7741b6George Mount duration = 300; 117265f209d551092a08f2969ec5fed94292d7741b6George Mount } 118265f209d551092a08f2969ec5fed94292d7741b6George Mount 119265f209d551092a08f2969ec5fed94292d7741b6George Mount return Math.round(duration * directionMultiplier / mPropagationSpeed * distanceFraction); 120d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount } 121d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount 1222db3bf5c49ebcd56f02f10145aa40b2445f6be06George Mount private int distance(View sceneRoot, int viewX, int viewY, int epicenterX, int epicenterY, 123d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount int left, int top, int right, int bottom) { 1242db3bf5c49ebcd56f02f10145aa40b2445f6be06George Mount final int side; 1252db3bf5c49ebcd56f02f10145aa40b2445f6be06George Mount if (mSide == Gravity.START) { 1262db3bf5c49ebcd56f02f10145aa40b2445f6be06George Mount final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; 1272db3bf5c49ebcd56f02f10145aa40b2445f6be06George Mount side = isRtl ? Gravity.RIGHT : Gravity.LEFT; 1282db3bf5c49ebcd56f02f10145aa40b2445f6be06George Mount } else if (mSide == Gravity.END) { 1292db3bf5c49ebcd56f02f10145aa40b2445f6be06George Mount final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; 1302db3bf5c49ebcd56f02f10145aa40b2445f6be06George Mount side = isRtl ? Gravity.LEFT : Gravity.RIGHT; 1312db3bf5c49ebcd56f02f10145aa40b2445f6be06George Mount } else { 1322db3bf5c49ebcd56f02f10145aa40b2445f6be06George Mount side = mSide; 1332db3bf5c49ebcd56f02f10145aa40b2445f6be06George Mount } 134d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount int distance = 0; 1352db3bf5c49ebcd56f02f10145aa40b2445f6be06George Mount switch (side) { 136dc21d3b2804c24fe29ec860796d11185901364c4George Mount case Gravity.LEFT: 137d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount distance = right - viewX + Math.abs(epicenterY - viewY); 138d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount break; 139dc21d3b2804c24fe29ec860796d11185901364c4George Mount case Gravity.TOP: 140d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount distance = bottom - viewY + Math.abs(epicenterX - viewX); 141d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount break; 142dc21d3b2804c24fe29ec860796d11185901364c4George Mount case Gravity.RIGHT: 143d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount distance = viewX - left + Math.abs(epicenterY - viewY); 144d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount break; 145dc21d3b2804c24fe29ec860796d11185901364c4George Mount case Gravity.BOTTOM: 146d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount distance = viewY - top + Math.abs(epicenterX - viewX); 147d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount break; 148d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount } 149d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount return distance; 150d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount } 151d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount 152d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount private int getMaxDistance(ViewGroup sceneRoot) { 153d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount switch (mSide) { 154dc21d3b2804c24fe29ec860796d11185901364c4George Mount case Gravity.LEFT: 155dc21d3b2804c24fe29ec860796d11185901364c4George Mount case Gravity.RIGHT: 1562db3bf5c49ebcd56f02f10145aa40b2445f6be06George Mount case Gravity.START: 1572db3bf5c49ebcd56f02f10145aa40b2445f6be06George Mount case Gravity.END: 158d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount return sceneRoot.getWidth(); 159d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount default: 160d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount return sceneRoot.getHeight(); 161d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount } 162d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount } 163d6107a3170df61d9e776fcd5666acfc9135c6f16George Mount} 164