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