Merge "Add animateAddition() to the ViewBoundAnimator API." into tm-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
4896c9224b
@@ -24,59 +24,19 @@ import android.util.IntProperty
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.Interpolator
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* A class that allows changes in bounds within a view hierarchy to animate seamlessly between the
|
||||
* start and end state.
|
||||
*/
|
||||
class ViewHierarchyAnimator {
|
||||
// TODO(b/221418522): make this private once it can't be passed as an arg anymore.
|
||||
enum class Bound(val label: String, val overrideTag: Int) {
|
||||
LEFT("left", R.id.tag_override_left) {
|
||||
override fun setValue(view: View, value: Int) {
|
||||
view.left = value
|
||||
}
|
||||
|
||||
override fun getValue(view: View): Int {
|
||||
return view.left
|
||||
}
|
||||
},
|
||||
TOP("top", R.id.tag_override_top) {
|
||||
override fun setValue(view: View, value: Int) {
|
||||
view.top = value
|
||||
}
|
||||
|
||||
override fun getValue(view: View): Int {
|
||||
return view.top
|
||||
}
|
||||
},
|
||||
RIGHT("right", R.id.tag_override_right) {
|
||||
override fun setValue(view: View, value: Int) {
|
||||
view.right = value
|
||||
}
|
||||
|
||||
override fun getValue(view: View): Int {
|
||||
return view.right
|
||||
}
|
||||
},
|
||||
BOTTOM("bottom", R.id.tag_override_bottom) {
|
||||
override fun setValue(view: View, value: Int) {
|
||||
view.bottom = value
|
||||
}
|
||||
|
||||
override fun getValue(view: View): Int {
|
||||
return view.bottom
|
||||
}
|
||||
};
|
||||
|
||||
abstract fun setValue(view: View, value: Int)
|
||||
abstract fun getValue(view: View): Int
|
||||
}
|
||||
|
||||
companion object {
|
||||
/** Default values for the animation. These can all be overridden at call time. */
|
||||
private const val DEFAULT_DURATION = 500L
|
||||
private val DEFAULT_INTERPOLATOR = Interpolators.EMPHASIZED
|
||||
private val DEFAULT_INTERPOLATOR = Interpolators.STANDARD
|
||||
private val DEFAULT_ADDITION_INTERPOLATOR = Interpolators.STANDARD_DECELERATE
|
||||
private val DEFAULT_BOUNDS = setOf(Bound.LEFT, Bound.TOP, Bound.RIGHT, Bound.BOTTOM)
|
||||
|
||||
/** The properties used to animate the view bounds. */
|
||||
@@ -112,6 +72,9 @@ class ViewHierarchyAnimator {
|
||||
* Successive calls to this method override the previous settings ([interpolator] and
|
||||
* [duration]). The changes take effect on the next animation.
|
||||
*
|
||||
* Returns true if the [rootView] is already visible and will be animated, false otherwise.
|
||||
* To animate the addition of a view, see [animateAddition].
|
||||
*
|
||||
* TODO(b/221418522): remove the ability to select which bounds to animate and always
|
||||
* animate all of them.
|
||||
*/
|
||||
@@ -121,8 +84,8 @@ class ViewHierarchyAnimator {
|
||||
bounds: Set<Bound> = DEFAULT_BOUNDS,
|
||||
interpolator: Interpolator = DEFAULT_INTERPOLATOR,
|
||||
duration: Long = DEFAULT_DURATION
|
||||
) {
|
||||
animate(rootView, bounds, interpolator, duration, false /* ephemeral */)
|
||||
): Boolean {
|
||||
return animate(rootView, bounds, interpolator, duration, ephemeral = false)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,8 +101,8 @@ class ViewHierarchyAnimator {
|
||||
bounds: Set<Bound> = DEFAULT_BOUNDS,
|
||||
interpolator: Interpolator = DEFAULT_INTERPOLATOR,
|
||||
duration: Long = DEFAULT_DURATION
|
||||
) {
|
||||
animate(rootView, bounds, interpolator, duration, true /* ephemeral */)
|
||||
): Boolean {
|
||||
return animate(rootView, bounds, interpolator, duration, ephemeral = true)
|
||||
}
|
||||
|
||||
private fun animate(
|
||||
@@ -148,9 +111,42 @@ class ViewHierarchyAnimator {
|
||||
interpolator: Interpolator,
|
||||
duration: Long,
|
||||
ephemeral: Boolean
|
||||
) {
|
||||
val listener = createListener(bounds, interpolator, duration, ephemeral)
|
||||
): Boolean {
|
||||
if (!isVisible(
|
||||
rootView.visibility,
|
||||
rootView.left,
|
||||
rootView.top,
|
||||
rootView.right,
|
||||
rootView.bottom
|
||||
)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
val listener = createUpdateListener(bounds, interpolator, duration, ephemeral)
|
||||
recursivelyAddListener(rootView, listener)
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new [View.OnLayoutChangeListener] that when called triggers a layout animation
|
||||
* for the specified [bounds], using [interpolator] and [duration].
|
||||
*
|
||||
* If [ephemeral] is true, the listener is unregistered after the first animation. Otherwise
|
||||
* it keeps listening for further updates.
|
||||
*/
|
||||
private fun createUpdateListener(
|
||||
bounds: Set<Bound>,
|
||||
interpolator: Interpolator,
|
||||
duration: Long,
|
||||
ephemeral: Boolean
|
||||
): View.OnLayoutChangeListener {
|
||||
return createListener(
|
||||
bounds,
|
||||
interpolator,
|
||||
duration,
|
||||
ephemeral
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -173,11 +169,87 @@ class ViewHierarchyAnimator {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Instruct the animator to watch for changes to the layout of [rootView] and its children,
|
||||
* and animate the next time the hierarchy appears after not being visible. It uses the
|
||||
* given [interpolator] and [duration].
|
||||
*
|
||||
* The start state of the animation is controlled by [origin]. This value can be any of the
|
||||
* four corners, any of the four edges, or the center of the view. If any margins are added
|
||||
* on the side(s) of the origin, the translation of those margins can be included by
|
||||
* specifying [includeMargins].
|
||||
*
|
||||
* Returns true if the [rootView] is invisible and will be animated, false otherwise. To
|
||||
* animate an already visible view, see [animate] and [animateNextUpdate].
|
||||
*
|
||||
* Then animator unregisters itself once the first addition animation is complete.
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun animateAddition(
|
||||
rootView: View,
|
||||
origin: Hotspot = Hotspot.CENTER,
|
||||
interpolator: Interpolator = DEFAULT_ADDITION_INTERPOLATOR,
|
||||
duration: Long = DEFAULT_DURATION,
|
||||
includeMargins: Boolean = false
|
||||
): Boolean {
|
||||
if (isVisible(
|
||||
rootView.visibility,
|
||||
rootView.left,
|
||||
rootView.top,
|
||||
rootView.right,
|
||||
rootView.bottom
|
||||
)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
val listener = createAdditionListener(
|
||||
origin, interpolator, duration, ignorePreviousValues = !includeMargins
|
||||
)
|
||||
recursivelyAddListener(rootView, listener)
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new [View.OnLayoutChangeListener] that on the next call triggers a layout
|
||||
* addition animation from the given [origin], using [interpolator] and [duration].
|
||||
*
|
||||
* If [ignorePreviousValues] is true, the animation will only span the area covered by the
|
||||
* new bounds. Otherwise it will include the margins between the previous and new bounds.
|
||||
*/
|
||||
private fun createAdditionListener(
|
||||
origin: Hotspot,
|
||||
interpolator: Interpolator,
|
||||
duration: Long,
|
||||
ignorePreviousValues: Boolean
|
||||
): View.OnLayoutChangeListener {
|
||||
return createListener(
|
||||
DEFAULT_BOUNDS,
|
||||
interpolator,
|
||||
duration,
|
||||
ephemeral = true,
|
||||
origin = origin,
|
||||
ignorePreviousValues = ignorePreviousValues
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new [View.OnLayoutChangeListener] that when called triggers a layout animation
|
||||
* for the specified [bounds], using [interpolator] and [duration].
|
||||
*
|
||||
* If [ephemeral] is true, the listener is unregistered after the first animation. Otherwise
|
||||
* it keeps listening for further updates.
|
||||
*
|
||||
* [origin] specifies whether the start values should be determined by a hotspot, and
|
||||
* [ignorePreviousValues] controls whether the previous values should be taken into account.
|
||||
*/
|
||||
private fun createListener(
|
||||
bounds: Set<Bound>,
|
||||
interpolator: Interpolator,
|
||||
duration: Long,
|
||||
ephemeral: Boolean
|
||||
ephemeral: Boolean,
|
||||
origin: Hotspot? = null,
|
||||
ignorePreviousValues: Boolean = false
|
||||
): View.OnLayoutChangeListener {
|
||||
return object : View.OnLayoutChangeListener {
|
||||
override fun onLayoutChange(
|
||||
@@ -186,21 +258,21 @@ class ViewHierarchyAnimator {
|
||||
top: Int,
|
||||
right: Int,
|
||||
bottom: Int,
|
||||
oldLeft: Int,
|
||||
oldTop: Int,
|
||||
oldRight: Int,
|
||||
oldBottom: Int
|
||||
previousLeft: Int,
|
||||
previousTop: Int,
|
||||
previousRight: Int,
|
||||
previousBottom: Int
|
||||
) {
|
||||
if (view == null) return
|
||||
|
||||
val startLeft = getBound(view, Bound.LEFT) ?: oldLeft
|
||||
val startTop = getBound(view, Bound.TOP) ?: oldTop
|
||||
val startRight = getBound(view, Bound.RIGHT) ?: oldRight
|
||||
val startBottom = getBound(view, Bound.BOTTOM) ?: oldBottom
|
||||
val startLeft = getBound(view, Bound.LEFT) ?: previousLeft
|
||||
val startTop = getBound(view, Bound.TOP) ?: previousTop
|
||||
val startRight = getBound(view, Bound.RIGHT) ?: previousRight
|
||||
val startBottom = getBound(view, Bound.BOTTOM) ?: previousBottom
|
||||
|
||||
(view.getTag(R.id.tag_animator) as? ObjectAnimator)?.cancel()
|
||||
|
||||
if (view.visibility == View.GONE || view.visibility == View.INVISIBLE) {
|
||||
if (!isVisible(view.visibility, left, top, right, bottom)) {
|
||||
setBound(view, Bound.LEFT, left)
|
||||
setBound(view, Bound.TOP, top)
|
||||
setBound(view, Bound.RIGHT, right)
|
||||
@@ -208,11 +280,17 @@ class ViewHierarchyAnimator {
|
||||
return
|
||||
}
|
||||
|
||||
val startValues = mapOf(
|
||||
Bound.LEFT to startLeft,
|
||||
Bound.TOP to startTop,
|
||||
Bound.RIGHT to startRight,
|
||||
Bound.BOTTOM to startBottom
|
||||
val startValues = processStartValues(
|
||||
origin,
|
||||
left,
|
||||
top,
|
||||
right,
|
||||
bottom,
|
||||
startLeft,
|
||||
startTop,
|
||||
startRight,
|
||||
startBottom,
|
||||
ignorePreviousValues
|
||||
)
|
||||
val endValues = mapOf(
|
||||
Bound.LEFT to left,
|
||||
@@ -221,11 +299,12 @@ class ViewHierarchyAnimator {
|
||||
Bound.BOTTOM to bottom
|
||||
)
|
||||
|
||||
val boundsToAnimate = bounds.toMutableSet()
|
||||
if (left == startLeft) boundsToAnimate.remove(Bound.LEFT)
|
||||
if (top == startTop) boundsToAnimate.remove(Bound.TOP)
|
||||
if (right == startRight) boundsToAnimate.remove(Bound.RIGHT)
|
||||
if (bottom == startBottom) boundsToAnimate.remove(Bound.BOTTOM)
|
||||
val boundsToAnimate = mutableSetOf<Bound>()
|
||||
bounds.forEach { bound ->
|
||||
if (endValues.getValue(bound) != startValues.getValue(bound)) {
|
||||
boundsToAnimate.add(bound)
|
||||
}
|
||||
}
|
||||
|
||||
if (boundsToAnimate.isNotEmpty()) {
|
||||
startAnimation(
|
||||
@@ -242,11 +321,136 @@ class ViewHierarchyAnimator {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given [visibility] and bounds are consistent with a view being
|
||||
* currently visible on screen.
|
||||
*/
|
||||
private fun isVisible(
|
||||
visibility: Int,
|
||||
left: Int,
|
||||
top: Int,
|
||||
right: Int,
|
||||
bottom: Int
|
||||
): Boolean {
|
||||
return visibility == View.VISIBLE && left != right && top != bottom
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the actual starting values based on the requested [origin] and on
|
||||
* [ignorePreviousValues].
|
||||
*
|
||||
* If [origin] is null, the resolved start values will be the same as those passed in, or
|
||||
* the same as the new values if [ignorePreviousValues] is true. If [origin] is not null,
|
||||
* the start values are resolved based on it, and [ignorePreviousValues] controls whether or
|
||||
* not newly introduced margins are included.
|
||||
*
|
||||
* Base case
|
||||
* 1) origin=TOP
|
||||
* x---------x x---------x x---------x x---------x x---------x
|
||||
* x---------x | | | | | |
|
||||
* -> -> x---------x -> | | -> | |
|
||||
* x---------x | |
|
||||
* x---------x
|
||||
* 2) origin=BOTTOM_LEFT
|
||||
* x---------x
|
||||
* x-------x | |
|
||||
* -> -> x----x -> | | -> | |
|
||||
* x--x | | | | | |
|
||||
* x x--x x----x x-------x x---------x
|
||||
* 3) origin=CENTER
|
||||
* x---------x
|
||||
* x-----x x-------x | |
|
||||
* x -> x---x -> | | -> | | -> | |
|
||||
* x-----x x-------x | |
|
||||
* x---------x
|
||||
*
|
||||
* In case the start and end values differ in the direction of the origin, and
|
||||
* [ignorePreviousValues] is false, the previous values are used and a translation is
|
||||
* included in addition to the view expansion.
|
||||
*
|
||||
* origin=TOP_LEFT - (0,0,0,0) -> (30,30,70,70)
|
||||
* x
|
||||
* x--x
|
||||
* x--x x----x
|
||||
* -> -> | | -> x------x
|
||||
* x----x | |
|
||||
* | |
|
||||
* x------x
|
||||
*/
|
||||
private fun processStartValues(
|
||||
origin: Hotspot?,
|
||||
newLeft: Int,
|
||||
newTop: Int,
|
||||
newRight: Int,
|
||||
newBottom: Int,
|
||||
previousLeft: Int,
|
||||
previousTop: Int,
|
||||
previousRight: Int,
|
||||
previousBottom: Int,
|
||||
ignorePreviousValues: Boolean
|
||||
): Map<Bound, Int> {
|
||||
val startLeft = if (ignorePreviousValues) newLeft else previousLeft
|
||||
val startTop = if (ignorePreviousValues) newTop else previousTop
|
||||
val startRight = if (ignorePreviousValues) newRight else previousRight
|
||||
val startBottom = if (ignorePreviousValues) newBottom else previousBottom
|
||||
|
||||
var left = startLeft
|
||||
var top = startTop
|
||||
var right = startRight
|
||||
var bottom = startBottom
|
||||
|
||||
if (origin != null) {
|
||||
left = when (origin) {
|
||||
Hotspot.CENTER -> (newLeft + newRight) / 2
|
||||
Hotspot.BOTTOM_LEFT, Hotspot.LEFT, Hotspot.TOP_LEFT -> min(startLeft, newLeft)
|
||||
Hotspot.TOP, Hotspot.BOTTOM -> newLeft
|
||||
Hotspot.TOP_RIGHT, Hotspot.RIGHT, Hotspot.BOTTOM_RIGHT -> max(
|
||||
startRight,
|
||||
newRight
|
||||
)
|
||||
}
|
||||
top = when (origin) {
|
||||
Hotspot.CENTER -> (newTop + newBottom) / 2
|
||||
Hotspot.TOP_LEFT, Hotspot.TOP, Hotspot.TOP_RIGHT -> min(startTop, newTop)
|
||||
Hotspot.LEFT, Hotspot.RIGHT -> newTop
|
||||
Hotspot.BOTTOM_RIGHT, Hotspot.BOTTOM, Hotspot.BOTTOM_LEFT -> max(
|
||||
startBottom,
|
||||
newBottom
|
||||
)
|
||||
}
|
||||
right = when (origin) {
|
||||
Hotspot.CENTER -> (newLeft + newRight) / 2
|
||||
Hotspot.TOP_RIGHT, Hotspot.RIGHT, Hotspot.BOTTOM_RIGHT -> max(
|
||||
startRight,
|
||||
newRight
|
||||
)
|
||||
Hotspot.TOP, Hotspot.BOTTOM -> newRight
|
||||
Hotspot.BOTTOM_LEFT, Hotspot.LEFT, Hotspot.TOP_LEFT -> min(startLeft, newLeft)
|
||||
}
|
||||
bottom = when (origin) {
|
||||
Hotspot.CENTER -> (newTop + newBottom) / 2
|
||||
Hotspot.BOTTOM_RIGHT, Hotspot.BOTTOM, Hotspot.BOTTOM_LEFT -> max(
|
||||
startBottom,
|
||||
newBottom
|
||||
)
|
||||
Hotspot.LEFT, Hotspot.RIGHT -> newBottom
|
||||
Hotspot.TOP_LEFT, Hotspot.TOP, Hotspot.TOP_RIGHT -> min(startTop, newTop)
|
||||
}
|
||||
}
|
||||
|
||||
return mapOf(
|
||||
Bound.LEFT to left,
|
||||
Bound.TOP to top,
|
||||
Bound.RIGHT to right,
|
||||
Bound.BOTTOM to bottom
|
||||
)
|
||||
}
|
||||
|
||||
private fun recursivelyAddListener(view: View, listener: View.OnLayoutChangeListener) {
|
||||
// Make sure that only one listener is active at a time.
|
||||
val oldListener = view.getTag(R.id.tag_layout_listener)
|
||||
if (oldListener != null && oldListener is View.OnLayoutChangeListener) {
|
||||
view.removeOnLayoutChangeListener(oldListener)
|
||||
val previousListener = view.getTag(R.id.tag_layout_listener)
|
||||
if (previousListener != null && previousListener is View.OnLayoutChangeListener) {
|
||||
view.removeOnLayoutChangeListener(previousListener)
|
||||
}
|
||||
|
||||
view.addOnLayoutChangeListener(listener)
|
||||
@@ -268,9 +472,12 @@ class ViewHierarchyAnimator {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates the animation of a single bound by creating the animator, registering it with
|
||||
* the [view], and starting it. If [ephemeral], the layout change listener is unregistered
|
||||
* at the end of the animation, so no more animations happen.
|
||||
* Initiates the animation of the requested [bounds] between [startValues] and [endValues]
|
||||
* by creating the animator, registering it with the [view], and starting it using
|
||||
* [interpolator] and [duration].
|
||||
*
|
||||
* If [ephemeral] is true, the layout change listener is unregistered at the end of the
|
||||
* animation, so no more animations happen.
|
||||
*/
|
||||
private fun startAnimation(
|
||||
view: View,
|
||||
@@ -325,4 +532,52 @@ class ViewHierarchyAnimator {
|
||||
animator.start()
|
||||
}
|
||||
}
|
||||
|
||||
/** An enum used to determine the origin of addition animations. */
|
||||
enum class Hotspot {
|
||||
CENTER, LEFT, TOP_LEFT, TOP, TOP_RIGHT, RIGHT, BOTTOM_RIGHT, BOTTOM, BOTTOM_LEFT
|
||||
}
|
||||
|
||||
// TODO(b/221418522): make this private once it can't be passed as an arg anymore.
|
||||
enum class Bound(val label: String, val overrideTag: Int) {
|
||||
LEFT("left", R.id.tag_override_left) {
|
||||
override fun setValue(view: View, value: Int) {
|
||||
view.left = value
|
||||
}
|
||||
|
||||
override fun getValue(view: View): Int {
|
||||
return view.left
|
||||
}
|
||||
},
|
||||
TOP("top", R.id.tag_override_top) {
|
||||
override fun setValue(view: View, value: Int) {
|
||||
view.top = value
|
||||
}
|
||||
|
||||
override fun getValue(view: View): Int {
|
||||
return view.top
|
||||
}
|
||||
},
|
||||
RIGHT("right", R.id.tag_override_right) {
|
||||
override fun setValue(view: View, value: Int) {
|
||||
view.right = value
|
||||
}
|
||||
|
||||
override fun getValue(view: View): Int {
|
||||
return view.right
|
||||
}
|
||||
},
|
||||
BOTTOM("bottom", R.id.tag_override_bottom) {
|
||||
override fun setValue(view: View, value: Int) {
|
||||
view.bottom = value
|
||||
}
|
||||
|
||||
override fun getValue(view: View): Int {
|
||||
return view.bottom
|
||||
}
|
||||
};
|
||||
|
||||
abstract fun setValue(view: View, value: Int)
|
||||
abstract fun getValue(view: View): Int
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,10 @@ import android.widget.LinearLayout
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import junit.framework.Assert.assertEquals
|
||||
import junit.framework.Assert.assertFalse
|
||||
import junit.framework.Assert.assertNotNull
|
||||
import junit.framework.Assert.assertNull
|
||||
import junit.framework.Assert.assertTrue
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
@@ -25,15 +27,23 @@ class ViewHierarchyAnimatorTest : SysuiTestCase() {
|
||||
private val TEST_INTERPOLATOR = Interpolators.LINEAR
|
||||
}
|
||||
|
||||
private val childParams = LinearLayout.LayoutParams(
|
||||
0 /* width */,
|
||||
LinearLayout.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
private lateinit var rootView: LinearLayout
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
rootView = LinearLayout(mContext)
|
||||
rootView.orientation = LinearLayout.HORIZONTAL
|
||||
rootView.weightSum = 1f
|
||||
childParams.weight = 0.5f
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
endAnimation(rootView)
|
||||
ViewHierarchyAnimator.stopAnimating(rootView)
|
||||
}
|
||||
|
||||
@@ -41,13 +51,45 @@ class ViewHierarchyAnimatorTest : SysuiTestCase() {
|
||||
fun respectsAnimationParameters() {
|
||||
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
|
||||
|
||||
ViewHierarchyAnimator.animate(
|
||||
// animate()
|
||||
var success = ViewHierarchyAnimator.animate(
|
||||
rootView, interpolator = TEST_INTERPOLATOR, duration = TEST_DURATION
|
||||
)
|
||||
rootView.layout(0 /* l */, 0 /* t */, 100 /* r */, 100 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNotNull(rootView.getTag(R.id.tag_animator))
|
||||
val animator = rootView.getTag(R.id.tag_animator) as ObjectAnimator
|
||||
var animator = rootView.getTag(R.id.tag_animator) as ObjectAnimator
|
||||
assertEquals(animator.interpolator, TEST_INTERPOLATOR)
|
||||
assertEquals(animator.duration, TEST_DURATION)
|
||||
|
||||
endAnimation(rootView)
|
||||
ViewHierarchyAnimator.stopAnimating(rootView)
|
||||
|
||||
// animateNextUpdate()
|
||||
success = ViewHierarchyAnimator.animateNextUpdate(
|
||||
rootView, interpolator = TEST_INTERPOLATOR, duration = TEST_DURATION
|
||||
)
|
||||
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNotNull(rootView.getTag(R.id.tag_animator))
|
||||
animator = rootView.getTag(R.id.tag_animator) as ObjectAnimator
|
||||
assertEquals(animator.interpolator, TEST_INTERPOLATOR)
|
||||
assertEquals(animator.duration, TEST_DURATION)
|
||||
|
||||
endAnimation(rootView)
|
||||
|
||||
// animateAddition()
|
||||
rootView.layout(0 /* l */, 0 /* t */, 0 /* r */, 0 /* b */)
|
||||
success = ViewHierarchyAnimator.animateAddition(
|
||||
rootView, interpolator = TEST_INTERPOLATOR, duration = TEST_DURATION
|
||||
)
|
||||
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNotNull(rootView.getTag(R.id.tag_animator))
|
||||
animator = rootView.getTag(R.id.tag_animator) as ObjectAnimator
|
||||
assertEquals(animator.interpolator, TEST_INTERPOLATOR)
|
||||
assertEquals(animator.duration, TEST_DURATION)
|
||||
}
|
||||
@@ -56,10 +98,11 @@ class ViewHierarchyAnimatorTest : SysuiTestCase() {
|
||||
fun animatesFromStartToEnd() {
|
||||
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
|
||||
|
||||
ViewHierarchyAnimator.animate(rootView)
|
||||
val success = ViewHierarchyAnimator.animate(rootView)
|
||||
// Change all bounds.
|
||||
rootView.layout(0 /* l */, 15 /* t */, 70 /* r */, 80 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNotNull(rootView.getTag(R.id.tag_animator))
|
||||
// The initial values should be those of the previous layout.
|
||||
checkBounds(rootView, l = 10, t = 10, r = 50, b = 50)
|
||||
@@ -73,10 +116,11 @@ class ViewHierarchyAnimatorTest : SysuiTestCase() {
|
||||
fun animatesSuccessiveLayoutChanges() {
|
||||
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
|
||||
|
||||
ViewHierarchyAnimator.animate(rootView)
|
||||
val success = ViewHierarchyAnimator.animate(rootView)
|
||||
// Change all bounds.
|
||||
rootView.layout(0 /* l */, 15 /* t */, 70 /* r */, 80 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNotNull(rootView.getTag(R.id.tag_animator))
|
||||
endAnimation(rootView)
|
||||
assertNull(rootView.getTag(R.id.tag_animator))
|
||||
@@ -103,10 +147,12 @@ class ViewHierarchyAnimatorTest : SysuiTestCase() {
|
||||
fun animatesFromPreviousAnimationProgress() {
|
||||
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
|
||||
|
||||
ViewHierarchyAnimator.animateNextUpdate(rootView, interpolator = TEST_INTERPOLATOR)
|
||||
val success =
|
||||
ViewHierarchyAnimator.animateNextUpdate(rootView, interpolator = TEST_INTERPOLATOR)
|
||||
// Change all bounds.
|
||||
rootView.layout(0 /* l */, 20 /* t */, 70 /* r */, 80 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNotNull(rootView.getTag(R.id.tag_animator))
|
||||
advanceAnimation(rootView, fraction = 0.5f)
|
||||
checkBounds(rootView, l = 5, t = 15, r = 60, b = 65)
|
||||
@@ -124,57 +170,448 @@ class ViewHierarchyAnimatorTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun animatesRootAndChildren() {
|
||||
val firstChild = View(mContext)
|
||||
firstChild.layoutParams = childParams
|
||||
rootView.addView(firstChild)
|
||||
val secondChild = View(mContext)
|
||||
secondChild.layoutParams = childParams
|
||||
rootView.addView(secondChild)
|
||||
rootView.measure(
|
||||
View.MeasureSpec.makeMeasureSpec(150, View.MeasureSpec.EXACTLY),
|
||||
View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY)
|
||||
)
|
||||
rootView.layout(0 /* l */, 0 /* t */, 150 /* r */, 100 /* b */)
|
||||
firstChild.layout(0 /* l */, 0 /* t */, 100 /* r */, 100 /* b */)
|
||||
secondChild.layout(100 /* l */, 0 /* t */, 150 /* r */, 100 /* b */)
|
||||
|
||||
ViewHierarchyAnimator.animate(rootView)
|
||||
val success = ViewHierarchyAnimator.animate(rootView)
|
||||
// Change all bounds.
|
||||
rootView.measure(
|
||||
View.MeasureSpec.makeMeasureSpec(190, View.MeasureSpec.EXACTLY),
|
||||
View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY)
|
||||
)
|
||||
rootView.layout(10 /* l */, 20 /* t */, 200 /* r */, 120 /* b */)
|
||||
firstChild.layout(10 /* l */, 20 /* t */, 150 /* r */, 120 /* b */)
|
||||
secondChild.layout(150 /* l */, 20 /* t */, 200 /* r */, 120 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNotNull(rootView.getTag(R.id.tag_animator))
|
||||
assertNotNull(firstChild.getTag(R.id.tag_animator))
|
||||
assertNotNull(secondChild.getTag(R.id.tag_animator))
|
||||
// The initial values should be those of the previous layout.
|
||||
checkBounds(rootView, l = 0, t = 0, r = 150, b = 100)
|
||||
checkBounds(firstChild, l = 0, t = 0, r = 100, b = 100)
|
||||
checkBounds(secondChild, l = 100, t = 0, r = 150, b = 100)
|
||||
checkBounds(firstChild, l = 0, t = 0, r = 75, b = 100)
|
||||
checkBounds(secondChild, l = 75, t = 0, r = 150, b = 100)
|
||||
endAnimation(rootView)
|
||||
assertNull(rootView.getTag(R.id.tag_animator))
|
||||
assertNull(firstChild.getTag(R.id.tag_animator))
|
||||
assertNull(secondChild.getTag(R.id.tag_animator))
|
||||
// The end values should be those of the latest layout.
|
||||
checkBounds(rootView, l = 10, t = 20, r = 200, b = 120)
|
||||
checkBounds(firstChild, l = 10, t = 20, r = 150, b = 120)
|
||||
checkBounds(secondChild, l = 150, t = 20, r = 200, b = 120)
|
||||
checkBounds(firstChild, l = 0, t = 0, r = 95, b = 100)
|
||||
checkBounds(secondChild, l = 95, t = 0, r = 190, b = 100)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun animatesAppearingViewsFromStartToEnd() {
|
||||
// Starting GONE.
|
||||
rootView.visibility = View.GONE
|
||||
rootView.layout(0 /* l */, 50 /* t */, 50 /* r */, 100 /* b */)
|
||||
var success = ViewHierarchyAnimator.animateAddition(rootView)
|
||||
rootView.visibility = View.VISIBLE
|
||||
rootView.layout(0 /* l */, 100 /* t */, 100 /* r */, 200 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNotNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 50, t = 150, r = 50, b = 150)
|
||||
endAnimation(rootView)
|
||||
assertNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 0, t = 100, r = 100, b = 200)
|
||||
|
||||
// Starting INVISIBLE.
|
||||
rootView.visibility = View.INVISIBLE
|
||||
rootView.layout(0 /* l */, 50 /* t */, 50 /* r */, 100 /* b */)
|
||||
success = ViewHierarchyAnimator.animateAddition(rootView)
|
||||
rootView.visibility = View.VISIBLE
|
||||
rootView.layout(0 /* l */, 100 /* t */, 100 /* r */, 200 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNotNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 50, t = 150, r = 50, b = 150)
|
||||
endAnimation(rootView)
|
||||
assertNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 0, t = 100, r = 100, b = 200)
|
||||
|
||||
// Starting with nothing.
|
||||
rootView.layout(0 /* l */, 0 /* t */, 0 /* r */, 0 /* b */)
|
||||
success = ViewHierarchyAnimator.animateAddition(rootView)
|
||||
rootView.layout(0 /* l */, 20 /* t */, 50 /* r */, 80 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNotNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 25, t = 50, r = 25, b = 50)
|
||||
endAnimation(rootView)
|
||||
assertNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 0, t = 20, r = 50, b = 80)
|
||||
|
||||
// Starting with 0 width.
|
||||
rootView.layout(0 /* l */, 50 /* t */, 0 /* r */, 100 /* b */)
|
||||
success = ViewHierarchyAnimator.animateAddition(rootView)
|
||||
rootView.layout(0 /* l */, 50 /* t */, 50 /* r */, 100 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNotNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 25, t = 75, r = 25, b = 75)
|
||||
endAnimation(rootView)
|
||||
assertNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 0, t = 50, r = 50, b = 100)
|
||||
|
||||
// Starting with 0 height.
|
||||
rootView.layout(0 /* l */, 50 /* t */, 50 /* r */, 50 /* b */)
|
||||
success = ViewHierarchyAnimator.animateAddition(rootView)
|
||||
rootView.layout(0 /* l */, 50 /* t */, 50 /* r */, 100 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNotNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 25, t = 75, r = 25, b = 75)
|
||||
endAnimation(rootView)
|
||||
assertNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 0, t = 50, r = 50, b = 100)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun animatesAppearingViewsRespectingOrigin() {
|
||||
// CENTER.
|
||||
rootView.layout(0 /* l */, 0 /* t */, 0 /* r */, 0 /* b */)
|
||||
var success = ViewHierarchyAnimator.animateAddition(
|
||||
rootView,
|
||||
origin = ViewHierarchyAnimator.Hotspot.CENTER
|
||||
)
|
||||
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNotNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 75, t = 75, r = 75, b = 75)
|
||||
endAnimation(rootView)
|
||||
|
||||
// LEFT.
|
||||
rootView.layout(0 /* l */, 0 /* t */, 0 /* r */, 0 /* b */)
|
||||
success = ViewHierarchyAnimator.animateAddition(
|
||||
rootView,
|
||||
origin = ViewHierarchyAnimator.Hotspot.LEFT
|
||||
)
|
||||
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNotNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 50, t = 50, r = 50, b = 100)
|
||||
endAnimation(rootView)
|
||||
|
||||
// TOP_LEFT.
|
||||
rootView.layout(0 /* l */, 0 /* t */, 0 /* r */, 0 /* b */)
|
||||
success = ViewHierarchyAnimator.animateAddition(
|
||||
rootView,
|
||||
origin = ViewHierarchyAnimator.Hotspot.TOP_LEFT
|
||||
)
|
||||
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNotNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 50, t = 50, r = 50, b = 50)
|
||||
endAnimation(rootView)
|
||||
|
||||
// TOP.
|
||||
rootView.layout(150 /* l */, 0 /* t */, 150 /* r */, 0 /* b */)
|
||||
success = ViewHierarchyAnimator.animateAddition(
|
||||
rootView,
|
||||
origin = ViewHierarchyAnimator.Hotspot.TOP
|
||||
)
|
||||
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNotNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 50, t = 50, r = 100, b = 50)
|
||||
endAnimation(rootView)
|
||||
|
||||
// TOP_RIGHT.
|
||||
rootView.layout(150 /* l */, 0 /* t */, 150 /* r */, 0 /* b */)
|
||||
success = ViewHierarchyAnimator.animateAddition(
|
||||
rootView,
|
||||
origin = ViewHierarchyAnimator.Hotspot.TOP_RIGHT
|
||||
)
|
||||
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNotNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 100, t = 50, r = 100, b = 50)
|
||||
endAnimation(rootView)
|
||||
|
||||
// RIGHT.
|
||||
rootView.layout(150 /* l */, 150 /* t */, 150 /* r */, 150 /* b */)
|
||||
success = ViewHierarchyAnimator.animateAddition(
|
||||
rootView,
|
||||
origin = ViewHierarchyAnimator.Hotspot.RIGHT
|
||||
)
|
||||
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNotNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 100, t = 50, r = 100, b = 100)
|
||||
endAnimation(rootView)
|
||||
|
||||
// BOTTOM_RIGHT.
|
||||
rootView.layout(150 /* l */, 150 /* t */, 150 /* r */, 150 /* b */)
|
||||
success = ViewHierarchyAnimator.animateAddition(
|
||||
rootView,
|
||||
origin = ViewHierarchyAnimator.Hotspot.BOTTOM_RIGHT
|
||||
)
|
||||
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNotNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 100, t = 100, r = 100, b = 100)
|
||||
endAnimation(rootView)
|
||||
|
||||
// BOTTOM.
|
||||
rootView.layout(0 /* l */, 150 /* t */, 0 /* r */, 150 /* b */)
|
||||
success = ViewHierarchyAnimator.animateAddition(
|
||||
rootView,
|
||||
origin = ViewHierarchyAnimator.Hotspot.BOTTOM
|
||||
)
|
||||
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNotNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 50, t = 100, r = 100, b = 100)
|
||||
endAnimation(rootView)
|
||||
|
||||
// BOTTOM_LEFT.
|
||||
rootView.layout(0 /* l */, 150 /* t */, 0 /* r */, 150 /* b */)
|
||||
success = ViewHierarchyAnimator.animateAddition(
|
||||
rootView,
|
||||
origin = ViewHierarchyAnimator.Hotspot.BOTTOM_LEFT
|
||||
)
|
||||
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNotNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 50, t = 100, r = 50, b = 100)
|
||||
endAnimation(rootView)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun animatesAppearingViewsRespectingMargins() {
|
||||
// CENTER.
|
||||
rootView.layout(0 /* l */, 0 /* t */, 0 /* r */, 0 /* b */)
|
||||
var success = ViewHierarchyAnimator.animateAddition(
|
||||
rootView,
|
||||
origin = ViewHierarchyAnimator.Hotspot.CENTER,
|
||||
includeMargins = true
|
||||
)
|
||||
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNotNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 75, t = 75, r = 75, b = 75)
|
||||
endAnimation(rootView)
|
||||
|
||||
// LEFT.
|
||||
rootView.layout(0 /* l */, 0 /* t */, 0 /* r */, 0 /* b */)
|
||||
success = ViewHierarchyAnimator.animateAddition(
|
||||
rootView, origin = ViewHierarchyAnimator.Hotspot.LEFT,
|
||||
includeMargins = true
|
||||
)
|
||||
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNotNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 0, t = 50, r = 0, b = 100)
|
||||
endAnimation(rootView)
|
||||
|
||||
// TOP_LEFT.
|
||||
rootView.layout(0 /* l */, 0 /* t */, 0 /* r */, 0 /* b */)
|
||||
success = ViewHierarchyAnimator.animateAddition(
|
||||
rootView,
|
||||
origin = ViewHierarchyAnimator.Hotspot.TOP_LEFT,
|
||||
includeMargins = true
|
||||
)
|
||||
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNotNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 0, t = 0, r = 0, b = 0)
|
||||
endAnimation(rootView)
|
||||
|
||||
// TOP.
|
||||
rootView.layout(150 /* l */, 0 /* t */, 150 /* r */, 0 /* b */)
|
||||
success = ViewHierarchyAnimator.animateAddition(
|
||||
rootView, origin = ViewHierarchyAnimator.Hotspot.TOP,
|
||||
includeMargins = true
|
||||
)
|
||||
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNotNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 50, t = 0, r = 100, b = 0)
|
||||
endAnimation(rootView)
|
||||
|
||||
// TOP_RIGHT.
|
||||
rootView.layout(150 /* l */, 0 /* t */, 150 /* r */, 0 /* b */)
|
||||
success = ViewHierarchyAnimator.animateAddition(
|
||||
rootView,
|
||||
origin = ViewHierarchyAnimator.Hotspot.TOP_RIGHT,
|
||||
includeMargins = true
|
||||
)
|
||||
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNotNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 150, t = 0, r = 150, b = 0)
|
||||
endAnimation(rootView)
|
||||
|
||||
// RIGHT.
|
||||
rootView.layout(150 /* l */, 150 /* t */, 150 /* r */, 150 /* b */)
|
||||
success = ViewHierarchyAnimator.animateAddition(
|
||||
rootView,
|
||||
origin = ViewHierarchyAnimator.Hotspot.RIGHT,
|
||||
includeMargins = true
|
||||
)
|
||||
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNotNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 150, t = 50, r = 150, b = 100)
|
||||
endAnimation(rootView)
|
||||
|
||||
// BOTTOM_RIGHT.
|
||||
rootView.layout(150 /* l */, 150 /* t */, 150 /* r */, 150 /* b */)
|
||||
success = ViewHierarchyAnimator.animateAddition(
|
||||
rootView,
|
||||
origin = ViewHierarchyAnimator.Hotspot.BOTTOM_RIGHT,
|
||||
includeMargins = true
|
||||
)
|
||||
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNotNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 150, t = 150, r = 150, b = 150)
|
||||
endAnimation(rootView)
|
||||
|
||||
// BOTTOM.
|
||||
rootView.layout(0 /* l */, 150 /* t */, 0 /* r */, 150 /* b */)
|
||||
success = ViewHierarchyAnimator.animateAddition(
|
||||
rootView,
|
||||
origin = ViewHierarchyAnimator.Hotspot.BOTTOM,
|
||||
includeMargins = true
|
||||
)
|
||||
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNotNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 50, t = 150, r = 100, b = 150)
|
||||
endAnimation(rootView)
|
||||
|
||||
// BOTTOM_LEFT.
|
||||
rootView.layout(0 /* l */, 150 /* t */, 0 /* r */, 150 /* b */)
|
||||
success = ViewHierarchyAnimator.animateAddition(
|
||||
rootView,
|
||||
origin = ViewHierarchyAnimator.Hotspot.BOTTOM_LEFT,
|
||||
includeMargins = true
|
||||
)
|
||||
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNotNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 0, t = 150, r = 0, b = 150)
|
||||
endAnimation(rootView)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun doesNotAnimateInvisibleViews() {
|
||||
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
|
||||
|
||||
ViewHierarchyAnimator.animate(rootView)
|
||||
// GONE.
|
||||
// GONE
|
||||
rootView.visibility = View.GONE
|
||||
var success = ViewHierarchyAnimator.animate(rootView)
|
||||
rootView.layout(0 /* l */, 15 /* t */, 55 /* r */, 80 /* b */)
|
||||
|
||||
assertFalse(success)
|
||||
assertNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 0, t = 15, r = 55, b = 80)
|
||||
|
||||
// INVISIBLE.
|
||||
rootView.visibility = View.INVISIBLE
|
||||
rootView.layout(0 /* l */, 20 /* t */, 0 /* r */, 20 /* b */)
|
||||
success = ViewHierarchyAnimator.animate(rootView)
|
||||
rootView.layout(0 /* l */, 20 /* t */, 10 /* r */, 50 /* b */)
|
||||
|
||||
assertFalse(success)
|
||||
assertNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 0, t = 20, r = 10, b = 50)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun doesNotAnimateAppearingViews() {
|
||||
// Starting with nothing.
|
||||
rootView.layout(0 /* l */, 0 /* t */, 0 /* r */, 0 /* b */)
|
||||
var success = ViewHierarchyAnimator.animate(rootView)
|
||||
rootView.layout(0 /* l */, 15 /* t */, 55 /* r */, 80 /* b */)
|
||||
|
||||
assertFalse(success)
|
||||
assertNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 0, t = 15, r = 55, b = 80)
|
||||
|
||||
// Starting with 0 width.
|
||||
rootView.layout(0 /* l */, 50 /* t */, 0 /* r */, 100 /* b */)
|
||||
success = ViewHierarchyAnimator.animate(rootView)
|
||||
rootView.layout(0 /* l */, 15 /* t */, 55 /* r */, 80 /* b */)
|
||||
|
||||
assertFalse(success)
|
||||
assertNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 0, t = 15, r = 55, b = 80)
|
||||
|
||||
// Starting with 0 height.
|
||||
rootView.layout(0 /* l */, 50 /* t */, 50 /* r */, 50 /* b */)
|
||||
success = ViewHierarchyAnimator.animate(rootView)
|
||||
rootView.layout(0 /* l */, 15 /* t */, 55 /* r */, 80 /* b */)
|
||||
|
||||
assertFalse(success)
|
||||
assertNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 0, t = 15, r = 55, b = 80)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun doesNotAnimateDisappearingViews() {
|
||||
rootView.layout(0 /* l */, 0 /* t */, 100 /* r */, 100 /* b */)
|
||||
|
||||
val success = ViewHierarchyAnimator.animate(rootView)
|
||||
// Ending with nothing.
|
||||
rootView.layout(0 /* l */, 0 /* t */, 0 /* r */, 0 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 0, t = 0, r = 0, b = 0)
|
||||
|
||||
// Ending with 0 width.
|
||||
rootView.layout(0 /* l */, 50 /* t */, 50 /* r */, 100 /* b */)
|
||||
endAnimation(rootView)
|
||||
rootView.layout(0 /* l */, 15 /* t */, 0 /* r */, 80 /* b */)
|
||||
|
||||
assertNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 0, t = 15, r = 0, b = 80)
|
||||
|
||||
// Ending with 0 height.
|
||||
rootView.layout(0 /* l */, 50 /* t */, 50 /* r */, 100 /* b */)
|
||||
endAnimation(rootView)
|
||||
rootView.layout(0 /* l */, 50 /* t */, 55 /* r */, 50 /* b */)
|
||||
|
||||
assertNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 0, t = 50, r = 55, b = 50)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun doesNotAnimateUnchangingBounds() {
|
||||
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
|
||||
|
||||
ViewHierarchyAnimator.animate(rootView)
|
||||
val success = ViewHierarchyAnimator.animate(rootView)
|
||||
// No bounds are changed.
|
||||
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNull(rootView.getTag(R.id.tag_animator))
|
||||
checkBounds(rootView, l = 10, t = 10, r = 50, b = 50)
|
||||
|
||||
@@ -191,7 +628,7 @@ class ViewHierarchyAnimatorTest : SysuiTestCase() {
|
||||
fun doesNotAnimateExcludedBounds() {
|
||||
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
|
||||
|
||||
ViewHierarchyAnimator.animate(
|
||||
val success = ViewHierarchyAnimator.animate(
|
||||
rootView,
|
||||
bounds = setOf(ViewHierarchyAnimator.Bound.LEFT, ViewHierarchyAnimator.Bound.TOP),
|
||||
interpolator = TEST_INTERPOLATOR
|
||||
@@ -199,6 +636,7 @@ class ViewHierarchyAnimatorTest : SysuiTestCase() {
|
||||
// Change all bounds.
|
||||
rootView.layout(0 /* l */, 20 /* t */, 70 /* r */, 80 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNotNull(rootView.getTag(R.id.tag_animator))
|
||||
advanceAnimation(rootView, 0.5f)
|
||||
checkBounds(rootView, l = 5, t = 15, r = 70, b = 80)
|
||||
@@ -211,10 +649,11 @@ class ViewHierarchyAnimatorTest : SysuiTestCase() {
|
||||
fun stopsAnimatingAfterSingleLayout() {
|
||||
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
|
||||
|
||||
ViewHierarchyAnimator.animateNextUpdate(rootView)
|
||||
val success = ViewHierarchyAnimator.animateNextUpdate(rootView)
|
||||
// Change all bounds.
|
||||
rootView.layout(0 /* l */, 15 /* t */, 70 /* r */, 80 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNotNull(rootView.getTag(R.id.tag_animator))
|
||||
endAnimation(rootView)
|
||||
assertNull(rootView.getTag(R.id.tag_animator))
|
||||
@@ -231,10 +670,11 @@ class ViewHierarchyAnimatorTest : SysuiTestCase() {
|
||||
fun stopsAnimatingWhenInstructed() {
|
||||
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
|
||||
|
||||
ViewHierarchyAnimator.animate(rootView)
|
||||
val success = ViewHierarchyAnimator.animate(rootView)
|
||||
// Change all bounds.
|
||||
rootView.layout(0 /* l */, 15 /* t */, 70 /* r */, 80 /* b */)
|
||||
|
||||
assertTrue(success)
|
||||
assertNotNull(rootView.getTag(R.id.tag_animator))
|
||||
endAnimation(rootView)
|
||||
assertNull(rootView.getTag(R.id.tag_animator))
|
||||
|
||||
Reference in New Issue
Block a user