Loading packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt +111 −57 Original line number Diff line number Diff line Loading @@ -360,7 +360,9 @@ class ViewHierarchyAnimator { * [interpolator] and [duration]. * * The end state of the animation is controlled by [destination]. This value can be any of * the four corners, any of the four edges, or the center of the view. * 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 [destination], the translation of those margins can be * included by specifying [includeMargins]. * * @param onAnimationEnd an optional runnable that will be run once the animation finishes * successfully. Will not be run if the animation is cancelled. Loading @@ -371,6 +373,7 @@ class ViewHierarchyAnimator { destination: Hotspot = Hotspot.CENTER, interpolator: Interpolator = DEFAULT_REMOVAL_INTERPOLATOR, duration: Long = DEFAULT_DURATION, includeMargins: Boolean = false, onAnimationEnd: Runnable? = null, ): Boolean { if ( Loading Loading @@ -428,10 +431,12 @@ class ViewHierarchyAnimator { val endValues = processEndValuesForRemoval( destination, rootView, rootView.left, rootView.top, rootView.right, rootView.bottom rootView.bottom, includeMargins, ) val boundsToAnimate = mutableSetOf<Bound>() Loading Loading @@ -718,70 +723,111 @@ class ViewHierarchyAnimator { * | | -> | | -> | | -> x---x -> x * | | x-------x x-----x * x---------x * 4) destination=TOP, includeMargins=true (and view has large top margin) * x---------x * x---------x * x---------x x---------x * x---------x | | * x---------x | | x---------x * | | | | * | | -> x---------x -> -> -> * | | * x---------x * ``` */ private fun processEndValuesForRemoval( destination: Hotspot, rootView: View, left: Int, top: Int, right: Int, bottom: Int bottom: Int, includeMargins: Boolean = false, ): Map<Bound, Int> { val endLeft = when (destination) { Hotspot.CENTER -> (left + right) / 2 Hotspot.BOTTOM, Hotspot.BOTTOM_LEFT, Hotspot.LEFT, Hotspot.TOP_LEFT, Hotspot.TOP -> left Hotspot.TOP_RIGHT, Hotspot.RIGHT, Hotspot.BOTTOM_RIGHT -> right } val endTop = when (destination) { Hotspot.CENTER -> (top + bottom) / 2 Hotspot.LEFT, Hotspot.TOP_LEFT, Hotspot.TOP, Hotspot.TOP_RIGHT, Hotspot.RIGHT -> top Hotspot.BOTTOM_RIGHT, Hotspot.BOTTOM, Hotspot.BOTTOM_LEFT -> bottom } val endRight = when (destination) { Hotspot.CENTER -> (left + right) / 2 Hotspot.TOP, Hotspot.TOP_RIGHT, Hotspot.RIGHT, Hotspot.BOTTOM_RIGHT, Hotspot.BOTTOM -> right Hotspot.BOTTOM_LEFT, Hotspot.LEFT, Hotspot.TOP_LEFT -> left } val endBottom = when (destination) { Hotspot.CENTER -> (top + bottom) / 2 Hotspot.RIGHT, Hotspot.BOTTOM_RIGHT, Hotspot.BOTTOM, Hotspot.BOTTOM_LEFT, Hotspot.LEFT -> bottom Hotspot.TOP_LEFT, Hotspot.TOP, Hotspot.TOP_RIGHT -> top val marginAdjustment = if (includeMargins && (rootView.layoutParams is ViewGroup.MarginLayoutParams)) { val marginLp = rootView.layoutParams as ViewGroup.MarginLayoutParams DimenHolder( left = marginLp.leftMargin, top = marginLp.topMargin, right = marginLp.rightMargin, bottom = marginLp.bottomMargin ) } else { DimenHolder(0, 0, 0, 0) } return mapOf( Bound.LEFT to endLeft, // These are the end values to use *if* this bound is part of the destination. val endLeft = left - marginAdjustment.left val endTop = top - marginAdjustment.top val endRight = right + marginAdjustment.right val endBottom = bottom + marginAdjustment.bottom // For the below calculations: We need to ensure that the destination bound and the // bound *opposite* to the destination bound end at the same value, to ensure that the // view has size 0 for that dimension. // For example, // - If destination=TOP, then endTop == endBottom. Left and right stay the same. // - If destination=RIGHT, then endRight == endLeft. Top and bottom stay the same. // - If destination=BOTTOM_LEFT, then endBottom == endTop AND endLeft == endRight. return when (destination) { Hotspot.TOP -> mapOf( Bound.TOP to endTop, Bound.BOTTOM to endTop, Bound.LEFT to left, Bound.RIGHT to right, ) Hotspot.TOP_RIGHT -> mapOf( Bound.TOP to endTop, Bound.BOTTOM to endTop, Bound.RIGHT to endRight, Bound.BOTTOM to endBottom Bound.LEFT to endRight, ) Hotspot.RIGHT -> mapOf( Bound.RIGHT to endRight, Bound.LEFT to endRight, Bound.TOP to top, Bound.BOTTOM to bottom, ) Hotspot.BOTTOM_RIGHT -> mapOf( Bound.BOTTOM to endBottom, Bound.TOP to endBottom, Bound.RIGHT to endRight, Bound.LEFT to endRight, ) Hotspot.BOTTOM -> mapOf( Bound.BOTTOM to endBottom, Bound.TOP to endBottom, Bound.LEFT to left, Bound.RIGHT to right, ) Hotspot.BOTTOM_LEFT -> mapOf( Bound.BOTTOM to endBottom, Bound.TOP to endBottom, Bound.LEFT to endLeft, Bound.RIGHT to endLeft, ) Hotspot.LEFT -> mapOf( Bound.LEFT to endLeft, Bound.RIGHT to endLeft, Bound.TOP to top, Bound.BOTTOM to bottom, ) Hotspot.TOP_LEFT -> mapOf( Bound.TOP to endTop, Bound.BOTTOM to endTop, Bound.LEFT to endLeft, Bound.RIGHT to endLeft, ) Hotspot.CENTER -> mapOf( Bound.LEFT to (endLeft + endRight) / 2, Bound.RIGHT to (endLeft + endRight) / 2, Bound.TOP to (endTop + endBottom) / 2, Bound.BOTTOM to (endTop + endBottom) / 2, ) } } /** Loading Loading @@ -1061,4 +1107,12 @@ class ViewHierarchyAnimator { abstract fun setValue(view: View, value: Int) abstract fun getValue(view: View): Int } /** Simple data class to hold a set of dimens for left, top, right, bottom. */ private data class DimenHolder( val left: Int, val top: Int, val right: Int, val bottom: Int, ) } packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt +1 −2 Original line number Diff line number Diff line Loading @@ -199,10 +199,9 @@ open class MediaTttChipControllerSender @Inject constructor( ViewHierarchyAnimator.Hotspot.TOP, Interpolators.EMPHASIZED_ACCELERATE, ANIMATION_DURATION, includeMargins = true, onAnimationEnd, ) // TODO(b/203800644): Add includeMargins as an option to ViewHierarchyAnimator so that the // animateChipOut matches the animateChipIn. } override fun shouldIgnoreViewRemoval(info: ChipSenderInfo, removalReason: String): Boolean { Loading packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt +269 −5 Original line number Diff line number Diff line Loading @@ -935,6 +935,251 @@ ViewHierarchyAnimatorTest : SysuiTestCase() { checkBounds(remainingChild, l = 0, t = 0, r = 100, b = 100) } /* ******** start of animatesViewRemoval_includeMarginsTrue tests ******** */ @Test fun animatesViewRemoval_includeMarginsTrue_center() { setUpRootWithChildren(includeMarginsOnFirstChild = true) val removedChild = rootView.getChildAt(0) val originalLeft = removedChild.left val originalTop = removedChild.top val originalRight = removedChild.right val originalBottom = removedChild.bottom val success = ViewHierarchyAnimator.animateRemoval( removedChild, destination = ViewHierarchyAnimator.Hotspot.CENTER, includeMargins = true, ) forceLayout() assertTrue(success) assertNotNull(removedChild.getTag(R.id.tag_animator)) advanceAnimation(removedChild, 1.0f) val expectedX = ((originalLeft - M_LEFT) + (originalRight + M_RIGHT)) / 2 val expectedY = ((originalTop - M_TOP) + (originalBottom + M_BOTTOM)) / 2 checkBounds( removedChild, l = expectedX, t = expectedY, r = expectedX, b = expectedY ) } @Test fun animatesViewRemoval_includeMarginsTrue_left() { setUpRootWithChildren(includeMarginsOnFirstChild = true) val removedChild = rootView.getChildAt(0) val originalLeft = removedChild.left val originalTop = removedChild.top val originalBottom = removedChild.bottom val success = ViewHierarchyAnimator.animateRemoval( removedChild, destination = ViewHierarchyAnimator.Hotspot.LEFT, includeMargins = true, ) forceLayout() assertTrue(success) assertNotNull(removedChild.getTag(R.id.tag_animator)) advanceAnimation(removedChild, 1.0f) checkBounds( removedChild, l = originalLeft - M_LEFT, t = originalTop, r = originalLeft - M_LEFT, b = originalBottom ) } @Test fun animatesViewRemoval_includeMarginsTrue_topLeft() { setUpRootWithChildren(includeMarginsOnFirstChild = true) val removedChild = rootView.getChildAt(0) val originalLeft = removedChild.left val originalTop = removedChild.top val success = ViewHierarchyAnimator.animateRemoval( removedChild, destination = ViewHierarchyAnimator.Hotspot.TOP_LEFT, includeMargins = true, ) forceLayout() assertTrue(success) assertNotNull(removedChild.getTag(R.id.tag_animator)) advanceAnimation(removedChild, 1.0f) checkBounds( removedChild, l = originalLeft - M_LEFT, t = originalTop - M_TOP, r = originalLeft - M_LEFT, b = originalTop - M_TOP ) } @Test fun animatesViewRemoval_includeMarginsTrue_top() { setUpRootWithChildren(includeMarginsOnFirstChild = true) val removedChild = rootView.getChildAt(0) val originalLeft = removedChild.left val originalTop = removedChild.top val originalRight = removedChild.right val success = ViewHierarchyAnimator.animateRemoval( removedChild, destination = ViewHierarchyAnimator.Hotspot.TOP, includeMargins = true, ) forceLayout() assertTrue(success) assertNotNull(removedChild.getTag(R.id.tag_animator)) advanceAnimation(removedChild, 1.0f) checkBounds( removedChild, l = originalLeft, t = originalTop - M_TOP, r = originalRight, b = originalTop - M_TOP ) } @Test fun animatesViewRemoval_includeMarginsTrue_topRight() { setUpRootWithChildren(includeMarginsOnFirstChild = true) val removedChild = rootView.getChildAt(0) val originalTop = removedChild.top val originalRight = removedChild.right val success = ViewHierarchyAnimator.animateRemoval( removedChild, destination = ViewHierarchyAnimator.Hotspot.TOP_RIGHT, includeMargins = true, ) forceLayout() assertTrue(success) assertNotNull(removedChild.getTag(R.id.tag_animator)) advanceAnimation(removedChild, 1.0f) checkBounds( removedChild, l = originalRight + M_RIGHT, t = originalTop - M_TOP, r = originalRight + M_RIGHT, b = originalTop - M_TOP ) } @Test fun animatesViewRemoval_includeMarginsTrue_right() { setUpRootWithChildren(includeMarginsOnFirstChild = true) val removedChild = rootView.getChildAt(0) val originalTop = removedChild.top val originalRight = removedChild.right val originalBottom = removedChild.bottom val success = ViewHierarchyAnimator.animateRemoval( removedChild, destination = ViewHierarchyAnimator.Hotspot.RIGHT, includeMargins = true, ) forceLayout() assertTrue(success) assertNotNull(removedChild.getTag(R.id.tag_animator)) advanceAnimation(removedChild, 1.0f) checkBounds( removedChild, l = originalRight + M_RIGHT, t = originalTop, r = originalRight + M_RIGHT, b = originalBottom ) } @Test fun animatesViewRemoval_includeMarginsTrue_bottomRight() { setUpRootWithChildren(includeMarginsOnFirstChild = true) val removedChild = rootView.getChildAt(0) val originalRight = removedChild.right val originalBottom = removedChild.bottom val success = ViewHierarchyAnimator.animateRemoval( removedChild, destination = ViewHierarchyAnimator.Hotspot.BOTTOM_RIGHT, includeMargins = true, ) forceLayout() assertTrue(success) assertNotNull(removedChild.getTag(R.id.tag_animator)) advanceAnimation(removedChild, 1.0f) checkBounds( removedChild, l = originalRight + M_RIGHT, t = originalBottom + M_BOTTOM, r = originalRight + M_RIGHT, b = originalBottom + M_BOTTOM ) } @Test fun animatesViewRemoval_includeMarginsTrue_bottom() { setUpRootWithChildren(includeMarginsOnFirstChild = true) val removedChild = rootView.getChildAt(0) val originalLeft = removedChild.left val originalRight = removedChild.right val originalBottom = removedChild.bottom val success = ViewHierarchyAnimator.animateRemoval( removedChild, destination = ViewHierarchyAnimator.Hotspot.BOTTOM, includeMargins = true, ) forceLayout() assertTrue(success) assertNotNull(removedChild.getTag(R.id.tag_animator)) advanceAnimation(removedChild, 1.0f) checkBounds( removedChild, l = originalLeft, t = originalBottom + M_BOTTOM, r = originalRight, b = originalBottom + M_BOTTOM ) } @Test fun animatesViewRemoval_includeMarginsTrue_bottomLeft() { setUpRootWithChildren(includeMarginsOnFirstChild = true) val removedChild = rootView.getChildAt(0) val originalLeft = removedChild.left val originalBottom = removedChild.bottom val success = ViewHierarchyAnimator.animateRemoval( removedChild, destination = ViewHierarchyAnimator.Hotspot.BOTTOM_LEFT, includeMargins = true, ) forceLayout() assertTrue(success) assertNotNull(removedChild.getTag(R.id.tag_animator)) advanceAnimation(removedChild, 1.0f) checkBounds( removedChild, l = originalLeft - M_LEFT, t = originalBottom + M_BOTTOM, r = originalLeft - M_LEFT, b = originalBottom + M_BOTTOM ) } /* ******** end of animatesViewRemoval_includeMarginsTrue tests ******** */ @Test fun animatesChildrenDuringViewRemoval() { setUpRootWithChildren() Loading Loading @@ -1215,7 +1460,7 @@ ViewHierarchyAnimatorTest : SysuiTestCase() { checkBounds(rootView, l = 10, t = 10, r = 50, b = 50) } private fun setUpRootWithChildren() { private fun setUpRootWithChildren(includeMarginsOnFirstChild: Boolean = false) { rootView = LinearLayout(mContext) (rootView as LinearLayout).orientation = LinearLayout.HORIZONTAL (rootView as LinearLayout).weightSum = 1f Loading @@ -1229,13 +1474,26 @@ ViewHierarchyAnimatorTest : SysuiTestCase() { val secondChild = View(mContext) rootView.addView(secondChild) val childParams = LinearLayout.LayoutParams( val firstChildParams = LinearLayout.LayoutParams( 0 /* width */, LinearLayout.LayoutParams.MATCH_PARENT ) firstChildParams.weight = 0.5f if (includeMarginsOnFirstChild) { firstChildParams.leftMargin = M_LEFT firstChildParams.topMargin = M_TOP firstChildParams.rightMargin = M_RIGHT firstChildParams.bottomMargin = M_BOTTOM } firstChild.layoutParams = firstChildParams val secondChildParams = LinearLayout.LayoutParams( 0 /* width */, LinearLayout.LayoutParams.MATCH_PARENT ) childParams.weight = 0.5f firstChild.layoutParams = childParams secondChild.layoutParams = childParams secondChildParams.weight = 0.5f secondChild.layoutParams = secondChildParams firstGrandChild.layoutParams = RelativeLayout.LayoutParams(40 /* width */, 40 /* height */) (firstGrandChild.layoutParams as RelativeLayout.LayoutParams) .addRule(RelativeLayout.ALIGN_PARENT_START) Loading Loading @@ -1315,3 +1573,9 @@ ViewHierarchyAnimatorTest : SysuiTestCase() { } } } // Margin values. private const val M_LEFT = 14 private const val M_TOP = 16 private const val M_RIGHT = 18 private const val M_BOTTOM = 20 Loading
packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt +111 −57 Original line number Diff line number Diff line Loading @@ -360,7 +360,9 @@ class ViewHierarchyAnimator { * [interpolator] and [duration]. * * The end state of the animation is controlled by [destination]. This value can be any of * the four corners, any of the four edges, or the center of the view. * 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 [destination], the translation of those margins can be * included by specifying [includeMargins]. * * @param onAnimationEnd an optional runnable that will be run once the animation finishes * successfully. Will not be run if the animation is cancelled. Loading @@ -371,6 +373,7 @@ class ViewHierarchyAnimator { destination: Hotspot = Hotspot.CENTER, interpolator: Interpolator = DEFAULT_REMOVAL_INTERPOLATOR, duration: Long = DEFAULT_DURATION, includeMargins: Boolean = false, onAnimationEnd: Runnable? = null, ): Boolean { if ( Loading Loading @@ -428,10 +431,12 @@ class ViewHierarchyAnimator { val endValues = processEndValuesForRemoval( destination, rootView, rootView.left, rootView.top, rootView.right, rootView.bottom rootView.bottom, includeMargins, ) val boundsToAnimate = mutableSetOf<Bound>() Loading Loading @@ -718,70 +723,111 @@ class ViewHierarchyAnimator { * | | -> | | -> | | -> x---x -> x * | | x-------x x-----x * x---------x * 4) destination=TOP, includeMargins=true (and view has large top margin) * x---------x * x---------x * x---------x x---------x * x---------x | | * x---------x | | x---------x * | | | | * | | -> x---------x -> -> -> * | | * x---------x * ``` */ private fun processEndValuesForRemoval( destination: Hotspot, rootView: View, left: Int, top: Int, right: Int, bottom: Int bottom: Int, includeMargins: Boolean = false, ): Map<Bound, Int> { val endLeft = when (destination) { Hotspot.CENTER -> (left + right) / 2 Hotspot.BOTTOM, Hotspot.BOTTOM_LEFT, Hotspot.LEFT, Hotspot.TOP_LEFT, Hotspot.TOP -> left Hotspot.TOP_RIGHT, Hotspot.RIGHT, Hotspot.BOTTOM_RIGHT -> right } val endTop = when (destination) { Hotspot.CENTER -> (top + bottom) / 2 Hotspot.LEFT, Hotspot.TOP_LEFT, Hotspot.TOP, Hotspot.TOP_RIGHT, Hotspot.RIGHT -> top Hotspot.BOTTOM_RIGHT, Hotspot.BOTTOM, Hotspot.BOTTOM_LEFT -> bottom } val endRight = when (destination) { Hotspot.CENTER -> (left + right) / 2 Hotspot.TOP, Hotspot.TOP_RIGHT, Hotspot.RIGHT, Hotspot.BOTTOM_RIGHT, Hotspot.BOTTOM -> right Hotspot.BOTTOM_LEFT, Hotspot.LEFT, Hotspot.TOP_LEFT -> left } val endBottom = when (destination) { Hotspot.CENTER -> (top + bottom) / 2 Hotspot.RIGHT, Hotspot.BOTTOM_RIGHT, Hotspot.BOTTOM, Hotspot.BOTTOM_LEFT, Hotspot.LEFT -> bottom Hotspot.TOP_LEFT, Hotspot.TOP, Hotspot.TOP_RIGHT -> top val marginAdjustment = if (includeMargins && (rootView.layoutParams is ViewGroup.MarginLayoutParams)) { val marginLp = rootView.layoutParams as ViewGroup.MarginLayoutParams DimenHolder( left = marginLp.leftMargin, top = marginLp.topMargin, right = marginLp.rightMargin, bottom = marginLp.bottomMargin ) } else { DimenHolder(0, 0, 0, 0) } return mapOf( Bound.LEFT to endLeft, // These are the end values to use *if* this bound is part of the destination. val endLeft = left - marginAdjustment.left val endTop = top - marginAdjustment.top val endRight = right + marginAdjustment.right val endBottom = bottom + marginAdjustment.bottom // For the below calculations: We need to ensure that the destination bound and the // bound *opposite* to the destination bound end at the same value, to ensure that the // view has size 0 for that dimension. // For example, // - If destination=TOP, then endTop == endBottom. Left and right stay the same. // - If destination=RIGHT, then endRight == endLeft. Top and bottom stay the same. // - If destination=BOTTOM_LEFT, then endBottom == endTop AND endLeft == endRight. return when (destination) { Hotspot.TOP -> mapOf( Bound.TOP to endTop, Bound.BOTTOM to endTop, Bound.LEFT to left, Bound.RIGHT to right, ) Hotspot.TOP_RIGHT -> mapOf( Bound.TOP to endTop, Bound.BOTTOM to endTop, Bound.RIGHT to endRight, Bound.BOTTOM to endBottom Bound.LEFT to endRight, ) Hotspot.RIGHT -> mapOf( Bound.RIGHT to endRight, Bound.LEFT to endRight, Bound.TOP to top, Bound.BOTTOM to bottom, ) Hotspot.BOTTOM_RIGHT -> mapOf( Bound.BOTTOM to endBottom, Bound.TOP to endBottom, Bound.RIGHT to endRight, Bound.LEFT to endRight, ) Hotspot.BOTTOM -> mapOf( Bound.BOTTOM to endBottom, Bound.TOP to endBottom, Bound.LEFT to left, Bound.RIGHT to right, ) Hotspot.BOTTOM_LEFT -> mapOf( Bound.BOTTOM to endBottom, Bound.TOP to endBottom, Bound.LEFT to endLeft, Bound.RIGHT to endLeft, ) Hotspot.LEFT -> mapOf( Bound.LEFT to endLeft, Bound.RIGHT to endLeft, Bound.TOP to top, Bound.BOTTOM to bottom, ) Hotspot.TOP_LEFT -> mapOf( Bound.TOP to endTop, Bound.BOTTOM to endTop, Bound.LEFT to endLeft, Bound.RIGHT to endLeft, ) Hotspot.CENTER -> mapOf( Bound.LEFT to (endLeft + endRight) / 2, Bound.RIGHT to (endLeft + endRight) / 2, Bound.TOP to (endTop + endBottom) / 2, Bound.BOTTOM to (endTop + endBottom) / 2, ) } } /** Loading Loading @@ -1061,4 +1107,12 @@ class ViewHierarchyAnimator { abstract fun setValue(view: View, value: Int) abstract fun getValue(view: View): Int } /** Simple data class to hold a set of dimens for left, top, right, bottom. */ private data class DimenHolder( val left: Int, val top: Int, val right: Int, val bottom: Int, ) }
packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt +1 −2 Original line number Diff line number Diff line Loading @@ -199,10 +199,9 @@ open class MediaTttChipControllerSender @Inject constructor( ViewHierarchyAnimator.Hotspot.TOP, Interpolators.EMPHASIZED_ACCELERATE, ANIMATION_DURATION, includeMargins = true, onAnimationEnd, ) // TODO(b/203800644): Add includeMargins as an option to ViewHierarchyAnimator so that the // animateChipOut matches the animateChipIn. } override fun shouldIgnoreViewRemoval(info: ChipSenderInfo, removalReason: String): Boolean { Loading
packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt +269 −5 Original line number Diff line number Diff line Loading @@ -935,6 +935,251 @@ ViewHierarchyAnimatorTest : SysuiTestCase() { checkBounds(remainingChild, l = 0, t = 0, r = 100, b = 100) } /* ******** start of animatesViewRemoval_includeMarginsTrue tests ******** */ @Test fun animatesViewRemoval_includeMarginsTrue_center() { setUpRootWithChildren(includeMarginsOnFirstChild = true) val removedChild = rootView.getChildAt(0) val originalLeft = removedChild.left val originalTop = removedChild.top val originalRight = removedChild.right val originalBottom = removedChild.bottom val success = ViewHierarchyAnimator.animateRemoval( removedChild, destination = ViewHierarchyAnimator.Hotspot.CENTER, includeMargins = true, ) forceLayout() assertTrue(success) assertNotNull(removedChild.getTag(R.id.tag_animator)) advanceAnimation(removedChild, 1.0f) val expectedX = ((originalLeft - M_LEFT) + (originalRight + M_RIGHT)) / 2 val expectedY = ((originalTop - M_TOP) + (originalBottom + M_BOTTOM)) / 2 checkBounds( removedChild, l = expectedX, t = expectedY, r = expectedX, b = expectedY ) } @Test fun animatesViewRemoval_includeMarginsTrue_left() { setUpRootWithChildren(includeMarginsOnFirstChild = true) val removedChild = rootView.getChildAt(0) val originalLeft = removedChild.left val originalTop = removedChild.top val originalBottom = removedChild.bottom val success = ViewHierarchyAnimator.animateRemoval( removedChild, destination = ViewHierarchyAnimator.Hotspot.LEFT, includeMargins = true, ) forceLayout() assertTrue(success) assertNotNull(removedChild.getTag(R.id.tag_animator)) advanceAnimation(removedChild, 1.0f) checkBounds( removedChild, l = originalLeft - M_LEFT, t = originalTop, r = originalLeft - M_LEFT, b = originalBottom ) } @Test fun animatesViewRemoval_includeMarginsTrue_topLeft() { setUpRootWithChildren(includeMarginsOnFirstChild = true) val removedChild = rootView.getChildAt(0) val originalLeft = removedChild.left val originalTop = removedChild.top val success = ViewHierarchyAnimator.animateRemoval( removedChild, destination = ViewHierarchyAnimator.Hotspot.TOP_LEFT, includeMargins = true, ) forceLayout() assertTrue(success) assertNotNull(removedChild.getTag(R.id.tag_animator)) advanceAnimation(removedChild, 1.0f) checkBounds( removedChild, l = originalLeft - M_LEFT, t = originalTop - M_TOP, r = originalLeft - M_LEFT, b = originalTop - M_TOP ) } @Test fun animatesViewRemoval_includeMarginsTrue_top() { setUpRootWithChildren(includeMarginsOnFirstChild = true) val removedChild = rootView.getChildAt(0) val originalLeft = removedChild.left val originalTop = removedChild.top val originalRight = removedChild.right val success = ViewHierarchyAnimator.animateRemoval( removedChild, destination = ViewHierarchyAnimator.Hotspot.TOP, includeMargins = true, ) forceLayout() assertTrue(success) assertNotNull(removedChild.getTag(R.id.tag_animator)) advanceAnimation(removedChild, 1.0f) checkBounds( removedChild, l = originalLeft, t = originalTop - M_TOP, r = originalRight, b = originalTop - M_TOP ) } @Test fun animatesViewRemoval_includeMarginsTrue_topRight() { setUpRootWithChildren(includeMarginsOnFirstChild = true) val removedChild = rootView.getChildAt(0) val originalTop = removedChild.top val originalRight = removedChild.right val success = ViewHierarchyAnimator.animateRemoval( removedChild, destination = ViewHierarchyAnimator.Hotspot.TOP_RIGHT, includeMargins = true, ) forceLayout() assertTrue(success) assertNotNull(removedChild.getTag(R.id.tag_animator)) advanceAnimation(removedChild, 1.0f) checkBounds( removedChild, l = originalRight + M_RIGHT, t = originalTop - M_TOP, r = originalRight + M_RIGHT, b = originalTop - M_TOP ) } @Test fun animatesViewRemoval_includeMarginsTrue_right() { setUpRootWithChildren(includeMarginsOnFirstChild = true) val removedChild = rootView.getChildAt(0) val originalTop = removedChild.top val originalRight = removedChild.right val originalBottom = removedChild.bottom val success = ViewHierarchyAnimator.animateRemoval( removedChild, destination = ViewHierarchyAnimator.Hotspot.RIGHT, includeMargins = true, ) forceLayout() assertTrue(success) assertNotNull(removedChild.getTag(R.id.tag_animator)) advanceAnimation(removedChild, 1.0f) checkBounds( removedChild, l = originalRight + M_RIGHT, t = originalTop, r = originalRight + M_RIGHT, b = originalBottom ) } @Test fun animatesViewRemoval_includeMarginsTrue_bottomRight() { setUpRootWithChildren(includeMarginsOnFirstChild = true) val removedChild = rootView.getChildAt(0) val originalRight = removedChild.right val originalBottom = removedChild.bottom val success = ViewHierarchyAnimator.animateRemoval( removedChild, destination = ViewHierarchyAnimator.Hotspot.BOTTOM_RIGHT, includeMargins = true, ) forceLayout() assertTrue(success) assertNotNull(removedChild.getTag(R.id.tag_animator)) advanceAnimation(removedChild, 1.0f) checkBounds( removedChild, l = originalRight + M_RIGHT, t = originalBottom + M_BOTTOM, r = originalRight + M_RIGHT, b = originalBottom + M_BOTTOM ) } @Test fun animatesViewRemoval_includeMarginsTrue_bottom() { setUpRootWithChildren(includeMarginsOnFirstChild = true) val removedChild = rootView.getChildAt(0) val originalLeft = removedChild.left val originalRight = removedChild.right val originalBottom = removedChild.bottom val success = ViewHierarchyAnimator.animateRemoval( removedChild, destination = ViewHierarchyAnimator.Hotspot.BOTTOM, includeMargins = true, ) forceLayout() assertTrue(success) assertNotNull(removedChild.getTag(R.id.tag_animator)) advanceAnimation(removedChild, 1.0f) checkBounds( removedChild, l = originalLeft, t = originalBottom + M_BOTTOM, r = originalRight, b = originalBottom + M_BOTTOM ) } @Test fun animatesViewRemoval_includeMarginsTrue_bottomLeft() { setUpRootWithChildren(includeMarginsOnFirstChild = true) val removedChild = rootView.getChildAt(0) val originalLeft = removedChild.left val originalBottom = removedChild.bottom val success = ViewHierarchyAnimator.animateRemoval( removedChild, destination = ViewHierarchyAnimator.Hotspot.BOTTOM_LEFT, includeMargins = true, ) forceLayout() assertTrue(success) assertNotNull(removedChild.getTag(R.id.tag_animator)) advanceAnimation(removedChild, 1.0f) checkBounds( removedChild, l = originalLeft - M_LEFT, t = originalBottom + M_BOTTOM, r = originalLeft - M_LEFT, b = originalBottom + M_BOTTOM ) } /* ******** end of animatesViewRemoval_includeMarginsTrue tests ******** */ @Test fun animatesChildrenDuringViewRemoval() { setUpRootWithChildren() Loading Loading @@ -1215,7 +1460,7 @@ ViewHierarchyAnimatorTest : SysuiTestCase() { checkBounds(rootView, l = 10, t = 10, r = 50, b = 50) } private fun setUpRootWithChildren() { private fun setUpRootWithChildren(includeMarginsOnFirstChild: Boolean = false) { rootView = LinearLayout(mContext) (rootView as LinearLayout).orientation = LinearLayout.HORIZONTAL (rootView as LinearLayout).weightSum = 1f Loading @@ -1229,13 +1474,26 @@ ViewHierarchyAnimatorTest : SysuiTestCase() { val secondChild = View(mContext) rootView.addView(secondChild) val childParams = LinearLayout.LayoutParams( val firstChildParams = LinearLayout.LayoutParams( 0 /* width */, LinearLayout.LayoutParams.MATCH_PARENT ) firstChildParams.weight = 0.5f if (includeMarginsOnFirstChild) { firstChildParams.leftMargin = M_LEFT firstChildParams.topMargin = M_TOP firstChildParams.rightMargin = M_RIGHT firstChildParams.bottomMargin = M_BOTTOM } firstChild.layoutParams = firstChildParams val secondChildParams = LinearLayout.LayoutParams( 0 /* width */, LinearLayout.LayoutParams.MATCH_PARENT ) childParams.weight = 0.5f firstChild.layoutParams = childParams secondChild.layoutParams = childParams secondChildParams.weight = 0.5f secondChild.layoutParams = secondChildParams firstGrandChild.layoutParams = RelativeLayout.LayoutParams(40 /* width */, 40 /* height */) (firstGrandChild.layoutParams as RelativeLayout.LayoutParams) .addRule(RelativeLayout.ALIGN_PARENT_START) Loading Loading @@ -1315,3 +1573,9 @@ ViewHierarchyAnimatorTest : SysuiTestCase() { } } } // Margin values. private const val M_LEFT = 14 private const val M_TOP = 16 private const val M_RIGHT = 18 private const val M_BOTTOM = 20