Merge "[Media TTT] Add the margins as part of the removal animation." into tm-qpr-dev am: 88c9f3af10 am: d91150b780
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20140432 Change-Id: Ibb5e13926da85cf2681c5b08814ba0f126d679c0 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -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.
|
||||
@@ -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 (
|
||||
@@ -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>()
|
||||
@@ -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,
|
||||
Bound.TOP to endTop,
|
||||
Bound.RIGHT to endRight,
|
||||
Bound.BOTTOM to endBottom
|
||||
)
|
||||
// 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.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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
@@ -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
|
||||
@@ -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
|
||||
)
|
||||
childParams.weight = 0.5f
|
||||
firstChild.layoutParams = childParams
|
||||
secondChild.layoutParams = childParams
|
||||
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
|
||||
)
|
||||
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)
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user