Loading packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt +47 −119 Original line number Diff line number Diff line Loading @@ -22,12 +22,11 @@ import android.annotation.IntRange import android.annotation.SuppressLint import android.content.Context import android.graphics.Canvas import android.graphics.Rect import android.text.Layout import android.text.TextUtils import android.text.format.DateFormat import android.util.AttributeSet import android.util.MathUtils import android.util.MathUtils.constrainedMap import android.widget.TextView import com.android.internal.annotations.VisibleForTesting import com.android.systemui.animation.GlyphCallback Loading @@ -40,8 +39,6 @@ import java.io.PrintWriter import java.util.Calendar import java.util.Locale import java.util.TimeZone import kotlin.math.max import kotlin.math.min /** * Displays the time with the hour positioned above the minutes. (ie: 09 above 30 is 9:30) Loading Loading @@ -478,122 +475,56 @@ class AnimatableClockView @JvmOverloads constructor( pw.println(" time=$time") } fun moveForSplitShade(fromRect: Rect, toRect: Rect, fraction: Float) { // Do we need to cancel an in-flight animation? // Need to also check against 0.0f here; we can sometimes get two calls with fraction == 0, // which trips up the check otherwise. if (lastSeenAnimationProgress != 1.0f && lastSeenAnimationProgress != 0.0f && fraction == 0.0f) { // New animation, but need to stop the old one. Figure out where each glyph currently // is in relation to the box position. After that, use the leading digit's current // position as the stop target. currentAnimationNeededStop = true private val moveToCenterDelays get() = if (isLayoutRtl) MOVE_LEFT_DELAYS else MOVE_RIGHT_DELAYS // We assume that the current glyph offsets would be relative to the "from" position. val moveAmount = toRect.left - fromRect.left // Remap the current glyph offsets to be relative to the new "end" position, and figure // out the start/end positions for the stop animation. for (i in 0 until NUM_DIGITS) { glyphOffsets[i] = -moveAmount + glyphOffsets[i] animationCancelStartPosition[i] = glyphOffsets[i] } // Use the leading digit's offset as the stop position. if (toRect.left > fromRect.left) { // It _was_ moving left animationCancelStopPosition = glyphOffsets[0] } else { // It was moving right animationCancelStopPosition = glyphOffsets[1] } } // Is there a cancellation in progress? if (currentAnimationNeededStop && fraction < ANIMATION_CANCELLATION_TIME) { val animationStopProgress = MathUtils.constrainedMap( 0.0f, 1.0f, 0.0f, ANIMATION_CANCELLATION_TIME, fraction ) // One of the digits has already stopped. val animationStopStep = 1.0f / (NUM_DIGITS - 1) private val moveToSideDelays get() = if (isLayoutRtl) MOVE_RIGHT_DELAYS else MOVE_LEFT_DELAYS /** * Offsets the glyphs of the clock for the step clock animation. * * The animation makes the glyphs of the clock move at different speeds, when the clock is * moving horizontally. * * @param clockStartLeft the [getLeft] position of the clock, before it started moving. * @param clockMoveDirection the direction in which it is moving. A positive number means right, * and negative means left. * @param moveFraction fraction of the clock movement. 0 means it is at the beginning, and 1 * means it finished moving. */ fun offsetGlyphsForStepClockAnimation( clockStartLeft: Int, clockMoveDirection: Int, moveFraction: Float ) { val isMovingToCenter = if (isLayoutRtl) clockMoveDirection < 0 else clockMoveDirection > 0 val currentMoveAmount = left - clockStartLeft val digitOffsetDirection = if (isLayoutRtl) -1 else 1 for (i in 0 until NUM_DIGITS) { val stopAmount = if (toRect.left > fromRect.left) { // It was moving left (before flipping) MOVE_LEFT_DELAYS[i] * animationStopStep } else { // It was moving right (before flipping) MOVE_RIGHT_DELAYS[i] * animationStopStep } // Leading digit stops immediately. if (stopAmount == 0.0f) { glyphOffsets[i] = animationCancelStopPosition } else { val actualStopAmount = MathUtils.constrainedMap( 0.0f, 1.0f, 0.0f, stopAmount, animationStopProgress ) val easedProgress = MOVE_INTERPOLATOR.getInterpolation(actualStopAmount) val glyphMoveAmount = animationCancelStopPosition - animationCancelStartPosition[i] glyphOffsets[i] = animationCancelStartPosition[i] + glyphMoveAmount * easedProgress } } } else { // Normal part of the animation. // Do we need to remap the animation progress to take account of the cancellation? val actualFraction = if (currentAnimationNeededStop) { MathUtils.constrainedMap( 0.0f, 1.0f, ANIMATION_CANCELLATION_TIME, 1.0f, fraction ) } else { fraction } val digitFractions = (0 until NUM_DIGITS).map { // The delay for each digit, in terms of fraction (i.e. the digit should not move // The delay for the digit, in terms of fraction (i.e. the digit should not move // during 0.0 - 0.1). val initialDelay = if (toRect.left > fromRect.left) { MOVE_RIGHT_DELAYS[it] * MOVE_DIGIT_STEP val digitInitialDelay = if (isMovingToCenter) { moveToCenterDelays[i] * MOVE_DIGIT_STEP } else { MOVE_LEFT_DELAYS[it] * MOVE_DIGIT_STEP } val f = MathUtils.constrainedMap( 0.0f, 1.0f, initialDelay, initialDelay + AVAILABLE_ANIMATION_TIME, actualFraction moveToSideDelays[i] * MOVE_DIGIT_STEP } val digitFraction = MOVE_INTERPOLATOR.getInterpolation( constrainedMap( 0.0f, 1.0f, digitInitialDelay, digitInitialDelay + AVAILABLE_ANIMATION_TIME, moveFraction ) MOVE_INTERPOLATOR.getInterpolation(max(min(f, 1.0f), 0.0f)) } // Was there an animation halt? val moveAmount = if (currentAnimationNeededStop) { // Only need to animate over the remaining space if the animation was aborted. -animationCancelStopPosition } else { toRect.left.toFloat() - fromRect.left.toFloat() } for (i in 0 until NUM_DIGITS) { glyphOffsets[i] = -moveAmount + (moveAmount * digitFractions[i]) } ) val moveAmountForDigit = currentMoveAmount * digitFraction val moveAmountDeltaForDigit = moveAmountForDigit - currentMoveAmount glyphOffsets[i] = digitOffsetDirection * moveAmountDeltaForDigit } invalidate() if (fraction == 1.0f) { // Reset currentAnimationNeededStop = false } lastSeenAnimationProgress = fraction // Ensure that the actual clock container is always in the "end" position. this.setLeftTopRightBottom(toRect.left, toRect.top, toRect.right, toRect.bottom) } // DateFormat.getBestDateTimePattern is extremely expensive, and refresh is called often. Loading Loading @@ -647,9 +578,6 @@ class AnimatableClockView @JvmOverloads constructor( private const val NUM_DIGITS = 4 private const val DIGITS_PER_LINE = 2 // How much of "fraction" to spend on canceling the animation, if needed private const val ANIMATION_CANCELLATION_TIME = 0f // Delays. Each digit's animation should have a slight delay, so we get a nice // "stepping" effect. When moving right, the second digit of the hour should move first. // When moving left, the first digit of the hour should move first. The lists encode Loading @@ -668,6 +596,6 @@ class AnimatableClockView @JvmOverloads constructor( // Total available transition time for each digit, taking into account the step. If step is // 0.1, then digit 0 would animate over 0.0 - 0.7, making availableTime 0.7. private val AVAILABLE_ANIMATION_TIME = 1.0f - MOVE_DIGIT_STEP * (NUM_DIGITS - 1) private const val AVAILABLE_ANIMATION_TIME = 1.0f - MOVE_DIGIT_STEP * (NUM_DIGITS - 1) } } packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt +5 −4 Original line number Diff line number Diff line Loading @@ -189,8 +189,9 @@ class DefaultClockController( view.setLayoutParams(lp) } fun moveForSplitShade(fromRect: Rect, toRect: Rect, fraction: Float) { view.moveForSplitShade(fromRect, toRect, fraction) /** See documentation at [AnimatableClockView.offsetGlyphsForStepClockAnimation]. */ fun offsetGlyphsForStepClockAnimation(fromLeft: Int, direction: Int, fraction: Float) { view.offsetGlyphsForStepClockAnimation(fromLeft, direction, fraction) } } Loading Loading @@ -277,8 +278,8 @@ class DefaultClockController( dozeFraction: Float, foldFraction: Float, ) : DefaultClockAnimations(view, dozeFraction, foldFraction) { override fun onPositionUpdated(fromRect: Rect, toRect: Rect, fraction: Float) { largeClock.moveForSplitShade(fromRect, toRect, fraction) override fun onPositionUpdated(fromLeft: Int, direction: Int, fraction: Float) { largeClock.offsetGlyphsForStepClockAnimation(fromLeft, direction, fraction) } } Loading packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt +10 −2 Original line number Diff line number Diff line Loading @@ -141,8 +141,16 @@ interface ClockAnimations { /** Runs the battery animation (if any). */ fun charge() {} /** Move the clock, for example, if the notification tray appears in split-shade mode. */ fun onPositionUpdated(fromRect: Rect, toRect: Rect, fraction: Float) {} /** * Runs when the clock's position changed during the move animation. * * @param fromLeft the [View.getLeft] position of the clock, before it started moving. * @param direction the direction in which it is moving. A positive number means right, and * negative means left. * @param fraction fraction of the clock movement. 0 means it is at the beginning, and 1 means * it finished moving. */ fun onPositionUpdated(fromLeft: Int, direction: Int, fraction: Float) {} /** * Runs when swiping clock picker, swipingFraction: 1.0 -> clock is scaled up in the preview, Loading packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +14 −13 Original line number Diff line number Diff line Loading @@ -363,8 +363,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV } else { View clockView = clockContainerView.getChildAt(0); transition.excludeTarget(clockView, /* exclude= */ true); TransitionSet set = new TransitionSet(); set.addTransition(transition); Loading @@ -389,8 +387,9 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV @VisibleForTesting static class SplitShadeTransitionAdapter extends Transition { private static final String PROP_BOUNDS = "splitShadeTransitionAdapter:bounds"; private static final String[] TRANSITION_PROPERTIES = { PROP_BOUNDS }; private static final String PROP_BOUNDS_LEFT = "splitShadeTransitionAdapter:boundsLeft"; private static final String PROP_X_IN_WINDOW = "splitShadeTransitionAdapter:xInWindow"; private static final String[] TRANSITION_PROPERTIES = { PROP_BOUNDS_LEFT, PROP_X_IN_WINDOW}; private final KeyguardClockSwitchController mController; Loading @@ -400,12 +399,10 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV } private void captureValues(TransitionValues transitionValues) { Rect boundsRect = new Rect(); boundsRect.left = transitionValues.view.getLeft(); boundsRect.top = transitionValues.view.getTop(); boundsRect.right = transitionValues.view.getRight(); boundsRect.bottom = transitionValues.view.getBottom(); transitionValues.values.put(PROP_BOUNDS, boundsRect); transitionValues.values.put(PROP_BOUNDS_LEFT, transitionValues.view.getLeft()); int[] locationInWindowTmp = new int[2]; transitionValues.view.getLocationInWindow(locationInWindowTmp); transitionValues.values.put(PROP_X_IN_WINDOW, locationInWindowTmp[0]); } @Override Loading @@ -427,8 +424,12 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV } ValueAnimator anim = ValueAnimator.ofFloat(0, 1); Rect from = (Rect) startValues.values.get(PROP_BOUNDS); Rect to = (Rect) endValues.values.get(PROP_BOUNDS); int fromLeft = (int) startValues.values.get(PROP_BOUNDS_LEFT); int fromWindowX = (int) startValues.values.get(PROP_X_IN_WINDOW); int toWindowX = (int) endValues.values.get(PROP_X_IN_WINDOW); // Using windowX, to determine direction, instead of left, as in RTL the difference of // toLeft - fromLeft is always positive, even when moving left. int direction = toWindowX - fromWindowX > 0 ? 1 : -1; anim.addUpdateListener(animation -> { ClockController clock = mController.getClock(); Loading @@ -437,7 +438,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV } clock.getLargeClock().getAnimations() .onPositionUpdated(from, to, animation.getAnimatedFraction()); .onPositionUpdated(fromLeft, direction, animation.getAnimatedFraction()); }); return anim; Loading packages/SystemUI/tests/src/com/android/keyguard/SplitShadeTransitionAdapterTest.kt +18 −3 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.keyguard import android.animation.Animator import android.testing.AndroidTestingRunner import android.transition.TransitionValues import android.view.View import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardStatusViewController.SplitShadeTransitionAdapter import com.android.systemui.SysuiTestCase Loading @@ -44,14 +45,16 @@ class SplitShadeTransitionAdapterTest : SysuiTestCase() { @Test fun createAnimator_nullStartValues_returnsNull() { val animator = adapter.createAnimator(startValues = null, endValues = TransitionValues()) val endValues = createEndValues() val animator = adapter.createAnimator(startValues = null, endValues = endValues) assertThat(animator).isNull() } @Test fun createAnimator_nullEndValues_returnsNull() { val animator = adapter.createAnimator(startValues = TransitionValues(), endValues = null) val animator = adapter.createAnimator(startValues = createStartValues(), endValues = null) assertThat(animator).isNull() } Loading @@ -59,10 +62,22 @@ class SplitShadeTransitionAdapterTest : SysuiTestCase() { @Test fun createAnimator_nonNullStartAndEndValues_returnsAnimator() { val animator = adapter.createAnimator(startValues = TransitionValues(), endValues = TransitionValues()) adapter.createAnimator(startValues = createStartValues(), endValues = createEndValues()) assertThat(animator).isNotNull() } private fun createStartValues() = TransitionValues().also { values -> values.view = View(context) adapter.captureStartValues(values) } private fun createEndValues() = TransitionValues().also { values -> values.view = View(context) adapter.captureEndValues(values) } } private fun SplitShadeTransitionAdapter.createAnimator( Loading Loading
packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt +47 −119 Original line number Diff line number Diff line Loading @@ -22,12 +22,11 @@ import android.annotation.IntRange import android.annotation.SuppressLint import android.content.Context import android.graphics.Canvas import android.graphics.Rect import android.text.Layout import android.text.TextUtils import android.text.format.DateFormat import android.util.AttributeSet import android.util.MathUtils import android.util.MathUtils.constrainedMap import android.widget.TextView import com.android.internal.annotations.VisibleForTesting import com.android.systemui.animation.GlyphCallback Loading @@ -40,8 +39,6 @@ import java.io.PrintWriter import java.util.Calendar import java.util.Locale import java.util.TimeZone import kotlin.math.max import kotlin.math.min /** * Displays the time with the hour positioned above the minutes. (ie: 09 above 30 is 9:30) Loading Loading @@ -478,122 +475,56 @@ class AnimatableClockView @JvmOverloads constructor( pw.println(" time=$time") } fun moveForSplitShade(fromRect: Rect, toRect: Rect, fraction: Float) { // Do we need to cancel an in-flight animation? // Need to also check against 0.0f here; we can sometimes get two calls with fraction == 0, // which trips up the check otherwise. if (lastSeenAnimationProgress != 1.0f && lastSeenAnimationProgress != 0.0f && fraction == 0.0f) { // New animation, but need to stop the old one. Figure out where each glyph currently // is in relation to the box position. After that, use the leading digit's current // position as the stop target. currentAnimationNeededStop = true private val moveToCenterDelays get() = if (isLayoutRtl) MOVE_LEFT_DELAYS else MOVE_RIGHT_DELAYS // We assume that the current glyph offsets would be relative to the "from" position. val moveAmount = toRect.left - fromRect.left // Remap the current glyph offsets to be relative to the new "end" position, and figure // out the start/end positions for the stop animation. for (i in 0 until NUM_DIGITS) { glyphOffsets[i] = -moveAmount + glyphOffsets[i] animationCancelStartPosition[i] = glyphOffsets[i] } // Use the leading digit's offset as the stop position. if (toRect.left > fromRect.left) { // It _was_ moving left animationCancelStopPosition = glyphOffsets[0] } else { // It was moving right animationCancelStopPosition = glyphOffsets[1] } } // Is there a cancellation in progress? if (currentAnimationNeededStop && fraction < ANIMATION_CANCELLATION_TIME) { val animationStopProgress = MathUtils.constrainedMap( 0.0f, 1.0f, 0.0f, ANIMATION_CANCELLATION_TIME, fraction ) // One of the digits has already stopped. val animationStopStep = 1.0f / (NUM_DIGITS - 1) private val moveToSideDelays get() = if (isLayoutRtl) MOVE_RIGHT_DELAYS else MOVE_LEFT_DELAYS /** * Offsets the glyphs of the clock for the step clock animation. * * The animation makes the glyphs of the clock move at different speeds, when the clock is * moving horizontally. * * @param clockStartLeft the [getLeft] position of the clock, before it started moving. * @param clockMoveDirection the direction in which it is moving. A positive number means right, * and negative means left. * @param moveFraction fraction of the clock movement. 0 means it is at the beginning, and 1 * means it finished moving. */ fun offsetGlyphsForStepClockAnimation( clockStartLeft: Int, clockMoveDirection: Int, moveFraction: Float ) { val isMovingToCenter = if (isLayoutRtl) clockMoveDirection < 0 else clockMoveDirection > 0 val currentMoveAmount = left - clockStartLeft val digitOffsetDirection = if (isLayoutRtl) -1 else 1 for (i in 0 until NUM_DIGITS) { val stopAmount = if (toRect.left > fromRect.left) { // It was moving left (before flipping) MOVE_LEFT_DELAYS[i] * animationStopStep } else { // It was moving right (before flipping) MOVE_RIGHT_DELAYS[i] * animationStopStep } // Leading digit stops immediately. if (stopAmount == 0.0f) { glyphOffsets[i] = animationCancelStopPosition } else { val actualStopAmount = MathUtils.constrainedMap( 0.0f, 1.0f, 0.0f, stopAmount, animationStopProgress ) val easedProgress = MOVE_INTERPOLATOR.getInterpolation(actualStopAmount) val glyphMoveAmount = animationCancelStopPosition - animationCancelStartPosition[i] glyphOffsets[i] = animationCancelStartPosition[i] + glyphMoveAmount * easedProgress } } } else { // Normal part of the animation. // Do we need to remap the animation progress to take account of the cancellation? val actualFraction = if (currentAnimationNeededStop) { MathUtils.constrainedMap( 0.0f, 1.0f, ANIMATION_CANCELLATION_TIME, 1.0f, fraction ) } else { fraction } val digitFractions = (0 until NUM_DIGITS).map { // The delay for each digit, in terms of fraction (i.e. the digit should not move // The delay for the digit, in terms of fraction (i.e. the digit should not move // during 0.0 - 0.1). val initialDelay = if (toRect.left > fromRect.left) { MOVE_RIGHT_DELAYS[it] * MOVE_DIGIT_STEP val digitInitialDelay = if (isMovingToCenter) { moveToCenterDelays[i] * MOVE_DIGIT_STEP } else { MOVE_LEFT_DELAYS[it] * MOVE_DIGIT_STEP } val f = MathUtils.constrainedMap( 0.0f, 1.0f, initialDelay, initialDelay + AVAILABLE_ANIMATION_TIME, actualFraction moveToSideDelays[i] * MOVE_DIGIT_STEP } val digitFraction = MOVE_INTERPOLATOR.getInterpolation( constrainedMap( 0.0f, 1.0f, digitInitialDelay, digitInitialDelay + AVAILABLE_ANIMATION_TIME, moveFraction ) MOVE_INTERPOLATOR.getInterpolation(max(min(f, 1.0f), 0.0f)) } // Was there an animation halt? val moveAmount = if (currentAnimationNeededStop) { // Only need to animate over the remaining space if the animation was aborted. -animationCancelStopPosition } else { toRect.left.toFloat() - fromRect.left.toFloat() } for (i in 0 until NUM_DIGITS) { glyphOffsets[i] = -moveAmount + (moveAmount * digitFractions[i]) } ) val moveAmountForDigit = currentMoveAmount * digitFraction val moveAmountDeltaForDigit = moveAmountForDigit - currentMoveAmount glyphOffsets[i] = digitOffsetDirection * moveAmountDeltaForDigit } invalidate() if (fraction == 1.0f) { // Reset currentAnimationNeededStop = false } lastSeenAnimationProgress = fraction // Ensure that the actual clock container is always in the "end" position. this.setLeftTopRightBottom(toRect.left, toRect.top, toRect.right, toRect.bottom) } // DateFormat.getBestDateTimePattern is extremely expensive, and refresh is called often. Loading Loading @@ -647,9 +578,6 @@ class AnimatableClockView @JvmOverloads constructor( private const val NUM_DIGITS = 4 private const val DIGITS_PER_LINE = 2 // How much of "fraction" to spend on canceling the animation, if needed private const val ANIMATION_CANCELLATION_TIME = 0f // Delays. Each digit's animation should have a slight delay, so we get a nice // "stepping" effect. When moving right, the second digit of the hour should move first. // When moving left, the first digit of the hour should move first. The lists encode Loading @@ -668,6 +596,6 @@ class AnimatableClockView @JvmOverloads constructor( // Total available transition time for each digit, taking into account the step. If step is // 0.1, then digit 0 would animate over 0.0 - 0.7, making availableTime 0.7. private val AVAILABLE_ANIMATION_TIME = 1.0f - MOVE_DIGIT_STEP * (NUM_DIGITS - 1) private const val AVAILABLE_ANIMATION_TIME = 1.0f - MOVE_DIGIT_STEP * (NUM_DIGITS - 1) } }
packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt +5 −4 Original line number Diff line number Diff line Loading @@ -189,8 +189,9 @@ class DefaultClockController( view.setLayoutParams(lp) } fun moveForSplitShade(fromRect: Rect, toRect: Rect, fraction: Float) { view.moveForSplitShade(fromRect, toRect, fraction) /** See documentation at [AnimatableClockView.offsetGlyphsForStepClockAnimation]. */ fun offsetGlyphsForStepClockAnimation(fromLeft: Int, direction: Int, fraction: Float) { view.offsetGlyphsForStepClockAnimation(fromLeft, direction, fraction) } } Loading Loading @@ -277,8 +278,8 @@ class DefaultClockController( dozeFraction: Float, foldFraction: Float, ) : DefaultClockAnimations(view, dozeFraction, foldFraction) { override fun onPositionUpdated(fromRect: Rect, toRect: Rect, fraction: Float) { largeClock.moveForSplitShade(fromRect, toRect, fraction) override fun onPositionUpdated(fromLeft: Int, direction: Int, fraction: Float) { largeClock.offsetGlyphsForStepClockAnimation(fromLeft, direction, fraction) } } Loading
packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt +10 −2 Original line number Diff line number Diff line Loading @@ -141,8 +141,16 @@ interface ClockAnimations { /** Runs the battery animation (if any). */ fun charge() {} /** Move the clock, for example, if the notification tray appears in split-shade mode. */ fun onPositionUpdated(fromRect: Rect, toRect: Rect, fraction: Float) {} /** * Runs when the clock's position changed during the move animation. * * @param fromLeft the [View.getLeft] position of the clock, before it started moving. * @param direction the direction in which it is moving. A positive number means right, and * negative means left. * @param fraction fraction of the clock movement. 0 means it is at the beginning, and 1 means * it finished moving. */ fun onPositionUpdated(fromLeft: Int, direction: Int, fraction: Float) {} /** * Runs when swiping clock picker, swipingFraction: 1.0 -> clock is scaled up in the preview, Loading
packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +14 −13 Original line number Diff line number Diff line Loading @@ -363,8 +363,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV } else { View clockView = clockContainerView.getChildAt(0); transition.excludeTarget(clockView, /* exclude= */ true); TransitionSet set = new TransitionSet(); set.addTransition(transition); Loading @@ -389,8 +387,9 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV @VisibleForTesting static class SplitShadeTransitionAdapter extends Transition { private static final String PROP_BOUNDS = "splitShadeTransitionAdapter:bounds"; private static final String[] TRANSITION_PROPERTIES = { PROP_BOUNDS }; private static final String PROP_BOUNDS_LEFT = "splitShadeTransitionAdapter:boundsLeft"; private static final String PROP_X_IN_WINDOW = "splitShadeTransitionAdapter:xInWindow"; private static final String[] TRANSITION_PROPERTIES = { PROP_BOUNDS_LEFT, PROP_X_IN_WINDOW}; private final KeyguardClockSwitchController mController; Loading @@ -400,12 +399,10 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV } private void captureValues(TransitionValues transitionValues) { Rect boundsRect = new Rect(); boundsRect.left = transitionValues.view.getLeft(); boundsRect.top = transitionValues.view.getTop(); boundsRect.right = transitionValues.view.getRight(); boundsRect.bottom = transitionValues.view.getBottom(); transitionValues.values.put(PROP_BOUNDS, boundsRect); transitionValues.values.put(PROP_BOUNDS_LEFT, transitionValues.view.getLeft()); int[] locationInWindowTmp = new int[2]; transitionValues.view.getLocationInWindow(locationInWindowTmp); transitionValues.values.put(PROP_X_IN_WINDOW, locationInWindowTmp[0]); } @Override Loading @@ -427,8 +424,12 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV } ValueAnimator anim = ValueAnimator.ofFloat(0, 1); Rect from = (Rect) startValues.values.get(PROP_BOUNDS); Rect to = (Rect) endValues.values.get(PROP_BOUNDS); int fromLeft = (int) startValues.values.get(PROP_BOUNDS_LEFT); int fromWindowX = (int) startValues.values.get(PROP_X_IN_WINDOW); int toWindowX = (int) endValues.values.get(PROP_X_IN_WINDOW); // Using windowX, to determine direction, instead of left, as in RTL the difference of // toLeft - fromLeft is always positive, even when moving left. int direction = toWindowX - fromWindowX > 0 ? 1 : -1; anim.addUpdateListener(animation -> { ClockController clock = mController.getClock(); Loading @@ -437,7 +438,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV } clock.getLargeClock().getAnimations() .onPositionUpdated(from, to, animation.getAnimatedFraction()); .onPositionUpdated(fromLeft, direction, animation.getAnimatedFraction()); }); return anim; Loading
packages/SystemUI/tests/src/com/android/keyguard/SplitShadeTransitionAdapterTest.kt +18 −3 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.keyguard import android.animation.Animator import android.testing.AndroidTestingRunner import android.transition.TransitionValues import android.view.View import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardStatusViewController.SplitShadeTransitionAdapter import com.android.systemui.SysuiTestCase Loading @@ -44,14 +45,16 @@ class SplitShadeTransitionAdapterTest : SysuiTestCase() { @Test fun createAnimator_nullStartValues_returnsNull() { val animator = adapter.createAnimator(startValues = null, endValues = TransitionValues()) val endValues = createEndValues() val animator = adapter.createAnimator(startValues = null, endValues = endValues) assertThat(animator).isNull() } @Test fun createAnimator_nullEndValues_returnsNull() { val animator = adapter.createAnimator(startValues = TransitionValues(), endValues = null) val animator = adapter.createAnimator(startValues = createStartValues(), endValues = null) assertThat(animator).isNull() } Loading @@ -59,10 +62,22 @@ class SplitShadeTransitionAdapterTest : SysuiTestCase() { @Test fun createAnimator_nonNullStartAndEndValues_returnsAnimator() { val animator = adapter.createAnimator(startValues = TransitionValues(), endValues = TransitionValues()) adapter.createAnimator(startValues = createStartValues(), endValues = createEndValues()) assertThat(animator).isNotNull() } private fun createStartValues() = TransitionValues().also { values -> values.view = View(context) adapter.captureStartValues(values) } private fun createEndValues() = TransitionValues().also { values -> values.view = View(context) adapter.captureEndValues(values) } } private fun SplitShadeTransitionAdapter.createAnimator( Loading