diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt index 9d1dd1b678018..3a19990f06279 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt @@ -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 @@ -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) @@ -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 + private val moveToSideDelays + get() = if (isLayoutRtl) MOVE_RIGHT_DELAYS else MOVE_LEFT_DELAYS - // 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) - - 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 + /** + * 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) { + // The delay for the digit, in terms of fraction (i.e. the digit should not move + // during 0.0 - 0.1). + val digitInitialDelay = + if (isMovingToCenter) { + moveToCenterDelays[i] * MOVE_DIGIT_STEP + } else { + moveToSideDelays[i] * MOVE_DIGIT_STEP + } + val digitFraction = + MOVE_INTERPOLATOR.getInterpolation( + constrainedMap( + 0.0f, + 1.0f, + digitInitialDelay, + digitInitialDelay + AVAILABLE_ANIMATION_TIME, + moveFraction + ) ) - 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 - // during 0.0 - 0.1). - val initialDelay = if (toRect.left > fromRect.left) { - MOVE_RIGHT_DELAYS[it] * 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 - ) - 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. @@ -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 @@ -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) } } diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt index 3fda83d7b6f4d..2cc36008e4f8f 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt @@ -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) } } @@ -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) } } diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt index 8ef2d80330002..ca3e710a4acb9 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt @@ -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, diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index edfcb8d0d1a61..0826f8a8b9857 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -363,8 +363,6 @@ public class KeyguardStatusViewController extends ViewController 0 ? 1 : -1; anim.addUpdateListener(animation -> { ClockController clock = mController.getClock(); @@ -437,7 +438,7 @@ public class KeyguardStatusViewController extends ViewController + values.view = View(context) + adapter.captureStartValues(values) + } + + private fun createEndValues() = + TransitionValues().also { values -> + values.view = View(context) + adapter.captureEndValues(values) + } } private fun SplitShadeTransitionAdapter.createAnimator(